How do you add a reaction to the response of a slash command? - discord.py

I'm facing problems trying to add a reaction to the bot's response when it responds to a slash command I entered.
#bot.slash_command(name='test', description="Test.")
async def test(ctx):
msg = await ctx.send("Hello.")
await msg.add_reaction('🤖')
As you can see it's supposed to add a reaction to it's own message.
But I get this error:
nextcord.errors.ApplicationInvokeError: Command raised an exception: AttributeError: 'PartialInteractionMessage' object has no attribute 'add_reaction'
Please tell me how do I add a reaction to a slash command.

Explanation
As per the error, ctx.send is returning a PartialInteractionMessage. From the docs:
This does not support most attributes and methods of nextcord.Message. The fetch() method can be used to retrieve the full InteractionMessage object.
Code
#bot.slash_command(name='test', description="Test.")
async def test(ctx):
msg = await ctx.send("Hello.")
full_msg = await msg.fetch()
await full_msg.add_reaction('🤖')

Related

Discord.py 2.0 - Button showing before message finishes editing view

I'm hosting my bot online and sometimes messages take time to edit their own View components which is fine. The problem is when i modify a view by calling
await message.edit(view=...)
, the new button/select components are displayed instantly but their callbacks are not operational because the message editing is taking some time to complete. Thus, unknown interaction error tends to occur when clicking on the button a little too early, the callbacks are not being called, and I need to wait to re-click.
My question is : Is it possible to wait for a message.edit() to fully complete before showing the buttons, or is there another way to solve this issue?
Code sample :
async def throw_dice(self,ctx):
try :
superself = self
async def action(superself):
...
if isinstance(self.current,PlayerDiscord) :
class myButton(ui.Button):
def __init__(self,label,style,row=None):
super().__init__(label=label,style=style,row=row)
async def callback(self,interaction):
await interaction.response.defer()
nonlocal superself
if interaction.user.id==superself.current.member.id:
self.view.stop()
await superself.msg_play.edit(view=None)
await action(superself)
self.view = FRPGame.myView(ctx,self) #Create new view
self.view.add_item(myButton("\U0001F3B2",style=discord.ButtonStyle.primary))
#self.msg_play stores the message
await self.msg_play.edit(content=self.content,view=self.view) #<-- problem is this single line
else :
...
except BaseException :
traceback.print_exc()
Please send your entire code so we can see what could be done
#code here
asyncio.sleep(10) #Will wait 10 seconds before sending
You can use the wait_for method to wait for the message to be edited. You can then use the message.edit method to edit the message and add the buttons.
import discord
from discord.ext import commands
from discord_components import DiscordComponents, Button, ButtonStyle
bot = commands.Bot(command_prefix="!")
DiscordComponents(bot)
#bot.command()
async def test(ctx):
message = await ctx.send("Hello")
await message.edit(content="Hello World")
await message.edit(components=[
Button(style=ButtonStyle.red, label="Red"),
Button(style=ButtonStyle.green, label="Green"),
Button(style=ButtonStyle.blue, label="Blue")
])
bot.run("token")
This uses discord-components, which you can get here:
https://devkiki7000.gitbook.io/discord-components

Using CTX in on_reaction_add event -- Discord.py

async def on_reaction_add(reaction, user):
emoji = reaction.emoji
if emoji == "💌":
await user.channel.send("HI")
I got problem here with user.
I want to use here with ctx like ctx.channel.send.
but also it occured error how to use ctx in here?
Instead of using the on_reaction_add event, it's better in this case to use a wait_for command event. This would mean the event can only be triggered once and only when the command was invoked. However with your current event, this allows anyone to react to a message with that emoji and the bot would respond.
By using client.wait_for("reaction_add"), this would allow you to control when a user can react to the emoji. You can also add checks, this means only the user would be able to use the reactions on the message the bot sends. Other parameters can be passed, but it's up to you how you want to style it.
In the example below shows, the user can invoke the command, then is asked to react. The bot already adds these reactions, so the user would only need to react. The wait_for attribute would wait for the user to either react with the specified emojis and your command would send a message.
#client.command()
async def react(ctx):
message = await ctx.send('React to this message!')
mail = '💌'
post = '📮'
await message.add_reaction(mail)
await message.add_reaction(post)
def check(reaction, user):
return user == ctx.author and str(
reaction.emoji) in [mail, post]
member = ctx.author
while True:
try:
reaction, user = await client.wait_for("reaction_add", timeout=10.0, check=check)
if str(reaction.emoji) == mail:
await ctx.send('Hi you just received')
if str(reaction.emoji) == post:
await ctx.send('Hi you just posted...')
You need to use reaction.message.channel.send
async def on_reaction_add(reaction, user):
emoji = reaction.emoji
if str(emoji) == "💌": await reaction.message.channel.send("HI")

Discord.py react to bot own messages

I am trying to react to my bot own messages. But it does not work.
if args[0] == "solido":
reaction = "👍"
sent = await ctx.message.channel.send(client.get_channel("795387716967333889"), embed=embed)
# await message.add_reaction(sent, emoji = "\U0001F44D")
await ctx.message.add_reaction(reaction)
if I use this type of code, the bot will react to MY message commands. But if i dont use the ctx method the bot will give me this error: NameError: name 'message' is not defined
How could I fix?
using await sent.add_reaction(reaction) instead of await ctx.message.add_reaction(reaction) should work

Discord.py sending an embed without the use of add fields

I am trying to get a message from a user, and then sending it to a specific text channel. The message shall be in an embed. But I don't like how it looks when you have a bunch of fields, I would like the description in the discord.Embed() to hold the text content. But it is giving me an error
TypeError: Object of type Message is not JSON serializable
This is my code:
class Changelog(commands.Cog):
def __init__(self, client):
self.client = client
#commands.Cog.listener()
async def on_ready(self):
print('Changelog is loaded')
#commands.command()
async def changelog(self, ctx):
changelog_channel = self.client.get_channel(759547196433104956)
await ctx.send("`Message: `")
message = await self.client.wait_for('message', check=lambda message: message.author == ctx.author, timeout=300)
embed = discord.Embed(title="Changelog", description=message, color=0)
await changelog_channel.send(embed=embed)
def setup(client):
client.add_cog(Changelog(client))
You're not getting this error because you're not adding fields, but because you're trying to put a message instance in the description, while you should be putting that message's content in there instead. The description can only take strings as it's value.
embed = discord.Embed(title="Changelog", description=message.content, color=0)
wait_for("message") returns a discord.Message instance. A Message holds that message's content, id, author, channel, and way more inside. If you only want to get the text the user sent, you need the content attribute.
More info on what a Message can do can be found in the API docs.

Using Adf.ly with Discord.py

So I am using MurkAPI and had gotten help with this but I cannot seem to get it so when someone does $adfly and a url which is a adfly shortend link, the bot returns the link through the API and into the bot. This is the current code I have.
#commands.command(pass_context=True)
async def adfly(self, ctx):
async with aiohttp.ClientSession() as session:
await self.client.say(await fetch_adfly(session))
async def fetch_adfly(session):
async with session.get(adfly_url) as response:
return await response.text()
I have the MURKKEY working, however, I cannot get the URL part working.
adfly_url = 'https://murkapi.com/adfly.php?key={}&url={}'.format(MURKKEY)
If fetch_adfly is meant to handle transforming your url into an adfly url, you want the session in that function. Instead of passing a session, we want to pass the url that we want transformed. That might give us something like:
async def fetch_adfly(url):
async with aiohttp.ClientSession() as session:
adfly_url = 'https://murkapi.com/adfly.php?key={}&url={}'.format(MURKKEY, url)
async with session.get(adfly_url) as response:
return await response.text()
Then, you want to add a url parameter to your command and pass it to your function:
#commands.command(pass_context=True)
async def adfly(self, ctx, url):
await self.client.say(await fetch_adfly(url))
FWIW, I would suggest validating URLs.

Resources