Не работает FSM в телеграм боте
Хочу реализовать систему промо-кодов. Написал код, но при повторном использовании промо-кода пользователю снова зачисляется валюта, хотя такого не должно быть, ведь при использовании промо-код пользователю выставляется значение 1, в хэндлере идёт проверка, если в базе данных стоит 1, то промо-код нельзя использовать.
Также при нажатии на кейборд кнопку состояние не останавливается.
Помогите пожалуйста выявить проблему. Вот сам код:
elif message.text == '?️Промокод?️':
await PromoCode.code.set()
await message.answer("<b>Введите ваш промокод</b> ✅",
parse_mode='HTML',
reply_markup=back())
Вот хэндлер обработки FSM:
@dp.message_handler(state=PromoCode.code)
async def FSM_Code(message: types.Message, state: FSMContext):
bool1 = await check_bool(message.from_user.id)
if message.text != 'GOLDAPLUS':
await message.answer(f"❌ Промо-кода <b>'{message.text}'</b> не существует",
parse_mode='HTML')
if message.text == 'GOLDAPLUS':
if bool1 == 1:
await message.answer("❌ Промо-код можно использовать только один раз")
return
await message.answer("Отлично,<b> на ваш баланс зачислено 5 голды</b>",
parse_mode='HTML')
await plus_balance(message.from_user.id, 5)
await plus_check_promo(message.from_user.id, 1)
elif message.text == '?Вернуться назад?':
await state.finish()
await message.answer("? <b>Главное Меню</b>",
parse_mode='HTML',
reply_markup=kb_start())
Ответы (1 шт):
Я покажу так как я сделал обработчик промокодов через машину состояний, правда через инлайн кнопку, но здесь ничего сложного.
Кратко расскажу о его алгоритме действий:
При нажатии на кнопку "Промокод", пользователь входит в машину состояний, где сразу проверяется есть ли он в базе и если нет то его туда вносят сразу со всеми промокодами, но в значении c_promo_code(скор. от check_promo_code) будет находиться значение "0". После чего ему приходит сообщение:
"Введите промокод"
Юзер вводит какой-либо текст, он проверяется на наличие данного промокода и посылает ответ в зависимости от введенного текста. Если данный промокод существует, то ему начисляется бонус и в базе данных обновляется значение c_promo_code на "1" и при попытке ввести этот промокод еще раз, будет проверяться значение в бд и оно будет равно "1" на что пользователь получит ответ:
"Промокод можно использовать только один раз"
Напоминаю! Я еще студент и это мой первый бот, код явно не оптимальный, но рабочий. Желательно разделить код по разным питоновским файлам(например: все sql-запросы в файлик db.py и тд.), но я для общей картины написал всё более-менее вместе
if call.data == 'promo_bon_cab':
await states.promo_code.type_promo.set()
await bot.send_message(call.message.chat.id, '✍ Введите промокод:', reply_markup=kb.markup_cancel_promo)
@dp.message_handler(state=states.promo_code.type_promo)
async def promo_text(message: types.Message, state: FSMContext):
try:
if not BotDB.promo_exists(message.from_user.id):
cur.execute("INSERT INTO `promo_codes` (`user_id`, `promo_code`) VALUES (?, ?)", (message.from_user.id, 'free_10_coin',))
cur.execute("INSERT INTO `promo_codes` (`user_id`, `promo_code`) VALUES (?, ?)", (message.from_user.id, 'free_5_coin',))
base.commit()
async with state.proxy() as data:
data['type_promo'] = message.text
if data['type_promo'] == 'free_10_coin':
for value_check_promo in cur.execute(f"SELECT c_promo_code FROM promo_codes WHERE user_id = {message.from_user.id} AND promo_code = '{data['type_promo']}'"):
if value_check_promo[0] != 1:
await bot.send_message(message.from_user.id, '✅ Вы активировали промокод - "free_10_coin"', reply_markup=kb.markup_start)
await bot.send_message(message.from_user.id, '? Вам начислено 10 coins')
cur.execute(f"UPDATE promo_codes SET `c_promo_code` = 1 WHERE user_id = {message.from_user.id} AND promo_code = '{data['type_promo']}'")
cur.execute(f"UPDATE users SET bal_trx = bal_trx + 10 WHERE user_id = {message.from_user.id}")
base.commit()
await state.finish()
else:
await bot.send_message(message.from_user.id, '⛔ Промокод можно использовать только один раз ❗', reply_markup=kb.markup_start)
await state.finish()
elif data['type_promo'] == 'free_5_coin':
for value_check_promo in cur.execute(f"SELECT c_promo_code FROM promo_codes WHERE user_id = {message.from_user.id} AND promo_code = '{data['type_promo']}'"):
if value_check_promo[0] != 1:
await bot.send_message(message.from_user.id, '✅ Вы активировали промокод - "free_5_coin"', reply_markup=kb.markup_start)
await bot.send_message(message.from_user.id, '? Вам начислено 5 coins')
cur.execute(f"UPDATE promo_codes SET `c_promo_code` = 1 WHERE user_id = {message.from_user.id} AND promo_code = '{data['type_promo']}'")
cur.execute(f"UPDATE users SET bal_trx = bal_trx + 5 WHERE user_id = {message.from_user.id}")
base.commit()
await state.finish()
else:
await bot.send_message(message.from_user.id, '⛔ Промокод можно использовать только один раз ❗', reply_markup=kb.markup_start)
await state.finish()
else:
await bot.send_message(message.from_user.id, f'❌ Промокода " <u><b>{message.text}</b></u> " не существует, или время его действия истекло ❗\n\nВы можете выйти из ввода промокода: /cancel_promo_code', parse_mode='html', reply_markup=kb.markup_cancel_promo)
except:
await bot.send_message(message.chat.id, f'❌ Промокод введён не коректно ❗', parse_mode='html', reply_markup=kb.markup_start)
await bot.send_message(message.chat.id, f'❌ Вы вернулись в главное меню ‼', parse_mode='html')
await state.finish()
await call.answer()
Листинг файла db.py
class BotDB:
global base, cur
base = sqlite3.connect('bot_db.sqlite3')
cur = base.cursor()
"""Проверяем, есть ли юзер в базе"""
def promo_exists(user_id):
result = cur.execute("SELECT `id` FROM `promo_codes` WHERE `user_id` = ?", (user_id,))
return bool(len(result.fetchall()))