how to read a line in discord - python

I am writing a bot for discord and there is a command ($ bone) in which the bot writes the following: "select a number from 1 - 9" and after that the user should answer and here's how I can read the line in which he wrote the number?

As alluded to in the comments, we will use a coroutine called wait_for, that waits for events like message: https://discordpy.readthedocs.io/en/latest/api.html#discord.Client.wait_for
Because these are the same events we might hook into with bot.event, we will be able to process the returned message.
#bot.command()
async def bone(ctx):
await ctx.send("Select a number 1-9")
def my_check_func(message): #A checking function: When we return True, consider our message "passed".
if message.author == ctx.author: #Make sure we're only responding to the original invoker
return True
else:
return False
response = await bot.wait_for("message", check=my_check_func) #This waits for the first message event that passes the check function.
#This execution process now freezes until a message that passes that check is sent,
#but other bot interactions can still happen.
c = response.content #equal to message.content, because response IS a message
if c.isdigit() and 1 <= int(c) <= 9: #if our response is a number equal to 1, 9, or between those...
await ctx.send("You sent: {}".format(response.content))
else:
await ctx.send("That's not a number between 1 and 9!")
Real example interaction to prove it works:
Cook1
$bone
Tutbot
Select a number 1-9
Cook1
4
Tutbot
You sent: 4
Cook1
$bone
Tutbot
Select a number 1-9
Cook1
no
Tutbot
That's not a number between 1 and 9!

Related

Discord bot line of code not working, in an if statement

so I've been stuck on this for a couple days now, and I can't seem to figure it out. The program is supposed to send characters on an adventure, and when typing a non existing character the bot recognizes that, but when typing an existing character the bot just does not respond back.
for i, slot in enumerate(adventure_slots):
if not slot:
adventure_slots[i] = True
break
else:
return
await ctx.send(f"{character_name} has been sent on an adventure! They will return in 3 hours.")
await asyncio.sleep(3 * 3600) # 3 hours in seconds
await ctx.author.send(f"{character_name}'s adventure is complete! You can collect your loot now.")
session.close()
for i, slot in enumerate(adventure_slots):
if not slot:
adventure_slots[i] = True
break
else:
return
return leaves the function. I am assuming what you mean is simply:
for i, slot in enumerate(adventure_slots):
if not slot:
adventure_slots[i] = True
break

discord bot (python) "wait_for" "reaction_add" duplicating embed

I've been having an issue with my attempt to make a embed that reveals a list of characters that a play owns. I basically want to have the :left_arrow: and :right_arrow: emojis as reaction to pan left and right between the long database of characters a user owns.
I did in fact manage to do that however I noticed that if I use the command which shows an embed with all the characters twice before the timeout of one and then i click the left arrow to see the next page it goes to the next page on both embeds instead of just the one i reacted to.
Here's my source code.
#commands.command(aliases=["collection", "c"])
#commands.cooldown(1, 5, commands.BucketType.user)
async def characters(self, ctx):
if not await bot.check_channel_permissions(ctx.author, ctx.channel): # Checks if the bot can send messages in the channel.
return
playerData = await bot.get_player_data(ctx.author, ctx.channel) # Returns the user's data.
if playerData:
if len(playerData["Characters"]) > 0: # Checks amount of characters the user owns.
if len(playerData["Characters"]) > 20: # Player owns 20 so it will create an embed with reactions.
global pageNumber
global maxPages
pageNumber = 1
maxPages = math.ceil(len(playerData["Characters"]) / 20)
embed = await characters.setupEmbed(self, playerData, ctx.author, pageNumber)
currentEmbed = await ctx.channel.send(ctx.author.mention, embed=embed)
await currentEmbed.add_reaction("⬅")
await currentEmbed.add_reaction("➡")
async def listenForRection(self, mainUser): # Waits until the user reacts with the left or right arrow in order to edit the current embed.
global pageNumber # the current page (20 characters are shown a page)
global maxPages # the max amount of pages the user can pan between.
def check(reaction, reactionuser):
if reactionuser != mainUser:
return False
elif str(reaction.emoji) == '⬅' and pageNumber > 1:
return True
elif str(reaction.emoji) == '➡' and pageNumber < maxPages:
return True
else:
return False
try:
reaction, reactionuser = await self.client.wait_for(
"reaction_add",
timeout=60,
check=check
)
except:
asyncio.TimeoutError
await currentEmbed.edit(content="Timed out.")
else:
if str(reaction.emoji) == '⬅':
pageNumber += -1
elif str(reaction.emoji) == "➡":
pageNumber += 1
newEmbed = await characters.setupEmbed(self, playerData, mainUser, pageNumber) # Returns an embeded list given the page number and info on the user.
await currentEmbed.edit(embed=newEmbed) # re-edits the current embed to show the next or previous page.
await listenForRection(self, mainUser) # re-runs the function again to listen for the next reaction.
await listenForRection(self, ctx.author) # runs the listener once so that it can begin detecting and re-running on its own.
else: # player will get an embeded list with no reactions since they only have 1 page of characters.
embed = await characters.setupEmbed(self, playerData, ctx.author, 1)
await ctx.channel.send(ctx.author.mention, embed=embed)
else:
await ctx.reply("You don't have any characters.")
What would happen is I run the command once and it works perfectly fine. Then when I run it again before the timeout and click a reaction it either wouldn't work or run both on the new embed and the old. I spent a ton of hours trying multiple things and searching but I didn't manage to do much apart from creating a new bug right after i fix the current.
There is actually an ext library for making paginated menus called discord-ext-menus. It does most of the work for you so you don't have to create your own.

Making a guessing game in discord.py

I have been trying to make a discord bot which starts a game when !play message is sent. In the game there is a variable whose value is chosen randomly and if you predict the right number (between 1 and 10) then a "You win" message will be sent and if the it is greater or less then a message will be sent accordingly. I know I am messing it because I am using discord.py for the first time.
My code:
if message.content.startswith("!play"):
await message.channel.send("Choose a number between 1-10. Enter numerical values only.")
num = random.randint(1,10)
try:
if message.content.startswith(str(num)):
await message.channel.send("You won.")
elif int(message.content) > num:
await message.channel.send("Go a bit lower.")
elif int(message.content) < num:
await message.channel.send("Go a bit up.")
except Exception as e:
await message.channel.send("Check inputs.")
Please help
Here:
# imports the discord module and the random module.
import discord
from discord.ext import commands
import random
client = discord.Client()
client = commands.Bot(command_prefix = "!")
Token = "" #your token
#client.event
async def on_message(message):
if message.content.startswith("!play"): #the play command to start the guessing game.
channel = message.channel
await channel.send("Choose a number between 1-10. Enter numerical values only.") #message that tells about the start of the game
# generates a random number and turns it into a string
number1 = random.randint(1,10)
number2 = str(number1)
def check(m):
return m.content == number2 and m.channel == channel
"""
The check function, first it checks if the message is the correct number.
Then it checks if the channel is the same channel as the channel that the play command was sent in.
If both is true, then it returns true. Else it returns false.
"""
msg = await client.wait_for('message', check=check) #waits for the check function to return true
await channel.send("Correct answer {.author}" .format(msg)) #sends the correct answer message
client.run(Token)
This should work.
I have a working bot here: https://discord.com/api/oauth2/authorize?client_id=793152585346711573&permissions=8&scope=bot
Example: https://i.stack.imgur.com/ou60F.png

How to make a certain function stop executing when a certain condition is met

I am designing a discord bot that loops certain text when a certain conditions are met, which then triggers this function to run.
async def inform(self,message):
while flag==1:
await message.channel.send("text")
await asyncio.sleep(5)
await message.channel.send("text2")
await asyncio.sleep(5)
await message.channel.send("text3")
await asyncio.sleep(5)
Now the problem is when the conditions are not met anymore the function completes the entire cycle before haulting. I want it to stop the moment the condition is not satisfied anymore.
I thought of adding an
if flag==0:
return
After every line but that is not the elegant solution I am looking for.
I don't want the entire code to stop running, but just this function.
I am a beginner to python and any insights are welcome :)
Thank You!
A while loop, before each iteration will look if the condition is True, if it is, it will continue, if not it will stop.
while flag == 1:
if 'some condition met':
flag = 0
# And the loop will stop
The loop will stop by itself, nothing more. Also it's better to use booleans.
Here's your code a bit improved
# Defining variables
flag = True
iteration = 0
while flag:
# Adding `1` each iteration
iteration += 1
# Sending the message
await message.channel.send(f'text{iteration}')
# Sleeping
await asyncio.sleep(5)
If I got it right, You want to check if a condition is met. Here is an infinite loop until some criteria is met. If this is not what you wanted please explain it again.
import random
msgs = ['msg1','msg2','msg3']
condition_met = False
while True:
if condition_met:
break
# also return works here
else:
await message.channel.send(random.choise(msgs))
await asyncio.sleep(5)
if something == 'Condition met here':
condition_met = True
Each iteration of the loop should only send one message, to keep track of the order of messages sent, I used the variable i which is incremented every time a message is sent.
async def inform(self,message):
i = 0
while flag==1:
if i == 0:
await message.channel.send("text")
await asyncio.sleep(5)
elif i == 1:
await message.channel.send("text2")
await asyncio.sleep(5)
elif i == 2:
await message.channel.send("text3")
await asyncio.sleep(5)
i = (i + 1) % 3

Discord Python Bot: Clear Command If statements

I am currently tying to make a discord bot in python. It has been about 1 week since I started learning and I thought I would give this a go. I am trying to make a clear function to have it clear the chat. I want to make the bot say "please enter a valid number". If you type anything else other than an int ex. some char.
For example when I put ".clear t" it wont do anything and it gets angry in the terminal. When I put a valid argument such as ".clear 3" it will throw up the "please enter a valid number." after clearing all of it.
I tried different variations of the if statement including placement. Can't figure it out. I have a feeling it be something about where to place the if statement. Thank you for taking the time to read.
#client.command(pass_context = True)
async def clear(ctx, number = 5):
number = int(number)
counter = 0
channel = ctx.message.channel
async for x in client.logs_from(ctx.message.channel, limit = number):
if counter < number:
await client.delete_message(x)
counter += 1
await asyncio.sleep(0.5)
if number != int():
await client.send_message(channel, "Please enter a valid number.")
You need to learn how to properly check an object's type. In your case, if number = 5, and you do if number != int():, since int() returns 0, you're basically saying if 5 != 0:, which is always.
To overcome the aforementioned, useisinstance(obj, type). However, it doesn't matter since number will always be a string (unless the default value is used).
Having the following line will raise an error if number cannot be converted into an integer.
number = int(number)
Which raises a ValueError, you need to catch that in order to send your error message.
#client.command(pass_context=True)
async def clear(ctx, number=5):
channel = ctx.message.channel
try:
number = int(number)
except ValueError:
return await client.send_message(channel, "Please enter a valid number.")
async for x in client.logs_from(channel, limit=number):
await client.delete_message(x)
await asyncio.sleep(0.5)
With discord.py however, there's an easier way to do type conversations, by using the type annotation syntax:
#client.command(pass_context=True)
async def clear(ctx, number: int=5):
channel = ctx.message.channel
async for x in client.logs_from(channel, limit=number):
await client.delete_message(x)
await asyncio.sleep(0.5)
If you desperately need to send that error message to the channel, you can do it from the on_command_error event. (Search their documentation)
You might've noticed that I removed the counter part, since limit does exactly the same thing. (Removed redundancy)

Categories

Resources