A bit more of refactoring and added way to send image files.
This commit is contained in:
parent
b5fbdac972
commit
522b9ada02
5 changed files with 254 additions and 144 deletions
|
@ -152,6 +152,12 @@ var beh = tg.NewBehaviour().
|
||||||
}
|
}
|
||||||
c.Sendf("You typed %q", msg)
|
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 {
|
func mutateMessage(fn func(string) string) tg.ActionFunc {
|
||||||
|
|
BIN
media/cat.jpg
Normal file
BIN
media/cat.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 242 KiB |
144
tg/context.go
144
tg/context.go
|
@ -6,150 +6,6 @@ import (
|
||||||
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
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.
|
// Context for interaction inside groups.
|
||||||
type groupContext struct {
|
type groupContext struct {
|
||||||
*GroupSession
|
*GroupSession
|
||||||
|
|
71
tg/file.go
Normal file
71
tg/file.go
Normal 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 ""
|
||||||
|
}
|
177
tg/private.go
177
tg/private.go
|
@ -1,5 +1,182 @@
|
||||||
package tg
|
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.
|
// Interface to interact with the user.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
*context
|
*context
|
||||||
|
|
Loading…
Reference in a new issue