Discord.py modify a message based on reaction - python

I have an issue and I can't understand how to do it.
Basically I have a bot that send a message if he detect a word.
Now I wanted to add the possibility to modify that message based on who add a reaction (the bot add 3 reactions at each message he send).
So far, it's working, my bot respond and add the 3 reactions, but now I don't understant how to make my bot edit his message (I don't even manage to make the bot say who click on a reaction) and also remove the reaction.
I've read the docs, read a lot of forum and I can't find a way to make it work...
Please help =)
blue = '<:blue:1033370324135333898>'
red = '<:red:1033370379663704148>'
yellow = '<:yellow:1033370423078952970>'
#client.event
async def on_message(message):
if 'ping' in message.content.lower():
if message.channel.id == 1032275628470308895:
channel = client.get_channel(1032275628470308895)
react = await channel.send('pong!')
await react.add_reaction(blue)
await react.add_reaction(red)
await react.add_reaction(yellow)
#client.event
async def on_raw_reaction_add(payload):
# and then I don't know what to do, everything I've tried doesn't work
# I wanted to modify the message to (for example "{user} said blue pong!" and remove the reaction the user just use, and just keep the 3 from the bot)
# And then if another user use the yellow reaction, the bot modify his message to for example "{user] said yellow pong!"
# That could be great if the message was edited but keeping what was said before (in that case "pong! \n{user} said blue pong! \n{user} said yellow pong!")
# And last thing, if possible I want the bot to remove all reactions including his after 30mins

https://discordpy.readthedocs.io/en/stable/api.html?highlight=on_raw_reaction_add#discord.RawReactionActionEvent
The event on_raw_reaction_add returns a payload which is a RawReactionActionEvent class.
From this payload you can get the message_id which can be used to fetch the message (using for example discord.utils) and then you can edit the message using the class method edit
https://discordpy.readthedocs.io/en/latest/api.html#discord.Message.remove_reaction
The discord message object has a method called remove reaction that can remove reactions when you pass it an emoji and member object.
https://discordpy.readthedocs.io/en/latest/api.html#discord.Reaction.remove
You can also use the class method remove from the reaction object itself
These resources should be enough for you to code a solution to your problem :)

Alright I think I'm done!
Now my bot answer to a specific word, and add reactions to his message.
If someone react to it, my bot remove the user reaction, and edit his own message, keep his previous text and add something else at the end (in that case):
"Previous message"
"user" said it's blue pong
And then after some times, the bot remove all the reactions
Here the code if someone want to do that someday..
blue = '<:blue:1033370324135333898>'
red = '<:red:1033370379663704148>'
yellow = '<:yellow:1033370423078952970>'
#client.event
async def on_message(message):
if 'ping' in message.content.lower():
if message.channel.id == 1032275628470308895:
channel = client.get_channel(1032275628470308895)
react = await channel.send('pong!')
await react.add_reaction(blue)
await react.add_reaction(red)
await react.add_reaction(yellow)
await asyncio.sleep(10)
await react.clear_reactions()
#client.event
async def on_raw_reaction_add(payload):
channel = client.get_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
user = client.get_user(payload.user_id)
if str(payload.emoji) == blue:
if str(payload.user_id) == "bot_ID": # bot_ID here, don't want the bot trigered by his own reaction
return
await message.remove_reaction(payload.emoji, user)
await message.edit(content=f"{message.content} \n{user.name} said it's a blue pong !")
Thanks #hexxx for your help, it's was my first time programming something ^^

Related

Send sequence of Embedded-messages in Discord.py

I'm trying to make a welcome bot that is going to send the first message, when the person hits a button/emoji below, the next message will come. Going to post the first message, then the next message underneath. Right now I'm using a so to start it you need to write test.
Any suggestion on how I can make it so?
#client.event
async def on_message(message):
if message.content == 'test':
how_channel = client.get_channel(CHANNELNAME)
myEmbed = discord.Embed(title="**Now then, how does everything work? Here's a quick tour!**",
description="You can see your progression as a percentage as you progress, click the button below to continue trough!",
color=0x00ff00)
myEmbed.add_field(name="It should only take a few minutes!", value="Press the button below to continue")
myEmbed.set_footer(text="Christer - How does it work", icon_url="https://cdn.icon-icons.com/icons2/2619/PNG/512/among_us_discord_icon_156922.png")
await how_channel.send(embed=myEmbed)
So that was the first message that is getting sent, so I want the underneath message to be sent after a button or emoji has been pressed.
#client.event
async def on_message(message):
if message.content == 'test':
how_channel = client.get_channel(CHANNELNAME)
myEmbed = discord.Embed(title="News Feed",
description="The #announcements will be your most important channel here! You'll be able to keep up to date with everything which is happening within the server so you'll never miss a beat!",
color=0x00ff00)
myEmbed.add_field(name="It should only take a few minutes!", value="Press the button below to continue")
myEmbed.set_footer(text="Christer - How does it work", icon_url="https://cdn.icon-icons.com/icons2/2619/PNG/512/among_us_discord_icon_156922.png")
await how_channel.send(embed=myEmbed)
If you want to use the buttons, you need to specify the lib you use to implement them so we can help you. For reactions, you need to add one via add_reaction (https://discordpy.readthedocs.io/en/stable/api.html#discord.Message.add_reaction) and then redirect the on_reaction_add event to your control with wait_for (https://discordpy.readthedocs.io/en/stable/api.html#discord.Client.wait_for), as you probably do for messages. Don't forget to set a check in your wait_for.
Example :
message = await ctx.send(first_embed)
await message.add_reaction("🟢")
await client.wait_for('reaction_add', check = lambda reaction, user: user == message.author and str(reaction.emoji) == "🟢")
await ctx.send(second_embed)

Discord Bot Delete Messages in Specific Channel

TIA for your help and apologies, I'm a newbie so this may be a foolish question. I've searched through and can't find anything specific on how to make a discord bot (in Python) delete messages only within a specific channel. I want all messages sent to a specific channel to be deleted, their contents sent via PM to the user and the user's role changed.
Is there a way to use on_message and specify in an specific channel?
#client.event
async def on_message(message):
user = message.author
if message.content.startswith("Cluebot:"):
await message.delete()
await user.send("Yes?")
await user.remove_roles(get(user.guild.roles, "Investigator"))
The problem I'm having is I'm also using commands which now no longer work because the bot only responds if the message begins with "Cluebot:" Can I have the bot only look for "Cluebot:" in a specific channel?
Is it possible to make this work through a command instead of an event?
Thanks for your help. :)
The problem I'm having is I'm also using commands which now no longer work because the bot only responds if the message begins with "Cluebot:" Can I have the bot only look for "Cluebot:" in a specific channel?
About this problem the clue is:
await bot.process_commands(message)
as docs says Without this coroutine, none of the commands will be triggered.
about the main question you could try using get_channel by ID and then purge it:
eg.
#client.event
async def on_message(message):
purgeChannel = client.get_channel([CHANNEL ID]])
await purgeChannel.purge(limit=1)
you can add check to purge() to check for deleting specific message:
eg.:
def check(message):
return message.author.id == [author's ID]
#client.event
async def on_message(message):
purgeChannel = client.get_channel([CHANNEL ID]])
await purgeChannel.purge(limit=1, check=check)

Discord.py | Avoid getting more than one reaction by a same user to a message sent by a bot

Im trying to make a command that will send in an embed and that has two reactions, a tick and a cross, i want to make the user just react to one of the reaction, rather than reacting to both of them. I also need some help in making a system to ensure that the person reacting has a specific role. Any help would be highly appreciated!
This is possible using the on_raw_reaction_add() event.
#bot.event
async def on_raw_reaction_add(payload): # checks whenever a reaction is added to a message
# whether the message is in the cache or not
# check which channel the reaction was added in
if payload.channel_id == 112233445566778899:
channel = await bot.fetch_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
# iterating through each reaction in the message
for r in message.reactions:
# checks the reactant isn't a bot and the emoji isn't the one they just reacted with
if payload.member in await r.users().flatten() and not payload.member.bot and str(r) != str(payload.emoji):
# removes the reaction
await message.remove_reaction(r.emoji, payload.member)
References:
on_raw_reaction_add()
Message.remove_reaction()
Reaction.users()
User.bot
RawReactionActionEvent - (the payload)
Message.reactions
Client.fetch_channel()
TextChannel.fetch_message()
I think you can store the userId or something that is unique for all the user.
And then create a function check this ID appeared more than once or other logic.
https://discordpy.readthedocs.io/en/latest/api.html#userUser API
Here, you can get user ID from this object.
Further more, you can get author id from message your client receive.
def on_message(message):
print(message.author.id)

Deleting channels, and only running code if a USER, not BOT reacts to a message

I'm trying to make a ticketing system in which a user does .ticket.
I'm having an issue where 'bot' doesn't have the attribute 'delete_channel', and I also want to make it so the bot ignores the reaction if a bot reacts to the message, but acknowledges that a normal guild member has reacted to it.
Here's my code:
#bot.command()
async def ticket(ctx):
global ticket_channel
name = "tickets"
category = discord.utils.get(ctx.guild.categories, name=name)
guild = ctx.message.guild
ticket_id = randint(0, 100)
ticket_channel = await guild.create_text_channel(f"ticket-0{ticket_id}", category=category)
embed = discord.Embed(title="Tickets", description="Support will be with you shortly.\nTo close this ticket, react with :lock:.")
message = await ticket_channel.send(embed=embed)
await message.add_reaction(emoji="\N{LOCK}")
#bot.event
async def on_reaction_add(reaction: discord.Reaction, user: discord.Member):
if reaction.message.channel != ticket_channel:
return
if reaction.emoji == "\N{LOCK}":
await bot.delete_channel(ticket_channel)
I've been trying for a while to figure out the issue, but I'm clueless.
Seems like you're asking several questions here:
I'm having an issue where 'bot' doesn't have the attribute 'delete_channel'
The bot does not have a delete_channel() function. However, a Discord.TextChannel class has a .delete() function (shown in the docs).
and I also want to make it so the bot ignores the reaction if a bot reacts to the message
Alternative 1
Every user, including bot users, have the attribute .bot. You can use that to check if the user is a bot, and if so, return the function early.
#bot.event
async def on_reaction_add(reaction, user):
if user.bot: return
# Code goes here
Note that this listens for all reactions, not just that specific message; thus comes:...
Alternative 2
As Patrick Haugh mentions, you could use the discord.Client.wait_for() function (doc link) and parse a function to as the check argument of the function.
reaction, user = bot.wait_for('reaction', check=lambda reac: reac.author == ctx.author)
*Note that this approach doesn't add this code under any event (as in the first alternative), except the command event. It will only be run once per command received unless put in a loop of some sorts.

Stopping a discord bot from responding to itself

I'm really new to coding, and I'm just wondering if I can get some help with a discord bot I've based off of Frost Bot.
The idea is pretty simple, Frost will respond to users, but I've removed the need to mention it to get a response.
I have a twin bot, too, and the idea is to get them to converse, which they do, however it seems that they're trying to respond to what they themselves have said, which causes a backlog of replies to come out.
Here is the bulk of the code, written in Python- if that matters.
#client.event
async def on_ready():
print('Logged in as '+client.user.name+' (ID:'+client.user.id+') | '+str(len(client.servers))+' servers')
await client.change_presence(game=discord.Game(name='chat with me!'))
#client.event
async def on_message(message):
if not message.author.bot == client.user:
await client.send_typing(message.channel)
txt = message.content.replace(message.server.me.mention,'') if message.server else message.content
r = json.loads(requests.post('https://cleverbot.io/1.0/ask', json={'user':user, 'key':key, 'nick':'frost', 'text':txt}).text)
if r['status'] == 'success':
await client.send_message(message.channel, r['response'] )
Any help would seriously be appreciated, as I'm incredibly new to scripting/coding and have spent a few hours already trying to research a solution.
Do a message.author.id == otherBotID: #stuff instead.
If you have more than one other bot to have a conversation with, switch to a NOT operator and place it as your own bot's ID, then check if the message author is a bot.
EDIT
Replace it in if not message.author.bot == client.user:.
Originally, you were checking if the message's author is not a user. And if it was not a user you would do stuff.
But since your own bot is not a user itself, the if statement would pass too when your bot sends a message (and recieves it's own message).
Hence we are placing a if-statement to check if the bot's ID equals to the targeted bot's ID.

Categories

Resources