I write a telegram bot on aiogram that gives me information about my accounts market.csgo.com. The meaning of the script is simple - I click on the button, it displays the text and and the function is run.
My functions send async requests and work fine, but I don't know how to get aiohttp and aiogram to work together.
from aiogram import Bot, types
from aiogram.dispatcher import Dispatcher
from aiogram.utils import executor
from auth import *
import asyncio
import aiohttp
bot = Bot(token=token)
dp = Dispatcher(bot)
def users():
***Data of my accounts from txt to dict***
async def get_info(session, dictt, message):
total_wallet = 0
async with session.get(f'https://market.csgo.com/api/v2/get-money?key={dictt[1][1]}') as resp:
html = await resp.json()
total_wallet += int(html['money'])
#await bot.send_message(message.from_user.id, f'{total_wallet}')
async def get_on_sale(session, dictt, message):
sale_total_sum = 0
async with session.get(f'https://market.csgo.com/api/v2/items?key={dictt[1][1]}') as resp:
html = await resp.json()
for i in html['items']:
sale_total_sum += i['price']
#await bot.send_message(message.from_user.id, f'{sale_total_sum}')
#dp.message_handler(content_types=['text'])
async def Main():
try:
profiles = users()
async with aiohttp.ClientSession(trust_env=True) as session:
tasks = []
if message.text == 'info 📊':
await bot.send_message(message.from_user.id, 'Wait for information..')
for i in profiles.items():
task = asyncio.ensure_future(get_info(session, i))
tasks.append(task)
await asyncio.gather(*tasks)
if message.text == 'on sale 💰':
await bot.send_message(message.from_user.id, 'Wait for information..')
for i in profiles.items():
task = asyncio.ensure_future(get_on_sale(session, i))
tasks.append(task)
await asyncio.gather(*tasks)
except Exception as ex:
print(f'Error {ex}')
loop = asyncio.get_event_loop()
loop.run_until_complete(Main())
executor.start_polling(dp, skip_updates=True)
My problem is that I don't know how to properly pass the message argument to the Main function
#dp.message_handler(content_types=['text'])
async def Main(): #async def Main(message)
And run aiogram along with aiohttp.
loop.run_until_complete(Main()) #loop.run_until_complete(Main(message))
If I do like this: async def Main(message) and loop.run_until_complete(Main(message)) Then I get an error:
loop.run_until_complete(Main(message))
NameError: name 'message' is not defined
or if I use only async def Main(message) get this:
loop.run_until_complete(Main())
TypeError: Main() missing 1 required positional argument: 'message'
Solution:
async def loop_on(message):
loop = asyncio.get_event_loop()
loop.run_until_complete(Main(message))
Related
I am writing a personal telegram bot, one of its functions is to show the balance of my accounts market.csgo.com. My code:
import asyncio
import aiogram
import aiohttp
...
async def get_balance(session, profiles_dict, message):
async with session.get(f'https://market.csgo.com/api/v2/get-money?key={profiles_dict[1][1]}') as resp:
html = await resp.json()
each_wallet = int(html['money'])
await bot.send_message(message.from_user.id,
f'🟢 {profiles_dict[0]} : <i>{each_wallet}</i>',
disable_web_page_preview=True, parse_mode=types.ParseMode.HTML)
...
#dp.message_handler(content_types=['text'])
async def main(message):
profiles = users()
async with aiohttp.ClientSession(trust_env=True) as session:
tasks = []
if message.text == 'Balance 💸':
await bot.send_message(message.from_user.id, 'Information request. Wait..')
for i in profiles.items():
task = asyncio.ensure_future(get_balance(session, i, message, stats))
tasks.append(task)
await asyncio.gather(*tasks)
if message.text == 'On Sale 💰':
...
if message.text == 'Timeout Items ⌛':
...
executor.start_polling(dp, skip_updates=False)
get_balance() works in async mode, sends aiohttp requests to the API and outputs information await bot.send_message(). Result:
Now the launch of the function is implemented through the keyboard button, but how to make the function run every hour? I am aware of the existence of asynchronous task scheduler aioschedule and have seen this example. But they run a function without arguments, but I have as many as 3 of them async def get_balance(session, profiles_dict, message). I tried to do this:
import asyncio
import aioschedule
async def scheduler(session, profiles_dict, message):
aioschedule.every().hour.do(get_balance(session, profiles_dict, message))
while True:
await aioschedule.run_pending()
await asyncio.sleep(1)
async def on_startup(session, profiles_dict, message):
asyncio.create_task(scheduler(session, profiles_dict, message))
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=False, on_startup=on_startup(session, profiles_dict, message))
Obviously it doesn't work that way.
My question is:
How to run an async function with arguments that sends aiohttp requests through task scheduling aioschedule and display the result through telegram aiogram?
Solution:
import aiogram
import asyncio
import aiohttp
import aioschedule
...
async def get_balance(session, profiles_dict):
async with session.get(f'https://market.csgo.com/api/v2/get-money?key={profiles_dict[1][1]}') as resp:
html = await resp.json()
each_wallet = int(html['money'])
await bot.send_message(MY_TELEGRAM_ID,
f'🟢 {profiles_dict[0]} : <i>{each_wallet}</i>',
disable_web_page_preview=True, parse_mode=types.ParseMode.HTML)
...
#dp.message_handler(content_types=['text'])
async def main(message):
profiles = users()
async with aiohttp.ClientSession(trust_env=True) as session:
tasks = []
if message.text == 'Balance 💸':
await bot.send_message(message.from_user.id, 'Information request. Wait..')
for i in profiles.items():
task = asyncio.ensure_future(get_balance(session, i))
tasks.append(task)
await asyncio.gather(*tasks)
if message.text == 'On Sale 💰':
...
if message.text == 'Timeout Items ⌛':
...
# Client session get_balance function
async def session_get_balance():
profiles = users()
async with aiohttp.ClientSession(trust_env=True) as session:
tasks = []
for i in profiles.items():
task = asyncio.ensure_future(get_balance(session, i))
tasks.append(task)
await asyncio.gather(*tasks)
# Schedule functions by time
async def scheduler():
aioschedule.every().hour.do(session_get_balance)
while True:
await aioschedule.run_pending()
await asyncio.sleep(1)
# Function at start
async def on_startup(_):
asyncio.create_task(scheduler())
# Launch telegram bot
if __name__ == "__main__":
executor.start_polling(dp, skip_updates=True, on_startup=on_startup)
Since this is my personal bot, instead of message.from_user.id I specified my MY_TELEGRAM_ID.
await bot.send_message(MY_TELEGRAM_ID,
f'🟢 {profiles_dict[0]} : <i>{each_wallet}</i>',
disable_web_page_preview=True, parse_mode=types.ParseMode.HTML)
Please help me deal with async and threads in the telethon module. I'm trying to make a bot that, with a certain command placed in the queue, will either send a file or send a message, but I ran into difficulty
I'm trying to run telethon async inside the stream, but the clock function does not start, although the function works with the event
import asyncio
import threading
from queue import Queue
from telethon import TelegramClient, events, sync
async def send_message():
loop = asyncio.new_event_loop()
api_id = 11
api_hash = '11'
try:
client = TelegramClient('session', api_id, api_hash, loop=loop)
await client.connect()
if not await client.is_user_authorized():
client.disconnected()
await client.start()
except:
print('qqqqq')
#client.on(events.NewMessage(chats='me'))
async def handler(event):
await client.forward_messages('me', event.message)
async def clock():
global cl
while True:
while (queue_send_al.qsize() == 0):
print("33")
event_send_all.wait()
async with client:
cl = queue_send_al.get()
print(cl)
if cl[0] == 1:
await client.send_file('me', str(cl[1]),use_cache=False, part_size_kb=512)
event_send_all.clear()
elif cl[0] == 2:
print('1111')
event_send_all.clear()
loop.create_task(clock())
print('22')
await client.run_until_disconnected()
def go():
asyncio.run(send_message())
queue_send_al = Queue()
event_send_all = threading.Event()
queue_send_al.put([2, fr"11111111"])
event_send_all.set()
th3 = threading.Thread(target=go).start()
There is the following code:
import asyncio
import aiohttp
aut_token = ("token")
tasks = []
iter_flag = False
class WAPI:
async def receiver(WAPI_S):
async for msg in WAPI_S:
data = msg.json()
raise aiohttp.ClientError #test
async def heartbeating(WAPI_S):
while iter_flag:
await WAPI_S.send_json({
"op": 1,
"d": None
})
await asyncio.sleep(42.5)
async def event_manager():
loop = asyncio.get_running_loop()
try:
async with aiohttp.ClientSession().ws_connect("url") as WAPI_S:
task_receive = loop.create_task(WAPI.receiver(WAPI_S)); task_heartbeating = loop.create_task(WAPI.heartbeating(WAPI_S))
tasks.append(task_receive); tasks.append(task_heartbeating)
await asyncio.gather(*tasks)
except aiohttp.ClientError:
global iter_flag
iter_flag = False
await asyncio.sleep(44)
[task.cancel() for task in tasks]
try:
loop.close()
except:
loop.stop()
asyncio.run(WAPI.event_manager())
I want to correctly shutdown the client when the exception is raised. My implementation throws "RuntimeError: Event loop stopped before Future completed" exception while executing. How to do it right?
In method event_manager, the statement:
async with aiohttp.ClientSession().ws_connect("url") as WAPI_S:
needs to be replaced with:
async with aiohttp.ClientSession() as session:
async with session.ws_connect("url") as WAPI_S:
Also, it is considered anti-Pythonic to use a list comprehension for its side effects. See Is it Pythonic to use list comprehensions for just side effects? So you really should replace:
[task.cancel() for task in tasks]
with:
for task in tasks:
task.cancel()
Putting this all together:
async def event_manager():
loop = asyncio.get_running_loop()
try:
async with aiohttp.ClientSession() as session:
async with session.ws_connect("url") as WAPI_S:
task_receive = loop.create_task(WAPI.receiver(WAPI_S)); task_heartbeating = loop.create_task(WAPI.heartbeating(WAPI_S))
tasks.append(task_receive); tasks.append(task_heartbeating)
await asyncio.gather(*tasks)
except aiohttp.ClientError:
global iter_flag
iter_flag = False
await asyncio.sleep(44)
for task in tasks:
task.cancel()
try:
loop.close()
except:
loop.stop()
Basically, what it does, is to do 20 requests async to google.
If I launch it without using PyTest, just a snip of code, like this, it works:
import asyncio
import aiohttp
async def get(
session: aiohttp.ClientSession,
) -> dict:
url = f"https://www.google.com/"
resp = await session.request('GET', url=url)
data = await resp.json()
return data
async def sessions():
async with aiohttp.ClientSession() as session:
tasks = []
for i in range(20):
tasks.append(get(session=session))
return await asyncio.gather(*tasks, return_exceptions=True)
def main():
loop = asyncio.new_event_loop()
try:
asyncio.set_event_loop(loop)
htmls = loop.run_until_complete(sessions())
finally:
loop.close()
print(htmls)
But when I use PyTest, in spite of being the same code (almost), the "htmls" variable at the end is not assignated any value
import aiohttp
import asyncio
async def get(
session: aiohttp.ClientSession,
) -> dict:
url = f"https://www.google.com/"
resp = await session.request('GET', url=url)
data = await resp.json()
return data
async def sessions(self):
async with aiohttp.ClientSession() as session:
tasks = []
for i in range(20):
tasks.append(self.get(session=session))
return await asyncio.gather(*tasks, return_exceptions=True)
def test_example(self):
loop = asyncio.new_event_loop()
try:
asyncio.set_event_loop(loop)
htmls = loop.run_until_complete(self.sessions())
finally:
loop.close()
print(htmls)
Why is this? It is like loop.run_until_complete(self.sessions()) is not waiting for it to finish.
It is resolved. It needed a self as first parameter for the get() method :S
I'm new to this library. I want to make my bot to have a queue option, I already tried declaring an empty list queue=[] and use
#commands.command()
async def play(self, ctx, *, query: str):
while True:
tracks = await self.bot.wavelink.get_tracks(f'ytsearch:{query}')
if not tracks:
continue
else:
break
player = self.bot.wavelink.get_player(ctx.guild.id)
if not player.is_connected:
await ctx.invoke(self.connect_)
channel = ctx.author.voice.channel
if player.is_playing:
if any(f"{channel.id}" in s for s in queue):
await ctx.channel.send(embed=discord.Embed(description='Your queue exceed maximum capacity of 1 element',color=0xfd2121))
else:
queue.append(f'{channel.id}:{tracks[0]}')
await ctx.channel.send(embed=discord.Embed(description=f"Your song has been queued\n{tracks[0]}",color=0xfd2121))
else:
await ctx.channel.send(embed=discord.Embed(description=f'Now playing {tracks[0]} .', color=0xfd2121))
await player.play(tracks[0])
while player.is_playing:
await asyncio.sleep(1)
if not any(f"{channel.id}" in s for s in queue):
await player.disconnect()
else:
res = [i for i in queue if f'{channel.id}' in i]
sw=''.join(res)
while True:
sw2 = await self.bot.wavelink.get_tracks(f'ytsearch:{sw[19:]}')
if not sw2:
continue
else:
break
player = self.bot.wavelink.get_player(ctx.guild.id)
await player.play(sw2[0])
queue.remove(f'{channel.id}:{sw[19:]}')
but this can only contain 1 queue per channel id and the scripts are way too complicated. I saw some people are using asyncio.Queue - some kind of background process - can anybody provide me with an example of using asyncio.Queue to queue up songs?
Here is a simple example of setting up a queue system with asyncio.Queue() and asyncio.Event(), utilising wavelink to handle audio.
import wavelink
import asyncio
from discord.ext import commands
client = commands.Bot(command_prefix='!')
songs = asyncio.Queue()
play_next_song = asyncio.Event()
if not hasattr(client, 'wavelink'):
client.wavelink = wavelink.Client(bot=client)
async def on_event_hook(event):
if isinstance(event, (wavelink.TrackEnd, wavelink.TrackException)):
play_next_song.set()
async def audio_player_task():
await client.wait_until_ready()
node = await client.wavelink.initiate_node(host='127.0.0.1',
port=2333,
rest_uri='http://127.0.0.1:2333',
password='PASSWORD',
identifier='TEST',
region='us_central')
node.set_hook(on_event_hook)
while True:
play_next_song.clear()
song, guild_id = await songs.get()
player = client.wavelink.get_player(guild_id)
await player.play(song)
await play_next_song.wait()
#client.command()
async def play(ctx, *, query: str):
tracks = await client.wavelink.get_tracks(f'ytsearch:{query}')
if not tracks:
return await ctx.send('Could not find any songs with that query.')
player = client.wavelink.get_player(ctx.guild.id)
if not player.is_connected:
await player.connect(ctx.author.voice.channel.id)
await ctx.send(f'Added {str(tracks[0])} to the queue.')
queue_item = (tracks[0], ctx.guild.id)
await songs.put(queue_item)
client.loop.create_task(audio_player_task())
client.run('token')