I wanted to run Telethon events in multi thread, multithreading.
For the following code, what I expected is multi thread, cocurrent independent events. await asyncio.sleep( 3 ) will sleep 3 seconds. So If you send any text on telegram to the account, it will wait 3 seconds and send me back "hi.". I expected that if I send a text for several times at once, let's say 5 times, it should wait for approximately 3 seconds and send me "hi" back at once, but not like wait for 3 seconds, send "hi.", wait for 3 seconds, send "hi." .. for 5 times and takes approximately 15 seconds. This is not what I expected for.
So, How can I modify the code to run the code as expected? Or it's impossible in telethon?
I read google, stackoverflow, github issue, but couldn't find a solution by myself. So I want your help here. Thank you.
import time
from pathlib import Path
import re
import random
import asyncio
from telethon import TelegramClient, events
from telethon.sessions import StringSession
from telethon import utils
import logging
logging.basicConfig(level=logging.ERROR)
api_id = ""
api_hash = ''
phone = ''
string = Path('./string').read_text()
message = "hi."
if __name__ == '__main__':
client = TelegramClient(StringSession(string), api_id, api_hash, sequential_updates=True)
#client.on(events.NewMessage(incoming=True))
async def handle_new_message(event):
print('message:', event.message.message)
# // not working here, the sleep blocks next events but not only current event.
print('wait for 3 seconds...')
await asyncio.sleep( 3 )
await event.respond(message)
print('Auto-replying...')
client.start(phone)
client.run_until_disconnected()
# client.loop.run_forever()
# string = StringSession.save(client.session)
# Path('./string').write_text(string)
You are using sequential_updates so until the first update is not completed it will not move to other update. I guess you need to turn off sequential updates.
Related
I have an issue related to queue using a background task that never ends (continuously run to grab real-time data.
What I want to achieve:
Starting server via flask-socketio (eventlet),
monkey_patch(),
Using start_background_task, run a function from another file that grabs data in real time,
While this background task is running (indefinitely), storing incoming data in a queue via queue.put(),
Always while this task is running, from the main program watching for new data in the queue and processing them, meaning here socketio.emit().
What works: my program works well if, in the background task file, the while loop ends (while count < 100: for instance). In this case, I can access the queue from the main file and emit data.
What doesn't work: if this while loop is now while True:, the program blocks somewhere, I can't access the queue from the main program as it seems that it waits until the background task returns or stops.
So I guess I'm missing something here... so if you guys can help me with that, or give me some clues, that would be awesome.
Here some relevant parts of the code:
main.py
from threading import Thread
from threading import Lock
from queue import Queue
from get_raw_program import get_raw_data
from flask import Flask, send_from_directory, Response, jsonify, request, abort
from flask_socketio import SocketIO
import eventlet
eventlet.patcher.monkey_patch(select=True, socket=True)
app = Flask(__name__, static_folder=static_folder, static_url_path='')
app.config['SECRET_KEY'] = 'secret_key'
socketio = SocketIO(app, binary=True, async_mode="eventlet", logger=True, engineio_logger=True)
thread = None
thread_lock = Lock()
data_queue = Queue()
[...]
#socketio.on('WebSocket_On')
def grab_raw_data(test):
global thread
with thread_lock:
if thread is None:
socketio.emit('raw_data', {'msg': 'Thread is None:'})
socketio.emit('raw_data', {'msg': 'Starting Thread... '})
thread = socketio.start_background_task(target=get_raw_data(data_queue, test['mode']))
while True:
if not data_queue.empty():
data = data_queue.get(block=True, timeout=0.05)
socketio.emit('raw_data', {'msg': data})
socketio.sleep(0.0001)
get_raw_program.py (which works, can access queue from main.py)
def get_raw_data(data_queue, test):
count = 0
while count < 100:
data.put(b'\xe5\xce\x04\x00\xfe\xd2\x04\x00')
time.sleep(0.001)
count += 1
get_raw_program.py (which DOESN'T work, can't access queue from main.py)
def get_raw_data(data_queue, test):
count = 0
while True:
data.put(b'\xe5\xce\x04\x00\xfe\xd2\x04\x00')
time.sleep(0.001)
count += 1
I tried with regular Thread instead of start_background_task, and it works well. Thanks again for your help, greatly appreciated :-)
import asyncio
from aiogram import Bot, Dispatcher, types
# Handlers
async def echo_hello(message: types.Message):
while True:
await message.reply("Hello!")
await asyncio.sleep(20)
# AWS Lambda funcs
async def register_handlers(dp: Dispatcher):
dp.register_message_handler(echo_hello, commands=['sayhello'])
async def process_event(event, dp: Dispatcher):
Bot.set_current(dp.bot)
update = types.Update.to_object(event)
await dp.process_update(update)
async def main(event):
bot = Bot(token=TOKEN)
dp = Dispatcher(bot)
await register_handlers(dp)
await process_event(event, dp)
return 'ok'
def lambda_handler(event, context):
return asyncio.get_event_loop().run_until_complete(main(event))
I can't seem to get my telegram bot in AWS Python Lambda to loop the Hello message. Provided I got no idea how else to get the lambda_handler function, I only saw this as an example from https://github.com/DavisDmitry/aiogram-aws-serverless-example
EDIT: NVM the default timeout for functions is 15 minutes at best so any loop will only work for 15 minutes at the most.
As #luk2302 mentions in the comment above, indeed Lambdas only have an upper limit of at most 15 minutes customizable by the user in the settings.
If set to 15 minutes, any loop will execute for that amount of time given the conditions make it so. At the time I asked this question I was unaware of the fact and by default the timeout value was set to 5 seconds, while my code was putting the while loop to sleep for 20 seconds. Hence I was unable to get any repeated messages. Once I set it to 2 minutes (for example), it repeated the message about 6 times after which it stopped.
Lambda functions do run almost forever but these are not suitable for functions which might have long execution process (including loops). For that, EC2 instances can be used.
Let's say I have a program in Python which looks like this:
import time
def send_message_realtime(s):
print("Real Time: ", s)
def send_message_delay(s):
time.sleep(5)
print("Delayed Message ", s)
for i in range(10):
send_message_realtime(str(i))
time.sleep(1)
send_message_delay(str(i))
What I am trying to do here is some sort of multithreading, so that the contents of my main for loop continues to execute without having to wait for the delay caused by time.sleep(5) in the delayed function.
Ideally, the piece of code that I am working upon looks something like below. I get a message from some API endpoint which I want to send to a particular telegram channel in real-time(paid subscribers), but I also want to send it to another channel by delaying it exactly 10 minutes or 600 seconds since they're free members. The problem I am facing is, I want to keep sending the message in real-time to my paid subscribers and kind of create a new thread/process for the delayed message which runs independently of the main while loop.
def send_message_realtime(my_realtime_message):
telegram.send(my_realtime_message)
def send_message_delayed(my_realtime_message):
time.sleep(600)
telegram.send(my_realtime_message)
while True:
my_realtime_message = api.get()
send_message_realtime(my_realtime_message)
send_message_delayed(my_realtime_message)
I think something like ThreadPoolExecutor does what you are look for:
import time
from concurrent.futures.thread import ThreadPoolExecutor
def send_message_realtime(s):
print("Real Time: ", s)
def send_message_delay(s):
time.sleep(5)
print("Delayed Message ", s)
def work_to_do(i):
send_message_realtime(str(i))
time.sleep(1)
send_message_delay(str(i))
with ThreadPoolExecutor(max_workers=4) as executor:
for i in range(10):
executor.submit(work_to_do, i)
The max_workers would be the number of parallel messages that could have at a given moment in time.
Instead of a multithread solution, you can also use a multiprocessing solution, for instance
from multiprocessing import Pool
...
with Pool(4) as p:
print(p.map(work_to_do, range(10)))
I am very new to coding and want to just add a command timer to the code i got off the internet. I don't know how to do this and all the other codes I find are too much for me to know. I just want to be able to add around a 10 second cooldown timer to each command.
import discord
import asyncio
from discord.ext import commands
import datetime as DT
import discord.utils
class MyClient (discord.Client):
async def on_ready(self):
print('Logged in as')
print(self.user.name)
print(self.user.id)
print('------')
async def on_message(self, message):
# we do not want the bot to reply to itself
if message.author.id == self.user.id:
return
if message.content.startswith('!discord'): # I want to add a timer
channel = client.get_user(message.author.id)
await channel.send(''.format(message))
async def on_member_join(self, member):
guild = member.guild
if guild.system_channel is not None:
to_send = 'Welcome {0.mention} to {1.name}!'.format(member, guild)
await guild.system_channel.send(to_send)
client = MyClient()
client.run('')
The time.sleep() function, which another user suggested is a blocking call. Which means it will block the entire thread from being ran. I am not sure exactly how the Discord framework works, but I imagine thread blocking might be a problem.
A better solution I think would be to use asyncio, especially since you've already imported it. It will allow the program to do other stuff, while waiting the 10 seconds. Another Stackoverflow thread if you're interested
if message.content.startswith('!discord'):
channel = client.get_user(message.author.id)
await asyncio.sleep(10)
await channel.send(''.format(message))
Edit
You can save the last time the function was called, and check with an IF-statement if 10 seconds have passed since the last call.
class MyClient (discord.Client):
last_called = None
async def on_ready(self):
# other stuff
async def on_message(self, message):
# check if 10 seconds have passed since the last call
if MyClient.last_called and DT.datetime.now() < MyClient.last_called + DT.timedelta(seconds=10):
return
# we do not want the bot to reply to itself
if message.author.id == self.user.id:
return
if message.content.startswith('!discord'):
channel = client.get_user(message.author.id)
await channel.send(''.format(message))
MyClient.last_called = DT.datetime.now() # save the last call time
If you add this to the imports at the top of the file:
from time import sleep
then you can use the sleep() function to pause the script for a set number of seconds when execution reaches that line. So for instance, adding this:
sleep(10)
somewhere inside the functions would sleep for 10 seconds whenever the script reaches that line.
I'm building a telegram bot using telepot(python) and a weird thing happened:
since the bot is supposed to keep running, I've set a
while 1:
time.sleep(.3)
at the end of my bot, right after I define how to handle the MessageLoop.
The bot has some print() to assert that the bot is setting up (or better, that it's setting up the message handler) and that it is reading the chat, waiting for any input.
The problem is that if I run the code without the
while 1:
time.sleep(.3)
it prints the messages and stops the execution (not having a loop to wait for new input), but if I add the while 1: (...) the code stops before being able to print anything.
Here's the code:
"""Launch the bot."""
import json
import telepot
from telepot.loop import MessageLoop
import time
from base import splitter
from base import handler
messageHandler = handler.Handler()
with open('triggers.json') as f:
triggers = json.load(f)
with open('config.json') as f:
config = json.load(f)
botKey = config['BOT_KEY']
# define the bot and the botname
bot = telepot.Bot(botKey)
botName = bot.getMe()['username']
# split commands in arrays ordered by priority
configSplitter = splitter.Splitter()
triggers = configSplitter.splitByPriority(triggers)
# define the handler
messageHandler.setBotname(botName)
messageHandler.setMessages(triggers)
# handle the messageLoop
print("setting up the bot...")
MessageLoop(bot, messageHandler.handle).run_as_thread()
print("Multibot is listening!")
# loop to catch all the messages
while 1:
time.sleep(.3)
Python version: 3.6 32-bit
The solution was to add sys.stdout.flush() after the various print()to restore the functionality of print(), while in order to make the code work again i had to change the telepot function
MessageLoop(bot, messageHandler.handle).run_as_thread()
which wasn't working properly to:
bot.message_loop(messageHandler.handle)