Add File creation from io.Reader (fix #137)

Refactor sendFiles to accept Files
pull/163/head
Viktor Oreshkin 6 years ago
parent 52a3603547
commit 629fe58962

@ -40,37 +40,78 @@ func (b *Bot) Raw(method string, payload interface{}) ([]byte, error) {
return json, nil
}
func addFileToWriter(writer *multipart.Writer, fieldName string, file interface{}) error {
var reader io.Reader
var part io.Writer
var err error
var filename string
if r, ok := file.(io.Reader); ok {
// Telegram requires fields to have a filename, otherwise
// `Bad Request: wrong URL host` would be returned
filename = "empty"
reader = r
} else if path, ok := file.(string); ok {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
reader = f
filename = filepath.Base(path)
} else {
return errors.Errorf("File for field `%v` should be an io.ReadCloser or string", fieldName)
}
part, err = writer.CreateFormFile(fieldName, filename)
if err != nil {
return err
}
_, err = io.Copy(part, reader)
return err
}
func (b *Bot) sendFiles(
method string,
files map[string]string,
files map[string]File,
params map[string]string) ([]byte, error) {
// ---
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
rawFiles := map[string]interface{}{}
for name, f := range files {
switch {
case f.InCloud():
params[name] = f.FileID
case f.FileURL != "":
params[name] = f.FileURL
case f.OnDisk():
rawFiles[name] = f.FileLocal
case f.FileReader != nil:
rawFiles[name] = f.FileReader
default:
return nil, errors.Errorf("sendFiles: File for field %s doesn't exist", name)
}
}
for name, path := range files {
if err := func() error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
if len(rawFiles) == 0 {
return b.Raw(method, params)
}
part, err := writer.CreateFormFile(name, filepath.Base(path))
if err != nil {
return err
}
writer := multipart.NewWriter(body)
_, err = io.Copy(part, file)
return err
} (); err != nil {
for field, file := range rawFiles {
if err := addFileToWriter(writer, field, file); err != nil {
return nil, wrapSystem(err)
}
}
for field, value := range params {
writer.WriteField(field, value)
if err := writer.WriteField(field, value); err != nil {
return nil, wrapSystem(err)
}
}
if err := writer.Close(); err != nil {
@ -109,20 +150,7 @@ func (b *Bot) sendObject(f *File, what string, params map[string]string) (*Messa
what = "video_note"
}
var respJSON []byte
var err error
if f.InCloud() {
params[what] = f.FileID
respJSON, err = b.Raw(sendWhat, params)
} else if f.FileURL != "" {
params[what] = f.FileURL
respJSON, err = b.Raw(sendWhat, params)
} else {
respJSON, err = b.sendFiles(sendWhat,
map[string]string{what: f.FileLocal}, params)
}
respJSON, err := b.sendFiles(sendWhat, map[string]File{what: *f}, params)
if err != nil {
return nil, err
}

@ -484,7 +484,7 @@ func (b *Bot) Send(to Recipient, what interface{}, options ...interface{}) (*Mes
// From all existing options, it only supports telebot.Silent.
func (b *Bot) SendAlbum(to Recipient, a Album, options ...interface{}) ([]Message, error) {
media := make([]string, len(a))
files := make(map[string]string)
files := make(map[string]File)
for i, x := range a {
var mediaRepr string
@ -496,9 +496,9 @@ func (b *Bot) SendAlbum(to Recipient, a Album, options ...interface{}) ([]Messag
mediaRepr = f.FileID
} else if f.FileURL != "" {
mediaRepr = f.FileURL
} else if f.OnDisk() {
} else if f.OnDisk() || f.FileReader != nil {
mediaRepr = fmt.Sprintf("attach://%d", i)
files[strconv.Itoa(i)] = f.FileLocal
files[strconv.Itoa(i)] = *f
} else {
return nil, errors.Errorf(
"telebot: album entry #%d doesn't exist anywhere", i)
@ -939,8 +939,7 @@ func (b *Bot) SetGroupPhoto(chat *Chat, p *Photo) error {
"chat_id": chat.Recipient(),
}
respJSON, err := b.sendFiles("setChatPhoto",
map[string]string{"photo": p.FileLocal}, params)
respJSON, err := b.sendFiles("setChatPhoto", map[string]File{"photo": p.File}, params)
if err != nil {
return err
}

@ -1,6 +1,7 @@
package telebot
import (
"io"
"os"
)
@ -17,6 +18,9 @@ type File struct {
// file on the internet
FileURL string `json:"file_url"`
// file backed with io.Reader
FileReader io.Reader `json:"-"`
}
// FromDisk constructs a new local (on-disk) file object.
@ -45,6 +49,19 @@ func FromURL(url string) File {
return File{FileURL: url}
}
// FromReader constructs a new file from io.Reader.
//
// Note, it returns File, not *File for a very good reason:
// in telebot, File is pretty much an embeddable struct,
// so upon uploading media you'll need to set embedded File
// with something. NewFile() returning File makes it a one-liner.
//
// photo := &tb.Photo{File: tb.FromReader(bytes.NewReader(...))}
//
func FromReader(reader io.Reader) File {
return File{FileReader: reader}
}
func (f *File) stealRef(g *File) {
if g.OnDisk() {
f.FileLocal = g.FileLocal

@ -48,10 +48,11 @@ type registerResult struct {
Description string `json:"description"`
}
func (h *Webhook) getFiles() map[string]string {
m := make(map[string]string)
func (h *Webhook) getFiles() map[string]File {
m := make(map[string]File)
if h.TLS != nil {
m["certificate"] = h.TLS.Cert
m["certificate"] = FromDisk(h.TLS.Cert)
}
// check if it is overwritten by an endpoint
if h.Endpoint != nil {
@ -63,7 +64,7 @@ func (h *Webhook) getFiles() map[string]string {
delete(m, "certificate")
} else {
// someone configured a certificate
m["certificate"] = h.Endpoint.Cert
m["certificate"] = FromDisk(h.Endpoint.Cert)
}
}
return m

Loading…
Cancel
Save