I am currently working on a Telegram Bot using the python api. I am using this example here https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot.py. With that example I want the bot to have a timed response.
For example if the user does not respond within 30 sec send a "Are you still there message" or something and after 1 min end the conversation. The reason I want to implement something like this is because the conversation does not close if there is no response. It is in that state until the I end the script. Therefore the user can not send /start command to start all over. I was able to find this https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/conversationhandler.py where it states I can enable allow_reentry. I did and it sort of solves that issue where the user can start a new converation over and over using the command /start. But I would still like to have the conversation end after a set amount of time. To end a conversation I need to return ConversationHandler.END
I have tried a while loop counting down from 9 with a time.sleep of 2 each time. with it reading the response update.message.text but it only reads the command /start which means I can never advance in the script, unless I return it using return GENDER but I am not able to find a method in which I can tell when the user has chosen the gender to then return GENDER.
So how do I implement an timer based response? Thank You
from telegram import (ReplyKeyboardMarkup)
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
ConversationHandler)
import logging
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
GENDER, PHOTO, LOCATION, BIO = range(4)
def start(bot, update):
reply_keyboard = [['Boy', 'Girl', 'Other']]
bot.sendMessage(update.message.chat_id,
text='Hi! My name is Professor Bot. I will hold a conversation with you. '
'Send /cancel to stop talking to me.\n\n'
'Are you a boy or a girl?',
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
return GENDER
def gender(bot, update):
user = update.message.from_user
logger.info("Gender of %s: %s" % (user.first_name, update.message.text))
bot.sendMessage(update.message.chat_id,
text='I see! Please send me a photo of yourself, '
'so I know what you look like, or send /skip if you don\'t want to.')
return PHOTO
def photo(bot, update):
user = update.message.from_user
photo_file = bot.getFile(update.message.photo[-1].file_id)
photo_file.download('user_photo.jpg')
logger.info("Photo of %s: %s" % (user.first_name, 'user_photo.jpg'))
bot.sendMessage(update.message.chat_id, text='Gorgeous! Now, send me your location please, '
'or send /skip if you don\'t want to.')
return LOCATION
def skip_photo(bot, update):
user = update.message.from_user
logger.info("User %s did not send a photo." % user.first_name)
bot.sendMessage(update.message.chat_id, text='I bet you look great! Now, send me your '
'location please, or send /skip.')
return LOCATION
def location(bot, update):
user = update.message.from_user
user_location = update.message.location
logger.info("Location of %s: %f / %f"
% (user.first_name, user_location.latitude, user_location.longitude))
bot.sendMessage(update.message.chat_id, text='Maybe I can visit you sometime! '
'At last, tell me something about yourself.')
return BIO
def skip_location(bot, update):
user = update.message.from_user
logger.info("User %s did not send a location." % user.first_name)
bot.sendMessage(update.message.chat_id, text='You seem a bit paranoid! '
'At last, tell me something about yourself.')
return BIO
def bio(bot, update):
user = update.message.from_user
logger.info("Bio of %s: %s" % (user.first_name, update.message.text))
bot.sendMessage(update.message.chat_id,
text='Thank you! I hope we can talk again some day.')
return ConversationHandler.END
def cancel(bot, update):
user = update.message.from_user
logger.info("User %s canceled the conversation." % user.first_name)
bot.sendMessage(update.message.chat_id,
text='Bye! I hope we can talk again some day.')
return ConversationHandler.END
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
# Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
GENDER: [RegexHandler('^(Boy|Girl|Other)$', gender)],
PHOTO: [MessageHandler([Filters.photo], photo),
CommandHandler('skip', skip_photo)],
LOCATION: [MessageHandler([Filters.location], location),
CommandHandler('skip', skip_location)],
BIO: [MessageHandler([Filters.text], bio)]
},
fallbacks=[CommandHandler('cancel', cancel)],
allow_reentry=True
)
dp.add_handler(conv_handler)
# log all errors
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
You can use the JobQueue for that.
You can see an example here: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/timerbot.py
Make sure to initialize your handler with pass_job_queue=True (as shown in the above example).
Related
The logic is the following:
With the /start command the bot shows the Main Menu with buttons (each button represents a file the user wants to get access to);
When any button is pressed, the conversation starts where the bot asks for a gmail address;
The user sends their gmail address, the bot checks it, if the address format is correct then the bot grants the permission to view the file and posts the link to the chat.
I used these examples as my starting point:
https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot.py
https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/conversationbot2.py
My code is this one:
from telegram import (
Bot,
Update,
InlineKeyboardMarkup,
InlineKeyboardButton,
)
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
CallbackContext,
CallbackQueryHandler,
ConversationHandler,
)
def startCommand(update: Update, context: CallbackContext):
keyboardMarkup = InlineKeyboardMarkup(
[[InlineKeyboardButton('Share File 1', callback_data='sharingFile1')]]
)
update.message.reply_text(f'Howdy, {update.effective_user.first_name}.\nThis is the Main Menu.',
reply_markup=keyboardMarkup)
def convGetGMailAddr(update: Update, context: CallbackContext):
update.message.reply_text('Waiting for your gmail address.\n\nSend /end and I\'ll stop waiting.')
return convEmailAddr
def convMismatch(update: Update, context: CallbackContext):
text = f"""Sorry, I don't understand this gmail address.
Please, send me your gmail address again.\n\nSend /end and I\'ll stop waiting.
"""
update.message.reply_text(text)
return convEmailAddr
def convGiveLink(update: Update, context: CallbackContext):
link = 'https://docs.google.com/spreadsheets/d/1ZP1xZ0WaH8w2yaQTSx99gafNZWawQabcdVW5DSngavQ'
update.message.reply_text(f'Thank you! Here\'s your link to the shared file:\n{link}')
return ConversationHandler.END
def convEnd(update: Update, context: CallbackContext):
update.message.reply_text('I\'ve stopped waiting.\n\nSend /start to go to the Main Menu.')
return ConversationHandler.END
def sharingFileHandler(update: Update, context: CallbackContext):
if update.callback_query.data == 'sharingFile1':
update.callback_query.edit_message_text(
update.effective_message.text,
reply_markup=InlineKeyboardMarkup([])
)
conv_sharing = ConversationHandler(
entry_points=[MessageHandler(Filters.regex('.*[File 1]*.*'), convGetGMailAddr)],
states={
convEmailAddr: [
MessageHandler(~Filters.regex('.*#gmail.com$') & ~Filters.command, convMismatch),
MessageHandler(Filters.regex('.*#gmail.com$'), convGiveLink),
],
},
fallbacks=[CommandHandler('end', convEnd)],
)
disp.add_handler(conv_sharing)
bot.send_message(update.effective_chat.id, 'I\'ll share the File 1 with you.')
bot_token = 'abcd1234'
bot = Bot(bot_token)
updater = Updater(bot_token, use_context=True)
convEmailAddr = ''
disp = updater.dispatcher
disp.add_handler(CommandHandler('start', startCommand))
disp.add_handler(CallbackQueryHandler(sharingFileHandler))
updater.start_polling(drop_pending_updates=True)
updater.idle()
The issue is that the bot doesn't read it's own reply in the function sharingFileHandler to start the conversation handler. The entry point of the conversation is posting the string "File 1" and when I send something like "asdklhasdlkh file 1 asdaskldha" then everything works fine.
Another question is is it possible for the bot to listen to email addresses only inside of the conversation? Right now the function convGetGMailAddr starts at any moment.
Update 1 (2021-10-20)
Based on the CallMeStag's answer I changed my code.
Deleted the function convGetGMailAddr and modified the function sharingFileHandler:
def sharingFileHandler(update: Update, context: CallbackContext):
if update.callback_query.data == 'sharingFile1':
update.callback_query.edit_message_text(
update.effective_message.text,
reply_markup=InlineKeyboardMarkup([])
)
text = f"""I\'ll share the File 1 with you to your Google account.
Please, send me your gmail address.\n\nSend /end and I\'ll stop waiting."""
bot.send_message(update.effective_chat.id, text)
return convEmailAddr
bot_token = '1234abcd'
bot = Bot(bot_token)
updater = Updater(bot_token, use_context=True)
convEmailAddr = ''
disp = updater.dispatcher
disp.add_handler(CommandHandler('start', startCommand))
conv_sharing = ConversationHandler(
entry_points=[CallbackQueryHandler(sharingFileHandler)],
states={
convEmailAddr: [
MessageHandler(~Filters.regex('.*#gmail.com$') & ~Filters.command, convMismatch),
MessageHandler(Filters.regex('.*#gmail.com$'), convGiveLink),
],
},
fallbacks=[CommandHandler('end', convEnd)],
)
disp.add_handler(conv_sharing)
updater.start_polling(drop_pending_updates=True)
updater.idle()
Now my bot does exactly what I want and it stopped doing what I wanted it to stop doing. 🙂
Thank you, CallMeStag!
You're building a new conversationhandler & adding it to the dispatcher every time sharingFileHandler is called. that's surely not what you want. You should instead build it only once and add it to the dispatcher also only once right where you add the other handlers (at the very end of your snippet).
You should then make CallbackQueryHandler(sharingFileHandler) the entry point of that conversation. this will automatically solve your second problem.
Disclaimer: I'm currently the maintainer of python-telegram-bot.
I'm looking for a way for a bot to wait for a reply from a user after a command. For example, you first type "/ask", then the bot waits for a plain message (not a command) from the user and after the user replies is stores his/her reply in a variable
I'm sure this is quite simple, but all the tutorials I've seen are in Russian and the documentation for python-telegram-api is very chaotic and I'm not the most advanced
If I'm dumb, sorry, just please help a fellow beginner out
Okay, this was pointless. I thought you couldn't use arguments, but the post I read was 5 years old so... I'm stupid. I just used arguments instead, thanks for the help tho, really appreciate it
here's the code that takes user's input and stores it in the "store_user_input" variable.
import logging
from telegram.ext import Updater,
CommandHandler, MessageHandler, Filters,
CallbackContext
from telegram import Update
logging.basicConfig(
format='%(asctime)s - %(name)s - %
(levelname)s - %(message)s',
level=logging.INFO
)
logger = logging.getLogger(__name__)
# function to handle the /start command
def startcommand(update:Update, context:
CallbackContext) -> None:
first_name = update.message.chat.first_name
update.message.reply_text\
(
f'Welcome to the bot, {first_name},
what are you interested in?'
)
# function to handle and store user input
def text(update:Update, context:
CallbackContext) -> None:
text_received = update.message.text
store_user_input = text_received
# function to handle the /help command
def helpcommand(update:Update, context:
CallbackContext) -> None:
update.message.reply_text('Here is a list of
all available commands:\n '
'/start - start the
bot\n'
'/help - get all
available commands\n')
# function to handle errors occured in the
dispatcher
def errormsg(update:Update, context:
CallbackContext) -> None:
update.message.reply_text('An error
occured.')
# main function
def main():
# "bot_data is your .txt file with bot API token"
fo = open("bot_data")
fr = fo.read()
updater = Updater(fr)
dispatcher = updater.dispatcher
# add handlers for start and help commands
dispatcher.add_handler(CommandHandler("start", startcommand))
dispatcher.add_handler(CommandHandler("help", helpcommand))
# add an handler for normal text (not commands)
dispatcher.add_handler(MessageHandler(Filters.text, text))
# add an handler for errors
dispatcher.add_error_handler(errormsg)
# start bot
updater.start_polling()
# run the bot
updater.idle()
if __name__ == '__main__':
main()
You can check the value by adding:
print("store_user_input")
after store_user_input line.
I am trying to create a bot that receives a response from the user and asks again if needed. The problem is that after:
update.reply_text("Did you report all you working hour on freshdesk for this week?, ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
I can't get the new updates. The message text remains /start in the first print, and the second print doesn't work at all.
How can I get correctly the response from the user? Can it be an issue related to ReplyMarkup?
def check_the_week(bot, update):
agent_username = update.message.from_user['username']
parameters = {"username": agent_username}
url = "{}/weekly_hours/".format(API_URL)
report = get_request_forwarder(url=url, method="GET", parameters=parameters)["messages"]
reply_keyboard = [['YES', 'NO']]
bot.send_message(
chat_id=update.message.chat_id,
text=report,
reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)) # sends the total nr of hours
print update.message.text
update.reply_text("Did you report all you working hour on freshdesk for this week?",
ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
print update.message.text
if update.message.text == "YES":
update.message.reply_text(text="Are you sure?", reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))
# Asks confirmation
if update.message.text == "YES":
update.message.reply_text(text="Thank you for reporting your working hours in time!")
elif update.message.text == "NO":
update.message.reply_text(text="Please, check you time reports and add missing")
elif update.message.text == "NO":
update.message.reply_text(text="Please, check you time reports and add missing")
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater(TELEGRAM_TOKEN)
j = updater.job_queue
# # Get the dispatcher to register handlers
dp = updater.dispatcher
# # Start the Bot
dp.add_handler(CommandHandler("start", check_the_week))
# Send information to manager by command
updater.start_polling()
updater.idle()
print("bot started")
if __name__ == '__main__':
main()
Because you are using a CommandHandler, which is only used to capture a single command at a time.
What you want to do can be achieved by using a ConversationHandler. Please read the example scripts here and here. Also you can read more details on the handler here.
I'm working on my wetherstation which collects temperature and humidity every minute. To this project I would like to add a Telegram bot which sends me a message if the data record gets stopped.
I downloaded the Telegram bot library and made some tests with my Telegram bot.
For now, my bot is able to answer if I request, for example, the current humidity by sending /humidity to my bot.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.
First, a few handler functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and runs until we press Ctrl-C on the command line.
Usage:
Basic Echobot example, repeats messages.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import logging
import read_from_database
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
# Define a few command handlers. These usually take the two arguments bot and
# update. Error handlers also receive the raised TelegramError object in error.
def start(bot, update):
update.message.reply_text('Hi!')
def status(bot, update):
zeitstempel = read_from_database.get_timestamp()
update.message.reply_text('letzter Eintrag: {:%d.%m.%Y %H:%M}'.format(zeitste$
def temperatur(bot, update):
temperatur = read_from_database.get_temperature()
# update.message.reply_text('letzter Temperatureintrag: %s ' % temperatur + u'$
update.message.reply_text('letzter Temperatureintrag: %s C' % temperatur)
def feuchtigkeit(bot, update):
feuchtigkeit = read_from_database.get_humidity()
update.message.reply_text('letzter Feuchtigkeitseintrag: %s %%' % feuchtigkei$
def echo(bot, update):
update.message.reply_text(update.message.text)
def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater("TOKEN")
# Get the dispatcher to register handlers
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("status", status))
dp.add_handler(CommandHandler("feuchtigkeit", feuchtigkeit))
dp.add_handler(CommandHandler("temperatur", temperatur))
# on noncommand i.e message - echo the message on Telegram
#dp.add_handler(MessageHandler(Filters.text, echo))
# log all errors
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
But I have no idea how to send out an alarm when my python script runs into an exception during the record. Is there a possibility to send a message from my python script without sending a request to my Telegram bot?
use updater.bot.send_message to send message
I'd like to be able to retrieve a users Google Talk Status Message with Python, it's really hard to find documentation on how to use some of the libraries out there.
I don't have anything to hand with xmpp installed, but here's some old code I had lying around that might help you. You'll want to update the USERNAME/PASSWORD to your own values for test purposes.
Things to note: users logged in to Google Talk get a random presence string on their userid: that doesn't matter if you are trying to get the status of some other user, but if you want to write some code so want to communicate with yourself you need to distinguish the user logged in from GMail or a GTalk client from the test program. Hence the code searches through the userids.
Also, if you read the status immediately after logging in you probably won't get anything. There's a delay in the code because it takes a little while for the status to become available.
"""Send a single GTalk message to myself"""
import xmpp
import time
_SERVER = 'talk.google.com', 5223
USERNAME = 'someuser#gmail.com'
PASSWORD = 'whatever'
def sendMessage(tojid, text, username=USERNAME, password=PASSWORD):
jid = xmpp.protocol.JID(username)
client = xmpp.Client(jid.getDomain(), debug=[])
#self.client.RegisterHandler('message', self.message_cb)
if not client:
print 'Connection failed!'
return
con = client.connect(server=_SERVER)
print 'connected with', con
auth = client.auth(jid.getNode(), password, 'botty')
if not auth:
print 'Authentication failed!'
return
client.RegisterHandler('message', message_cb)
roster = client.getRoster()
client.sendInitPresence()
if '/' in tojid:
tail = tojid.split('/')[-1]
t = time.time() + 1
while time.time() < t:
client.Process(1)
time.sleep(0.1)
if [ res for res in roster.getResources(tojid) if res.startswith(tail) ]:
break
for res in roster.getResources(tojid):
if res.startswith(tail):
tojid = tojid.split('/', 1)[0] + '/' + res
print "sending to", tojid
id = client.send(xmpp.protocol.Message(tojid, text))
t = time.time() + 1
while time.time() < t:
client.Process(1)
time.sleep(0.1)
print "status", roster.getStatus(tojid)
print "show", roster.getShow(tojid)
print "resources", roster.getResources(tojid)
client.disconnect()
def message_cb(session, message):
print ">", message
sendMessage(USERNAME + '/Talk', "This is an automatically generated gtalk message: did you get it?")