State machine freezes when switching - python

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

Related

Delete previous message in Python aiogram FSM

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

Is it possible to somehow define create_thread in PyCord

Hello I encountered the error 'Message' object has no attribute 'create_thread'
Code:
#commands.command()
async def sugesstion(self, ctx, *,sugestia):
await ctx.message.delete()
embed = discord.Embed(colour = 0xFFA500)
embed.set_author(name=f' {ctx.message.author}', icon_url = f'{ctx.author.avatar_url}')
embed.add_field(name = 'Member,', value = f'{sugestia}')
embed.set_footer(text="Apples")
msg = await ctx.send(embed=embed)
await msg.add_reaction('✔️')
await msg.add_reaction('➖')
await msg.create_thread(name="Comments", auto_archive_duration=MAX)

Implementing a finite state bot with webhook (aiogram):

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)

I want to add a queue function to MusicBOT with Discord.py [duplicate]

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')

How to unban a discord user given their ID

The code I'm currently using was perfectly fine, but I really want to unban with ID because it is much easier for me.
Current command is in a Cog file:
import discord
from discord.ext import commands
class unban(commands.Cog):
def __init__(self, client):
self.client = client
#commands.command()
#commands.has_permissions(administrator=True)
async def unban(self, ctx, *, member : discord.Member):
banned_users = await ctx.guild.bans()
member_name, member_discriminator = member.split("#")
for ban_entry in banned_users:
user = ban_entry.user
if (user.name, user.discriminator) == (member_name, member_discriminator):
await ctx.guild.unban(user)
unban = discord.Embed(title='UnBan Hammer Has Spoken! :boom:', description=f'**Moderator:** {ctx.author}\n **User UnBanned:** {member}\n', color=0x10940b)
unban.set_author(name="Moderating Action", icon_url=ctx.author.avatar_url)
await ctx.send(embed=unban)
return
def setup(client):
client.add_cog(unban(client))
How can I update this code to unban using the user ID instead?
async def unban(self, ctx, id: int) :
user = await client.fetch_user(id)
await ctx.guild.unban(user)
await ctx.send(f'{user} has been unbanned')
this should work for you, you can personalize even more, but that's just the main code you need.
Try
async def unban(self, ctx, *, member : discord.User):
await ctx.guild.unban(discord.Object(id = member.id))
unban = discord.Embed(title='UnBan Hammer Has Spoken! :boom:', description=f'**Moderator:** {ctx.author}\n **User UnBanned:** {member}\n', color=0x10940b)
unban.set_author(name="Moderating Action", icon_url=ctx.author.avatar_url)
await ctx.send(embed=unban)

Categories

Resources