- commands.py: try/except TelegramBadRequest around all Bot API calls (#2); /new handles "topics limit" with user-friendly message (#4) - start.py: isolate _check_and_prune_stale_topics with try/except Exception (#3) - message.py: asyncio.timeout(30) around stream_message; handle TimeoutError (#6) - db.py: add idx_chats_user_id index in init_db() (#7) - settings.py: remove dead active_chat_id variable (#8) - tests: add test_message.py (stream error/success); add 2 tests in test_commands.py (topics limit, /archive in General topic)
168 lines
6.1 KiB
Python
168 lines
6.1 KiB
Python
# adapter/telegram/handlers/settings.py
|
||
from __future__ import annotations
|
||
|
||
from aiogram import F, Router
|
||
from aiogram.filters import Command
|
||
from aiogram.fsm.context import FSMContext
|
||
from aiogram.types import CallbackQuery, Message
|
||
|
||
from adapter.telegram.keyboards.settings import (
|
||
back_keyboard,
|
||
safety_keyboard,
|
||
settings_main_keyboard,
|
||
skills_keyboard,
|
||
)
|
||
from adapter.telegram.states import SettingsState
|
||
from core.handler import EventDispatcher
|
||
from core.protocol import SettingsAction
|
||
|
||
router = Router(name="settings")
|
||
|
||
|
||
@router.message(Command("settings"))
|
||
async def cmd_settings(message: Message, state: FSMContext) -> None:
|
||
await state.set_state(SettingsState.menu)
|
||
await message.answer("⚙️ Настройки", reply_markup=settings_main_keyboard())
|
||
|
||
|
||
@router.callback_query(F.data == "settings:back")
|
||
async def cb_settings_back(callback: CallbackQuery, state: FSMContext) -> None:
|
||
await state.set_state(SettingsState.menu)
|
||
await callback.message.edit_text("⚙️ Настройки", reply_markup=settings_main_keyboard())
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data == "settings:skills")
|
||
async def cb_skills(callback: CallbackQuery, state: FSMContext, dispatcher: EventDispatcher) -> None:
|
||
platform_user_id = str(callback.from_user.id)
|
||
|
||
settings = await dispatcher._platform.get_settings(platform_user_id)
|
||
await callback.message.edit_text(
|
||
"🧩 Скиллы\nНажмите для переключения:",
|
||
reply_markup=skills_keyboard(settings.skills),
|
||
)
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data.startswith("toggle_skill:"))
|
||
async def cb_toggle_skill(
|
||
callback: CallbackQuery,
|
||
state: FSMContext,
|
||
dispatcher: EventDispatcher,
|
||
) -> None:
|
||
skill = callback.data.split(":", 1)[1]
|
||
platform_user_id = str(callback.from_user.id)
|
||
|
||
settings = await dispatcher._platform.get_settings(platform_user_id)
|
||
current = settings.skills.get(skill, False)
|
||
action = SettingsAction(
|
||
action="toggle_skill",
|
||
payload={"skill": skill, "enabled": not current},
|
||
)
|
||
await dispatcher._platform.update_settings(platform_user_id, action)
|
||
|
||
settings = await dispatcher._platform.get_settings(platform_user_id)
|
||
await callback.message.edit_reply_markup(reply_markup=skills_keyboard(settings.skills))
|
||
await callback.answer(f"{'Включён' if not current else 'Выключен'}: {skill}")
|
||
|
||
|
||
@router.callback_query(F.data == "settings:safety")
|
||
async def cb_safety(callback: CallbackQuery, state: FSMContext, dispatcher: EventDispatcher) -> None:
|
||
platform_user_id = str(callback.from_user.id)
|
||
|
||
settings = await dispatcher._platform.get_settings(platform_user_id)
|
||
await callback.message.edit_text(
|
||
"🔒 Безопасность\nПодтверждение перед выполнением:",
|
||
reply_markup=safety_keyboard(settings.safety),
|
||
)
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data.startswith("toggle_safety:"))
|
||
async def cb_toggle_safety(
|
||
callback: CallbackQuery,
|
||
state: FSMContext,
|
||
dispatcher: EventDispatcher,
|
||
) -> None:
|
||
trigger = callback.data.split(":", 1)[1]
|
||
platform_user_id = str(callback.from_user.id)
|
||
|
||
settings = await dispatcher._platform.get_settings(platform_user_id)
|
||
current = settings.safety.get(trigger, False)
|
||
action = SettingsAction(
|
||
action="set_safety",
|
||
payload={"trigger": trigger, "enabled": not current},
|
||
)
|
||
await dispatcher._platform.update_settings(platform_user_id, action)
|
||
|
||
settings = await dispatcher._platform.get_settings(platform_user_id)
|
||
await callback.message.edit_reply_markup(reply_markup=safety_keyboard(settings.safety))
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data == "settings:soul")
|
||
async def cb_soul_menu(callback: CallbackQuery, state: FSMContext) -> None:
|
||
await state.set_state(SettingsState.soul_editing)
|
||
await state.update_data(soul_field=None)
|
||
await callback.message.edit_text(
|
||
"🧠 Личность агента\n\nЧто хотите изменить?\n\n"
|
||
"Отправьте: name: <имя агента>\n"
|
||
"Или: instructions: <инструкции>\n\n"
|
||
"Или нажмите Назад.",
|
||
reply_markup=back_keyboard(),
|
||
)
|
||
await callback.answer()
|
||
|
||
|
||
@router.message(SettingsState.soul_editing)
|
||
async def handle_soul_input(
|
||
message: Message,
|
||
state: FSMContext,
|
||
dispatcher: EventDispatcher,
|
||
) -> None:
|
||
text = message.text or ""
|
||
platform_user_id = str(message.from_user.id)
|
||
|
||
if ":" in text:
|
||
field, _, value = text.partition(":")
|
||
field = field.strip().lower()
|
||
value = value.strip()
|
||
if field in ("name", "instructions"):
|
||
action = SettingsAction(
|
||
action="set_soul",
|
||
payload={"field": field, "value": value},
|
||
)
|
||
await dispatcher._platform.update_settings(platform_user_id, action)
|
||
await message.answer(f"✅ {field} обновлено.")
|
||
await state.set_state(SettingsState.menu)
|
||
await message.answer("⚙️ Настройки", reply_markup=settings_main_keyboard())
|
||
return
|
||
|
||
await message.answer(
|
||
"Формат: name: <имя> или instructions: <инструкции>\n"
|
||
"Пример: name: Алекс"
|
||
)
|
||
|
||
|
||
@router.callback_query(F.data == "settings:connectors")
|
||
async def cb_connectors(callback: CallbackQuery) -> None:
|
||
await callback.message.edit_text(
|
||
"🔗 Коннекторы\n\nОAuth-интеграции — скоро.",
|
||
reply_markup=back_keyboard(),
|
||
)
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data == "settings:plan")
|
||
async def cb_plan(callback: CallbackQuery, dispatcher: EventDispatcher) -> None:
|
||
platform_user_id = str(callback.from_user.id)
|
||
|
||
settings = await dispatcher._platform.get_settings(platform_user_id)
|
||
plan = settings.plan
|
||
text = (
|
||
f"💳 Подписка\n\n"
|
||
f"Тариф: {plan.get('name', '?')}\n"
|
||
f"Токены: {plan.get('tokens_used', 0)} / {plan.get('tokens_limit', 0)}"
|
||
)
|
||
await callback.message.edit_text(text, reply_markup=back_keyboard())
|
||
await callback.answer()
|