add heleket
This commit is contained in:
+340
-83
@@ -26,7 +26,8 @@ from services.browser_service import BrowserService
|
||||
from services.visit_service import VisitScheduler
|
||||
from utils.helpers import parse_range, format_range, extract_domain
|
||||
from utils.telegram import send_message_safe, send_visit_result
|
||||
from managers.storage import TaskStorage, ChatStorage, BalanceStorage
|
||||
from managers.storage import TaskStorage, ChatStorage, BalanceStorage, RubleBalanceStorage, PaymentStorage
|
||||
from services.payment_service import HelketPayment
|
||||
from auth.manager import AuthManager
|
||||
from auth.storage import AuthStorage
|
||||
from auth import ADMIN_PASSWORD
|
||||
@@ -54,8 +55,12 @@ class BotInterface:
|
||||
self.chat_storage = chat_storage
|
||||
self.auth_manager = AuthManager()
|
||||
self.auth_storage = AuthStorage()
|
||||
self.balance_storage = BalanceStorage()
|
||||
self.balance_storage = BalanceStorage() # переходы
|
||||
self.rub_storage = RubleBalanceStorage() # рубли
|
||||
self.payment_storage = PaymentStorage()
|
||||
self.heleket = HelketPayment()
|
||||
self._user_input_state: Dict[int, dict] = {}
|
||||
self._payment_state: Dict[int, str] = {} # user_id -> "topup" | "buy_clicks"
|
||||
self._user_task_state: Dict[int, dict] = {} # шаги создания задачи пользователем
|
||||
self._admin_balance_state: Dict[int, int] = {} # admin_id -> target_user_id
|
||||
self._menu_msg: Dict[int, int] = {} # user_id -> inline keyboard message_id
|
||||
@@ -90,13 +95,11 @@ class BotInterface:
|
||||
|
||||
exists = await interface.auth_storage.user_exists(user_id)
|
||||
if exists:
|
||||
sent = await message.answer("👋 С возвращением!\n\nВведите пароль для входа:")
|
||||
sent = await message.answer("С возвращением! Введите пароль:")
|
||||
interface._waiting_password[user_id] = "login"
|
||||
else:
|
||||
sent = await message.answer(
|
||||
"👋 Добро пожаловать!\n\n"
|
||||
"Вы здесь впервые. Придумайте пароль для регистрации\n"
|
||||
"_(минимум 4 символа):_"
|
||||
"Добро пожаловать!\n\nПридумайте пароль для входа (мин. 4 символа):"
|
||||
)
|
||||
interface._waiting_password[user_id] = "register"
|
||||
interface._menu_msg[user_id] = sent.message_id
|
||||
@@ -167,10 +170,7 @@ class BotInterface:
|
||||
async def require_auth(event):
|
||||
user_id = event.from_user.id
|
||||
if not interface.auth_manager.is_authenticated(user_id):
|
||||
await event.answer(
|
||||
"🔒 Требуется авторизация!\n\n"
|
||||
"Напишите администратору для получения доступа."
|
||||
)
|
||||
await event.answer("Требуется авторизация")
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -178,18 +178,10 @@ class BotInterface:
|
||||
async def require_admin(event):
|
||||
user_id = event.from_user.id
|
||||
if not interface.auth_manager.is_authenticated(user_id):
|
||||
await event.answer(
|
||||
"🔒 Требуется авторизация!\n\n"
|
||||
"/register <пароль> — зарегистрироваться\n"
|
||||
"/login <пароль> — войти"
|
||||
)
|
||||
await event.answer("Требуется авторизация")
|
||||
return False
|
||||
if not interface.auth_manager.is_admin(user_id):
|
||||
await event.answer(
|
||||
"⛔ **Нет доступа!**\n\n"
|
||||
"Эта функция доступна только администраторам.\n"
|
||||
"Войдите как администратор."
|
||||
)
|
||||
await event.answer("Нет доступа — войдите как администратор")
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -278,13 +270,13 @@ class BotInterface:
|
||||
if not await require_admin(message):
|
||||
return
|
||||
await message.answer(
|
||||
"🚀 **БЫСТРЫЙ СТАРТ**\n\n"
|
||||
"**Twitch мониторинг:**\n"
|
||||
"🚀 БЫСТРЫЙ СТАРТ\n\n"
|
||||
"Twitch мониторинг:\n"
|
||||
"`<канал> <username> [посещений] [минут] [задержка] [чтение] [домены]`\n\n"
|
||||
"**Пример:** `streamer user123 5 30 10-30 10-30`\n\n"
|
||||
"**Посещение URL:**\n"
|
||||
"Пример: `streamer user123 5 30 10-30 10-30`\n\n"
|
||||
"Посещение URL:\n"
|
||||
"`<url> [задержка] [повторов] [чтение]`\n\n"
|
||||
"**Пример:** `https://example.com 10-30 100 15-30`"
|
||||
"Пример: `https://example.com 10-30 100 15-30`"
|
||||
)
|
||||
|
||||
# === Управление задачей ===
|
||||
@@ -449,21 +441,117 @@ class BotInterface:
|
||||
# === Кабинет пользователя ===
|
||||
@dp.callback_query(F.data == "user_menu")
|
||||
async def cb_user_menu(callback: CallbackQuery):
|
||||
await interface._show_user_menu(callback.message, edit=True)
|
||||
await interface._show_user_menu(
|
||||
callback.message, edit=True, user_id=callback.from_user.id
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
@dp.callback_query(F.data == "my_balance")
|
||||
async def cb_my_balance(callback: CallbackQuery):
|
||||
uid = callback.from_user.id
|
||||
balance = await interface.balance_storage.get_balance(uid)
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="user_menu"))
|
||||
await callback.message.edit_text(
|
||||
f"💰 **Ваш баланс**\n\n**{balance}** переходов",
|
||||
reply_markup=builder.as_markup()
|
||||
await interface._show_user_menu(
|
||||
callback.message, edit=True, user_id=callback.from_user.id
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
# ── Пополнение рублей (крипта → рубли) ──────────────────────────────
|
||||
|
||||
@dp.callback_query(F.data == "topup_main")
|
||||
async def cb_topup_main(callback: CallbackQuery):
|
||||
uid = callback.from_user.id
|
||||
existing = await interface.payment_storage.get_by_user(uid)
|
||||
if existing:
|
||||
await callback.answer("⚠️ Уже есть незакрытый счёт", show_alert=True)
|
||||
await interface._show_invoice(callback, existing)
|
||||
return
|
||||
interface._payment_state[uid] = "topup"
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_pay_input"))
|
||||
await callback.message.edit_text(
|
||||
f"💳 ПОПОЛНЕНИЕ БАЛАНСА\n\n"
|
||||
f"Введите сумму пополнения в рублях:\n"
|
||||
f"(от {settings.TOPUP_MIN_RUB} до {settings.TOPUP_MAX_RUB} ₽)",
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
interface._menu_msg[uid] = callback.message.message_id
|
||||
await callback.answer()
|
||||
|
||||
@dp.callback_query(F.data == "check_pay")
|
||||
async def cb_check_payment(callback: CallbackQuery):
|
||||
uid = callback.from_user.id
|
||||
payment = await interface.payment_storage.get_by_user(uid)
|
||||
if not payment:
|
||||
await callback.answer("❌ Платёж не найден", show_alert=True)
|
||||
await interface._show_user_menu(callback.message, edit=True, user_id=callback.from_user.id)
|
||||
return
|
||||
|
||||
await callback.answer("⏳ Проверяем...")
|
||||
status = await interface.heleket.check_status(payment["payment_id"])
|
||||
|
||||
if status == "paid":
|
||||
await interface.payment_storage.delete(payment["payment_id"])
|
||||
new_rub = await interface.rub_storage.add_balance(uid, payment["rub"])
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="🛒 Купить переходы", callback_data="visit_shop"))
|
||||
builder.row(InlineKeyboardButton(text="🔙 В кабинет", callback_data="my_balance"))
|
||||
await callback.message.edit_text(
|
||||
f"✅ Оплата подтверждена!\n\n"
|
||||
f"Начислено: +{payment['rub']} ₽\n"
|
||||
f"Баланс: {new_rub} ₽",
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
elif status == "expired":
|
||||
await interface.payment_storage.delete(payment["payment_id"])
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="💳 Пополнить снова", callback_data="topup_main"))
|
||||
builder.row(InlineKeyboardButton(text="🔙 В кабинет", callback_data="my_balance"))
|
||||
await callback.message.edit_text(
|
||||
"Счёт истёк. Создайте новый.",
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
else:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="🔄 Проверить снова", callback_data="check_pay"))
|
||||
builder.row(InlineKeyboardButton(text="❌ Отменить", callback_data="cancel_pay"))
|
||||
await callback.message.edit_text(
|
||||
"⏳ Платёж ещё не поступил\n\nОбычно перевод занимает 1–5 минут.",
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
|
||||
@dp.callback_query(F.data == "cancel_pay")
|
||||
async def cb_cancel_payment(callback: CallbackQuery):
|
||||
uid = callback.from_user.id
|
||||
payment = await interface.payment_storage.get_by_user(uid)
|
||||
if payment:
|
||||
await interface.payment_storage.delete(payment["payment_id"])
|
||||
await callback.answer("Счёт отменён")
|
||||
await interface._show_user_menu(callback.message, edit=True, user_id=callback.from_user.id)
|
||||
|
||||
# ── Покупка переходов (рубли → переходы) ────────────────────────────
|
||||
|
||||
@dp.callback_query(F.data == "visit_shop")
|
||||
async def cb_visit_shop(callback: CallbackQuery):
|
||||
uid = callback.from_user.id
|
||||
rub_bal = await interface.rub_storage.get_balance(uid)
|
||||
price = settings.CLICK_PRICE_RUB
|
||||
interface._payment_state[uid] = "buy_clicks"
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_pay_input"))
|
||||
await callback.message.edit_text(
|
||||
f"🛒 Купить переходы\n\n"
|
||||
f"Баланс: {rub_bal} ₽\n"
|
||||
f"Цена: {price:.0f} ₽ за 1 переход (мин. {settings.CLICKS_MIN})\n\n"
|
||||
f"Введите количество:",
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
interface._menu_msg[uid] = callback.message.message_id
|
||||
await callback.answer()
|
||||
|
||||
@dp.callback_query(F.data == "cancel_pay_input")
|
||||
async def cb_cancel_pay_input(callback: CallbackQuery):
|
||||
uid = callback.from_user.id
|
||||
interface._payment_state.pop(uid, None)
|
||||
await interface._show_user_menu(callback.message, edit=True, user_id=callback.from_user.id)
|
||||
|
||||
@dp.callback_query(F.data == "new_task")
|
||||
async def cb_new_task(callback: CallbackQuery):
|
||||
uid = callback.from_user.id
|
||||
@@ -483,9 +571,9 @@ class BotInterface:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="❌ Отмена", callback_data="cancel_new_task"))
|
||||
await callback.message.edit_text(
|
||||
f"📝 **НОВАЯ ЗАДАЧА**\n\n"
|
||||
f"💰 Баланс: {balance} переходов\n\n"
|
||||
"Шаг 1/2: Введите название вашего Twitch канала:",
|
||||
f"📝 Новая задача\n\n"
|
||||
f"Переходов: {balance}\n\n"
|
||||
"Шаг 1/2 — введите название Twitch канала:",
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
interface._menu_msg[uid] = callback.message.message_id
|
||||
@@ -503,6 +591,7 @@ class BotInterface:
|
||||
builder.row(InlineKeyboardButton(text="❌ Отмена", callback_data="user_menu"))
|
||||
await callback.message.edit_text(
|
||||
"🔑 Введите пароль администратора:",
|
||||
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
await callback.answer()
|
||||
@@ -514,13 +603,14 @@ class BotInterface:
|
||||
await callback.answer("❌ Требуется авторизация", show_alert=True)
|
||||
return
|
||||
interface.auth_manager.login(uid, is_admin=False)
|
||||
await interface._show_user_menu(callback.message)
|
||||
await interface._show_user_menu(callback.message, user_id=uid)
|
||||
await callback.answer("👤 Переключились в режим пользователя")
|
||||
|
||||
@dp.callback_query(F.data == "cancel_new_task")
|
||||
async def cb_cancel_new_task(callback: CallbackQuery):
|
||||
interface._user_task_state.pop(callback.from_user.id, None)
|
||||
await interface._show_user_menu(callback.message)
|
||||
uid = callback.from_user.id
|
||||
interface._user_task_state.pop(uid, None)
|
||||
await interface._show_user_menu(callback.message, user_id=uid)
|
||||
await callback.answer("Отменено")
|
||||
|
||||
@dp.callback_query(F.data == "my_tasks")
|
||||
@@ -583,8 +673,8 @@ class BotInterface:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="❌ Отмена", callback_data="my_tasks"))
|
||||
await callback.message.edit_text(
|
||||
f"🌐 **Изменить домены**\n\n"
|
||||
f"Текущие: `{current}`\n\n"
|
||||
f"🌐 Изменить домены\n\n"
|
||||
f"Текущие: {current}\n\n"
|
||||
"Введите новые домены через запятую:",
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
@@ -651,7 +741,7 @@ class BotInterface:
|
||||
|
||||
if mode == "register":
|
||||
if len(text) < 4:
|
||||
sent = await message.answer("❌ Пароль слишком короткий (минимум 4 символа). Попробуйте ещё раз:")
|
||||
sent = await message.answer("Пароль слишком короткий, мин. 4 символа:")
|
||||
interface._menu_msg[user_id] = sent.message_id
|
||||
interface._waiting_password[user_id] = "register"
|
||||
return
|
||||
@@ -668,10 +758,10 @@ class BotInterface:
|
||||
elif mode == "login":
|
||||
user_data = await interface.auth_storage.get_user(user_id)
|
||||
if not user_data:
|
||||
await interface._send_temp(message, "❌ Вы не зарегистрированы.\nНапишите администратору.")
|
||||
await interface._send_temp(message, "Вы не зарегистрированы")
|
||||
return
|
||||
if interface.auth_manager.is_locked_out(user_id):
|
||||
await interface._send_temp(message, "🔒 Слишком много попыток. Попробуйте позже.")
|
||||
await interface._send_temp(message, "Слишком много попыток, попробуйте позже")
|
||||
return
|
||||
if text == ADMIN_PASSWORD:
|
||||
interface.auth_manager.login(user_id, is_admin=True)
|
||||
@@ -684,7 +774,7 @@ class BotInterface:
|
||||
await interface._show_user_menu(message)
|
||||
else:
|
||||
interface.auth_manager.record_failed_attempt(user_id)
|
||||
sent = await message.answer("❌ Неверный пароль. Попробуйте ещё раз:")
|
||||
sent = await message.answer("Неверный пароль, попробуйте ещё раз:")
|
||||
interface._menu_msg[user_id] = sent.message_id
|
||||
interface._waiting_password[user_id] = "login"
|
||||
|
||||
@@ -694,7 +784,7 @@ class BotInterface:
|
||||
await interface._update_commands()
|
||||
await interface._show_main_menu(message)
|
||||
else:
|
||||
sent = await message.answer("❌ Неверный пароль администратора. Попробуйте ещё раз:")
|
||||
sent = await message.answer("Неверный пароль администратора:")
|
||||
interface._menu_msg[user_id] = sent.message_id
|
||||
interface._waiting_password[user_id] = "admin_upgrade"
|
||||
return
|
||||
@@ -704,11 +794,13 @@ class BotInterface:
|
||||
await interface._process_user_task_input(message)
|
||||
return
|
||||
|
||||
# Ввод суммы пополнения или покупки переходов
|
||||
if user_id in interface._payment_state:
|
||||
await interface._process_payment_input(message)
|
||||
return
|
||||
|
||||
if not interface.auth_manager.is_authenticated(user_id):
|
||||
await interface._send_temp(
|
||||
message,
|
||||
"🔒 Требуется авторизация!\n\n/register <пароль> — зарегистрироваться\n/login <пароль> — войти"
|
||||
)
|
||||
await interface._send_temp(message, "Требуется авторизация")
|
||||
return
|
||||
|
||||
if not interface.auth_manager.is_admin(user_id):
|
||||
@@ -806,7 +898,7 @@ class BotInterface:
|
||||
await self.chat_storage.add_streamer(message.chat.id, channel)
|
||||
domains_str = ", ".join(domains) if domains else "все"
|
||||
text = (
|
||||
f"✅ **МОНИТОРИНГ ЗАПУЩЕН**\n\n"
|
||||
f"✅ Мониторинг запущен\n\n"
|
||||
f"📺 Канал: `{channel}`\n"
|
||||
f"👤 Отслеживание: все пользователи\n"
|
||||
f"🔗 На ссылку: {vl} визитов\n"
|
||||
@@ -878,7 +970,7 @@ class BotInterface:
|
||||
|
||||
if success:
|
||||
await message.answer(
|
||||
f"✅ **ПОСЕЩЕНИЕ ЗАПУЩЕНО**\n\n"
|
||||
f"✅ Посещение запущено\n\n"
|
||||
f"🌐 `{url}`\n"
|
||||
f"⏱️ Задержка: {format_range(min_delay, max_delay)}с\n"
|
||||
f"📖 Чтение: {format_range(min_reading, max_reading)}с\n"
|
||||
@@ -934,10 +1026,10 @@ class BotInterface:
|
||||
stats = await self.task_manager.get_stats()
|
||||
|
||||
text = (
|
||||
"👋 **ГЛАВНОЕ МЕНЮ**\n\n"
|
||||
f"📺 Twitch: **{stats['twitch']}**\n"
|
||||
f"🌐 Визитов: **{stats['visit']}**\n"
|
||||
f"⏸️ Пауза: **{stats['paused']}**\n\n"
|
||||
"👋 Главное меню\n\n"
|
||||
f"📺 Twitch: {stats['twitch']}\n"
|
||||
f"🌐 Визитов: {stats['visit']}\n"
|
||||
f"⏸️ Пауза: {stats['paused']}\n\n"
|
||||
"Выберите раздел:"
|
||||
)
|
||||
|
||||
@@ -970,21 +1062,21 @@ class BotInterface:
|
||||
twitch_done = {tid: p for tid, p in completed.items() if p.task_type == "twitch_irc"}
|
||||
|
||||
if not twitch_active and not twitch_done:
|
||||
text = "📺 **СТРИМЕРЫ**\n\nНет задач.\nОтправьте `<канал>` для добавления."
|
||||
text = "📺 Стримеры\n\nЗадач нет. Отправьте название канала для добавления."
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="🔙 В меню", callback_data="menu_main"))
|
||||
else:
|
||||
text = "📺 **СТРИМЕРЫ**\n\n"
|
||||
text = "📺 Стримеры\n\n"
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
if twitch_active:
|
||||
text += "**🔄 Активные:**\n"
|
||||
text += "🔄 Активные:\n"
|
||||
for tid, p in list(twitch_active.items())[:10]:
|
||||
emoji = p.get_status_emoji()
|
||||
domains = ", ".join(p.allowed_domains) if p.allowed_domains else "все"
|
||||
|
||||
text += (
|
||||
f"{emoji} **{p.channel}**\n"
|
||||
f"{emoji} {p.channel}\n"
|
||||
f" 🔗{p.links_found} | 📊{p.total_visits} | 🌐{domains}\n"
|
||||
)
|
||||
builder.row(InlineKeyboardButton(
|
||||
@@ -993,13 +1085,13 @@ class BotInterface:
|
||||
))
|
||||
|
||||
if twitch_done:
|
||||
text += "\n**📁 Завершенные:**\n"
|
||||
text += "\n📁 Завершенные:\n"
|
||||
for tid, p in list(twitch_done.items())[:5]:
|
||||
emoji = p.get_status_emoji()
|
||||
domains = ", ".join(p.allowed_domains) if p.allowed_domains else "все"
|
||||
|
||||
text += (
|
||||
f"{emoji} **{p.channel}**\n"
|
||||
f"{emoji} {p.channel}\n"
|
||||
f" 🔗{p.links_found} | 📊{p.total_visits} | 🌐{domains}\n"
|
||||
)
|
||||
builder.row(InlineKeyboardButton(
|
||||
@@ -1024,11 +1116,11 @@ class BotInterface:
|
||||
stats = await self.task_manager.get_stats()
|
||||
proxy_stats = self.proxy_manager.get_stats()
|
||||
text = (
|
||||
"📈 **СТАТУС**\n\n"
|
||||
f"📺 Twitch: **{stats['twitch']}**\n"
|
||||
f"🌐 Визиты: **{stats['visit']}**\n"
|
||||
f"⏸️ Пауза: **{stats['paused']}**\n"
|
||||
f"🔄 Активно: **{stats['active']}**\n\n"
|
||||
"📈 Статус\n\n"
|
||||
f"📺 Twitch: {stats['twitch']}\n"
|
||||
f"🌐 Визиты: {stats['visit']}\n"
|
||||
f"⏸️ Пауза: {stats['paused']}\n"
|
||||
f"🔄 Активно: {stats['active']}\n\n"
|
||||
f"🔌 Прокси: {proxy_stats['total']} всего, {proxy_stats['available']} доступно"
|
||||
)
|
||||
builder = InlineKeyboardBuilder()
|
||||
@@ -1046,7 +1138,7 @@ class BotInterface:
|
||||
await callback.answer("❌ Задача не найдена")
|
||||
return
|
||||
|
||||
text = f"📋 **УПРАВЛЕНИЕ**\n\n{params.get_stats_text()}"
|
||||
text = f"📋 Управление\n\n{params.get_stats_text()}"
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
@@ -1250,31 +1342,48 @@ class BotInterface:
|
||||
# КАБИНЕТ ПОЛЬЗОВАТЕЛЯ
|
||||
# =========================================================================
|
||||
|
||||
async def _show_user_menu(self, message: Message, edit: bool = False):
|
||||
user_id = message.from_user.id
|
||||
async def _show_user_menu(self, message: Message, edit: bool = False, user_id: int = None):
|
||||
if user_id is None:
|
||||
user_id = message.from_user.id if message.from_user else None
|
||||
if not user_id:
|
||||
return
|
||||
# Сбрасываем все незавершённые состояния при возврате в меню
|
||||
self._waiting_password.pop(user_id, None)
|
||||
self._user_task_state.pop(user_id, None)
|
||||
balance = await self.balance_storage.get_balance(user_id)
|
||||
self._payment_state.pop(user_id, None)
|
||||
|
||||
visits = await self.balance_storage.get_balance(user_id)
|
||||
rubles = await self.rub_storage.get_balance(user_id)
|
||||
all_tasks = await self.task_manager.get_all_tasks()
|
||||
active = sum(
|
||||
1 for p in all_tasks.values()
|
||||
if p.user_id == user_id and not p.completed and not p.stopped
|
||||
)
|
||||
pending = await self.payment_storage.get_by_user(user_id)
|
||||
|
||||
text = (
|
||||
"👤 **ЛИЧНЫЙ КАБИНЕТ**\n\n"
|
||||
f"💰 Баланс: **{balance}** переходов\n"
|
||||
f"📋 Активных задач: **{active}**\n\n"
|
||||
"Выберите действие:"
|
||||
"👤 Личный кабинет\n\n"
|
||||
f"🪙 Рублей: {rubles} ₽\n"
|
||||
f"🔢 Переходов: {visits}\n"
|
||||
f"📋 Активных задач: {active}"
|
||||
)
|
||||
if pending:
|
||||
text += "\n\n⏳ Есть незакрытый счёт"
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="📝 Новая задача", callback_data="new_task"),
|
||||
InlineKeyboardButton(text="📋 Мои задачи", callback_data="my_tasks"),
|
||||
)
|
||||
builder.row(InlineKeyboardButton(text="💰 Баланс", callback_data="my_balance"))
|
||||
if pending:
|
||||
builder.row(InlineKeyboardButton(text="⏳ Проверить оплату", callback_data="check_pay"))
|
||||
else:
|
||||
builder.row(
|
||||
InlineKeyboardButton(text="💳 Пополнить", callback_data="topup_main"),
|
||||
InlineKeyboardButton(text="🛒 Купить переходы", callback_data="visit_shop"),
|
||||
)
|
||||
builder.row(InlineKeyboardButton(text="🔑 Режим администратора", callback_data="enter_admin_mode"))
|
||||
await self._edit_or_send(message, text, builder.as_markup(), edit)
|
||||
await self._edit_or_send(message, text, builder.as_markup(), edit, user_id=user_id)
|
||||
|
||||
async def _show_user_tasks(
|
||||
self, message: Message, user_id: int,
|
||||
@@ -1284,9 +1393,9 @@ class BotInterface:
|
||||
user_tasks = {tid: p for tid, p in all_tasks.items() if p.user_id == user_id}
|
||||
|
||||
if not user_tasks:
|
||||
text = f"📋 **ЗАДАЧИ** {'пользователя `' + str(user_id) + '`' if for_admin else ''}\n\nЗадач нет."
|
||||
text = f"📋 Задачи {str(user_id) if for_admin else ''}\n\nЗадач нет."
|
||||
else:
|
||||
header = f"📋 **ЗАДАЧИ** `{user_id}`\n\n" if for_admin else "📋 **МОИ ЗАДАЧИ**\n\n"
|
||||
header = f"📋 Задачи пользователя {user_id}\n\n" if for_admin else "📋 Мои задачи\n\n"
|
||||
text = header
|
||||
for tid, p in list(user_tasks.items())[:10]:
|
||||
domains = ", ".join(p.allowed_domains) if p.allowed_domains else "все"
|
||||
@@ -1355,7 +1464,7 @@ class BotInterface:
|
||||
sent = await message.answer(
|
||||
f"📺 Канал: `{channel}`\n\n"
|
||||
"Шаг 2/2: Введите домены через запятую:\n"
|
||||
"_Пример: `twitch.tv, youtube.com`_",
|
||||
"Пример: twitch.tv, youtube.com",
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
self._menu_msg[user_id] = sent.message_id
|
||||
@@ -1437,11 +1546,159 @@ class BotInterface:
|
||||
# ПАНЕЛЬ ПОЛЬЗОВАТЕЛЕЙ (ADMIN)
|
||||
# =========================================================================
|
||||
|
||||
# =========================================================================
|
||||
# КАБИНЕТ / МАГАЗИН
|
||||
# =========================================================================
|
||||
|
||||
async def _process_payment_input(self, message: Message):
|
||||
user_id = message.from_user.id
|
||||
action = self._payment_state.pop(user_id)
|
||||
await self._safe_delete(message.bot, message.chat.id, message.message_id)
|
||||
|
||||
text = message.text.strip() if message.text else ""
|
||||
prompt_id = self._menu_msg.pop(user_id, None)
|
||||
|
||||
async def _edit_prompt(new_text: str, markup=None):
|
||||
if prompt_id:
|
||||
try:
|
||||
await message.bot.edit_message_text(
|
||||
chat_id=message.chat.id, message_id=prompt_id,
|
||||
text=new_text, reply_markup=markup
|
||||
)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
await message.answer(new_text, reply_markup=markup)
|
||||
|
||||
try:
|
||||
value = int(text)
|
||||
except (ValueError, TypeError):
|
||||
await self._send_temp(message, "❌ Введите целое число")
|
||||
self._payment_state[user_id] = action
|
||||
if prompt_id:
|
||||
self._menu_msg[user_id] = prompt_id
|
||||
return
|
||||
|
||||
if action == "topup":
|
||||
if value < settings.TOPUP_MIN_RUB:
|
||||
await self._send_temp(message, f"❌ Минимум {settings.TOPUP_MIN_RUB} ₽")
|
||||
self._payment_state[user_id] = "topup"
|
||||
if prompt_id:
|
||||
self._menu_msg[user_id] = prompt_id
|
||||
return
|
||||
if value > settings.TOPUP_MAX_RUB:
|
||||
await self._send_temp(message, f"❌ Максимум {settings.TOPUP_MAX_RUB} ₽")
|
||||
self._payment_state[user_id] = "topup"
|
||||
if prompt_id:
|
||||
self._menu_msg[user_id] = prompt_id
|
||||
return
|
||||
|
||||
order_id = f"{user_id}_topup_{int(datetime.now().timestamp())}"
|
||||
invoice = await self.heleket.create_invoice(value, order_id)
|
||||
if not invoice:
|
||||
await self._send_temp(message, "❌ Ошибка создания счёта. Попробуйте позже.")
|
||||
return
|
||||
|
||||
payment_data = {
|
||||
"user_id": user_id,
|
||||
"payment_id": invoice["payment_id"],
|
||||
"rub": value,
|
||||
"address": invoice.get("address"),
|
||||
"url": invoice.get("url"),
|
||||
"mock": invoice.get("mock", False),
|
||||
"created_at": datetime.now().isoformat(),
|
||||
}
|
||||
await self.payment_storage.save(invoice["payment_id"], payment_data)
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
if invoice.get("url"):
|
||||
builder.row(InlineKeyboardButton(text="💳 Оплатить через Heleket", url=invoice["url"]))
|
||||
builder.row(InlineKeyboardButton(text="✅ Я оплатил", callback_data="check_pay"))
|
||||
builder.row(InlineKeyboardButton(text="❌ Отменить", callback_data="cancel_pay"))
|
||||
|
||||
if payment_data["mock"]:
|
||||
body = "⚠️ Тестовый режим — добавьте HELEKET_API_KEY в .env"
|
||||
elif invoice.get("url"):
|
||||
body = "Нажмите кнопку ниже для оплаты"
|
||||
else:
|
||||
body = f"Адрес:\n`{invoice['address']}`"
|
||||
|
||||
await _edit_prompt(
|
||||
f"💳 Счёт на оплату\n\n"
|
||||
f"💰 Сумма: {value} ₽\n\n"
|
||||
f"{body}\n\n"
|
||||
f"⏱ Счёт действителен 60 минут",
|
||||
builder.as_markup()
|
||||
)
|
||||
|
||||
elif action == "buy_clicks":
|
||||
if value < settings.CLICKS_MIN:
|
||||
await self._send_temp(message, f"❌ Минимум {settings.CLICKS_MIN} переходов")
|
||||
self._payment_state[user_id] = "buy_clicks"
|
||||
if prompt_id:
|
||||
self._menu_msg[user_id] = prompt_id
|
||||
return
|
||||
|
||||
cost = int(value * settings.CLICK_PRICE_RUB)
|
||||
rub_bal = await self.rub_storage.get_balance(user_id)
|
||||
|
||||
if rub_bal < cost:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="💳 Пополнить рубли", callback_data="topup_main"))
|
||||
builder.row(InlineKeyboardButton(text="🔙 В кабинет", callback_data="my_balance"))
|
||||
await _edit_prompt(
|
||||
f"Недостаточно рублей\n\nНужно: {cost} ₽, у вас: {rub_bal} ₽",
|
||||
builder.as_markup()
|
||||
)
|
||||
return
|
||||
|
||||
new_rub = await self.rub_storage.deduct(user_id, cost)
|
||||
new_visits = await self.balance_storage.add_balance(user_id, value)
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.row(InlineKeyboardButton(text="🛒 Купить ещё", callback_data="visit_shop"))
|
||||
builder.row(InlineKeyboardButton(text="🔙 В кабинет", callback_data="my_balance"))
|
||||
await _edit_prompt(
|
||||
f"✅ Переходы начислены\n\n"
|
||||
f"+{value} переходов за {cost} ₽\n"
|
||||
f"Остаток: {new_rub} ₽ | Переходов: {new_visits}",
|
||||
builder.as_markup()
|
||||
)
|
||||
|
||||
|
||||
|
||||
async def _show_invoice(self, callback: CallbackQuery, payment: dict):
|
||||
"""Отображает счёт на оплату."""
|
||||
builder = InlineKeyboardBuilder()
|
||||
if payment.get("url"):
|
||||
builder.row(InlineKeyboardButton(text="💳 Оплатить через Heleket", url=payment["url"]))
|
||||
builder.row(InlineKeyboardButton(text="✅ Я оплатил", callback_data="check_pay"))
|
||||
builder.row(InlineKeyboardButton(text="❌ Отменить", callback_data="cancel_pay"))
|
||||
|
||||
if payment.get("mock"):
|
||||
body = "⚠️ Тестовый режим — добавьте HELEKET_API_KEY и HELEKET_SHOP_ID в .env"
|
||||
elif payment.get("url"):
|
||||
body = f"Нажмите кнопку ниже для оплаты через Heleket"
|
||||
else:
|
||||
body = f"Адрес для оплаты:\n`{payment['address']}`"
|
||||
|
||||
await callback.message.edit_text(
|
||||
f"💳 Счёт на оплату\n\n"
|
||||
f"💰 Сумма: {payment['rub']} ₽\n\n"
|
||||
f"{body}\n\n"
|
||||
f"⏱ Счёт действителен 60 минут",
|
||||
reply_markup=builder.as_markup()
|
||||
)
|
||||
|
||||
# =========================================================================
|
||||
# ПАНЕЛЬ ПОЛЬЗОВАТЕЛЕЙ (ADMIN)
|
||||
# =========================================================================
|
||||
|
||||
async def _show_users_list(self, message: Message, edit: bool = False):
|
||||
balances = await self.balance_storage.get_all()
|
||||
all_tasks = await self.task_manager.get_all_tasks()
|
||||
|
||||
text = "👥 **ПОЛЬЗОВАТЕЛИ**\n\n"
|
||||
text = "👥 Пользователи\n\n"
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
if not balances:
|
||||
@@ -1469,8 +1726,8 @@ class BotInterface:
|
||||
active = sum(1 for p in user_tasks.values() if not p.completed and not p.stopped)
|
||||
|
||||
text = (
|
||||
f"👤 **Пользователь** `{target_user_id}`\n\n"
|
||||
f"💰 Баланс: **{balance}** переходов\n"
|
||||
f"👤 Пользователь {target_user_id}\n\n"
|
||||
f"🔢 Переходов: {balance}\n"
|
||||
f"📋 Задач: {len(user_tasks)} (активных: {active})\n\n"
|
||||
"Пополнить баланс:"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user