tucnak / telebot

Telebot is a Telegram bot framework in Go.
MIT License
4.03k stars 469 forks source link

It is impossible to track commands for a third-party bot using telebot #607

Closed Dementor316 closed 1 year ago

Dementor316 commented 1 year ago

I am developing a moderator bot that tracks messages, commands, and calls of third-party bots in a specific group. I encountered a problem with implementing the functionality of removing messages that have commands to call third-party bots, for example: "/cmd@third_party_bot". Such messages should be deleted, and the user who sent such a message should receive a notification that calling other bots in the group is prohibited. The problem is that the message handler is not called if the message has a command for another bot. For example, if the moderator bot username is @botusername, then commands like "/start@third-party-bot" are discarded during parsing inside telebot. If I understand correctly, the root of my problem lies here: https://github.com/tucnak/telebot/blob/699e5dbbdd58d8ba59befb8c44c3b3d9ea4bfdc1/update.go#L45-L53 In line 50, it is checked whether the command is intended for the current bot or not. If not, then the parsing process is terminated in line 51, and no handlers like tb.OnText are called. Can you please tell me if it is possible to track messages with commands for third-party bots with the current implementation of update.go? I really need to be able to track such messages, but with the current implementation of telebot, I do not know how to do it without changing the main module for interacting with Telegram Bot API.

roreng commented 1 year ago

The command is also text. Use tele.OnText for manual processing of commands intended for other bots.

For example:

b.Handle(tele.OnText, func(c tele.Context) error {
    return c.Send("Hi!")
})

b.Handle("/hello", func(c tele.Context) error {
    return c.Send("Hello!")
})

Use /hello & /hello@your_bot_name & /hello@third-party-bot and see which handlers are invoked.

Dementor316 commented 1 year ago

@rorReddington, I am already using the tb.OnText message handler to process all text and commands manually. I have also tried defining handlers for specific commands, but the problem is that if a user's message contains exclusively a command for a third-party bot (e.g., /command@third-party-bot), no handler will be invoked. In this case, nothing happens, as the execution ends at the process of parsing the update from Telegram. Specifically, in these lines, a comparison is made to determine whether the command in the message relates to the current bot; if the command's username differs from the current bot's username, the execution doesn't even reach the invocation of any handler: https://github.com/tucnak/telebot/blob/699e5dbbdd58d8ba59befb8c44c3b3d9ea4bfdc1/update.go#L50-L52 I confirmed this by making a fork telebot and removing the given condition for checking the username. As a result, I can receive commands for any other bot in my bot, but this solution does not seem elegant. I wanted to ask the authors of telebot if they might be able to suggest a better solution to the problem, so that it would not be necessary to remove this condition, which in some cases may break the processing of specific commands when handlers like b.Handle("/command", handler) are used, rather than b.Handle(tb.OnText, handler).

roreng commented 1 year ago

@Dementor316 You are absolutely right. When offering you a possible solution, I did not delve into the internal logic of the framework in this matter. I only superficially checked it based on the examples you provided. In cases where /command@first-party-bot is called, tele.OnText works because the regular expression that parses commands does not include "-". In any case, this option is not suitable for you as the correct bot name example is either first_party_bot or firstPartyBot.

I'm afraid that in your case, the only possible solution may be to use your own fork or another library (framework). I apologize if my previous response misled you.

schnz commented 1 year ago

Hey,

I've never used this library and just randomly check the issues, but since no solution was offered (yet), I thought I'd give it a try.

First, let me make the remark, that the behavior you describe is intentional by the author of this library:

When handling commands, Telebot supports both direct (/command) and group-like syntax (/command@botname) and will never deliver messages addressed to some other bot, even if privacy mode is off. -- README.md

That being said, it looks like you can workaround that limitation by mimicking the ProcessUpdate routine, that you already identified in your opening post, in the Poller and apply a rewrite to the message Text before forwarding it to the bot.

Outline of the idea (not tested):

// Is supposed to return true or false based on whether the update should be forwarded or not.
// However, the update gets passed as pointer, which should also allow to mutate it prior to being forwarded
func RewriteThirdPartyCommands (u *tele.Update) bool {
    if u.Message != nil && u.Message.Text != "" {
        // Check if u.Message.Text matches tele.cmdRx and if so, rewrite the text message so that is forwarded afterwards.
        u.Message.Text = " " . u.Message.Text
    }

    return true
}

func main () {
    yourRegularPoller := tele.LongPoller{AllowedUpdates: []string{"message"}}
    rewriteMessagePoller := NewMiddlewarePoller(yourRegularPoller, RewriteThirdPartyCommands)

    bot := tele.NewBot(tele.Settings{
        Token: "...",
        Poller: &rewriteMessagePoller,
    })

    bot.Handle(tele.OnText, yourHandler, trimLeftSpacesMiddleware)
}
Dementor316 commented 1 year ago

@Coksnuss, thank you for your comment and for pointing out the information that I somehow missed from the README. Since the behavior of being unable to track commands for third-party bots is intentional, there is no more purpose in this issue. I also considered working around the inability to track commands for third-party bots by implementing my own update handler, but I decided to simply add a workaround to the existing processUpdate function in Telebot. Specifically, I added this line before the bot username check condition in my fork of the original repository:

botName = b.Me.Username

Now, regardless of which bot the command is intended for, the botName will default to the username of the moderator bot, allowing it to track and block any commands. Yes, this solution is not perfect, but it suits my needs very well, and I didn't have to abandon the processUpdate function and its benefits of update parsing. Thank you all for your comments and discussions. I am closing this issue.