From e62ccf578061f790aa2974beb3a12012649af39c Mon Sep 17 00:00:00 2001 From: surdeus Date: Mon, 25 Dec 2023 17:35:00 +0300 Subject: [PATCH] feat: better document sending handling with more general io.Reader interface to get the data. --- cmd/test/main.go | 23 +++++++++++++-- context.go | 23 ++++++++------- file.go | 75 ++++++++++++++++++++++++++++++++++-------------- media/hello.txt | 3 ++ send.go | 16 ++++------- update.go | 8 +++++- 6 files changed, 102 insertions(+), 46 deletions(-) create mode 100644 media/hello.txt diff --git a/cmd/test/main.go b/cmd/test/main.go index 832b45f..9e578cb 100644 --- a/cmd/test/main.go +++ b/cmd/test/main.go @@ -337,10 +337,27 @@ var beh = tg.NewBehaviour(). c.Sendf2("You typed `%s`", str) }), ), - tg.NewCommand("image", "sends a sample image"). + tg.NewCommand("cat", "sends a sample image of cat"). ActionFunc(func(c *tg.Context) { - img := tg.NewFile("media/cat.jpg").Image().Caption("A cat!") - c.Send(img) + f, err := os.Open("media/cat.jpg") + if err != nil { + c.Sendf("err: %s", err) + return + } + defer f.Close() + photo := tg.NewFile(f).Photo().Name("cat.jpg").Caption("A cat!") + c.Send(photo) + }), + tg.NewCommand("document", "sends a sample text document"). + ActionFunc(func(c *tg.Context) { + f, err := os.Open("media/hello.txt") + if err != nil { + c.Sendf("err: %s", err) + return + } + defer f.Close() + doc := tg.NewFile(f).Document().Name("hello.txt").Caption("The document") + c.Send(doc) }), tg.NewCommand("botname", "get the bot name"). WithAction(tg.Func(func(c *tg.Context) { diff --git a/context.go b/context.go index 4d950f2..5de40be 100644 --- a/context.go +++ b/context.go @@ -156,7 +156,7 @@ func (c *Context) Send(v Sendable) (*Message, error) { // Sends the formatted with fmt.Sprintf message to the user // using default Markdown parsing format. func (c *Context) Sendf(format string, v ...any) (*Message, error) { - return c.Send(NewMessage(fmt.Sprintf(format, v...))) + return c.Send(NewMessage(format, v...)) } // Same as Sendf but uses Markdown 2 format for parsing. @@ -372,10 +372,11 @@ func (c *Context) ReadString(pref string, args ...any) string { return text } -func (c *Context) GetFile(fileId FileId) (io.ReadCloser, error) { +// Returns the reader for specified file ID and path. +func (c *Context) GetFile(fileId FileId) (io.ReadCloser, string, error) { file, err := c.Bot.Api.GetFile(tgbotapi.FileConfig{FileID:string(fileId)}) if err != nil { - return nil, err + return nil, "", err } r, err := http.Get(fmt.Sprintf( "https://api.telegram.org/file/bot%s/%s", @@ -383,27 +384,27 @@ func (c *Context) GetFile(fileId FileId) (io.ReadCloser, error) { file.FilePath, )) if err != nil { - return nil, err + return nil, "", err } if r.StatusCode != 200 { - return nil, StatusCodeErr + return nil, "", StatusCodeErr } - return r.Body, nil + return r.Body, file.FilePath, nil } -func (c *Context) ReadFile(fileId FileId) ([]byte, error) { - file, err := c.GetFile(fileId) +func (c *Context) ReadFile(fileId FileId) ([]byte, string, error) { + file, pth, err := c.GetFile(fileId) if err != nil { - return nil, err + return nil, "", err } defer file.Close() bts, err := io.ReadAll(file) if err != nil { - return nil, err + return nil, "", err } - return bts, nil + return bts, pth, nil } diff --git a/file.go b/file.go index 020d88a..6021716 100644 --- a/file.go +++ b/file.go @@ -4,8 +4,8 @@ import ( "bufio" "errors" "io" - "os" - "path/filepath" + //"os" + //"path/filepath" "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) @@ -16,67 +16,96 @@ type FileType int const ( NoFileType FileType = iota - ImageFileType + PhotoFileType + DocumentFileType ) var ( UnknownFileTypeErr = errors.New("unknown file type") ) +// The type implements the structure to easily send +// files to the client. type File struct { *MessageCompo - path string + name string + reader io.Reader + upload bool typ FileType - caption string + data, caption string } -func NewFile(path string) *File { +// Create the new file with the specified reader. +// By default it NeedsUpload is set to true. +func NewFile(reader io.Reader) *File { ret := &File{} ret.MessageCompo = NewMessage("") - ret.path = path + ret.reader = reader + ret.upload = true return ret } +func (f *File) Name(name string) *File { + f.name = name + return f +} + func (f *File) withType(typ FileType) *File { f.typ = typ return f } +// Get the file type. func (f *File) Type() FileType { return f.typ } -func (f *File) Image() *File { - return f.withType(ImageFileType) +// Set the file type to PhotoFileType. +func (f *File) Photo() *File { + return f.withType(PhotoFileType) } +func (f *File) Document() *File { + return f.withType(DocumentFileType) +} + +// Set the file caption. func (f *File) Caption(caption string) *File { f.caption = caption return f } +// Specifiy whether the file needs to be uploaded to Telegram. +func (f *File) Upload(upload bool) *File { + f.upload = upload + return f +} + +// Set the data to return via SendData() +func (f *File) Data(data string) *File { + f.data = data + return f +} + func (f *File) NeedsUpload() bool { - return true + return f.upload } func (f *File) UploadData() (string, io.Reader, error) { - rd, err := os.Open(f.path) - if err != nil { - return "", nil, err - } - - bufRd := bufio.NewReader(rd) - - fileName := filepath.Base(f.path) + // Bufferizing the reader + // to make it faster. + bufRd := bufio.NewReader(f.reader) + fileName := f.name return fileName, bufRd, nil } func (f *File) SendData() string { - return "" + return f.data } + func (f *File) SendConfig( sid SessionId, bot *Bot, ) (*SendConfig) { @@ -84,11 +113,15 @@ func (f *File) SendConfig( cid := sid.ToApi() switch f.Type() { - case ImageFileType: + case PhotoFileType: photo := tgbotapi.NewPhoto(cid, f) photo.Caption = f.caption - config.Image = &photo + config.Photo = &photo + case DocumentFileType: + doc := tgbotapi.NewDocument(sid.ToApi(), f) + doc.Caption = f.caption + config.Document = &doc default: panic(UnknownFileTypeErr) } diff --git a/media/hello.txt b/media/hello.txt new file mode 100644 index 0000000..48c6117 --- /dev/null +++ b/media/hello.txt @@ -0,0 +1,3 @@ +Hello, World! +This is the text document! + diff --git a/send.go b/send.go index 36451c5..7f2ae54 100644 --- a/send.go +++ b/send.go @@ -21,22 +21,16 @@ type Errorer interface { // The type is used as an endpoint to send messages // via bot.Send . type SendConfig struct { - // The name will be used to store - // the message in the map. - Name string // Message with text and keyboard. Message *tgbotapi.MessageConfig // The image to be sent. - Image *tgbotapi.PhotoConfig + Photo *tgbotapi.PhotoConfig + Document *tgbotapi.DocumentConfig Location *tgbotapi.LocationConfig Error error } -func (cfg *SendConfig) WithName(name string) *SendConfig { - cfg.Name = name - return cfg -} type MessageMap map[string] *Message @@ -45,10 +39,12 @@ func (config *SendConfig) ToApi() tgbotapi.Chattable { switch { case config.Message != nil : return *(config.Message) - case config.Image != nil : - return *(config.Image) + case config.Photo != nil : + return *(config.Photo) case config.Location != nil : return *(config.Location) + case config.Document != nil : + return *(config.Document) } return nil } diff --git a/update.go b/update.go index 488ab0e..329563e 100644 --- a/update.go +++ b/update.go @@ -61,13 +61,19 @@ func (updates *UpdateChan) Close() { } func (u *Update) HasDocument() bool { - return u.Message != nil && u.Message.Document != nil + return u != nil && + u.Message != nil && + u.Message.Document != nil } func (u *Update) DocumentId() FileId { return FileId(u.Update.Message.Document.FileID) } +func (u *Update) DocumentName() string { + return u.Message.Document.FileName +} + func (u *Update) HasPhotos() bool { return u.Message != nil && u.Message.Photo != nil && len(u.Message.Photo) != 0