How to execute definite handler-function in aiogram (Telegram API)? - python

I have got a Telegram-bot in AIOgram. My aim in this bot is to run it and if user writes secret phrase - bot must send secret message.
My code in main.py file:
from aiogram.dispatcher.filters import Text
from aiogram.dispatcher import FSMContext
from database import Database
from states import *
from aiogram import Bot, Dispatcher, executor, types
bot = Bot(token="my_token")
dp = Dispatcher(bot)
#dp.message_handler(commands="start")
async def start(message: types.Message):
keyboard1 = types.InlineKeyboardMarkup()
links = ["one", "two", "three"]
for row in links:
button = types.InlineKeyboardButton(text=row[0])
keyboard1.add(button)
await message.answer("Chose the phrase", reply_markup=keyboard1)
# options - is the next handler function
dp.register_message_handler(options, state="*")
async def options(message: types.Message):
if message.text == "Secret phrase":
keyboard = types.ReplyKeyboardMarkup(one_time_keyboard=True)
keyboard.add(types.KeyboardButton(text="Secret 1"),
types.KeyboardButton(text="Secret 2"),
types.KeyboardButton(text="Secret 3"),
types.KeyboardButton(text="Main menu"))
await message.answer("Chose the phrase", reply_markup=keyboard)
dp.register_message_handler(workingWithLinks, state="*")
else:
await message.answer("This command is error, for phrases update call command /update")
async def workingWithLinks(message: types.Message):
if message.text == "Secret 1":
await message.answer("This is secret number 1")
await SecretOne.step_one.set()
elif message.text == "Secret 2":
await SecretTwo.step_one.set()
await message.answer("This is secret 2")
elif message.text == "Secret 3":
await SecretThree.step_one.set()
await message.answer("This is secret 3")
else:
await message.answer("This command is error, for phrases update call command /update")
def register_handlers_common(dp: Dispatcher):
dp.register_message_handler(start, commands="start", state="*")
dp.register_message_handler(start, commands="update", state="*")
if __name__ == "__main__":
register_handlers_common(dp)
executor.start_polling(dp, skip_updates=True)
My code in states.py file:
from aiogram.dispatcher.filters.state import State, StatesGroup
class SecretOne(StatesGroup):
step_one = State()
step_two = State()
step_three = State()
class SecretTwo(StatesGroup):
step_one = State()
step_two = State()
step_three = State()
class SecretThree(StatesGroup):
step_one = State()
step_two = State()
step_three = State()
Scenario of my bot is next: I click command /start, and bot sends me message "Chose the phrase" with three inline-buttons - "one", "two" and "three". And if user don't click on one of this button and type "Secret phrase" - program link user to "options" handler-function. This is string, where linking is:
dp.register_message_handler(options, state="*")
"options" is handler-function. This link works. But, if in "options" I chose the phrase "Secret 1" - link on workingWithLinks handler-function doesn't work.
Linking on workingWithLinks handler-function:
dp.register_message_handler(workingWithLinks, state="*")
Also I tried to link on next handler-function with states as in this tutorial, but this also doesn't works.
How to link on workingWithLinks handler-function?

Don't register message handlers in message handlers!
dp.register_message_handler(options, state="*")
This string will add handler to the stack on every message call.
Answer
If you wanna get something from user - use states.
Look at the FSM example

Related

How to make author only click button

i want to make author only button if someone clicked and if this is not the author of the message, then the bot will send an error.but my code is not working
#bot.command()
async def button(ctx):
await ctx.send(content="test", components=[Button(style=ButtonStyle.blue, label="Default Button", custom_id="button")])
interaction = await bot.wait_for("button_click", check = lambda i: i.custom_id == "button")
if interaction.user == ctx.author:
await ctx.send("successful test")
Change the check function to this:
check = lambda i: i.custom_id == "button" and i.user == ctx.author
This checks if the user associated with the interaction is the author of the message.

How to stop schedule multiple times and restart if needed. Python. Telebot

I'm trying to make /stop command for the bot (the greetings stop to send). It works, but only one time, after pressing the /start command it is unable to stop again and runs forever.
Tried different methods of stopping, manged either to stop and unable to restart or to stop once and unable to stop after repeating /start or "greet".
I would be very grateful for the advice of how to improve the existing code
import random, telebot, schedule, time
from telebot import types
from config import token
bot = telebot.TeleBot(token)
greetings = ["hello", "Have a nice day", "Goodbye", "Wow"]
#bot.message_handler(commands=['start'])
def start_message(message):
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
item1 = types.KeyboardButton("Greet")
markup.add(item1)
bot.send_message(message.chat.id, "Press button", reply_markup=markup)
#bot.message_handler(content_types=["text"])
def run_function(message):
def function_to_run():
random_greet = random.choice(greetings)
bot.send_message(message.chat.id, random_greet)
def scheduling():
schedule.every(4).seconds.do(function_to_run).tag('schedule')
scheduling()
if message.text.lower() == "greet":
bot.send_message(message.chat.id, "here you go")
function_to_run()
def schedule_run():
while True:
schedule.run_pending()
time.sleep(1)
schedule_run()
elif message.text.lower() == "stop":
bot.send_message(message.chat.id, "stopping...")
schedule.clear('schedule')
time.sleep(1)
bot.send_message(message.chat.id, "stopped successfully")
bot.polling()
import random, telebot, schedule, time
from telebot import types
from config import token
bot = telebot.TeleBot(token)
greetings = ["hello", "Have a nice day", "Goodbye", "Wow"]
#bot.message_handler(commands=['start', 'stop'])
def start_message(message):
command = message.text.split()[0]
if command == "/start":
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
item1 = types.KeyboardButton("Greet")
markup.add(item1)
bot.send_message(message.chat.id, "Press button", reply_markup=markup)
elif command == "/stop":
bot.send_message(message.chat.id, "stopping...")
schedule.clear('schedule')
time.sleep(1)
bot.send_message(message.chat.id, "stopped successfully")
exit()
#bot.message_handler(content_types=["text"])
def run_function(message):
...
# or
elif message.text.lower() == "stop":
bot.send_message(message.chat.id, "stopping...")
schedule.clear('schedule')
time.sleep(1)
bot.send_message(message.chat.id, "stopped successfully")
exit()
bot.polling()
if you want to on it again try this:
import random, telebot, schedule, time
from telebot import types
from config import token
bot = telebot.TeleBot(token)
greetings = ["hello", "Have a nice day", "Goodbye", "Wow"]
on = True
#bot.message_handler(commands=['start', 'stop'])
def start_message(message):
global on
command = message.text.split()[0]
if command == "/start" and not on:
on = True
# your code
if command == "/stop" and on:
on = False
# your code
#bot.message_handler(content_types=["text"])
def run_function(message):
global on
if on:
# your code
bot.polling()

Discord.py creating a reaction game

What I've tried
StackOverflow articles
Need help to fix a certain bug in rock, scissor and paper.
discord.py documentation
How can I add reaction to a message
(checked plenty of articles, but I'm not going to add them in order to get into the point)
I've managed to retrieve the emoji from the user in the check method, but still the issue with timeouterror appears, so there is something I'm missing which I can't get a hold on.
In this senario im using Cog,
The issue
Even though the wait_for passes the check method, I'm not sure how to send it forward to the logical conditon. The result I get is passed in the exception.
bot.py
import os
from dotenv import load_dotenv
from discord import Intents
# lib
from lib.cog.miniGames import miniGames # Games
from lib.client.krigjo25 import krigjo25
# Importing .evn file
load_dotenv()
def botSetup ():
# necsessary values from .env
botKey = os.getenv('Token')
server = os.getenv('server')
# discord configs
intents= Intents.all()
#retrieving the module command prefix is created in the bot module
bot=krigjo25(intents=intents)
# adding cogs into the bot
bot.add_cog(miniGames(bot))
bot.run(botKey)
if __name__ == '__main__':
botSetup()
miniGames.py
class miniGames(Cog, name='miniGames module'):
def __init__(self, bot):
self.bot = bot
#command(name='rsp')
#Cog.listener()
# 1: Creating a game where the user has three choices to choose between from
# 2: The bot adds 3 reactions, where you have to choose one of them as an answer
# 3: The bot checks wheter the conditions is true or false, in order to send the correct messge:
async def RockscissorsPaper(self, message:Message):
# Declearing variables
# Rock, Scissors, Paper
rock,scissor, paper = '\U0001FAA8', '\U00002702','\U0001F4C4'
answer = { 0:'\U0001FAA8',
1:'\U00002702',
2:'\U0001F4C4'}
# Randomizing the answer and assign it to the variable
shuffle(answer)
x = randrange(0,2)
answer = '\U0001FAA8' #answer.get(x)
print(answer)
# Creating Embed message
embed = Embed(color = Color.blurple())
embed.title = 'Rock, Scissors & Paper'
embed.description = ' In order to play the game, please click one one of the following reactions'
# Sending & adding reactions
ch = await message.channel.send(embed=embed)
await ch.add_reaction(rock)
await ch.add_reaction(scissor)
await ch.add_reaction(paper)
# passes the check method
def check( user, reaction):
print('check function', reaction, user)
return user == message.author and reaction == str(reaction.emoji)
# Checks the user's input¨'
# Questions : Is it possible to use dictionaries in order to find the given emoji?
try :
# How does this work? If wait_for is a "asynico" function how can it work like this?
reaction, user = await self.bot.wait_for('reaction_add', timeout=30, check=check)
except TimeoutError as e:
print(e)
else:
reaction = str(reaction.emoji)
# Checking if its the bots reaction
if user == self.bot.user:
return None
else:
if reaction == '\U0001F4C4' and answer == '\U0001FAA8':
await message.channel.send(' you won')
Add this at the beginning of your code:
intents = discord.Intents.all()
Then add this into your discord.Bot's declaration:
bot = discord.commands.Bot(prefixes=['!'], intents=intents)
Then go to Discord Developer Portal and enable all the intents writing from the Application's "bot" page.
can u remove this line?
#command(name='rsp')
--> #Cog.listener()
# 1: Creating a game where the user has three choices to choose between from
What is this listener supposed to answer?
It appears that i had to change :
def check( user, reaction):
print('check function', reaction, user)
return user == message.author and reaction == str(reaction.emoji)
New
def emojiCheck(reaction, member):
reaction = str(reaction)
member == ctx.author.name
# Do not return the bot's last reaction
return member !=self.bot.user and reaction
--
I also had to add the code block in the try - except statement.
** OLD **
try :
# How does this work? If wait_for is a "asynico" function how can it work like this?
reaction, user = await self.bot.wait_for('reaction_add', timeout=30, check=check)
except TimeoutError as e:
print(e)
else:
reaction = str(reaction.emoji)
# Checking if its the bots reaction
if user == self.bot.user:
return None
else:
if reaction == '\U0001F4C4' and answer == '\U0001FAA8':
await message.channel.send(' you won')
New
try:
# Timer Check
reaction, member = await self.bot.wait_for('reaction_add', timeout=60.0, check=emojiCheck)
print(member, reaction, answer)
# Dictionaries
# Tie
tie = {
0:f'{self.bot.user} draws a **tie** for {member}',
1:'Sir, lets **tie** a **tie**',
2:'What did the **tie** say to the bowtie? You\'re a weirdo',
3:'lets have a wii-match',
}
reactionRock = {
}
# Randomize the dictionary
shuffle(tie)
x = randrange(0,3)
tie = tie.get(x)
# If the situation is a draw / tie
if str(reaction) == answer:
self.embed.description = tie
await ctx.send(embed = self.embed)
The point is if-elif or else statement has to be inside the try-except statement.
Hope this is for help for other developers which has the same issue.
Thanks for the assistance which I received. Its appreciated.

Is there anyway to use a dropdown without sending a response in discord.py?

I have made a dropdown select in discord.py but when the user selects an option i dont want to respond with a message but if i dont respond with a message i get "Interaction Failed" in discord.
#commands.command()
async def shop(self,ctx):
em = discord.Embed(title = "Store",description = "Buy smthing", colour = ctx.author.colour)
await ctx.send(embed = em,
components=
[
Select(placeholder="Choose a item",
options=[
SelectOption(
label = "A",
value = "a",
description = ""
),
SelectOption(
label = "B",
value = "b",
description = ""
),
]),
Button(style = ButtonStyle.blue, label="button 1")
])
def check(msg):
return msg.author == ctx.author and msg.channel == ctx.channel
while True:
try:
res = await self.client.wait_for("select_option", check=check, timeout=10)
label = res.values[0]
await res.respond(content=f"U have clicked {label}") #If I don't write this line i get the message "Interaction failed"
print(label) #This was just for checking the output.
break
except asyncio.TimeoutError:
await ctx.send("Sorry, you didn't reply in time!")
break
except discord.NotFound:
print("error")
I dont fully understand how dropdown works, can someone please explain how to go abt this.
For example dank memer doesnt send response when i click on a option.
NO respones from bot when i clicked on the option
You can try
res.edit_origin(content = "This message can be exactly the same as your original message")
This function does not send any message, but it does stop the interaction failed message to appear
You can also do the following to change the embed and buttons / select menu:
res.edit_origin(content = "just some random message",component =[newButtons],embed=EmbedObject)
BTW: The image in your question was done by disabling the message's button (basically editing the buttons).
You can try this :
await interaction.response.defer()
will complete the interaction without the error
This interaction failed'
This is especially helpful while doing edit messages or add roles to users without any output
You must respond to a message when a dropdown is clicked. This is a Discord API limit.
There is a way but it isn't that clean:
#client.command(name='test')
async def text(ctx):
global ctxn #make a global variable named for example ctxn
ctxn = ctx #double the value from the ctx ;)
global msgTest
#Define the Fields you want to have in the Dropdown
msgTest = await ctx.send(f'```Select a component```',components = [
[Select(placeholder="Select Settings",
options=[SelectOption(label="Label1", value="Value1"), #Label is the Display Name, Value is for Interaction Selection
SelectOption(label="Label2", value="Value2"),
SelectOption(label="Label3", value="Value3")])]
]
)
#client.event
async def on_select_option(interaction):
#After the selection it searches for the value="XXXXX"
if interaction.values[0] == "Value1":
await msgTest.delete() #Here you delete the dropdown after you selected an entry
await TEST1(ctx=ctxn) #Call the Function with the copied ctx start value
if interaction.values[0] == "Value2":
await msgTest.delete() #Here you delete the dropdown after you selected an entry
await TEST2(ctx=ctxn) #Call the Function with the copied ctx start value
if interaction.values[0] == "Value3":
await msgTest.delete() #Here you delete the dropdown after you selected an entry
await TEST3(ctx=ctxn) #Call the Function with the copied ctx start value
#Here you can programm your functions
async def TEST1(ctx): #First Function
return await ctx.send("Function 1 were selected")
async def TEST2(ctx): #Second Function
return await ctx.send("Function 2 were selected")
async def TEST3(ctx): #Third Function
return await ctx.send("Function 3 were selected")
If you don't delete the dropdown menu which you sendet within the msgTest variable, you will still get the interaction failed msg.

How can I get user input in a Python Discord Bot (Rewrite)?

I have a discord bot that I'm making on Python 3.8.5. I am printing this menu via the proper syntax and commands. Now I want the user to type number 1 or 2 depending on what option he wants to select. Here is my code:
async def menu(self, ctx):
await ctx.message.delete()
embed = discord.Embed(
title = "Heartbeat Menu",
description = "Select and enter a number.\n 1. Check Status of Heartbeat\n 2. Hearbeat Check"
)
sent = await ctx.send(embed=embed)
It works properly. What code should be written that when the user types 1 or 2, the bot recognizes that and prints something else.
#client.command()
async def menu(ctx):
await ctx.message.delete()
embed = discord.Embed(
title = "Heartbeat Menu",
description = "Select and enter a number.\n 1. Check Status of Heartbeat\n 2. Hearbeat Check"
)
sent = await ctx.send(embed=embed)
def check(msg):
return msg.author == ctx.author and msg.channel == ctx.channel and msg.content in ["1", "2"]
msg = await client.wait_for("message", check=check)
if msg.content == "1":
#code for number 1
else:
#code for number 2
I used client.wait_for method to wait for the user input and the check() function inside the menu function will check for the right user , channel , message is 1/2 and if it is satisfied, then the #code for number 1 or #code for number 2 will be executed! if you still have trouble understanding i recommend you to read the document!

Categories

Resources