Restore Russian bot interface

This commit is contained in:
2026-07-20 08:58:10 +05:00
parent cea7da5d2b
commit 4adf9aa32e
7 changed files with 213 additions and 151 deletions
+129 -67
View File
@@ -49,32 +49,40 @@ def _user_card(uid: int) -> tuple[str, InlineKeyboardMarkup] | None:
if not profile: if not profile:
return None return None
text = ( text = (
"<b>User</b>\n\n" "👤 <b>Пользователь</b>\n\n"
f"ID: <code>{uid}</code>\n" f"🆔 ID: <code>{uid}</code>\n"
f"Username: @{profile.username or '-'}\n" f"👤 @{profile.username or ''}\n"
f"Registered: {profile.registered or '-'}\n" f"📅 Регистрация: {profile.registered or '?'}\n"
f"Balance: {profile.balance:.2f} RUB\n" f"💳 Баланс: {profile.balance:.2f} \n"
f"Total earned: {profile.total_earned:.2f} RUB\n" f"💰 Всего заработано: {profile.total_earned:.2f} \n"
f"Cookies: {profile.cookies_loaded}\n" f"🍪 Cookie: {profile.cookies_loaded}\n"
f"Referrals: {len(profile.referrals)}\n" f"👥 Рефералов: {len(profile.referrals)}\n"
f"Admin: {'yes' if profile.is_admin else 'no'}\n" f"👑 Админ: {'' if profile.is_admin else ''}\n"
f"Banned: {'yes: ' + profile.ban_reason if profile.is_banned else 'no'}" f"🚫 Бан: {' ' + profile.ban_reason if profile.is_banned else ''}"
) )
keyboard = InlineKeyboardMarkup( keyboard = InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[ [
InlineKeyboardButton(text="Add balance", callback_data=f"u_bal_add_{uid}"), InlineKeyboardButton(
InlineKeyboardButton(text="Subtract balance", callback_data=f"u_bal_sub_{uid}"), text=" Баланс", callback_data=f"u_bal_add_{uid}"
),
InlineKeyboardButton(
text=" Баланс", callback_data=f"u_bal_sub_{uid}"
),
], ],
[InlineKeyboardButton( [
text="Unban" if profile.is_banned else "Ban", InlineKeyboardButton(
callback_data=f"u_ban_{uid}", text="🚫 Разбанить" if profile.is_banned else "🚫 Забанить",
)], callback_data=f"u_ban_{uid}",
[InlineKeyboardButton( )
text="Remove admin" if profile.is_admin else "Grant admin", ],
callback_data=f"u_admin_{uid}", [
)], InlineKeyboardButton(
[InlineKeyboardButton(text="Back", callback_data="admin_back")], text="👑 Убрать админку" if profile.is_admin else "👑 Выдать админку",
callback_data=f"u_admin_{uid}",
)
],
[InlineKeyboardButton(text="🔙 Назад", callback_data="admin_back")],
] ]
) )
return text, keyboard return text, keyboard
@@ -85,7 +93,9 @@ async def cmd_admin(message: Message, state: FSMContext):
if not _admin(message.from_user.id): if not _admin(message.from_user.id):
return return
await state.clear() await state.clear()
await message.answer("<b>Admin panel</b>", parse_mode="HTML", reply_markup=admin_menu_kb()) await message.answer(
"🛠 <b>Админ-панель</b>", parse_mode="HTML", reply_markup=admin_menu_kb()
)
@router.callback_query(F.data == "admin_close") @router.callback_query(F.data == "admin_close")
@@ -105,7 +115,7 @@ async def admin_back(callback: CallbackQuery, state: FSMContext):
if not _admin(callback.from_user.id): if not _admin(callback.from_user.id):
return return
await state.clear() await state.clear()
await safe_edit(callback, "<b>Admin panel</b>", reply_markup=admin_menu_kb()) await safe_edit(callback, "🛠 <b>Админ-панель</b>", reply_markup=admin_menu_kb())
await callback.answer() await callback.answer()
@@ -115,7 +125,7 @@ async def admin_toggle_shop(callback: CallbackQuery):
return return
enabled = toggle_shop() enabled = toggle_shop()
await callback.message.edit_reply_markup(reply_markup=admin_menu_kb()) await callback.message.edit_reply_markup(reply_markup=admin_menu_kb())
await callback.answer(f"Shop: {'ON' if enabled else 'OFF'}") await callback.answer(f"Скупка: {'ВКЛ' if enabled else 'ВЫКЛ'}")
@router.callback_query(F.data == "admin_toggle_bot") @router.callback_query(F.data == "admin_toggle_bot")
@@ -124,7 +134,7 @@ async def admin_toggle_bot(callback: CallbackQuery):
return return
enabled = toggle_bot() enabled = toggle_bot()
await callback.message.edit_reply_markup(reply_markup=admin_menu_kb()) await callback.message.edit_reply_markup(reply_markup=admin_menu_kb())
await callback.answer(f"Bot: {'ON' if enabled else 'OFF'}") await callback.answer(f"Бот: {'ВКЛ' if enabled else 'ВЫКЛ'}")
@router.callback_query(F.data == "admin_robsec_get") @router.callback_query(F.data == "admin_robsec_get")
@@ -133,18 +143,18 @@ async def admin_robsec_get(callback: CallbackQuery):
return return
lines, size = robsec_info() lines, size = robsec_info()
if not size: if not size:
await callback.answer("robsec.txt is empty", show_alert=True) await callback.answer("robsec.txt пуст", show_alert=True)
return return
try: try:
bot = get_bot() or callback.message.bot bot = get_bot() or callback.message.bot
await bot.send_document( await bot.send_document(
callback.from_user.id, callback.from_user.id,
FSInputFile(ROBSEC_FILE), FSInputFile(ROBSEC_FILE),
caption=f"robsec.txt\nLines: {lines}\nSize: {size} bytes", caption=f"📥 <b>robsec.txt</b>\nСтрок: <b>{lines}</b>\nРазмер: <b>{size} байт</b>",
) )
await callback.answer("Sent") await callback.answer("Отправлено")
except Exception as exc: except Exception as exc:
await callback.answer(f"Error: {exc}", show_alert=True) await callback.answer(f"Ошибка: {exc}", show_alert=True)
@router.callback_query(F.data == "admin_robsec_clear") @router.callback_query(F.data == "admin_robsec_clear")
@@ -154,11 +164,19 @@ async def admin_robsec_clear_prompt(callback: CallbackQuery):
lines, _ = robsec_info() lines, _ = robsec_info()
keyboard = InlineKeyboardMarkup( keyboard = InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[InlineKeyboardButton(text="Yes, clear", callback_data="admin_robsec_clear_yes")], [
[InlineKeyboardButton(text="Cancel", callback_data="admin_back")], InlineKeyboardButton(
text="✅ Да, очистить", callback_data="admin_robsec_clear_yes"
)
],
[InlineKeyboardButton(text="🔙 Отмена", callback_data="admin_back")],
] ]
) )
await safe_edit(callback, f"Clear robsec.txt? Current lines: <b>{lines}</b>", reply_markup=keyboard) await safe_edit(
callback,
f"🗑 <b>Очистить robsec.txt?</b>\n\nСтрок сейчас: <b>{lines}</b>\n\n⚠️ Данные будут удалены безвозвратно!",
reply_markup=keyboard,
)
@router.callback_query(F.data == "admin_robsec_clear_yes") @router.callback_query(F.data == "admin_robsec_clear_yes")
@@ -166,8 +184,8 @@ async def admin_robsec_clear_yes(callback: CallbackQuery):
if not _admin(callback.from_user.id): if not _admin(callback.from_user.id):
return return
clear_robsec() clear_robsec()
await safe_edit(callback, "<b>robsec.txt cleared</b>", reply_markup=admin_menu_kb()) await safe_edit(callback, "<b>robsec.txt очищен</b>", reply_markup=admin_menu_kb())
await callback.answer("Cleared") await callback.answer("Очищено")
@router.callback_query(F.data == "admin_rates") @router.callback_query(F.data == "admin_rates")
@@ -176,13 +194,18 @@ async def admin_rates(callback: CallbackQuery):
return return
cfg = load_config() cfg = load_config()
rows = [ rows = [
[InlineKeyboardButton(text=f"{rate.name}: {rate.price:.2f} RUB", callback_data=f"admin_rate_{key}")] [
InlineKeyboardButton(
text=f"{rate.name}: {rate.price:.2f}",
callback_data=f"admin_rate_{key}",
)
]
for key, rate in cfg.rates.items() for key, rate in cfg.rates.items()
] ]
rows.append([InlineKeyboardButton(text="Back", callback_data="admin_back")]) rows.append([InlineKeyboardButton(text="🔙 Назад", callback_data="admin_back")])
await safe_edit( await safe_edit(
callback, callback,
"<b>Rate management</b>\nSelect a rate:", "💰 <b>Управление курсами</b>\nВыберите курс для изменения цены:",
reply_markup=InlineKeyboardMarkup(inline_keyboard=rows), reply_markup=InlineKeyboardMarkup(inline_keyboard=rows),
) )
await callback.answer() await callback.answer()
@@ -196,13 +219,13 @@ async def admin_rate_select(callback: CallbackQuery, state: FSMContext):
cfg = load_config() cfg = load_config()
rate = cfg.rates.get(key) rate = cfg.rates.get(key)
if not rate: if not rate:
await callback.answer("Rate not found", show_alert=True) await callback.answer("Курс не найден", show_alert=True)
return return
await state.update_data(rate_key=key) await state.update_data(rate_key=key)
await state.set_state(Form.admin_edit_rate) await state.set_state(Form.admin_edit_rate)
await safe_edit( await safe_edit(
callback, callback,
f"Rate: <b>{rate.name}</b>\nCurrent price: {rate.price:.2f} RUB\n\nSend a new price:", f"💰 Курс: <b>{rate.name}</b>\nТекущая цена: {rate.price:.2f} \n\nОтправьте новую цену числом:",
reply_markup=back_kb("admin_back"), reply_markup=back_kb("admin_back"),
) )
@@ -216,7 +239,7 @@ async def admin_edit_rate_save(message: Message, state: FSMContext):
if price < 0: if price < 0:
raise ValueError raise ValueError
except ValueError: except ValueError:
await message.answer("Send a non-negative number.") await message.answer("❌ Введите неотрицательное число.")
return return
data = await state.get_data() data = await state.get_data()
key = data.get("rate_key") key = data.get("rate_key")
@@ -226,7 +249,10 @@ async def admin_edit_rate_save(message: Message, state: FSMContext):
return return
update_rate(key, price) update_rate(key, price)
await state.clear() await state.clear()
await message.answer(f"Rate updated: {cfg.rates[key].name} = {price:.2f} RUB", reply_markup=admin_menu_kb()) await message.answer(
f"✅ Цена <b>{cfg.rates[key].name}</b> = {price:.2f}",
reply_markup=admin_menu_kb(),
)
@router.callback_query(F.data == "admin_min_withdraw") @router.callback_query(F.data == "admin_min_withdraw")
@@ -236,7 +262,7 @@ async def admin_min_withdraw(callback: CallbackQuery, state: FSMContext):
await state.set_state(Form.admin_min_withdraw) await state.set_state(Form.admin_min_withdraw)
await safe_edit( await safe_edit(
callback, callback,
f"Current minimum withdrawal: {load_config().min_withdraw:.2f} RUB\nSend a new value:", f"💳 Текущий минимальный вывод: {load_config().min_withdraw:.2f} \nОтправьте новое значение:",
reply_markup=back_kb("admin_back"), reply_markup=back_kb("admin_back"),
) )
@@ -250,11 +276,13 @@ async def admin_min_withdraw_save(message: Message, state: FSMContext):
if value <= 0: if value <= 0:
raise ValueError raise ValueError
except ValueError: except ValueError:
await message.answer("Send a positive number.") await message.answer("❌ Введите положительное число.")
return return
update_min_withdraw(value) update_min_withdraw(value)
await state.clear() await state.clear()
await message.answer(f"Minimum withdrawal updated to {value:.2f} RUB", reply_markup=admin_menu_kb()) await message.answer(
f"✅ Минимальный вывод: {value:.2f}", reply_markup=admin_menu_kb()
)
@router.callback_query(F.data == "admin_treasury") @router.callback_query(F.data == "admin_treasury")
@@ -262,7 +290,11 @@ async def admin_treasury(callback: CallbackQuery, state: FSMContext):
if not _admin(callback.from_user.id): if not _admin(callback.from_user.id):
return return
await state.set_state(Form.admin_treasury_amount) await state.set_state(Form.admin_treasury_amount)
await safe_edit(callback, "How much RUB should be added to the treasury?", reply_markup=back_kb("admin_back")) await safe_edit(
callback,
"🏦 На какую сумму пополнить казну? (в ₽)\nОтправьте число:",
reply_markup=back_kb("admin_back"),
)
@router.message(Form.admin_treasury_amount) @router.message(Form.admin_treasury_amount)
@@ -274,22 +306,29 @@ async def admin_treasury_amount(message: Message, state: FSMContext):
if amount <= 0: if amount <= 0:
raise ValueError raise ValueError
except ValueError: except ValueError:
await message.answer("Send a positive number.") await message.answer("❌ Введите положительное число.")
return return
await state.clear() await state.clear()
invoice = await create_treasury_invoice(amount) invoice = await create_treasury_invoice(amount)
if not invoice: if not invoice:
await message.answer("CryptoBot invoice creation failed.", reply_markup=admin_menu_kb()) await message.answer(
"❌ Не удалось создать счет CryptoBot.", reply_markup=admin_menu_kb()
)
return return
pay_url, invoice_id, amount_usdt = invoice pay_url, invoice_id, amount_usdt = invoice
keyboard = InlineKeyboardMarkup( keyboard = InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[InlineKeyboardButton(text="Pay", url=pay_url)], [InlineKeyboardButton(text="💳 Оплатить", url=pay_url)],
[InlineKeyboardButton(text="Check payment", callback_data=f"treasury_check_{invoice_id}_{amount}")], [
InlineKeyboardButton(
text="✅ Проверить оплату",
callback_data=f"treasury_check_{invoice_id}_{amount}",
)
],
] ]
) )
await message.answer( await message.answer(
f"Treasury invoice: <b>{amount:.2f} RUB</b> (~{amount_usdt} USDT)\nPay it and check the status.", f"🏦 Счет на пополнение казны\n\n💰 Сумма: <b>{amount:.2f} </b> (~{amount_usdt} USDT)\n\nПосле оплаты нажмите «Проверить оплату».",
parse_mode="HTML", parse_mode="HTML",
reply_markup=keyboard, reply_markup=keyboard,
) )
@@ -307,11 +346,15 @@ async def treasury_check(callback: CallbackQuery):
paid = False paid = False
amount_rub = 0.0 amount_rub = 0.0
if not paid: if not paid:
await callback.answer("Payment has not arrived yet", show_alert=True) await callback.answer("Оплата еще не поступила", show_alert=True)
return return
balance = add_treasury(amount_rub) balance = add_treasury(amount_rub)
await safe_edit(callback, f"Treasury topped up by {amount_rub:.2f} RUB. Total: {balance:.2f} RUB", reply_markup=admin_menu_kb()) await safe_edit(
await callback.answer("Paid") callback,
f"✅ Казна пополнена на {amount_rub:.2f}\n🏦 Всего: {balance:.2f}",
reply_markup=admin_menu_kb(),
)
await callback.answer("Оплачено")
@router.callback_query(F.data == "admin_broadcast") @router.callback_query(F.data == "admin_broadcast")
@@ -319,7 +362,11 @@ async def admin_broadcast_prompt(callback: CallbackQuery, state: FSMContext):
if not _admin(callback.from_user.id): if not _admin(callback.from_user.id):
return return
await state.set_state(Form.admin_broadcast) await state.set_state(Form.admin_broadcast)
await safe_edit(callback, "Send the broadcast text or a photo with caption.", reply_markup=back_kb("admin_back")) await safe_edit(
callback,
"📣 Отправьте сообщение для рассылки (текст или фото с подписью):",
reply_markup=back_kb("admin_back"),
)
@router.message(Form.admin_broadcast) @router.message(Form.admin_broadcast)
@@ -333,7 +380,10 @@ async def admin_broadcast_send(message: Message, state: FSMContext):
photo_id=message.photo[-1].file_id if message.photo else None, photo_id=message.photo[-1].file_id if message.photo else None,
caption=message.caption, caption=message.caption,
) )
await message.answer(f"Broadcast complete. Sent: {sent}, failed: {failed}.", reply_markup=admin_menu_kb()) await message.answer(
f"✅ Рассылка завершена. Отправлено: {sent}\n❌ Ошибок: {failed}",
reply_markup=admin_menu_kb(),
)
@router.callback_query(F.data == "admin_users") @router.callback_query(F.data == "admin_users")
@@ -341,7 +391,9 @@ async def admin_users(callback: CallbackQuery, state: FSMContext):
if not _admin(callback.from_user.id): if not _admin(callback.from_user.id):
return return
await state.set_state(Form.admin_find_user) await state.set_state(Form.admin_find_user)
await safe_edit(callback, "Send a user ID or @username.", reply_markup=back_kb("admin_back")) await safe_edit(
callback, "Отправьте ID пользователя или @username.", reply_markup=back_kb("admin_back")
)
@router.message(Form.admin_find_user) @router.message(Form.admin_find_user)
@@ -351,7 +403,7 @@ async def admin_find_user(message: Message, state: FSMContext):
uid = find_user((message.text or "").strip()) uid = find_user((message.text or "").strip())
await state.clear() await state.clear()
if uid is None: if uid is None:
await message.answer("User not found.", reply_markup=admin_menu_kb()) await message.answer("❌ Пользователь не найден.", reply_markup=admin_menu_kb())
return return
card = _user_card(uid) card = _user_card(uid)
if card: if card:
@@ -365,7 +417,9 @@ async def u_bal_add(callback: CallbackQuery, state: FSMContext):
uid = int(callback.data.removeprefix("u_bal_add_")) uid = int(callback.data.removeprefix("u_bal_add_"))
await state.update_data(target_uid=uid) await state.update_data(target_uid=uid)
await state.set_state(Form.admin_user_balance_add) await state.set_state(Form.admin_user_balance_add)
await safe_edit(callback, "Send the amount to add:", reply_markup=back_kb("admin_back")) await safe_edit(
callback, "Отправьте сумму для начисления:", reply_markup=back_kb("admin_back")
)
@router.message(Form.admin_user_balance_add) @router.message(Form.admin_user_balance_add)
@@ -377,12 +431,14 @@ async def u_bal_add_save(message: Message, state: FSMContext):
if amount <= 0: if amount <= 0:
raise ValueError raise ValueError
except ValueError: except ValueError:
await message.answer("Send a positive number.") await message.answer("❌ Введите положительное число.")
return return
uid = (await state.get_data()).get("target_uid") uid = (await state.get_data()).get("target_uid")
add_balance(uid, amount) add_balance(uid, amount)
await state.clear() await state.clear()
await message.answer(f"Added {amount:.2f} RUB to {uid}.", reply_markup=admin_menu_kb()) await message.answer(
f"✅ Пользователю {uid} начислено {amount:.2f} ₽.", reply_markup=admin_menu_kb()
)
@router.callback_query(F.data.startswith("u_bal_sub_")) @router.callback_query(F.data.startswith("u_bal_sub_"))
@@ -392,7 +448,9 @@ async def u_bal_sub(callback: CallbackQuery, state: FSMContext):
uid = int(callback.data.removeprefix("u_bal_sub_")) uid = int(callback.data.removeprefix("u_bal_sub_"))
await state.update_data(target_uid=uid) await state.update_data(target_uid=uid)
await state.set_state(Form.admin_user_balance_sub) await state.set_state(Form.admin_user_balance_sub)
await safe_edit(callback, "Send the amount to subtract:", reply_markup=back_kb("admin_back")) await safe_edit(
callback, "Отправьте сумму для списания:", reply_markup=back_kb("admin_back")
)
@router.message(Form.admin_user_balance_sub) @router.message(Form.admin_user_balance_sub)
@@ -404,12 +462,14 @@ async def u_bal_sub_save(message: Message, state: FSMContext):
if amount <= 0: if amount <= 0:
raise ValueError raise ValueError
except ValueError: except ValueError:
await message.answer("Send a positive number.") await message.answer("❌ Введите положительное число.")
return return
uid = (await state.get_data()).get("target_uid") uid = (await state.get_data()).get("target_uid")
sub_balance(uid, amount) sub_balance(uid, amount)
await state.clear() await state.clear()
await message.answer(f"Subtracted {amount:.2f} RUB from {uid}.", reply_markup=admin_menu_kb()) await message.answer(
f"У пользователя {uid} списано {amount:.2f} ₽.", reply_markup=admin_menu_kb()
)
@router.callback_query(F.data.startswith("u_ban_")) @router.callback_query(F.data.startswith("u_ban_"))
@@ -424,11 +484,13 @@ async def u_ban(callback: CallbackQuery, state: FSMContext):
from services.admin import unban_user from services.admin import unban_user
unban_user(uid) unban_user(uid)
await callback.answer("Unbanned") await callback.answer("Разблокирован")
else: else:
await state.update_data(target_uid=uid) await state.update_data(target_uid=uid)
await state.set_state(Form.admin_user_ban_reason) await state.set_state(Form.admin_user_ban_reason)
await safe_edit(callback, "Send the ban reason:", reply_markup=back_kb("admin_back")) await safe_edit(
callback, "Отправьте причину блокировки:", reply_markup=back_kb("admin_back")
)
return return
card = _user_card(uid) card = _user_card(uid)
if card: if card:
@@ -440,7 +502,7 @@ async def u_ban_reason(message: Message, state: FSMContext):
if not _admin(message.from_user.id): if not _admin(message.from_user.id):
return return
uid = (await state.get_data()).get("target_uid") uid = (await state.get_data()).get("target_uid")
ban_user(uid, (message.text or "No reason").strip()) ban_user(uid, (message.text or "Причина не указана").strip())
await state.clear() await state.clear()
card = _user_card(uid) card = _user_card(uid)
if card: if card:
@@ -453,7 +515,7 @@ async def u_admin(callback: CallbackQuery):
return return
uid = int(callback.data.removeprefix("u_admin_")) uid = int(callback.data.removeprefix("u_admin_"))
enabled = toggle_user_admin(uid) enabled = toggle_user_admin(uid)
await callback.answer(f"Admin: {'ON' if enabled else 'OFF'}") await callback.answer(f"Админ: {'ВКЛ' if enabled else 'ВЫКЛ'}")
card = _user_card(uid) card = _user_card(uid)
if card: if card:
await safe_edit(callback, card[0], reply_markup=card[1]) await safe_edit(callback, card[0], reply_markup=card[1])
+11 -11
View File
@@ -16,15 +16,15 @@ router = Router(name="start")
def _welcome_text(cfg) -> str: def _welcome_text(cfg) -> str:
text = ( text = (
f"Welcome to <b>{SHOP_NAME}</b>!\n\n" f"💎 Добро пожаловать в <b>{SHOP_NAME}</b>!\n\n"
"You can sell cookies at these rates:\n" "💵 Здесь можно продать куки по следующим ставкам:\n"
) )
for rate in cfg.rates.values(): for rate in cfg.rates.values():
text += f"- {rate.name}: {rate.price:.2f} RUB\n" text += f"🔸 {rate.name}: {rate.price:.2f} \n"
return text + ( return text + (
f"\nAVG pricing applies to {cfg.avg_min_cookies}+ cookies with donations.\n" f"\n📊 <b>AVG-система ({cfg.avg_min_price:.2f}-{cfg.avg_max_price:.2f} ₽/шт):</b>\n"
f"The AVG price is capped at {cfg.avg_min_price:.2f}-{cfg.avg_max_price:.2f} RUB per cookie.\n\n" f"При отправке {cfg.avg_min_cookies}+ донатных cookie бот рассчитает средний Lifetime donate и предложит выгодную цену за каждую cookie.\n\n"
"Send a .txt file or paste your cookie text." "🍪 Отправьте .txt-файл или вставьте текст с cookie."
) )
@@ -40,24 +40,24 @@ async def _show_start(
profile = get(user_id) profile = get(user_id)
cfg = load_config() cfg = load_config()
if profile and profile.is_banned: if profile and profile.is_banned:
await message.answer(f"You are banned. Reason: {profile.ban_reason or '-'}") await message.answer(f"❌ Вы заблокированы. Причина: {profile.ban_reason or ''}")
return return
if not cfg.bot_enabled and user_id != ADMIN_ID: if not cfg.bot_enabled and user_id != ADMIN_ID:
await message.answer("The bot is temporarily disabled by an administrator.") await message.answer("🚫 Бот временно отключен администратором.")
return return
if is_new: if is_new:
bot = get_bot() or message.bot bot = get_bot() or message.bot
await log_admin( await log_admin(
bot, bot,
ADMIN_ID, ADMIN_ID,
f"<b>New user</b>\nID: <code>{user_id}</code>\n" f"🆕 <b>Новый пользователь</b>\nID: <code>{user_id}</code>\n"
f"@{username or '-'}\nReferrer: {referrer or '-'}", f"@{username or 'нет'}\nРеферер: {referrer or ''}",
get_log_bot(), get_log_bot(),
) )
text = _welcome_text(cfg) text = _welcome_text(cfg)
if is_admin(user_id): if is_admin(user_id):
text += "\n\n<b>Admin accounts cannot sell cookies. Use /admin.</b>" text += "\n\n👑 <i>Вы администратор. Продажа cookie отключена. Используйте /admin.</i>"
await message.answer(text, parse_mode="HTML", reply_markup=main_menu_kb()) await message.answer(text, parse_mode="HTML", reply_markup=main_menu_kb())
+27 -27
View File
@@ -36,14 +36,14 @@ async def _blocked(message: Message) -> bool:
register(message.from_user.id, message.from_user.username) register(message.from_user.id, message.from_user.username)
profile = get(message.from_user.id) profile = get(message.from_user.id)
if profile.is_banned: if profile.is_banned:
await message.answer(f"You are banned. Reason: {profile.ban_reason or '-'}") await message.answer(f"❌ Вы заблокированы. Причина: {profile.ban_reason or ''}")
return True return True
cfg = load_config() cfg = load_config()
if not cfg.bot_enabled and message.from_user.id != ADMIN_ID: if not cfg.bot_enabled and message.from_user.id != ADMIN_ID:
await message.answer("The bot is temporarily disabled.") await message.answer("🚫 Бот временно отключен.")
return True return True
if not cfg.shop_enabled and message.from_user.id != ADMIN_ID: if not cfg.shop_enabled and message.from_user.id != ADMIN_ID:
await message.answer("Cookie buying is temporarily disabled.") await message.answer("🚫 Скупка cookie временно отключена.")
return True return True
return False return False
@@ -51,13 +51,13 @@ async def _blocked(message: Message) -> bool:
@router.message(StateFilter(None), F.document) @router.message(StateFilter(None), F.document)
async def handle_document(message: Message): async def handle_document(message: Message):
if is_admin(message.from_user.id): if is_admin(message.from_user.id):
await message.answer("Admin accounts cannot sell cookies. Use /admin.") await message.answer("👑 Администраторы не могут продавать cookie. Используйте /admin.")
return return
if await _blocked(message): if await _blocked(message):
return return
filename = message.document.file_name or "" filename = message.document.file_name or ""
if not filename.lower().endswith(".txt"): if not filename.lower().endswith(".txt"):
await message.answer("Send a .txt file.") await message.answer("❌ Отправьте файл формата .txt.")
return 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"
@@ -72,13 +72,13 @@ async def handle_document(message: Message):
@router.message(StateFilter(None), F.text & ~F.text.startswith("/")) @router.message(StateFilter(None), F.text & ~F.text.startswith("/"))
async def handle_text(message: Message): async def handle_text(message: Message):
if is_admin(message.from_user.id): if is_admin(message.from_user.id):
await message.answer("Admin accounts cannot sell cookies. Use /admin.") await message.answer("👑 Администраторы не могут продавать cookie. Используйте /admin.")
return return
if await _blocked(message): if await _blocked(message):
return return
text = message.text or "" text = message.text or ""
if "_|WARNING:-DO-NOT-SHARE-THIS." not in text: if "_|WARNING:-DO-NOT-SHARE-THIS." not in text:
await message.answer("Use the menu below or send cookie text.", reply_markup=main_menu_kb()) await message.answer("👋 Используйте меню ниже или отправьте текст с cookie.", reply_markup=main_menu_kb())
return return
await process_cookies(message, text) await process_cookies(message, text)
@@ -86,14 +86,14 @@ async def handle_text(message: Message):
async def process_cookies(message: Message, raw_text: str): async def process_cookies(message: Message, raw_text: str):
cookies, duplicates = parse_cookies_text(raw_text) cookies, duplicates = parse_cookies_text(raw_text)
if not cookies: if not cookies:
await message.answer("No cookies were found in the submitted data.") await message.answer("❌ В отправленных данных cookie не найдены.")
return return
cfg = load_config() cfg = load_config()
proxies = load_proxies() proxies = load_proxies()
total = len(cookies) total = len(cookies)
progress = await message.answer( progress = await message.answer(
f"<b>Processing cookies</b>\nTotal: {total}\nDuplicates removed: {duplicates}\nStage: validation...", f"<b>Начинаю обработку cookie</b>\nВсего: {total}\nДубликатов удалено: {duplicates}\nЭтап: проверка...",
parse_mode="HTML", parse_mode="HTML",
) )
@@ -110,17 +110,17 @@ async def process_cookies(message: Message, raw_text: str):
if index % 20 == 0 or index == total: if index % 20 == 0 or index == total:
try: try:
await progress.edit_text( await progress.edit_text(
f"<b>Validation</b>\nProgress: {index}/{total}\nValid: {len(valid)}", f"<b>Проверка cookie</b>\nПрогресс: {index}/{total}\n✅ Валидных: {len(valid)}",
parse_mode="HTML", parse_mode="HTML",
) )
except Exception: except Exception:
pass pass
if not valid: if not valid:
await progress.edit_text(f"No valid cookies found. Duplicates: {duplicates}") await progress.edit_text(f"❌ Валидные cookie не найдены. Дубликатов: {duplicates}")
return return
await progress.edit_text(f"<b>Checking donations</b>\nValid cookies: {len(valid)}", parse_mode="HTML") await progress.edit_text(f"<b>Проверяю донаты</b>\nВалидных cookie: {len(valid)}", parse_mode="HTML")
donations = [] donations = []
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
async def get_donation(result): async def get_donation(result):
@@ -140,13 +140,13 @@ async def process_cookies(message: Message, raw_text: str):
priced, avg_used, avg_price = price_cookie_batch(donations, cfg) priced, avg_used, avg_price = price_cookie_batch(donations, cfg)
if not priced: if not priced:
await progress.edit_text( await progress.edit_text(
f"No cookies matched the rates. Valid: {len(valid)}", f"❌ Ни одна cookie не подошла под курсы. Валидных: {len(valid)}",
reply_markup=back_kb(), reply_markup=back_kb(),
) )
return return
await progress.edit_text( await progress.edit_text(
f"<b>Refreshing cookies</b>\nPayable: {len(priced)}\nMethod: {'AVG' if avg_used else 'per-rate'}", f"<b>Обновляю cookie</b>\nК оплате: {len(priced)}\nМетод: {'AVG' if avg_used else 'поштучный'}",
parse_mode="HTML", parse_mode="HTML",
) )
fresh_ok = [] fresh_ok = []
@@ -166,8 +166,8 @@ async def process_cookies(message: Message, raw_text: str):
fresh_failed += 1 fresh_failed += 1
try: try:
await progress.edit_text( await progress.edit_text(
f"<b>Refreshing cookies</b>\nProgress: {min(offset + len(batch), len(priced))}/{len(priced)}\n" f"<b>Обновление cookie</b>\nПрогресс: {min(offset + len(batch), len(priced))}/{len(priced)}\n"
f"Refreshed: {len(fresh_ok)}\nFailed: {fresh_failed}", f"✅ Обновлено: {len(fresh_ok)}\n❌ Ошибок: {fresh_failed}",
parse_mode="HTML", parse_mode="HTML",
) )
except Exception: except Exception:
@@ -175,7 +175,7 @@ async def process_cookies(message: Message, raw_text: str):
if not fresh_ok: if not fresh_ok:
await progress.edit_text( await progress.edit_text(
f"No cookies could be refreshed. Valid: {len(valid)}, failed: {fresh_failed}", f"❌ Не удалось обновить ни одной cookie. Валидных: {len(valid)}, ошибок: {fresh_failed}",
reply_markup=back_kb(), reply_markup=back_kb(),
) )
return return
@@ -204,30 +204,30 @@ async def process_cookies(message: Message, raw_text: str):
bot = get_bot() or message.bot 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 "per-rate" method = "📊 AVG-система" if avg_used else "📋 Поштучный"
report = ( report = (
"<b>Processing complete</b>\n\n" "<b>Обработка завершена</b>\n\n"
f"Total: {total}\nDuplicates: {duplicates}\nValid: {len(valid)}\n" f"Всего cookie: {total}\nДубликатов: {duplicates}\nВалидных: {len(valid)}\n"
f"Payable: {len(priced)}\nRefreshed: {len(fresh_ok)}\nFailed refresh: {fresh_failed}\n" f"Подошло под курс: {len(priced)}\nОбновлено: {len(fresh_ok)}\nОшибок обновления: {fresh_failed}\n"
f"Method: {method}\n" f"Метод оплаты: {method}\n"
) )
if avg_used: if avg_used:
report += f"AVG price: {avg_price:.2f} RUB\n" report += f"AVG цена за cookie: {avg_price:.2f} \n"
report += f"\nPayout: <b>{payout:.2f} RUB</b>\nBalance: <b>{profile.balance:.2f} RUB</b>" 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 progress.edit_text(report, parse_mode="HTML", reply_markup=main_menu_kb())
await log_admin( await log_admin(
bot, bot,
ADMIN_ID, ADMIN_ID,
f"<b>Cookie purchase</b>\n@{profile.username or '-'} (<code>{profile.user_id}</code>)\n" f"🍪 <b>Новая покупка cookie</b>\n@{profile.username or ''} (<code>{profile.user_id}</code>)\n"
f"Total: {total}, valid: {len(valid)}, refreshed: {len(fresh_ok)}\nPayout: {payout:.2f} RUB", f"Всего: {total}, валидных: {len(valid)}, обновлено: {len(fresh_ok)}\nВыплачено: {payout:.2f} ",
get_log_bot(), get_log_bot(),
) )
await log_admin_document( await log_admin_document(
bot, bot,
ADMIN_ID, ADMIN_ID,
str(user_file), str(user_file),
caption=f"robsec from <code>{profile.user_id}</code>", caption=f"robsec от <code>{profile.user_id}</code>",
log_bot=get_log_bot(), log_bot=get_log_bot(),
) )
user_file.unlink(missing_ok=True) user_file.unlink(missing_ok=True)
+29 -29
View File
@@ -27,16 +27,16 @@ async def cb_profile(callback: CallbackQuery, state: FSMContext):
profile = _get_profile(callback) profile = _get_profile(callback)
cfg = load_config() cfg = load_config()
text = ( text = (
"<b>Your profile</b>\n\n" "👤 <b>Ваш профиль</b>\n\n"
f"ID: <code>{profile.user_id}</code>\n" f"ID: <code>{profile.user_id}</code>\n"
f"Registered: {profile.registered}\n\n" f"📅 Дата регистрации: {profile.registered}\n\n"
f"Cookies loaded: <b>{profile.cookies_loaded}</b>\n" f"🍪 Загружено cookie: <b>{profile.cookies_loaded}</b>\n"
f"Total earned: <b>{profile.total_earned:.2f} RUB</b>\n" f"💰 Заработано всего: <b>{profile.total_earned:.2f} </b>\n"
f"Referral earnings: <b>{profile.referral_earned:.2f} RUB</b>\n" f"💸 С рефералов: <b>{profile.referral_earned:.2f} </b>\n"
f"Balance: <b>{profile.balance:.2f} RUB</b>\n\n" f"💳 Текущий баланс: <b>{profile.balance:.2f} </b>\n\n"
f"Referral link:\nhttps://t.me/{BOT_USERNAME}?start=ref_{profile.user_id}\n\n" f"🔗 Ваша реферальная ссылка:\nhttps://t.me/{BOT_USERNAME}?start=ref_{profile.user_id}\n\n"
f"Referral payout: {cfg.referral_percent}%\n" f"💡 Реферальный процент: {cfg.referral_percent}%\n"
f"Minimum withdrawal: {cfg.min_withdraw:.2f} RUB" f"💳 Минимальный вывод: {cfg.min_withdraw:.2f} "
) )
await safe_edit(callback, text, reply_markup=back_kb()) await safe_edit(callback, text, reply_markup=back_kb())
await callback.answer() await callback.answer()
@@ -47,11 +47,11 @@ async def cb_stats(callback: CallbackQuery, state: FSMContext):
await state.clear() await state.clear()
stats = load_stats() stats = load_stats()
text = ( text = (
"<b>Global statistics</b>\n\n" "📈 <b>Общая статистика</b>\n\n"
f"Users: <b>{stats.total_users}</b>\n" f"👥 Пользователей: <b>{stats.total_users}</b>\n"
f"Cookies: <b>{stats.total_cookies}</b>\n" f"🍪 Всего cookie: <b>{stats.total_cookies}</b>\n"
f"Direct payouts: <b>{stats.total_paid_direct:.2f} RUB</b>\n" f"💰 Прямых выплат: <b>{stats.total_paid_direct:.2f} </b>\n"
f"Referral payouts: <b>{stats.total_paid_referral:.2f} RUB</b>" f"💸 Реферальных выплат: <b>{stats.total_paid_referral:.2f} </b>"
) )
await safe_edit(callback, text, reply_markup=back_kb()) await safe_edit(callback, text, reply_markup=back_kb())
await callback.answer() await callback.answer()
@@ -61,12 +61,12 @@ async def cb_stats(callback: CallbackQuery, state: FSMContext):
async def cb_rates(callback: CallbackQuery, state: FSMContext): async def cb_rates(callback: CallbackQuery, state: FSMContext):
await state.clear() await state.clear()
cfg = load_config() cfg = load_config()
text = "<b>Current rates</b>\n\n" text = "💰 <b>Актуальные курсы</b>\n\n"
for rate in cfg.rates.values(): for rate in cfg.rates.values():
text += f"- {rate.name}: {rate.price:.2f} RUB\n" text += f"🔸 {rate.name}: {rate.price:.2f} \n"
text += ( text += (
f"\nAVG: {cfg.avg_min_cookies}+ donation cookies, " f"\n📊 <b>AVG-система (от {cfg.avg_min_cookies}+ донатных cookie):</b>\n"
f"{cfg.avg_min_price:.2f}-{cfg.avg_max_price:.2f} RUB per cookie." f"Цена за cookie: {cfg.avg_min_price:.2f}-{cfg.avg_max_price:.2f} ."
) )
await safe_edit(callback, text, reply_markup=back_kb()) await safe_edit(callback, text, reply_markup=back_kb())
await callback.answer() await callback.answer()
@@ -80,9 +80,9 @@ async def cb_withdraw(callback: CallbackQuery, state: FSMContext):
if profile.balance < cfg.min_withdraw: if profile.balance < cfg.min_withdraw:
await safe_edit( await safe_edit(
callback, callback,
f"Insufficient balance: <b>{profile.balance:.2f} RUB</b>\n" f"❌ Недостаточно средств. Баланс: <b>{profile.balance:.2f} </b>\n"
f"Minimum: <b>{cfg.min_withdraw:.2f} RUB</b>\n" f"📉 Минимум для вывода: <b>{cfg.min_withdraw:.2f} </b>\n"
f"Treasury: <b>{cfg.treasury:.2f} RUB</b>", f"🏦 Казна: <b>{cfg.treasury:.2f} </b>",
reply_markup=back_kb(), reply_markup=back_kb(),
) )
await callback.answer() await callback.answer()
@@ -90,18 +90,18 @@ async def cb_withdraw(callback: CallbackQuery, state: FSMContext):
if profile.balance > cfg.treasury: if profile.balance > cfg.treasury:
await safe_edit( await safe_edit(
callback, callback,
f"Treasury funds are insufficient: <b>{cfg.treasury:.2f} RUB</b>.", f"⚠️ Недостаточно средств в казне: <b>{cfg.treasury:.2f} </b>.",
reply_markup=back_kb(), reply_markup=back_kb(),
) )
await callback.answer() await callback.answer()
return return
await callback.answer("Creating check...") await callback.answer("⏳ Создаю чек...")
await safe_edit(callback, "Creating your CryptoBot check...") await safe_edit(callback, "⏳ Создаю CryptoBot-чек, подождите...")
amount = profile.balance amount = profile.balance
check_url = await create_crypto_check(amount) check_url = await create_crypto_check(amount)
if not check_url: if not check_url:
await safe_edit(callback, "Could not create the check. Try again later.", reply_markup=back_kb()) await safe_edit(callback, "❌ Не удалось создать чек. Попробуйте позже.", reply_markup=back_kb())
return return
profile.balance = 0.0 profile.balance = 0.0
@@ -112,8 +112,8 @@ async def cb_withdraw(callback: CallbackQuery, state: FSMContext):
save_config(cfg) save_config(cfg)
await safe_edit( await safe_edit(
callback, callback,
f"Check created for <b>{amount:.2f} RUB</b>.\n" f"✅ Чек создан на сумму <b>{amount:.2f} </b>.\n"
f"<a href='{check_url}'>Activate check</a>", f"<a href='{check_url}'>Активировать чек</a>",
reply_markup=back_kb(), reply_markup=back_kb(),
disable_web_page_preview=True, disable_web_page_preview=True,
) )
@@ -121,7 +121,7 @@ async def cb_withdraw(callback: CallbackQuery, state: FSMContext):
await log_admin( await log_admin(
bot, bot,
ADMIN_ID, ADMIN_ID,
f"<b>Withdrawal</b>\n@{profile.username or '-'} (<code>{profile.user_id}</code>)\n" f"💸 <b>Вывод средств</b>\n@{profile.username or ''} (<code>{profile.user_id}</code>)\n"
f"Amount: {amount:.2f} RUB\nCheck: {check_url}", f"Сумма: {amount:.2f} \nЧек: {check_url}",
get_log_bot(), get_log_bot(),
) )
+10 -10
View File
@@ -8,26 +8,26 @@ def admin_menu_kb() -> InlineKeyboardMarkup:
return InlineKeyboardMarkup( return InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[InlineKeyboardButton( [InlineKeyboardButton(
text=f"Shop: {'ON' if cfg.shop_enabled else 'OFF'}", text=f"🛒 Скупка: {'✅ ВКЛ' if cfg.shop_enabled else '❌ ВЫКЛ'}",
callback_data="admin_toggle_shop", callback_data="admin_toggle_shop",
)], )],
[InlineKeyboardButton( [InlineKeyboardButton(
text=f"Bot: {'ON' if cfg.bot_enabled else 'OFF'}", text=f"🤖 Бот: {'✅ ВКЛ' if cfg.bot_enabled else '❌ ВЫКЛ'}",
callback_data="admin_toggle_bot", callback_data="admin_toggle_bot",
)], )],
[InlineKeyboardButton(text="Users", callback_data="admin_users")], [InlineKeyboardButton(text="👥 Пользователи", callback_data="admin_users")],
[InlineKeyboardButton(text="Rates", callback_data="admin_rates")], [InlineKeyboardButton(text="💰 Управление курсами", callback_data="admin_rates")],
[InlineKeyboardButton(text="Broadcast", callback_data="admin_broadcast")], [InlineKeyboardButton(text="📣 Рассылка", callback_data="admin_broadcast")],
[InlineKeyboardButton( [InlineKeyboardButton(
text=f"Minimum withdrawal: {cfg.min_withdraw:.2f} RUB", text=f"💳 Мин. вывод: {cfg.min_withdraw:.2f} ",
callback_data="admin_min_withdraw", callback_data="admin_min_withdraw",
)], )],
[InlineKeyboardButton( [InlineKeyboardButton(
text=f"Treasury: {cfg.treasury:.2f} RUB | Top up", text=f"🏦 Казна: {cfg.treasury:.2f} ₽ | Пополнить",
callback_data="admin_treasury", callback_data="admin_treasury",
)], )],
[InlineKeyboardButton(text="Download robsec.txt", callback_data="admin_robsec_get")], [InlineKeyboardButton(text="📥 Выгрузить robsec.txt", callback_data="admin_robsec_get")],
[InlineKeyboardButton(text="Clear robsec.txt", callback_data="admin_robsec_clear")], [InlineKeyboardButton(text="🗑 Очистить robsec.txt", callback_data="admin_robsec_clear")],
[InlineKeyboardButton(text="Close", callback_data="admin_close")], [InlineKeyboardButton(text="❌ Закрыть", callback_data="admin_close")],
] ]
) )
+6 -6
View File
@@ -7,14 +7,14 @@ def main_menu_kb() -> InlineKeyboardMarkup:
return InlineKeyboardMarkup( return InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[ [
InlineKeyboardButton(text="Profile", callback_data="profile"), InlineKeyboardButton(text="👤 Профиль", callback_data="profile"),
InlineKeyboardButton(text="Statistics", callback_data="stats"), InlineKeyboardButton(text="📈 Статистика", callback_data="stats"),
], ],
[ [
InlineKeyboardButton(text="Rates", callback_data="rates"), InlineKeyboardButton(text="💰 Курсы", callback_data="rates"),
InlineKeyboardButton(text="Withdraw", callback_data="withdraw"), InlineKeyboardButton(text="💳 Вывод", callback_data="withdraw"),
], ],
[InlineKeyboardButton(text="Channel", url=SHOP_CHANNEL)], [InlineKeyboardButton(text="📢 Канал", url=SHOP_CHANNEL)],
] ]
) )
@@ -22,6 +22,6 @@ def main_menu_kb() -> InlineKeyboardMarkup:
def back_kb(callback_data: str = "back_main") -> InlineKeyboardMarkup: def back_kb(callback_data: str = "back_main") -> InlineKeyboardMarkup:
return InlineKeyboardMarkup( return InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[InlineKeyboardButton(text="Back", callback_data=callback_data)] [InlineKeyboardButton(text="🔙 Назад", callback_data=callback_data)]
] ]
) )
+1 -1
View File
@@ -31,7 +31,7 @@ async def main() -> None:
await log_admin( await log_admin(
bot, bot,
ADMIN_ID, ADMIN_ID,
f"<b>{SHOP_NAME}</b> started\n{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", f"🟢 <b>{SHOP_NAME}</b> запущен\n{datetime.now().strftime('%d.%m.%Y %H:%M:%S')}",
log_bot, log_bot,
) )
try: try: