sending notification to one user using Channels 2 - python

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.

Related

Perform tasks when the user has responded to a recation, otherwise do nothing

I have a problem. I want that when the user writes a message, my bot should send a message. And once the user has responded to that message, the user should send further instructions.
I have the problem that when the user sends a message, the bot sends the message with the reactions, but as soon as the user sends a second message. The bot sends a message again.
The bot should therefore only send another message once the user has reacted. How can this be done?
I tried this with the responsed variable.
import asyncio
import discord
from discord.ext import commands
from datetime import datetime
class command(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.Cog.listener()
async def on_message(self, message):
if str(message.channel.type) == "private":
if message.author == self.bot.user:
return
else:
# check if channel exists
if (channel is None):
responsed = True
if(responsed):
responsed = False
...
await message.author.send("Hello")
# add reactions
reaction, user = await self.bot.wait_for('reaction_add', check=check) # check reactions
if(reaction.emoji == '✅'):
responsed = True
else:
pass # do nothing
else:
await message.author.send("How are you?")
async def setup(bot):
await bot.add_cog(command(bot))
User sends a Message
1.1. check if user reacted
1.2 if a message is sent and not reacted then pass
Bot replies with Hello -> Wait for reaction
2.1 bot adds reaction
2.2 if user sends a second message 2 will started again. That should be forbidden.
User Reacts
Bot send How are you
The problem with what you're trying to do, is that discord.py, and the discord API in general, is designed to run a bot on multiple servers, on multiple channels, etc.
This means that, if you want to forbid a user sending a second message, you have to specify what you mean with that. Not twice on the same channel? Not twice for the same user? Should there be some form of a timeout?
I see you're currently working only in private channels (which you can also check with if isinstance(message.channel, discord.abc.PrivateChannel) if you want to do that properly) In that case, you might want to restrict it like "not if in the last x messages in the private channel." You can do this with another guard clause at start, here shown with the entire list you're using:
if not isinstance(message.channel, discord.abc.PrivateChannel):
return
if message.author == self.bot.user:
return
previous_messages = await message.channel.history(limit=10).flatten()
if any([(message.content == "Hello" and message.author == self.bot.user) for message in previous_messages]):
return
... # Rest of the function
I personally don't think it's the cleanest, but it will at least work. (if I didn't make a typo somewhere...)
If I can give you one do-something-else bit of advice: use buttons and views instead. Those have this type of callback functionality build-in. :-)
I hope that helps! :-)

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

how to send albums as new messages in telethon

I want to send every new message from one channel to another.
But when there are albums in the message, they are sent separately to the destination channel.
How can I send them as an album (as one message)?
#client.on(events.NewMessage('your_channel'))
async def my_event_handler(event):
await client.send_message('my_channel', event.message)
And I used events.album instead of events.NewMessage, but it didn't work!
I find a simple way, maybe someone will use it
#client.on(events.Album(chats="your_channel"))
async def handler(event):
await client.send_message(
"my_channel", #output channel
file=event.messages, #list of messages
message=event.original_update.message.message, #caption
)

How to get a telegram private channel id with telethon

Hi can't figure out how to solve this problem, so any help will be really appreciated.
I'm subscribed to a private channel. This channel has no username and I don't have the invite link (the admin just added me).
Since I use this channel at work, to speed up the things I want to process the messages posted on the channel using Telethon.
The core of the program is:
#events.register(events.NewMessage(chats = my_private_channel))
async def handler(event):
#do things
The problem is that I am not able to filter the messages coming to that specific channel id. I get the error:
ValueError: Cannot find any entity corresponding to "0123456789"
I have tried different technique to obtain my channel Id but the error is always the same. In particular:
The channel is private so it has no username ("#blablabla")
I have no invite link
I have tried to process all incoming messages until the admin sent a message on the channel, print sender information and get the value from the "ID" key
I have tried to use telegram web and get the ID from the url (also adding -100 in front of it)
But when I put the ID in the parameter chats, I get always the error reported above.
Thanks in advance,
Have a nice day
if you have access to the channel, then it's shown in your chat list.
You have to loop through your chats checking their titles and then store the desired chat in a variable:
my_private_channel_id = None
my_private_channel = None
async for dialog in tg.client.iter_dialogs():
if dialog.name == "private chat name":
my_private_channel = dialog
my_private_channel_id = dialog.id
break
if my_private_channel is None:
print("chat not found")
else:
print("chat id is", my_private_channel_id)
Than you can filter messages sent to my_private_channel.
You can print all the dialogs/conversations that you are part of.
also you need to remove -100 prefix from the id you got like: -1001419092328 = 1419092328 (actual ID)
from telethon import TelegramClient, events
client = TelegramClient("bot", API_ID, API_HASH)
client.start()
print("🎉 Connected")
#client.on(events.NewMessage())
async def my_event_handler(event):
async for dialog in client.iter_dialogs():
print(dialog.name, 'has ID', dialog.id) # test ID -1001419092328
client.run_until_disconnected()
if you want to listen to a specific channel, you can use channel_id=1419092328. you will only receive messages that are broadcasted to it:
from telethon import TelegramClient, events
from telethon.tl.types import PeerChannel
print(f"👉 Connecting...")
client = TelegramClient("bot", API_ID, API_HASH)
client.start()
print("🎉 Connected")
#client.on(events.NewMessage(PeerChannel(channel_id=1419092328)))
async def my_event_handler(event):
msg = event.text
print(f"[M] {msg}")
client.run_until_disconnected()
You can't join a private channel without the invite link, nor can you get any information about it. It's private, as the name implies.

How to send a message on the bot startup to every server it is in?

I want to send an announcement to all the servers my bot is in. I found this on GitHub, but it requires a server ID and a channel ID.
#bot.event
async def on_ready():
server = bot.get_server("server id")
await bot.send_message(bot.get_channel("channel id"), "Test")
I found a similar question, but it is in discord.js. It says something with default channel, but when I tried this:
#bot.event
async def on_ready():
await bot.send_message(discord.Server.default_channel, "Hello everyone")
it gave me this error:
Destination must be Channel, PrivateChannel, User, or Object
First, to answer your question about default_channel: Since about June 2017, Discord no longer defines a "default" channel, and as such, the default_channel element of a server is usually set to None.
Next, by saying discord.Server.default_channel, you're asking for an element of the class definition, not an actual channel. To get an actual channel, you need an instance of a server.
Now, to answer the original question, which is to send a message to every channel, you need to find a channel in the server that you can actually post messages in:
#bot.event
async def on_ready():
for server in bot.servers:
# Spin through every server
for channel in server.channels:
# Channels on the server
if channel.permissions_for(server.me).send_messages:
await bot.send_message(channel, "...")
# So that we don't send to every channel:
break

Categories

Resources