feat: better document sending handling with more general io.Reader interface to get the data.

This commit is contained in:
Andrey Parhomenko 2023-12-25 17:35:00 +03:00
parent 23a34b0ed3
commit e62ccf5780
6 changed files with 102 additions and 46 deletions

View file

@ -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) {

View file

@ -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
}

75
file.go
View file

@ -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)
}

3
media/hello.txt Normal file
View file

@ -0,0 +1,3 @@
Hello, World!
This is the text document!

16
send.go
View file

@ -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
}

View file

@ -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