Create interface for file data.

bot-api-6.1
Syfaro 2021-08-20 15:31:52 -04:00
parent 000cb2eb0e
commit 816532053b
7 changed files with 268 additions and 209 deletions

65
bot.go
View File

@ -3,7 +3,6 @@
package tgbotapi package tgbotapi
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -12,7 +11,6 @@ import (
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"net/url" "net/url"
"os"
"strings" "strings"
"time" "time"
) )
@ -180,54 +178,37 @@ func (bot *BotAPI) UploadFiles(endpoint string, params Params, files []RequestFi
} }
for _, file := range files { for _, file := range files {
switch f := file.File.(type) { if file.Data.NeedsUpload() {
case string: name, reader, err := file.Data.UploadData()
fileHandle, err := os.Open(f)
if err != nil {
w.CloseWithError(err)
return
}
defer fileHandle.Close()
part, err := m.CreateFormFile(file.Name, fileHandle.Name())
if err != nil { if err != nil {
w.CloseWithError(err) w.CloseWithError(err)
return return
} }
io.Copy(part, fileHandle) part, err := m.CreateFormFile(file.Name, name)
case FileBytes:
part, err := m.CreateFormFile(file.Name, f.Name)
if err != nil { if err != nil {
w.CloseWithError(err) w.CloseWithError(err)
return return
} }
buf := bytes.NewBuffer(f.Bytes) if _, err := io.Copy(part, reader); err != nil {
io.Copy(part, buf)
case FileReader:
part, err := m.CreateFormFile(file.Name, f.Name)
if err != nil {
w.CloseWithError(err) w.CloseWithError(err)
return return
} }
io.Copy(part, f.Reader) if closer, ok := reader.(io.ReadCloser); ok {
case FileURL: if err = closer.Close(); err != nil {
val := string(f) w.CloseWithError(err)
if err := m.WriteField(file.Name, val); err != nil { return
}
}
} else {
value := file.Data.SendData()
if err := m.WriteField(file.Name, value); err != nil {
w.CloseWithError(err) w.CloseWithError(err)
return 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 (bot *BotAPI) IsMessageToMe(message Message) bool {
func hasFilesNeedingUpload(files []RequestFile) bool { func hasFilesNeedingUpload(files []RequestFile) bool {
for _, file := range files { for _, file := range files {
switch file.File.(type) { if file.Data.NeedsUpload() {
case string, FileBytes, FileReader:
return true return true
} }
} }
@ -344,20 +324,7 @@ func (bot *BotAPI) Request(c Chattable) (*APIResponse, error) {
// However, if there are no files to be uploaded, there's likely things // However, if there are no files to be uploaded, there's likely things
// that need to be turned into params instead. // that need to be turned into params instead.
for _, file := range files { for _, file := range files {
var s string params[file.Name] = file.Data.SendData()
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
} }
} }

View File

@ -127,7 +127,7 @@ func TestCopyMessage(t *testing.T) {
func TestSendWithNewPhoto(t *testing.T) { func TestSendWithNewPhoto(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewPhoto(ChatID, "tests/image.jpg") msg := NewPhoto(ChatID, FilePath("tests/image.jpg"))
msg.Caption = "Test" msg.Caption = "Test"
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -169,7 +169,7 @@ func TestSendWithNewPhotoWithFileReader(t *testing.T) {
func TestSendWithNewPhotoReply(t *testing.T) { func TestSendWithNewPhotoReply(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewPhoto(ChatID, "tests/image.jpg") msg := NewPhoto(ChatID, FilePath("tests/image.jpg"))
msg.ReplyToMessageID = ReplyToMessageID msg.ReplyToMessageID = ReplyToMessageID
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -182,7 +182,7 @@ func TestSendWithNewPhotoReply(t *testing.T) {
func TestSendNewPhotoToChannel(t *testing.T) { func TestSendNewPhotoToChannel(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewPhotoToChannel(Channel, "tests/image.jpg") msg := NewPhotoToChannel(Channel, FilePath("tests/image.jpg"))
msg.Caption = "Test" msg.Caption = "Test"
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -239,7 +239,7 @@ func TestSendWithExistingPhoto(t *testing.T) {
func TestSendWithNewDocument(t *testing.T) { func TestSendWithNewDocument(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewDocument(ChatID, "tests/image.jpg") msg := NewDocument(ChatID, FilePath("tests/image.jpg"))
_, err := bot.Send(msg) _, err := bot.Send(msg)
if err != nil { if err != nil {
@ -250,8 +250,8 @@ func TestSendWithNewDocument(t *testing.T) {
func TestSendWithNewDocumentAndThumb(t *testing.T) { func TestSendWithNewDocumentAndThumb(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewDocument(ChatID, "tests/voice.ogg") msg := NewDocument(ChatID, FilePath("tests/voice.ogg"))
msg.Thumb = "tests/image.jpg" msg.Thumb = FilePath("tests/image.jpg")
_, err := bot.Send(msg) _, err := bot.Send(msg)
if err != nil { if err != nil {
@ -273,7 +273,7 @@ func TestSendWithExistingDocument(t *testing.T) {
func TestSendWithNewAudio(t *testing.T) { func TestSendWithNewAudio(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewAudio(ChatID, "tests/audio.mp3") msg := NewAudio(ChatID, FilePath("tests/audio.mp3"))
msg.Title = "TEST" msg.Title = "TEST"
msg.Duration = 10 msg.Duration = 10
msg.Performer = "TEST" msg.Performer = "TEST"
@ -302,7 +302,7 @@ func TestSendWithExistingAudio(t *testing.T) {
func TestSendWithNewVoice(t *testing.T) { func TestSendWithNewVoice(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewVoice(ChatID, "tests/voice.ogg") msg := NewVoice(ChatID, FilePath("tests/voice.ogg"))
msg.Duration = 10 msg.Duration = 10
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -356,7 +356,7 @@ func TestSendWithVenue(t *testing.T) {
func TestSendWithNewVideo(t *testing.T) { func TestSendWithNewVideo(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewVideo(ChatID, "tests/video.mp4") msg := NewVideo(ChatID, FilePath("tests/video.mp4"))
msg.Duration = 10 msg.Duration = 10
msg.Caption = "TEST" msg.Caption = "TEST"
@ -384,7 +384,7 @@ func TestSendWithExistingVideo(t *testing.T) {
func TestSendWithNewVideoNote(t *testing.T) { func TestSendWithNewVideoNote(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewVideoNote(ChatID, 240, "tests/videonote.mp4") msg := NewVideoNote(ChatID, 240, FilePath("tests/videonote.mp4"))
msg.Duration = 10 msg.Duration = 10
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -410,7 +410,7 @@ func TestSendWithExistingVideoNote(t *testing.T) {
func TestSendWithNewSticker(t *testing.T) { func TestSendWithNewSticker(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewSticker(ChatID, "tests/image.jpg") msg := NewSticker(ChatID, FilePath("tests/image.jpg"))
_, err := bot.Send(msg) _, err := bot.Send(msg)
@ -434,7 +434,7 @@ func TestSendWithExistingSticker(t *testing.T) {
func TestSendWithNewStickerAndKeyboardHide(t *testing.T) { func TestSendWithNewStickerAndKeyboardHide(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewSticker(ChatID, "tests/image.jpg") msg := NewSticker(ChatID, FilePath("tests/image.jpg"))
msg.ReplyMarkup = ReplyKeyboardRemove{ msg.ReplyMarkup = ReplyKeyboardRemove{
RemoveKeyboard: true, RemoveKeyboard: true,
Selective: false, Selective: false,
@ -550,7 +550,7 @@ func TestSetWebhookWithCert(t *testing.T) {
bot.Request(DeleteWebhookConfig{}) 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 { if err != nil {
t.Error(err) t.Error(err)
@ -609,8 +609,8 @@ func TestSendWithMediaGroupPhotoVideo(t *testing.T) {
cfg := NewMediaGroup(ChatID, []interface{}{ cfg := NewMediaGroup(ChatID, []interface{}{
NewInputMediaPhoto(FileURL("https://github.com/go-telegram-bot-api/telegram-bot-api/raw/0a3a1c8716c4cd8d26a262af9f12dcbab7f3f28c/tests/image.jpg")), NewInputMediaPhoto(FileURL("https://github.com/go-telegram-bot-api/telegram-bot-api/raw/0a3a1c8716c4cd8d26a262af9f12dcbab7f3f28c/tests/image.jpg")),
NewInputMediaPhoto("tests/image.jpg"), NewInputMediaPhoto(FilePath("tests/image.jpg")),
NewInputMediaVideo("tests/video.mp4"), NewInputMediaVideo(FilePath("tests/video.mp4")),
}) })
messages, err := bot.SendMediaGroup(cfg) messages, err := bot.SendMediaGroup(cfg)
@ -632,7 +632,7 @@ func TestSendWithMediaGroupDocument(t *testing.T) {
cfg := NewMediaGroup(ChatID, []interface{}{ cfg := NewMediaGroup(ChatID, []interface{}{
NewInputMediaDocument(FileURL("https://i.imgur.com/unQLJIb.jpg")), NewInputMediaDocument(FileURL("https://i.imgur.com/unQLJIb.jpg")),
NewInputMediaDocument("tests/image.jpg"), NewInputMediaDocument(FilePath("tests/image.jpg")),
}) })
messages, err := bot.SendMediaGroup(cfg) messages, err := bot.SendMediaGroup(cfg)
@ -653,8 +653,8 @@ func TestSendWithMediaGroupAudio(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
cfg := NewMediaGroup(ChatID, []interface{}{ cfg := NewMediaGroup(ChatID, []interface{}{
NewInputMediaAudio("tests/audio.mp3"), NewInputMediaAudio(FilePath("tests/audio.mp3")),
NewInputMediaAudio("tests/audio.mp3"), NewInputMediaAudio(FilePath("tests/audio.mp3")),
}) })
messages, err := bot.SendMediaGroup(cfg) messages, err := bot.SendMediaGroup(cfg)
@ -715,7 +715,7 @@ func ExampleNewWebhook() {
log.Printf("Authorized on account %s", bot.Self.UserName) 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 { if err != nil {
panic(err) panic(err)
@ -755,7 +755,7 @@ func ExampleWebhookHandler() {
log.Printf("Authorized on account %s", bot.Self.UserName) 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 { if err != nil {
panic(err) panic(err)
@ -1004,7 +1004,7 @@ func TestCommands(t *testing.T) {
func TestEditMessageMedia(t *testing.T) { func TestEditMessageMedia(t *testing.T) {
bot, _ := getBot(t) bot, _ := getBot(t)
msg := NewPhoto(ChatID, "tests/image.jpg") msg := NewPhoto(ChatID, FilePath("tests/image.jpg"))
msg.Caption = "Test" msg.Caption = "Test"
m, err := bot.Send(msg) m, err := bot.Send(msg)
@ -1017,7 +1017,7 @@ func TestEditMessageMedia(t *testing.T) {
ChatID: ChatID, ChatID: ChatID,
MessageID: m.MessageID, MessageID: m.MessageID,
}, },
Media: NewInputMediaVideo("tests/video.mp4"), Media: NewInputMediaVideo(FilePath("tests/video.mp4")),
} }
_, err = bot.Request(edit) _, err = bot.Request(edit)
@ -1028,17 +1028,17 @@ func TestEditMessageMedia(t *testing.T) {
func TestPrepareInputMediaForParams(t *testing.T) { func TestPrepareInputMediaForParams(t *testing.T) {
media := []interface{}{ media := []interface{}{
NewInputMediaPhoto("tests/image.jpg"), NewInputMediaPhoto(FilePath("tests/image.jpg")),
NewInputMediaVideo(FileID("test")), NewInputMediaVideo(FileID("test")),
} }
prepared := prepareInputMediaForParams(media) 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") 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") t.Error("New media was not replaced")
} }

View File

@ -1,9 +1,11 @@
package tgbotapi package tgbotapi
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"net/url" "net/url"
"os"
"strconv" "strconv"
) )
@ -97,9 +99,7 @@ const (
// Library errors // Library errors
const ( const (
// ErrBadFileType happens when you pass an unknown type ErrBadURL = "bad or empty url"
ErrBadFileType = "bad file type"
ErrBadURL = "bad or empty url"
) )
// Chattable is any config type that can be sent. // Chattable is any config type that can be sent.
@ -108,21 +108,136 @@ type Chattable interface {
method() string method() string
} }
// RequestFile represents a file associated with a request. May involve
// uploading a file, or passing an existing ID.
type RequestFile struct {
// The multipart upload field name.
Name string
// The file to upload.
File interface{}
}
// Fileable is any config type that can be sent that includes a file. // Fileable is any config type that can be sent that includes a file.
type Fileable interface { type Fileable interface {
Chattable Chattable
files() []RequestFile files() []RequestFile
} }
// RequestFile represents a file associated with a field name.
type RequestFile struct {
// The file field name.
Name string
// 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
}
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. // LogOutConfig is a request to log out of the cloud Bot API server.
// //
// Note that you may not log back in for at least 10 minutes. // Note that you may not log back in for at least 10 minutes.
@ -176,7 +291,7 @@ func (chat *BaseChat) params() (Params, error) {
// BaseFile is a base type for all file config types. // BaseFile is a base type for all file config types.
type BaseFile struct { type BaseFile struct {
BaseChat BaseChat
File interface{} File RequestFileData
} }
func (file BaseFile) params() (Params, error) { func (file BaseFile) params() (Params, error) {
@ -291,7 +406,7 @@ func (config CopyMessageConfig) method() string {
// PhotoConfig contains information about a SendPhoto request. // PhotoConfig contains information about a SendPhoto request.
type PhotoConfig struct { type PhotoConfig struct {
BaseFile BaseFile
Thumb interface{} Thumb RequestFileData
Caption string Caption string
ParseMode string ParseMode string
CaptionEntities []MessageEntity CaptionEntities []MessageEntity
@ -317,13 +432,13 @@ func (config PhotoConfig) method() string {
func (config PhotoConfig) files() []RequestFile { func (config PhotoConfig) files() []RequestFile {
files := []RequestFile{{ files := []RequestFile{{
Name: "photo", Name: "photo",
File: config.File, Data: config.File,
}} }}
if config.Thumb != nil { if config.Thumb != nil {
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: "thumb", Name: "thumb",
File: config.Thumb, Data: config.Thumb,
}) })
} }
@ -333,7 +448,7 @@ func (config PhotoConfig) files() []RequestFile {
// AudioConfig contains information about a SendAudio request. // AudioConfig contains information about a SendAudio request.
type AudioConfig struct { type AudioConfig struct {
BaseFile BaseFile
Thumb interface{} Thumb RequestFileData
Caption string Caption string
ParseMode string ParseMode string
CaptionEntities []MessageEntity CaptionEntities []MessageEntity
@ -365,13 +480,13 @@ func (config AudioConfig) method() string {
func (config AudioConfig) files() []RequestFile { func (config AudioConfig) files() []RequestFile {
files := []RequestFile{{ files := []RequestFile{{
Name: "audio", Name: "audio",
File: config.File, Data: config.File,
}} }}
if config.Thumb != nil { if config.Thumb != nil {
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: "thumb", Name: "thumb",
File: config.Thumb, Data: config.Thumb,
}) })
} }
@ -381,7 +496,7 @@ func (config AudioConfig) files() []RequestFile {
// DocumentConfig contains information about a SendDocument request. // DocumentConfig contains information about a SendDocument request.
type DocumentConfig struct { type DocumentConfig struct {
BaseFile BaseFile
Thumb interface{} Thumb RequestFileData
Caption string Caption string
ParseMode string ParseMode string
CaptionEntities []MessageEntity CaptionEntities []MessageEntity
@ -405,13 +520,13 @@ func (config DocumentConfig) method() string {
func (config DocumentConfig) files() []RequestFile { func (config DocumentConfig) files() []RequestFile {
files := []RequestFile{{ files := []RequestFile{{
Name: "document", Name: "document",
File: config.File, Data: config.File,
}} }}
if config.Thumb != nil { if config.Thumb != nil {
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: "thumb", Name: "thumb",
File: config.Thumb, Data: config.Thumb,
}) })
} }
@ -434,14 +549,14 @@ func (config StickerConfig) method() string {
func (config StickerConfig) files() []RequestFile { func (config StickerConfig) files() []RequestFile {
return []RequestFile{{ return []RequestFile{{
Name: "sticker", Name: "sticker",
File: config.File, Data: config.File,
}} }}
} }
// VideoConfig contains information about a SendVideo request. // VideoConfig contains information about a SendVideo request.
type VideoConfig struct { type VideoConfig struct {
BaseFile BaseFile
Thumb interface{} Thumb RequestFileData
Duration int Duration int
Caption string Caption string
ParseMode string ParseMode string
@ -471,13 +586,13 @@ func (config VideoConfig) method() string {
func (config VideoConfig) files() []RequestFile { func (config VideoConfig) files() []RequestFile {
files := []RequestFile{{ files := []RequestFile{{
Name: "video", Name: "video",
File: config.File, Data: config.File,
}} }}
if config.Thumb != nil { if config.Thumb != nil {
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: "thumb", Name: "thumb",
File: config.Thumb, Data: config.Thumb,
}) })
} }
@ -488,7 +603,7 @@ func (config VideoConfig) files() []RequestFile {
type AnimationConfig struct { type AnimationConfig struct {
BaseFile BaseFile
Duration int Duration int
Thumb interface{} Thumb RequestFileData
Caption string Caption string
ParseMode string ParseMode string
CaptionEntities []MessageEntity CaptionEntities []MessageEntity
@ -515,13 +630,13 @@ func (config AnimationConfig) method() string {
func (config AnimationConfig) files() []RequestFile { func (config AnimationConfig) files() []RequestFile {
files := []RequestFile{{ files := []RequestFile{{
Name: "animation", Name: "animation",
File: config.File, Data: config.File,
}} }}
if config.Thumb != nil { if config.Thumb != nil {
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: "thumb", Name: "thumb",
File: config.Thumb, Data: config.Thumb,
}) })
} }
@ -531,7 +646,7 @@ func (config AnimationConfig) files() []RequestFile {
// VideoNoteConfig contains information about a SendVideoNote request. // VideoNoteConfig contains information about a SendVideoNote request.
type VideoNoteConfig struct { type VideoNoteConfig struct {
BaseFile BaseFile
Thumb interface{} Thumb RequestFileData
Duration int Duration int
Length int Length int
} }
@ -552,13 +667,13 @@ func (config VideoNoteConfig) method() string {
func (config VideoNoteConfig) files() []RequestFile { func (config VideoNoteConfig) files() []RequestFile {
files := []RequestFile{{ files := []RequestFile{{
Name: "video_note", Name: "video_note",
File: config.File, Data: config.File,
}} }}
if config.Thumb != nil { if config.Thumb != nil {
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: "thumb", Name: "thumb",
File: config.Thumb, Data: config.Thumb,
}) })
} }
@ -568,7 +683,7 @@ func (config VideoNoteConfig) files() []RequestFile {
// VoiceConfig contains information about a SendVoice request. // VoiceConfig contains information about a SendVoice request.
type VoiceConfig struct { type VoiceConfig struct {
BaseFile BaseFile
Thumb interface{} Thumb RequestFileData
Caption string Caption string
ParseMode string ParseMode string
CaptionEntities []MessageEntity CaptionEntities []MessageEntity
@ -596,13 +711,13 @@ func (config VoiceConfig) method() string {
func (config VoiceConfig) files() []RequestFile { func (config VoiceConfig) files() []RequestFile {
files := []RequestFile{{ files := []RequestFile{{
Name: "voice", Name: "voice",
File: config.File, Data: config.File,
}} }}
if config.Thumb != nil { if config.Thumb != nil {
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: "thumb", Name: "thumb",
File: config.Thumb, Data: config.Thumb,
}) })
} }
@ -1045,7 +1160,7 @@ func (config UpdateConfig) params() (Params, error) {
// WebhookConfig contains information about a SetWebhook request. // WebhookConfig contains information about a SetWebhook request.
type WebhookConfig struct { type WebhookConfig struct {
URL *url.URL URL *url.URL
Certificate interface{} Certificate RequestFileData
IPAddress string IPAddress string
MaxConnections int MaxConnections int
AllowedUpdates []string AllowedUpdates []string
@ -1075,7 +1190,7 @@ func (config WebhookConfig) files() []RequestFile {
if config.Certificate != nil { if config.Certificate != nil {
return []RequestFile{{ return []RequestFile{{
Name: "certificate", Name: "certificate",
File: config.Certificate, Data: config.Certificate,
}} }}
} }
@ -1099,25 +1214,6 @@ func (config DeleteWebhookConfig) params() (Params, error) {
return params, nil 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. // InlineConfig contains information on making an InlineQuery response.
type InlineConfig struct { type InlineConfig struct {
InlineQueryID string `json:"inline_query_id"` InlineQueryID string `json:"inline_query_id"`
@ -1706,7 +1802,7 @@ func (config SetChatPhotoConfig) method() string {
func (config SetChatPhotoConfig) files() []RequestFile { func (config SetChatPhotoConfig) files() []RequestFile {
return []RequestFile{{ return []RequestFile{{
Name: "photo", Name: "photo",
File: config.File, Data: config.File,
}} }}
} }
@ -1790,7 +1886,7 @@ func (config GetStickerSetConfig) params() (Params, error) {
// UploadStickerConfig allows you to upload a sticker for use in a set later. // UploadStickerConfig allows you to upload a sticker for use in a set later.
type UploadStickerConfig struct { type UploadStickerConfig struct {
UserID int64 UserID int64
PNGSticker interface{} PNGSticker RequestFileData
} }
func (config UploadStickerConfig) method() string { func (config UploadStickerConfig) method() string {
@ -1808,7 +1904,7 @@ func (config UploadStickerConfig) params() (Params, error) {
func (config UploadStickerConfig) files() []RequestFile { func (config UploadStickerConfig) files() []RequestFile {
return []RequestFile{{ return []RequestFile{{
Name: "png_sticker", Name: "png_sticker",
File: config.PNGSticker, Data: config.PNGSticker,
}} }}
} }
@ -1819,8 +1915,8 @@ type NewStickerSetConfig struct {
UserID int64 UserID int64
Name string Name string
Title string Title string
PNGSticker interface{} PNGSticker RequestFileData
TGSSticker interface{} TGSSticker RequestFileData
Emojis string Emojis string
ContainsMasks bool ContainsMasks bool
MaskPosition *MaskPosition MaskPosition *MaskPosition
@ -1850,13 +1946,13 @@ func (config NewStickerSetConfig) files() []RequestFile {
if config.PNGSticker != nil { if config.PNGSticker != nil {
return []RequestFile{{ return []RequestFile{{
Name: "png_sticker", Name: "png_sticker",
File: config.PNGSticker, Data: config.PNGSticker,
}} }}
} }
return []RequestFile{{ return []RequestFile{{
Name: "tgs_sticker", Name: "tgs_sticker",
File: config.TGSSticker, Data: config.TGSSticker,
}} }}
} }
@ -1864,8 +1960,8 @@ func (config NewStickerSetConfig) files() []RequestFile {
type AddStickerConfig struct { type AddStickerConfig struct {
UserID int64 UserID int64
Name string Name string
PNGSticker interface{} PNGSticker RequestFileData
TGSSticker interface{} TGSSticker RequestFileData
Emojis string Emojis string
MaskPosition *MaskPosition MaskPosition *MaskPosition
} }
@ -1890,13 +1986,13 @@ func (config AddStickerConfig) files() []RequestFile {
if config.PNGSticker != nil { if config.PNGSticker != nil {
return []RequestFile{{ return []RequestFile{{
Name: "png_sticker", Name: "png_sticker",
File: config.PNGSticker, Data: config.PNGSticker,
}} }}
} }
return []RequestFile{{ return []RequestFile{{
Name: "tgs_sticker", Name: "tgs_sticker",
File: config.TGSSticker, Data: config.TGSSticker,
}} }}
} }
@ -1941,7 +2037,7 @@ func (config DeleteStickerConfig) params() (Params, error) {
type SetStickerSetThumbConfig struct { type SetStickerSetThumbConfig struct {
Name string Name string
UserID int64 UserID int64
Thumb interface{} Thumb RequestFileData
} }
func (config SetStickerSetThumbConfig) method() string { func (config SetStickerSetThumbConfig) method() string {
@ -1960,7 +2056,7 @@ func (config SetStickerSetThumbConfig) params() (Params, error) {
func (config SetStickerSetThumbConfig) files() []RequestFile { func (config SetStickerSetThumbConfig) files() []RequestFile {
return []RequestFile{{ return []RequestFile{{
Name: "thumb", Name: "thumb",
File: config.Thumb, Data: config.Thumb,
}} }}
} }
@ -2134,45 +2230,38 @@ func (config DeleteMyCommandsConfig) params() (Params, error) {
func prepareInputMediaParam(inputMedia interface{}, idx int) interface{} { func prepareInputMediaParam(inputMedia interface{}, idx int) interface{} {
switch m := inputMedia.(type) { switch m := inputMedia.(type) {
case InputMediaPhoto: case InputMediaPhoto:
switch m.Media.(type) { if m.Media.NeedsUpload() {
case string, FileBytes, FileReader: m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx))
m.Media = fmt.Sprintf("attach://file-%d", idx)
} }
return m return m
case InputMediaVideo: case InputMediaVideo:
switch m.Media.(type) { if m.Media.NeedsUpload() {
case string, FileBytes, FileReader: m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx))
m.Media = fmt.Sprintf("attach://file-%d", idx)
} }
switch m.Thumb.(type) { if m.Thumb != nil && m.Thumb.NeedsUpload() {
case string, FileBytes, FileReader: m.Thumb = fileAttach(fmt.Sprintf("attach://file-%d-thumb", idx))
m.Thumb = fmt.Sprintf("attach://file-%d-thumb", idx)
} }
return m return m
case InputMediaAudio: case InputMediaAudio:
switch m.Media.(type) { if m.Media.NeedsUpload() {
case string, FileBytes, FileReader: m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx))
m.Media = fmt.Sprintf("attach://file-%d", idx)
} }
switch m.Thumb.(type) { if m.Thumb != nil && m.Thumb.NeedsUpload() {
case string, FileBytes, FileReader: m.Thumb = fileAttach(fmt.Sprintf("attach://file-%d-thumb", idx))
m.Thumb = fmt.Sprintf("attach://file-%d-thumb", idx)
} }
return m return m
case InputMediaDocument: case InputMediaDocument:
switch m.Media.(type) { if m.Media.NeedsUpload() {
case string, FileBytes, FileReader: m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx))
m.Media = fmt.Sprintf("attach://file-%d", idx)
} }
switch m.Thumb.(type) { if m.Thumb != nil && m.Thumb.NeedsUpload() {
case string, FileBytes, FileReader: m.Thumb = fileAttach(fmt.Sprintf("attach://file-%d-thumb", idx))
m.Thumb = fmt.Sprintf("attach://file-%d-thumb", idx)
} }
return m return m
@ -2194,59 +2283,52 @@ func prepareInputMediaFile(inputMedia interface{}, idx int) []RequestFile {
switch m := inputMedia.(type) { switch m := inputMedia.(type) {
case InputMediaPhoto: case InputMediaPhoto:
switch f := m.Media.(type) { if m.Media.NeedsUpload() {
case string, FileBytes, FileReader:
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: fmt.Sprintf("file-%d", idx), Name: fmt.Sprintf("file-%d", idx),
File: f, Data: m.Media,
}) })
} }
case InputMediaVideo: case InputMediaVideo:
switch f := m.Media.(type) { if m.Media.NeedsUpload() {
case string, FileBytes, FileReader:
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: fmt.Sprintf("file-%d", idx), Name: fmt.Sprintf("file-%d", idx),
File: f, Data: m.Media,
}) })
} }
switch f := m.Thumb.(type) { if m.Thumb != nil && m.Thumb.NeedsUpload() {
case string, FileBytes, FileReader:
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: fmt.Sprintf("file-%d-thumb", idx), Name: fmt.Sprintf("file-%d", idx),
File: f, Data: m.Thumb,
}) })
} }
case InputMediaDocument: case InputMediaDocument:
switch f := m.Media.(type) { if m.Media.NeedsUpload() {
case string, FileBytes, FileReader:
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: fmt.Sprintf("file-%d", idx), Name: fmt.Sprintf("file-%d", idx),
File: f, Data: m.Media,
}) })
} }
switch f := m.Thumb.(type) { if m.Thumb != nil && m.Thumb.NeedsUpload() {
case string, FileBytes, FileReader:
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: fmt.Sprintf("file-%d", idx), Name: fmt.Sprintf("file-%d", idx),
File: f, Data: m.Thumb,
}) })
} }
case InputMediaAudio: case InputMediaAudio:
switch f := m.Media.(type) { if m.Media.NeedsUpload() {
case string, FileBytes, FileReader:
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: fmt.Sprintf("file-%d", idx), Name: fmt.Sprintf("file-%d", idx),
File: f, Data: m.Media,
}) })
} }
switch f := m.Thumb.(type) { if m.Thumb != nil && m.Thumb.NeedsUpload() {
case string, FileBytes, FileReader:
files = append(files, RequestFile{ files = append(files, RequestFile{
Name: fmt.Sprintf("file-%d", idx), Name: fmt.Sprintf("file-%d", idx),
File: f, Data: m.Thumb,
}) })
} }
} }

View File

@ -70,7 +70,7 @@ func NewCopyMessage(chatID int64, fromChatID int64, messageID int) CopyMessageCo
// FileReader, or FileBytes. // FileReader, or FileBytes.
// //
// Note that you must send animated GIFs as a document. // 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{ return PhotoConfig{
BaseFile: BaseFile{ BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID}, BaseChat: BaseChat{ChatID: chatID},
@ -82,7 +82,7 @@ func NewPhoto(chatID int64, file interface{}) PhotoConfig {
// NewPhotoToChannel creates a new photo uploader to send a photo to a channel. // NewPhotoToChannel creates a new photo uploader to send a photo to a channel.
// //
// Note that you must send animated GIFs as a document. // 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{ return PhotoConfig{
BaseFile: BaseFile{ BaseFile: BaseFile{
BaseChat: BaseChat{ BaseChat: BaseChat{
@ -94,7 +94,7 @@ func NewPhotoToChannel(username string, file interface{}) PhotoConfig {
} }
// NewAudio creates a new sendAudio request. // NewAudio creates a new sendAudio request.
func NewAudio(chatID int64, file interface{}) AudioConfig { func NewAudio(chatID int64, file RequestFileData) AudioConfig {
return AudioConfig{ return AudioConfig{
BaseFile: BaseFile{ BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID}, BaseChat: BaseChat{ChatID: chatID},
@ -104,7 +104,7 @@ func NewAudio(chatID int64, file interface{}) AudioConfig {
} }
// NewDocument creates a new sendDocument request. // NewDocument creates a new sendDocument request.
func NewDocument(chatID int64, file interface{}) DocumentConfig { func NewDocument(chatID int64, file RequestFileData) DocumentConfig {
return DocumentConfig{ return DocumentConfig{
BaseFile: BaseFile{ BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID}, BaseChat: BaseChat{ChatID: chatID},
@ -114,7 +114,7 @@ func NewDocument(chatID int64, file interface{}) DocumentConfig {
} }
// NewSticker creates a new sendSticker request. // NewSticker creates a new sendSticker request.
func NewSticker(chatID int64, file interface{}) StickerConfig { func NewSticker(chatID int64, file RequestFileData) StickerConfig {
return StickerConfig{ return StickerConfig{
BaseFile: BaseFile{ BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID}, BaseChat: BaseChat{ChatID: chatID},
@ -124,7 +124,7 @@ func NewSticker(chatID int64, file interface{}) StickerConfig {
} }
// NewVideo creates a new sendVideo request. // NewVideo creates a new sendVideo request.
func NewVideo(chatID int64, file interface{}) VideoConfig { func NewVideo(chatID int64, file RequestFileData) VideoConfig {
return VideoConfig{ return VideoConfig{
BaseFile: BaseFile{ BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID}, BaseChat: BaseChat{ChatID: chatID},
@ -134,7 +134,7 @@ func NewVideo(chatID int64, file interface{}) VideoConfig {
} }
// NewAnimation creates a new sendAnimation request. // NewAnimation creates a new sendAnimation request.
func NewAnimation(chatID int64, file interface{}) AnimationConfig { func NewAnimation(chatID int64, file RequestFileData) AnimationConfig {
return AnimationConfig{ return AnimationConfig{
BaseFile: BaseFile{ BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID}, BaseChat: BaseChat{ChatID: chatID},
@ -147,7 +147,7 @@ func NewAnimation(chatID int64, file interface{}) AnimationConfig {
// //
// chatID is where to send it, file is a string path to the file, // chatID is where to send it, file is a string path to the file,
// FileReader, or FileBytes. // FileReader, or FileBytes.
func NewVideoNote(chatID int64, length int, file interface{}) VideoNoteConfig { func NewVideoNote(chatID int64, length int, file RequestFileData) VideoNoteConfig {
return VideoNoteConfig{ return VideoNoteConfig{
BaseFile: BaseFile{ BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID}, BaseChat: BaseChat{ChatID: chatID},
@ -158,7 +158,7 @@ func NewVideoNote(chatID int64, length int, file interface{}) VideoNoteConfig {
} }
// NewVoice creates a new sendVoice request. // NewVoice creates a new sendVoice request.
func NewVoice(chatID int64, file interface{}) VoiceConfig { func NewVoice(chatID int64, file RequestFileData) VoiceConfig {
return VoiceConfig{ return VoiceConfig{
BaseFile: BaseFile{ BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID}, BaseChat: BaseChat{ChatID: chatID},
@ -177,7 +177,7 @@ func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig {
} }
// NewInputMediaPhoto creates a new InputMediaPhoto. // NewInputMediaPhoto creates a new InputMediaPhoto.
func NewInputMediaPhoto(media interface{}) InputMediaPhoto { func NewInputMediaPhoto(media RequestFileData) InputMediaPhoto {
return InputMediaPhoto{ return InputMediaPhoto{
BaseInputMedia{ BaseInputMedia{
Type: "photo", Type: "photo",
@ -187,7 +187,7 @@ func NewInputMediaPhoto(media interface{}) InputMediaPhoto {
} }
// NewInputMediaVideo creates a new InputMediaVideo. // NewInputMediaVideo creates a new InputMediaVideo.
func NewInputMediaVideo(media interface{}) InputMediaVideo { func NewInputMediaVideo(media RequestFileData) InputMediaVideo {
return InputMediaVideo{ return InputMediaVideo{
BaseInputMedia: BaseInputMedia{ BaseInputMedia: BaseInputMedia{
Type: "video", Type: "video",
@ -197,7 +197,7 @@ func NewInputMediaVideo(media interface{}) InputMediaVideo {
} }
// NewInputMediaAnimation creates a new InputMediaAnimation. // NewInputMediaAnimation creates a new InputMediaAnimation.
func NewInputMediaAnimation(media interface{}) InputMediaAnimation { func NewInputMediaAnimation(media RequestFileData) InputMediaAnimation {
return InputMediaAnimation{ return InputMediaAnimation{
BaseInputMedia: BaseInputMedia{ BaseInputMedia: BaseInputMedia{
Type: "animation", Type: "animation",
@ -207,7 +207,7 @@ func NewInputMediaAnimation(media interface{}) InputMediaAnimation {
} }
// NewInputMediaAudio creates a new InputMediaAudio. // NewInputMediaAudio creates a new InputMediaAudio.
func NewInputMediaAudio(media interface{}) InputMediaAudio { func NewInputMediaAudio(media RequestFileData) InputMediaAudio {
return InputMediaAudio{ return InputMediaAudio{
BaseInputMedia: BaseInputMedia{ BaseInputMedia: BaseInputMedia{
Type: "audio", Type: "audio",
@ -217,7 +217,7 @@ func NewInputMediaAudio(media interface{}) InputMediaAudio {
} }
// NewInputMediaDocument creates a new InputMediaDocument. // NewInputMediaDocument creates a new InputMediaDocument.
func NewInputMediaDocument(media interface{}) InputMediaDocument { func NewInputMediaDocument(media RequestFileData) InputMediaDocument {
return InputMediaDocument{ return InputMediaDocument{
BaseInputMedia: BaseInputMedia{ BaseInputMedia: BaseInputMedia{
Type: "document", Type: "document",
@ -316,7 +316,7 @@ func NewWebhook(link string) (WebhookConfig, error) {
// //
// link is the url you wish to get webhooks, // link is the url you wish to get webhooks,
// file contains a string to a file, FileReader, or FileBytes. // 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) u, err := url.Parse(link)
if err != nil { if err != nil {
@ -769,7 +769,7 @@ func NewChatDescription(chatID int64, description string) SetChatDescriptionConf
} }
// NewChatPhoto allows you to update the photo for a chat. // 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{ return SetChatPhotoConfig{
BaseFile: BaseFile{ BaseFile: BaseFile{
BaseChat: BaseChat{ BaseChat: BaseChat{
@ -781,7 +781,7 @@ func NewChatPhoto(chatID int64, photo interface{}) SetChatPhotoConfig {
} }
// NewDeleteChatPhoto allows you to delete the photo for a chat. // NewDeleteChatPhoto allows you to delete the photo for a chat.
func NewDeleteChatPhoto(chatID int64, photo interface{}) DeleteChatPhotoConfig { func NewDeleteChatPhoto(chatID int64) DeleteChatPhotoConfig {
return DeleteChatPhotoConfig{ return DeleteChatPhotoConfig{
ChatID: chatID, ChatID: chatID,
} }

View File

@ -17,7 +17,7 @@ func TestNewWebhook(t *testing.T) {
} }
func TestNewWebhookWithCert(t *testing.T) { func TestNewWebhookWithCert(t *testing.T) {
exampleFile := File{FileID: "123"} exampleFile := FileID("123")
result, err := NewWebhookWithCert("https://example.com/token", exampleFile) result, err := NewWebhookWithCert("https://example.com/token", exampleFile)
if err != nil || if err != nil ||

View File

@ -1685,7 +1685,7 @@ type BaseInputMedia struct {
// pass an HTTP URL for Telegram to get a file from the Internet, // pass an HTTP URL for Telegram to get a file from the Internet,
// or pass “attach://<file_attach_name>” to upload a new one // or pass “attach://<file_attach_name>” to upload a new one
// using multipart/form-data under <file_attach_name> name. // 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 // thumb intentionally missing as it is not currently compatible
// Caption of the video to be sent, 0-1024 characters after entities parsing. // Caption of the video to be sent, 0-1024 characters after entities parsing.
@ -1717,7 +1717,7 @@ type InputMediaVideo struct {
// the file is supported server-side. // the file is supported server-side.
// //
// optional // optional
Thumb interface{} `json:"thumb,omitempty"` Thumb RequestFileData `json:"thumb,omitempty"`
// Width video width // Width video width
// //
// optional // optional
@ -1743,7 +1743,7 @@ type InputMediaAnimation struct {
// the file is supported server-side. // the file is supported server-side.
// //
// optional // optional
Thumb interface{} `json:"thumb,omitempty"` Thumb RequestFileData `json:"thumb,omitempty"`
// Width video width // Width video width
// //
// optional // optional
@ -1765,7 +1765,7 @@ type InputMediaAudio struct {
// the file is supported server-side. // the file is supported server-side.
// //
// optional // optional
Thumb interface{} `json:"thumb,omitempty"` Thumb RequestFileData `json:"thumb,omitempty"`
// Duration of the audio in seconds // Duration of the audio in seconds
// //
// optional // optional
@ -1787,7 +1787,7 @@ type InputMediaDocument struct {
// the file is supported server-side. // the file is supported server-side.
// //
// optional // optional
Thumb interface{} `json:"thumb,omitempty"` Thumb RequestFileData `json:"thumb,omitempty"`
// DisableContentTypeDetection disables automatic server-side content type // DisableContentTypeDetection disables automatic server-side content type
// detection for files uploaded using multipart/form-data. Always true, if // detection for files uploaded using multipart/form-data. Always true, if
// the document is sent as part of an album // the document is sent as part of an album

View File

@ -361,3 +361,13 @@ var (
_ Fileable = (*WebhookConfig)(nil) _ Fileable = (*WebhookConfig)(nil)
_ Fileable = (*SetStickerSetThumbConfig)(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)
)