198 lines
5.5 KiB
Python
198 lines
5.5 KiB
Python
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"👤 <b>Пользователь</b>\n\n"
|
||
f"🆔 ID: <code>{uid}</code>\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)
|