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)
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
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
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')
I want the bot to react to its own message with the ✅ and ❎ emojis for a suggestion command. Here is the code. How do i do it?
import discord
from discord.ext import commands
class suggestions(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.command(description = 'Add a suggestion for this community!')
async def suggest(self, ctx, *,suggestion):
await ctx.channel.purge(limit = 1)
channel = discord.utils.get(ctx.guild.text_channels, name = '💡│suggestions')
suggestEmbed = discord.Embed(colour = 0xFF0000)
suggestEmbed.set_author(name=f'Suggested by {ctx.message.author}', icon_url = f'{ctx.author.avatar_url}')
suggestEmbed.add_field(name = 'New suggestion!', value = f'{suggestion}')
await channel.send(embed=suggestEmbed)
def setup(bot):
bot.add_cog(suggestions(bot))
Messageable.send returns a discord.Message object, you can then simply .add_reaction
message = await ctx.send(embed=suggestEmbed)
await message.add_reaction('✅')
await message.add_reaction('❌')
Note: You need the unicode of the emoji to react, to get it simply \:{emoji}:
Reference:
Messageable.send
Message.add_reaction
Me, I use this:
#bot.command()
async def suggestion(ctx, *, content: str):
title, description= content.split('/')
embed = discord.Embed(title=title, description=description, color=0x00ff40)
channel = bot.get_channel(insert the channel ID here)
vote = await channel.send(embed=embed)
await vote.add_reaction("✅")
await vote.add_reaction("❌")
await ctx.send("your suggestion has been send")
You can vote using the emojis, enjoy!