Checking message.content for certain text - python-3.x

I'm trying to make a multiple-choice trivia background task. I'm trying to await client.wait_for_message only for either the correct answer or the incorrect choices. I'm checking message.content but I'm missing something because no matter what anyone types, it says "incorrect answer" UNLESS it's the correct answer. If someone types something other than the incorrect answer or correct answer then I want it to ignore their text.
async def trivia_loop():
await client.wait_until_ready()
channel = client.get_channel("123456778999533")
triviamonies = random.randint(1250, 2550)
while not client.is_closed:
await client.send_message(channel, '**Category**: {}\n**Difficulty**: {}\n\n{}\n\n**Potential Answers:**\n{}'.format(category, formatdiff, formatquestion, '\n'.join(tup)))
winner = ''
def check(m):
return m.content.lower() == formatanswer.lower() and m.content.lower() != incorrectanswer1 or incorrectanswer2 or incorrectanswer3
while winner == '':
answerid = await client.wait_for_message(timeout=420, channel=channel, check=check)
if answerid is None:
winner = 1
await client.delete_message(questionsend)
await client.send_message(channel, 'No one answered correctly! The answer was {}'.format(formatanswer))
await asyncio.sleep(840)
elif get_triviaguessesTaken(answerid.author) < 1 and answerid.content.lower() == formatanswer.lower():
winner = 1
await client.delete_message(questionsend)
await client.send_message(channel, '**{}** is correct!\n{} earns **${:,}** !'.format(formatanswer, answerid.author.mention, triviamonies))
add_dollars(answerid.author, triviamonies)
add_trivias(answerid.author, 1)
await asyncio.sleep(900)
elif get_triviaguessesTaken(answerid.author) < 1 and answerid.content.lower() == incorrectanswer1 or incorrectanswer2 or incorrectanswer3:
await client.send_message(channel, '**{}** that is incorrect! Please try again on the next trivia question'.format(answerid.author.mention))
add_triviaguessesTaken(answerid.author, 1)
elif get_triviaguessesTaken(answerid.author) > 0 and answerid.content.lower() == incorrectanswer1 or incorrectanswer2 or incorrectanswer3:
await client.send_message(channel, '{} you have already tried to guess this trivia question. Try again on the next one!'.format(answerid.author.mention))

Discord.py Version:
From the looks of your code, you're still using the async version of d.py (v0.16.12), and you haven't moved to the more up-to-date and stable rewrite version of d.py.
I can highly recommend running pip install --upgrade discord.py for the most recent stable version. Make sure you do this and rewrite your code sooner rather than later, as it'll be less of a pain when you have less code to work with.
Wait_for() usage in rewrite:
The docs use an example in an on_message event, but I'll make an example here to suit your case with a command instead, so you have both ways available to you and can mix-and-match:
#bot.command(aliases=["trivia"])
async def starttrivia(ctx):
answers = ["paris", "london", "oslo"]
await ctx.send(f"What's the capital of France?\n\n*Potential answers:*\n{', '.join(answers)}")
correct = False
def check(m):
for a in answers:
if a in m.content.lower():
return True
while not correct:
try:
reply = await bot.wait_for("message", check=check, timeout=30)
if "paris" in reply.content.lower():
await ctx.send(f"Correct! {reply.author.mention} got the right answer!")
correct = True
else:
await ctx.send("Incorrect, try again!")
except asyncio.TimeoutError: # exception thrown when no reply is sent in time
await ctx.send("Nobody guessed for 30 seconds! The answer was: Paris")
break

Related

Message repeating even after explicitly checking for history in discord

I'm trying to make a discord bot and I've added functionality of the tasks repeating after 24 hours using tasks.loop after at an interval of 24 hours( 64800 seconds). Since the code is run on repl.it, I've used uptime robot to continuously send a ping after 5 minutes to make it running always in the repl.it.
My issue is that even after checking historyin the channel the message is getting send again as you can see in the below image. ( by checking here I mean the explanation which is the explanation that comes with the API). I'm using NASA api here.
Code snippet
def get_APOD():
image = requests.get("https://api.nasa.gov/planetary/apod?api_key="+apod)
json_data = json.loads(image.text)
description = json_data["url"]
explanation = "'" + json_data["explanation"]+ "'"
title = json_data["title"]
return description,explanation,title
#client.event
async def on_ready():
print('We have logged in as {0.user}'.format(client))
send_message.start()
#tasks.loop(seconds=64800.0)
async def send_message():
channel = client.get_channel(int(apod_channel))
messages = await channel.history(limit=1).flatten()
description,explanation,title = get_APOD()
if messages[0].content == explanation:
print("Its the same")
else:
await channel.send(description)
await channel.send(title)
await channel.send(explanation)
. Here you can see the messages repeating twice. It shouldn't by the code I've written here. Since it check for the history in the channel.
What is the issue here? Appreciate any help.
EDIT: When I restart the repl.it IDE the code will run alright and print "It's the same" as it should if the message is already sent but otherwise it's failing.

How I implement reset_cooldown in a cog file?

I m trying to do a command with which my friends can troll each other by moving them to a special channel.
I put a cooldown so they won t abuse it but know, when i m trying to rewrite my code to a cog version i can t handle the #command.after_invoke so i can t reset cooldown if the author is me or someone special.
I tried to do it in #command.error but if i reset it there i can t reactivate it
My problem is that i can t figure out how am i suppose to transpose this into a cog version:
#m.after_invoke
async def reset_cooldown(ctx):
if ctx.message.author.id==1234567890:
m.reset_cooldown(ctx)
I ll put cog file with my function, it works but thw down is that i can t reset cooldown:
import discord
from datetime import datetime
protected=[1234567890]
class move(commands.Cog):
def __init__(self,bot):
self.bot=bot
#commands.command(pass_context=True,aliases=['m'])
#commands.cooldown(1,300,commands.BucketType.user)
async def move(self,ctx,member:discord.Member):
global protected
message=ctx.message
delete_module=self.bot.get_cog('delete') #this will erase the message so the victim won tknow who s doing teh prank
await delete_module.delete(ctx,ctx.author)
canal_sclav=list(filter(lambda x: x.name=='sclav',ctx.guild.voice_channels))[0] # this is the channel where they llbe sent
reports=list(filter(lambda x: x.id==1234567890,member.guild.channels))[0] # this is a channel in which i track the activity
current_time = datetime.now().strftime("%H:%M:%S")
embed=discord.Embed(description=message.author.mention+'l-a maltrafoxat pe '+member.mention+ ' la ora '+current_time,color=discord.Colour.red()) # this is the sign that someone was abused
######################## daca autorul nu e conectat -- if the author is not connected
if ctx.author.voice==None:
embed=discord.Embed(description=message.author.mention+'Conecteza-te fiti-ar alifia de ras',color=discord.Colour.gold())
await ctx.send(embed=embed)
######################## daca beleste pe cine nu trebuie -- this is for the protected one
elif member.id in protected:
embed=discord.Embed(description=message.author.mention+' altadata, suge-o acum',color=discord.Colour.gold())
await ctx.send(embed=embed)
######################## daca membrul nu e conectat -- if member is not connected
elif member.voice==None:
embed=discord.Embed(description=message.author.mention+' nu poate fi babardit incearca alt fraier',color=discord.Colour.gold())
await ctx.send(embed=embed)
######################## daca membrul e pe sclav -- if he is abused
elif member.voice.channel==canal_sclav:
embed=discord.Embed(description=message.author.mention+' si-o ia in cur schimba prostu',color=discord.Colour.gold())
await ctx.send(embed=embed)
######################## daca e ok -- if it is ok
else:
await reports.send(embed=embed)
await member.move_to(canal_sclav)
embed=discord.Embed(description=member.mention+' este abuzat',color=discord.Colour.gold())
await ctx.send(embed=embed)
# for error handling
#move.error
async def move_error(self,ctx,error):
global protected
retry_after = round(error.retry_after)
text=ctx.message.author.mention+' ti-o pot suge in '+str(round(error.retry_after))+'s'
embed=discord.Embed(description=text,color=discord.Colour.magenta())
await ctx.send(embed=embed)
return await ctx.send(embed=embed
)
# f"\N{HOURGLASS} Command is on cooldown, try again after {retry_after} seconds"
# HERE I TRIED TO DO IT BUT I CAN T FIND ENOUGH RESOURCES
#move.afetr_invoke
async def reset_cooldown(ctx):
if ctx.message.author.id in protected:
move.reset_cooldown(ctx)
def setup(bot):
bot.add_cog(move(bot))
I finally found the answer; the reset function that should be add is this:
async def cog_after_invoke(self,ctx):
if ctx.message.author.id in protejati:
ctx.command.reset_cooldown(ctx)

Discord.PY Help Command

Ok, so I'm having an issue with my bot. I know what it is, but I don't know what the string to fix it is.
Basically I have it set to store config and prefixes and such in a json but the help command which is loading one of my commands multiple times.
I've checked the help command and i don't think that's it. Because after redoing my setprefix command (which is being displayed multiple times) it added yet another one on so now it's even worse.
Here is my storage system that I believe to be causing the issue.
with open("./config/config.json", "r") as configjsonFile:
configData = json.load(configjsonFile)
login_token = configData["discordtoken"]
with open("./config/prefixes.json") as f:
prefixes = json.load(f)
default_prefix = "-"
def prefix(client, message):
id = message.guild.id
return prefixes.get(id, default_prefix)
client = commands.Bot(command_prefix=prefix)
client.remove_command('help')
#client.command(name="setprefix", aliases=["Prefix", "prefix"])
#commands.has_permissions(administrator=True)
async def setprefix(ctx, new_prefix):
"""Change the server prefix for the bot.
Alt : Prefix, prefix
Usage : prefix <custom prefix>"""
prefixes[ctx.message.guild.id] = new_prefix
with open("./config/prefixes.json", "w") as f:
json.dump(prefixes, f, indent=4)
embed = discord.Embed(color=0x4a3d9a, timestamp=ctx.message.created_at)
embed.set_author(name=f"{client.user.name}", icon_url=client.user.avatar_url)
embed.add_field(name="Success", value=f"Successfully changed prefix changed to `{new_prefix}`")
embed.set_thumbnail(url=client.user.avatar_url)
embed.set_footer(text="NewHorizon Development | https://newhorizon-development.netlify.app", icon_url=client.user.avatar_url)
await ctx.message.delete()
await ctx.send(embed=embed, delete_after=4)
and here is my custom help command just in case I'm wrong about what is causing the issue.
#commands.command(name="help", aliases=["Help", "H", "h"])
#commands.has_permissions(add_reactions=True, embed_links=True)
async def help(self, ctx, *cog):
"""Gets all cogs and commands.
Alt : h, H, Help
Usage : [h]elp <cog or command>"""
try:
if not cog:
"""Cog listing. What more?"""
embed = discord.Embed(title='Cog Listing and Uncatergorized Commands',
description='Use `help <cog>` to find out more about them!\n(BTW, the Cog Name Must Be in Title Case, Just Like this Sentence.)')
cogs_desc = ''
for x in self.client.cogs:
cogs_desc += ('{} - {}'.format(x, self.client.cogs[x].__doc__) + '\n')
embed.add_field(name='Cogs', value=cogs_desc[0:len(cogs_desc) - 1], inline=False)
cmds_desc = ''
for y in self.client.walk_commands():
if not y.cog_name and not y.hidden:
cmds_desc += ('{} - {}'.format(y.name, y.help) + '\n')
embed.add_field(name='Uncatergorized Commands', value=cmds_desc[0:len(cmds_desc) - 1], inline=False)
await ctx.message.add_reaction(emoji='✉')
await ctx.message.author.send('', embed=embed)
else:
"""Helps me remind you if you pass too many args."""
if len(cog) > 1:
embed = discord.Embed(title='Error!', description='That is way too many cogs!', color=discord.Color.red())
await ctx.message.author.send('', embed=embed)
else:
"""Command listing within a cog."""
found = False
for x in self.client.cogs:
for y in cog:
if x == y:
embed = discord.Embed(title=cog[0] + ' Command Listing', description=self.client.cogs[cog[0]].__doc__)
for c in self.client.get_cog(y).get_commands():
if not c.hidden:
embed.add_field(name=c.name, value=c.help, inline=False)
found = True
if not found:
"""Reminds you if that cog doesn't exist."""
embed = discord.Embed(title='Error!', description='How do you even use "' + cog[0] + '"?', color=discord.Color.red())
else:
await ctx.message.add_reaction(emoji='✉')
await ctx.message.author.send('', embed=embed)
except:
await ctx.send("Excuse me, I can't send embeds.")
I am using the rewrite branch if it helps. And my setprefix command IS NOT in a cog, unlike the rest of my commands. Thus why they appear a slight bit different.
Now I'm ASSUMING my issue is that the json are not closing, which I need to know how to make them close, but if that's not the issue, I would very much appreciate someone helping me figure out what's going wrong.

How can you reset or cancel asyncio.sleep events?

I am trying to set up a bot in discord that works on timers. One of them will work if one person types in the '!challenge' command, in which the bot will wait for 60 seconds to see if anyone types the '!accept' command in response. If it does not, it states 'Challenge was ignored. Resetting.' Or something along those lines.
Another timer actually runs during the game itself, and is an hour long. However, the hour resets after a command has been put in. If the games is idle for an hour (One player DCs or quits) the bot resets the game itself.
I had this working with threading:
# Initiate a challenge to the room. Opponent is whoever uses the !accept command. This command should be
# unavailable for use the moment someone !accepts, to ensure no one trolls during a fight.
if message[:10] == "!challenge":
msg, opponent, pOneInfo, new_game, bTimer, playerOne = message_10_challenge(channel, charFolder, message, unspoiledArena, character, self.game)
if opponent is not "":
self.opponent = opponent
if pOneInfo is not None:
self.pOneInfo = pOneInfo
self.pOneTotalHP = self.pOneInfo['thp']
self.pOneCurrentHP = self.pOneInfo['thp']
self.pOneLevel = self.pOneInfo['level']
if new_game != 0:
self.game = new_game
if bTimer is True:
timeout = 60
self.timer = Timer(timeout, self.challengeTimeOut)
self.timer.start()
if playerOne is not "":
self.playerOne = playerOne
super().MSG(channel, msg)
and:
# Response to use to accept a challenge.
if message == "!accept":
msg, pTwoInfo, new_game, playerTwo, bTimer, bGameTimer, new_oppenent, token = message_accept(channel, charFolder, unspoiledArena, character, self.game, self.opponent, self.pOneInfo)
if not charFile.is_file():
super().MSG(channel, "You don't even have a character made to fight.")
else:
if new_game is not None:
self.game = new_game
if pTwoInfo is not None:
self.pTwoInfo = pTwoInfo
self.pTwoTotalHP = self.pTwoInfo['thp']
self.pTwoCurrentHP = self.pTwoInfo['thp']
self.pTwoLevel = self.pTwoInfo['level']
if bTimer:
self.timer.cancel()
if bGameTimer:
gametimeout = 3600
self.gameTimer = Timer(gametimeout, self.combatTimeOut)
self.gameTimer.start()
if new_oppenent is not None:
self.opponent = new_oppenent
if playerTwo is not None:
self.playerTwo = playerTwo
if token is not None:
self.token = token
for msg_item in msg:
super().MSG(channel, msg_item)
with functions:
def challengeTimeOut(self):
super().MSG(unspoiledArena, "Challenge was not accepted. Challenge reset.")
self.game = 0
def combatTimeOut(self):
super().PRI('Unspoiled Desire', "!reset")
The above is an example of the same game, but on a different chat platform, with threading to handle the timers. But threading and discord.py aren't friends I guess. So I am trying to get the above code to work with discord.py, which seems to use asyncio.
The thought was to use asyncio.sleep() in the
if bTimer is true:
await asyncio.sleep(60)
self.game = 0
await ctx.send("Challenge was not accepted. Challenge reset.")
And this works...but it doesn't stop the timer, so even if someone !accepts, thus changing bTimer to False, which would cancel the timer:
if bTimer:
self.timer.cancel()
it's still going to say "Challenge was not accepted. Challenge reset."
The same problem will occure with bGameTimer if I try:
if bGameTimer:
await asyncio.sleep(3600)
await ctx.send("!reset")
the game will be hardwired to reset after 1 hours time, no matter if the game is done or not. Rather than resetting the 1 hour timer after every turn, and ONLY resetting if a full hour has passed in which no commands are made.
Is there a way to easily cancel or reset sleep cycles?
The asyncio code equivalent to your threading code would be:
...
if bTimer:
self.timer = asyncio.create_task(self.challengeTimeout())
...
async def challengeTimeout(self):
await asyncio.sleep(60)
self.game = 0
await ctx.send("Challenge was not accepted. Challenge reset.")
asyncio.create_task() creates a light-weight "task" object roughly equivalent to a thread. It runs "in parallel" to the your other coroutines, and you can cancel it by invoking its cancel() method:
if bTimer:
self.timer.cancel()

UnboundLocalError: Referenced before assignment local variable 'emoji_count'

Hi I had this niggling issue with a cog (bot module) on writing and I keep getting an UnboundLocalError: Referenced before assignment I'm aware this is a very common issue however I'm not seeing the issue.
The module works but every time a post is reacted to with a star it throws off this error in the console.
The error is:
starboard.py", line 22, in on_reaction_add
if emoji_count > 0: #if 0 then 1 counts
UnboundLocalError: local variable 'emoji_count' referenced before assignment
The area of more specific I'm looking at is:
async def on_reaction_add(self, reaction, user):
for guild in self.bot.guilds:
chan = get(guild.channels, name="starboard")
if chan:
if reaction.message.author == user:
return
if reaction.emoji == '⭐' or reaction.emoji == '🌟':
if not chan:
return
emoji_count = reaction.message.reactions[0].count
msg = f"{reaction.message.author.mention} your post was posted to starboard."
em = discord.Embed(color=discord.Color(random.randint(0x000000, 0xFFFFFF)))
display = f"""{reaction.message.content}"""
em.description = display
em.set_author(name=reaction.message.author.name, icon_url=reaction.message.author.avatar_url)
em.set_footer(text=f"Posted in: #{chan.name}")
em.timestamp = dt.datetime.utcnow()
try:
img_url = reaction.message.attachments[0].url
except IndexError:
img_url = None
if not img_url:
try:
img_url = reaction.message.embeds[0].url
except IndexError:
img_url = None
if img_url:
em.set_image(url=str(img_url))
if emoji_count > 0: #if 0 then 1 counts
if not chan:
return
await chan.send(msg)
await chan.send(embed=em)
If anyone can tell me whats going on here and where I'm going wrong I'd much appreciated it.
When your if statement condition in if reaction.emoji == '⭐' or reaction.emoji == '🌟': doesn't return True , emoji_count won't get initialized
(emoji_count = reaction.message.reactions[0].count)
So when you try to use it a couple of lines underneath in if emoji_count > 0: it causes
local variable 'emoji_count' referenced before assignment, which is exactly what it says, python not being able to find your variable's initialization anywhere in the code that ran
I think what is being said here is the following:
if emoji_count >= 2 :
if not chan:
return True
As said in the previous answers it should be
if reaction.emoji == '⭐' or reaction.emoji == '🌟' is True:

Resources