Get User Input after keyboard inline keyboard selection Telegram Bot Python - python

I have an issue with my telegram bot. I wrote a bot in which the user needs to make a choice with an Inline Keyboard. If he selects 'Cerca un prodotto' the bot should wait for user input and print it. But if I start the bot then it prints the text 'FU' in update.message.reply_text('HELLO FU', reply_markup=reply_markup_main) row instead of printing user input. This is my code:
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
from telegram.ext import CommandHandler, CallbackQueryHandler, CallbackContext
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardMarkup
from telegram import Update
import telegram
def start(update, context):
kb_main = [
[InlineKeyboardButton("Cerca le migliori offerte", callback_data='trovaofferte')],
[InlineKeyboardButton("Cerca un prodotto", callback_data='cercaprod')]
]
reply_markup_main = InlineKeyboardMarkup(kb_main)
update.message.reply_text('HELLO FU', reply_markup=reply_markup_main)
def kb(update: Update, _: CallbackContext) -> None:
query = update.callback_query
query.answer()
#===> IF USER CHOICE IS 'cercaprod' THEN BOT SHOULD WAIT FOR USER INPUT<====
if query.data == 'cercaprod':
print(query.message.text)
def main():
update= Updater('1655760090:AAFKyOR-i7wLW1zIAH9-air0EtqIDQJrCwk', use_context=True)
disp=update.dispatcher
disp.add_handler(CommandHandler("start", start))
disp.add_handler(MessageHandler(Filters.text & ~Filters.command, kb))
update.dispatcher.add_handler(CallbackQueryHandler(kb))
update.start_polling()
update.idle()
if __name__=='__main__':
main()

A have a few remarks:
You should revoke your bot token with #Botfather. Otherwise anyone could use it, because you posted it
you use the callback kb in both a MessageHandler and a CallbackQueryHandler, but call update.callback_query.answer(). However, if the update is a message (in the MessageHandler case, update.callback_query will be None and you'll get an exception.
Now to the actual problem:
When the button is pressed, the only update you get is the CallbackQuery and query.message is the message that the button is attached to - in your case the message with text HELLO FU. If you want the user to enter some text after pressing the button, that will come in a new update. So you'd have to use a MessageHandler to catch that. I guess that's what you tried by setting up a MessageHandler with kb as callback, but as mentioned above that will only raise an error, which you currently won't see because you have no logging set up and no error handler.
For this kind of setup, I strongly recommend to use ConversationHandler, which is perfect for setups where multiple user inputs are needed. There's also a neat example here.

Related

I'm creating a Slackbot in Python and want to repeat the message until a reaction is added to that message. What am I doing wrong?

as the title states, I'm writing a Slack Bot in Python and using NGROK to host it locally. I'm not super experienced with decorators, and I can get the bot posting messages in slack, however I can't seem to handle two events at once. For example, I want to handle a message and have the message keep repeating in slack until a thumbs up reaction is added to that message. The issue is I cannot figure out how to handle an event while another event is still running, please see the following code:
rom slack import WebClient
import os
import time
from pathlib import Path
from dotenv import load_dotenv
from flask import Flask
from slackeventsapi import SlackEventAdapter
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)
app = Flask(__name__)
slack_event_adapter = SlackEventAdapter(
os.environ['SIGNING_SECRET'],'/slack/events',app)
client = WebClient(token=os.environ['SLACK_TOKEN'])
BOT_ID = client.api_call("auth.test")['user_id']
state = {}
#slack_event_adapter.on('message')
def handle_message(event_data):
message = event_data.get('event', {})
channel_id = message.get('channel')
user_id = message.get('user')
text = message.get('text')
messageid = message.get('ts')
state[messageid] = {"channel_id": channel_id, "user_id": user_id, "text": text}
if BOT_ID != user_id:
if text[0:12] == ":red_circle:":
time.sleep(5)
client.chat_postMessage(channel=channel_id, text=text)
if text[0:21] == ":large_yellow_circle:":
client.chat_postMessage(channel=channel_id, text="it's a yellow question!")
if text[0:14] == ":white_circle:":
client.chat_postMessage(channel=channel_id, text="it's a white question!")
#slack_event_adapter.on('reaction_added')
def reaction_added(event_data):
reaction = event_data.get('event',{})
emoji = reaction.get('reaction')
emoji_id = reaction.get('item',{}).get('ts')
emoji_channel_id = reaction.get('item',{}).get('channel')
client.chat_postMessage(channel=emoji_channel_id, text=emoji)
for message_id, message_data in state.items():
channel_id = message_data["channel_id"]
text = message_data["text"]
client.chat_postMessage(channel=channel_id, text=text)
print(message_id,message_data)
if __name__ == "__main__":
app.run(debug=True)
I can handle individual events, but I cannot handle them while another is running. Please help! :)
Flask is a synchronous web framework.
When it's running a view handler, it uses up a web worker thread. If you does something like time.sleep(...), that worker thread will still be occupied and unavailable to handle other requests until the sleep finishes.
There are a couple options you can do here.
You can use Bolt for Python, which is a Python Slack library that natively support asynchronous even processing. Instead of time.sleep(), you can do await asyncio.sleep(...), which returns the thread to the async loop, and allow the worker thread to process other events.
If you already have an existing slack application and don't want to rewrite your entire codebase to Bolt, then you'll need to handle the event processing yourself. You can do this by doing your work in an ThreadLoopExecutor, or by building your own async event Queue mechanism, or use Celery. Or if your slack bot has very low volume, you can probably just add more web workers, and hope for the best that you don't run out of workers.

Trouble deploying telegram bot on Render

I am trying to deploy this telegram bot on Render, but the deploy always fails (although on the first minutes after I receive the build successful log, it works perfectly).
However, minutes later the deploy fails and I keep getting this error log:
telebot.apihelper.ApiTelegramException: A request to the Telegram API was unsuccessful. Error code: 409. Description: Conflict: terminated by other getUpdates request; make sure that only one bot instance is running
Here is the "bot.py" script I coded. Suppressed some non important parts of the code. Also, all "src." modules were created by me for better code organization. There are no telebot methods used on those modules.
import pandas as pd
from datetime import datetime, date
import src.visualization.visualize as vz
import src.auxiliary.auxiliary as aux
import src.data.wrangling as w
import src.data.database as db
import os
import telebot
API = os.getenv("API_KEY")
bot = telebot.TeleBot(API)
#bot.message_handler(commands=["IBOVESPA", "SP500", "NASDAQ"])
def responder(mensagem):
# generic code and calculations suppressed
if condition:
# telebot method used
bot.send_photo(
mensagem.chat.id,
photo = photo
)
else:
# more generic code suppressed
# telebot method used
bot.send_message(
mensagem.chat.id,
aux.create_answer(assets)
)
# telebot method used
bot.send_photo(
mensagem.chat.id,
photo = photo
)
def verificar(mensagem):
return True
def calculating_msg(mensagem, date):
# telebot method used
bot.send_message(
mensagem.chat.id,
“generic answer”
)
#bot.message_handler(func=verificar)
def responder(mensagem):
text= (
“standard reply to any message”
)
# telebot method used
bot.reply_to(mensagem, text)
# telebot method used
bot.polling()
Already revoked an old api_key on telegram and used a fresh-generated one and nothing changes.
Edit: works perfectly running on local. Only receive this warning:
\src\visualization\visualize.py:34: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.
fig = plt.figure(figsize=(15, 8))
WARNING: QApplication was not created in the main() thread.

How to run Streamlit and Telegram Bot (python-telegram-bot) simultaneously?

I'm trying to run a Streamlit webpage and a python-telegram-bot instance together, because I need to call Telegram Bot functions from the Webpage.
Below is my code:
import streamlit as st
from telegram import Update
from telegram.ext import Updater, CallbackContext, MessageHandler, Filters
def echo(update: Update, context: CallbackContext) -> None:
update.effective_chat.send_message(text="Hi")
def run_website():
st.set_page_config(page_title="My Webpage", page_icon=":tada:", layout="wide")
def run_bot() -> None:
updater = Updater('5222690292:AAHpTsw3ynWX0WA-2WonGErslqmoFjLHkvA')
dispatcher = updater.dispatcher
# Add handlers
echo_handler = MessageHandler(Filters.all, echo)
dispatcher.add_handler(echo_handler)
# Start Bot
updater.start_polling()
updater.idle()
if __name__ == '__main__':
run_website()
run_bot()
When starting the script with streamlit run test.py, I run into the following error:
ValueError: signal only works in main thread of the main interpreter
I then tried removing updater.idle(), but got the following error:
telegram.error.Conflict: Conflict: terminated by other getUpdates request; make sure that only one bot instance is running
I also tried adding #st.cache to my run_bot() function to no avail (same error as above).

How do I edit a telegram message multiple times?

Hi I'm trying to make a telegram bot that edits its same message multiple times like BotFather does, but every time that I try it gives me this error:
telegram.error.BadRequest: Message is not modified: specified new message content and reply markup are exactly the same as a current content and reply markup of the message
Here is the code, I tried to make it as clear as possible.
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import *
from API import API_KEY
from bot_messages import *
updater = Updater(API_KEY, use_context=True)
dispatcher = updater.dispatcher
def start(update, context):
update.message.reply_text(WELCOME, reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Ciao", callback_data="ciao")]]))
def ciao(update, context):
update.callback_query.edit_message_text("Ciao", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Dona!", callback_data="donate")]]))
def donate(update, context):
update.callback_query.edit_message_text("Dona", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Dona", callback_data="dona")]]))
dispatcher.add_handler(CommandHandler("start", start))
dispatcher.add_handler(CommandHandler("restart", start))
dispatcher.add_handler(CallbackQueryHandler(ciao))
dispatcher.add_handler(CallbackQueryHandler(donate))
updater.start_polling()
updater.idle()
Also can you tell me the proper way of making inline query handlers? Because if it is giving me error it could perhaps mean that I'm not doing it quite right. Thanks in advance.
In your snippet only the first CallbackQueryHandler will ever handle updates - please see the docs of Dispatcher.add_handler for details on how dispatcher decides which handler gets to handle an update.
That's why your code is trying to update the message with the unchanged text and you get that error.
To fix that, you can e.g. use the pattern argument of CallbackQueryhandler.
Disclaimer: I'm currently the maintainer of python-telegram-bot.

Telegram Quiz Bot with pyTelegramBotAPI

Trying to build a Telegram Quiz Bot using pyTelegramBotAPI. I'm using sched to schedule the message Handler but i don't know how to stop the Message Handler and return to my Main Script which will scheudle the next Round.
Tryed to use timeout but it is not working!
My Code:
import telebot
import sched, time
def listen():
print("Send my your Answer")
#bot.message_handler(func=lambda message: True, content_types=['text'])
def command_default(m):
print(m.text)
bot.polling()
API_TOKEN = 'xxxx'
s = sched.scheduler(time.time, time.sleep)
bot = telebot.TeleBot(API_TOKEN)
s.enter(50, 1, listen)
s.run()
In this use case you have to use something called a Finite State Machine (FSM). You keep track of user states, such as one where the user is ready to send an answer.
This is already implemented in pyTelegramBotAPI, with the next_step_handler(). However, I suggest you instead create your own solution, as the one provided by the wrapper is quite buggy.
Here is an example (you can translate the page): https://groosha.gitbooks.io/telegram-bot-lessons/content/chapter11.html

Categories