I have been making a bot that adds people who react to a message to an array, and then #'s everyone on the array once a goal is met. I have gotten it to send out the message that should # everyone but it just shows the text instead of actually notifying the people. "Names" is the list that it gathers and "send_out" is supposed to format it.
#client.event
async def on_raw_reaction_add(payload):
if payload.message_id == messageid and payload.member.bot == False:
if str(payload.emoji.name) == "🦍":
name = str(payload.member.name + "#" + payload.member.discriminator)
global count
count += 1
global names
names.append(name)
print (names)
if (count == goal_actual):
print("Goal has been reached.")
channel = client.get_channel(channel_id)
await channel.send("We now have " + str(goal_actual) + " for " + game_actual + "!")
print(channel)
global send_out
for x in names:
send_out += ("#" + x +" ")
await channel.send(send_out)
send_out = []
else:
print("Detected reaction from " + name + ". There are is now " , count , " people ready.")
It's best to use the member object that payload gives you
Here (discord.py docs) we can see that discord.on_raw_reaction_add(payload) gives a RawReactionActionEvent object.
RawReactionActionEvent also has a member object, which is a normal member object, this has an attribute member.mention
You could use this instead to tag people
#client.event
async def on_raw_reaction_add(payload):
if payload.message_id == messageid and payload.member.bot == False:
if str(payload.emoji.name) == "🦍":
global count
count += 1
global names
names.append(payload.member)
print (names)
if (count == goal_actual):
print("Goal has been reached.")
channel = client.get_channel(channel_id)
await channel.send("We now have " + str(goal_actual) + " for " + game_actual + "!")
print(channel)
global send_out
for member in names:
send_out += (member.mention)
await channel.send(send_out)
send_out = []
else:
print("Detected reaction from " + name + ". There are is now " , count , " people ready.")
Related
I'm trying to reply a function from another file called "blockchain.py" but when I tried to await for ctx.reply(blq.blockchain_connection) it gives me this on Discord:
<function blockchain_connection at 0x000001922A5AEEF0>
The code is:
# bot = scrt.bot
bot = commands.Bot(command_prefix = "!")
login = 0
blq.tokens_dict
# Login
#bot.event
async def on_ready():
global login
print('We have logged in as {0.user}'.format(bot))
login = 1
my_background_task.start()
# Coin Price Command
#bot.command()
async def coin(ctx, arg1):
if blq.tokens_dict == blq.tokens_dict:
blq.blockchain_connection
else:
await ctx.reply("The token " + str(arg1) + " is not in the token list, if you want to add " + str(arg1) + " to the list please use the command : " + '\n' + "!add_token and insert key and value with an space on each")
#bot.command()
async def add_token(ctx, key, value):
global tokens_dict
if(key in blq.tokens_dict.keys()):
await ctx.reply('I already have this token on my list, please add another Contract or another Value')
else:
blq.tokens_dict[key] = value
This other file (blockchain) has a dict and this function:
async def blockchain_connection(ctx, arg1):
async with aiohttp.ClientSession() as cs:
async with cs.get('https://api.pancakeswap.info/api/v2/tokens/' + tokens_dict[arg1]) as r:
res = await r.json() # returns dict
await ctx.reply(res['data']['price'])
It looks like the issue is here:
# Coin Price Command
#bot.command()
async def coin(ctx, arg1):
if blq.tokens_dict == blq.tokens_dict:
blq.blockchain_connection
else:
await ctx.reply("The token " + str(arg1) + " is not in the token list, if you want to add " + str(arg1) + " to the list please use the command : " + '\n' + "!add_token and insert key and value with an space on each")
blq.blockchain_connection is a function, receiving two arguments, which are named the same. It looks like you have to call it like so:
# Coin Price Command
#bot.command()
async def coin(ctx, arg1):
if blq.tokens_dict == blq.tokens_dict:
await blq.blockchain_connection(ctx, arg1)
else:
await ctx.reply("The token " + str(arg1) + " is not in the token list, if you want to add " + str(arg1) + " to the list please use the command : " + '\n' + "!add_token and insert key and value with an space on each")
This will actually call the function, which then internally (conditionally) calls ctx.reply(...) with the message you actually want to send.
im trying to add a role when the user react to this emojis but i cant get
Setup Roles
#bot.command()
async def roles(ctx):
global IDMessage
reaction = await ctx.reply("Select your Game" + '\n' + '\n' + "- Valorant 🦸♀️" '\n' + "- World of Warcraft ⚔" + '\n' + "- League of Legends 🧙♀️" + '\n' + "- Cs:Go 🔫")
await reaction.add_reaction('🦸♀️')
await reaction.add_reaction('⚔')
await reaction.add_reaction('🧙♀️')
await reaction.add_reaction('🔫')
IDMessage = reaction.message_id
And this is the part of the code that doesnt work for me
Add role on reaction
#bot.event
async def on_reaction_add(reaction, user):
ChID = '920320584032870424'
if reaction.message.channel.id != ChID:
return
if reaction.emoji == "🦸♀️":
Valorant = discord.utils.get(user.server.roles, name="Valorant")
await bot.add_roles(user, Valorant)
ChID = '920320584032870424' (string) is being compared to an integer reaction.message.channel.id.
ChID = '920320584032870424'
Instead, use
ChID = 920320584032870424
Which is an integer.
There are some issues here. Instead of on_reaction_add, use on_raw_reaction_add. Most bots use the latter instead of the former.
Why you may ask? Because, on_reaction_add only works for messages in bot's cache.
So every message sent after your bot can read messages is stored in a dictionary or list (this is what a cache is). By default the limit of caching is 1000 messages.
So if you ever restarted your bot, the message will never be in cache, and thus the event won't trigger.
Another issue is that you are comparing a string (ChID) variable and an integer (reaction.message.channel.id), they can never be equal so if reaction.message.channel.id != ChID will always return True and thus won't do anything (as you return for that condition being True).
Third issue is that bot.add_roles does not exist. add_roles is a method of discord.Member object.
Fourth issue is, Member.server is not a thing, Member.guild is
So your updated code will be like:
#bot.event
async def on_raw_reaction_add(payload: discord.RawReactionActionEvent):
ChID = 920320584032870424
if payload.channel.id != ChID:
return
if str(payload.emoji) == "🦸♀️":
valorant = discord.utils.get(payload.member.guild.roles, name="Valorant")
await payload.member.add_roles(valorant)
Also my personal advice of not using global
there are very rare few cases where you would actually require this keyword
But it is highly unlikely for you to use this in a bot made with discord.py lib
More on this:
discord.on_reaction_add
discord.RawReactionActionEvent
discord.Member.guild
discord.Member.add_roles
why-are-global-variables-evil
I had the same problem, and this is how I solved it.
You have the reactions being added, now you need to wait for the user to react.
For this, you can use discord.py’s wait_for function.
Check function:
def check(reaction):
if str(reaction.emoji) == “EMOJIHERE”:
return “emoji name”
elif str(reaction.emoji) == “SECONDEMOJI”:
return “emoji name”
elif str(reaction.emoji) == “THIRDEMOJI”:
return “emoji name”
elif str(reaction.emoji) == “FOURTHEMOJI”:
return “emoji name”
else:
return “error”
Code:
async def roles(ctx):
global IDMessage
reaction = await ctx.reply("Select your Game" + '\n' + '\n' + "- Valorant 🦸♀️" '\n' + "- World of Warcraft ⚔" + '\n' + "- League of Legends 🧙♀️" + '\n' + "- Cs:Go 🔫")
await reaction.add_reaction('🦸♀️')
await reaction.add_reaction('⚔')
await reaction.add_reaction('🧙♀️')
await reaction.add_reaction('🔫')
confirm = await bot.wait_for(“reaction_add”, check=check)
if confirm == "emoji name 1":
doSomething()
elif confirm == "emoji name 2":
doSomething()
elif confirm == "emoji name 3":
doSomething()
elif confirm == "emoji name 4":
doSomething()
else:
print("Error")
await ctx.send("An error occurred.")
Replace EMOJIHERE and emoji name with the appropriate values.
I'm currently trying to make a bot using discord.py. One of the functions I want it to have is to show the users with X role. I've managed to create said function, and make the bot send the results with a nice embed. However, now I want to improve it and make it more similar to Nadeko's inrole function (i.e. only one embed and the user can react to it to change the page).
#client.command(name = "inrole", brief = "Users with a certain role",
description= "I'll give you a list with all the users with a certain role.", pass_context=True)
async def inrole(ctx, *args):
server = ctx.message.guild
role_name = (' '.join(args))
role_id = server.roles[0]
for role in server.roles:
if role_name.lower() == role.name.lower():
role_id = role
break
else:
await ctx.send(f"Oh noes! That role doesn't exist!")
return
n = 0
members = []
for member in server.members:
if role_id in member.roles:
n += 1
name = str(n) + ". " + member.name + " #" + member.discriminator + "\n"
members.append(name)
composite_members = [members[x:x + 20] for x in range(0, len(members), 20)]
for elements in composite_members:
string = ""
for element in elements:
string = string + element
embedVar = discord.Embed(title=f"List of users with {role} - {n}", colour=discord.Colour.blue())
embedVar.add_field(name = "Members", value = string)
await ctx.send(embed=embedVar)
I've read the documentation, but I'm still quite lost in how to make it like Nadeko's. Right now, if there are 200 users with that role, then it will print 10 embeds, which can be rather annoying.
Thanks in advance!
EDIT:
I've improved the code a bit, but now I have a different problem.
def predicate(message, l, r):
def check(reaction, user):
if reaction.message.id != message.id or user == client.user:
return False
if l and reaction.emoji == "⏪":
return True
if r and reaction.emoji == "⏩":
return True
return False
return check
#inrole function. To see all members with that role
#client.command(name = "inrole", brief = "Users with a certain role",
description= "I'll give you a list with all the users with a certain role.", pass_context=True)
async def inrole(ctx, *role):
server = ctx.message.guild
role_name = (' '.join(role))
role_id = server.roles[0]
for role in server.roles:
if role_name.lower() == role.name.lower():
role_id = role
break
else:
await ctx.send(f"Oh noes! That role doesn't exist!")
return
n = 0
members = []
for member in server.members:
if role_id in member.roles:
n += 1
name = str(n) + ". " + member.name + " #" + member.discriminator + "\n"
members.append(name)
composite_members = [members[x:x + 20] for x in range(0, len(members), 20)]
pages = []
for elements in composite_members:
string = ""
for element in elements:
string = string + element
embedVar = discord.Embed(title=f"List of users with {role} - {n}", colour=discord.Colour.blue())
embedVar.add_field(name = "Members", value = string)
pages.append(embedVar)
page = 0
left = "⏪"
right = "⏩"
while True:
msg = await ctx.send(embed = pages[(page)])
l = page != 0
r = page != len(pages) - 1
if l:
await msg.add_reaction(left)
if r:
await msg.add_reaction(right)
# bot.wait_for_reaction
react = await client.wait_for('reaction_add', check=predicate(msg, l, r))
if react[0] == left:
page -= 1
elif react[0] == right:
page += 1
await msg.delete()
In the last if/elif statement, it doesn't work. There isn't an error, but although react[0] is the same emoji as right or left (respectively), page never changes, and thus the embed doesn't change either. I changed the elif for an else, and it that case it does work, but it can only go forward.
Figured it out. Issue was that react[0] wasn't the same type as left and right. Here's the solution for that issue, if anyone is interested:
if str(react[0]) == left:
page -= 1
elif str(react[0]) == right:
page += 1
The rest of the code is exactly the same as the one I posted in the edit.
message_ = None
#client.command()
async def edit(ctx):
global message_
message_ = await ctx.send('EMBED PAGE[1]')
#client.event
async def on_reaction_add(reaction, user):
global message_
msg_ID = 487165969903517696
if int(reaction.message.id) != ChID:
return;
elif user.reaction.emoji == "🏃":
message_.edit(content='EMBED PAGE [2]')
this is just an idea! I'm not sure this code works but there is an option to edit our embed message! so use that to make your embed message better [documentation for edit]
I am making a blackjack game using discord.py, I am changing the value of Ace from 11 to 1 instead but no message is being sent to the channel and I am not getting any errors.
def ask_A(self):
num_A = []
for c in range(0, len(self.deck)):
if self.deck[c].get_value() == "A" and self.deck[c].get_points() == 11:
num_A.append(c)
for i in num_A:
message.channel.send(
"Your current cards are " + str(self.print_cards()) + ", Total is " + str(self.sum_cards()) + "\n")
self.deck[i].change_points(1)
message.channel.send("Changed value of Ace to prevent loss")
message.channel.send("Your cards are " + str(self.print_cards()) + ", Total is " + str(self.sum_cards()) + "\n")
You're forgetting to await your statements. Anything in the docs that is a coroutine needs to have await expression.
Example:
await message.channel.send("Hello! This is my message")
Read up about Async IO here.
I have a game that is basically two commands, test and test2.
test makes you generate a word that you have to guess in test2, and if you miss the word 6 times you lose.
from collections import defaultdict
word = ""
guessesLeft = 6
blanks = []
guessedLetters = []
lettersFound = 0
bot = commands.Bot(command_prefix=("!"))
bot.gamex = defaultdict(bool)
#bot.command()
async def test(ctx, *, message):
await ctx.message.delete()
global word, guessesLeft, blanks, lettersFound, guessedLetters
if not bot.gamex[ctx.guild.id]:
word = message.lower()
blanks = []
guessedLetters = []
lettersFound = 0
guessesLeft = 6
bot.gamex[ctx.guild.id] = True
for i in range(0, len(word)):
blanks .append("-")
print(i)
await ctx.send(embed=discord.Embed(title="hangman: " + " ".join(blanks)))
#bot.command()
async def test2(ctx, *, guess):
global word, guessesLeft, blanks, lettersFound, guessedLetters
if bot.gamex[ctx.guild.id]:
if str.isalpha(guess) and len(guess) is 1 and str.lower(guess) not in guessedLetters:
if str.lower(guess) in word:
await ctx.send(guess + " is in the word. Good job!")
for i in range(0, len(word)):
if word[i] == str.lower(guess):
blanks[i] = str.lower(guess)
lettersFound += 1
else:
await ctx.send(guess + " is NOT in the word.")
guessesLeft -= 1
guessedLetters.append(str.lower(guess))
await ctx.send(" ".join(blanks))
await ctx.send("Guessed letters: " + " ".join(guessedLetters))
await ctx.send("Guesses left: " + str(guessesLeft))
if guessesLeft == 0:
await ctx.send("No guesses left. You lose!")
bot.gamex[ctx.guild.id] = False
if lettersFound == len(word)-1:
await ctx.send("You've won! The word was: " + word)
bot.gamex[ctx.guild.id] = False
It's a hangman game, but the game variables are mixing on every server the bot is on, if I guess a word on one server, it appears on another server, I want each server to be individual and have commands individual.
Only those in the global are mixing.
What would the command look like so that the variables don't get mixed up between the servers?
Make a class that represents the state of the game, and replace your gamex mapping with a mapping of guild ids to games:
games = {}
class Game:
def __init__(self, word, guesses=6):
self.word = word.lower()
self.blanks = ["-"]*len(word)
self.guessedLetters = []
self.lettersFound = 0
self.guessesLeft = guesses
Then in your commands, you get the Game storing the state for that guild.
#bot.command()
async def create_game(ctx, *, word):
await ctx.message.delete()
if ctx.guild.id in games:
await ctx.send("Game already in progress")
else:
games[ctx.guild.id] = Game(word)
await ctx.send(embed=discord.Embed(title="hangman: " + " ".join(games[ctx.guild.id].blanks)))
And you would call del games[ctx.guild.id] to remove entries when the game is complete.
A well-designed Game object would mean that the logic about how the game is played would be pulled out of your commands and into the Game object. Ideally, you would be able to take the Game class from this code and use it to implement the same game in a browser or other interface with minimal changes.