Now can automatically set the commands on the user side.
This commit is contained in:
parent
221df4b0ba
commit
6c6b041133
7 changed files with 168 additions and 78 deletions
|
@ -26,7 +26,7 @@ func NewMutateMessageWidget(fn func(string) string) *MutateMessageWidget {
|
|||
return ret
|
||||
}
|
||||
|
||||
func (w *MutateMessageWidget) Serve(c *tg.Context, updates chan *tg.Update) {
|
||||
func (w *MutateMessageWidget) Serve(c *tg.Context, updates chan *tg.Update) error {
|
||||
for u := range updates {
|
||||
if u.Message == nil {
|
||||
continue
|
||||
|
@ -34,6 +34,7 @@ func (w *MutateMessageWidget) Serve(c *tg.Context, updates chan *tg.Update) {
|
|||
text := u.Message.Text
|
||||
c.Sendf("%s", w.Mutate(text))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ExtractSessionData(c *tg.Context) *SessionData {
|
||||
|
@ -173,47 +174,47 @@ var beh = tg.NewBehaviour().
|
|||
),
|
||||
),
|
||||
),
|
||||
).WithCommands(
|
||||
tg.NewCommand("start").
|
||||
Desc("start or restart the bot or move to the start screen").
|
||||
ActionFunc(func(c *tg.Context){
|
||||
c.ChangeScreen("start")
|
||||
}),
|
||||
tg.NewCommand("hello").
|
||||
Desc("sends the 'Hello, World!' message back").
|
||||
ActionFunc(func(c *tg.Context) {
|
||||
c.Sendf("Hello, World!")
|
||||
}),
|
||||
tg.NewCommand("read").
|
||||
Desc("reads a string and sends it back").
|
||||
ActionFunc(func(c *tg.Context) {
|
||||
/*c.Sendf("Type some text:")
|
||||
msg, err := c.ReadTextMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
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)
|
||||
}),
|
||||
tg.NewCommand("botname").
|
||||
Desc("get the bot name").
|
||||
ActionFunc(func(c *tg.Context) {
|
||||
bd := c.Bot.Value().(*BotData)
|
||||
c.Sendf("My name is %q", bd.Name)
|
||||
}),
|
||||
)
|
||||
).WithCommands(
|
||||
tg.NewCommand("start").
|
||||
Desc("start or restart the bot or move to the start screen").
|
||||
ActionFunc(func(c *tg.Context){
|
||||
c.ChangeScreen("start")
|
||||
}),
|
||||
tg.NewCommand("hello").
|
||||
Desc("sends the 'Hello, World!' message back").
|
||||
ActionFunc(func(c *tg.Context) {
|
||||
c.Sendf("Hello, World!")
|
||||
}),
|
||||
tg.NewCommand("read").
|
||||
Desc("reads a string and sends it back").
|
||||
ActionFunc(func(c *tg.Context) {
|
||||
/*c.Sendf("Type some text:")
|
||||
msg, err := c.ReadTextMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
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)
|
||||
}),
|
||||
tg.NewCommand("botname").
|
||||
Desc("get the bot name").
|
||||
ActionFunc(func(c *tg.Context) {
|
||||
bd := c.Bot.Data.(*BotData)
|
||||
c.Sendf("My name is %q", bd.Name)
|
||||
}),
|
||||
)
|
||||
|
||||
var gBeh = tg.NewGroupBehaviour().
|
||||
InitFunc(func(c *tg.GC) {
|
||||
}).
|
||||
WithCommands(
|
||||
tg.NewGroupCommand("hello").ActionFunc(func(c *tg.GC) {
|
||||
c.Send("Hello, World!")
|
||||
c.Sendf("Hello, World!")
|
||||
}),
|
||||
tg.NewGroupCommand("mycounter").ActionFunc(func(c *tg.GC) {
|
||||
d := c.Session().Data.(*SessionData)
|
||||
|
@ -231,11 +232,15 @@ func main() {
|
|||
bot = bot.
|
||||
WithBehaviour(beh).
|
||||
WithGroupBehaviour(gBeh).
|
||||
WithValue(&BotData{
|
||||
Name: "Jay",
|
||||
}).
|
||||
Debug(true)
|
||||
|
||||
bot.Data = &BotData{
|
||||
Name: "Jay",
|
||||
}
|
||||
|
||||
log.Printf("Authorized on account %s", bot.Api.Self.UserName)
|
||||
bot.Run()
|
||||
err = bot.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
67
tg/bot.go
67
tg/bot.go
|
@ -2,6 +2,7 @@ package tg
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
//"fmt"
|
||||
|
||||
|
@ -14,6 +15,8 @@ type User = tgbotapi.User
|
|||
|
||||
// The wrapper around Telegram API.
|
||||
type Bot struct {
|
||||
// Custom data value.
|
||||
Data any
|
||||
Api *tgbotapi.BotAPI
|
||||
Me *User
|
||||
// Private bot behaviour.
|
||||
|
@ -24,7 +27,6 @@ type Bot struct {
|
|||
channelBehaviour *ChannelBehaviour
|
||||
sessions SessionMap
|
||||
groupSessions GroupSessionMap
|
||||
value any
|
||||
|
||||
}
|
||||
|
||||
|
@ -40,19 +42,6 @@ func NewBot(token string) (*Bot, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// Set the custom global value for the bot,
|
||||
// so it can be accessed from the callback
|
||||
// functions.
|
||||
func (bot *Bot) WithValue(v any) *Bot {
|
||||
bot.value = v
|
||||
return bot
|
||||
}
|
||||
|
||||
// Get the global bot value.
|
||||
func (bot *Bot) Value() any {
|
||||
return bot.value
|
||||
}
|
||||
|
||||
func (bot *Bot) Debug(debug bool) *Bot {
|
||||
bot.Api.Debug = debug
|
||||
return bot
|
||||
|
@ -127,6 +116,40 @@ func (b *Bot) WithGroupSessions(sessions GroupSessionMap) *Bot {
|
|||
return b
|
||||
}
|
||||
|
||||
// Setting the command on the user side.
|
||||
func (bot *Bot) setCommands(
|
||||
scope tgbotapi.BotCommandScope,
|
||||
cmdMap map[CommandName] BotCommander,
|
||||
) {
|
||||
// First the private commands.
|
||||
names := []string{}
|
||||
for name := range cmdMap {
|
||||
names = append(names, string(name))
|
||||
}
|
||||
sort.Strings([]string(names))
|
||||
|
||||
cmds := []BotCommander{}
|
||||
for _, name := range names {
|
||||
cmds = append(
|
||||
cmds,
|
||||
cmdMap[CommandName(name)],
|
||||
)
|
||||
}
|
||||
|
||||
botCmds := []tgbotapi.BotCommand{}
|
||||
for _, cmd := range cmds {
|
||||
botCmds = append(botCmds, cmd.ToApi())
|
||||
}
|
||||
|
||||
//tgbotapi.NewBotCommandScopeAllPrivateChats(),
|
||||
cfg := tgbotapi.NewSetMyCommandsWithScope(
|
||||
scope,
|
||||
botCmds...,
|
||||
)
|
||||
|
||||
bot.Api.Request(cfg)
|
||||
}
|
||||
|
||||
// Run the bot with the Behaviour.
|
||||
func (bot *Bot) Run() error {
|
||||
if bot.behaviour == nil &&
|
||||
|
@ -139,12 +162,28 @@ func (bot *Bot) Run() error {
|
|||
handles := make(map[string]chan *Update)
|
||||
|
||||
if bot.behaviour != nil {
|
||||
commanders := make(map[CommandName] BotCommander)
|
||||
for k, v := range bot.behaviour.Commands {
|
||||
commanders[k] = v
|
||||
}
|
||||
bot.setCommands(
|
||||
tgbotapi.NewBotCommandScopeAllPrivateChats(),
|
||||
commanders,
|
||||
)
|
||||
chn := make(chan *Update)
|
||||
handles["private"] = chn
|
||||
go bot.handlePrivate(chn)
|
||||
}
|
||||
|
||||
if bot.groupBehaviour != nil {
|
||||
commanders := make(map[CommandName] BotCommander)
|
||||
for k, v := range bot.groupBehaviour.Commands {
|
||||
commanders[k] = v
|
||||
}
|
||||
bot.setCommands(
|
||||
tgbotapi.NewBotCommandScopeAllGroupChats(),
|
||||
commanders,
|
||||
)
|
||||
chn := make(chan *Update)
|
||||
handles["group"] = chn
|
||||
handles["supergroup"] = chn
|
||||
|
|
|
@ -3,10 +3,13 @@ package tg
|
|||
import (
|
||||
//"flag"
|
||||
|
||||
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
type Message = apix.Message
|
||||
type BotCommander interface {
|
||||
ToApi() tgbotapi.BotCommand
|
||||
}
|
||||
type Message = tgbotapi.Message
|
||||
type CommandName string
|
||||
|
||||
type Command struct {
|
||||
|
@ -31,6 +34,13 @@ func (c *Command) ActionFunc(af ActionFunc) *Command {
|
|||
return c.WithAction(af)
|
||||
}
|
||||
|
||||
func (c *Command) ToApi() tgbotapi.BotCommand {
|
||||
ret := tgbotapi.BotCommand{}
|
||||
ret.Command = string(c.Name)
|
||||
ret.Description = c.Description
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *Command) Desc(desc string) *Command {
|
||||
c.Description = desc
|
||||
return c
|
||||
|
@ -62,3 +72,10 @@ func (cmd *GroupCommand) Desc(desc string) *GroupCommand {
|
|||
cmd.Description = desc
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *GroupCommand) ToApi() tgbotapi.BotCommand {
|
||||
ret := tgbotapi.BotCommand{}
|
||||
ret.Command = string(c.Name)
|
||||
ret.Description = c.Description
|
||||
return ret
|
||||
}
|
||||
|
|
31
tg/group.go
31
tg/group.go
|
@ -3,7 +3,7 @@ package tg
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
//tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
// Customized actions for the group behaviour.
|
||||
|
@ -56,7 +56,12 @@ func (c *groupContext) handleUpdateChan(updates chan *Update) {
|
|||
cmdName := CommandName(msg.Command())
|
||||
|
||||
// Skipping the commands sent not to us.
|
||||
atName := msg.CommandWithAt()[len(cmdName)+1:]
|
||||
withAt := msg.CommandWithAt()
|
||||
if len(cmdName) == len(withAt) {
|
||||
continue
|
||||
}
|
||||
|
||||
atName := withAt[len(cmdName)+1:]
|
||||
if c.Bot.Me.UserName != atName {
|
||||
continue
|
||||
}
|
||||
|
@ -74,16 +79,20 @@ func (c *groupContext) handleUpdateChan(updates chan *Update) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *groupContext) Sendf(format string, v ...any) error {
|
||||
return c.Send(fmt.Sprintf(format, v...))
|
||||
func (c *groupContext) Sendf(
|
||||
format string,
|
||||
v ...any,
|
||||
) (*Message, error) {
|
||||
msg, err := c.Send(NewMessage(
|
||||
c.Session.Id, fmt.Sprintf(format, v...),
|
||||
))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
// Sends into the chat specified values converted to strings.
|
||||
func (c *groupContext) Send(v ...any) error {
|
||||
msg := tgbotapi.NewMessage(
|
||||
c.Session.Id.ToApi(),
|
||||
fmt.Sprint(v...),
|
||||
)
|
||||
_, err := c.Bot.Api.Send(msg)
|
||||
return err
|
||||
func (c *groupContext) Send(v Sendable) (*Message, error) {
|
||||
return c.Bot.Send(c.Session.Id, v)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ type context struct {
|
|||
// To reach the bot abilities inside callbacks.
|
||||
Bot *Bot
|
||||
widgetUpdates chan *Update
|
||||
CurScreen, PrevScreen *Screen
|
||||
curScreen, prevScreen *Screen
|
||||
}
|
||||
|
||||
// The type represents way to interact with user in
|
||||
|
@ -71,10 +71,7 @@ func (c *context) handleUpdateChan(updates chan *Update) {
|
|||
}
|
||||
|
||||
func (c *context) run(a Action, u *Update) {
|
||||
go a.Act(&Context{
|
||||
context: c,
|
||||
Update: u,
|
||||
})
|
||||
a.Act(&Context{context: c, Update: u})
|
||||
}
|
||||
|
||||
func (c *context) Render(v Renderable) ([]*Message, error) {
|
||||
|
@ -149,8 +146,8 @@ func (c *Context) ChangeScreen(screenId ScreenId) error {
|
|||
// Getting the screen and changing to
|
||||
// then executing its widget.
|
||||
screen := c.Bot.behaviour.Screens[screenId]
|
||||
c.PrevScreen = c.CurScreen
|
||||
c.CurScreen = screen
|
||||
c.prevScreen = c.curScreen
|
||||
c.curScreen = screen
|
||||
|
||||
// Making the new channel for the widget.
|
||||
if c.widgetUpdates != nil {
|
||||
|
|
|
@ -6,10 +6,14 @@ type ScreenId string
|
|||
// Screen statement of the bot.
|
||||
// Mostly what buttons to show.
|
||||
type Screen struct {
|
||||
// Unique identifer to change to the screen
|
||||
// via Context.ChangeScreen method.
|
||||
Id ScreenId
|
||||
// The widget to run when reaching the screen.
|
||||
Widget Widget
|
||||
|
||||
// Needs implementation later.
|
||||
Dynamic DynamicWidget
|
||||
}
|
||||
|
||||
// Map structure for the screens.
|
||||
|
@ -23,3 +27,8 @@ func NewScreen(id ScreenId, widget Widget) *Screen {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Screen) WithDynamic(dynamic DynamicWidget) *Screen {
|
||||
s.Dynamic = dynamic
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
22
tg/widget.go
22
tg/widget.go
|
@ -12,7 +12,20 @@ type Widget interface {
|
|||
// widget MUST end its work.
|
||||
// Mostly made by looping over the
|
||||
// updates range.
|
||||
Serve(*Context, chan *Update)
|
||||
Serve(*Context, chan *Update) error
|
||||
}
|
||||
|
||||
// Implementing the interface provides
|
||||
type DynamicWidget interface {
|
||||
MakeWidget() Widget
|
||||
}
|
||||
|
||||
// The function that implements the Widget
|
||||
// interface.
|
||||
type WidgetFunc func(*Context, chan *Update)
|
||||
|
||||
func (wf WidgetFunc) Serve(c *Context, updates chan *Update){
|
||||
wf(c, updates)
|
||||
}
|
||||
|
||||
// The basic widget to provide keyboard functionality
|
||||
|
@ -64,10 +77,10 @@ func (p *Page) WithSub(sub Widget) *Page {
|
|||
|
||||
func (p *Page) Serve(
|
||||
c *Context, updates chan *Update,
|
||||
) {
|
||||
) error {
|
||||
msgs, err := c.Render(p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// The inline message is always returned
|
||||
|
@ -128,7 +141,7 @@ func (p *Page) Serve(
|
|||
|
||||
_, err := c.Bot.Api.Request(cb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
kbd := p.Inline
|
||||
if kbd == nil {
|
||||
|
@ -156,6 +169,7 @@ func (p *Page) Serve(
|
|||
c.run(act, u)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Page) Render(
|
||||
|
|
Loading…
Reference in a new issue