mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-22 04:12:09 +00:00
Revert "fix: group WATA payments in the admin menu"
This commit is contained in:
@@ -46,18 +46,9 @@ CATEGORY_GROUP_METADATA: Dict[str, Dict[str, object]] = {
|
||||
},
|
||||
"payments": {
|
||||
"title": "💳 Платежные системы",
|
||||
"description": "YooKassa, WATA, CryptoBot, MulenPay, PAL24, Tribute и Telegram Stars.",
|
||||
"description": "YooKassa, CryptoBot, MulenPay, PAL24, Tribute и Telegram Stars.",
|
||||
"icon": "💳",
|
||||
"categories": (
|
||||
"PAYMENT",
|
||||
"YOOKASSA",
|
||||
"CRYPTOBOT",
|
||||
"MULENPAY",
|
||||
"PAL24",
|
||||
"TRIBUTE",
|
||||
"TELEGRAM",
|
||||
"WATA",
|
||||
),
|
||||
"categories": ("PAYMENT", "YOOKASSA", "CRYPTOBOT", "MULENPAY", "PAL24", "TRIBUTE", "TELEGRAM"),
|
||||
},
|
||||
"subscriptions": {
|
||||
"title": "📅 Подписки и цены",
|
||||
@@ -221,21 +212,6 @@ def _get_group_description(group_key: str) -> str:
|
||||
return str(meta.get("description", ""))
|
||||
|
||||
|
||||
def _format_days(count: int) -> str:
|
||||
remainder = count % 100
|
||||
if 11 <= remainder <= 14:
|
||||
suffix = "дней"
|
||||
else:
|
||||
last_digit = count % 10
|
||||
if last_digit == 1:
|
||||
suffix = "день"
|
||||
elif last_digit in {2, 3, 4}:
|
||||
suffix = "дня"
|
||||
else:
|
||||
suffix = "дней"
|
||||
return f"{count} {suffix}"
|
||||
|
||||
|
||||
def _get_group_icon(group_key: str) -> str:
|
||||
meta = _get_group_meta(group_key)
|
||||
return str(meta.get("icon", "⚙️"))
|
||||
@@ -250,7 +226,6 @@ def _get_group_status(group_key: str) -> Tuple[str, str]:
|
||||
"MulenPay": settings.is_mulenpay_enabled(),
|
||||
"PAL24": settings.is_pal24_enabled(),
|
||||
"Tribute": settings.TRIBUTE_ENABLED,
|
||||
"WATA": settings.is_wata_enabled(),
|
||||
"Stars": settings.TELEGRAM_STARS_ENABLED,
|
||||
}
|
||||
active = sum(1 for value in payment_statuses.values() if value)
|
||||
@@ -269,7 +244,7 @@ def _get_group_status(group_key: str) -> Tuple[str, str]:
|
||||
or (settings.REMNAWAVE_USERNAME and settings.REMNAWAVE_PASSWORD)
|
||||
)
|
||||
)
|
||||
return ("🟢", "Подключено") if api_ready else ("🟡", "Нужно указать URL и ключи")
|
||||
return ("🟢", "API подключено") if api_ready else ("🟡", "Нужно указать URL и ключи")
|
||||
|
||||
if key == "server":
|
||||
mode = (settings.SERVER_STATUS_MODE or "").lower()
|
||||
@@ -289,14 +264,14 @@ def _get_group_status(group_key: str) -> Tuple[str, str]:
|
||||
user_on = settings.is_notifications_enabled()
|
||||
admin_on = settings.is_admin_notifications_enabled()
|
||||
if user_on and admin_on:
|
||||
return "🟢", "Все включены"
|
||||
return "🟢", "Все уведомления включены"
|
||||
if user_on or admin_on:
|
||||
return "🟡", "Часть уведомлений включена"
|
||||
return "⚪", "Уведомления отключены"
|
||||
|
||||
if key == "trial":
|
||||
if settings.TRIAL_DURATION_DAYS > 0:
|
||||
return "🟢", f"{_format_days(settings.TRIAL_DURATION_DAYS)}"
|
||||
return "🟢", f"{settings.TRIAL_DURATION_DAYS} дней пробного периода"
|
||||
return "⚪", "Триал отключен"
|
||||
|
||||
if key == "referral":
|
||||
@@ -306,14 +281,7 @@ def _get_group_status(group_key: str) -> Tuple[str, str]:
|
||||
or settings.REFERRAL_INVITER_BONUS_KOPEKS
|
||||
or settings.get_referred_user_reward_kopeks()
|
||||
)
|
||||
return ("🟢", "Активна") if active else ("⚪", "Бонусы не заданы")
|
||||
|
||||
if key == "support":
|
||||
if not settings.SUPPORT_MENU_ENABLED:
|
||||
return "⚪", "Меню отключено"
|
||||
if settings.SUPPORT_USERNAME and settings.SUPPORT_USERNAME != "@support":
|
||||
return "🟢", "Команда готова"
|
||||
return "🟡", "Требует настройки"
|
||||
return ("🟢", "Программа активна") if active else ("⚪", "Бонусы не заданы")
|
||||
|
||||
if key == "core":
|
||||
token_ok = bool(getattr(settings, "BOT_TOKEN", ""))
|
||||
@@ -338,12 +306,7 @@ def _get_group_status(group_key: str) -> Tuple[str, str]:
|
||||
branding = bool(settings.ENABLE_LOGO_MODE or settings.MINIAPP_CUSTOM_URL)
|
||||
return ("🟢", "Брендинг настроен") if branding else ("⚪", "Настройки по умолчанию")
|
||||
|
||||
if key == "external_admin":
|
||||
if getattr(settings, "WEB_API_DEFAULT_TOKEN", ""):
|
||||
return "🟢", "Настроено"
|
||||
return "⚪", "Требуется токен"
|
||||
|
||||
return "🟢", "Готово"
|
||||
return "🟢", "Готово к работе"
|
||||
|
||||
|
||||
def _get_setting_icon(definition, current_value: object) -> str:
|
||||
@@ -390,27 +353,22 @@ def _render_dashboard_overview() -> str:
|
||||
)
|
||||
|
||||
lines: List[str] = [
|
||||
"⚙️ <b>ПАНЕЛЬ УПРАВЛЕНИЯ БОТОМ</b>",
|
||||
"⚙️ <b>Панель управления ботом</b>",
|
||||
"",
|
||||
f"Всего параметров: <b>{total_settings}</b> • Переопределено: <b>{total_overrides}</b>",
|
||||
"",
|
||||
"Группы настроек:",
|
||||
"Выберите категорию ниже или используйте быстрые действия:",
|
||||
"",
|
||||
]
|
||||
|
||||
for group_key, title, items in grouped:
|
||||
status_icon, status_text = _get_group_status(group_key)
|
||||
description = (
|
||||
_get_group_description(group_key)
|
||||
if group_key != CATEGORY_FALLBACK_KEY
|
||||
else "Настройки без категории."
|
||||
)
|
||||
description = _get_group_description(group_key) if group_key != CATEGORY_FALLBACK_KEY else "Настройки без категории."
|
||||
total = sum(count for _, _, count in items)
|
||||
icon = _get_group_icon(group_key)
|
||||
lines.append(f"{status_icon} {icon} <b>{title}</b> — {status_text}")
|
||||
lines.append(f"{status_icon} <b>{title}</b> — {status_text}")
|
||||
if description:
|
||||
lines.append(f" {description}")
|
||||
lines.append(f"└ Настроек: {total}")
|
||||
lines.append(f" Настроек: {total}")
|
||||
lines.append("")
|
||||
|
||||
lines.append("🔍 Кнопка поиска поможет найти параметр по названию, описанию или ключу.")
|
||||
@@ -503,15 +461,7 @@ def _build_search_results_keyboard(results: List[Dict[str, object]]) -> types.In
|
||||
rows.append(
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text="⬅️ Попробовать снова",
|
||||
callback_data="botcfg_action:search",
|
||||
)
|
||||
]
|
||||
)
|
||||
rows.append(
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text="🏠 Главное меню",
|
||||
text="⬅️ В главное меню",
|
||||
callback_data="admin_bot_config",
|
||||
)
|
||||
]
|
||||
@@ -555,7 +505,7 @@ async def start_settings_search(
|
||||
|
||||
await callback.message.edit_text(
|
||||
"🔍 <b>Поиск по настройкам</b>\n\n"
|
||||
"Отправьте часть ключа или названия.\n"
|
||||
"Отправьте часть ключа или названия настройки. \n"
|
||||
"Например: <code>yookassa</code> или <code>уведомления</code>.",
|
||||
reply_markup=keyboard,
|
||||
parse_mode="HTML",
|
||||
@@ -592,8 +542,6 @@ async def handle_search_query(
|
||||
lines.append(
|
||||
f"{index}. {item['name']} — {item['value']} ({item['category_label']})"
|
||||
)
|
||||
lines.append("")
|
||||
lines.append("Нажмите на нужную настройку, чтобы открыть её.")
|
||||
text = "\n".join(lines)
|
||||
else:
|
||||
keyboard = types.InlineKeyboardMarkup(
|
||||
@@ -632,7 +580,7 @@ async def show_presets(
|
||||
lines = [
|
||||
"🎯 <b>Готовые пресеты</b>",
|
||||
"",
|
||||
"Выберите набор параметров:",
|
||||
"Выберите набор параметров, чтобы быстро применить его к боту.",
|
||||
"",
|
||||
]
|
||||
for key, meta in PRESET_METADATA.items():
|
||||
@@ -766,14 +714,12 @@ async def apply_preset(
|
||||
|
||||
title = PRESET_METADATA.get(preset_key, {}).get("title", preset_key)
|
||||
summary_lines = [
|
||||
f'✅ <b>Пресет "{title}" применен</b>',
|
||||
f"✅ Пресет <b>{title}</b> применен",
|
||||
"",
|
||||
f"Изменено параметров: <b>{len(applied)}</b>",
|
||||
]
|
||||
if applied:
|
||||
summary_lines.extend(
|
||||
["", "\n".join(f"• <code>{key}</code>" for key in applied)]
|
||||
)
|
||||
summary_lines.append("\n".join(f"• <code>{key}</code>" for key in applied))
|
||||
|
||||
keyboard = types.InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
@@ -860,7 +806,7 @@ async def start_import_settings(
|
||||
|
||||
await callback.message.edit_text(
|
||||
"📥 <b>Импорт настроек</b>\n\n"
|
||||
"Прикрепите .env файл или отправьте текстом пары <code>KEY=value</code>.\n\n"
|
||||
"Прикрепите .env файл или отправьте текстом пары <code>KEY=value</code>.\n"
|
||||
"Неизвестные параметры будут проигнорированы.",
|
||||
parse_mode="HTML",
|
||||
reply_markup=keyboard,
|
||||
@@ -940,30 +886,15 @@ async def handle_import_message(
|
||||
f"Обновлено параметров: <b>{len(applied)}</b>",
|
||||
]
|
||||
if applied:
|
||||
summary_lines.extend(
|
||||
[
|
||||
"",
|
||||
"\n".join(f"• <code>{key}</code>" for key in applied),
|
||||
]
|
||||
)
|
||||
summary_lines.append("\n".join(f"• <code>{key}</code>" for key in applied))
|
||||
|
||||
if skipped:
|
||||
summary_lines.extend(
|
||||
[
|
||||
"",
|
||||
"<b>Пропущено (неизвестные ключи):</b>",
|
||||
"\n".join(f"• <code>{key}</code>" for key in skipped),
|
||||
]
|
||||
)
|
||||
summary_lines.append("\nПропущено (неизвестные ключи):")
|
||||
summary_lines.append("\n".join(f"• <code>{key}</code>" for key in skipped))
|
||||
|
||||
if errors:
|
||||
summary_lines.extend(
|
||||
[
|
||||
"",
|
||||
"<b>Ошибки разбора:</b>",
|
||||
"\n".join(f"• {html.escape(err)}" for err in errors),
|
||||
]
|
||||
)
|
||||
summary_lines.append("\nОшибки разбора:")
|
||||
summary_lines.append("\n".join(f"• {html.escape(err)}" for err in errors))
|
||||
|
||||
keyboard = types.InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
@@ -1006,9 +937,7 @@ async def show_settings_history(
|
||||
)
|
||||
except Exception:
|
||||
formatted_value = row.value or "—"
|
||||
lines.append(
|
||||
f"{ts_text} • <code>{row.key}</code> = {html.escape(str(formatted_value))}"
|
||||
)
|
||||
lines.append(f"{ts_text} • <code>{row.key}</code> = {formatted_value}")
|
||||
else:
|
||||
lines.append("История изменений пуста.")
|
||||
|
||||
@@ -1180,12 +1109,8 @@ def _build_groups_keyboard() -> types.InlineKeyboardMarkup:
|
||||
|
||||
for group_key, title, items in grouped:
|
||||
total = sum(count for _, _, count in items)
|
||||
status_icon, status_text = _get_group_status(group_key)
|
||||
icon = _get_group_icon(group_key)
|
||||
button_text = (
|
||||
f"{status_icon} {icon} {title} — {status_text}\n"
|
||||
f"└ Настроек: {total}"
|
||||
)
|
||||
status_icon, _ = _get_group_status(group_key)
|
||||
button_text = f"{status_icon} {title} ({total})"
|
||||
rows.append(
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
@@ -1237,7 +1162,7 @@ def _build_groups_keyboard() -> types.InlineKeyboardMarkup:
|
||||
rows.append(
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text="⬅️ Назад в админку",
|
||||
text="⬅️ Назад",
|
||||
callback_data="admin_submenu_settings",
|
||||
)
|
||||
]
|
||||
@@ -1268,10 +1193,7 @@ def _build_categories_keyboard(
|
||||
rows.append(
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text=(
|
||||
f"{status_icon} {_get_group_icon(group_key)} {group_title}\n"
|
||||
f"└ Статус: {_status_text}"
|
||||
),
|
||||
text=f"{status_icon} {group_title}",
|
||||
callback_data="botcfg_group:noop",
|
||||
)
|
||||
]
|
||||
@@ -1284,7 +1206,7 @@ def _build_categories_keyboard(
|
||||
if bot_configuration_service.has_override(definition.key):
|
||||
overrides += 1
|
||||
badge = "✳️" if overrides else "•"
|
||||
button_text = f"{badge} • {label} ({count})"
|
||||
button_text = f"{badge} {label} ({count})"
|
||||
buttons.append(
|
||||
types.InlineKeyboardButton(
|
||||
text=button_text,
|
||||
@@ -1306,7 +1228,7 @@ def _build_categories_keyboard(
|
||||
)
|
||||
nav_row.append(
|
||||
types.InlineKeyboardButton(
|
||||
text=f"[{page}/{total_pages}]",
|
||||
text=f"{page}/{total_pages}",
|
||||
callback_data="botcfg_group:noop",
|
||||
)
|
||||
)
|
||||
@@ -1431,7 +1353,7 @@ def _build_settings_keyboard(
|
||||
)
|
||||
nav_row.append(
|
||||
types.InlineKeyboardButton(
|
||||
text=f"[{page}/{total_pages}]", callback_data="botcfg_cat_page:noop"
|
||||
text=f"{page}/{total_pages}", callback_data="botcfg_cat_page:noop"
|
||||
)
|
||||
)
|
||||
if page < total_pages:
|
||||
@@ -1542,11 +1464,6 @@ def _build_setting_keyboard(
|
||||
def _render_setting_text(key: str) -> str:
|
||||
summary = bot_configuration_service.get_setting_summary(key)
|
||||
guidance = bot_configuration_service.get_setting_guidance(key)
|
||||
description = guidance.get("description") or "—"
|
||||
format_hint = guidance.get("format") or "—"
|
||||
example = guidance.get("example") or "—"
|
||||
warning = guidance.get("warning") or "—"
|
||||
dependencies = guidance.get("dependencies") or "—"
|
||||
|
||||
lines = [
|
||||
f"🧩 <b>{summary['name']}</b>",
|
||||
@@ -1562,11 +1479,11 @@ def _render_setting_text(key: str) -> str:
|
||||
else []
|
||||
),
|
||||
"",
|
||||
f"📘 <b>Описание:</b> {description}",
|
||||
f"📐 <b>Формат:</b> {format_hint}",
|
||||
f"💡 <b>Пример:</b> {example}",
|
||||
f"⚠️ <b>Важно:</b> {warning}",
|
||||
f"🔗 <b>Связанные настройки:</b> {dependencies}",
|
||||
f"📘 <b>Описание:</b> {guidance['description']}",
|
||||
f"📐 <b>Формат:</b> {guidance['format']}",
|
||||
f"💡 <b>Пример:</b> {guidance['example']}",
|
||||
f"⚠️ <b>Важно:</b> {guidance['warning']}",
|
||||
f"🔗 <b>Связанные настройки:</b> {guidance['dependencies']}",
|
||||
]
|
||||
|
||||
choices = bot_configuration_service.get_choice_options(key)
|
||||
@@ -1626,13 +1543,11 @@ async def show_bot_config_group(
|
||||
keyboard = _build_categories_keyboard(group_key, group_title, items, page)
|
||||
status_icon, status_text = _get_group_status(group_key)
|
||||
description = _get_group_description(group_key)
|
||||
icon = _get_group_icon(group_key)
|
||||
lines = [f"{icon} <b>{group_title}</b>"]
|
||||
lines.append(f"Хлебные крошки: 🏠 → {group_title}")
|
||||
if status_text:
|
||||
lines.append(f"Статус: {status_icon} {status_text}")
|
||||
lines = [f"{status_icon} <b>{group_title}</b>"]
|
||||
if description:
|
||||
lines.append(description)
|
||||
if status_text:
|
||||
lines.append(f"Статус: {status_text}")
|
||||
lines.append("")
|
||||
lines.append("📂 Выберите категорию настроек:")
|
||||
await callback.message.edit_text(
|
||||
@@ -1672,12 +1587,12 @@ async def show_bot_config_category(
|
||||
)
|
||||
text_lines = [
|
||||
f"🗂 <b>{category_label}</b>",
|
||||
f"Хлебные крошки: 🏠 → {group_title} → {category_label}",
|
||||
f"Навигация: 🏠 Главное → {group_title} → {category_label}",
|
||||
]
|
||||
if category_description:
|
||||
text_lines.append(category_description)
|
||||
text_lines.append("")
|
||||
text_lines.append("📋 Список настроек категории:")
|
||||
text_lines.append("📋 Выберите настройку для просмотра или редактирования:")
|
||||
await callback.message.edit_text(
|
||||
"\n".join(text_lines),
|
||||
reply_markup=keyboard,
|
||||
@@ -1923,70 +1838,6 @@ async def test_payment_provider(
|
||||
await _refresh_markup()
|
||||
return
|
||||
|
||||
if method == "wata":
|
||||
if not settings.is_wata_enabled():
|
||||
await callback.answer("❌ WATA отключена", show_alert=True)
|
||||
return
|
||||
|
||||
amount_kopeks = 10 * 100
|
||||
description = settings.get_balance_payment_description(amount_kopeks)
|
||||
try:
|
||||
payment_result = await payment_service.create_wata_payment(
|
||||
db=db,
|
||||
user_id=db_user.id,
|
||||
amount_kopeks=amount_kopeks,
|
||||
description=f"Тестовый платеж WATA (админ): {description}",
|
||||
language=language,
|
||||
)
|
||||
except Exception:
|
||||
payment_result = None
|
||||
|
||||
if not payment_result or not payment_result.get("payment_url"):
|
||||
await callback.answer("❌ Не удалось создать платеж WATA", show_alert=True)
|
||||
await _refresh_markup()
|
||||
return
|
||||
|
||||
payment_url = payment_result["payment_url"]
|
||||
local_payment_id = payment_result.get("local_payment_id")
|
||||
payment_link_id = payment_result.get("payment_link_id")
|
||||
|
||||
message_lines = [
|
||||
"🧪 <b>Тестовый платеж WATA</b>",
|
||||
"",
|
||||
f"💰 Сумма: {texts.format_price(amount_kopeks)}",
|
||||
]
|
||||
if payment_link_id:
|
||||
message_lines.append(f"🆔 ID: {payment_link_id}")
|
||||
|
||||
reply_rows: list[list[types.InlineKeyboardButton]] = [
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text="💳 Перейти к оплате",
|
||||
url=payment_url,
|
||||
)
|
||||
]
|
||||
]
|
||||
|
||||
if local_payment_id:
|
||||
reply_rows.append(
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text="📊 Проверить статус",
|
||||
callback_data=f"check_wata_{local_payment_id}",
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
reply_markup = types.InlineKeyboardMarkup(inline_keyboard=reply_rows)
|
||||
await callback.message.answer(
|
||||
"\n".join(message_lines),
|
||||
reply_markup=reply_markup,
|
||||
parse_mode="HTML",
|
||||
)
|
||||
await callback.answer("✅ Ссылка на платеж WATA отправлена", show_alert=True)
|
||||
await _refresh_markup()
|
||||
return
|
||||
|
||||
if method == "pal24":
|
||||
if not settings.is_pal24_enabled():
|
||||
await callback.answer("❌ PayPalych отключен", show_alert=True)
|
||||
@@ -2220,7 +2071,7 @@ async def show_bot_config_setting(
|
||||
return
|
||||
text = _render_setting_text(key)
|
||||
keyboard = _build_setting_keyboard(key, group_key, category_page, settings_page)
|
||||
await callback.message.edit_text(text, reply_markup=keyboard, parse_mode="HTML")
|
||||
await callback.message.edit_text(text, reply_markup=keyboard)
|
||||
await _store_setting_context(
|
||||
state,
|
||||
key=key,
|
||||
|
||||
@@ -72,7 +72,6 @@ class BotConfigurationService:
|
||||
"LOCALIZATION": "🌍 Языки интерфейса",
|
||||
"CHANNEL": "📣 Обязательная подписка",
|
||||
"PAYMENT": "💳 Общие платежные настройки",
|
||||
"WATA": "💳 WATA",
|
||||
"TELEGRAM": "⭐ Telegram Stars",
|
||||
"CRYPTOBOT": "🪙 CryptoBot",
|
||||
"YOOKASSA": "🟣 YooKassa",
|
||||
@@ -122,7 +121,6 @@ class BotConfigurationService:
|
||||
"LOCALIZATION": "Доступные языки, локализация интерфейса и выбор языка.",
|
||||
"CHANNEL": "Настройки обязательной подписки на канал или группу.",
|
||||
"PAYMENT": "Общие тексты платежей, описания чеков и шаблоны.",
|
||||
"WATA": "Интеграция WATA для платежных ссылок и лимитов.",
|
||||
"YOOKASSA": "Интеграция с YooKassa: идентификаторы магазина и вебхуки.",
|
||||
"CRYPTOBOT": "CryptoBot и криптоплатежи через Telegram.",
|
||||
"MULENPAY": "Платежи MulenPay и параметры магазина.",
|
||||
@@ -221,7 +219,6 @@ class BotConfigurationService:
|
||||
"PAYMENT_SUBSCRIPTION_DESCRIPTION": "PAYMENT",
|
||||
"PAYMENT_BALANCE_TEMPLATE": "PAYMENT",
|
||||
"PAYMENT_SUBSCRIPTION_TEMPLATE": "PAYMENT",
|
||||
"DISABLE_TOPUP_BUTTONS": "PAYMENT",
|
||||
"ENABLE_NOTIFICATIONS": "NOTIFICATIONS",
|
||||
"NOTIFICATION_RETRY_ATTEMPTS": "NOTIFICATIONS",
|
||||
"NOTIFICATION_CACHE_HOURS": "NOTIFICATIONS",
|
||||
@@ -270,10 +267,8 @@ class BotConfigurationService:
|
||||
"CRYPTOBOT_": "CRYPTOBOT",
|
||||
"MULENPAY_": "MULENPAY",
|
||||
"PAL24_": "PAL24",
|
||||
"WATA_": "WATA",
|
||||
"PAYMENT_": "PAYMENT",
|
||||
"EXTERNAL_ADMIN_": "EXTERNAL_ADMIN",
|
||||
"BOT_": "CORE",
|
||||
"CONNECT_BUTTON_HAPP": "HAPP",
|
||||
"HAPP_": "HAPP",
|
||||
"SKIP_": "SKIP",
|
||||
|
||||
Reference in New Issue
Block a user