Send chain of messages to multiple users using pyTelegramBotApi

Viewed 467

I have to send chain of messages to multiple users. Some of them have buttons that should freeze message sending (kind of confirmation):

>>> bot: 'Hello'
>>> bot: 'My name is %bot name%'
>>> bot: 'How are you?'
<<< button: 'Fine'  # <-- this is button. Any message should not to be sent before the user clicks `Fine` button
>>> bot: 'Can we continue?'  # <-- this message shows only after user clicks `Fine` button
<<< button: 'Yes'  # <-- this is button. Any message should not to be sent before the user clicks `Yes` button
>>> bot: 'That\'s great!'  # <-- this message shows only after user clicks `Yes` button
<<< button: 'Of course'  # <-- this is button. Any message should not to be sent before the user clicks `Of course` button
>>> bot: 'See you next time'  # <-- this message shows only after user clicks `Yes` button

I have lot of similar pipelines with multiple buttons and I what to do function/class that could do it.

Minimal working example:

import time
from dataclasses import dataclass
from typing import Any, Dict, List

import telebot

token = '%TELEGRAM_BOTTOKEN%'

bot = telebot.TeleBot(token)


users: List[int] = []


class MessageTypes:
    INFO_MESSAGE = 0
    BASIC_MESSAGE_WITH_BUTTON = 1


@dataclass
class Message:
    text: str
    message_type: int
    params: Dict[str, Any]


@bot.message_handler(commands=['start'])
def _start(message: telebot.types.Message):
    users.append(message.chat.id)


@bot.message_handler(func=lambda x: 'exec' in x.text)
def _send_message(message: telebot.types.Message):
    for i, user in enumerate(users):
        try:
            _send_message_to_user(user)
        except telebot.apihelper.ApiTelegramException as telegramEx:
            print(f'chat_id:{user}\t{telegramEx}')


def _send_message_to_user(user_id: int):
    send_next = True

    @bot.callback_query_handler(func=lambda q: q.data == 'messages')
    def __clear_reply_markup(query: telebot.types.CallbackQuery):
        bot.edit_message_reply_markup(chat_id=query.message.chat.id,
                                      message_id=query.message.id,
                                      reply_markup=None)
        nonlocal send_next
        send_next = True

    def __send_message(msg):
        if msg.message_type == MessageTypes.INFO_MESSAGE:
            return bot.send_message(user_id,
                                    msg.text,
                                    parse_mode='HTML')
        elif msg.message_type == MessageTypes.BASIC_MESSAGE_WITH_BUTTON:
            kb = telebot.types.InlineKeyboardMarkup()
            kb.add(telebot.types.InlineKeyboardButton(text=msg.params['buttons'][0]['text'],
                                                      callback_data='messages'))

            nonlocal send_next
            send_next = False

            return bot.send_message(chat_id=user_id,
                                    text=msg.text,
                                    parse_mode='HTML',
                                    reply_markup=kb)

    def _message_generator():
        while i < len(messages):
            msg = yield
            time.sleep(1.25)

            __send_message(msg)

    messages = [
        Message('Hello', 0, {}),
        Message('My name is %bot name%', 0, {}),
        Message('How are you?', 1, {'buttons': [{'text': 'Fine'}]}),
        Message('Can we continue?', 1, {'buttons': [{'text': 'Yes'}]}),
        Message('That\'s great!', 1, {'buttons': [{'text': 'Of course'}]}),
        Message('See you next time', 0, {}),
    ]

    i = 0
    mg = _message_generator()
    next(mg)
    while i < len(messages):
        if send_next:
            mg.send(messages[i])
            i += 1
            if i == len(messages):
                mg.close()


bot.polling()

The problem occures when more than one users attaches to the bot. First user gets full pipeline but second — doesn't get any message.

Easiest way to reproduce:

  1. don't start the bot
  2. send /start command to the bot
  3. send two exec messages to the bot
  4. start the bot

You will get two Hello, My name is %bot name%, How are you? messages. If you click any button Fine (first or second) you'll see Loading... message in the top of the chat and will not get any response.

But if you send /start command and single exec message everything will work fine.

Looks like I'm on right way with generators but can't figure out how to avoid this behaviour.

0 Answers
Related