Run two asynco loop in a python script (telethon, aiopg) - python

I would like to use Telethon (Telegram bot) and aiopg (PostgreSQL) library.
Telethon example:
from telethon import TelegramClient
api_id = 12345
api_hash = '0123456789abcdef0123456789abcdef'
client = TelegramClient('anon', api_id, api_hash)
async def main():
# Getting information about yourself
me = await client.get_me()
print(me.stringify())
#client.on(events.NewMessage)
async def my_event_handler(event):
if 'hello' in event.raw_text:
await event.reply('hi!')
client.start()
client.run_until_disconnected()
aiopg example:
import aiopg
dsn = 'dbname=aiopg user=aiopg password=passwd host=127.0.0.1'
async def notify(conn):
async with conn.cursor() as cur:
for i in range(5):
msg = "message {}".format(i)
print('Send ->', msg)
await cur.execute("NOTIFY channel, %s", (msg,))
await cur.execute("NOTIFY channel, 'finish'")
async def listen(conn):
async with conn.cursor() as cur:
await cur.execute("LISTEN channel")
while True:
msg = await conn.notifies.get()
if msg.payload == 'finish':
return
else:
print('Receive <-', msg.payload)
async def main():
async with aiopg.create_pool(dsn) as pool:
async with pool.acquire() as conn1:
listener = listen(conn1)
async with pool.acquire() as conn2:
notifier = notify(conn2)
await asyncio.gather(listener, notifier)
print("ALL DONE")
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
I would like to use both in a same python script.
I tried to find the solution and it is maybe the asyncio.gather(...), but I don't know how can I combine these two library, how to start both loop.
Can you please help me?

create a new async function which creates a new client instance and add all the handlers you need, for the sake of this example I showed some example handlers of mine.
async def init_bot() -> TelegramClient:
client = TelegramClient(
session="trade-bot",
api_hash=Config.API_HASH,
api_id=Config.API_ID,
)
await client.start(bot_token=Config.BOT_TOKEN)
client.add_event_handler(
register_handler,
events.NewMessage(incoming=True, pattern=r"^[\/?!]register$"),
)
client.add_event_handler(
get_webhook_handler,
events.NewMessage(incoming=True, pattern=r"^[\/?!]webhook$"),
)
client.add_event_handler(
status_handler,
events.NewMessage(incoming=True, pattern=r"^[\/?!]status$"),
)
_LOG.info("Bot client started")
return client
then later on you main function
client = await init_bot()
await client.connect()
# the below code is non-blocking
asyncio.create_task(client.run_until_disconnected())

Related

Discord.py async-await won't close connection with Airflow DAG/Task

I'm trying to post a dataframe to a Discord channel. However, I am having issue getting Discord.py to close the connection and move on the next task. I've tried using the event loop as suggested in this thread (How to run async function in Airflow?) as well as asyncio.run() function. Not really familiar with the async and hoping to get some pointers here. Below is my code in Python that I've tried importing in DAG and Task without success. Thanks in advance!
Airflow: 2.5.1
Python: 3.7
import discord
from tabulate import tabulate
import asyncio
import pandas as pd
async def post_to_discord(df, channel_id, bot_token, as_message=True, num_rows=5):
intents = discord.Intents.default()
intents.members = True
client = discord.Client(intents=intents)
try:
#client.event
async def on_ready():
channel = client.get_channel(channel_id)
if as_message:
# Post the dataframe as a message, num_rows rows at a time
for i in range(0, len(df), num_rows):
message = tabulate(df.iloc[i:i+num_rows,:], headers='keys', tablefmt='pipe', showindex=False)
await channel.send(message)
else:
# Send the dataframe as a CSV file
df.to_csv("dataframe.csv", index=False)
with open("dataframe.csv", "rb") as f:
await channel.send(file=discord.File(f))
# client.run(bot_token)
await client.start(bot_token)
await client.wait_until_ready()
finally:
await client.close()
async def main(df, channel_id, bot_token, as_message=True, num_rows=5):
# loop = asyncio.get_event_loop()
# result = loop.run_until_complete(post_to_discord(df, channel_id, bot_token, as_message, num_rows))
result = asyncio.run(post_to_discord(df, channel_id, bot_token, as_message, num_rows))
await result
return result
if __name__ =='__main__':
main()
It seems like your script works but the server is blocking the open socket (and kudos - the discord server is good at that). So we will work by creating a ping function (adopted from another answer).
def ping(ip, port):
try:
s = socket.socket() # TCP - standard values are `socket.AF_INET, socket.SOCK_STREAM` so you don't have to write them
s.settimeout(2)
print('[DEBUG] connect')
s.connect((ip, int(port)))
#result = s.connect_ex((ip, int(port)))
#print('result:', result)
return True
except socket.timeout as ex:
print('[DEBUG] timeout')
return True
except Exception as ex:
print('[Exception]', ex)
return False
finally:
print('[DEBUG] close')
s.close()
Feel free to test your ID
id = ...channel number...
print(id, type(id))
and you should see
<built-in function id> <class 'builtin_function_or_method'>
Then let us move on to improving your code:
import discord
import asyncio
import time # you are not using this module
import socket
import os
from tabulate import tabulate
import pandas as pd # as pd is not required
def ping(ip, port):
try:
s = socket.socket() # TCP - standard values are `socket.AF_INET, socket.SOCK_STREAM` so you don't have to write them
s.settimeout(2)
print('[DEBUG] connect')
s.connect((ip, int(port)))
#result = s.connect_ex((ip, int(port)))
#print('result:', result)
return True
except socket.timeout as ex:
print('[DEBUG] timeout')
return True
except Exception as ex:
print('[Exception]', ex)
return False
finally:
print('[DEBUG] close')
s.close()
TOKEN = os.getenv('DISCORD_TOKEN')
client = discord.Client()
async def post_to_discord(df, channel_id, bot_token, as_message=True, num_rows=5):
intents = discord.Intents.default()
intents.members = True
client = discord.Client(intents=intents)
try:
#client.event
async def on_ready():
channel = client.get_channel(channel_id)
if as_message:
# Post the dataframe as a message, num_rows rows at a time
for i in range(0, len(df), num_rows):
message = tabulate(df.iloc[i:i+num_rows,:], headers='keys', tablefmt='pipe', showindex=False)
await channel.send(message)
else:
# Send the dataframe as a CSV file
df.to_csv("dataframe.csv", index=False)
with open("dataframe.csv", "rb") as f:
await channel.send(file=discord.File(f))
# client.run(bot_token)
await client.start(bot_token)
await client.wait_until_ready()
while True:
online = ping("26.51.174.109", "25565") #modify it as you see fit
#online = ping("192.168.1.101", "8081") #same as above
if online:
print("server online")
#await channel.edit(name="Server Status - Online")
else:
print("server offline")
#await channel.edit(name="Server Status - Offline")
await asyncio.sleep(5)
# optional - client.run(TOKEN)
finally:
await client.close()
async def main(df, channel_id, bot_token, as_message=True, num_rows=5):
# loop = asyncio.get_event_loop()
# result = loop.run_until_complete(post_to_discord(df, channel_id, bot_token, as_message, num_rows))
result = asyncio.run(post_to_discord(df, channel_id, bot_token, as_message, num_rows))
await result
return result
if __name__ =='__main__':
main()

BinanceSocketManager doesn't show futures_user_socket()

I'm using Binance UMFutures testnet and want to build stream for user_data ( order updates).
Keys are ok. Other methods like klines work perfect as well. But .futures_user_socket() shows nothing when I place or cancel orders. Could you please advice what am I doing wrong?
`
from binance import AsyncClient , BinanceSocketManager
import asyncio
api_key_testnet = '<--->'
secret_key_testnet = '<--->'
async def user_data_listener(client):
bm = BinanceSocketManager(client)
async with bm.futures_user_socket() as stream:
while True:
res = await stream.recv()
print(res)
async def main():
client = await AsyncClient.create(api_key_testnet,secret_key_testnet,testnet=True)
await user_data_listener(client)
if __name__ == "__main__":
asyncio.run(main())
`

Multiple client request using httpx Runtime Error: Cannot send a request, as the client has been closed

I have the below piece of code
async def get_data(uuid):
async with sema, httpx.AsyncClient(
base_url=udi_data_url, params=params
) as udi_client:
udi_result = udi_client.get(f"/{uuid}")
async with sema, httpx.AsyncClient(
base_url=manufacturer_data_url, params=params
) as client:
manufacturing_result = client.get(f"/{uuid}")
result1, result2 = await asyncio.gather(udi_result, manufacturing_result)
print(result1, result2)
async def main():
await get_data(uuid)
asyncio.run(main())
How do I keep the client connections open as i understand the moment i reach this line
result1, result2 = await asyncio.gather(udi_result, manufacturing_result)
I know that i can do something like
udi_result = await udi_client.get(f"/{uuid}")
and
manufacturing_result = await client.get(f"/{uuid}")
But that's not what I want to do.
I am out of the context and thus
I am getting an error.

How to connect via websocket?

I have such part of code(Python):
def func_login_params():
async with websockets.connect(url) as ws:
# login
timestamp = str(get_local_timestamp())
login_str = login_params(timestamp, api_key, passphrase, secret_key)
await ws.send(login_str)
res = await ws.recv()
# params
sub_str = json.dumps(params)
await ws.send(sub_str)
Upper code works - okey. But I need to have two function: first - login, second - send params. As I thought I could do this:
def func_login():
async with websockets.connect(url) as ws:
# login
timestamp = str(get_local_timestamp())
login_str = login_params(timestamp, api_key, passphrase, secret_key)
await ws.send(login_str)
res = await ws.recv()
def func_params():
async with websockets.connect(url) as ws:
# params
sub_str = json.dumps(params)
await ws.send(sub_str)
But in this situation params don't come. Maybe it don't connect correctly?
When you try to connect websockets, it creates new connection.
So, in that case, you should take an websocket connection as function argument:
async def func_login(ws):
timestamp = str(get_local_timestamp())
login_str = login_params(timestamp, api_key, passphrase, secret_key)
await ws.send(login_str)
res = await ws.recv()
async def func_params(ws):
sub_str = json.dumps(params)
await ws.send(sub_str)
def main():
async with web sockets.connect(url) as ws:
await func_login(ws)
await func_params(ws)

Telethon reading message from Channel with id

Hello I'm using Telethon 1.21.1 
The most question here are outdated.
This scripts task is to read a message of a specific Channel per id.
I'm not sure where to pass the info for the channel and how if I use the method to read the msg in the proper way. await but I'm not sure how I pull it off
This is what I have:
my_private_channel_id = "-100777000"
my_private_channel = "test"
api_id = # 7 Digit Telegram API ID.
api_hash = '' # 32 Character API Hash
phone = '+' #Enter Your Mobilr Number
client = TelegramClient(phone, api_id, api_hash)
async def main():
await client.send_message('me', 'Hello !!!!') # just to test connection
with client:
client.loop.run_until_complete(main())
client.connect()
if not client.is_user_authorized():
client.send_code_request(phone)
client.sign_in(phone, input('Enter verification code: '))
chats = []
last_date = None
chunk_size = 200
channels=[] #target channel
result = client(GetDialogsRequest(
offset_date=last_date,
offset_id=0,
offset_peer=InputPeerEmpty(),
limit=chunk_size,
hash = 0
))
chats.extend(result.chats)
for chat in chats:
try:
if chat.channels== True:
readmsg = client.get_messages(chat, None)
except:
continue
from telethon import TelegramClient, events
client = TelegramClient('session', api_id, api_hash)
#client.on(events.NewMessage(chats="#TelethonUpdates"))
async def my_event_handler(event):
print(event.text)
client.start()
client.run_until_disconnected()
This is the right and simple way.

Categories

Resources