I have 2 threads in main.py file, the first one is created via Thread (bot thread) the second one is the main python thread where Pyrogram runs screenshot of main.py code
But when I run the code, Pyrogram terminates code execution when it gets to the point of sending the error message "Timeout context manager should be used inside a task", here is the full log full log error
future: <Task finished name='Task-157' coro=<__send_message() done, defined at C:\Programming\BybitTradeBot\services\trade.py:7> exception=RuntimeError('Timeout context manager should be used inside a task')>
Traceback (most recent call last):
File "C:\Programming\BybitTradeBot\services\trade.py", line 8, in __send_message
await bot.send_message(chat_id=db.get_user_value('tg_id'), text=msg)
File "C:\Programming\BybitTradeBot\venv\Lib\site-packages\aiogram\bot\bot.py", line 346, in send_message
result = await self.request(api.Methods.SEND_MESSAGE, payload)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Programming\BybitTradeBot\venv\Lib\site-packages\aiogram\bot\base.py", line 236, in request
return await api.make_request(await self.get_session(), self.server, self.__token, method, data, files,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Programming\BybitTradeBot\venv\Lib\site-packages\aiogram\bot\api.py", line 139, in make_request
async with session.post(url, data=req, **kwargs) as response:
File "C:\Programming\BybitTradeBot\venv\Lib\site-packages\aiohttp\client.py", line 1167, in __aenter__
self._resp = await self._coro
^^^^^^^^^^^^^^^^
File "C:\Programming\BybitTradeBot\venv\Lib\site-packages\aiohttp\client.py", line 493, in _request
with timer:
File "C:\Programming\BybitTradeBot\venv\Lib\site-packages\aiohttp\helpers.py", line 705, in __enter__
raise RuntimeError(
RuntimeError: Timeout context manager should be used inside a task
Here's the code that was used userbot code code from trade.py
If userbot is not given any asynchronous tasks, it does not cause an error "Timeout context manager should be used inside a task"
If run just Pyrogram, there are no problems and this code runs without any errors, even if it is asynchronous code
What is the cause of this error and how can it be fixed?
main.py
import asyncio
from aiogram import executor
from threading import Thread
from loader import dp, logging
import handlers
from userbot import main_client
async def start(_):
logging.info('The bot is up and running')
print('[+] The bot is up and running')
def bot_thread():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
executor.start_polling(dp, skip_updates=True, on_startup=start)
if __name__ == '__main__':
try:
process = Thread(target=bot_thread)
process.start()
main_client(logging) # Pyrogram
except Exception as error:
logging.warning(error)
print(error)
userbot.py
from pyrogram import Client, filters
from pyrogram.handlers import MessageHandler
from loader import logging as log
from config import USERBOT_API_HASH, USERBOT_API_ID, CHAT_ID_FOR_PARSING
from services.trade import do_futures_trade
last_photo_id = None
def main_client(logging):
client = Client(name='my_bot', api_id=USERBOT_API_ID, api_hash=USERBOT_API_HASH)
async def message_photo(client: Client, message): # pass the chat ID or Username to the function
global last_photo_id
async for msg in client.get_chat_history(chat_id=CHAT_ID_FOR_PARSING, limit=1, offset_id=-1): # looking at the end of the story
try:
if msg.photo and msg.caption and msg.photo.file_id != last_photo_id: # if there's a photo
logging.info(f'A prediction was sent to the group file_id - {msg.photo.file_id} caption - {msg.caption}')
await client.download_media(msg.photo, file_name=f'forecast.jpg') # Downloading a photo
last_photo_id = msg.photo.file_id
logging.info('A new forecast {msg.photo.file_id} has been sent')
do_futures_trade(msg.caption, client.loop)
except Exception as e:
logging.warning(f"There's been an error {e}")
client.add_handler(MessageHandler(message_photo, filters=filters.photo & filters.chat(chats=int(CHAT_ID_FOR_PARSING))))
client.run()
if __name__ == '__main__':
main_client(log)
trade.py
from loader import bot
import asyncio
from services.connection_db import db
async def __send_message(msg):
await bot.send_message(chat_id=db.get_user_value('tg_id'), text=msg)
def send_msg(msg, client_loop):
asyncio.run_coroutine_threadsafe(__send_message(msg), client_loop)
def do_futures_trade(msg, client_loop):
send_msg(msg, client_loop)
...
handlers.py
from loader import dp, bot
@dp.message_handler(commands=['start'])
async def start_command(message: Message):
await bot.send_message(chat_id=message.from_user.id, text="Hello User")
From the solution I came up with is using the nest_asyncio library
Sample solution:
import asyncio
import nest_asyncio
from pyrogram import Client
from aiogram import executor, Bot, Dispatcher
nest_asyncio.apply() # apply before triggering event loops
client = Client("my_userbot", api_id=USERBOT_API_ID, api_hash=USERBOT_API_HASH)
@client.on_message()
async def my_handler(client, message):
if message.text == "ping":
await message.reply("pong")
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)
@dp.message_handler(commands=['start', 'help'])
async def send_welcome(message: types.Message):
await message.reply("Hi! I'm a bot on aiogram.")
async def start_up(_):
logging.info('Bot launched successfully!')
print('[+] Bot launched successfully')
async def start_bot():
""" Start aiogram """
executor.start_polling(dp, skip_updates=True, on_startup=start_up)
async def start_userbot():
""" Start pyrogram """
await client.start()
async def main():
bot_task = asyncio.create_task(start_bot())
userbot_task = asyncio.create_task(start_userbot())
await asyncio.gather(bot_task, userbot_task)
if __name__ == '__main__':
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
except Exception as error:
logging.error("Error", exc_info=True)
finally:
logging.info('\n[+] Bye...')
print('\n[+] Bye...')
The nest_asyncio library connects all event loops into a single thread, which solves the problem when using libraries that create their own event loop