Убраны подсказки лишние и добавлено пояснение для димитриев
This commit is contained in:
@@ -11,6 +11,7 @@ from aiogram.filters import Command
|
||||
from aiogram.types import CallbackQuery, Message
|
||||
|
||||
from glitchup_bot.bot.keyboards import (
|
||||
admin_admins_keyboard,
|
||||
admin_home_keyboard,
|
||||
admin_recipient_group_keyboard,
|
||||
admin_recipients_keyboard,
|
||||
@@ -58,6 +59,7 @@ logger = logging.getLogger(__name__)
|
||||
MAX_PAGE_CHARS = 3000
|
||||
MAX_PAGE_LINES = 18
|
||||
MAX_PAGINATION_SESSIONS = 200
|
||||
PENDING_ADMIN_ACTIONS: dict[int, str] = {}
|
||||
PENDING_RECIPIENT_ACTIONS: dict[int, tuple[str, str]] = {}
|
||||
PENDING_SETTING_ACTIONS: dict[int, tuple[str, str]] = {}
|
||||
|
||||
@@ -798,68 +800,11 @@ async def cb_admin_actions(callback: CallbackQuery) -> None:
|
||||
reply_markup=admin_routing_keyboard(),
|
||||
)
|
||||
return
|
||||
if action == "hint:sync":
|
||||
await _deliver_result(
|
||||
if action == "menu:admins":
|
||||
await _show_callback_screen(
|
||||
callback,
|
||||
_admin_hint_text("sync"),
|
||||
back_callback="admin:menu:sync",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
return
|
||||
if action == "hint:recipients":
|
||||
await _deliver_result(
|
||||
callback,
|
||||
_admin_hint_text("recipients"),
|
||||
back_callback="admin:menu:recipients",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
return
|
||||
if action == "hint:routing":
|
||||
await _deliver_result(
|
||||
callback,
|
||||
_admin_hint_text("routing"),
|
||||
back_callback="admin:menu:routing",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
return
|
||||
if action == "hint:settings":
|
||||
await _deliver_result(
|
||||
callback,
|
||||
_admin_hint_text("settings"),
|
||||
back_callback="admin:menu:settings",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
return
|
||||
if action == "hint:mute":
|
||||
await _deliver_result(
|
||||
callback,
|
||||
_admin_hint_text("mute"),
|
||||
back_callback="admin:open",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
return
|
||||
if action == "hint:admins":
|
||||
await _deliver_result(
|
||||
callback,
|
||||
_admin_hint_text("admins"),
|
||||
back_callback="admin:open",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
return
|
||||
if action.startswith("hint:recipient_group:"):
|
||||
group_name = action.rsplit(":", 1)[1]
|
||||
await _deliver_result(
|
||||
callback,
|
||||
_admin_hint_text("recipient_group", group_name),
|
||||
back_callback=f"admin:recipients:{group_name}",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
await _admins_text(),
|
||||
reply_markup=admin_admins_keyboard(),
|
||||
)
|
||||
return
|
||||
if action == "menu:settings":
|
||||
@@ -925,11 +870,37 @@ async def cb_admin_actions(callback: CallbackQuery) -> None:
|
||||
await _deliver_result(
|
||||
callback,
|
||||
await _admins_text(),
|
||||
back_callback="admin:open",
|
||||
back_callback="admin:menu:admins",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
return
|
||||
if action == "admins:add":
|
||||
admin_id = _callback_sender_id(callback)
|
||||
if admin_id is not None:
|
||||
PENDING_ADMIN_ACTIONS[admin_id] = "add"
|
||||
await _show_callback_screen(
|
||||
callback,
|
||||
(
|
||||
"<b>Добавление администратора</b>\n\n"
|
||||
"Отправьте следующим сообщением Telegram ID, который нужно добавить."
|
||||
),
|
||||
reply_markup=admin_result_keyboard("admin:menu:admins"),
|
||||
)
|
||||
return
|
||||
if action == "admins:del":
|
||||
admin_id = _callback_sender_id(callback)
|
||||
if admin_id is not None:
|
||||
PENDING_ADMIN_ACTIONS[admin_id] = "del"
|
||||
await _show_callback_screen(
|
||||
callback,
|
||||
(
|
||||
"<b>Удаление администратора</b>\n\n"
|
||||
"Отправьте следующим сообщением Telegram ID, который нужно удалить."
|
||||
),
|
||||
reply_markup=admin_result_keyboard("admin:menu:admins"),
|
||||
)
|
||||
return
|
||||
if action == "settings:sync_enabled":
|
||||
runtime = await get_runtime_settings()
|
||||
await set_runtime_setting("sync_enabled", "false" if runtime.sync_enabled else "true")
|
||||
@@ -938,8 +909,8 @@ async def cb_admin_actions(callback: CallbackQuery) -> None:
|
||||
await reload_scheduler()
|
||||
await _show_callback_screen(
|
||||
callback,
|
||||
await _runtime_settings_text(),
|
||||
reply_markup=admin_settings_keyboard(),
|
||||
_admin_sync_text(),
|
||||
reply_markup=admin_sync_keyboard(),
|
||||
)
|
||||
return
|
||||
if action == "settings:sync_interval":
|
||||
@@ -949,7 +920,7 @@ async def cb_admin_actions(callback: CallbackQuery) -> None:
|
||||
await _show_callback_screen(
|
||||
callback,
|
||||
_setting_prompt_text("sync_interval_minutes", ""),
|
||||
reply_markup=admin_result_keyboard("admin:menu:settings"),
|
||||
reply_markup=admin_result_keyboard("admin:menu:sync"),
|
||||
)
|
||||
return
|
||||
if action == "settings:digest_day":
|
||||
@@ -959,7 +930,7 @@ async def cb_admin_actions(callback: CallbackQuery) -> None:
|
||||
await _show_callback_screen(
|
||||
callback,
|
||||
_setting_prompt_text("digest_cron_day", ""),
|
||||
reply_markup=admin_result_keyboard("admin:menu:settings"),
|
||||
reply_markup=admin_result_keyboard("admin:menu:sync"),
|
||||
)
|
||||
return
|
||||
if action == "settings:digest_time":
|
||||
@@ -969,7 +940,7 @@ async def cb_admin_actions(callback: CallbackQuery) -> None:
|
||||
await _show_callback_screen(
|
||||
callback,
|
||||
_setting_prompt_text("digest_time", ""),
|
||||
reply_markup=admin_result_keyboard("admin:menu:settings"),
|
||||
reply_markup=admin_result_keyboard("admin:menu:sync"),
|
||||
)
|
||||
return
|
||||
if action == "settings:digest_timezone":
|
||||
@@ -979,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:settings"),
|
||||
reply_markup=admin_result_keyboard("admin:menu:sync"),
|
||||
)
|
||||
return
|
||||
if action == "settings:bot_title":
|
||||
@@ -1217,7 +1188,7 @@ async def cmd_admins(message: Message) -> None:
|
||||
await _deliver_result(
|
||||
message,
|
||||
await _admins_text(),
|
||||
back_callback="admin:open",
|
||||
back_callback="admin:menu:admins",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
@@ -1244,7 +1215,7 @@ async def cmd_admin_add(message: Message) -> None:
|
||||
await _deliver_result(
|
||||
message,
|
||||
text,
|
||||
back_callback="admin:open",
|
||||
back_callback="admin:menu:admins",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
@@ -1269,7 +1240,7 @@ async def cmd_admin_del(message: Message) -> None:
|
||||
await _deliver_result(
|
||||
message,
|
||||
text,
|
||||
back_callback="admin:open",
|
||||
back_callback="admin:menu:admins",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
@@ -1306,14 +1277,14 @@ async def cmd_pending_recipient_input(message: Message) -> None:
|
||||
|
||||
await reload_scheduler()
|
||||
text = f"Интервал sync обновлён: <code>{escape(raw_value)}</code> мин."
|
||||
back_callback = "admin:menu:settings"
|
||||
back_callback = "admin:menu:sync"
|
||||
elif setting_key == "digest_cron_day":
|
||||
await set_runtime_setting("digest_cron_day", raw_value)
|
||||
from glitchup_bot.tasks.scheduler import reload_scheduler
|
||||
|
||||
await reload_scheduler()
|
||||
text = f"День weekly digest обновлён: <code>{escape(raw_value)}</code>."
|
||||
back_callback = "admin:menu:settings"
|
||||
back_callback = "admin:menu:sync"
|
||||
elif setting_key == "digest_time":
|
||||
if ":" not in raw_value:
|
||||
await message.answer("Нужно указать время в формате HH:MM.")
|
||||
@@ -1328,14 +1299,14 @@ async def cmd_pending_recipient_input(message: Message) -> None:
|
||||
|
||||
await reload_scheduler()
|
||||
text = f"Время weekly digest обновлено: <code>{escape(raw_value)}</code>."
|
||||
back_callback = "admin:menu:settings"
|
||||
back_callback = "admin:menu:sync"
|
||||
elif setting_key == "digest_timezone":
|
||||
await set_runtime_setting("digest_timezone", raw_value)
|
||||
from glitchup_bot.tasks.scheduler import reload_scheduler
|
||||
|
||||
await reload_scheduler()
|
||||
text = f"Timezone обновлён: <code>{escape(raw_value)}</code>."
|
||||
back_callback = "admin:menu:settings"
|
||||
back_callback = "admin:menu:sync"
|
||||
else:
|
||||
await set_runtime_setting(setting_key, raw_value)
|
||||
text = f"Настройка <code>{escape(setting_key)}</code> обновлена."
|
||||
@@ -1350,6 +1321,40 @@ async def cmd_pending_recipient_input(message: Message) -> None:
|
||||
)
|
||||
return
|
||||
|
||||
if user_id in PENDING_ADMIN_ACTIONS:
|
||||
if not raw_value.lstrip("-").isdigit():
|
||||
await message.answer("Нужен числовой Telegram ID.")
|
||||
return
|
||||
|
||||
action = PENDING_ADMIN_ACTIONS.pop(user_id)
|
||||
target_id = int(raw_value)
|
||||
if action == "add":
|
||||
added = await add_admin(target_id)
|
||||
text = (
|
||||
f"Администратор <code>{target_id}</code> добавлен."
|
||||
if added
|
||||
else (
|
||||
f"Пользователь <code>{target_id}</code> уже есть среди "
|
||||
"runtime-администраторов."
|
||||
)
|
||||
)
|
||||
else:
|
||||
removed = await remove_admin(target_id)
|
||||
text = (
|
||||
f"Runtime-администратор <code>{target_id}</code> удалён."
|
||||
if removed
|
||||
else f"Runtime-администратор <code>{target_id}</code> не найден."
|
||||
)
|
||||
|
||||
await _deliver_result(
|
||||
message,
|
||||
text,
|
||||
back_callback="admin:menu:admins",
|
||||
is_admin=True,
|
||||
admin_mode=True,
|
||||
)
|
||||
return
|
||||
|
||||
if user_id not in PENDING_RECIPIENT_ACTIONS:
|
||||
return
|
||||
|
||||
@@ -1385,6 +1390,95 @@ async def cmd_pending_recipient_input(message: Message) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _admin_routing_text() -> str:
|
||||
return "\n".join(
|
||||
[
|
||||
"<b>Topic и routing</b>",
|
||||
"",
|
||||
"Здесь можно смотреть текущую схему доставки и менять topic override без env.",
|
||||
(
|
||||
"Routing определяет, к какой группе относится проект, а topic override "
|
||||
"определяет тему Telegram для backend, frontend и digest."
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def _admin_sync_text() -> str:
|
||||
return "\n".join(
|
||||
[
|
||||
"<b>Синхронизация</b>",
|
||||
"",
|
||||
"Синхронизация подтягивает актуальные issues из GlitchTip в локальный кэш бота.",
|
||||
(
|
||||
"Здесь удобно запускать ручной sync, смотреть статус и менять "
|
||||
"расписание, не трогая .env."
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def _admin_recipients_text() -> str:
|
||||
return "\n".join(
|
||||
[
|
||||
"<b>Получатели уведомлений</b>",
|
||||
"",
|
||||
"Выберите группу и назначайте Telegram ID через кнопки.",
|
||||
(
|
||||
"Получатели — это пользователи, которым бот отправляет уведомления "
|
||||
"по backend или frontend."
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
async def _runtime_settings_text() -> str:
|
||||
runtime = await get_runtime_settings()
|
||||
return "\n".join(
|
||||
[
|
||||
"<b>Настройки бота</b>",
|
||||
"",
|
||||
"Здесь собраны тексты интерфейса, которые можно менять без правки .env.",
|
||||
f"• название: {escape(runtime.bot_title)}",
|
||||
f"• описание: {escape(runtime.bot_purpose)}",
|
||||
f"• подсказка админу: {escape(runtime.bot_admin_hint)}",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def _recipient_group_text(group_name: str) -> str:
|
||||
return "\n".join(
|
||||
[
|
||||
f"<b>{escape(group_name.capitalize())} получатели</b>",
|
||||
"",
|
||||
"Здесь можно посмотреть список, добавить новый Telegram ID или удалить существующий.",
|
||||
(
|
||||
"Удаление отсюда прекращает доставку уведомлений этому пользователю "
|
||||
"по выбранной группе."
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
async def _admins_text() -> str:
|
||||
admins = await list_effective_admins()
|
||||
lines = [
|
||||
"<b>Администраторы</b>",
|
||||
"",
|
||||
"Администраторы могут менять настройки бота, получателей и расписание.",
|
||||
"Добавление и удаление можно делать прямо кнопками ниже по 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"• <code>{user_id}</code> — {source_label}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@router.message(Command("subscribe"))
|
||||
async def cmd_subscribe(message: Message) -> None:
|
||||
await message.answer("Самоподписка отключена. Получателей настраивает администратор.")
|
||||
|
||||
@@ -71,13 +71,11 @@ def admin_home_keyboard() -> InlineKeyboardMarkup:
|
||||
builder.button(text="Получатели", callback_data="admin:menu:recipients")
|
||||
builder.button(text="Topic и routing", callback_data="admin:menu:routing")
|
||||
builder.button(text="Настройки бота", callback_data="admin:menu:settings")
|
||||
builder.button(text="Администраторы", callback_data="admin:admins")
|
||||
builder.button(text="Что такое админы?", callback_data="admin:hint:admins")
|
||||
builder.button(text="Администраторы", callback_data="admin:menu:admins")
|
||||
builder.button(text="Mute rules", callback_data="admin:mute_list")
|
||||
builder.button(text="Что такое mute?", callback_data="admin:hint:mute")
|
||||
builder.button(text="Инструкция", callback_data="admin:guide")
|
||||
builder.button(text="Назад", callback_data="help:open")
|
||||
builder.adjust(2, 2, 2, 2, 1)
|
||||
builder.adjust(2, 2, 2, 2)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
@@ -85,9 +83,13 @@ def admin_sync_keyboard() -> InlineKeyboardMarkup:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="Запустить sync", callback_data="admin:sync")
|
||||
builder.button(text="Статус sync", callback_data="admin:sync_status")
|
||||
builder.button(text="Что это?", callback_data="admin:hint:sync")
|
||||
builder.button(text="Автосинк: вкл/выкл", callback_data="admin:settings:sync_enabled")
|
||||
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, 1, 1)
|
||||
builder.adjust(2, 2, 2, 1, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
@@ -97,7 +99,6 @@ def admin_routing_keyboard() -> InlineKeyboardMarkup:
|
||||
builder.button(text="Topic backend", callback_data="admin:settings:topic:backend")
|
||||
builder.button(text="Topic frontend", callback_data="admin:settings:topic:frontend")
|
||||
builder.button(text="Topic digest", callback_data="admin:settings:topic:digest")
|
||||
builder.button(text="Что это?", callback_data="admin:hint:routing")
|
||||
builder.button(text="Назад", callback_data="admin:open")
|
||||
builder.adjust(2, 2, 1, 1)
|
||||
return builder.as_markup()
|
||||
@@ -105,17 +106,11 @@ def admin_routing_keyboard() -> InlineKeyboardMarkup:
|
||||
|
||||
def admin_settings_keyboard() -> InlineKeyboardMarkup:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="Автосинк: вкл/выкл", callback_data="admin:settings:sync_enabled")
|
||||
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: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:hint:settings")
|
||||
builder.button(text="Назад", callback_data="admin:open")
|
||||
builder.adjust(2, 2, 2, 2, 1, 1)
|
||||
builder.adjust(2, 1, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
@@ -123,9 +118,8 @@ def admin_recipients_keyboard() -> InlineKeyboardMarkup:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(text="Backend", callback_data="admin:recipients:backend")
|
||||
builder.button(text="Frontend", callback_data="admin:recipients:frontend")
|
||||
builder.button(text="Что это?", callback_data="admin:hint:recipients")
|
||||
builder.button(text="Назад", callback_data="admin:open")
|
||||
builder.adjust(2, 1, 1)
|
||||
builder.adjust(2, 1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
@@ -134,9 +128,18 @@ def admin_recipient_group_keyboard(group_name: str) -> InlineKeyboardMarkup:
|
||||
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=f"admin:hint:recipient_group:{group_name}")
|
||||
builder.button(text="Назад", callback_data="admin:menu:recipients")
|
||||
builder.adjust(2, 1, 1, 1)
|
||||
builder.adjust(2, 1, 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)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user