feat(matrix): add adapter baseline and platform-aware command hints
This commit is contained in:
parent
bcdaea5143
commit
82eb711844
20 changed files with 1127 additions and 3 deletions
41
adapter/matrix/handlers/__init__.py
Normal file
41
adapter/matrix/handlers/__init__.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from adapter.matrix.handlers.chat import (
|
||||
handle_archive,
|
||||
handle_list_chats,
|
||||
handle_new_chat,
|
||||
handle_rename,
|
||||
)
|
||||
from adapter.matrix.handlers.confirm import handle_cancel, handle_confirm
|
||||
from adapter.matrix.handlers.settings import (
|
||||
handle_settings,
|
||||
handle_settings_connectors,
|
||||
handle_settings_plan,
|
||||
handle_settings_safety,
|
||||
handle_settings_skills,
|
||||
handle_settings_soul,
|
||||
handle_settings_status,
|
||||
handle_settings_whoami,
|
||||
handle_toggle_skill,
|
||||
)
|
||||
from core.handler import EventDispatcher
|
||||
from core.protocol import IncomingCallback, IncomingCommand
|
||||
|
||||
|
||||
def register_matrix_handlers(dispatcher: EventDispatcher) -> None:
|
||||
dispatcher.register(IncomingCommand, "new", handle_new_chat)
|
||||
dispatcher.register(IncomingCommand, "chats", handle_list_chats)
|
||||
dispatcher.register(IncomingCommand, "rename", handle_rename)
|
||||
dispatcher.register(IncomingCommand, "archive", handle_archive)
|
||||
dispatcher.register(IncomingCommand, "settings", handle_settings)
|
||||
dispatcher.register(IncomingCommand, "settings_skills", handle_settings_skills)
|
||||
dispatcher.register(IncomingCommand, "settings_connectors", handle_settings_connectors)
|
||||
dispatcher.register(IncomingCommand, "settings_soul", handle_settings_soul)
|
||||
dispatcher.register(IncomingCommand, "settings_safety", handle_settings_safety)
|
||||
dispatcher.register(IncomingCommand, "settings_plan", handle_settings_plan)
|
||||
dispatcher.register(IncomingCommand, "settings_status", handle_settings_status)
|
||||
dispatcher.register(IncomingCommand, "settings_whoami", handle_settings_whoami)
|
||||
|
||||
dispatcher.register(IncomingCallback, "confirm", handle_confirm)
|
||||
dispatcher.register(IncomingCallback, "cancel", handle_cancel)
|
||||
dispatcher.register(IncomingCallback, "toggle_skill", handle_toggle_skill)
|
||||
34
adapter/matrix/handlers/auth.py
Normal file
34
adapter/matrix/handlers/auth.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from adapter.matrix.store import get_room_meta, set_room_meta
|
||||
|
||||
|
||||
async def handle_invite(client: Any, room: Any, event: Any, platform, store, auth_mgr) -> None:
|
||||
existing = await get_room_meta(store, room.room_id)
|
||||
if existing is not None:
|
||||
return
|
||||
|
||||
user = await platform.get_or_create_user(
|
||||
external_id=getattr(event, "sender", ""),
|
||||
platform="matrix",
|
||||
display_name=getattr(room, "display_name", None),
|
||||
)
|
||||
await auth_mgr.confirm(getattr(event, "sender", ""))
|
||||
await client.join(room.room_id)
|
||||
await set_room_meta(
|
||||
store,
|
||||
room.room_id,
|
||||
{
|
||||
"room_type": "chat",
|
||||
"chat_id": "C1",
|
||||
"display_name": getattr(room, "display_name", room.room_id),
|
||||
"matrix_user_id": getattr(event, "sender", user.external_id),
|
||||
},
|
||||
)
|
||||
message = (
|
||||
f"Привет, {user.display_name or user.external_id}! Пиши — я здесь.\n\n"
|
||||
f"Команды: !new · !chats · !rename · !archive · !skills"
|
||||
)
|
||||
await client.room_send(room.room_id, "m.room.message", {"msgtype": "m.text", "body": message})
|
||||
50
adapter/matrix/handlers/chat.py
Normal file
50
adapter/matrix/handlers/chat.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from core.protocol import IncomingCommand, OutgoingMessage
|
||||
|
||||
|
||||
async def handle_new_chat(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
if not await auth_mgr.is_authenticated(event.user_id):
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text="Введите !start чтобы начать.")]
|
||||
|
||||
name = " ".join(event.args).strip() if event.args else ""
|
||||
chats = await chat_mgr.list_active(event.user_id)
|
||||
chat_id = f"C{len(chats) + 1}"
|
||||
ctx = await chat_mgr.get_or_create(
|
||||
user_id=event.user_id,
|
||||
chat_id=chat_id,
|
||||
platform=event.platform,
|
||||
surface_ref=event.chat_id,
|
||||
name=name or None,
|
||||
)
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=event.chat_id, text=f"Создан чат: {ctx.display_name} ({ctx.chat_id})"
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
async def handle_list_chats(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
chats = await chat_mgr.list_active(event.user_id)
|
||||
if not chats:
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text="Нет активных чатов.")]
|
||||
lines = [f"• {c.display_name} ({c.chat_id})" for c in chats]
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text="\n".join(lines))]
|
||||
|
||||
|
||||
async def handle_rename(event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr) -> list:
|
||||
if not event.args:
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text="Укажите название: !rename Название")]
|
||||
ctx = await chat_mgr.rename(event.chat_id, " ".join(event.args), user_id=event.user_id)
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text=f"Переименован в: {ctx.display_name}")]
|
||||
|
||||
|
||||
async def handle_archive(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
await chat_mgr.archive(event.chat_id, user_id=event.user_id)
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text="Чат архивирован.")]
|
||||
19
adapter/matrix/handlers/confirm.py
Normal file
19
adapter/matrix/handlers/confirm.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from core.protocol import IncomingCallback, OutgoingMessage
|
||||
|
||||
|
||||
async def handle_confirm(
|
||||
event: IncomingCallback, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
action_id = event.payload.get("action_id", "unknown")
|
||||
return [
|
||||
OutgoingMessage(chat_id=event.chat_id, text=f"Действие подтверждено (id: {action_id}).")
|
||||
]
|
||||
|
||||
|
||||
async def handle_cancel(
|
||||
event: IncomingCallback, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
action_id = event.payload.get("action_id", "unknown")
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text=f"Действие отменено (id: {action_id}).")]
|
||||
145
adapter/matrix/handlers/settings.py
Normal file
145
adapter/matrix/handlers/settings.py
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from adapter.matrix.reactions import build_skills_text
|
||||
from core.protocol import IncomingCommand, OutgoingMessage, SettingsAction
|
||||
|
||||
|
||||
def _render_mapping(title: str, data: dict | None) -> str:
|
||||
data = data or {}
|
||||
lines = [title]
|
||||
if not data:
|
||||
lines.append("Нет данных.")
|
||||
else:
|
||||
for key, value in data.items():
|
||||
lines.append(f"• {key}: {value}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _parse_bool(value: str) -> bool:
|
||||
return value.lower() in {"1", "true", "yes", "on", "enable", "enabled"}
|
||||
|
||||
|
||||
async def handle_settings(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=event.chat_id,
|
||||
text=(
|
||||
"⚙️ Настройки Matrix\n"
|
||||
"!skills\n"
|
||||
"!connectors\n"
|
||||
"!soul [field value]\n"
|
||||
"!safety [trigger on|off]\n"
|
||||
"!plan\n"
|
||||
"!status\n"
|
||||
"!whoami"
|
||||
),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
async def handle_settings_skills(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
settings = await settings_mgr.get(event.user_id)
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text=build_skills_text(settings))]
|
||||
|
||||
|
||||
async def handle_settings_connectors(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
settings = await settings_mgr.get(event.user_id)
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=event.chat_id, text=_render_mapping("🔗 Коннекторы", settings.connectors)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
async def handle_settings_soul(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
if len(event.args) >= 2:
|
||||
field = event.args[0]
|
||||
value = " ".join(event.args[1:])
|
||||
await settings_mgr.apply(
|
||||
event.user_id,
|
||||
SettingsAction(action="set_soul", payload={"field": field, "value": value}),
|
||||
)
|
||||
return [
|
||||
OutgoingMessage(chat_id=event.chat_id, text=f"Личность обновлена: {field} = {value}")
|
||||
]
|
||||
settings = await settings_mgr.get(event.user_id)
|
||||
return [
|
||||
OutgoingMessage(chat_id=event.chat_id, text=_render_mapping("🧠 Личность", settings.soul))
|
||||
]
|
||||
|
||||
|
||||
async def handle_settings_safety(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
if len(event.args) >= 2:
|
||||
trigger = event.args[0]
|
||||
enabled = _parse_bool(event.args[1])
|
||||
await settings_mgr.apply(
|
||||
event.user_id,
|
||||
SettingsAction(action="set_safety", payload={"trigger": trigger, "enabled": enabled}),
|
||||
)
|
||||
state = "включена" if enabled else "выключена"
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text=f"Безопасность {trigger} {state}")]
|
||||
settings = await settings_mgr.get(event.user_id)
|
||||
return [
|
||||
OutgoingMessage(
|
||||
chat_id=event.chat_id, text=_render_mapping("🔒 Безопасность", settings.safety)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
async def handle_settings_plan(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
settings = await settings_mgr.get(event.user_id)
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text=_render_mapping("💳 План", settings.plan))]
|
||||
|
||||
|
||||
async def handle_settings_status(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
chats = await chat_mgr.list_active(event.user_id)
|
||||
settings = await settings_mgr.get(event.user_id)
|
||||
text = "\n".join(
|
||||
[
|
||||
"📊 Статус",
|
||||
f"Активных чатов: {len(chats)}",
|
||||
f"Скиллов: {len(settings.skills)}",
|
||||
f"Коннекторов: {len(settings.connectors)}",
|
||||
]
|
||||
)
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text=text)]
|
||||
|
||||
|
||||
async def handle_settings_whoami(
|
||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||
) -> list:
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text=f"👤 {event.platform}:{event.user_id}")]
|
||||
|
||||
|
||||
async def handle_toggle_skill(event, auth_mgr, platform, chat_mgr, settings_mgr) -> list:
|
||||
settings = await settings_mgr.get(event.user_id)
|
||||
keys = list(settings.skills.keys())
|
||||
skill = event.payload.get("skill")
|
||||
if not skill:
|
||||
idx = event.payload.get("skill_index")
|
||||
if isinstance(idx, int) and 1 <= idx <= len(keys):
|
||||
skill = keys[idx - 1]
|
||||
if not skill:
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text="Ошибка: не удалось определить навык.")]
|
||||
|
||||
enabled = not bool(settings.skills.get(skill, False))
|
||||
await settings_mgr.apply(
|
||||
event.user_id,
|
||||
SettingsAction(action="toggle_skill", payload={"skill": skill, "enabled": enabled}),
|
||||
)
|
||||
state = "включён" if enabled else "выключен"
|
||||
return [OutgoingMessage(chat_id=event.chat_id, text=f"Навык {skill} {state}.")]
|
||||
Loading…
Add table
Add a link
Reference in a new issue