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)
|
||||
}),
|
||||
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
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"
|
||||
)
|
||||
|
||||
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
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
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue