Create interface for file data.
@@ -3,7 +3,6 @@ // the Telegram Bot API.
package tgbotapi import ( - "bytes" "encoding/json" "errors" "fmt"@@ -12,7 +11,6 @@ "io/ioutil"
"mime/multipart" "net/http" "net/url" - "os" "strings" "time" )@@ -180,54 +178,37 @@ }
} for _, file := range files { - switch f := file.File.(type) { - case string: - fileHandle, err := os.Open(f) + if file.Data.NeedsUpload() { + name, reader, err := file.Data.UploadData() if err != nil { w.CloseWithError(err) return } - defer fileHandle.Close() - part, err := m.CreateFormFile(file.Name, fileHandle.Name()) + part, err := m.CreateFormFile(file.Name, name) if err != nil { w.CloseWithError(err) return } - io.Copy(part, fileHandle) - case FileBytes: - part, err := m.CreateFormFile(file.Name, f.Name) - if err != nil { + if _, err := io.Copy(part, reader); err != nil { w.CloseWithError(err) return } - buf := bytes.NewBuffer(f.Bytes) - io.Copy(part, buf) - case FileReader: - part, err := m.CreateFormFile(file.Name, f.Name) - if err != nil { - w.CloseWithError(err) - return + if closer, ok := reader.(io.ReadCloser); ok { + if err = closer.Close(); err != nil { + w.CloseWithError(err) + return + } } + } else { + value := file.Data.SendData() - io.Copy(part, f.Reader) - case FileURL: - val := string(f) - if err := m.WriteField(file.Name, val); err != nil { + if err := m.WriteField(file.Name, value); err != nil { w.CloseWithError(err) return } - case FileID: - val := string(f) - if err := m.WriteField(file.Name, val); err != nil { - w.CloseWithError(err) - return - } - default: - w.CloseWithError(errors.New(ErrBadFileType)) - return } } }()@@ -316,8 +297,7 @@ }
func hasFilesNeedingUpload(files []RequestFile) bool { for _, file := range files { - switch file.File.(type) { - case string, FileBytes, FileReader: + if file.Data.NeedsUpload() { return true } }@@ -344,20 +324,7 @@
// However, if there are no files to be uploaded, there's likely things // that need to be turned into params instead. for _, file := range files { - var s string - - switch f := file.File.(type) { - case string: - s = f - case FileID: - s = string(f) - case FileURL: - s = string(f) - default: - return nil, errors.New(ErrBadFileType) - } - - params[file.Name] = s + params[file.Name] = file.Data.SendData() } }
@@ -127,7 +127,7 @@
func TestSendWithNewPhoto(t *testing.T) { bot, _ := getBot(t) - msg := NewPhoto(ChatID, "tests/image.jpg") + msg := NewPhoto(ChatID, FilePath("tests/image.jpg")) msg.Caption = "Test" _, err := bot.Send(msg)@@ -169,7 +169,7 @@
func TestSendWithNewPhotoReply(t *testing.T) { bot, _ := getBot(t) - msg := NewPhoto(ChatID, "tests/image.jpg") + msg := NewPhoto(ChatID, FilePath("tests/image.jpg")) msg.ReplyToMessageID = ReplyToMessageID _, err := bot.Send(msg)@@ -182,7 +182,7 @@
func TestSendNewPhotoToChannel(t *testing.T) { bot, _ := getBot(t) - msg := NewPhotoToChannel(Channel, "tests/image.jpg") + msg := NewPhotoToChannel(Channel, FilePath("tests/image.jpg")) msg.Caption = "Test" _, err := bot.Send(msg)@@ -239,7 +239,7 @@
func TestSendWithNewDocument(t *testing.T) { bot, _ := getBot(t) - msg := NewDocument(ChatID, "tests/image.jpg") + msg := NewDocument(ChatID, FilePath("tests/image.jpg")) _, err := bot.Send(msg) if err != nil {@@ -250,8 +250,8 @@
func TestSendWithNewDocumentAndThumb(t *testing.T) { bot, _ := getBot(t) - msg := NewDocument(ChatID, "tests/voice.ogg") - msg.Thumb = "tests/image.jpg" + msg := NewDocument(ChatID, FilePath("tests/voice.ogg")) + msg.Thumb = FilePath("tests/image.jpg") _, err := bot.Send(msg) if err != nil {@@ -273,7 +273,7 @@
func TestSendWithNewAudio(t *testing.T) { bot, _ := getBot(t) - msg := NewAudio(ChatID, "tests/audio.mp3") + msg := NewAudio(ChatID, FilePath("tests/audio.mp3")) msg.Title = "TEST" msg.Duration = 10 msg.Performer = "TEST"@@ -302,7 +302,7 @@
func TestSendWithNewVoice(t *testing.T) { bot, _ := getBot(t) - msg := NewVoice(ChatID, "tests/voice.ogg") + msg := NewVoice(ChatID, FilePath("tests/voice.ogg")) msg.Duration = 10 _, err := bot.Send(msg)@@ -356,7 +356,7 @@
func TestSendWithNewVideo(t *testing.T) { bot, _ := getBot(t) - msg := NewVideo(ChatID, "tests/video.mp4") + msg := NewVideo(ChatID, FilePath("tests/video.mp4")) msg.Duration = 10 msg.Caption = "TEST"@@ -384,7 +384,7 @@
func TestSendWithNewVideoNote(t *testing.T) { bot, _ := getBot(t) - msg := NewVideoNote(ChatID, 240, "tests/videonote.mp4") + msg := NewVideoNote(ChatID, 240, FilePath("tests/videonote.mp4")) msg.Duration = 10 _, err := bot.Send(msg)@@ -410,7 +410,7 @@
func TestSendWithNewSticker(t *testing.T) { bot, _ := getBot(t) - msg := NewSticker(ChatID, "tests/image.jpg") + msg := NewSticker(ChatID, FilePath("tests/image.jpg")) _, err := bot.Send(msg)@@ -434,7 +434,7 @@
func TestSendWithNewStickerAndKeyboardHide(t *testing.T) { bot, _ := getBot(t) - msg := NewSticker(ChatID, "tests/image.jpg") + msg := NewSticker(ChatID, FilePath("tests/image.jpg")) msg.ReplyMarkup = ReplyKeyboardRemove{ RemoveKeyboard: true, Selective: false,@@ -550,7 +550,7 @@ time.Sleep(time.Second * 2)
bot.Request(DeleteWebhookConfig{}) - wh, err := NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, "tests/cert.pem") + wh, err := NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, FilePath("tests/cert.pem")) if err != nil { t.Error(err)@@ -609,8 +609,8 @@ bot, _ := getBot(t)
cfg := NewMediaGroup(ChatID, []interface{}{ NewInputMediaPhoto(FileURL("https://github.com/go-telegram-bot-api/telegram-bot-api/raw/0a3a1c8716c4cd8d26a262af9f12dcbab7f3f28c/tests/image.jpg")), - NewInputMediaPhoto("tests/image.jpg"), - NewInputMediaVideo("tests/video.mp4"), + NewInputMediaPhoto(FilePath("tests/image.jpg")), + NewInputMediaVideo(FilePath("tests/video.mp4")), }) messages, err := bot.SendMediaGroup(cfg)@@ -632,7 +632,7 @@ bot, _ := getBot(t)
cfg := NewMediaGroup(ChatID, []interface{}{ NewInputMediaDocument(FileURL("https://i.imgur.com/unQLJIb.jpg")), - NewInputMediaDocument("tests/image.jpg"), + NewInputMediaDocument(FilePath("tests/image.jpg")), }) messages, err := bot.SendMediaGroup(cfg)@@ -653,8 +653,8 @@ func TestSendWithMediaGroupAudio(t *testing.T) {
bot, _ := getBot(t) cfg := NewMediaGroup(ChatID, []interface{}{ - NewInputMediaAudio("tests/audio.mp3"), - NewInputMediaAudio("tests/audio.mp3"), + NewInputMediaAudio(FilePath("tests/audio.mp3")), + NewInputMediaAudio(FilePath("tests/audio.mp3")), }) messages, err := bot.SendMediaGroup(cfg)@@ -715,7 +715,7 @@ bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName) - wh, err := NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem") + wh, err := NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, FilePath("cert.pem")) if err != nil { panic(err)@@ -755,7 +755,7 @@ bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName) - wh, err := NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem") + wh, err := NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, FilePath("cert.pem")) if err != nil { panic(err)@@ -1004,7 +1004,7 @@
func TestEditMessageMedia(t *testing.T) { bot, _ := getBot(t) - msg := NewPhoto(ChatID, "tests/image.jpg") + msg := NewPhoto(ChatID, FilePath("tests/image.jpg")) msg.Caption = "Test" m, err := bot.Send(msg)@@ -1017,7 +1017,7 @@ BaseEdit: BaseEdit{
ChatID: ChatID, MessageID: m.MessageID, }, - Media: NewInputMediaVideo("tests/video.mp4"), + Media: NewInputMediaVideo(FilePath("tests/video.mp4")), } _, err = bot.Request(edit)@@ -1028,17 +1028,17 @@ }
func TestPrepareInputMediaForParams(t *testing.T) { media := []interface{}{ - NewInputMediaPhoto("tests/image.jpg"), + NewInputMediaPhoto(FilePath("tests/image.jpg")), NewInputMediaVideo(FileID("test")), } prepared := prepareInputMediaForParams(media) - if media[0].(InputMediaPhoto).Media != "tests/image.jpg" { + if media[0].(InputMediaPhoto).Media != FilePath("tests/image.jpg") { t.Error("Original media was changed") } - if prepared[0].(InputMediaPhoto).Media != "attach://file-0" { + if prepared[0].(InputMediaPhoto).Media != FileID("attach://file-0") { t.Error("New media was not replaced") }
@@ -1,9 +1,11 @@
package tgbotapi import ( + "bytes" "fmt" "io" "net/url" + "os" "strconv" )@@ -97,9 +99,7 @@ )
// Library errors const ( - // ErrBadFileType happens when you pass an unknown type - ErrBadFileType = "bad file type" - ErrBadURL = "bad or empty url" + ErrBadURL = "bad or empty url" ) // Chattable is any config type that can be sent.@@ -108,19 +108,134 @@ params() (Params, error)
method() string } -// RequestFile represents a file associated with a request. May involve -// uploading a file, or passing an existing ID. +// Fileable is any config type that can be sent that includes a file. +type Fileable interface { + Chattable + files() []RequestFile +} + +// RequestFile represents a file associated with a field name. type RequestFile struct { - // The multipart upload field name. + // The file field name. Name string - // The file to upload. - File interface{} + // The file data to include. + Data RequestFileData +} + +// RequestFileData represents the data to be used for a file. +type RequestFileData interface { + // If the file needs to be uploaded. + NeedsUpload() bool + + // Get the file name and an `io.Reader` for the file to be uploaded. This + // must only be called when the file needs to be uploaded. + UploadData() (string, io.Reader, error) + // Get the file data to send when a file does not need to be uploaded. This + // must only be called when the file does not need to be uploaded. + SendData() string +} + +// FileBytes contains information about a set of bytes to upload +// as a File. +type FileBytes struct { + Name string + Bytes []byte +} + +func (fb FileBytes) NeedsUpload() bool { + return true +} + +func (fb FileBytes) UploadData() (string, io.Reader, error) { + return fb.Name, bytes.NewReader(fb.Bytes), nil +} + +func (fb FileBytes) SendData() string { + panic("FileBytes must be uploaded") +} + +// FileReader contains information about a reader to upload as a File. +type FileReader struct { + Name string + Reader io.Reader +} + +func (fr FileReader) NeedsUpload() bool { + return true +} + +func (fr FileReader) UploadData() (string, io.Reader, error) { + return fr.Name, fr.Reader, nil +} + +func (fr FileReader) SendData() string { + panic("FileReader must be uploaded") +} + +// FilePath is a path to a local file. +type FilePath string + +func (fp FilePath) NeedsUpload() bool { + return true +} + +func (fp FilePath) UploadData() (string, io.Reader, error) { + fileHandle, err := os.Open(string(fp)) + if err != nil { + return "", nil, err + } + + name := fileHandle.Name() + return name, fileHandle, err } -// Fileable is any config type that can be sent that includes a file. -type Fileable interface { - Chattable - files() []RequestFile +func (fp FilePath) SendData() string { + panic("FilePath must be uploaded") +} + +// FileURL is a URL to use as a file for a request. +type FileURL string + +func (fu FileURL) NeedsUpload() bool { + return false +} + +func (fu FileURL) UploadData() (string, io.Reader, error) { + panic("FileURL cannot be uploaded") +} + +func (fu FileURL) SendData() string { + return string(fu) +} + +// FileID is an ID of a file already uploaded to Telegram. +type FileID string + +func (fi FileID) NeedsUpload() bool { + return false +} + +func (fi FileID) UploadData() (string, io.Reader, error) { + panic("FileID cannot be uploaded") +} + +func (fi FileID) SendData() string { + return string(fi) +} + +// fileAttach is a internal file type used for processed media groups. +type fileAttach string + +func (fa fileAttach) NeedsUpload() bool { + return false +} + +func (fa fileAttach) UploadData() (string, io.Reader, error) { + panic("fileAttach cannot be uploaded") +} + +func (fa fileAttach) SendData() string { + return string(fa) } // LogOutConfig is a request to log out of the cloud Bot API server.@@ -176,7 +291,7 @@
// BaseFile is a base type for all file config types. type BaseFile struct { BaseChat - File interface{} + File RequestFileData } func (file BaseFile) params() (Params, error) {@@ -291,7 +406,7 @@
// PhotoConfig contains information about a SendPhoto request. type PhotoConfig struct { BaseFile - Thumb interface{} + Thumb RequestFileData Caption string ParseMode string CaptionEntities []MessageEntity@@ -317,13 +432,13 @@
func (config PhotoConfig) files() []RequestFile { files := []RequestFile{{ Name: "photo", - File: config.File, + Data: config.File, }} if config.Thumb != nil { files = append(files, RequestFile{ Name: "thumb", - File: config.Thumb, + Data: config.Thumb, }) }@@ -333,7 +448,7 @@
// AudioConfig contains information about a SendAudio request. type AudioConfig struct { BaseFile - Thumb interface{} + Thumb RequestFileData Caption string ParseMode string CaptionEntities []MessageEntity@@ -365,13 +480,13 @@
func (config AudioConfig) files() []RequestFile { files := []RequestFile{{ Name: "audio", - File: config.File, + Data: config.File, }} if config.Thumb != nil { files = append(files, RequestFile{ Name: "thumb", - File: config.Thumb, + Data: config.Thumb, }) }@@ -381,7 +496,7 @@
// DocumentConfig contains information about a SendDocument request. type DocumentConfig struct { BaseFile - Thumb interface{} + Thumb RequestFileData Caption string ParseMode string CaptionEntities []MessageEntity@@ -405,13 +520,13 @@
func (config DocumentConfig) files() []RequestFile { files := []RequestFile{{ Name: "document", - File: config.File, + Data: config.File, }} if config.Thumb != nil { files = append(files, RequestFile{ Name: "thumb", - File: config.Thumb, + Data: config.Thumb, }) }@@ -434,14 +549,14 @@
func (config StickerConfig) files() []RequestFile { return []RequestFile{{ Name: "sticker", - File: config.File, + Data: config.File, }} } // VideoConfig contains information about a SendVideo request. type VideoConfig struct { BaseFile - Thumb interface{} + Thumb RequestFileData Duration int Caption string ParseMode string@@ -471,13 +586,13 @@
func (config VideoConfig) files() []RequestFile { files := []RequestFile{{ Name: "video", - File: config.File, + Data: config.File, }} if config.Thumb != nil { files = append(files, RequestFile{ Name: "thumb", - File: config.Thumb, + Data: config.Thumb, }) }@@ -488,7 +603,7 @@ // AnimationConfig contains information about a SendAnimation request.
type AnimationConfig struct { BaseFile Duration int - Thumb interface{} + Thumb RequestFileData Caption string ParseMode string CaptionEntities []MessageEntity@@ -515,13 +630,13 @@
func (config AnimationConfig) files() []RequestFile { files := []RequestFile{{ Name: "animation", - File: config.File, + Data: config.File, }} if config.Thumb != nil { files = append(files, RequestFile{ Name: "thumb", - File: config.Thumb, + Data: config.Thumb, }) }@@ -531,7 +646,7 @@
// VideoNoteConfig contains information about a SendVideoNote request. type VideoNoteConfig struct { BaseFile - Thumb interface{} + Thumb RequestFileData Duration int Length int }@@ -552,13 +667,13 @@
func (config VideoNoteConfig) files() []RequestFile { files := []RequestFile{{ Name: "video_note", - File: config.File, + Data: config.File, }} if config.Thumb != nil { files = append(files, RequestFile{ Name: "thumb", - File: config.Thumb, + Data: config.Thumb, }) }@@ -568,7 +683,7 @@
// VoiceConfig contains information about a SendVoice request. type VoiceConfig struct { BaseFile - Thumb interface{} + Thumb RequestFileData Caption string ParseMode string CaptionEntities []MessageEntity@@ -596,13 +711,13 @@
func (config VoiceConfig) files() []RequestFile { files := []RequestFile{{ Name: "voice", - File: config.File, + Data: config.File, }} if config.Thumb != nil { files = append(files, RequestFile{ Name: "thumb", - File: config.Thumb, + Data: config.Thumb, }) }@@ -1045,7 +1160,7 @@
// WebhookConfig contains information about a SetWebhook request. type WebhookConfig struct { URL *url.URL - Certificate interface{} + Certificate RequestFileData IPAddress string MaxConnections int AllowedUpdates []string@@ -1075,7 +1190,7 @@ func (config WebhookConfig) files() []RequestFile {
if config.Certificate != nil { return []RequestFile{{ Name: "certificate", - File: config.Certificate, + Data: config.Certificate, }} }@@ -1099,25 +1214,6 @@
return params, nil } -// FileBytes contains information about a set of bytes to upload -// as a File. -type FileBytes struct { - Name string - Bytes []byte -} - -// FileReader contains information about a reader to upload as a File. -type FileReader struct { - Name string - Reader io.Reader -} - -// FileURL is a URL to use as a file for a request. -type FileURL string - -// FileID is an ID of a file already uploaded to Telegram. -type FileID string - // InlineConfig contains information on making an InlineQuery response. type InlineConfig struct { InlineQueryID string `json:"inline_query_id"`@@ -1706,7 +1802,7 @@
func (config SetChatPhotoConfig) files() []RequestFile { return []RequestFile{{ Name: "photo", - File: config.File, + Data: config.File, }} }@@ -1790,7 +1886,7 @@
// UploadStickerConfig allows you to upload a sticker for use in a set later. type UploadStickerConfig struct { UserID int64 - PNGSticker interface{} + PNGSticker RequestFileData } func (config UploadStickerConfig) method() string {@@ -1808,7 +1904,7 @@
func (config UploadStickerConfig) files() []RequestFile { return []RequestFile{{ Name: "png_sticker", - File: config.PNGSticker, + Data: config.PNGSticker, }} }@@ -1819,8 +1915,8 @@ type NewStickerSetConfig struct {
UserID int64 Name string Title string - PNGSticker interface{} - TGSSticker interface{} + PNGSticker RequestFileData + TGSSticker RequestFileData Emojis string ContainsMasks bool MaskPosition *MaskPosition@@ -1850,13 +1946,13 @@ func (config NewStickerSetConfig) files() []RequestFile {
if config.PNGSticker != nil { return []RequestFile{{ Name: "png_sticker", - File: config.PNGSticker, + Data: config.PNGSticker, }} } return []RequestFile{{ Name: "tgs_sticker", - File: config.TGSSticker, + Data: config.TGSSticker, }} }@@ -1864,8 +1960,8 @@ // AddStickerConfig allows you to add a sticker to a set.
type AddStickerConfig struct { UserID int64 Name string - PNGSticker interface{} - TGSSticker interface{} + PNGSticker RequestFileData + TGSSticker RequestFileData Emojis string MaskPosition *MaskPosition }@@ -1890,13 +1986,13 @@ func (config AddStickerConfig) files() []RequestFile {
if config.PNGSticker != nil { return []RequestFile{{ Name: "png_sticker", - File: config.PNGSticker, + Data: config.PNGSticker, }} } return []RequestFile{{ Name: "tgs_sticker", - File: config.TGSSticker, + Data: config.TGSSticker, }} }@@ -1941,7 +2037,7 @@ // SetStickerSetThumbConfig allows you to set the thumbnail for a sticker set.
type SetStickerSetThumbConfig struct { Name string UserID int64 - Thumb interface{} + Thumb RequestFileData } func (config SetStickerSetThumbConfig) method() string {@@ -1960,7 +2056,7 @@
func (config SetStickerSetThumbConfig) files() []RequestFile { return []RequestFile{{ Name: "thumb", - File: config.Thumb, + Data: config.Thumb, }} }@@ -2134,45 +2230,38 @@ // It is expected to be used in conjunction with prepareInputMediaFile.
func prepareInputMediaParam(inputMedia interface{}, idx int) interface{} { switch m := inputMedia.(type) { case InputMediaPhoto: - switch m.Media.(type) { - case string, FileBytes, FileReader: - m.Media = fmt.Sprintf("attach://file-%d", idx) + if m.Media.NeedsUpload() { + m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx)) } return m case InputMediaVideo: - switch m.Media.(type) { - case string, FileBytes, FileReader: - m.Media = fmt.Sprintf("attach://file-%d", idx) + if m.Media.NeedsUpload() { + m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx)) } - switch m.Thumb.(type) { - case string, FileBytes, FileReader: - m.Thumb = fmt.Sprintf("attach://file-%d-thumb", idx) + if m.Thumb != nil && m.Thumb.NeedsUpload() { + m.Thumb = fileAttach(fmt.Sprintf("attach://file-%d-thumb", idx)) } return m case InputMediaAudio: - switch m.Media.(type) { - case string, FileBytes, FileReader: - m.Media = fmt.Sprintf("attach://file-%d", idx) + if m.Media.NeedsUpload() { + m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx)) } - switch m.Thumb.(type) { - case string, FileBytes, FileReader: - m.Thumb = fmt.Sprintf("attach://file-%d-thumb", idx) + if m.Thumb != nil && m.Thumb.NeedsUpload() { + m.Thumb = fileAttach(fmt.Sprintf("attach://file-%d-thumb", idx)) } return m case InputMediaDocument: - switch m.Media.(type) { - case string, FileBytes, FileReader: - m.Media = fmt.Sprintf("attach://file-%d", idx) + if m.Media.NeedsUpload() { + m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx)) } - switch m.Thumb.(type) { - case string, FileBytes, FileReader: - m.Thumb = fmt.Sprintf("attach://file-%d-thumb", idx) + if m.Thumb != nil && m.Thumb.NeedsUpload() { + m.Thumb = fileAttach(fmt.Sprintf("attach://file-%d-thumb", idx)) } return m@@ -2194,59 +2283,52 @@ files := []RequestFile{}
switch m := inputMedia.(type) { case InputMediaPhoto: - switch f := m.Media.(type) { - case string, FileBytes, FileReader: + if m.Media.NeedsUpload() { files = append(files, RequestFile{ Name: fmt.Sprintf("file-%d", idx), - File: f, + Data: m.Media, }) } case InputMediaVideo: - switch f := m.Media.(type) { - case string, FileBytes, FileReader: + if m.Media.NeedsUpload() { files = append(files, RequestFile{ Name: fmt.Sprintf("file-%d", idx), - File: f, + Data: m.Media, }) } - switch f := m.Thumb.(type) { - case string, FileBytes, FileReader: + if m.Thumb != nil && m.Thumb.NeedsUpload() { files = append(files, RequestFile{ - Name: fmt.Sprintf("file-%d-thumb", idx), - File: f, + Name: fmt.Sprintf("file-%d", idx), + Data: m.Thumb, }) } case InputMediaDocument: - switch f := m.Media.(type) { - case string, FileBytes, FileReader: + if m.Media.NeedsUpload() { files = append(files, RequestFile{ Name: fmt.Sprintf("file-%d", idx), - File: f, + Data: m.Media, }) } - switch f := m.Thumb.(type) { - case string, FileBytes, FileReader: + if m.Thumb != nil && m.Thumb.NeedsUpload() { files = append(files, RequestFile{ Name: fmt.Sprintf("file-%d", idx), - File: f, + Data: m.Thumb, }) } case InputMediaAudio: - switch f := m.Media.(type) { - case string, FileBytes, FileReader: + if m.Media.NeedsUpload() { files = append(files, RequestFile{ Name: fmt.Sprintf("file-%d", idx), - File: f, + Data: m.Media, }) } - switch f := m.Thumb.(type) { - case string, FileBytes, FileReader: + if m.Thumb != nil && m.Thumb.NeedsUpload() { files = append(files, RequestFile{ Name: fmt.Sprintf("file-%d", idx), - File: f, + Data: m.Thumb, }) } }
@@ -70,7 +70,7 @@ // chatID is where to send it, file is a string path to the file,
// FileReader, or FileBytes. // // Note that you must send animated GIFs as a document. -func NewPhoto(chatID int64, file interface{}) PhotoConfig { +func NewPhoto(chatID int64, file RequestFileData) PhotoConfig { return PhotoConfig{ BaseFile: BaseFile{ BaseChat: BaseChat{ChatID: chatID},@@ -82,7 +82,7 @@
// NewPhotoToChannel creates a new photo uploader to send a photo to a channel. // // Note that you must send animated GIFs as a document. -func NewPhotoToChannel(username string, file interface{}) PhotoConfig { +func NewPhotoToChannel(username string, file RequestFileData) PhotoConfig { return PhotoConfig{ BaseFile: BaseFile{ BaseChat: BaseChat{@@ -94,7 +94,7 @@ }
} // NewAudio creates a new sendAudio request. -func NewAudio(chatID int64, file interface{}) AudioConfig { +func NewAudio(chatID int64, file RequestFileData) AudioConfig { return AudioConfig{ BaseFile: BaseFile{ BaseChat: BaseChat{ChatID: chatID},@@ -104,7 +104,7 @@ }
} // NewDocument creates a new sendDocument request. -func NewDocument(chatID int64, file interface{}) DocumentConfig { +func NewDocument(chatID int64, file RequestFileData) DocumentConfig { return DocumentConfig{ BaseFile: BaseFile{ BaseChat: BaseChat{ChatID: chatID},@@ -114,7 +114,7 @@ }
} // NewSticker creates a new sendSticker request. -func NewSticker(chatID int64, file interface{}) StickerConfig { +func NewSticker(chatID int64, file RequestFileData) StickerConfig { return StickerConfig{ BaseFile: BaseFile{ BaseChat: BaseChat{ChatID: chatID},@@ -124,7 +124,7 @@ }
} // NewVideo creates a new sendVideo request. -func NewVideo(chatID int64, file interface{}) VideoConfig { +func NewVideo(chatID int64, file RequestFileData) VideoConfig { return VideoConfig{ BaseFile: BaseFile{ BaseChat: BaseChat{ChatID: chatID},@@ -134,7 +134,7 @@ }
} // NewAnimation creates a new sendAnimation request. -func NewAnimation(chatID int64, file interface{}) AnimationConfig { +func NewAnimation(chatID int64, file RequestFileData) AnimationConfig { return AnimationConfig{ BaseFile: BaseFile{ BaseChat: BaseChat{ChatID: chatID},@@ -147,7 +147,7 @@ // NewVideoNote creates a new sendVideoNote request.
// // chatID is where to send it, file is a string path to the file, // FileReader, or FileBytes. -func NewVideoNote(chatID int64, length int, file interface{}) VideoNoteConfig { +func NewVideoNote(chatID int64, length int, file RequestFileData) VideoNoteConfig { return VideoNoteConfig{ BaseFile: BaseFile{ BaseChat: BaseChat{ChatID: chatID},@@ -158,7 +158,7 @@ }
} // NewVoice creates a new sendVoice request. -func NewVoice(chatID int64, file interface{}) VoiceConfig { +func NewVoice(chatID int64, file RequestFileData) VoiceConfig { return VoiceConfig{ BaseFile: BaseFile{ BaseChat: BaseChat{ChatID: chatID},@@ -177,7 +177,7 @@ }
} // NewInputMediaPhoto creates a new InputMediaPhoto. -func NewInputMediaPhoto(media interface{}) InputMediaPhoto { +func NewInputMediaPhoto(media RequestFileData) InputMediaPhoto { return InputMediaPhoto{ BaseInputMedia{ Type: "photo",@@ -187,7 +187,7 @@ }
} // NewInputMediaVideo creates a new InputMediaVideo. -func NewInputMediaVideo(media interface{}) InputMediaVideo { +func NewInputMediaVideo(media RequestFileData) InputMediaVideo { return InputMediaVideo{ BaseInputMedia: BaseInputMedia{ Type: "video",@@ -197,7 +197,7 @@ }
} // NewInputMediaAnimation creates a new InputMediaAnimation. -func NewInputMediaAnimation(media interface{}) InputMediaAnimation { +func NewInputMediaAnimation(media RequestFileData) InputMediaAnimation { return InputMediaAnimation{ BaseInputMedia: BaseInputMedia{ Type: "animation",@@ -207,7 +207,7 @@ }
} // NewInputMediaAudio creates a new InputMediaAudio. -func NewInputMediaAudio(media interface{}) InputMediaAudio { +func NewInputMediaAudio(media RequestFileData) InputMediaAudio { return InputMediaAudio{ BaseInputMedia: BaseInputMedia{ Type: "audio",@@ -217,7 +217,7 @@ }
} // NewInputMediaDocument creates a new InputMediaDocument. -func NewInputMediaDocument(media interface{}) InputMediaDocument { +func NewInputMediaDocument(media RequestFileData) InputMediaDocument { return InputMediaDocument{ BaseInputMedia: BaseInputMedia{ Type: "document",@@ -316,7 +316,7 @@ // NewWebhookWithCert creates a new webhook with a certificate.
// // link is the url you wish to get webhooks, // file contains a string to a file, FileReader, or FileBytes. -func NewWebhookWithCert(link string, file interface{}) (WebhookConfig, error) { +func NewWebhookWithCert(link string, file RequestFileData) (WebhookConfig, error) { u, err := url.Parse(link) if err != nil {@@ -769,7 +769,7 @@ }
} // NewChatPhoto allows you to update the photo for a chat. -func NewChatPhoto(chatID int64, photo interface{}) SetChatPhotoConfig { +func NewChatPhoto(chatID int64, photo RequestFileData) SetChatPhotoConfig { return SetChatPhotoConfig{ BaseFile: BaseFile{ BaseChat: BaseChat{@@ -781,7 +781,7 @@ }
} // NewDeleteChatPhoto allows you to delete the photo for a chat. -func NewDeleteChatPhoto(chatID int64, photo interface{}) DeleteChatPhotoConfig { +func NewDeleteChatPhoto(chatID int64) DeleteChatPhotoConfig { return DeleteChatPhotoConfig{ ChatID: chatID, }
@@ -17,7 +17,7 @@ }
} func TestNewWebhookWithCert(t *testing.T) { - exampleFile := File{FileID: "123"} + exampleFile := FileID("123") result, err := NewWebhookWithCert("https://example.com/token", exampleFile) if err != nil ||
@@ -1685,7 +1685,7 @@ // that exists on the Telegram servers (recommended),
// pass an HTTP URL for Telegram to get a file from the Internet, // or pass “attach://<file_attach_name>” to upload a new one // using multipart/form-data under <file_attach_name> name. - Media interface{} `json:"media"` + Media RequestFileData `json:"media"` // thumb intentionally missing as it is not currently compatible // Caption of the video to be sent, 0-1024 characters after entities parsing.@@ -1717,7 +1717,7 @@ // Thumbnail of the file sent; can be ignored if thumbnail generation for
// the file is supported server-side. // // optional - Thumb interface{} `json:"thumb,omitempty"` + Thumb RequestFileData `json:"thumb,omitempty"` // Width video width // // optional@@ -1743,7 +1743,7 @@ // Thumbnail of the file sent; can be ignored if thumbnail generation for
// the file is supported server-side. // // optional - Thumb interface{} `json:"thumb,omitempty"` + Thumb RequestFileData `json:"thumb,omitempty"` // Width video width // // optional@@ -1765,7 +1765,7 @@ // Thumbnail of the file sent; can be ignored if thumbnail generation for
// the file is supported server-side. // // optional - Thumb interface{} `json:"thumb,omitempty"` + Thumb RequestFileData `json:"thumb,omitempty"` // Duration of the audio in seconds // // optional@@ -1787,7 +1787,7 @@ // Thumbnail of the file sent; can be ignored if thumbnail generation for
// the file is supported server-side. // // optional - Thumb interface{} `json:"thumb,omitempty"` + Thumb RequestFileData `json:"thumb,omitempty"` // DisableContentTypeDetection disables automatic server-side content type // detection for files uploaded using multipart/form-data. Always true, if // the document is sent as part of an album
@@ -361,3 +361,13 @@ _ Fileable = (*MediaGroupConfig)(nil)
_ Fileable = (*WebhookConfig)(nil) _ Fileable = (*SetStickerSetThumbConfig)(nil) ) + +// Ensure all RequestFileData types are correct. +var ( + _ RequestFileData = (*FilePath)(nil) + _ RequestFileData = (*FileBytes)(nil) + _ RequestFileData = (*FileReader)(nil) + _ RequestFileData = (*FileURL)(nil) + _ RequestFileData = (*FileID)(nil) + _ RequestFileData = (*fileAttach)(nil) +)