diff --git a/src/glitchup_bot/bot/handlers/commands.py b/src/glitchup_bot/bot/handlers/commands.py index d56eb25..c44edfa 100644 --- a/src/glitchup_bot/bot/handlers/commands.py +++ b/src/glitchup_bot/bot/handlers/commands.py @@ -789,7 +789,7 @@ async def cb_admin_actions(callback: CallbackQuery) -> None: if action == "menu:recipients": await _show_callback_screen( callback, - _admin_recipients_text(), + await _admin_recipients_text(), reply_markup=admin_recipients_keyboard(), ) return @@ -950,7 +950,7 @@ async def cb_admin_actions(callback: CallbackQuery) -> None: await _show_callback_screen( callback, _setting_prompt_text("digest_timezone", ""), - reply_markup=admin_result_keyboard("admin:menu:sync"), + reply_markup=admin_result_keyboard("admin:menu:settings"), ) return if action == "settings:bot_title": @@ -1306,7 +1306,7 @@ async def cmd_pending_recipient_input(message: Message) -> None: await reload_scheduler() text = f"Timezone обновлён: {escape(raw_value)}." - back_callback = "admin:menu:sync" + back_callback = "admin:menu:settings" else: await set_runtime_setting(setting_key, raw_value) text = f"Настройка {escape(setting_key)} обновлена." @@ -1479,6 +1479,120 @@ async def _admins_text() -> str: return "\n".join(lines) +async def _admin_recipients_text() -> str: + backend = await resolve_subscribers("backend") + frontend = await resolve_subscribers("frontend") + + def _format_users(values: list[int]) -> str: + if not values: + return "никого нет" + return ", ".join(f"{user_id}" for user_id in values) + + return "\n".join( + [ + "Получатели уведомлений", + "", + "Здесь настраивается, кому бот отправляет уведомления в личные сообщения.", + "Для каждой группы ниже сразу показан текущий список пользователей.", + "", + f"Backend: {_format_users(backend)}", + f"Frontend: {_format_users(frontend)}", + ] + ) + + +def _admin_sync_text() -> str: + return "\n".join( + [ + "Синхронизация", + "", + "Синхронизация подтягивает актуальные issues из GlitchTip в локальный кэш бота.", + "Здесь запускается ручной sync и настраивается расписание обновления и отчёта.", + ] + ) + + +def _admin_routing_text() -> str: + return "\n".join( + [ + "Topic и routing", + "", + ( + "Здесь настраивается доставка в Telegram-топики и привязка " + "проектов к backend/frontend." + ), + "Это нужно, когда уведомления должны уходить не в личку, а в темы группы.", + ] + ) + + +async def _runtime_settings_text() -> str: + runtime = await get_runtime_settings() + return "\n".join( + [ + "Настройки бота", + "", + "Здесь собраны общие тексты бота и часовой пояс для расписания.", + f"• часовой пояс: {escape(runtime.digest_timezone)}", + f"• название: {escape(runtime.bot_title)}", + f"• описание: {escape(runtime.bot_purpose)}", + f"• подсказка админу: {escape(runtime.bot_admin_hint)}", + ] + ) + + +def _recipient_group_text(group_name: str) -> str: + title = group_name.capitalize() + return "\n".join( + [ + f"{escape(title)} получатели", + "", + "Здесь вы управляете пользователями, которым бот пишет в личные сообщения.", + "Можно быстро добавить или удалить Telegram ID кнопками ниже.", + ] + ) + + +async def _admins_text() -> str: + admins = await list_effective_admins() + lines = [ + "Администраторы", + "", + "Администраторы настраивают бота, получателей, топики и расписание.", + "Добавление и удаление можно делать прямо кнопками ниже по Telegram ID.", + ] + if not admins: + lines.extend(["", "Список пока пуст."]) + return "\n".join(lines) + + lines.append("") + for user_id, source in admins: + source_label = source.replace("env", "из .env") + lines.append(f"• {user_id} — {source_label}") + return "\n".join(lines) + + +async def _mute_rules_text() -> str: + rules = await list_rules() + lines = [ + "Mute rules", + "", + ( + "Mute rules скрывают шумные и неважные события, чтобы они не мешали " + "в сводках и уведомлениях." + ), + ] + if not rules: + lines.extend(["", "Правила пока не настроены."]) + return "\n".join(lines) + + lines.append("") + for rule in rules: + suffix = f" — {escape(rule.description)}" if rule.description else "" + lines.append(f"• #{rule.id} {escape(rule.pattern)}{suffix}") + return "\n".join(lines) + + @router.message(Command("subscribe")) async def cmd_subscribe(message: Message) -> None: await message.answer("Самоподписка отключена. Получателей настраивает администратор.") diff --git a/src/glitchup_bot/bot/keyboards.py b/src/glitchup_bot/bot/keyboards.py index 9478b23..c0e8f8b 100644 --- a/src/glitchup_bot/bot/keyboards.py +++ b/src/glitchup_bot/bot/keyboards.py @@ -52,16 +52,10 @@ def help_result_keyboard( total_pages=total_pages, ) builder.button(text="Назад", callback_data=back_callback) - if is_admin: - if total_pages > 1: - builder.adjust(3, 1) - else: - builder.adjust(1) + if total_pages > 1: + builder.adjust(3, 1) else: - if total_pages > 1: - builder.adjust(3, 1) - else: - builder.adjust(1) + builder.adjust(1) return builder.as_markup() @@ -73,9 +67,8 @@ def admin_home_keyboard() -> InlineKeyboardMarkup: builder.button(text="Настройки бота", callback_data="admin:menu:settings") builder.button(text="Администраторы", callback_data="admin:menu:admins") builder.button(text="Mute rules", callback_data="admin:mute_list") - builder.button(text="Инструкция", callback_data="admin:guide") builder.button(text="Назад", callback_data="help:open") - builder.adjust(2, 2, 2, 2) + builder.adjust(2, 2, 2, 1) return builder.as_markup() @@ -87,9 +80,8 @@ def admin_sync_keyboard() -> InlineKeyboardMarkup: builder.button(text="Интервал sync", callback_data="admin:settings:sync_interval") builder.button(text="День отчёта", callback_data="admin:settings:digest_day") builder.button(text="Время отчёта", callback_data="admin:settings:digest_time") - builder.button(text="Часовой пояс", callback_data="admin:settings:digest_timezone") builder.button(text="Назад", callback_data="admin:open") - builder.adjust(2, 2, 2, 1, 1) + builder.adjust(2, 2, 2, 1) return builder.as_markup() @@ -106,11 +98,12 @@ def admin_routing_keyboard() -> InlineKeyboardMarkup: def admin_settings_keyboard() -> InlineKeyboardMarkup: builder = InlineKeyboardBuilder() + builder.button(text="Часовой пояс", callback_data="admin:settings:digest_timezone") builder.button(text="Название бота", callback_data="admin:settings:bot_title") builder.button(text="Описание бота", callback_data="admin:settings:bot_purpose") builder.button(text="Подсказка админу", callback_data="admin:settings:bot_admin_hint") builder.button(text="Назад", callback_data="admin:open") - builder.adjust(2, 1, 1) + builder.adjust(2, 2, 1) return builder.as_markup() @@ -125,21 +118,19 @@ def admin_recipients_keyboard() -> InlineKeyboardMarkup: def admin_recipient_group_keyboard(group_name: str) -> InlineKeyboardMarkup: builder = InlineKeyboardBuilder() - builder.button(text="Список", callback_data=f"admin:recipients:list:{group_name}") builder.button(text="Добавить ID", callback_data=f"admin:recipients:add:{group_name}") builder.button(text="Удалить ID", callback_data=f"admin:recipients:del:{group_name}") builder.button(text="Назад", callback_data="admin:menu:recipients") - builder.adjust(2, 1, 1) + builder.adjust(2, 1) return builder.as_markup() def admin_admins_keyboard() -> InlineKeyboardMarkup: builder = InlineKeyboardBuilder() - builder.button(text="Список", callback_data="admin:admins") builder.button(text="Добавить ID", callback_data="admin:admins:add") builder.button(text="Удалить ID", callback_data="admin:admins:del") builder.button(text="Назад", callback_data="admin:open") - builder.adjust(2, 1, 1) + builder.adjust(2, 1) return builder.as_markup()