from __future__ import annotations import asyncio import os from aiogram.enums import ParseMode from aiogram.types import FSInputFile, InlineKeyboardButton, InlineKeyboardMarkup from config import ADMIN_ID, BOT_USERNAME, ROBSEC_FILE from db.storage import load_config, save_config, load_stats, save_stats from db.users import all_users, get, save, find_by_username from keyboards.admin import admin_menu_kb from models.user import UserProfile from runtime import get_log_bot from utils.logging import log_admin, log_admin_document, safe_edit def is_admin(user_id: int) -> bool: if user_id == ADMIN_ID: return True profile = get(user_id) return bool(profile and profile.is_admin) def toggle_shop() -> bool: cfg = load_config() cfg.shop_enabled = not cfg.shop_enabled save_config(cfg) return cfg.shop_enabled def toggle_bot() -> bool: cfg = load_config() cfg.bot_enabled = not cfg.bot_enabled save_config(cfg) return cfg.bot_enabled def update_rate(key: str, price: float) -> None: cfg = load_config() if key in cfg.rates: cfg.rates[key].price = price save_config(cfg) def update_min_withdraw(value: float) -> None: cfg = load_config() cfg.min_withdraw = value save_config(cfg) def add_treasury(amount_rub: float) -> float: cfg = load_config() cfg.treasury += amount_rub save_config(cfg) return cfg.treasury def subtract_treasury(amount_rub: float) -> float: cfg = load_config() cfg.treasury = max(0.0, cfg.treasury - amount_rub) save_config(cfg) return cfg.treasury def robsec_info() -> tuple[int, int]: if not os.path.exists(ROBSEC_FILE): return 0, 0 size = os.path.getsize(ROBSEC_FILE) try: with open(ROBSEC_FILE, "r", encoding="utf-8", errors="ignore") as fh: lines = sum(1 for _ in fh) except Exception: lines = 0 return lines, size def clear_robsec() -> None: with open(ROBSEC_FILE, "w", encoding="utf-8") as fh: fh.write("") async def send_user_card(target, uid: int) -> None: profile = get(uid) if not profile: return text = ( f"👤 Пользователь\n\n" f"🆔 ID: {uid}\n" f"👤 @{profile.username or '—'}\n" f"📅 Рег: {profile.registered or '?'}\n" f"💳 Баланс: {profile.balance:.2f} ₽\n" f"💰 Всего: {profile.total_earned:.2f} ₽\n" f"🍪 Куки: {profile.cookies_loaded}\n" f"👥 Рефералов: {len(profile.referrals)}\n" f"👑 Админ: {'✅' if profile.is_admin else '❌'}\n" f"🚫 Бан: {'✅ ' + profile.ban_reason if profile.is_banned else '❌'}" ) kb = InlineKeyboardMarkup( inline_keyboard=[ [ InlineKeyboardButton(text="➕ Баланс", callback_data=f"u_bal_add_{uid}"), InlineKeyboardButton(text="➖ Баланс", callback_data=f"u_bal_sub_{uid}"), ], [ InlineKeyboardButton( text="🚫 Разбанить" if profile.is_banned else "🚫 Забанить", callback_data=f"u_ban_{uid}", ) ], [ InlineKeyboardButton( text="👑 Убрать админку" if profile.is_admin else "👑 Выдать админку", callback_data=f"u_admin_{uid}", ) ], [InlineKeyboardButton(text="🔙 Назад", callback_data="admin_back")], ] ) if hasattr(target, "answer"): await target.answer(text, parse_mode=ParseMode.HTML, reply_markup=kb) else: await safe_edit(target, text, reply_markup=kb) async def broadcast(bot, text: str | None = None, photo_id: str | None = None, caption: str | None = None) -> tuple[int, int]: ok = 0 fail = 0 for uid in all_users(): try: if photo_id: await bot.send_photo(uid, photo_id, caption=caption or "", parse_mode=ParseMode.HTML) else: await bot.send_message(uid, text or caption or "-", parse_mode=ParseMode.HTML) ok += 1 except Exception: fail += 1 await asyncio.sleep(0.05) return ok, fail def find_user(query: str) -> int | None: if query.isdigit(): uid = int(query) return uid if get(uid) else None return find_by_username(query) def set_balance(uid: int, value: float) -> None: profile = get(uid) if not profile: return profile.balance = value save(uid, profile) def add_balance(uid: int, amount: float) -> None: profile = get(uid) if not profile: return profile.balance += amount save(uid, profile) def sub_balance(uid: int, amount: float) -> None: profile = get(uid) if not profile: return profile.balance = max(0.0, profile.balance - amount) save(uid, profile) def toggle_user_admin(uid: int) -> bool: profile = get(uid) if not profile: return False profile.is_admin = not profile.is_admin save(uid, profile) return profile.is_admin def ban_user(uid: int, reason: str) -> None: profile = get(uid) if not profile: return profile.is_banned = True profile.ban_reason = reason save(uid, profile) def unban_user(uid: int) -> None: profile = get(uid) if not profile: return profile.is_banned = False profile.ban_reason = "" save(uid, profile)