My chat consumer is not working django-channels? - python

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.

Related

How do I make my discord bot send this message to a specific channel?

Here is my code, I need it to send the response to a specific channel. Can't seem to find how to do it anywhere in the API.
#bot.tree.command(name='training', description='Set up a training date & announce it to everyone in the server')
#app_commands.describe(date = "Date")
#app_commands.describe(time = "Time")
#app_commands.describe(location = "Location")
async def say(interaction: discord.Interaction, date: str, time: str, location: str):
await interaction.response.send_message(f"{interaction.user.mention} is training at {location} on {date} at {time}")
I've tried changing the "interaction.response.send_message" into "channel.send" but that gave back an error with the atr: send
You need to have the channel ID somewhere - and then fetch that channel object when the interaction is invoked and then send the message.
The docs:
Interaction object
Guild object
Channel object
interaction response object
MY_CHANNEL_ID = 123455678
#bot.tree.command(name='training', description='Set up a training date & announce it to everyone in the server')
#app_commands.describe(date = "Date")
#app_commands.describe(time = "Time")
#app_commands.describe(location = "Location")
async def say(interaction: discord.Interaction, date: str, time: str, location: str):
my_channel = await interaction.guild.fetch_channel(MY_CHANNEL_ID)
# could also do it this way if the command can be used outside a guild context
# my_channel = await bot.fetch_channel(MY_CHANNEL_ID)
await my_channel.send(f"{interaction.user.mention} is training at {location} on {date} at {time}")```
# make sure we notify the user that we did what they wanted to do and respond
await interaction.response.send_message("Done the thing", ephemeral=True)
To get the Channel ID follow this guide and right-click on the channel you want to send the message to.

How would I write a bot which sends it's owner a direct message whenever a target user changes their custom status?

Here's what I have so far:
import discord
client = discord.Client(intents=discord.Intents.default())
token = 'PRETEND MY BOT TOKEN IS HERE'
#client.event
async def on_ready():
print(f'{client.user} has connected to Discord!')
#client.event
async def on_user_update(before, after):
if after.id == PRETEND THE TARGET USERS ID IS HERE:
if before.status != after.status:
owner = client.get_user(PRETEND THE BOT OWNERS ID IS HERE)
await owner.send(f'{after.name} has changed their status from {before.status} to {after.status}')
client.run(token)
It just doesn't seem to be working.
Here's what I've tried so far:
Making it so that instead of sending a direct message to the bot owner, the message is instead printed in the terminal. Didn't seem to work.
Using the term 'activity' instead of 'status', though I'm not sure if that makes a difference.
Using on_user_update, on_member_update, and on_presence_update. To be honest I don't know the difference between the three and which I should be using.
Based on the on_ready() function, the bot seems to be up and running. I suppose I just can't get it to detect the change in custom status. Also, I'm not sure if I'm taking the right approach when it comes to sending a direct message.
I believe this should work:
import discord as dc
class MyClient(dc.Client):
ownerid = <your id here>
targetid = <target id here>
async def on_ready(self):
print(f'Logged on as {self.user}!')
async def on_presence_update(self, before: dc.Member, after: dc.Member):
if before.id != self.targetid:
return
owner = await self.fetch_user(self.ownerid)
msg = ""
# If a user changed their name or discriminator
if before.name != after.name or before.discriminator != after.discriminator:
msg += f"{before.name}#{before.discriminator} has changed their name to {after.name}#{after.discriminator}\n"
# If a user changed their status (Online, Idle, Do not disturb, Invisible)
if before.status != after.status:
msg += f"{after.name}#{after.discriminator} has changed their status to {after.status}\n"
# If a user changed their activity (The emoji with some text)
if before.activity != after.activity:
msg += f"{after.name}#{after.discriminator} has changed their activity to:\n{after.activity}"
if msg != "":
await owner.send(msg)
intents = dc.Intents.default()
intents.members = True
intents.presences = True
client = MyClient(intents=intents)
client.run('<your token here>')
You just need to use the on_presence_update event and its required intents (members and presences).

Cannot send message to specific group in Django Channels

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()

Waiting for message to be sent, then storing the last message as a variable. Discord.py

I'm trying to write some code that sends a message, then waits for a response, then stores that response as a variable.
the code that I have so far looks like this
import discord
global sMessage
sMessage = message.content.replace('`', '')
client.wait_for(sMessage)
channel = client.get_channel("(the id of my channel)")
msgtest = channel.history(limit = 1)
await message.channel.send(msgtest)
this code, unfortunately, doesn't work. if anyone knows how I would go about getting this code to work, I'd appreciate it.
( Discord.py )
That is wrong use. You can see the official reference.
msg = await client.wait_for('message')
You can store the message like any other variable:
message = await client.wait_for('message')
You can add a check function to make sure the message is sent by the user that used the command
def check(m):
return m.author == ctx.author
message = await client.wait_for('message', check=check)

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.

Categories

Resources