I'm trying to attach to Telegram APIs to interact through such platform.
I need to send messages on multiple different threads, but I've not found any solution so far allowing me to do that.
Here you can find two sketch codes of my attempts to manage messages on different threads, using both telethon and pyrogram libraries.
So far, I've not found a solution yet.
Telethon case
import time
import threading
from telethon import TelegramClient, events
telegram_client = None
async def telethon_telegram_init(api_id, api_hash):
global telegram_client
client = TelegramClient('my_account', api_id, api_hash)
await client.start()
try: assert await client.connect()
except: pass
if not await client.is_user_authorized():
client.send_code_request(phone_number)
me = client.sign_in(phone_number, input('Enter code: '))
client.parse_mode = 'html'
telegram_client = client
#telegram_client.on(events.NewMessage(incoming=True))
async def telethon_telegram_receive(event):
if event.is_private: await event.respond('Thank you for your message')
def telethon_telegram_send(to, message):
global telegram_client
while True:
telegram_client.send_message(to, message)
time.sleep(60)
# initiating the client object
telegram_client = telethon_telegram_init(api_id, api_hash)
# running the thread aimed to send messages
threading.Thread(target=telethon_telegram_send, args=('me', 'Hello')).start()
Results:
initialization: ok
message reception: not working (no errors are returned)
message sending: not working (AttributeError: 'coroutine' object has no attribute 'send_message' returned)
Pyrogram case
import time
import threading
from pyrogram import Client, filters
def pyrogram_telegram_init(api_id, api_hash):
client = Client('my_account', api_id, api_hash)
#client.on_message(filters.private)
async def pyrogram_telegram_receive(event):
await event.reply_text(f'Thank you for your message')
client.start()
return client
def pyrogram_telegram_send(client, to, message):
while True:
client.send_message(to, message)
time.sleep(60)
# initiating the client object
telegram_client = pyrogram_telegram_init(api_id, api_hash)
# running the thread aimed to send messages
threading.Thread(target=pyrogram_telegram_send, args=(telegram_client, 'me', 'Hello')).start()
Results:
initialization: ok
message reception: not working (no errors are returned)
message sending: not working (no errors are returned)
Update:
Attempts with asyncio
I've also tried to use asyncio, as suggested by #Lonami. I've focused on the telethon library.
Nevertheless, please note that in my case, new Telegram messages are generated by a TCP server listening on the host, in a way similar to the simplified sketch code below.
Here is the code reporting my attempts.
import time
import asyncio
from telethon import TelegramClient, events
RECIPIENT = 'me'
api_id = '...'
api_hash = '...'
telegram_client = None
# server management part
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
class ServerSample(StreamRequestHandler):
def handle(self):
message = 'New connection from %s:%s' % self.client_address
print(message)
self.server.loop.call_soon_threadsafe(self.server.queue.put_nowait, message)
# also tried with the following:
#self.server.queue.put_nowait(message)
async def initialize_server(loop, queue):
with ThreadingTCPServer(('127.0.0.1', 8080), ServerSample) as server:
server.loop = loop
server.queue = queue
server.serve_forever()
async def telethon_telegram_init(api_id, api_hash):
global telegram_client
client = TelegramClient('my_account', api_id, api_hash)
await client.start()
try: assert await client.connect()
except Exception as e: print(str(e))
if not await client.is_user_authorized():
client.send_code_request(phone_number)
me = client.sign_in(phone_number, input('Enter code: '))
client.parse_mode = 'html'
telegram_client = client
#telegram_client.on(events.NewMessage(incoming=True))
async def telethon_telegram_receive(event):
if event.is_private: await event.respond('Thank you for your message')
return telegram_client
async def telethon_telegram_generate(loop, queue, t):
loop.call_soon_threadsafe(queue.put_nowait, 'Hello from direct {} call'.format(t))
async def main():
global telegram_client
loop = asyncio.get_running_loop()
queue = asyncio.Queue()
# initiating the Telegram client object
await telethon_telegram_init(api_id, api_hash)
# initializing the server (non blocking)
loop.create_task(initialize_server(loop, queue))
# creating a new message (non blocking)
loop.create_task(telethon_telegram_generate(loop, queue, 'non blocking'))
# creating a new message (blocking)
loop.run_in_executor(None, telethon_telegram_generate, loop, queue, 'blocking')
# managing the queue
while True:
message = await queue.get()
print("Sending '{}'...".format(message))
await telegram_client.send_message(RECIPIENT, message)
time.sleep(1)
asyncio.run(main())
In this case, to trigger the generation of a new Telegram message from the TCP server itself, it is needed to launch a curl command like the following one:
curl http://localhost:8080
Nevertheless, results are not promising.
Results:
initialization: ok for telethon_telegram_init and initialize_server, apparently not working for both telethon_telegram_generate method calls
message reception: not working (no errors are returned)
message sending: not working (no errors are returned)
additional notes: after the curl command is executed, a New connection from 127.0.0.1:<random_port> message is printed, but no Telegram messages are sent
Related
I trying to use Python's logging and asyncio libraries to send logs via WebSockets. For context Python's logging runs logging handlers in different threads.
In the code bellow the event loop and all WebSocket connections get passed to the log handler so it can broadcast the messages to everyone.
class ESSocketLoggerHandlerWS(logging.Handler, object):
def __init__(self, name, other_attr=None, **kwargs):
logging.Handler.__init__(self)
self.connections = []
self.loop = kwargs.get("loop")
def addConnection(self, connection):
self.connections.append(connection)
async def send(self, socketcon, msg):
print("Sending...")
await socketcon.send(msg)
def emit(self, record):
msg = (self.format(record) + "\n").encode("utf-8")
# Remove old clients no longer connected
self.connections[:] = [x for x in self.connections if not x.closed]
# Send logs to the available sockets
for socketcon in self.connections:
print("Sending message to all WS clients")
self.loop.call_soon_threadsafe(self.send(socketcon, msg))
eventLoop = asyncio.new_event_loop()
asyncio.set_event_loop(eventLoop);
esSocketHandlerWS = ESSocketLoggerHandlerWS(name='mainlogger', loop=eventLoop)
log.addHandler(esSocketHandlerWS)
async def handle_client_ws(websocket):
log.info('New websocket connection')
esSocketHandlerWS.addConnection(websocket)
async for message in websocket:
# do suff with incoming messages...
async def main():
serverWs = await websockets.serve(handle_client_ws, "127.0.0.1", 8765)
async with server, serverWs:
await asyncio.gather(
...,
serverWs.serve_forever()
)
asyncio.run(main())
Unfortunately it doesn't work nor gives me any errors. Clients can connect and send messages but no logs get to them. I get Sending message to all WS clients in stdout, however print("Sending...") is never called nor await socketcon.send(msg).
What's the proper way of doing this? Thank you.
Hi there I have an idea for this script as usaull telethon libary when the system internet was dissconnect (not isp) it will try for 5 time(s) , I want to increase the number of trying to reconnect
for exmple for many times or a infinity loop for trying to reconnect
.this is my code if the internet was dissconnet telethon trying to reconnect for 5 times I want increase that as infinity loop
from config import configuration
from telethon.sync import TelegramClient, events
client = TelegramClient('anon', configuration["api_id"], configuration["api_hash"])
print('please wait this action take a less than minute'.title())
with client:
for dialog in client.iter_dialogs():
if dialog.is_channel:
if dialog.name == configuration["send_channel"]:
channel_id = dialog.id
break
print('start'.title())
#client.on(events.NewMessage(chats=configuration["monitoring_channel"]))
async def my_event_handler(event):
print(event.raw_text)
await client.send_message(entity=channel_id, message=event.message)
client.start()
client.run_until_disconnected()
This code retries to connect again if the connection failed.
from config import configuration
from telethon.sync import TelegramClient, events
client = TelegramClient('anon', configuration["api_id"], configuration["api_hash"])
print('please wait this action take a less than minute'.title())
with client:
for dialog in client.iter_dialogs():
if dialog.is_channel and dialog.name == configuration["send_channel"]:
channel_id = dialog.id
break
print('start'.title())
#client.on(events.NewMessage(chats=configuration["monitoring_channel"]))
async def my_event_handler(event):
print(event.raw_text)
await client.send_message(entity=channel_id, message=event.message)
def test():
try:
client.start()
client.run_until_disconnected()
except ConnectionError: #catches the ConnectionError and starts the connections process again
print('ConnectionError')
test()
I am trying to test out if I send out multiple requests at the same moment using coroutine can cause the server side receives corrupted data.
My test is based on the sample code from: https://websockets.readthedocs.io/en/stable/intro.html
Somehow, for the following code, the server side only receive one requests? Anyone has some insights? thx
server (this is basically the same code from the websockets Getting Started webpage):
#!/usr/bin/env python3
# WS server example
import asyncio
import websockets
async def hello(websocket, path):
name = await websocket.recv()
print(f"< {name}")
greeting = f"Hello {name}!"
await websocket.send(greeting)
print(f"> {greeting}")
start_server = websockets.serve(hello, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Client, I created 1000 tasks, and schedule to run them as soon as possible:
#!/usr/bin/env python3
# WS client example
import asyncio
import websockets
uri = "ws://localhost:8765"
connection = None
async def hello():
global connection
name = "What's your name? "
await connection.send(name)
print(f"> {name}")
async def main():
global connection
connection = await websockets.connect(uri)
#asyncio.run(main())
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.run_until_complete(asyncio.wait(
[hello() for i in range(1000)], return_when=asyncio.ALL_COMPLETED
))
UPDATE
The solution is to use a loop.
I found the reason: the server side, handler should use a loop so that the corroutine will not finish immediately after received the first request.
The documentation you linked also includes this paragraph just below the server code:
On the server side, websockets executes the handler coroutine hello once for each WebSocket connection. It closes the connection when the handler coroutine returns.
The client code you linked creates one connection and sends messages on that connection. After the client sends the first message, the server closes the connection, so the next 999 messages you attempt to send are being sent on a closed connection.
If you update the hello handler to include a loop, you will see all messages.
import asyncio
import websockets
async def hello(websocket, path):
while True:
name = await websocket.recv()
print(f"< {name}")
greeting = f"Hello {name}!"
await websocket.send(greeting)
print(f"> {greeting}")
start_server = websockets.serve(hello, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
I'm trying to build a websocket client on Python using websockets package from here: Websockets 4.0 API
I'm using this way instead of example code because I want to create a websocket client class object, and use it as gateway.
I'm having issues with my listener method (receiveMessage) on client side, which raises a ConnectionClose exception at execution. I think maybe there is any problem with the loop.
This is the simple webSocket client I've tried to build:
import websockets
class WebSocketClient():
def __init__(self):
pass
async def connect(self):
'''
Connecting to webSocket server
websockets.client.connect returns a WebSocketClientProtocol, which is used to send and receive messages
'''
self.connection = await websockets.client.connect('ws://127.0.0.1:8765')
if self.connection.open:
print('Connection stablished. Client correcly connected')
# Send greeting
await self.sendMessage('Hey server, this is webSocket client')
# Enable listener
await self.receiveMessage()
async def sendMessage(self, message):
'''
Sending message to webSocket server
'''
await self.connection.send(message)
async def receiveMessage(self):
'''
Receiving all server messages and handling them
'''
while True:
message = await self.connection.recv()
print('Received message from server: ' + str(message))
And this is the main:
'''
Main file
'''
import asyncio
from webSocketClient import WebSocketClient
if __name__ == '__main__':
# Creating client object
client = WebSocketClient()
loop = asyncio.get_event_loop()
loop.run_until_complete(client.connect())
loop.run_forever()
loop.close()
To test incoming messages listener, server sends two messages to client when it stablishes the connection.
Client connects correctly to server, and sends the greeting. However, when client receives both messages, it raises a ConnectionClosed exception with code 1000 (no reason).
If I remove the loop in the receiveMessage client method, client does not raise any exception, but it only receives one message, so I suppose I need a loop to keep listener alive, but I don't know exactly where or how.
Any solution?
Thanks in advance.
EDIT: I realize that client closes connection (and breaks loop) when it receives all pending messages from server. However, I want client keeps alive listening future messages.
In addition, I've tried to add another function whose task is to send a 'heartbeat' to server, but client closes connection anyway.
Finally, based on this post answer, I modified my client and main files this way:
WebSocket Client:
import websockets
import asyncio
class WebSocketClient():
def __init__(self):
pass
async def connect(self):
'''
Connecting to webSocket server
websockets.client.connect returns a WebSocketClientProtocol, which is used to send and receive messages
'''
self.connection = await websockets.client.connect('ws://127.0.0.1:8765')
if self.connection.open:
print('Connection stablished. Client correcly connected')
# Send greeting
await self.sendMessage('Hey server, this is webSocket client')
return self.connection
async def sendMessage(self, message):
'''
Sending message to webSocket server
'''
await self.connection.send(message)
async def receiveMessage(self, connection):
'''
Receiving all server messages and handling them
'''
while True:
try:
message = await connection.recv()
print('Received message from server: ' + str(message))
except websockets.exceptions.ConnectionClosed:
print('Connection with server closed')
break
async def heartbeat(self, connection):
'''
Sending heartbeat to server every 5 seconds
Ping - pong messages to verify connection is alive
'''
while True:
try:
await connection.send('ping')
await asyncio.sleep(5)
except websockets.exceptions.ConnectionClosed:
print('Connection with server closed')
break
Main:
import asyncio
from webSocketClient import WebSocketClient
if __name__ == '__main__':
# Creating client object
client = WebSocketClient()
loop = asyncio.get_event_loop()
# Start connection and get client connection protocol
connection = loop.run_until_complete(client.connect())
# Start listener and heartbeat
tasks = [
asyncio.ensure_future(client.heartbeat(connection)),
asyncio.ensure_future(client.receiveMessage(connection)),
]
loop.run_until_complete(asyncio.wait(tasks))
Now, client keeps alive listening all messages from server and sending 'ping' messages every 5 seconds to server.
I am learning how to use the websockets package for python 3.6 with asyncio.
Using the Websockets Getting Started example, here are my server and client code (both running in two separate console using python <script>)
wsserver.py
import asyncio
import websockets
msg_queue = asyncio.Queue()
async def consumer_handler(websocket):
global msg_queue
while True:
message = await websocket.recv()
print("Received message {}".format(message))
await msg_queue.put("Hello {}".format(message))
print("Message queued")
async def producer_handler(websocket):
global msg_queue
while True:
print("Waiting for message in queue")
message = await msg_queue.get()
print("Poped message {}".format(message))
websocket.send(message)
print("Message '{}' sent".format(message))
async def handler(websocket, path):
print("Got a new connection...")
consumer_task = asyncio.ensure_future(consumer_handler(websocket))
producer_task = asyncio.ensure_future(producer_handler(websocket))
done, pending = await asyncio.wait([consumer_task, producer_task]
, return_when=asyncio.FIRST_COMPLETED)
print("Connection closed, canceling pending tasks")
for task in pending:
task.cancel()
start_server = websockets.serve(handler, 'localhost', 5555)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
wsclient.py
import asyncio
import websockets
async def repl():
async with websockets.connect('ws://localhost:5555') as websocket:
while True:
name = input("\nWhat's your name? ")
await websocket.send(name)
print("Message sent! Waiting for server answer")
greeting = await websocket.recv()
# never goes here
print("> {}".format(greeting))
asyncio.get_event_loop().run_until_complete(repl())
During the execution, the server is doing what is expected of him :
Wait for a client message
Queue 'Hello $message'
Dequeue it
Send the dequeued message back to the sender
The client does work up to the waiting of the server response :
Wait for a user input
Send it to the server
Wait answer from the server <-- Holds on indefinitely
Print it & loop
Here are the console outputs of the execution :
Server
Got a new connection...
Waiting for message in queue
Received message TestName
Message queued
Poped message Hello TestName
Message 'Hello TestName' sent
Waiting for message in queue
Client
What's your name? TestName
Message sent! Waiting for server answer
_
What am I missing?
Server-side, you're missing an await on the websocket.send(message) line.
To find those kind of bugs, start your program with the PYTHONASYNCIODEBUG environment variable, like: PYTHONASYNCIODEBUG=1 python3 wsserver.py which prints:
<CoroWrapper WebSocketCommonProtocol.send() running at […]/site-packages/websockets/protocol.py:301, created at wsserver.py:23> was never yielded from