feat(01-03): switch Matrix confirmations to text commands
- replace reaction-based helper text with !yes/!no and !skill commands - resolve confirm and cancel through pending confirmation state - render !settings as a read-only status dashboard
This commit is contained in:
parent
8a6a33a2ce
commit
01610ef768
4 changed files with 86 additions and 70 deletions
|
|
@ -6,7 +6,7 @@ from adapter.matrix.handlers.chat import (
|
||||||
make_handle_new_chat,
|
make_handle_new_chat,
|
||||||
make_handle_rename,
|
make_handle_rename,
|
||||||
)
|
)
|
||||||
from adapter.matrix.handlers.confirm import handle_cancel, handle_confirm
|
from adapter.matrix.handlers.confirm import make_handle_cancel, make_handle_confirm
|
||||||
from adapter.matrix.handlers.settings import (
|
from adapter.matrix.handlers.settings import (
|
||||||
handle_settings,
|
handle_settings,
|
||||||
handle_settings_connectors,
|
handle_settings_connectors,
|
||||||
|
|
@ -36,6 +36,6 @@ def register_matrix_handlers(dispatcher: EventDispatcher, client=None, store=Non
|
||||||
dispatcher.register(IncomingCommand, "settings_status", handle_settings_status)
|
dispatcher.register(IncomingCommand, "settings_status", handle_settings_status)
|
||||||
dispatcher.register(IncomingCommand, "settings_whoami", handle_settings_whoami)
|
dispatcher.register(IncomingCommand, "settings_whoami", handle_settings_whoami)
|
||||||
|
|
||||||
dispatcher.register(IncomingCallback, "confirm", handle_confirm)
|
dispatcher.register(IncomingCallback, "confirm", make_handle_confirm(store))
|
||||||
dispatcher.register(IncomingCallback, "cancel", handle_cancel)
|
dispatcher.register(IncomingCallback, "cancel", make_handle_cancel(store))
|
||||||
dispatcher.register(IncomingCallback, "toggle_skill", handle_toggle_skill)
|
dispatcher.register(IncomingCallback, "toggle_skill", handle_toggle_skill)
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,40 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from adapter.matrix.store import clear_pending_confirm, get_pending_confirm
|
||||||
from core.protocol import IncomingCallback, OutgoingMessage
|
from core.protocol import IncomingCallback, OutgoingMessage
|
||||||
|
|
||||||
|
|
||||||
async def handle_confirm(
|
def make_handle_confirm(store=None):
|
||||||
event: IncomingCallback, auth_mgr, platform, chat_mgr, settings_mgr
|
async def handle_confirm(
|
||||||
) -> list:
|
event: IncomingCallback, auth_mgr, platform, chat_mgr, settings_mgr
|
||||||
action_id = event.payload.get("action_id", "unknown")
|
) -> list:
|
||||||
return [
|
if store is None:
|
||||||
OutgoingMessage(chat_id=event.chat_id, text=f"Действие подтверждено (id: {action_id}).")
|
return [OutgoingMessage(chat_id=event.chat_id, text="Нет ожидающих подтверждений.")]
|
||||||
]
|
|
||||||
|
pending = await get_pending_confirm(store, event.chat_id)
|
||||||
|
if not pending:
|
||||||
|
return [OutgoingMessage(chat_id=event.chat_id, text="Нет ожидающих подтверждений.")]
|
||||||
|
|
||||||
|
description = pending.get("description", "действие")
|
||||||
|
await clear_pending_confirm(store, event.chat_id)
|
||||||
|
|
||||||
|
return [OutgoingMessage(chat_id=event.chat_id, text=f"Подтверждено: {description}")]
|
||||||
|
|
||||||
|
return handle_confirm
|
||||||
|
|
||||||
|
|
||||||
async def handle_cancel(
|
def make_handle_cancel(store=None):
|
||||||
event: IncomingCallback, auth_mgr, platform, chat_mgr, settings_mgr
|
async def handle_cancel(
|
||||||
) -> list:
|
event: IncomingCallback, auth_mgr, platform, chat_mgr, settings_mgr
|
||||||
action_id = event.payload.get("action_id", "unknown")
|
) -> list:
|
||||||
return [OutgoingMessage(chat_id=event.chat_id, text=f"Действие отменено (id: {action_id}).")]
|
if store is None:
|
||||||
|
return [OutgoingMessage(chat_id=event.chat_id, text="Нет ожидающих подтверждений.")]
|
||||||
|
|
||||||
|
pending = await get_pending_confirm(store, event.chat_id)
|
||||||
|
if not pending:
|
||||||
|
return [OutgoingMessage(chat_id=event.chat_id, text="Нет ожидающих подтверждений.")]
|
||||||
|
|
||||||
|
await clear_pending_confirm(store, event.chat_id)
|
||||||
|
return [OutgoingMessage(chat_id=event.chat_id, text="Действие отменено.")]
|
||||||
|
|
||||||
|
return handle_cancel
|
||||||
|
|
|
||||||
|
|
@ -22,21 +22,50 @@ def _parse_bool(value: str) -> bool:
|
||||||
async def handle_settings(
|
async def handle_settings(
|
||||||
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
event: IncomingCommand, auth_mgr, platform, chat_mgr, settings_mgr
|
||||||
) -> list:
|
) -> list:
|
||||||
return [
|
settings = await settings_mgr.get(event.user_id)
|
||||||
OutgoingMessage(
|
chats = await chat_mgr.list_active(event.user_id)
|
||||||
chat_id=event.chat_id,
|
|
||||||
text=(
|
skills_lines = []
|
||||||
"⚙️ Настройки Matrix\n"
|
for name, enabled in settings.skills.items():
|
||||||
"!skills\n"
|
state = "on" if enabled else "off"
|
||||||
"!connectors\n"
|
skills_lines.append(f" {state} {name}")
|
||||||
"!soul [field value]\n"
|
skills_text = "\n".join(skills_lines) if skills_lines else " нет навыков"
|
||||||
"!safety [trigger on|off]\n"
|
|
||||||
"!plan\n"
|
soul_lines = []
|
||||||
"!status\n"
|
for key, value in (settings.soul or {}).items():
|
||||||
"!whoami"
|
soul_lines.append(f" {key}: {value}")
|
||||||
),
|
soul_text = "\n".join(soul_lines) if soul_lines else " по умолчанию"
|
||||||
)
|
|
||||||
]
|
safety_lines = []
|
||||||
|
for key, value in (settings.safety or {}).items():
|
||||||
|
state = "on" if value else "off"
|
||||||
|
safety_lines.append(f" {state} {key}")
|
||||||
|
safety_text = "\n".join(safety_lines) if safety_lines else " по умолчанию"
|
||||||
|
|
||||||
|
chat_lines = [f" {chat.display_name} ({chat.chat_id})" for chat in chats]
|
||||||
|
chats_text = "\n".join(chat_lines) if chat_lines else " нет активных чатов"
|
||||||
|
|
||||||
|
dashboard = "\n".join(
|
||||||
|
[
|
||||||
|
"Настройки",
|
||||||
|
"",
|
||||||
|
"Скиллы:",
|
||||||
|
skills_text,
|
||||||
|
"",
|
||||||
|
"Личность:",
|
||||||
|
soul_text,
|
||||||
|
"",
|
||||||
|
"Безопасность:",
|
||||||
|
safety_text,
|
||||||
|
"",
|
||||||
|
f"Активные чаты ({len(chats)}):",
|
||||||
|
chats_text,
|
||||||
|
"",
|
||||||
|
"Изменить: !skills, !soul, !safety",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
return [OutgoingMessage(chat_id=event.chat_id, text=dashboard)]
|
||||||
|
|
||||||
|
|
||||||
async def handle_settings_skills(
|
async def handle_settings_skills(
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from nio import AsyncClient
|
|
||||||
|
|
||||||
from sdk.interface import UserSettings
|
from sdk.interface import UserSettings
|
||||||
|
|
||||||
CONFIRM_REACTION = "👍"
|
CONFIRM_REACTION = "👍"
|
||||||
|
|
@ -13,56 +9,26 @@ REACTION_TO_INDEX = {emoji: idx + 1 for idx, emoji in enumerate(SKILL_REACTIONS)
|
||||||
|
|
||||||
|
|
||||||
def build_skills_text(settings: UserSettings) -> str:
|
def build_skills_text(settings: UserSettings) -> str:
|
||||||
lines: list[str] = ["🧩 Скиллы"]
|
lines: list[str] = ["Скиллы"]
|
||||||
for idx, (name, enabled) in enumerate(settings.skills.items(), start=1):
|
for idx, (name, enabled) in enumerate(settings.skills.items(), start=1):
|
||||||
state = "✅" if enabled else "❌"
|
state = "on" if enabled else "off"
|
||||||
emoji = SKILL_REACTIONS[idx - 1] if idx - 1 < len(SKILL_REACTIONS) else f"{idx}."
|
emoji = SKILL_REACTIONS[idx - 1] if idx - 1 < len(SKILL_REACTIONS) else f"{idx}."
|
||||||
lines.append(f"{state} {emoji} {name}")
|
lines.append(f" {state} {emoji} {name}")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("Реакции 1️⃣-9️⃣ переключают навыки.")
|
lines.append("!skill on/off <название> — переключить навык.")
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
def build_confirmation_text(description: str) -> str:
|
def build_confirmation_text(description: str) -> str:
|
||||||
return "\n".join(
|
return "\n".join(
|
||||||
[
|
[
|
||||||
"🤖 Lambda",
|
"Lambda",
|
||||||
description,
|
description,
|
||||||
"",
|
"",
|
||||||
f"{CONFIRM_REACTION} подтвердить · {CANCEL_REACTION} отменить",
|
"Ответьте !yes для подтверждения или !no для отмены.",
|
||||||
"!yes — подтвердить · !no — отменить",
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def reaction_to_skill_index(key: str) -> int | None:
|
def reaction_to_skill_index(key: str) -> int | None:
|
||||||
return REACTION_TO_INDEX.get(key)
|
return REACTION_TO_INDEX.get(key)
|
||||||
|
|
||||||
|
|
||||||
async def add_reaction(client: AsyncClient, room_id: str, event_id: str, key: str) -> Any:
|
|
||||||
return await client.room_send(
|
|
||||||
room_id,
|
|
||||||
"m.reaction",
|
|
||||||
{
|
|
||||||
"m.relates_to": {
|
|
||||||
"rel_type": "m.annotation",
|
|
||||||
"event_id": event_id,
|
|
||||||
"key": key,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def remove_reaction(client: AsyncClient, room_id: str, event_id: str, key: str) -> Any:
|
|
||||||
return await client.room_send(
|
|
||||||
room_id,
|
|
||||||
"m.reaction",
|
|
||||||
{
|
|
||||||
"m.relates_to": {
|
|
||||||
"rel_type": "m.annotation",
|
|
||||||
"event_id": event_id,
|
|
||||||
"key": key,
|
|
||||||
},
|
|
||||||
"undo": True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue