Почему не обрабатывается ссылка в tg bot aiogram
"""Модуль обработчиков команд и callback'ов для бота игры в кости.
Содержит:
- Машину состояний для обработки ставок
- Логику проверки платежей
- Игровую механику (бросок костей)
"""
import asyncio
import logging
from aiogram import F, Router, Bot
from aiogram.types import Message, CallbackQuery
from aiogram.filters import Command
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import StatesGroup, State
from aiogram.types import InlineKeyboardMarkup
import aiohttp
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
import app.keyboards as kb
from config import MIN_BET, CHANNEL_ID, CRYPTOBOT_TOKEN, CRYPTOBOT_API, bot_username, MIN_WITHDRAWAL
import time
router = Router()
from collections import deque
from asyncio import Lock
id = None
amount = None
# https://t.me/Image_ConverterTGBot?start=withdraw_5330912748_1.80
# @router.message(F.text.contains("withdraw")) # Ловим /start withdraw_*
# async def handle_deep_link(message: Message):
# print(message.text)
# args = message.text.split("_") # Разбиваем "/start withdraw_123_1.80"
# # user_id = int(args[1]) # 123
# # amount = float(args[2]) # 1.80
# print(args)
# # await message.answer(
# # f"🔔 Запрос на вывод!\n"
# # f"👤 ID: {user_id}\n"
# # f"💵 Сумма: {amount} USD"
# # )
# Глобальная переменная для хранения текущей ставки
current_bet = {
'type': None, # 'number_X', 'even' или 'odd'
'amount': None,
'user_id': None
}
# Очередь сообщений для канала
channel_queue = deque()
channel_lock = Lock()
is_channel_busy = False
class BetStates(StatesGroup):
"""Класс состояний для машины состояний при оформлении ставки."""
waiting_for_amount = State() # Ожидание ввода суммы ставки
waiting_for_choice = State() # Ожидание выбора типа ставки
waiting_for_number = State() # Ожидание выбора числа (если тип - число)
async def process_channel_queue():
"""Обрабатывает очередь сообщений в канал."""
global is_channel_busy
while channel_queue:
is_channel_busy = True
game_data = channel_queue.popleft()
# print(game_data)
try:
# 1. Отправляем сообщение о ставке в канал
await asyncio.sleep(1.5)
bet_msg = await game_data['bot'].send_message(
chat_id=CHANNEL_ID,
text=(
f"🎲 Игрок {game_data['display_name']} сделал ставку\n\n"
f"💵 Сумма: {game_data['amount']} $\n"
f"📊 Тип ставки: {game_data['bet_description']}\n"
f"📈 Коэффициент: x{game_data['multiplier']}\n"
f"💰 Потенциальный выигрыш: {game_data['win_amount']:.2f} $\n\n"
"🔄 Бросаем кубик..."
)
)
await asyncio.sleep(1.5)
# 2. Отправляем анимацию кубика в канал
dice_msg = await game_data['bot'].send_dice(
chat_id=CHANNEL_ID,
emoji="🎲"
)
dice_value = dice_msg.dice.value
await asyncio.sleep(4) # Ждем завершение анимации
# 3. Определяем результат
is_win = False
if game_data['bet_type'].startswith("number_"):
selected_number = int(game_data['bet_type'].split("_")[1])
is_win = (dice_value == selected_number)
elif game_data['bet_type'] == "even":
is_win = (dice_value % 2 == 0)
elif game_data['bet_type'] == "odd":
is_win = (dice_value % 2 != 0)
# 4. Отправляем результат в канал
result_text = (
f"🎯 Результат ставки {game_data['display_name']}\n\n"
f"🎲 Выпало число: {dice_value}\n"
)
if is_win:
result_text += (
f"🏆 Результат: ВЫИГРЫШ {game_data['win_amount']:.2f} $\n"
f"✅ Ставка на {game_data['bet_description']} сыграла!"
)
# Формируем URL для вывода с логированием
try:
id = game_data['user_id']
win_amount = game_data['win_amount']
# withdraw_url = f"https://t.me/{bot_username}?start=withdraw_{id}_{win_amount}"
withdraw_url = f"https://t.me/{bot_username}?start=withdraw#{id}#{win_amount}"
play_again_url = f"https://t.me/{bot_username}?start=bet"
print(withdraw_url)
logging.info(f"Generated withdraw URL: {withdraw_url}")
logging.info(f"Generated play again URL: {play_again_url}")
logging.info(f"Bot username: {bot_username}, User ID: {game_data['user_id']}, Amount: {game_data['win_amount']:.2f}")
markup = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="💸 Вывести чек",
url=withdraw_url #_{game_data['user_id']}_{game_data['win_amount']}"
),
InlineKeyboardButton(
text="🎲 Сыграть ещё",
url=play_again_url
)
]
]
)
except Exception as e:
logging.error(f"Error creating inline buttons: {e}")
markup = await kb.get_start_keyboard()
else:
result_text += (
f"💸 Результат: ПРОИГРЫШ {game_data['amount']} $\n"
f"❌ Ставка на {game_data['bet_description']} не сыграла"
)
markup = await kb.get_start_keyboard()
# Отправляем результат с клавиатурой
await game_data['bot'].send_message(
chat_id=CHANNEL_ID,
text=result_text,
reply_to_message_id=bet_msg.message_id,
reply_markup=markup
)
except Exception as e:
logging.error(f"Channel queue error: {e}", exc_info=True)
if 'message' in game_data:
await game_data['message'].answer("⚠ Ошибка при обработке ставки")
await asyncio.sleep(1) # Пауза между играми
is_channel_busy = False
async def check_cryptobot_payment(invoice_id: str) -> dict:
"""Проверяет статус платежа через CryptoBot API и возвращает данные инвойса или None."""
url = f"{CRYPTOBOT_API}getInvoices"
headers = {"Crypto-Pay-API-Token": CRYPTOBOT_TOKEN}
params = {"invoice_ids": invoice_id}
try:
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers, params=params) as resp:
data = await resp.json()
logging.info(f"CryptoBot response: {data}")
if not data.get("ok"):
logging.error(f"CryptoBot API error: {data}")
return None
invoices = data.get("result", {}).get("items", [])
if not invoices:
return None
return invoices[0] # Возвращаем первый найденный инвойс
except Exception as e:
logging.error(f"Payment check error: {e}")
return None
@router.message(Command("start"))
async def handle_start(message: Message, state: FSMContext, bot: Bot) -> None:
try:
logging.info(f"Full start command received: {message.text}")
print(message.text)
# Проверяем, есть ли параметры после /start
parts = message.text.split(maxsplit=1)
if len(parts) > 1:
param_part = parts[1]
# Обработка ссылки с withdraw
if "withdraw#" in param_part:
withdraw_parts = param_part.split('#')
if len(withdraw_parts) == 3:
user_id_str = withdraw_parts[1]
amount_str = withdraw_parts[2]
print(f"Обработка withdraw: user_id={user_id_str}, amount={amount_str}")
# Здесь можно вызвать функцию для обработки вывода, например:
# await handle_withdrawal(user_id_str, amount_str, message)
# Или послать сообщение, что вывод подтвержден и далее логика
await message.answer(f"Подтверждение вывода для пользователя {user_id_str} на сумму {amount_str}")
return # Выходим, чтобы не выполнять остальную логику
# Если параметров нет или это не withdraw, продолжаем стандартную обработку
args = message.text.split()
print(args)
if len(args) > 1:
logging.info(f"Command args: {args}")
if args[1] == "bet":
await message.answer(f"🎲 Введите сумму ставки (мин. {MIN_BET} USDT):")
await state.set_state(BetStates.waiting_for_amount)
elif args[1].startswith("paid_"):
await handle_payment_confirmation(message, bot)
# УДАЛЕНО: блок обработки withdraw_
except Exception as e:
logging.error(f"Start command error: {e}", exc_info=True)
await message.answer("⚠ Произошла ошибка. Пожалуйста, попробуйте позже.")
async def handle_payment_confirmation(message: Message, bot: Bot) -> None:
"""Обрабатывает подтверждение оплаты через стартовую команду."""
try:
# Формат команды: /start paid_INVOICEID_USERID_AMOUNT_BETTYPE
parts = message.text.split()
if len(parts) < 2 or not parts[1].startswith('paid_'):
return
params = parts[1].split('_')
if len(params) < 5:
await message.answer("⚠ Неверный формат команды оплаты")
return
invoice_id = params[1]
user_id = params[2]
amount = params[3]
bet_type = '_'.join(params[4:]) # Исправление для bet_type с подчеркиваниями
# Проверяем соответствие пользователя
if str(message.from_user.id) != user_id:
await message.answer("⚠ Это не ваш платеж")
return
# Проверяем платеж
invoice_data = await check_cryptobot_payment(invoice_id)
if not invoice_data or invoice_data.get('status') != 'paid':
await message.answer("❌ Оплата не подтверждена")
return
await start_game(message, bot, bet_type, float(amount), user_id=int(user_id))
except Exception as e:
logging.error(f"Payment confirmation error: {e}", exc_info=True)
await message.answer("⚠ Ошибка при обработке платежа")
async def start_game(message: Message, bot: Bot, bet_type: str, amount: float, user_id: int = None):
"""Добавляет игру в очередь канала"""
try:
# Получаем информацию о пользователе
if user_id is None:
user = message.from_user
user_id = user.id # Получаем user_id из сообщения
else:
try:
user = await bot.get_chat(user_id)
except Exception as e:
logging.error(f"Can't get user info: {e}")
user = type('SimpleUser', (), {'username': None, 'full_name': f"ID {user_id}"})
display_name = f"@{user.username}" if user.username else user.full_name
bet_description = get_bet_description(bet_type)
multiplier = 4 if bet_type.startswith("number_") else 1.8
win_amount = float(amount) * multiplier
# Добавляем в очередь
game_data = {
'bot': bot,
'display_name': display_name,
'amount': amount,
'bet_description': bet_description,
'multiplier': multiplier,
'win_amount': win_amount,
'bet_type': bet_type,
'message': message,
'user_id': user_id # Добавляем user_id в словарь
}
channel_queue.append(game_data)
# Запускаем обработку очереди, если она не активна
global is_channel_busy
if not is_channel_busy:
asyncio.create_task(process_channel_queue())
except Exception as e:
logging.error(f"Error in start_game: {e}", exc_info=True)
await message.answer("⚠ Произошла ошибка при запуске игры")
@router.message(BetStates.waiting_for_amount)
async def process_bet_amount(message: Message, state: FSMContext) -> None:
"""Обрабатывает введенную сумму ставки.
Args:
message (Message): Входящее сообщение с суммой
state (FSMContext): Контекст состояния
"""
try:
amount = float(message.text)
if amount < MIN_BET:
await message.answer(f"❌ Минимальная ставка - {MIN_BET} USDT")
return
await state.update_data(amount=amount)
await message.answer(
text=f"💰 Ваша ставка: {amount} USDT\n\nВыберите вариант:",
reply_markup=await kb.get_bet_options_keyboard())
await state.set_state(BetStates.waiting_for_choice)
except ValueError:
await message.answer("❌ Введите число (например: 0.5)")
# Остальные обработчики остаются аналогичными с добавлением докстрингов
# ...
def get_bet_description(bet_type: str) -> str:
"""Возвращает текстовое описание типа ставки.
Args:
bet_type (str): Тип ставки ('number_X', 'even', 'odd')
Returns:
str: Текстовое описание ставки
"""
if bet_type.startswith("number_"):
return f"Число {bet_type.split('_')[1]}"
elif bet_type == "even":
return "Чётное"
elif bet_type == "odd":
return "Нечётное"
return "Неизвестный тип ставки"
@router.callback_query(F.data == 'number')
async def handle_number_bet(callback: CallbackQuery, state: FSMContext):
"""Обработчик выбора ставки на конкретное число."""
try:
await callback.message.answer(
"Выберите число от 1 до 6:",
reply_markup=await kb.get_number_selection_keyboard()
)
await state.set_state(BetStates.waiting_for_number)
await callback.answer()
except Exception as e:
logging.error(f"Number bet error: {e}")
await callback.answer("Произошла ошибка. Попробуйте позже.")
@router.callback_query(F.data == 'even')
async def handle_even_bet(callback: CallbackQuery, state: FSMContext):
"""Обработчик выбора ставки на чётное."""
try:
user_data = await state.get_data()
amount = user_data.get('amount')
# Создаем инвойс для оплаты
keyboard = await kb.create_cryptobot_invoice(
amount,
callback.from_user.id,
"even",
callback.from_user.username # Передаем username пользователя
)
if keyboard:
await callback.message.answer(
f"💰 Ставка {amount} USDT на ЧЁТНОЕ (x1.8)\n\n"
"Оплатите счет для подтверждения ставки:",
reply_markup=keyboard
)
else:
await callback.message.answer("Ошибка при создании платежа. Попробуйте позже.")
await state.clear()
await callback.answer()
except Exception as e:
logging.error(f"Even bet error: {e}")
await callback.answer("Произошла ошибка. Попробуйте позже.")
@router.callback_query(F.data == 'odd')
async def handle_odd_bet(callback: CallbackQuery, state: FSMContext):
"""Обработчик выбора ставки на нечётное."""
try:
user_data = await state.get_data()
amount = user_data.get('amount')
# Создаем инвойс для оплаты
keyboard = await kb.create_cryptobot_invoice(
amount,
callback.from_user.id,
"odd",
callback.from_user.username
)
if keyboard:
await callback.message.answer(
f"💰 Ставка {amount} USDT на НЕЧЁТНОЕ (x1.8)\n\n"
"Оплатите счет для подтверждения ставки:",
reply_markup=keyboard
)
else:
await callback.message.answer("Ошибка при создании платежа. Попробуйте позже.")
await state.clear()
await callback.answer()
except Exception as e:
logging.error(f"Odd bet error: {e}")
await callback.answer("Произошла ошибка. Попробуйте позже.")
@router.callback_query(F.data.startswith('check_'))
async def handle_payment_check(callback: CallbackQuery, bot: Bot):
"""Обработчик проверки оплаты с абсолютно надежным парсингом"""
try:
# Получаем invoice_id
invoice_id = callback.data.split('_')[1]
# Проверяем платеж
payment_data = await check_cryptobot_payment(invoice_id)
if not payment_data or payment_data.get('status') != 'paid':
await callback.answer("❌ Оплата не подтверждена")
return
# Получаем payload
payload = payment_data.get('payload', '')
logging.info(f"Raw payload: {payload}")
# Надежный парсинг с регулярными выражениями
import re
user_id_match = re.search(r'user_id:(\d+)', payload)
amount_match = re.search(r'amount:([\d.]+)', payload)
bet_type_match = re.search(r'bet_type:(\w+)', payload)
# Формируем параметры
params = {}
if user_id_match:
params['user_id'] = user_id_match.group(1)
if amount_match:
params['amount'] = amount_match.group(1)
if bet_type_match:
params['bet_type'] = bet_type_match.group(1)
logging.info(f"Regex parsed params: {params}")
# Проверяем обязательные параметры
required = ['user_id', 'amount', 'bet_type']
if not all(p in params for p in required):
missing = [p for p in required if p not in params]
logging.error(f"Missing params: {missing} in payload: {payload}")
await callback.answer("⚠ Неверный формат платежа")
return
# Проверяем соответствие пользователя
if str(callback.from_user.id) != params['user_id']:
logging.error(f"User ID mismatch: callback {callback.from_user.id}, payment {params['user_id']}")
await callback.answer("⚠ Это не ваш платеж")
return
# Проверяем сумму
try:
amount = float(params['amount'])
if amount < MIN_BET:
await callback.answer(f"❌ Сумма меньше минимальной ставки {MIN_BET}")
return
except ValueError:
await callback.answer("⚠ Неверная сумма платежа")
return
# Запускаем игру
await callback.answer("✅ Оплата подтверждена! Игра начинается...")
await start_game(
callback.message,
bot,
params['bet_type'],
amount,
user_id=int(params['user_id'])
)
except Exception as e:
logging.error(f"Payment processing error: {e}", exc_info=True)
await callback.answer("⚠ Ошибка обработки платежа")
@router.callback_query(F.data.startswith('num_'), BetStates.waiting_for_number)
async def process_selected_number(callback: CallbackQuery, state: FSMContext):
"""Обрабатывает выбранное число для ставки через inline кнопки."""
try:
number = int(callback.data.split('_')[1])
user_data = await state.get_data()
amount = user_data.get('amount')
bet_type = f"number_{number}"
# Создаем инвойс для оплаты
keyboard = await kb.create_cryptobot_invoice(
amount,
callback.from_user.id,
bet_type,
callback.from_user.username
)
if keyboard:
await callback.message.answer(
f"💰 Ставка {amount} USDT на ЧИСЛО {number} (x4)\n\n"
"Оплатите счет для подтверждения ставки:",
reply_markup=keyboard
)
else:
await callback.message.answer("Ошибка при создании платежа. Попробуйте позже.")
await state.clear()
await callback.answer()
except Exception as e:
logging.error(f"Number selection error: {e}")
await callback.answer("Произошла ошибка. Попробуйте позже.")
@router.message(Command("start"))
async def handle_start(message: Message):
text = message.text
# Проверяем, есть ли параметры после /start
if text.startswith("/start "):
param_part = text[7:].strip() # Удаляем "/start "
if "withdraw#" in param_part:
parts = param_part.split('#')
if len(parts) == 3:
user_id_str = parts[1]
amount_str = parts[2]
print(user_id_str, amount_str)
# Здесь дальше ваша логика
У меня есть код. Есть переменная withdraw_url. Я в нее передаю в текст ссылки дополнительно id пользователя и выигрыш(win_amount) но при переходе ссылка не обрабатывается, она даже не отправляется в чат с ботом
пытался изменить формат ссылки и т.п.
Источник: Stack Overflow на русском