Cannot send message to specific group in Django Channels - python

I have a websocket that receives a token as a query string. My custom middleware then decodes this token to find the username passed through this token.
The current user connecting to the websocket is then subscribed to a group named after their user_id. Let's assume UserA goes through this process initially and they are subscribed to group '1'
UserB then connects to the websocket following the same process. At this point I assume there are two groups; one named '1' and another names '2'.
Upon UserA triggering the send & receive method in the frontend, I have a consumer function that sends a message to the group with the user_id it receives ('2').
However, when this code runs, I don't seem to be sending a message to any group. I am not getting any error message (I can see the message I want to send being printed on my terminal).
However when I open two browsers; one with UserA and the other with UserB, when I connect to the websocket and trigger send/receive I don't see the message I am sending being console logged on either browsers.
Here is the code for the consumer class handling the connect, send and receive methods:
class PracticeConsumer(AsyncConsumer):
async def websocket_connect(self, event):
username = self.scope['user']
username_id = str(await self.get_user(username))
await self.channel_layer.group_add('{}'.format(username_id), self.channel_name)
await self.send({"type": "websocket.accept", })
async def websocket_receive(self, event):
received = event["text"]
user_and_id = received.split()
username = user_and_id[0]
user_id = str(await self.get_user(username))
sleep(1)
await self.channel_layer.group_send(
'{}'.format(user_id),
{
"type": "send.message",
"message": "!!!!the websocket is sending this back!!!!",
},
)
async def websocket_disconnect(self, event):
print("disconnected", event)
async def send_message(self, event):
message = event['message']
self.send({
'message': message
})
#database_sync_to_async
def get_user(self, user_id):
try:
return User.objects.get(username=user_id).pk
except User.DoesNotExist:
return AnonymousUser()

Related

Why doesn't FSM(state) work with channel_post_handler?

When you click on the button, the bot sends a message asking you to specify the name(I'm sending the name in response) by writing message_data (for example), then the post handler in the channel should be called via state, but this does not happen (the code does not send the last print "test handler"). If you use it without state, everything works, just as if you use it in the bot itself via message_handler, everything works too. Why doesn't it work and how can I use FSM for messages in the channel, because I need to save several consecutive messages from there when I press the button.
#dp.callback_query_handler(text='test_video')
async def test_video(call: types.CallbackQuery, state: FSMContext):
message_data = await bot.send_message(chat_id=call.message.chat.id, text='OK now send me the title', reply_markup= ikb_back)
async with state.proxy() as data:
data['message_data'] = message_data
print("test set") # sends
await astate.Admin_State.test_video.name.set()
#dp.channel_post_handler(state=astate.Admin_State.test_video.name)
async def print_test_video(message: types.Message, state: FSMContext):
print("test handler") # does not send

Telethon and messages not received from some channels

I have a software which monitors multiple telegram channels and performs actions based on the received messages.
The problem is that in the past month it stopped received messages from some of the channels.
I'm using one of the examples in the official repositories with some additions.
#callback
async def check_chat(self, event=None):
"""
Checks the input chat where to send and listen messages from.
"""
if self.me is None:
return # Not logged in yet
chat = self.chat.get().strip()
try:
chat = int(chat)
except ValueError:
pass
async def on_msg_received(self, event):
global folders_list
now = datetime.now()
hour = now.hour
minute = now.minute
nome_chat = ''
try:
async for dialog in self.cl.iter_dialogs():
if(dialog.id == event.chat_id):
nome_chat = dialog.name
except:
logging.warning('error at on_msg_received')
logging.warning('------- chat = %s', nome_chat)
testo = event.raw_text
With some chats I don't get any activity at all, I expect to get either an error or the chat name.
What could be the issue here?
Does it make any difference if the channel is sending messages from telegram web or desktop?

How to send a message to all consumers using Django Channels 3.0.0?

I am trying to develop an interface that contains a section that shows all active users. So, when a user connects to the WebSocket I want to send data to all connected consumers.
Currently, I wrote a code that when the user connects it sends data to the connected user only. I want somehow to make the message be sent to all active users/consumers.
This is the path to my WebSocket handler/view
path('test_ws/', websocket.TestConsumer.as_asgi()),
And here is the handler
class NewsMentionConsumer(AsyncWebsocketConsumer):
groups = ["newsmention1"]
channel_name = 'news_mention'
active_users = []
async def connect(self):
await self.channel_layer.group_add(self.groups[0], self.channel_name)
await self.channel_layer.group_send(self.groups[0], {
'type': 'send_updates',
'text': json.dumps({
'id': self.scope['user'].id,
'username': self.scope['user'].username,
'active_users': self.active_users
})
})
await self.accept()
self.active_users.append(self.scope['user'])
async def send_updates(self, event):
# TODO: Make this send to all users/consumers
await self.send(event["text"])
I am facing a problem understanding the examples in django.channels docs and tried using send_group function but it doesn't really work. Any suggestions?
EDIT: add all users to the same group with unique names.
async def connect(self):
await self.channel_layer.group_add(self.groups[0], self.unique_name());
await self.channel_layer.group_send(self.groups[0], { ... });
group_send still sends the message to the requested user only.
EDIT 2: after adding the init function.
Now, I have a channel_name but I am getting a new error when I am trying to send the data to all consumers in the group:
class NewsMentionConsumer(AsyncWebsocketConsumer):
group = "newsmention1"
active_users = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
async def connect(self):
await self.channel_layer.group_add(
self.group,
self.channel_name,
)
await self.channel_layer.group_send(self.group, {
'type': 'send_updates',
'id': self.scope['user'].id,
'username': self.scope['user'].username,
'some_data_to_all_clients': some_data_to_all_clients()
})
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.group, self.channel_name)
async def send_updates(self, event):
await self.channel_layer.send_group(self.group, event)
'RedisChannelLayer' object has no attribute 'send_group'
Any suggestions?
EDIT 3:
async def connect(self):
await self.accept()
await self.channel_layer.group_add(
self.group,
self.channel_name,
)
self.active_users.append({
'id': self.scope['user'].id,
'username': self.scope['user'].username
})
await self.channel_layer.group_send(self.group, {
'type': 'send_updates',
'id': self.scope['user'].id,
'username': self.scope['user'].username,
'some_data_to_all_clients': some_data_to_all_clients()
})
async def send_updates(self, event):
await self.send(json.dumps(event))
This is a bug of Django Channels 3.0.0. It was fixed in 3.0.1.
You shouldn't set a static channel name as it is used to identify channels and so have to be unique. The usual practice is to use the channel names generated by Django Channels.
Regarding sending an event to all connected users, you just need to have a group to which you add all channels as they connect and then send the message there. What you are doing already looks similar except the channel name part.

My chat consumer is not working django-channels?

I am tryna make a one-one chat application but my chat socket get's disconnected immediately when i send the data to it.I think the problem is in my asynchronous recieve function of my consumer?
It's not giving me any reason for the error?The socket disconnects silently
Here's the recieve handler
async def receive(self, text_data):
data = json.loads(text_data)
text = data['message']
room_name = data['room_name']
username = data["username"]
only_one_user = False
profile = self.scope["user"].profile
# GET THE ROOM AND THE PROFILE
room_obj = await database_sync_to_async(Room.objects.get)(pk=self.room_name)
other_user = await database_sync_to_async(room_obj.other_user)(profile)
# CREATE AND ADD THE MESSAGE TO THE ROOM
message = Message.objects.create(author=profile,text=text,to=other_user.user.username)
room_obj.messages.add(message)
room_obj.updated = timezone.now()
room_obj.save()
profile = self.scope["user"].profile
clients_connected = await database_sync_to_async(Websocketclient.objects.filter)(room=int(self.room_name))
if clients_connected.count() < 2:
only_one_user = True
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'data': {"text":text,"pk":room_obj.pk,"author":{"username":message.author.user.username,"image":str(message.author.image)},"only_one_user":only_one_user}
}
)
Also how can i know what exactly is the error in my async code if i get any in future work...
Message.objects.create should also be wrapped in database_sync_to_async and room_obj.save()
in short any DB operation needs to be wrapped in database_sync_to_async.
As to why the error is be swallowed... welcome to async python :)
put a try except around the body of your function and print out the exception you get to test that the error is a this point.

sending notification to one user using Channels 2

I want to send notification to specific authenticated user using Channels 2.
In below code i am sending notification as a broadcast instead of that i want to send notification to a specific user.
from channels.generic.websocket import AsyncJsonWebsocketConsumer
class NotifyConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
await self.accept()
await self.channel_layer.group_add("gossip", self.channel_name)
print(f"Added {self.channel_name} channel to gossip")
async def disconnect(self, close_code):
await self.channel_layer.group_discard("gossip", self.channel_name)
print(f"Removed {self.channel_name} channel to gossip")
async def user_gossip(self, event):
await self.send_json(event)
print(f"Got message {event} at {self.channel_name}")
Most users new to Django-channels 2.x face this problem. let me explain.
self.channel_layer.group_add("gossip", self.channel_name) takes two arguments: room_name and channel_name
When you connect from your browser via socket to this consumer, you are creating a new socket connection called as channel. So, when you open multiple pages in your browser, multiple channels are created. Each channel has a unique Id/name : channel_name
room is a group of channels. If anyone sends a message to the room, all channels in that room will receive that message.
So, if you need to send a notification/message to a single user, then you must create a room only for that particular user.
Assuming that current user is passed in the consumer's scope.
self.user = self.scope["user"]
self.user_room_name = "notif_room_for_user_"+str(self.user.id) ##Notification room name
await self.channel_layer.group_add(
self.user_room_name,
self.channel_name
)
Whenever you send/broadcast a message to user_room_name, It will only be received by that user.

Categories

Resources