tg/bot.go

296 lines
5.7 KiB
Go
Raw Normal View History

2023-08-19 09:12:26 +03:00
package tg
2023-07-09 01:28:59 +03:00
import (
2023-08-13 15:37:36 +03:00
"errors"
"sort"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
2023-07-09 01:28:59 +03:00
)
type Chat = tgbotapi.Chat
type User = tgbotapi.User
2023-08-13 15:37:36 +03:00
2023-07-09 01:28:59 +03:00
// The wrapper around Telegram API.
type Bot struct {
// Custom data value.
data any
api *tgbotapi.BotAPI
me User
2023-08-13 15:37:36 +03:00
// Private bot behaviour.
behaviour *Behaviour
// Group bot behaviour.
2023-09-30 09:55:45 +03:00
//groupBehaviour *GroupBehaviour
2023-08-13 15:37:36 +03:00
// Bot behaviour in channels.
2023-09-30 09:55:45 +03:00
//channelBehaviour *ChannelBehaviour
2023-08-13 15:37:36 +03:00
sessions SessionMap
2023-09-30 09:55:45 +03:00
//groupSessions GroupSessionMap
2023-07-09 01:28:59 +03:00
}
2023-08-13 15:37:36 +03:00
// Return the new bot with empty sessions and behaviour.
func NewBot(token string) (*Bot, error) {
bot, err := tgbotapi.NewBotAPI(token)
if err != nil {
return nil, err
}
return &Bot{
api: bot,
}, nil
2023-07-09 01:28:59 +03:00
}
func (bot *Bot) Debug(debug bool) *Bot {
bot.api.Debug = debug
return bot
}
func (bot *Bot) Api() *tgbotapi.BotAPI {
return bot.api
}
func (bot *Bot) Me() User {
return bot.me
}
// Send the Renderable to the specified session client side.
// Can be used for both group and private sessions because
// SessionId represents both for chat IDs.
func (bot *Bot) Send(
sid SessionId, v Sendable,
) (Message, error) {
2023-12-13 09:05:29 +03:00
config := v.SendConfig(sid, bot)
if config.Error != nil {
return Message{}, config.Error
2023-09-26 17:13:31 +03:00
}
msg, err := bot.api.Send(config.ToApi())
2023-12-13 09:05:29 +03:00
if err != nil {
return Message{}, err
}
v.SetMessage(msg)
return msg, nil
}
2024-01-17 16:39:52 +03:00
func (bot *Bot) Sendf(
sid SessionId, format string, v ...any,
) (Message, error){
msg := Messagef(format, v...)
2024-01-17 16:39:52 +03:00
return bot.Send(
sid,
&msg,
2024-01-17 16:39:52 +03:00
)
}
// Send to the session specified its ID raw chattable from the tgbotapi.
func (bot *Bot) SendRaw(
sid SessionId, v tgbotapi.Chattable,
) (*Message, error) {
msg, err := bot.api.Send(v)
if err != nil {
return nil, err
}
return &msg, nil
}
2023-09-30 09:55:45 +03:00
// Get session by its ID. Can be used for any scope
// including private, group and channel.
func (bot *Bot) GetSession(
2023-08-13 15:37:36 +03:00
sid SessionId,
) (*Session, bool) {
session, ok := bot.sessions[sid]
return session, ok
2023-08-13 15:37:36 +03:00
}
func (b *Bot) WithBehaviour(beh *Behaviour) *Bot {
b.behaviour = beh
b.sessions = make(SessionMap)
return b
}
func (b *Bot) WithSessions(sessions SessionMap) *Bot {
b.sessions = sessions
return b
}
2023-09-30 09:55:45 +03:00
/*func (b *Bot) WithGroupBehaviour(beh *GroupBehaviour) *Bot {
2023-08-13 15:37:36 +03:00
b.groupBehaviour = beh
b.groupSessions = make(GroupSessionMap)
return b
}
func (b *Bot) WithGroupSessions(sessions GroupSessionMap) *Bot {
b.groupSessions = sessions
return b
2023-09-30 09:55:45 +03:00
}*/
func (bot *Bot) DeleteCommands() {
//tgbotapi.NewBotCommandScopeAllPrivateChats(),
cfg := tgbotapi.NewDeleteMyCommands()
bot.api.Request(cfg)
2023-08-13 15:37:36 +03:00
}
// Setting the command on the user side.
func (bot *Bot) SetCommands(
scope tgbotapi.BotCommandScope,
2023-09-30 09:55:45 +03:00
cmdMap CommandMap,
) error {
// First the private commands.
names := []string{}
for name := range cmdMap {
names = append(names, string(name))
}
sort.Strings(names)
2023-09-30 09:55:45 +03:00
cmds := []*Command{}
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...,
)
_, err := bot.api.Request(cfg)
if err != nil {
return err
}
return nil
}
2023-07-09 01:28:59 +03:00
// Run the bot with the Behaviour.
func (bot *Bot) Run() error {
2023-09-30 09:55:45 +03:00
if bot.behaviour == nil {
2023-08-13 15:37:36 +03:00
return errors.New("no behaviour defined")
}
if bot.behaviour != nil && bot.behaviour.Root == nil {
return errors.New("the root widget is not set, cannot run")
}
uc := tgbotapi.NewUpdate(0)
2023-09-30 09:55:45 +03:00
uc.Timeout = 10
updates := bot.api.GetUpdatesChan(uc)
handles := make(map[string] chan Update)
2023-08-13 15:37:36 +03:00
if bot.behaviour != nil {
chn := make(chan Update)
2023-08-13 15:37:36 +03:00
handles["private"] = chn
go bot.handlePrivate(chn)
}
2023-08-12 14:35:33 +03:00
2023-09-30 09:55:45 +03:00
/*if bot.groupBehaviour != nil {
commanders := make(map[CommandName] BotCommander)
for k, v := range bot.groupBehaviour.Commands {
commanders[k] = v
}
bot.SetCommands(
tgbotapi.NewBotCommandScopeAllGroupChats(),
commanders,
)
2023-08-13 15:37:36 +03:00
chn := make(chan *Update)
handles["group"] = chn
handles["supergroup"] = chn
go bot.handleGroup(chn)
2023-09-30 09:55:45 +03:00
}*/
2023-08-13 15:37:36 +03:00
me, _ := bot.Api.GetMe()
bot.me = me
for up := range updates {
u := Update{
Update: up,
}
// Sometimes returns nil.
fromChat := u.FromChat()
if fromChat == nil {
continue
}
chn, ok := handles[fromChat.Type]
2023-08-13 15:37:36 +03:00
if !ok {
continue
2023-07-12 14:20:52 +03:00
}
2023-08-13 15:37:36 +03:00
chn <- u
2023-07-09 01:28:59 +03:00
}
2023-07-09 01:28:59 +03:00
return nil
}
2023-08-12 14:35:33 +03:00
// The function handles updates supposed for the private
// chat with the bot.
func (bot *Bot) handlePrivate(updates chan Update) {
2023-08-12 14:35:33 +03:00
var sid SessionId
2023-08-13 15:37:36 +03:00
for u := range updates {
sid = SessionId(u.FromChat().ID)
2023-09-26 17:13:31 +03:00
ctx, ctxOk := bot.contexts[sid]
if u.Message != nil && !ctxOk {
2023-09-26 17:13:31 +03:00
session, sessionOk := bot.sessions[sid]
if !sessionOk {
// Creating session if we have none.
session = bot.sessions.Add(sid, PrivateSessionScope)
2023-09-26 17:13:31 +03:00
}
session = bot.sessions[sid]
// Create context on any message
// if we have no one.
2023-09-26 17:13:31 +03:00
ctx = &context{
2023-09-07 15:45:38 +03:00
Bot: bot,
2023-09-26 17:13:31 +03:00
Session: session,
updates: NewUpdateChan(),
2023-08-12 14:35:33 +03:00
}
2023-09-26 17:13:31 +03:00
if !ctxOk {
bot.contexts[sid] = ctx
}
go Context{
session: session,
bot: bot,
Update: u,
2023-09-26 17:13:31 +03:00
input: ctx.updates,
}.serve()
ctx.session.updates.Send(u)
2023-09-26 17:13:31 +03:00
continue
2023-08-13 15:37:36 +03:00
}
2023-08-19 13:25:47 +03:00
2023-09-26 17:13:31 +03:00
if ctxOk {
ctx.updates.Send(u)
2023-08-12 14:35:33 +03:00
}
}
}
2023-09-30 09:55:45 +03:00
/*
2023-08-13 15:37:36 +03:00
func (bot *Bot) handleGroup(updates chan *Update) {
var sid SessionId
chans := make(map[SessionId]chan *Update)
for u := range updates {
sid = SessionId(u.FromChat().ID)
// If no session add new.
if _, ok := bot.groupSessions[sid]; !ok {
bot.groupSessions.Add(sid)
session := bot.groupSessions[sid]
ctx := &groupContext{
Bot: bot,
Session: session,
updates: make(chan *Update),
2023-08-13 15:37:36 +03:00
}
chn := make(chan *Update)
chans[sid] = chn
go ctx.handleUpdateChan(chn)
}
chn := chans[sid]
chn <- u
}
2023-08-12 14:35:33 +03:00
}
2023-09-30 09:55:45 +03:00
*/