I am doing a telegram bot, I have a merger that is to divide that does not work but to add if it does, does anyone know why?
import logging
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
def start(update, context):
update.message.reply_text('Hola!')
def help(update, context):
update.message.reply_text('Help!')
def sumar(update, context):
try:
numero1 = int(context.args[0])
numero2 = int(context.args[1])
suma = numero1 + numero2
update.message.reply_text('La suma es '+str(suma))
except (IndexError, ValueError):
update.message.reply_text('Por favor utiliza dos numeros')
def dividir(update, context):
try:
numero1 = int(context.args[0])
numero2 = int(context.args[1])
div= numero1 / numero2
update.message.reply_text('La division da '+str(div))
except (IndexError, ValueError):
update.message.reply_text('Por favor utiliza dos numeros')
def echo(update, context):
"""Echo the user message."""
update.message.reply_text(update.message.text)
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
"""Start the bot."""
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("1225696978:AAFsJYex51HMRbKL814tLJJPczJMu3nLlYY", use_context=True)
# Get the dispatcher to register handlers
botm3 = updater.dispatcher
# on different commands - answer in Telegram
botm3.add_handler(CommandHandler("start", start))
botm3.add_handler(CommandHandler("help", help))
botm3.add_handler(CommandHandler("Sumar", sumar))
botm3.add_handler(CommandHandler("Division", dividir))
# on noncommand i.e message - echo the message on Telegram
botm3.add_handler(MessageHandler(Filters.text, echo))
# log all errors
botm3.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()
I have two functions for the bot, one works the other does not and they are the same structure but it does not take effect.
I leave you my token so you can do the tests if you want the name of the bot is:
# moha_m03_1bot
I think the name of the command is confusing:
'/Sumar 2 2' -> La suma es 4
'/Division 4 2' -> La division da 2.0
'/Dividir 2 2' -> doesnt match any command
The commands are called Sumar and Division, maybe you meant them to be called Sumar and Dividir
it is 'division' not 'dividir' don't try the function name in telegram try 'division'
botm3.add_handler(CommandHandler("Division", dividir))
It is working perfectly for me
Related
From time to time my telegram bot seems to ignore messages. Unfortunately it never happens to me but to other users and only when they are asked to upload a photo or pdf. Sometimes the corresponding handler is not called. The problem persists even when the MessageHandler has no filters at all. I am using python-telegram-bot v13.7.
Please find below a minimal sample. It should print "receiving something" whenever the handler is called but sometimes it doesn't.
Is there anything I can do?
EDIT: modified the sample to be a MWE (you have to provide a valid telegram bot ID). Most of the time it works just fine but sometimes it will fail and not print "receiving something" although the user uploaded some document (mostly images).
from telegram import Update
from telegram.ext import (
Updater,
CommandHandler,
MessageHandler,
Filters,
ConversationHandler,
CallbackContext,
)
UPLOAD = 0
def welcome(update: Update, context: CallbackContext) -> int:
update.message.reply_text('Available commands: /upload')
return ConversationHandler.END
def upload(update: Update, context: CallbackContext) -> int:
update.message.reply_text('Please upload document.')
return UPLOAD
def receive(update: Update, context: CallbackContext) -> int:
print('receiving something...')
update.message.reply_text('Thank you.')
return ConversationHandler.END
def cancel(*args) -> int:
print('cancelled')
return welcome(*args)
def handle(*args, **options):
updater = Updater(" ...... ")
dispatcher = updater.dispatcher
conv_handler = ConversationHandler(
entry_points=[CommandHandler('upload', upload),
MessageHandler(Filters.text, welcome)
],
states={
# UPLOAD: [CommandHandler('cancel', cancel), MessageHandler(Filters.photo | Filters.document.pdf, receive)],
UPLOAD: [CommandHandler('cancel', cancel), MessageHandler(Filters.all, receive)],
},
fallbacks=[CommandHandler('cancel', cancel)],
conversation_timeout=60
)
dispatcher.add_handler(conv_handler)
updater.start_polling()
updater.idle()
handle()
One situation where such a behaviour can occure is a timeout. You have defined a timeout of 60 seconds and if the time is over, the bot silently does nothing and can not react to your uploaded file, because it's not anymore in the correct state for that. If this is you problem you can easily check this by defining a function triggering in the timeout-state.
I changed 2 things of your code:
add a timeout function:
def timeout(*args):
print("timeout")
add function to timeout state (ConversationHandler.TIMEOUT)
...
conv_handler = ConversationHandler(
entry_points=[CommandHandler('upload', upload),
MessageHandler(Filters.text, welcome)],
states={
UPLOAD: [CommandHandler('cancel', cancel), MessageHandler(Filters.all, receive)],
ConversationHandler.TIMEOUT: [MessageHandler(Filters.all, timeout)]
},
fallbacks=[CommandHandler('cancel', cancel)],
conversation_timeout=2,
)
...
(3. Reduce timeout for easier testing :))
From the documentation of conversation_timeout:
When this handler is inactive more than this timeout (in seconds), it will be automatically ended. If this value is 0 or None (default), there will be no timeout. The last received update and the corresponding context will be handled by ALL the handler’s who’s check_update method returns True that are in the state ConversationHandler.TIMEOUT.
A second situation where a bot CAN ignore messages is, when multiple users use the bot simultaneously. As far as i know CAN this lead to a situation where a user "hijacks" anothers state, so that the other users isn't anymore in the state of "uploading", so that this users can not upload a file until he "starts" the upload state.
(I am not 100% shure about this, but you should give it a try)
My bot is a 'nightmode' bot that starts at 10pm and ends at 7am, it will delete the messages in between those. I'm trying to delete the previous nightmode message sent from the bot but unsure how to go about this, I thought about storing the previous message update and then adding 1 to it and storing that, but I'm not sure how to go about this if the bot is running in multiple groups. Here's my code:
import logging
from telegram import Update, ForceReply
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackContext, Handler
import datetime as dtm
import pytz
night_hours = ['22','23','00','01','02','03','04','05','06']
allowed_groups = ['GROUP ID 1','GROUP ID 2','GROUP ID 3']
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def start(context: CallbackContext):
morning_time = dtm.time(hour=7, minute=0, tzinfo=pytz.timezone('Europe/London'))
evening_time = dtm.time(hour=22, minute=0, tzinfo=pytz.timezone('Europe/London'))
context.job_queue.run_daily(morning_message, morning_time)
context.job_queue.run_daily(night_message, evening_time)
def log_channel(context: CallbackContext,name,chat_name,message):
deleted_message = ("Message Deleted from *" +name+ "* in the group *'" +chat_name+ "'*: " +message)
context.bot.send_message(chat_id="LOG CHANNEL ID", text=deleted_message,
disable_notification=True, parse_mode="MARKDOWNV2")
print(deleted_message)
def morning_message(context: CallbackContext):
context.bot.send_message(chat_id="TEST GROUP ID",text="*NIGHT MODE END:*\n\nMessage sending enabled until 10PM",
disable_notification=True,parse_mode="MARKDOWNV2")
def night_message(context: CallbackContext):
context.bot.send_message(chat_id="TEST GROUP ID", text="*NIGHT MODE START:*\n\nMessage sending disabled until 7AM",
disable_notification=True,parse_mode="MARKDOWNV2")
def during_night_mode(update: Update,context: CallbackContext):
UK = pytz.timezone('Europe/London')
uk_time = dtm.datetime.now(UK).strftime('%H:%M:%S')
hour = dtm.datetime.now(UK).strftime('%H')
chat_id = update.message.chat.id
chat_name = update.message.chat.title
message_id = update.message.message_id
print(chat_id)
print(message_id)
incoming_text = str(update.message.text)
incoming_entities = update.message.entities #images, videos, files etc... probably not gonna use.
message_sender = str(update.message.from_user.first_name)
print(hour)
if str(hour) in night_hours and str(chat_id) in allowed_groups: # and Enabled == True
print("working")
context.bot.delete_message(chat_id,message_id)
log_channel(context,message_sender,chat_name,incoming_text)
def main() -> None:
"""Start the bot."""
updater = Updater("BOT KEY")
dp = updater.dispatcher
#dp.add_handler(CommandHandler("start", start))#, pass_job_queue=True))
#dp.add_handler(CommandHandler(start))
dp.add_handler(MessageHandler(Filters.all,during_night_mode))
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
start(dp)
updater.idle()
if __name__ == '__main__':
main()
Thanks
EDIT 1:
Basically, i only want 1 instance of the night mode message in the chat at all times. So for example, when the morning message runs, it will delete the previous night time message so it does not clutter the chat with the messages.
Documentation for send_message() shows
Returns: On success, the sent message is returned.
Return type: telegram.Message
So you can run
msg = context.bot.send_message(...)
to get all information about sent Message - message_id, chat, etc. - and save all (ie. using pickle) or only needed values (ie. using text file, json, etc.) or keep on some list (if you run code all time) and later you can use it to delete message(s).
from coinbase.wallet.client import Client
from telegram import ParseMode
from telegram.ext import CommandHandler, Defaults, Updater
COINBASE_KEY = 'xxxxxxxxxxxx'
COINBASE_SECRET = 'xxxxxxxxxxxx'
TELEGRAM_TOKEN = 'xxxxxxxxxxxx'
coinbase_client = Client(COINBASE_KEY, COINBASE_SECRET)
#if __name__ == '__main__':
updater = Updater(token=TELEGRAM_TOKEN, defaults=Defaults(parse_mode=ParseMode.HTML))
dispatcher = updater.dispatcher
dispatcher.add_handler('start', startCommand) # Accessed via /start
dispatcher.add_handler('alert', priceAlert) # Accessed via /alert
updater.start_polling() # Start the bot
updater.idle() # Wait for the script to be stopped, this will stop the bot
def startCommand(update, context):
context.bot.send_message(chat_id=update.effective_chat.id, text='Hello there!')
def priceAlert(update, context):
if len(context.args) > 2:
crypto = context.args[0].upper()
sign = context.args[1]
price = context.args[2]
context.job_queue.run_repeating(priceAlertCallback, interval=15, first=15, context=[crypto, sign, price, update.message.chat_id])
response = f"⏳ I will send you a message when the price of {crypto} reaches £{price}, \n"
response += f"the current price of {crypto} is £{coinbase_client.get_spot_price(currency_pair=crypto + '-GBP')['amount']}"
else:
response = '⚠️ Please provide a crypto code and a price value: \n<i>/price_alert {crypto code} {> / <} {price}</i>'
context.bot.send_message(chat_id=update.effective_chat.id, text=response)
def priceAlertCallback(context):
crypto = context.job.context[0]
sign = context.job.context[1]
price = context.job.context[2]
chat_id = context.job.context[3]
send = False
spot_price = coinbase_client.get_spot_price(currency_pair=crypto + '-GBP')['amount']
if sign == '<':
if float(price) >= float(spot_price):
send = True
else:
if float(price) <= float(spot_price):
send = True
if send:
response = f'👋 {crypto} has surpassed £{price} and has just reached <b>£{spot_price}</b>!'
context.job.schedule_removal()
context.bot.send_message(chat_id=chat_id, text=response)
enter image description here
I get this error of the code above, also I have already tried changing the position of the def but, it also shows error, How to solve this?
It is the code for telegram bot and also this keeps on showing me NameError, I have already added python3 and pip, but still not solved
Python reads files top to bottom. So when you call dispatcher.add_handler('start', startCommand), the function startCommand is not yet known. Move the part
updater = Updater(token=TELEGRAM_TOKEN, defaults=Defaults(parse_mode=ParseMode.HTML))
dispatcher = updater.dispatcher
dispatcher.add_handler('start', startCommand) # Accessed via /start
dispatcher.add_handler('alert', priceAlert) # Accessed via /alert
updater.start_polling() # Start the bot
updater.idle() # Wait for the script to be stopped, this will stop the bot
below the callback definitions.
Apart from that, add_handler needs a Handler as argument, in your case something like add_handler(CommandHanlder('start', startCommand). Please see PTB tutorial as well as the examples.
Disclaimer: I'm the current maintainer of the python-telegram-bot library.
Try
dispatcher.add_handler('start', startCommand()) # Accessed via /start
dispatcher.add_handler('alert', priceAlert()) # Accessed via /alert
You will also need to add the two arguments required by both functions.
dispatcher.add_handler('start', startCommand(update, context))
dispatcher.add_handler('alert', startCommand(update, context))
I'm not exactly sure what data the two functions take in but I'm going to guess that it is whatever the bot is returning.
I have a custom code that does its routine and I want to send a message to myself in Telegram if something goes wrong. In my case I use python-telegram-bot library along with apscheduler and its listeners, where certain events could be catched.
I came up with such working code, but my question is: is it possible to make it better, namely without using global variable? This was done to overcome the problem that listeners do not accept arguments needed for bot to send a message.
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from telegram.ext import Updater, CommandHandler
import copy
import my_custom_library
saved_update = None
def my_listener(event): # not related with bot
if event.exception:
if saved_update is not None:
alert(saved_update, 'Scheduler threw event.exception.') # should have bot related args
else:
record = event.retval # get returned value from my_custom_library.repetitive_function
try:
processed_record = my_custom_library.my_unsafe_business_logic(record) # something might go wrong here
my_custom_library.add_to_db(processed_record) # and here
except Exception as e:
if saved_update is not None:
alert(saved_update, e) # should have bot related args
def start(update, context):
global saved_update
saved_update = copy.deepcopy(update) # this is what I don't like
update.message.reply_text('You have subscribed for notifications.')
def alert(update, reason):
update.message.reply_text('Something went wrong: {}'.format(reason))
def main():
scheduler = BackgroundScheduler()
scheduler.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
scheduler.add_job(my_custom_library.repetitive_function, args=(my_args,), trigger='interval', minutes=1)
scheduler.start()
# bot
updater = Updater(TOKEN, use_context=True)
dp = updater.dispatcher
dp.add_handler(CommandHandler("start", callback=start))
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
The Telegram Bot API is fairly simple, you just ned to send an HTTP GET Request to this URL:
https://api.telegram.org/bot_token_/sendMessage?chat_id=123&text=Hello%20World!
Just create a bot with Botfather and send the Bot a message.
With the specified Token from Botfather and this URL:
https://api.telegram.org/bot_token_/getUpdates
You can get the messages which were sent to the Bot and the chat_id.
The simplest way would be to use the requests module and send the output of the updater to the first URL as the text parameter.
In my scripts I often use callbacks. It's a clean solution that provides separation between the two scripts. The send_message function can accept kwargs, so you can essentially create a dictionary, and update it when sending the message. The first part (channel ID) is something you know on the bot-side, and the second part (the text itself) is something you know one the third-party side.
In your third-party, provide a set_cb function that accepts a callback and a dictionary. Like so:
def set_cb(self, callback, params):
self.callback = callback
self.callback_params = params
In your bot script, set the callback before updater.idle()
# Start the Bot
updater = Updater("TOKEN")
updater.start_polling()
# Set the callback
r.set_cb(updater.bot.send_message, {"chat_id": 123456})
Then, in your third-party, once you want to send a message, simply add the message text call the following:
self.callback_params.update({"text": text})
self.callback(**self.callback_params) # converts the dict to kwargs
I'm making a Telegram bot using python-telegram-bot, and I need some way to receive voice messages. For that, I need to download them, and to do that, I have to get their file_ids. However, the MessageHandler handles... well, messages, and Handler gives me a NotImplementedError. Is there a way to get the file_id?
I know this question is old but I was facing a problem with this in the latest version (12+)
So it appears that the bot- pass_user_data in the callback function is deprecated and from now on you should use context based callbacks.
CallbackContext is an object that contains all the extra context
information regarding an Update, error or Job.
to the new style using CallbackContext:
def voice_handler(update: Update, context: CallbackContext):
file = context.bot.getFile(update.message.audio.file_id)
file.download('./voice.ogg')
You can read more in the Transition-guide-to-Version-12.0
The easiest way to download voice messages is to register a MessageHandler with a voice filter. The Docs provide more information on Filters and the voice module.
import telegram
from telegram.ext import Updater
def voice_handler(bot, update):
file = bot.getFile(update.message.voice.file_id)
print ("file_id: " + str(update.message.voice.file_id))
file.download('voice.ogg')
updater = Updater(token='TOKEN')
dispatcher = updater.dispatcher
dispatcher.add_handler(MessageHandler(Filters.voice, voice_handler))
In version 13+, you need to use update.message.voice.file_id instead of update.message.audio.file_id. So the code will be:
def voice_handler(update: Update, context: CallbackContext):
file = context.bot.getFile(update.message.voice.file_id)
file.download('./voice.ogg')
I'll show you an example with a photo file, but it works for any file (you'll just need to change the parameters)
from telegram.ext import Updater, CommandHandler
from telegram.ext.callbackcontext import CallbackContext
from telegram.update import Update
def start (update: Update, context: CallbackContext):
# getting chat_id:
chatID = update.effective_chat.id
# sending the photo to discover its file_id:
photo1 = context.bot.send_photo(chat_id=chatID, photo=open('photo1.jpg','rb'))
photo1_fileID = photo1.photo[-1].file_id
context.bot.send_message(chat_id=update.effective_chat.id, text=('file_id photo1.jpg = ' + photo1_fileID))
def main():
updater = Updater(token='TOKEN', use_context=True)
dispatcher = updater.dispatcher
dispatcher.add_handler(CommandHandler('start', start))
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()