123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- package tg
- import (
- "errors"
- "sort"
- tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
- )
- type Chat = tgbotapi.Chat
- 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.
- behaviour *Behaviour
- // Group bot behaviour.
- //groupBehaviour *GroupBehaviour
- // Bot behaviour in channels.
- //channelBehaviour *ChannelBehaviour
- sessions SessionMap
- //groupSessions GroupSessionMap
- }
- // 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
- }
- 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) {
- config := v.SendConfig(sid, bot)
- if config.Error != nil {
- return Message{}, config.Error
- }
- msg, err := bot.api.Send(config.ToApi())
- if err != nil {
- return Message{}, err
- }
- v.SetMessage(msg)
- return msg, nil
- }
- func (bot *Bot) Sendf(
- sid SessionId, format string, v ...any,
- ) (Message, error){
- msg := Messagef(format, v...)
- return bot.Send(
- sid,
- &msg,
- )
- }
- // 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
- }
- // Get session by its ID. Can be used for any scope
- // including private, group and channel.
- func (bot *Bot) GetSession(
- sid SessionId,
- ) (*Session, bool) {
- session, ok := bot.sessions[sid]
- return session, ok
- }
- 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
- }
- /*func (b *Bot) WithGroupBehaviour(beh *GroupBehaviour) *Bot {
- b.groupBehaviour = beh
- b.groupSessions = make(GroupSessionMap)
- return b
- }
- func (b *Bot) WithGroupSessions(sessions GroupSessionMap) *Bot {
- b.groupSessions = sessions
- return b
- }*/
- func (bot *Bot) DeleteCommands() {
- //tgbotapi.NewBotCommandScopeAllPrivateChats(),
- cfg := tgbotapi.NewDeleteMyCommands()
- bot.api.Request(cfg)
- }
- // Setting the command on the user side.
- func (bot *Bot) SetCommands(
- scope tgbotapi.BotCommandScope,
- cmdMap CommandMap,
- ) error {
- // First the private commands.
- names := []string{}
- for name := range cmdMap {
- names = append(names, string(name))
- }
- sort.Strings(names)
- 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
- }
- // Run the bot with the Behaviour.
- func (bot *Bot) Run() error {
- if bot.behaviour == nil {
- 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)
- uc.Timeout = 10
- updates := bot.api.GetUpdatesChan(uc)
- handles := make(map[string] chan Update)
- if bot.behaviour != nil {
- 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
- go bot.handleGroup(chn)
- }*/
- 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]
- if !ok {
- continue
- }
- chn <- u
- }
- return nil
- }
- // The function handles updates supposed for the private
- // chat with the bot.
- func (bot *Bot) handlePrivate(updates chan Update) {
- var sid SessionId
- for u := range updates {
- sid = SessionId(u.FromChat().ID)
- ctx, ctxOk := bot.contexts[sid]
- if u.Message != nil && !ctxOk {
- session, sessionOk := bot.sessions[sid]
- if !sessionOk {
- // Creating session if we have none.
- session = bot.sessions.Add(sid, PrivateSessionScope)
- }
- session = bot.sessions[sid]
- // Create context on any message
- // if we have no one.
- ctx = &context{
- Bot: bot,
- Session: session,
- updates: NewUpdateChan(),
- }
- if !ctxOk {
- bot.contexts[sid] = ctx
- }
- go Context{
- session: session,
- bot: bot,
- Update: u,
- input: ctx.updates,
- }.serve()
- ctx.session.updates.Send(u)
- continue
- }
- if ctxOk {
- ctx.updates.Send(u)
- }
- }
- }
- /*
- 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),
- }
- chn := make(chan *Update)
- chans[sid] = chn
- go ctx.handleUpdateChan(chn)
- }
- chn := chans[sid]
- chn <- u
- }
- }
- */
|