Flask with Telethon dont close thread - flask

I am trying to send a message to telegram via telethon and flask but the Telehton is not releasing the thread so i can not get response in postman.
I followed the telethon documentation but with no success :(
SendMessageApi.Exec is the second function that is only calling the SendMessage function.
#app.post('/send-message')
async def send_message():
req = request.get_json()
return await SendMessageApi.Exec(req)
async def Exec(req):
request = SendMessageRequest(**req)
response = await SendMessage(request)
return response
async def SendMessage(request: SendMessageRequest):
client = TelegramClient(request.phone, request.apiId, request.apiHash)
await client.connect()
if not await client.is_user_authorized():
await client.send_code_request(request.phone)
await client.sign_in(request.phone, input('send code'))
destionationEntity = await client.get_entity(request.destinationUsername)
responseMessage = await client.send_message(destionationEntity, request.message, parse_mode=request.parseMode, link_preview=True)
if request.pin:
await client.pin_message(destionationEntity, responseMessage, notify=True)
client.session.close()
return json.dumps(responseMessage, skipkeys=True, cls=json.JSONEncoder, default=json_default)

You can use flash + telethon with hypercorn
see full example here (scroll down)
https://urlk.ga/#telethon_codes

Related

Why don't I receive my Django Channels Websocket message on the frontend?

It is connecting just fine but I don't receive the message I want to send.
class ArticleConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
print("WebSocket Connected")
await self.accept()
async def send(self, text_data=None, bytes_data=None):
print("Hello")
print("Sending WebSocket Text Data : ", text_data)
print("Sending WebSocket Bytes Data : ", bytes_data)
await send(text_data="Hi this is text")
await send(bytes_data="Hi this is frame")
async def disconnect(self):
print("WebSocket Disconnected")
The terminal just shows this
WebSocket Connected
WebSocket CONNECT /api/ [127.0.0.1:52360]
WebSocket HANDSHAKING /api/ [127.0.0.1:52362]
WebSocket Connected
WebSocket CONNECT /api/ [127.0.0.1:52362]
which means async def send isn't working.
I also tried to change it to async def receive, still it doesn't work.
Here is the backend project repo :
https://www.github.com/prateekamana/tempstack
you should send data to the frontend in JSON format.
await send(text_data=json.dumps({"message": "Hi this is text"}))

Implementing simple Server Sent Event Stream with Django Channels

Django Channels docs has following basic example of a Server Sent Events. AsyncHttpConsumer
from datetime import datetime
from channels.generic.http import AsyncHttpConsumer
class ServerSentEventsConsumer(AsyncHttpConsumer):
async def handle(self, body):
await self.send_headers(headers=[
(b"Cache-Control", b"no-cache"),
(b"Content-Type", b"text/event-stream"),
(b"Transfer-Encoding", b"chunked"),
])
while True:
payload = "data: %s\n\n" % datetime.now().isoformat()
await self.send_body(payload.encode("utf-8"), more_body=True)
await asyncio.sleep(1)
I want to accept messages sent via channel_layer and send them as events.
I changed the handle method, so it subscribes the new channel to a group. And I'm planning to send messages to the channel layer via channel_layer.group_send
But I couldn't figure out how to get the messages sent to the group, within handle method. I tried awaiting for the channel_layer.receive, it doesn't seem to work.
class ServerSentEventsConsumer(AsyncHttpConsumer):
group_name = 'my_message_group'
async def myevent(self, event):
# according to the docs, this method will be called \
# when a group received a message with type 'myevent'
# I'm not sure how to get this event within `handle` method's while loop.
pass
async def handle(self, body):
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.send_headers(headers=[
(b"Cache-Control", b"no-cache"),
(b"Content-Type", b"text/event-stream"),
(b"Transfer-Encoding", b"chunked"),
])
while True:
payload = "data: %s\n\n" % datetime.now().isoformat()
result = await self.channel_receive()
payload = "data: %s\n\n" % 'received'
I'm sending the messages to channel_layer like below: ( from a management command)
def send_event(event_data):
group_name = 'my_message_group'
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
group_name,
{
'type': 'myevent',
'data': [event_data]
}
)
I had the same issue and I even went to dig into Django Channels code but without success.
... Until I found this answer in this (still opened) issue: https://github.com/django/channels/issues/1302#issuecomment-508896846
That should solve your issue.
In your case the code would be (or something quite similar):
class ServerSentEventsConsumer(AsyncHttpConsumer):
group_name = 'my_message_group'
async def http_request(self, message):
if "body" in message:
self.body.append(message["body"])
if not message.get("more_body"):
await self.handle(b"".join(self.body))
async def myevent(self, event):
# according to the docs, this method will be called \
# when a group received a message with type 'myevent'
# I'm not sure how to get this event within `handle` method's while loop.
pass
async def handle(self, body):
await self.channel_layer.group_add(
self.group_name,
self.channel_name
)
await self.send_headers(headers=[
(b"Cache-Control", b"no-cache"),
(b"Content-Type", b"text/event-stream"),
(b"Transfer-Encoding", b"chunked"),
])

Constantly send data to client from server

Take a look at this example.
As you can see, some sort of event is constantly being sent to the client. I want to imitate this using Django-Channels, inside consumers.py. Here's a simplified version of what I have:
class ChatConsumer(AsyncConsumer):
async def ws_connect(self, event):
self.send = get_db_object()
....
await self.send({
"type": "websocket.accept"
})
# I need to CONSTANTLY receive & send data
async def ws_receive(self, event):
obj = ...# query DB and get the newest object
json_obj = {
'field_1': obj.field_1,
'field_2': obj.field_2,
}
await self.send({
"type": "websocket.send",
"text": json.dumps(json_obj)
})
#database_sync_to_async
def get_db_object(self, **kwargs):
return Some_Model.objects.get(**kwargs)[0]
Here, I want my Django backend to constantly:
Query DB
Receive obj from DB
Send the received obj to Front-End WebSocket as event
How can I achieve this? The important thing is that I need to CONSTANTLY send data to the client.
Most of the Django-Channels resources on the internet cover only Chat Apps, which don't necessarily constantly send data to the client. I couldn't find any working code that does this job.
Please, no more recommendation for Redis or channels documentation... or some random 3rd party libraries that lacks good documentation... It's easy to recommend but hard to implement. For example, I found someone recommending Snorky, but it really lacks documentation on how to implement it.
However, if there's a website that specifically does this job, I might take a look at it, even if it doesn't use Django-Channels.
consumers.py
import asyncio
from channels.consumer import AsyncConsumer
class ChatConsumer(AsyncConsumer):
async def websocket_connect(self, event):
self.connected = True
print("connected", event)
await self.send({
"type": "websocket.accept"
})
while self.connected:
await asyncio.sleep(2)
obj = # do_something (Ex: constantly query DB...)
await self.send({
'type': 'websocket.send',
'text': # obj,
})
async def websocket_receive(self, event):
print("receive", event)
async def websocket_disconnect(self, event):
print("disconnected", event)
self.connected = False
Javascript
var loc = window.location;
var wsStart = 'ws://';
if (loc.protocol == 'https:') {
wsStart = 'wss://'
}
var endpoint = wsStart + loc.host + loc.pathname;
var socket = new WebSocket(endpoint);
socket.onmessage = function(e){
console.log("message", e);
};
socket.onopen = function(e){
console.log("open", e);
};
socket.onerror = function(e){
console.log("error", e)
};
socket.onclose = function(e){
console.log("close", e)
};
All you need to do is just modify obj and send it. You can extend this function as much as you want. So, right now I'm interested in getting the latest inserted row in my PostgreSQL and injecting that row into my WebSocket. I can query my DB every 2 seconds as it was specified by await asyncio.sleep(2), and inject it into the Front-End socket.
Using channels==1.* and Django==1.* you can use the threading module for example:
# Some view.py
import threading
import time
class Publisher(threading.Thread):
def __init__(self, reply_channel, frequency=0.5):
super(Publisher, self).__init__()
self._running = True
self._reply_channel = reply_channel
self._publish_interval = 1.0 / frequency
def run(self):
while self._running:
self._reply_channel.send({'text': 'some data'})
time.sleep(self._publish_interval)
def stop(self):
self._running = False
publishers = {}
def ws_connect(message):
message.reply_channel.send({'accept': True})
publisher = Publisher(reply_channel=message.reply_channel)
publisher.start()
publishers[message.reply_channel] = publisher
def ws_disconnect(message):
publisher = publishers[message.reply_channel]
publisher.stop()
del publishers[message.reply_channel]
A little late to the party here, but when it comes to Django you should always try to do things "their way" first...
So, to do this I simply used Django Channels. My client sends a message to the server, which the server then responds to with the needed database info. It looks as follows:
class SettingsConsumer(WebsocketConsumer):
def connect(self):
self.accept()
def disconnect(self, close_code):
pass
def receive(self, text_data):
settings = get_settings(self.scope["user"])
self.send(
text_data=json.dumps(
{
"playMode": settings.play_mode,
"isRecording": settings.is_recording,
}
)
)
Now, as for the JS to trigger constant events... I simply use SetInterval to request updates from the consumer every .25s!
My logic is that it's the client so if it's doing a little extra work no biggie, as the server is gonna be responding anyways. The JS looks as follows...
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/macros/update/'
);
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
if(data["playMode"] == true) {
$('#playmode-checkbox').attr('checked', true);
} else {
$('#playmode-checkbox').attr('checked', false);
}
if(data["isRecording"] == true) {
$('#recording-checkbox').attr('checked', true);
} else {
$('#recording-checkbox').attr('checked', false);
}
};
chatSocket.onclose = function(e) {
console.error('Chat socket closed unexpectedly');
};
window.setInterval(function() {
chatSocket.send(JSON.stringify({
'message': "start"
}));
}, 250);
And yes, you can do more to make this more async-friendly and optimized. But, you asked for simple and working so I hope this helps!

django channels async process

I am still pretty new to django-channels and directly starting with channels 2.0 so diverse examples are still a bit hard to find yet. I am trying to understand how can I create an asynchronous process in a channel and make a js client listen to it?
while connecting to my consumer, I am checking for a running stream on a thread and try to send back predictions on the channel. This process is asynchronous but I am not sure how to properly use an AsyncConsumer or AsyncJsonWebsocketConsumer.
so far I have a simple consumer like this:
consumers.py
import threading
from sklearn.externals import joblib
from channels.generic.websocket import JsonWebsocketConsumer
from .views import readStream
class My_Consumer(JsonWebsocketConsumer):
groups = ["broadcast"]
the_thread_name = 'TestThread'
def connect(self):
the_thread = None
self.accept()
# check if there is an active stream on a thread
for thread in threading.enumerate():
if thread.name == self.the_thread_name:
the_thread = thread
break
if the_thread == None:
xsLog.infoLog('No Stream found yet...')
self.close()
else:
the_stream = readStream()
#...
the_estimator = joblib.load('some_file','r')
the_array = np.zeros(shape=(1,93), dtype=float)
self.predict(the_thread,the_stream,the_array,the_estimator)
def disconnect(self,close_code):
pass
async def predict(self,the_thread,the_stream,the_array,the_estimator):
while the_thread.isAlive():
sample = await the_stream.read()
if sample != [0.0,0.0,0.0] and 'nan' not in str(sample):
the_array = np.roll(self.the_array,-3)
the_array[0][-3] = sample[0]
the_array[0][-2] = sample[1]
the_array[0][-1] = sample[2]
new_val = await '{},{}'.format(the_estimator.predict(the_array)[0][0],the_estimator.predict(the_array)[0][1])
await self.send_json(new_val)
my js client is pretty simple and tries to listen:
const webSocketBridge = new channels.WebSocketBridge();
webSocketBridge.connect('some_route');
webSocketBridge.socket.addEventListener('open', function() {
console.log("Connected to WebSocket");
});
webSocketBridge.listen(function(message, stream) {console.log(message);});
EDIT
from the above mentionned question I tried the following simplified solution with an AsyncJsonWebsocketConsumer:
import threading
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from channels.db import database_sync_to_async
class My_Consumer(AsyncJsonWebsocketConsumer):
groups = ["broadcast"]
the_thread = None
the_data = None
#
#database_sync_to_async
def getUserData(self,the_user):
return models.UserData.objects.filter(user=the_user,datatype='codebooks')
#
async def connect(self):
await self.accept()
for thread in threading.enumerate():
if thread.name == 'some_thread_name':
self.the_thread = thread
break
if self.the_thread == None:
xsLog.infoLog('No Stream found yet...')
await self.close()
else:
the_data = await self.getUserData(the_user)
#
async def receive(self,content=None,text_data=None):
await self.predict(self.the_thread,self.the_data)
#
async def predict(self,the_thread,the_data):
"""..."""
while the_thread.isAlive():
# doing something here
new_data = data_from_thread + the_data
self.send_json(new_data)
#
async def disconnect(self,close_code):
await self.close()
and then I am sending once a message by the JS client to init the receive method of the consumer:
webSocketBridge.socket.addEventListener('open', function() {
console.log("Connected to WebSocket, Launching Predictions");
webSocketBridge.send('');
});
The issue here is that while the predict coroutine is launched it is blocking the whole receive one and my js client cannot receive any message through:
webSocketBridge.listen(function(message, stream) {
console.log(message.stream);
});
the disconnect coroutine is then also not recognized either.

Where do you pass the handler info in aiohttp test case client.request

I was trying AioHttp TestCase library and just using the sample code given in the docs but it says 404 when I run pytest filename.py.
I see some examples above too, first of all I don't understand the difference between test_client and self.client. Are they both doing the same thing but just different methods?
Also, in the below implementation, where do you pass the handler info? self.client does not accept handler param hello.
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
from aiohttp import web
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
from aiohttp import web
async def hello(request):
return web.Response(text='Hello')
class MyAppTestCase(AioHTTPTestCase):
async def get_application(self):
return web.Application()
#unittest_run_loop
async def test_example(self):
request = await self.client.request("GET", "/")
assert request.status == 200
text = await request.text()
assert "Hello, world" in text
test_client fixture from pytest should be just factory that make is the same as AioHTTPTestCase.client
When I look to code in TestClient it all got down to cal _request that looks like this:
async def _request(
self,
method: str,
str_or_url: StrOrURL, *,
params: Optional[Mapping[str, str]]=None,
data: Any=None,
json: Any=None,
cookies: Optional[LooseCookies]=None,
headers: LooseHeaders=None,
skip_auto_headers: Optional[Iterable[str]]=None,
auth: Optional[BasicAuth]=None,
allow_redirects: bool=True,
max_redirects: int=10,
compress: Optional[str]=None,
chunked: Optional[bool]=None,
expect100: bool=False,
raise_for_status: Optional[bool]=None,
read_until_eof: bool=True,
proxy: Optional[StrOrURL]=None,
proxy_auth: Optional[BasicAuth]=None,
timeout: Union[ClientTimeout, object]=sentinel,
verify_ssl: Optional[bool]=None,
fingerprint: Optional[bytes]=None,
ssl_context: Optional[SSLContext]=None,
ssl: Optional[Union[SSLContext, bool, Fingerprint]]=None,
proxy_headers: Optional[LooseHeaders]=None,
trace_request_ctx: Optional[SimpleNamespace]=None
) -> ClientResponse:
So you can pass these to client and it will make the call. data and json should be the same (expecting as in requests it is like so), json just dumps it.
So something like this should work. At least for me:
class BookingTest(AioHTTPTestCase):
async def get_application(self):
return await create_test_app()
#unittest_run_loop
async def test_create_booking(self):
data = {
"my_id": "1234"
}
# the post data should be in request.body
# (we use connexion and we have it parsed as an argument to handler named body)
resp = await self.client.request("POST", "/api/bookings", json=data)
assert resp.status == 201
#unittest_run_loop
async def test_get_booking(self):
booking_id = "1234"
url = f"/be/api/bookings/{booking_id}"
resp = await self.client.request("GET", url)
assert resp.status == 200