# - *- coding: utf- 8 - *- import html as _html from aiogram import F, Router from aiogram.filters import Command from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from tgbot.database.db_settings import SettingsRepository from tgbot.utils.const_functions import ikb, _interval_label, _interval_kb, _show_panel from tgbot.utils.misc.bot_models import FSM router = Router(name=__name__) ALLOWED_INTERVALS = (0, 5, 12, 24) # ─── FSM ──────────────────────────────────────────────────────────────────── class AdminStates(StatesGroup): wait_message = State() # ждём новое приветственное сообщение wait_btn_text = State() # ждём текст inline-кнопки wait_btn_link = State() # ждём ссылку inline-кнопки # ─── Команда /admin ────────────────────────────────────────────────────────── @router.message(Command("admin")) async def cmd_admin(message: Message, state: FSM, **_) -> None: await state.clear() await _show_panel(message) # ─── Главное меню (возврат из подменю) ─────────────────────────────────────── @router.callback_query(F.data == "adm:main") async def adm_main(call: CallbackQuery, state: FSM, **_) -> None: await state.clear() await _show_panel(call) # ─── Включить / выключить бота ─────────────────────────────────────────────── @router.callback_query(F.data == "adm:toggle") async def adm_toggle(call: CallbackQuery, **_) -> None: repo = SettingsRepository() settings = await repo.get() await repo.update(status_work=not settings.status_work) await _show_panel(call) # ─── Изменение приветственного сообщения ───────────────────────────────────── @router.callback_query(F.data == "adm:set_msg") async def adm_set_msg(call: CallbackQuery, state: FSM, **_) -> None: await state.set_state(AdminStates.wait_message) kb = InlineKeyboardBuilder() kb.row(ikb("◀ Отмена", data="adm:cancel")) await call.message.edit_text( "📝 Отправьте новое приветственное сообщение\n\n" "• Просто текст или фото с подписью (caption)\n" "• HTML-разметка поддерживается\n\n" "Чтобы отменить — нажмите кнопку ниже.", reply_markup=kb.as_markup(), ) await call.answer() @router.message(AdminStates.wait_message) async def adm_recv_message(message: Message, state: FSM, **_) -> None: repo = SettingsRepository() if message.photo: await repo.update( misc_message=message.caption or "", misc_message_media=message.photo[-1].file_id, ) elif message.text: await repo.update(misc_message=message.text, misc_message_media=None) else: await message.answer("❌ Поддерживаются только текст и фото с подписью") return await state.clear() settings = await repo.get() await message.answer("✅ Сообщение сохранено") await _show_panel(message, settings) # ─── Интервал напоминаний ───────────────────────────────────────────────────── @router.callback_query(F.data == "adm:interval") async def adm_interval_menu(call: CallbackQuery, **_) -> None: settings = await SettingsRepository().get() await call.message.edit_text( "🔔 Интервал напоминаний\n\n" "Бот повторно отправит сообщение через выбранный промежуток.\n" "Выкл — напоминания не отправляются.", reply_markup=_interval_kb(settings.remind_interval), ) await call.answer() @router.callback_query(F.data.startswith("adm:int:")) async def adm_set_interval(call: CallbackQuery, **_) -> None: try: hours = int(call.data.split(":")[2]) except (IndexError, ValueError): await call.answer("Неверное значение", show_alert=True) return if hours not in ALLOWED_INTERVALS: await call.answer("Неверное значение", show_alert=True) return await SettingsRepository().update(remind_interval=hours) await call.answer(f"✅ Интервал: {_interval_label(hours)}") await _show_panel(call) # ─── Кнопка в приветственном сообщении ─────────────────────────────────────── @router.callback_query(F.data == "adm:set_btn") async def adm_set_btn(call: CallbackQuery, state: FSM, **_) -> None: await state.set_state(AdminStates.wait_btn_text) kb = InlineKeyboardBuilder() kb.row(ikb("◀ Отмена", data="adm:cancel")) await call.message.edit_text( "🔗 Введите текст кнопки\n\nНапример: Перейти на сайт", reply_markup=kb.as_markup(), ) await call.answer() @router.message(AdminStates.wait_btn_text, F.text) async def adm_recv_btn_text(message: Message, state: FSM, **_) -> None: await state.update_data(btn_text=message.text.strip()) await state.set_state(AdminStates.wait_btn_link) await message.answer( "🔗 Теперь введите ссылку для кнопки\n\n" "Например: https://example.com" ) @router.message(AdminStates.wait_btn_link, F.text) async def adm_recv_btn_link(message: Message, state: FSM, **_) -> None: data = await state.get_data() btn_text = data.get("btn_text", "") btn_link = message.text.strip() if not btn_link.startswith(("http://", "https://", "tg://")): await message.answer( "❌ Ссылка должна начинаться с http://, " "https:// или tg://" ) return repo = SettingsRepository() await repo.update(misc_finl_text=btn_text, misc_finl_link=btn_link) await state.clear() settings = await repo.get() await message.answer("✅ Кнопка сохранена") await _show_panel(message, settings) @router.callback_query(F.data == "adm:clr_btn") async def adm_clr_btn(call: CallbackQuery, **_) -> None: await SettingsRepository().update(misc_finl_text=None, misc_finl_link=None) await call.answer("🗑 Кнопка удалена") await _show_panel(call) # ─── Отмена текущего действия ───────────────────────────────────────────────── @router.callback_query(F.data == "adm:cancel") async def adm_cancel(call: CallbackQuery, state: FSM, **_) -> None: await state.clear() await _show_panel(call)