Discord bot reading reactions - python

I need to implement some of the feature and one of the feature is implementing polls type feature. Can't use public discord bots due to some policies so we have to implement something on my own. Did some research yesterday and was able to make basic bot using python3 and commands api from discord.ext. Now what i need to figure out is:
Read reactions added by a user to a message?
Create a message with reactions (like bots which create reaction polls?)
Pin a message?
I believe from ctx i can get user tags (admin etc). Is there a better way to do so?
Couldn't find anything helpful on Commands reference page or probably i am looking at wrong documentation. any help would be appreciated.
thanks
Updated: Thanks guys. now i am stuck at how to add emoji, here is my code
poll_emojis = {0: ':zero:', 1: ':one:', 2: ':two:', 3: ':three:', 4: ':four:'}
#client.event
async def on_message(message):
if message.author == client.user:
return
if message.content.startswith('$create_poll'):
poll_content = message.content.split('"')
poll_text = poll_content[1]
poll_options = []
poll_option_text = ''
count = 0
for poll_option in poll_content[2:]:
if poll_option.strip() != '':
poll_options.append(poll_option)
poll_option_text += '{0}: {1}\t'.format(poll_emojis[count], poll_option)
count += 1
posted_message = await message.channel.send('**{0}**\n{1}'.format(poll_text, poll_option_text))
count = 0
for poll_option in poll_options:
await posted_message.add_reaction(Emoji(poll_emojis[count]))
count += 1

As an aside, given you're starting this project, and are already using the rewrite documentation, make sure you're using the rewrite version. There are some questions on here about how to make sure, and how to get it if you're not, but it's better documented and easier to use. My answers below assume you are using discord.py-rewrite
Message.reactions is a list of Reactions. You could get a mapping of reactions to their counts with
{react.emoji: react.count for react in message.reactions}
You could react to the message immediately after posting it:
#bot.command()
async def poll(ctx, *, text):
message = await ctx.send(text)
for emoji in ('👍', '👎'):
await message.add_reaction(emoji)
You can use Message.pin: await message.pin()
I'm not sure what you mean by "user tags". Do you mean roles?
I would write your command as
#bot.command()
async def create_poll(ctx, text, *emojis: discord.Emoji):
msg = await ctx.send(text)
for emoji in emojis:
await msg.add_reaction(emoji)
Note that this will only work for custom emoji, the ones you have added to your own server (This is because discord.py treats unicode emoji and custom emoji differently.) This would accept commands like
!create_poll "Vote in the Primary!" :obamaemoji: :hillaryemoji:
assuming those two emoji were on the server you send the command in.
With the new commands.Greedy converter, I would rewrite the above command like so:
#bot.command()
async def create_poll(ctx, emojis: Greedy[Emoji], *, text):
msg = await ctx.send(text)
for emoji in emojis:
await msg.add_reaction(emoji)
So invocation would be a little more natural, without the quotes:
!create_poll :obamaemoji: :hillaryemoji: Vote in the Primary!

I have just seen what you are stuck and I was stuck at this too. The solution is that reactions are not actually things like ":one:" it is actual emojis, there are different ways to fix this, the simplest being adding them to the dictionary yourself. Or you can use python3 -m pip install discordhelp and use the functions for it listed here.

Related

Checking for a non-existent command

Why, when I have existing commands in the array, do I check with the function on_message and nothing happens in the loop.
available_commands = ['/help', '/profile', '/register', '/shop', '/daily']
#bot.event
async def on_message(message):
if message.content.startswith('/'):
msg = message.content
for x in range(len(available_commands)):
if msg != available_commands[x]:
await message.channel.send('Unknown command! Please type /help!')
return
await bot.process_commands(message)
Your logic is wrong. you are checking to see if your command is EVERY SINGLE OPTION in available_commands, what you should instead do is check if the command is in available_commands (the command being the first "word" of message.content)
try this:
available_commands = ['/help', '/profile', '/register', '/shop', '/daily']
#bot.event
async def on_message(message):
if not message.content.startswith('/'):
return
if message.content.split()[0] not in available_commands:
await message.channel.send('Unknown command! Please type /help!')
return
await bot.process_commands(message)
If you are unfamiliar with the in keyword, I suggest you try reading this
So ... first of all after seeing your code I can literally say that you have seen youtube tutorials ...
don't watch them first of all because they are bad and dont provide a good way to do things also some are even outdated I would recommend you to first read docs of discord.py and then join https://discord.gg/dpy/ and you can find pretty much good amount of help
as for why this code is bad
first of all if you don't know after april 30 , 2022 message content intent will be privileged and if your bot reached above 100 servers either you will need intent or you wil have to go with slash commands. (you will learn about them as you code further .... just join some coding communities like the one I mentioned above you will get good amount of info there)
next you really dont have need to check for commands if they exist or not.. what I mean is instead of using on_message event what you can really do is using another decorator #bot.command()
here is example how to do things with this
#bot.command()
async def shop(message):
#any function here that you want
await message.send('your msg') #this will send msg ofc
now if suppose you havent defined a function(command) so it will raise an error CommandNotFound which can be detected by another bot event on_command_error
now how to implement this?
in the same decorator under #bot.event in your code you will have to do this
#bot.event
async def on_message(message):
#if you still want to do something with this
async def on_command_error(message,error):
if isinstance(error, commands.CommandNotFound):
await message.send("This command don't exist")
#or you can simply just pass this one
and it's not your fault for following yt tutorials lol because I also followed them at a time.... but yeah they are bad :) welcome to discord.py

Trying to make discord.py bot say something when its gets mentioned, doesnt respond

When someone mentions the bot, I want it to respond saying, "Hey! I've been mentioned. My prefix is >. You can type >help to see my commands." I have the code written out but the bot does not respond at all when it gets mentioned. I do not get any errors when I run it or mention the bot. How do I get this to work? My code is below. Thanks in advance.
#bot.event
async def on_message(message):
if message.author.id == bot.user.id:
return
msg_content = message.content.lower()
mention = ['#830599839623675925', '#Ultimate Bot']
if any(word in msg_content for word in mention):
await message.channel.send("Hey! I've been mentioned. My prefix is `>`. You can type `>help` to see my commands.")
else:
await bot.process_commands(message)
You can use the mentioned_in() method.
So in this case it would be;
#bot.event
async def on_message(message):
if message.author.id == bot.user.id: return
if bot.user.mentioned_in(message):
await message.channel.send("Hey! I've been mentioned.")
await bot.process_commands(message)
Some things to note:
Hard-coding in values such as in your mention variable is bad practice. It makes it much harder to maintain your code, and when your project gets bigger you may question what the purpose of these seemingly random strings are referring to. For example, if you were to change the name of the bot, or to transfer your code to a new bot, going through your entire codebase and picking out and changing these values would be far from ideal.
I don't know about the rest of your application, but you probably don't want process_commands() to be in the else statement. For example, if the user mentions the bot in their message, but are using a command, the command will be blocked because you are not triggering process_commands().
Hope this answers your question
You could instead just check if bot.user is in the message mentions.
You can also check if the bot display name was used.
if bot.user in message.mentions or (message.guild and message.guild.me in message.mentions):
print("bot was mentioned")
elif message.guild and message.guild.me.display_name in message.content:
print("bot name was used")

How to program a discord.py bot to dm me when a command is used by another person?

I want to create a command that people can use to send suggestions to me. They'd use the command "!suggest", followed by their input, and the bot privately dms that whole message to me. However I am struggling with making the bot dm me instead of the author of the command.
if message.content.startswith('!suggest'):
await message.author.send(message.content)
The problem here lies with the author part in the code- instead of dming me every time, it will dm the person who typed the command, which is useless to me. How can I replace 'author' with my discord ID, to make it dm me and only me with the message? I've asked this before and all answers have not worked for me. These are the solution's I've gotten that did not work:
message.author.dm_channel.send("message"), but that is the same problem, it does not get rid of the author issue.
me = await client.get_user_info('MY_SNOWFLAKE_ID')
await client.send_message(me, "Hello!")
This code also did not work for me. The user that it is dming is the same each time, so I have no need to run the get_user_info in the first place. And whenever the second line runs, I get an error code because the MyClient cannot use the send_message attribute.
Your code is from the old discord version. It has been changed in the discord.py rewrite. I also recommend you to use discord.ext.commands instead of using on_message to read commands manually.
As for the dm, simply use
your_id = 123456789012345678
me = client.get_user(your_id)
await me.send("Hello")
Note that it requires members intents enabled. Use await fetch_user(your_id) if you don't have it enabled. Also, note that there might be also rate-limiting consequences if multiple users use commands simultaneously.
Docs:
get_user
fetch_user
User.send
#client.command()
async def suggest(ctx, message = None):
if message == None:
await ctx.send("Provide a suggestion")
else:
await ctx.{your_id}.send("message")
Please don't mind the indent
You can dm a user by doing this:
user = client.get_user(your_id_here)
await user.send('work!')
FAQ : https://discordpy.readthedocs.io/en/stable/faq.html#how-do-i-send-a-dm
if you want the full code here it is:
#client.command()
async def suggest(ctx, *, suggest_text):
user_suggestor = ctx.author
user = client.get_user(user_id) # replace the user_id to your user id
if suggest_text == None:
await ctx.send("Enter your suggestion! example : !suggest {your suggest here}")
else:
await user.send(f"{user_suggestor.name} sent an suggestion! \nSuggestion : {suggest_text}") # the \n thing is to space
thank me later :D

I want to use a Discord Role as requirement for a Command

Im coding a Discord bot in Python, and i want to make a Command which requires a Role. Is there a way to use a role as requirement in a if statement?
You just need to do
#client.command()
#commands.has_role(role='Role name')
async def command(ctx):
and yea
or if it doesn't work you need to put the #commands.has_role(role='Role name') above #client.command()
if you don't get it you can read the documentation
or you can do #commands.has_permission(kind of permission=True/False) for it as well role means the NEED the role and permission means they also need the permission but you need to have a discord role permission or else it would send it doesn't know the kind of permission
I've done exactly that in a bot I've created recently, I set up my commands as JSON configs with data including syntax, description, and allowed roles. The bot uses DiscordJS, but I'm sure you can use the same principles for a Python bot:
if (cmd.allowedRoles.includes('everyone') || message.member.roles.cache.some(role => cmd.allowedRoles.includes(role.name.toLowerCase())))
You can use
#client.command()
#commands.has_permissions(**permission needed**=True)
Which will only allow people with certain permissions to be allowed to do the command (Error messages option). Or if you want only people with a role you can use
if message.author.role.id == **role id**:
or
if ctx.message.author.role.id == **role id**:. Here is an example code:
#client.event
async def on_message(message):
link = ["https://"]
for word in link:
if message.content.count(word) > 0:
if message.author.role.id == 706694479847096381:
return
else:
print(f'{message.author}({message.author.id}) Sent an link')
await message.delete()
This code allows the bot to ignore people with that role when they send a link.
You can use argparse and create a required argument called role. Take a look at the documentation.

Sending embeds through discord.py

I've been doing a lot of work creating discord bots with the discord api through javascript with discord.js.
I am trying to create my first discord bot with python using the discord api through discord.py and with requests through requests.py.
My goal is to check status code on a site, and when a message is sent that contains "status code," it will reply with the site's status code in an embed.
Here is my code to do so:
import discord
import requests
r = requests.get('redactedurl')
test = r.status_code
class MyClient(discord.Client):
async def on_ready(self):
print('Logged on as {0}!'.format(self.user))
async def on_message(self, message):
if (message.channel.id == redacted):
if "status code" in message.content:
print('Message from {0.author}: {0.content}'.format(message))
embed = discord.Embed(color=0x00ff00)
embed.title = "test"
embed.description = '**Status Code:** {r.status_code}'
await message.channel.send(embed=embed)
client = MyClient()
client.run('redacted')
The following is a list of questions I hope anybody can answer to help me out :)
As you see here: https://gyazo.com/f6ae7082486cade72389534a05655fec, this just sends "{r.status_code}" in the embed, instead of the actual status code, what am I doing wrong?
What does it mean when i see 0 in the curly brackets. For example, can somebody explain "('Logged on as {0}!'.format(self.user))" to me? Since I'm new to python and discord.py, I am confused about this whole line. I know what the outcome is, but forgive my ignorance, is it all necessary?
In "send(embed=embed)", why can't i just put send(embed)?
Finally, is there anything else that i can do to improve the code?
Thank you so much if you're able to help!
In the line you set the embed description, outputting r.status_code as a string instead of the value it contains. Try embed.description = '**Status Code:** {0}'.format(r.status_code)
The 0 resembles the index of the value that is supposed to be there. For example, '{1}'.format(10, 20) would print out the value at index 1 which in this case is 20.
When you use send(embed) then the bot will end up sending the embed in string form which will look very off, if you try sending it you will see what I mean. In this case, we have to specify which argument we are assigning the value to. This function accepts kwargs which are Keyworded arguments, in this case, embed is one of the kwargs in this send() function. This function accepts other kwargs as well such as content, tts, delete_after, etc. It is all documented.
You can simplify creating the embed by passing in the kwargs, for example: discord.Embed(title='whatever title', color='whatever color')
The discord.Embed() can support more arguments if you look at the documentation.
Here is a link to the documentation: https://discordpy.readthedocs.io/en/latest/index.html
If you search for TextChannel, and look for the send() function, you can find more arguments supported, as well as for discord.Embed().
Okay working down your list of questions:
You're not formatting {r.status_code} but just sending it as a string which is why it shows up as just that. To fix it, all you have to do is add 1 "f".
embed.description = f'**Status Code:** {r.status_code}'
or, if you want to use a consistent way of formatting your strings:
embed.description = '**Status Code:** {0}'.format(r.status_code)
In Python, the curly brackets '{}' in a string can be used for formatting. This can be done in multiple ways including how you're doing it in your code. str.format() can take multiple arguments if you want to format different values in your string. The 0 is just an index for which argument you want to use.
I'm not too familiar with the discord library but having taken a quick look at the documentation it would seem that if you did that it would take your embed variable and pass it into send as the content variable. Thereby sending it as a normal message instead of embedding it.
I personally like f-strings more for formatting because it makes your code more easily readable. I can't really comment on your use of the discord library but besides that your code looks fine! Purely for aesthetics/readability I would put an empty line between your imports and defining your variables, and between your variables and your class.
Here is an example!
import discord
client = commands.Bot(command_prefix = '~')
#client.command()
async def embed(ctx):
embed=discord.Embed(title="Hello!",description="Im a embed text!")
await ctx.send(embed=embed)
client.run("TOKEN")
import discord
from discord.ext import commands
client = commands.Bot(command_prefix='>')
#client.event
async def on_ready():
print("Log : "+str(client.user))
#client.command()
async def embed(ctx):
em = discord.Embed(color=discord.Color.from_rgb(0,0,255),title="Your title",description="Your description")
await ctx.send(embed=em)
client.run("token")

Categories