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
Related
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.
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.
I try to start pyrogram client with loop.create_task(app.start()) but get an error TypeError: a coroutine was expected, got <pyrogram.client.Client object at 0x7f8bb7580520>, how can I fix it? It worked fine year ago but now it doesnt
import asyncio
from pyrogram import Client
loop = asyncio.get_event_loop()
app = Client(
"aaaaa",
bot_token="token",
api_id=12,
api_hash="a"
)
loop.create_task(app.start())
Pyrogram Client already has an event loop, you don't need to create again. If you follow the documentation it's clear how to run the bot.
Also you can use the Pyrostarter to create a bot project.
I'm writing a Telegram bot (with python-telegram-bot) that based on a command, cyclically sends messages to the user every hour.
I want to start/stop this using bot commands, adding command handlers like /start_cycle and /stop_cycle. To clarify, this is what I have in mind:
def start_cycle()
# start in some way send_hourly_message()
def stop_cycle()
# stop in some way send_hourly_message()
def main():
"""Entrypoint of the bot"""
# Create updater and get dispatcher
updater = Updater(...)
dp = updater.dispatcher
# Add command handlers
dp.add_handler(CommandHandler("start_cycle", start_cycle))
dp.add_handler(CommandHandler("stop_cycle", stop_cycle))
# Start the bot until interrupt
updater.start_polling(timeout=3)
updater.idle()
The thing that puzzles me is that for how the Telegram library is conceived, there is already an event-based logic, started by updater.start_polling() and updater.idle(). I didn't find any documentation/specific information on how to make this work properly with triggerable time-based events.
What would be in your opinion the best way to do what I have in mind? I looked into asyncio a little but maybe is too complex for what I actually need?
Thanks in advance for any suggestion!
Thanks to #GaganTK I was able to find what i need:
def start_notify(update, context):
new_job = context.job_queue.run_repeating(my_callback, interval=3, first=0, name="my_job")
def stop_notify(update, context):
job = context.job_queue.get_jobs_by_name("my_job")
job[0].schedule_removal()
def my_callback(context: telegram.ext.CallbackContext):
print(datetime.datetime.now())
Hi i want to send message from bot in specific time (without message from me), for example every Saturday morning at 8:00am.
Here is my code:
import telebot
import config
from datetime import time, date, datetime
bot = telebot.TeleBot(config.bot_token)
chat_id=config.my_id
#bot.message_handler(commands=['start', 'help'])
def print_hi(message):
bot.send_message(message.chat.id, 'Hi!')
#bot.message_handler(func=lambda message: False) #cause there is no message
def saturday_message():
now = datetime.now()
if (now.date().weekday() == 5) and (now.time() == time(8,0)):
bot.send_message(chat_id, 'Wake up!')
bot.polling(none_stop=True)
But ofc that's not working.
Tried with
urlopen("https://api.telegram.org/bot" +bot_id+ "/sendMessage?chat_id=" +chat_id+ "&text="+msg)
but again no result. Have no idea what to do, help please with advice.
I had this same issue and I was able to solve it using the schedule library. I always find examples are the easiest way:
import schedule
import telebot
from threading import Thread
from time import sleep
TOKEN = "Some Token"
bot = telebot.TeleBot(TOKEN)
some_id = 12345 # This is our chat id.
def schedule_checker():
while True:
schedule.run_pending()
sleep(1)
def function_to_run():
return bot.send_message(some_id, "This is a message to send.")
if __name__ == "__main__":
# Create the job in schedule.
schedule.every().saturday.at("07:00").do(function_to_run)
# Spin up a thread to run the schedule check so it doesn't block your bot.
# This will take the function schedule_checker which will check every second
# to see if the scheduled job needs to be ran.
Thread(target=schedule_checker).start()
# And then of course, start your server.
server.run(host="0.0.0.0", port=int(os.environ.get('PORT', 5000)))
I hope you find this useful, solved the problem for me :).
You could manage the task with cron/at or similar.
Make a script, maybe called alarm_telegram.py.
#!/usr/bin/env python
import telebot
import config
bot = telebot.TeleBot(config.bot_token)
chat_id=config.my_id
bot.send_message(chat_id, 'Wake up!')
Then program in cron like this.
00 8 * * 6 /path/to/your/script/alarm_telegram.py
Happy Coding!!!
If you want your bot to both schedule a message and also get commands from typing something inside, you need to put Thread in a specific position (took me a while to understand how I can make both polling and threading to work at the same time).
By the way, I am using another library here, but it would also work nicely with schedule library.
import telebot
from apscheduler.schedulers.blocking import BlockingScheduler
from threading import Thread
def run_scheduled_task():
print("I am running")
bot.send_message(some_id, "This is a message to send.")
scheduler = BlockingScheduler(timezone="Europe/Berlin") # You need to add a timezone, otherwise it will give you a warning
scheduler.add_job(run_scheduled_task, "cron", hour=22) # Runs every day at 22:00
def schedule_checker():
while True:
scheduler.start()
#bot.message_handler(commands=['start', 'help'])
def print_hi(message):
bot.send_message(message.chat.id, 'Hi!')
Thread(target=schedule_checker).start() # Notice that you refer to schedule_checker function which starts the job
bot.polling() # Also notice that you need to include polling to allow your bot to get commands from you. But it should happen AFTER threading!