О да фиксы
Some checks failed
CI / Lint (ruff + mypy) (push) Failing after 35s
CI / Run tests (push) Has been skipped
CI / Docker build test (push) Successful in 17s

This commit is contained in:
2026-03-31 15:31:48 +07:00
parent c10f3bf862
commit a26584c4fe
2 changed files with 126 additions and 21 deletions

View File

@@ -789,7 +789,7 @@ async def cb_admin_actions(callback: CallbackQuery) -> None:
if action == "menu:recipients": if action == "menu:recipients":
await _show_callback_screen( await _show_callback_screen(
callback, callback,
_admin_recipients_text(), await _admin_recipients_text(),
reply_markup=admin_recipients_keyboard(), reply_markup=admin_recipients_keyboard(),
) )
return return
@@ -950,7 +950,7 @@ async def cb_admin_actions(callback: CallbackQuery) -> None:
await _show_callback_screen( await _show_callback_screen(
callback, callback,
_setting_prompt_text("digest_timezone", ""), _setting_prompt_text("digest_timezone", ""),
reply_markup=admin_result_keyboard("admin:menu:sync"), reply_markup=admin_result_keyboard("admin:menu:settings"),
) )
return return
if action == "settings:bot_title": if action == "settings:bot_title":
@@ -1306,7 +1306,7 @@ async def cmd_pending_recipient_input(message: Message) -> None:
await reload_scheduler() await reload_scheduler()
text = f"Timezone обновлён: <code>{escape(raw_value)}</code>." text = f"Timezone обновлён: <code>{escape(raw_value)}</code>."
back_callback = "admin:menu:sync" back_callback = "admin:menu:settings"
else: else:
await set_runtime_setting(setting_key, raw_value) await set_runtime_setting(setting_key, raw_value)
text = f"Настройка <code>{escape(setting_key)}</code> обновлена." text = f"Настройка <code>{escape(setting_key)}</code> обновлена."
@@ -1479,6 +1479,120 @@ async def _admins_text() -> str:
return "\n".join(lines) 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"<code>{user_id}</code>" for user_id in values)
return "\n".join(
[
"<b>Получатели уведомлений</b>",
"",
"Здесь настраивается, кому бот отправляет уведомления в личные сообщения.",
"Для каждой группы ниже сразу показан текущий список пользователей.",
"",
f"<b>Backend:</b> {_format_users(backend)}",
f"<b>Frontend:</b> {_format_users(frontend)}",
]
)
def _admin_sync_text() -> str:
return "\n".join(
[
"<b>Синхронизация</b>",
"",
"Синхронизация подтягивает актуальные issues из GlitchTip в локальный кэш бота.",
"Здесь запускается ручной sync и настраивается расписание обновления и отчёта.",
]
)
def _admin_routing_text() -> str:
return "\n".join(
[
"<b>Topic и routing</b>",
"",
(
"Здесь настраивается доставка в Telegram-топики и привязка "
"проектов к backend/frontend."
),
"Это нужно, когда уведомления должны уходить не в личку, а в темы группы.",
]
)
async def _runtime_settings_text() -> str:
runtime = await get_runtime_settings()
return "\n".join(
[
"<b>Настройки бота</b>",
"",
"Здесь собраны общие тексты бота и часовой пояс для расписания.",
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"<b>{escape(title)} получатели</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)
async def _mute_rules_text() -> str:
rules = await list_rules()
lines = [
"<b>Mute rules</b>",
"",
(
"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} <code>{escape(rule.pattern)}</code>{suffix}")
return "\n".join(lines)
@router.message(Command("subscribe")) @router.message(Command("subscribe"))
async def cmd_subscribe(message: Message) -> None: async def cmd_subscribe(message: Message) -> None:
await message.answer("Самоподписка отключена. Получателей настраивает администратор.") await message.answer("Самоподписка отключена. Получателей настраивает администратор.")

View File

@@ -52,12 +52,6 @@ def help_result_keyboard(
total_pages=total_pages, total_pages=total_pages,
) )
builder.button(text="Назад", callback_data=back_callback) builder.button(text="Назад", callback_data=back_callback)
if is_admin:
if total_pages > 1:
builder.adjust(3, 1)
else:
builder.adjust(1)
else:
if total_pages > 1: if total_pages > 1:
builder.adjust(3, 1) builder.adjust(3, 1)
else: else:
@@ -73,9 +67,8 @@ def admin_home_keyboard() -> InlineKeyboardMarkup:
builder.button(text="Настройки бота", callback_data="admin:menu:settings") builder.button(text="Настройки бота", callback_data="admin:menu:settings")
builder.button(text="Администраторы", callback_data="admin:menu:admins") builder.button(text="Администраторы", callback_data="admin:menu:admins")
builder.button(text="Mute rules", callback_data="admin:mute_list") 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.button(text="Назад", callback_data="help:open")
builder.adjust(2, 2, 2, 2) builder.adjust(2, 2, 2, 1)
return builder.as_markup() 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="Интервал sync", callback_data="admin:settings:sync_interval")
builder.button(text="День отчёта", callback_data="admin:settings:digest_day") 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_time")
builder.button(text="Часовой пояс", callback_data="admin:settings:digest_timezone")
builder.button(text="Назад", callback_data="admin:open") builder.button(text="Назад", callback_data="admin:open")
builder.adjust(2, 2, 2, 1, 1) builder.adjust(2, 2, 2, 1)
return builder.as_markup() return builder.as_markup()
@@ -106,11 +98,12 @@ def admin_routing_keyboard() -> InlineKeyboardMarkup:
def admin_settings_keyboard() -> InlineKeyboardMarkup: def admin_settings_keyboard() -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder() 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_title")
builder.button(text="Описание бота", callback_data="admin:settings:bot_purpose") 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:settings:bot_admin_hint")
builder.button(text="Назад", callback_data="admin:open") builder.button(text="Назад", callback_data="admin:open")
builder.adjust(2, 1, 1) builder.adjust(2, 2, 1)
return builder.as_markup() return builder.as_markup()
@@ -125,21 +118,19 @@ def admin_recipients_keyboard() -> InlineKeyboardMarkup:
def admin_recipient_group_keyboard(group_name: str) -> InlineKeyboardMarkup: def admin_recipient_group_keyboard(group_name: str) -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder() 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:add:{group_name}")
builder.button(text="Удалить ID", callback_data=f"admin:recipients:del:{group_name}") builder.button(text="Удалить ID", callback_data=f"admin:recipients:del:{group_name}")
builder.button(text="Назад", callback_data="admin:menu:recipients") builder.button(text="Назад", callback_data="admin:menu:recipients")
builder.adjust(2, 1, 1) builder.adjust(2, 1)
return builder.as_markup() return builder.as_markup()
def admin_admins_keyboard() -> InlineKeyboardMarkup: def admin_admins_keyboard() -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder() 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:add")
builder.button(text="Удалить ID", callback_data="admin:admins:del") builder.button(text="Удалить ID", callback_data="admin:admins:del")
builder.button(text="Назад", callback_data="admin:open") builder.button(text="Назад", callback_data="admin:open")
builder.adjust(2, 1, 1) builder.adjust(2, 1)
return builder.as_markup() return builder.as_markup()