Почему не обрабатывается ссылка в tg bot aiogram

Рейтинг: 0Ответов: 0Опубликовано: 16.08.2025
"""Модуль обработчиков команд и 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) но при переходе ссылка не обрабатывается, она даже не отправляется в чат с ботом

пытался изменить формат ссылки и т.п.

Ответы

Ответов пока нет.