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
|
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 {
|
for u := range updates {
|
||||||
if u.Message == nil {
|
if u.Message == nil {
|
||||||
continue
|
continue
|
||||||
|
@ -34,6 +34,7 @@ func (w *MutateMessageWidget) Serve(c *tg.Context, updates chan *tg.Update) {
|
||||||
text := u.Message.Text
|
text := u.Message.Text
|
||||||
c.Sendf("%s", w.Mutate(text))
|
c.Sendf("%s", w.Mutate(text))
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtractSessionData(c *tg.Context) *SessionData {
|
func ExtractSessionData(c *tg.Context) *SessionData {
|
||||||
|
@ -173,47 +174,47 @@ var beh = tg.NewBehaviour().
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).WithCommands(
|
).WithCommands(
|
||||||
tg.NewCommand("start").
|
tg.NewCommand("start").
|
||||||
Desc("start or restart the bot or move to the start screen").
|
Desc("start or restart the bot or move to the start screen").
|
||||||
ActionFunc(func(c *tg.Context){
|
ActionFunc(func(c *tg.Context){
|
||||||
c.ChangeScreen("start")
|
c.ChangeScreen("start")
|
||||||
}),
|
}),
|
||||||
tg.NewCommand("hello").
|
tg.NewCommand("hello").
|
||||||
Desc("sends the 'Hello, World!' message back").
|
Desc("sends the 'Hello, World!' message back").
|
||||||
ActionFunc(func(c *tg.Context) {
|
ActionFunc(func(c *tg.Context) {
|
||||||
c.Sendf("Hello, World!")
|
c.Sendf("Hello, World!")
|
||||||
}),
|
}),
|
||||||
tg.NewCommand("read").
|
tg.NewCommand("read").
|
||||||
Desc("reads a string and sends it back").
|
Desc("reads a string and sends it back").
|
||||||
ActionFunc(func(c *tg.Context) {
|
ActionFunc(func(c *tg.Context) {
|
||||||
/*c.Sendf("Type some text:")
|
/*c.Sendf("Type some text:")
|
||||||
msg, err := c.ReadTextMessage()
|
msg, err := c.ReadTextMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Sendf("You typed %q", msg)*/
|
c.Sendf("You typed %q", msg)*/
|
||||||
}),
|
}),
|
||||||
tg.NewCommand("image").
|
tg.NewCommand("image").
|
||||||
Desc("sends a sample image").
|
Desc("sends a sample image").
|
||||||
ActionFunc(func(c *tg.Context) {
|
ActionFunc(func(c *tg.Context) {
|
||||||
img := tg.NewFile("media/cat.jpg").Image().Caption("A cat!")
|
img := tg.NewFile("media/cat.jpg").Image().Caption("A cat!")
|
||||||
c.Send(img)
|
c.Send(img)
|
||||||
}),
|
}),
|
||||||
tg.NewCommand("botname").
|
tg.NewCommand("botname").
|
||||||
Desc("get the bot name").
|
Desc("get the bot name").
|
||||||
ActionFunc(func(c *tg.Context) {
|
ActionFunc(func(c *tg.Context) {
|
||||||
bd := c.Bot.Value().(*BotData)
|
bd := c.Bot.Data.(*BotData)
|
||||||
c.Sendf("My name is %q", bd.Name)
|
c.Sendf("My name is %q", bd.Name)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
var gBeh = tg.NewGroupBehaviour().
|
var gBeh = tg.NewGroupBehaviour().
|
||||||
InitFunc(func(c *tg.GC) {
|
InitFunc(func(c *tg.GC) {
|
||||||
}).
|
}).
|
||||||
WithCommands(
|
WithCommands(
|
||||||
tg.NewGroupCommand("hello").ActionFunc(func(c *tg.GC) {
|
tg.NewGroupCommand("hello").ActionFunc(func(c *tg.GC) {
|
||||||
c.Send("Hello, World!")
|
c.Sendf("Hello, World!")
|
||||||
}),
|
}),
|
||||||
tg.NewGroupCommand("mycounter").ActionFunc(func(c *tg.GC) {
|
tg.NewGroupCommand("mycounter").ActionFunc(func(c *tg.GC) {
|
||||||
d := c.Session().Data.(*SessionData)
|
d := c.Session().Data.(*SessionData)
|
||||||
|
@ -231,11 +232,15 @@ func main() {
|
||||||
bot = bot.
|
bot = bot.
|
||||||
WithBehaviour(beh).
|
WithBehaviour(beh).
|
||||||
WithGroupBehaviour(gBeh).
|
WithGroupBehaviour(gBeh).
|
||||||
WithValue(&BotData{
|
|
||||||
Name: "Jay",
|
|
||||||
}).
|
|
||||||
Debug(true)
|
Debug(true)
|
||||||
|
|
||||||
|
bot.Data = &BotData{
|
||||||
|
Name: "Jay",
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("Authorized on account %s", bot.Api.Self.UserName)
|
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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"sort"
|
||||||
|
|
||||||
//"fmt"
|
//"fmt"
|
||||||
|
|
||||||
|
@ -14,6 +15,8 @@ type User = tgbotapi.User
|
||||||
|
|
||||||
// The wrapper around Telegram API.
|
// The wrapper around Telegram API.
|
||||||
type Bot struct {
|
type Bot struct {
|
||||||
|
// Custom data value.
|
||||||
|
Data any
|
||||||
Api *tgbotapi.BotAPI
|
Api *tgbotapi.BotAPI
|
||||||
Me *User
|
Me *User
|
||||||
// Private bot behaviour.
|
// Private bot behaviour.
|
||||||
|
@ -24,7 +27,6 @@ type Bot struct {
|
||||||
channelBehaviour *ChannelBehaviour
|
channelBehaviour *ChannelBehaviour
|
||||||
sessions SessionMap
|
sessions SessionMap
|
||||||
groupSessions GroupSessionMap
|
groupSessions GroupSessionMap
|
||||||
value any
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,19 +42,6 @@ func NewBot(token string) (*Bot, error) {
|
||||||
}, nil
|
}, 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 {
|
func (bot *Bot) Debug(debug bool) *Bot {
|
||||||
bot.Api.Debug = debug
|
bot.Api.Debug = debug
|
||||||
return bot
|
return bot
|
||||||
|
@ -127,6 +116,40 @@ func (b *Bot) WithGroupSessions(sessions GroupSessionMap) *Bot {
|
||||||
return b
|
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.
|
// Run the bot with the Behaviour.
|
||||||
func (bot *Bot) Run() error {
|
func (bot *Bot) Run() error {
|
||||||
if bot.behaviour == nil &&
|
if bot.behaviour == nil &&
|
||||||
|
@ -139,12 +162,28 @@ func (bot *Bot) Run() error {
|
||||||
handles := make(map[string]chan *Update)
|
handles := make(map[string]chan *Update)
|
||||||
|
|
||||||
if bot.behaviour != nil {
|
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)
|
chn := make(chan *Update)
|
||||||
handles["private"] = chn
|
handles["private"] = chn
|
||||||
go bot.handlePrivate(chn)
|
go bot.handlePrivate(chn)
|
||||||
}
|
}
|
||||||
|
|
||||||
if bot.groupBehaviour != nil {
|
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)
|
chn := make(chan *Update)
|
||||||
handles["group"] = chn
|
handles["group"] = chn
|
||||||
handles["supergroup"] = chn
|
handles["supergroup"] = chn
|
||||||
|
|
|
@ -3,10 +3,13 @@ package tg
|
||||||
import (
|
import (
|
||||||
//"flag"
|
//"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 CommandName string
|
||||||
|
|
||||||
type Command struct {
|
type Command struct {
|
||||||
|
@ -31,6 +34,13 @@ func (c *Command) ActionFunc(af ActionFunc) *Command {
|
||||||
return c.WithAction(af)
|
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 {
|
func (c *Command) Desc(desc string) *Command {
|
||||||
c.Description = desc
|
c.Description = desc
|
||||||
return c
|
return c
|
||||||
|
@ -62,3 +72,10 @@ func (cmd *GroupCommand) Desc(desc string) *GroupCommand {
|
||||||
cmd.Description = desc
|
cmd.Description = desc
|
||||||
return cmd
|
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 (
|
import (
|
||||||
"fmt"
|
"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.
|
// Customized actions for the group behaviour.
|
||||||
|
@ -56,7 +56,12 @@ func (c *groupContext) handleUpdateChan(updates chan *Update) {
|
||||||
cmdName := CommandName(msg.Command())
|
cmdName := CommandName(msg.Command())
|
||||||
|
|
||||||
// Skipping the commands sent not to us.
|
// 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 {
|
if c.Bot.Me.UserName != atName {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -74,16 +79,20 @@ func (c *groupContext) handleUpdateChan(updates chan *Update) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *groupContext) Sendf(format string, v ...any) error {
|
func (c *groupContext) Sendf(
|
||||||
return c.Send(fmt.Sprintf(format, v...))
|
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.
|
// Sends into the chat specified values converted to strings.
|
||||||
func (c *groupContext) Send(v ...any) error {
|
func (c *groupContext) Send(v Sendable) (*Message, error) {
|
||||||
msg := tgbotapi.NewMessage(
|
return c.Bot.Send(c.Session.Id, v)
|
||||||
c.Session.Id.ToApi(),
|
|
||||||
fmt.Sprint(v...),
|
|
||||||
)
|
|
||||||
_, err := c.Bot.Api.Send(msg)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ type context struct {
|
||||||
// To reach the bot abilities inside callbacks.
|
// To reach the bot abilities inside callbacks.
|
||||||
Bot *Bot
|
Bot *Bot
|
||||||
widgetUpdates chan *Update
|
widgetUpdates chan *Update
|
||||||
CurScreen, PrevScreen *Screen
|
curScreen, prevScreen *Screen
|
||||||
}
|
}
|
||||||
|
|
||||||
// The type represents way to interact with user in
|
// 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) {
|
func (c *context) run(a Action, u *Update) {
|
||||||
go a.Act(&Context{
|
a.Act(&Context{context: c, Update: u})
|
||||||
context: c,
|
|
||||||
Update: u,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) Render(v Renderable) ([]*Message, error) {
|
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
|
// Getting the screen and changing to
|
||||||
// then executing its widget.
|
// then executing its widget.
|
||||||
screen := c.Bot.behaviour.Screens[screenId]
|
screen := c.Bot.behaviour.Screens[screenId]
|
||||||
c.PrevScreen = c.CurScreen
|
c.prevScreen = c.curScreen
|
||||||
c.CurScreen = screen
|
c.curScreen = screen
|
||||||
|
|
||||||
// Making the new channel for the widget.
|
// Making the new channel for the widget.
|
||||||
if c.widgetUpdates != nil {
|
if c.widgetUpdates != nil {
|
||||||
|
|
|
@ -6,10 +6,14 @@ type ScreenId string
|
||||||
// Screen statement of the bot.
|
// Screen statement of the bot.
|
||||||
// Mostly what buttons to show.
|
// Mostly what buttons to show.
|
||||||
type Screen struct {
|
type Screen struct {
|
||||||
|
// Unique identifer to change to the screen
|
||||||
|
// via Context.ChangeScreen method.
|
||||||
Id ScreenId
|
Id ScreenId
|
||||||
// The widget to run when reaching the screen.
|
// The widget to run when reaching the screen.
|
||||||
Widget Widget
|
Widget Widget
|
||||||
|
|
||||||
|
// Needs implementation later.
|
||||||
|
Dynamic DynamicWidget
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map structure for the screens.
|
// 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.
|
// widget MUST end its work.
|
||||||
// Mostly made by looping over the
|
// Mostly made by looping over the
|
||||||
// updates range.
|
// 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
|
// The basic widget to provide keyboard functionality
|
||||||
|
@ -64,10 +77,10 @@ func (p *Page) WithSub(sub Widget) *Page {
|
||||||
|
|
||||||
func (p *Page) Serve(
|
func (p *Page) Serve(
|
||||||
c *Context, updates chan *Update,
|
c *Context, updates chan *Update,
|
||||||
) {
|
) error {
|
||||||
msgs, err := c.Render(p)
|
msgs, err := c.Render(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The inline message is always returned
|
// The inline message is always returned
|
||||||
|
@ -128,7 +141,7 @@ func (p *Page) Serve(
|
||||||
|
|
||||||
_, err := c.Bot.Api.Request(cb)
|
_, err := c.Bot.Api.Request(cb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
kbd := p.Inline
|
kbd := p.Inline
|
||||||
if kbd == nil {
|
if kbd == nil {
|
||||||
|
@ -156,6 +169,7 @@ func (p *Page) Serve(
|
||||||
c.run(act, u)
|
c.run(act, u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Page) Render(
|
func (s *Page) Render(
|
||||||
|
|
Loading…
Reference in a new issue