A bit more of refactoring and added way to send image files.

This commit is contained in:
Andrey Parhomenko 2023-08-19 10:19:31 +03:00
parent b5fbdac972
commit 522b9ada02
5 changed files with 254 additions and 144 deletions

View file

@ -152,6 +152,12 @@ var beh = tg.NewBehaviour().
}
c.Sendf("You typed %q", msg)
}),
tg.NewCommand("image").
Desc("sends a sample image").
ActionFunc(func(c *tg.Context) {
img := tg.NewFile("media/cat.jpg").Image().Caption("A cat!")
c.Send(img)
}),
)
func mutateMessage(fn func(string) string) tg.ActionFunc {

BIN
media/cat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

View file

@ -6,150 +6,6 @@ import (
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
type context struct {
*Session
*Bot
updates chan *Update
// Is true if currently reading the Update.
readingUpdate bool
curScreen, prevScreen *Screen
}
// The type represents way to interact with user in
// handling functions. Is provided to Act() function always.
// Goroutie function to handle each user.
func (c *context) handleUpdateChan(updates chan *Update) {
beh := c.behaviour
if beh.Init != nil {
c.run(beh.Init, nil)
}
for u := range updates {
var act Action
screen := c.curScreen
// The part is added to implement custom update handling.
if u.Message != nil {
if u.Message.IsCommand() && !c.readingUpdate {
cmdName := CommandName(u.Message.Command())
cmd, ok := beh.Commands[cmdName]
if ok {
act = cmd.Action
} else {
}
} else {
kbd := screen.Keyboard
if kbd == nil {
if c.readingUpdate {
c.updates <- u
}
continue
}
btns := kbd.buttonMap()
text := u.Message.Text
btn, ok := btns[text]
if !ok {
if u.Message.Location != nil {
for _, b := range btns {
if b.SendLocation {
btn = b
ok = true
}
}
} else if c.readingUpdate {
// Skipping the update sending it to
// the reading goroutine.
c.updates <- u
continue
}
}
if ok {
act = btn.Action
}
}
} else if u.CallbackQuery != nil {
cb := apix.NewCallback(
u.CallbackQuery.ID,
u.CallbackQuery.Data,
)
data := u.CallbackQuery.Data
_, err := c.Request(cb)
if err != nil {
panic(err)
}
kbd := screen.InlineKeyboard
if kbd == nil {
if c.readingUpdate {
c.updates <- u
}
continue
}
btns := kbd.buttonMap()
btn, ok := btns[data]
if !ok && c.readingUpdate {
c.updates <- u
continue
}
if !ok {
c.Sendf("%q", btns)
continue
}
act = btn.Action
}
if act != nil {
c.run(act, u)
}
}
}
func (c *context) run(a Action, u *Update) {
go a.Act(&Context{
context: c,
Update: u,
})
}
// Returns the next update ignoring current screen.
func (c *context) ReadUpdate() (*Update, error) {
c.readingUpdate = true
u := <-c.updates
c.readingUpdate = false
if u == nil {
return nil, NotAvailableErr
}
return u, nil
}
// Returns the next text message that the user sends.
func (c *context) ReadTextMessage() (string, error) {
u, err := c.ReadUpdate()
if err != nil {
return "", err
}
if u.Message == nil {
return "", WrongUpdateType{}
}
return u.Message.Text, nil
}
// Sends to the user specified text.
func (c *context) Send(v ...any) error {
msg := apix.NewMessage(c.Id.ToTelegram(), fmt.Sprint(v...))
_, err := c.Bot.Send(msg)
return err
}
// Sends the formatted with fmt.Sprintf message to the user.
func (c *context) Sendf(format string, v ...any) error {
return c.Send(fmt.Sprintf(format, v...))
}
// Context for interaction inside groups.
type groupContext struct {
*GroupSession

71
tg/file.go Normal file
View file

@ -0,0 +1,71 @@
package tg
import (
"bufio"
"errors"
"io"
"os"
"path/filepath"
)
type FileType int
const (
NoFileType FileType = iota
ImageFileType
)
var (
UnknownFileTypeErr = errors.New("unknown file type")
)
type File struct {
path string
typ FileType
caption string
}
func NewFile(path string) *File {
return &File{
path: path,
}
}
func (f *File) withType(typ FileType) *File {
f.typ = typ
return f
}
func (f *File) Type() FileType {
return f.typ
}
func (f *File) Image() *File {
return f.withType(ImageFileType)
}
func (f *File) Caption(caption string) *File {
f.caption = caption
return f
}
func (f *File) NeedsUpload() bool {
return true
}
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)
return fileName, bufRd, nil
}
func (f *File) SendData() string {
return ""
}

View file

@ -1,5 +1,182 @@
package tg
import (
"fmt"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
type context struct {
*Session
*Bot
updates chan *Update
// Is true if currently reading the Update.
readingUpdate bool
curScreen, prevScreen *Screen
}
// The type represents way to interact with user in
// handling functions. Is provided to Act() function always.
// Goroutie function to handle each user.
func (c *context) handleUpdateChan(updates chan *Update) {
beh := c.behaviour
if beh.Init != nil {
c.run(beh.Init, nil)
}
for u := range updates {
var act Action
screen := c.curScreen
// The part is added to implement custom update handling.
if u.Message != nil {
if u.Message.IsCommand() && !c.readingUpdate {
cmdName := CommandName(u.Message.Command())
cmd, ok := beh.Commands[cmdName]
if ok {
act = cmd.Action
} else {
}
} else {
kbd := screen.Keyboard
if kbd == nil {
if c.readingUpdate {
c.updates <- u
}
continue
}
btns := kbd.buttonMap()
text := u.Message.Text
btn, ok := btns[text]
if !ok {
if u.Message.Location != nil {
for _, b := range btns {
if b.SendLocation {
btn = b
ok = true
}
}
} else if c.readingUpdate {
// Skipping the update sending it to
// the reading goroutine.
c.updates <- u
continue
}
}
if ok {
act = btn.Action
}
}
} else if u.CallbackQuery != nil {
cb := tgbotapi.NewCallback(
u.CallbackQuery.ID,
u.CallbackQuery.Data,
)
data := u.CallbackQuery.Data
_, err := c.Request(cb)
if err != nil {
panic(err)
}
kbd := screen.InlineKeyboard
if kbd == nil {
if c.readingUpdate {
c.updates <- u
}
continue
}
btns := kbd.buttonMap()
btn, ok := btns[data]
if !ok && c.readingUpdate {
c.updates <- u
continue
}
if !ok {
c.Sendf("%q", btns)
continue
}
act = btn.Action
}
if act != nil {
c.run(act, u)
}
}
}
func (c *context) run(a Action, u *Update) {
go a.Act(&Context{
context: c,
Update: u,
})
}
func (c *context) SendFile(f *File) error {
switch f.typ {
}
return nil
}
// Returns the next update ignoring current screen.
func (c *context) ReadUpdate() (*Update, error) {
c.readingUpdate = true
u := <-c.updates
c.readingUpdate = false
if u == nil {
return nil, NotAvailableErr
}
return u, nil
}
// Returns the next text message that the user sends.
func (c *context) ReadTextMessage() (string, error) {
u, err := c.ReadUpdate()
if err != nil {
return "", err
}
if u.Message == nil {
return "", WrongUpdateType{}
}
return u.Message.Text, nil
}
// Sends to the user specified text.
func (c *context) Send(values ...any) error {
cid := c.Id.ToTelegram()
for _, v := range values {
var msg tgbotapi.Chattable
switch rv := v.(type) {
case *File:
switch rv.Type() {
case ImageFileType:
msg = tgbotapi.NewPhoto(cid, rv)
default:
return UnknownFileTypeErr
}
default:
msg = tgbotapi.NewMessage(
cid, fmt.Sprint(v),
)
}
_, err := c.Bot.Send(msg)
if err != nil {
return err
}
}
return nil
}
// Sends the formatted with fmt.Sprintf message to the user.
func (c *context) Sendf(format string, v ...any) error {
return c.Send(fmt.Sprintf(format, v...))
}
// Interface to interact with the user.
type Context struct {
*context