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:
- don't start the bot
- send
/startcommand to the bot - send two
execmessages to the bot - 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.