I am writing a telegram bot using the aiogram library. I have a FSM module that shows the inline keyboard on every step except the last one. At each step, the keyboard message from the previous step is deleted, but the last message cannot be deleted. This is due to the fact that in the previous steps I use the callback_query_handler handlers, and in the last one I use message_handler.
Is there any way to remove the await callback.message.answer('Введите максимальную цену:') message after the user enters a number?
from aiogram import types, Dispatcher
from create_bot import dp, bot
from keyboards.client_keyboards import city_keyboard, district_keyboard, rooms_keyboard, floor_keyboard
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
import datetime
from databases.users_db import sql_add_users_flat
class FSMAdmin(StatesGroup):
city = State()
district = State()
# neighborhood = State()
rooms = State()
floor = State()
# heating = State()
price = State()
async def cm_start(message: types.Message):
await FSMAdmin.city.set()
await message.answer('Выбери город:', reply_markup=city_keyboard)
# await message.delete()
async def canceling(callback: types.CallbackQuery,
state: FSMContext):
current_state = await state.get_state()
if current_state is None:
return
await state.finish()
await callback.message.answer('ОК')
await callback.answer()
async def city_choose(callback: types.CallbackQuery,
state: FSMContext):
async with state.proxy() as data:
data['datetime'] = datetime.datetime.now().strftime("%d-%m-%Y %H:%M")
data['name'] = callback.from_user.username
data['city'] = callback.data
await callback.message.delete()
await FSMAdmin.next()
await callback.message.answer('Выбери район:', reply_markup=district_keyboard)
async def district_choose(callback: types.CallbackQuery,
state: FSMContext):
async with state.proxy() as data:
data['district'] = callback.data
data['neigborhood'] = None # заполнитель для таблицы, пока не написана функция
await callback.message.delete()
await FSMAdmin.next()
await callback.message.answer('Выбери количество комнат:', reply_markup=rooms_keyboard)
async def rooms_choose(callback: types.CallbackQuery,
state: FSMContext):
async with state.proxy() as data:
data['rooms'] = callback.data
await callback.message.delete()
await FSMAdmin.next()
await callback.message.answer('Выбери этаж:', reply_markup=floor_keyboard)
async def floor_choose(callback: types.CallbackQuery,
state: FSMContext):
async with state.proxy() as data:
data['floor'] = callback.data
data['heating'] = None # заполнитель для таблицы, пока не написана функция
await callback.message.delete()
await FSMAdmin.next()
await callback.message.answer('Введите максимальную цену:')
# async def heating_choose(callback: types.CallbackQuery,
# state: FSMContext):
# async with state.proxy() as data:
# data['heating'] = callback.data
# await callback.message.delete()
# await FSMAdmin.next()
# await callback.message.answer('Введи макcимальную цену:')
async def max_price_choose(message: types.Message,
state: FSMContext):
async with state.proxy() as data:
try:
data['price'] = int(message.text)
except Exception:
await message.reply('Введи число нормально')
await message.answer(
f"Выбранные параметры:\nГород: {data['city']}\nРайон: {data['district']}"
f"\nКоличество комнат: {data['rooms']}\nЭтаж: {data['floor']}\nМаксимальная цена: {data['price']}")
await message.delete()
try:
await sql_add_users_flat(state)
await message.answer('Выбор сохранен')
except Exception:
await message.answer('Произошла ошибка, изменения не сохранены')
await state.finish()
def register_handlers_survey(dp: Dispatcher):
dp.register_message_handler(cm_start, text='Выбрать параметры', state=None)
dp.register_callback_query_handler(canceling, text='Отменить', state='*')
dp.register_callback_query_handler(city_choose, state=FSMAdmin.city)
dp.register_callback_query_handler(district_choose, state=FSMAdmin.district)
# dp.register_callback_query_handler(neighbohood_choose, state=FSMAdmin.neighbourhood)
dp.register_callback_query_handler(rooms_choose, state=FSMAdmin.rooms)
dp.register_callback_query_handler(floor_choose, state=FSMAdmin.floor)
# dp.register_callback_query_handler(heating_choose, state=FSMAdmin.rooms)
dp.register_message_handler(max_price_choose, state=FSMAdmin.price)
This is my code
Related
I have an unpleasant situation: I start the state machine and answer a sequence of questions, but suppose I didn’t want to answer to the end and called another command from the bot’s arsenal. The bot reacts like this: it continues to send questions from FSM, and a little later it freezes. Only reboot helps.
Here is the code:
class Protest(StatesGroup):
full_name = State()
team_name = State()
enemy = State()
date = State()
time = State()
cause = State()
#dp.message_handler(commands=["protest"], state=None)
async def start_protest(message: types.Message):
await Protest.full_name.set()
await message.answer("Введите Ваше ФИО:")
#dp.message_handler(state=Protest.full_name)
async def name_protest(message: types.Message, state=FSMContext):
async with state.proxy() as data:
data["full_name"] = message.text
await Protest.next()
await message.answer("Введите название Вашей команды:")
#dp.message_handler(state=Protest.team_name)
async def teem_name_protest(message: types.Message, state=FSMContext):
await Protest.next()
async with state.proxy() as data:
data["teem_name"] = message.text
await message.answer("Введите название команды соперника")
#dp.message_handler(state=Protest.enemy)
async def enemy_teem(message: types.Message, state=FSMContext):
await Protest.next()
async with state.proxy() as data:
data["enemy"] = message.text
calendar, step = DetailedTelegramCalendar(min_date=date.today(), locale="ru").build()
await bot.send_message(message.chat.id,
f"Выберите дату проведения матча:",
reply_markup=calendar)
#dp.callback_query_handler(DetailedTelegramCalendar.func(), state=Protest.date)
async def inline_kb_answer_callback_handler(query, state=FSMContext):
result, key, step = DetailedTelegramCalendar(locale="ru").process(query.data)
if not result and key:
await bot.edit_message_text(f"Выберите дату проведения матча",
query.message.chat.id,
query.message.message_id,
reply_markup=key)
elif result:
await bot.edit_message_text(f"Вы выбрали: {result}. Напишите время проведения матча",
query.message.chat.id,
query.message.message_id)
await Protest.next()
async with state.proxy() as data:
data["date"] = result
#dp.message_handler(state=Protest.time)
async def time_match(message: types.Message, state=FSMContext):
async with state.proxy() as data:
data["time"] = message.text
await Protest.next()
await message.answer("Назовите причину Вашего протеста")
#dp.message_handler(state=Protest.cause)
async def cause_protest(message: types.Message, state=FSMContext):
async with state.proxy() as data:
data["cause"] = message.text
await protest_db.sql_add_command(state)
await state.finish()
await message.answer("Ваш протест будет рассмотрен")
I have two versions:
Need to use try except
You need to install the bot on the server
I could send you my bot, but it is made in Russian, I'm afraid that it will be inconvenient for you to use it
User insert some values. Just for not spamming him with many messages, I want to pause sending them in loop and wait user tap inline button. So how can I pause them and wait for user tap "Next result" button? Code beneath just simplified, but works for better understanding scenario.
import os
from aiogram.dispatcher import FSMContext
from aiogram import Bot, Dispatcher, executor, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher.filters.state import StatesGroup, State
bot = Bot(token=os.environ["TOKEN"])
dispatcher = Dispatcher(bot, storage=MemoryStorage())
class Searches(StatesGroup):
by_id = State()
#dispatcher.message_handler(state='*', commands='cancel')
async def cancel_state(message: types.Message, state: FSMContext):
if await state.get_state():
await state.finish()
await message.reply("Search was canceled")
#dispatcher.message_handler(commands="search")
async def answer_search_message(message: types.Message):
find_by_id_button = types.InlineKeyboardButton("по ID", callback_data="by_id")
keyboard = types.InlineKeyboardMarkup().row(find_by_id_button)
await message.answer("Choose search type", parse_mode="HTML", reply_markup=keyboard)
#dispatcher.callback_query_handler(text="by_id")
async def callback_id(callback: types.CallbackQuery):
await callback.message.answer("Insert IDs, split them with ','")
await Searches.by_id.set()
#dispatcher.message_handler(state=Searches.by_id)
async def find_by_id(message: types.Message, state: FSMContext):
ids_from_message = [item for item in range(5)] # get IDs from user message
for index, item in enumerate(ids_from_message):
photo, caption, url = ("https://www.google.ru/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", "some text", "https://www.example.org/") # get some data by id
button_to_site = types.InlineKeyboardButton("More on site", url)
keyboard = types.InlineKeyboardMarkup().row(button_to_site)
if len(ids_from_message) > index + 1: # if this is not last result - show button "Next result"
keyboard.add(types.InlineKeyboardButton("Next result", callback_data="show_next"))
await message.answer_photo(photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
# loop should pause here, awaiting for user tap inline button "Next result"
else:
await message.answer_photo(photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
await state.finish()
executor.start_polling(dispatcher)
Well, internet, you always helps me, so here what I found as solution for this problem. You can just save some data for user and send it to him, then it trigger button. Full code below.
import os
from aiogram.dispatcher import FSMContext
from aiogram import Bot, Dispatcher, executor, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher.filters.state import StatesGroup, State
def get_record_by_id(doc_id):
# blah-blah some data generating
return "https://www.google.ru/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", "текст документа", "https://www.example.org/"
bot = Bot(token=os.environ["TOKEN"])
storage = MemoryStorage()
dispatcher = Dispatcher(bot, storage=storage)
class Searches(StatesGroup):
by_id = State()
#dispatcher.message_handler(state='*', commands='cancel')
async def cancel_state(message: types.Message, state: FSMContext):
if await state.get_state():
await state.finish()
await message.reply("Search was canceled")
#dispatcher.message_handler(commands="search")
async def answer_search_message(message: types.Message):
find_by_id_button = types.InlineKeyboardButton("by ID", callback_data="by_id")
keyboard = types.InlineKeyboardMarkup().row(find_by_id_button)
await message.answer("Choose search type", parse_mode="HTML", reply_markup=keyboard)
#dispatcher.callback_query_handler(text="by_id")
async def callback_id(callback: types.CallbackQuery):
await callback.message.answer("Insert IDs, split them with ','")
await Searches.by_id.set()
await callback.answer()
#dispatcher.message_handler(state=Searches.by_id)
async def find_by_id(message: types.Message, state: FSMContext):
user_id = message.from_user.id
ids_from_message = [item for item in range(5)]
photo, caption, url = get_record_by_id(ids_from_message[0])
button_to_site = types.InlineKeyboardButton("More on site", url)
keyboard = types.InlineKeyboardMarkup().row(button_to_site)
if len(ids_from_message) > 1:
del ids_from_message[0]
await storage.set_data(user=user_id, data={"ids_to_send": ids_from_message})
keyboard.add(types.InlineKeyboardButton("Next result", callback_data="show_next"))
await message.answer_photo(photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
else:
await message.answer_photo(photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
await state.finish()
#dispatcher.callback_query_handler(state=Searches.by_id, text="show_next")
async def callback_id(callback: types.CallbackQuery, state: FSMContext):
user_id = callback.from_user.id
chat_id = callback.message.chat.id
data = await storage.get_data(user=user_id)
photo, caption, url = get_record_by_id(data["ids_to_send"][0])
button_to_site = types.InlineKeyboardButton("More on site", url)
keyboard = types.InlineKeyboardMarkup().row(button_to_site)
if len(data["ids_to_send"]) > 1:
del data["ids_to_send"][0]
await storage.set_data(user=user_id, data={"ids_to_send": data["ids_to_send"]})
keyboard.add(types.InlineKeyboardButton("Next result", callback_data="show_next"))
await bot.send_photo(chat_id=chat_id, photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
else:
await bot.send_photo(chat_id=chat_id, photo=photo, caption=caption, parse_mode="HTML", reply_markup=keyboard)
await state.finish()
await callback.answer()
executor.start_polling(dispatcher)
so my discord server got hacked and everyone got banned with "gotcha" reason
is there a way to make this code read this reason and unban everyone that has it?
if it's not a big problem can it send this unbanned nicks or id's in the channel?
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.members = True
bot = commands.Bot(command_prefix = '!', intents=intents)
#bot.event
async def on_ready():
print('TO PRONTO FDP')
#bot.command()
async def pronto(ctx):
await ctx.send("Esperando...")
#bot.command()
async def massunban(ctx):
banlist = await ctx.guild.bans()
for users in banlist:
try:
await ctx.guild.unban(user=users.user)
await ctx.channel.send(f"UNBANNED: **{users.user}**")
except:
pass
await ctx.channel.send(f"Finalizado")
For discord.py 1.7.3 (stable)
#bot.command()
async def massunban(ctx: commands.Context):
bans = await ctx.guild.bans() # list of discord.BanEntry
for ban_entry in bans:
await ctx.guild.unban(user=ban_entry.user)
await ctx.send("Done!")
References:
discord.Guild.bans
discord.BanEntry
discord.Guild.unban
For discord.py 2.0 (latest)
#bot.command()
async def massunban(ctx: commands.Context):
# Because of a change in Discord's API,
# discord.Guild.bans() returns now a paginated iterator
# Flattening into a list
bans = [ban_entry async for ban_entry in ctx.guild.bans()] # list of discord.BanEntry
for ban_entry in bans:
await ctx.guild.unban(user=ban_entry.user)
await ctx.send("Done!")
References:
discord.Guild.bans
discord.BanEntry
discord.Guild.unban
i solved it
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.members = True
bot = commands.Bot(command_prefix = '!', intents=intents)
#bot.event
async def on_ready():
print('ready to go')
#bot.command()
async def help(ctx):
await ctx.send("!start")
#bot.command()
async def start(ctx):
reason = "Zardex V3"
reason2 = "Zardex v3"
reason3 = "zardex v3"
reason4 = None
await ctx.channel.send(f"*Loading...*")
print('Loading...')
banlist = await ctx.guild.bans()
for users in banlist:
if (reason==users.reason):
try:
await ctx.guild.unban(user=users.user)
await ctx.channel.send(f"Unbanned: **{users.user}** Reason: **{users.reason}**")
except:
pass
if (reason2==users.reason):
try:
await ctx.guild.unban(user=users.user)
await ctx.channel.send(f"Unbanned: **{users.user}** Reason: **{users.reason}**")
except:
pass
if (reason3==users.reason):
try:
await ctx.guild.unban(user=users.user)
await ctx.channel.send(f"Unbanned: **{users.user}** Reason: **{users.reason}**")
except:
pass
if (reason4==users.reason):
try:
await ctx.guild.unban(user=users.user)
await ctx.channel.send(f"Unbanned: **{users.user}** Reason: **{users.reason}**")
except:
pass
await ctx.channel.send(f"*Finished.*")
print('Finished.')
I just implemented my very first telegram bot with aiogram. I used the finite state example provided in their API documentation. However, I have a pretty major issue. I also need to integrate webhook. How could I do this without making major changes to everything? My version of the code needs to send an image to the user, which is created by utilising the data that the user inputs.
I need to deploy it on Heroku, so there's that.
Here is the example.
import logging
import aiogram.utils.markdown as md
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters import Text
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.types import ParseMode
from aiogram.utils import executor
logging.basicConfig(level=logging.INFO)
API_TOKEN = 'BOT TOKEN HERE'
bot = Bot(token=API_TOKEN)
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)
class Form(StatesGroup):
name = State()
age = State()
gender = State()
#dp.message_handler(commands='start')
async def cmd_start(message: types.Message):
await Form.name.set()
await message.reply("Hi there! What's your name?")
#dp.message_handler(state='*', commands='cancel')
#dp.message_handler(Text(equals='cancel', ignore_case=True), state='*')
async def cancel_handler(message: types.Message, state: FSMContext):
current_state = await state.get_state()
if current_state is None:
return
logging.info('Cancelling state %r', current_state)
await state.finish()
await message.reply('Cancelled.', reply_markup=types.ReplyKeyboardRemove())
#dp.message_handler(state=Form.name)
async def process_name(message: types.Message, state: FSMContext):
Process user name
async with state.proxy() as data:
data['name'] = message.text
await Form.next()
await message.reply("How old are you?")
#dp.message_handler(lambda message: not message.text.isdigit(), state=Form.age)
async def process_age_invalid(message: types.Message):
return await message.reply("Age gotta be a number.\nHow old are you? (digits only)")
#dp.message_handler(lambda message: message.text.isdigit(), state=Form.age)
async def process_age(message: types.Message, state: FSMContext):
await Form.next()
await state.update_data(age=int(message.text))
markup = types.ReplyKeyboardMarkup(resize_keyboard=True, selective=True)
markup.add("Male", "Female")
markup.add("Other")
await message.reply("What is your gender?", reply_markup=markup)
#dp.message_handler(lambda message: message.text not in ["Male", "Female", "Other"], state=Form.gender)
async def process_gender_invalid(message: types.Message):
return await message.reply("Bad gender name. Choose your gender from the keyboard.")
#dp.message_handler(state=Form.gender)
async def process_gender(message: types.Message, state: FSMContext):
async with state.proxy() as data:
data['gender'] = message.text
markup = types.ReplyKeyboardRemove()
await bot.send_message(
message.chat.id,
md.text(
md.text('Hi! Nice to meet you,', md.bold(data['name'])),
md.text('Age:', md.code(data['age'])),
md.text('Gender:', data['gender']),
sep='\n',
),
reply_markup=markup,
parse_mode=ParseMode.MARKDOWN,
)
await state.finish()
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)
Hello I'm trying to create a music bot but I want it so I can play music and add additional music to a queue with the same command. I've tried to do this but I can't get it to work. Here's the code with play and queue being two separate commands:
#bot.command(pass_context=True)
async def join(ctx):
await bot.join_voice_channel(bot.get_channel('487315853482786820'))
#bot.command(pass_context=True)
async def leave(ctx):
voice_client = bot.voice_client_in(ctx.message.server)
await voice_client.disconnect()
players = {}
queues = {}
def check_queue(id):
if queues[id] != []:
player = queues[id].pop(0)
players[id] = player
player.start()
#bot.command(pass_context=True)
async def play(ctx, url):
server = ctx.message.server
voice_client = bot.voice_client_in(server)
player = await voice_client.create_ytdl_player(url, after=lambda: check_queue(server.id))
players[server.id] = player
player.start()
#bot.command(pass_context=True)
async def queue(ctx, url):
server = ctx.message.server
voice_client = bot.voice_client_in(server)
player = await voice_client.create_ytdl_player(url, after=lambda: check_queue(server.id))
if server.id in queues:
queues[server.id].append(player)
else:
queues[server.id] = [player]
await bot.say('Video queued.')
#bot.command(pass_context=True)
async def pause(ctx):
id = ctx.message.server.id
players[id].pause()
#bot.command(pass_context=True)
async def stop(ctx):
id = ctx.message.server.id
players[id].stop()
#bot.command(pass_context=True)
async def resume(ctx):
id = ctx.message.server.id
players[id].resume()
You need some sort of queue system where song requests are stored in the queue and a loop that checks if any item is in the queue, grabbing the first item in the queue if it's available. If no songs are in the queue, then the loop waits until a song is added. You can use asyncio.Queue() and asyncio.Event() to do this.
import asyncio
from discord.ext import commands
client = commands.Bot(command_prefix='!')
songs = asyncio.Queue()
play_next_song = asyncio.Event()
#client.event
async def on_ready():
print('client ready')
async def audio_player_task():
while True:
play_next_song.clear()
current = await songs.get()
current.start()
await play_next_song.wait()
def toggle_next():
client.loop.call_soon_threadsafe(play_next_song.set)
#client.command(pass_context=True)
async def play(ctx, url):
if not client.is_voice_connected(ctx.message.server):
voice = await client.join_voice_channel(ctx.message.author.voice_channel)
else:
voice = client.voice_client_in(ctx.message.server)
player = await voice.create_ytdl_player(url, after=toggle_next)
await songs.put(player)
client.loop.create_task(audio_player_task())
client.run('token')