yagop / node-telegram-bot-api

Telegram Bot API for NodeJS
MIT License
8.45k stars 1.53k forks source link

User chat receives effects of others chat messages #856

Closed robertokbr closed 3 years ago

robertokbr commented 3 years ago

Hello, community, I'm here to ask about something that is happening with my project in both environment, production, and local one:

I have a bot set up in an express server following the example of the documentation:

  const TOKEN = process.env.BOT_TOKEN as string;
  const PORT = process.env.PORT || 3333;
  const URL = 'https://<url>herokuapp.com';

  const telegramBot = new TelegramBot(TOKEN);

  telegramBot.setWebHook(`${URL}/bot${TOKEN}`);

  const app = express();

  app.use(cors());
  app.use(express.json());
  app.use(routes);

  app.get('/', async (_, response) => {
    return response.json({
      connected: true,
    });
  });

  app.post(`/bot${TOKEN}`, (request, response) => {
    telegramBot.processUpdate(request.body);
    response.sendStatus(200);
  });

  app.listen(PORT, () => {
    console.log(`Server Connected: ${PORT}`);
  });

  botCommands(telegramBot);

Everything works well in the application, but when the bot starts a listener like a bellow one, the bot function starts if other users in another telegram accounts send a message

  this.telegramBot.once('text', async response => {
        if (response  === 'language_itention') {
          let language = response.text!.toLowerCase();

        }
   })

That's the version I'm using: node-telegram-bot-api: "^0.51.0", Node: 14.15.4

Did anyone had the same behavior?

kamikazechaser commented 3 years ago

bot starts a listener like a bellow one, the bot function starts if other users in another telegram accounts send a message

I don't understand this part. Wouldn't EventEmitter detach the listener if you use once? could that be the issue?

robertokbr commented 3 years ago

bot starts a listener like a bellow one, the bot function starts if other users in another telegram accounts send a message

I don't understand this part. Wouldn't EventEmitter detach the listener if you use once? could that be the issue?

The users of my bot have been related that the bot is listening to all chat at the same time. Example: You start to use my bot at your home, and the bot will ask you for your name, if I start to use the bot at the same time, It will crash your usability, because the bot is running only in one chat by the time. After a search, I was thinking it is a problem in the development environment, but after setting up in the Heroku with express in the config above, still the problem

kamikazechaser commented 3 years ago

Could you post the source code of botCommands module.

robertokbr commented 3 years ago

Could you post the source code of botCommands module.

Off course! Thanks for the help! The botCommands return several functions, so I will post one of those:


    class BotTranslateService {
          ...
      }

      private async changeLanguage(messageId: number) {
          ...
      }

      private async getAudioText({
          ...
      }: IGetAudioText): Promise<string> {
        const fileLink = await this.telegramBot.getFileLink(message.voice!.file_id);
         ...
      }

      private async sendTranslate({
        parsedText,
        outputLanguageCode,
        message,
      }: ISendTranslate): Promise<string> {
        const translatedText =   ...
        const translatedAudioPath = ...

        await Promise.all([
          this.telegramBot.sendMessage(message.chat.id, translatedText),
          this.telegramBot.sendVoice(message.chat.id, translatedAudioPath, {}),
        ]);

        return translatedText;
      }

      public async execute({
        inputLanguageCode,
        outputLanguageCode,
      }: IExecute): Promise<string | void> {
        this.languageCode = outputLanguageCode;

        return new Promise(resolve => {
          this.telegramBot.once('message', async message => {
            if (message.voice) {
              return this.getAudioText({
                inputLanguageCode,
                outputLanguageCode,
                message,
              }).then(resp => resolve(resp));
            }

            const commands = [
              '/start',
              '/traduzir',
              '/pronuncia',
              '/inicio',
            ].includes(message.text!.toLowerCase());

            const alterLanguage = ['/alterar'].includes(
              message.text!.toLowerCase(),
            );

            if (commands) return resolve();

            if (alterLanguage) {
              return this.changeLanguage(message.chat.id);
            }

            return this.sendTranslate({
              parsedText: message.text!,
              outputLanguageCode: this.languageCode,
              message,
            }).then(resp => resolve(resp));
          });
        });
      }
    }

I always use promises to avoid the function return until the bot gets the user data, so ok since here everything goes well, but this "once message" gets for some reason the message of other users

kamikazechaser commented 3 years ago

If your bot is being used in groups, you should use message.from.id. It's much safer. I don't know how you are calling BotTranslateService.execute. If I am going to assume that you wait from some other event (say callback_query from a keyboard) then callingBotTranslateService.execute. If its such a case, your method will wait for the next message from anyone !. So say I was typing /alterar but someone else sent a /start then the promise will be resolved and I will be left in a hanging state because emitter.once detaches the listener after receiving 1 message.

You need some sort of state management and better flow. I don't know what calls BotTranslateService.execute, but I prefer persistent listeners using bot.onText

robertokbr commented 3 years ago

Thanks for your time! II will try to implement message.from.id in the application as a whole, but my problem is not just about groups, the listeners listen to messages from anyone regardless of the chat id, and I don't use on text, because the global scope in this kind of listener always breaks my application.