|
|
@@ -12,6 +12,7 @@ including both outbound messaging and realtime WebSocket connections.
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
+from builtins import ImportError
|
|
|
import random
|
|
|
|
|
|
from gateway.platforms.helpers import MessageDeduplicator
|
|
|
@@ -673,13 +674,14 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
except RuntimeError:
|
|
|
logger.warning("Chatto: chattolib event loop aborted - no client available")
|
|
|
return
|
|
|
-
|
|
|
- logger.info("Chatto WS: 'message_posted' event_payload:%s", payload)
|
|
|
+ logger.info("Chatto WS: 'message_posted' payload:%s", payload)
|
|
|
|
|
|
message = await payload.fetch_message(client=client)
|
|
|
if message is None or message.deleted_at:
|
|
|
return
|
|
|
message_body = message.body or ""
|
|
|
+ logger.info("message: %s", message)
|
|
|
+
|
|
|
attachments = list(message.attachments or [])
|
|
|
# A message carrying only an image/PDF has an empty body — dropping it
|
|
|
# here is what made attachments sent to Hermes disappear silently.
|
|
|
@@ -749,13 +751,13 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
await self.add_reaction(message.room_id, message.id, "🫥")
|
|
|
return
|
|
|
|
|
|
- # Thread anchoring — if the incoming message is inside a thread, we
|
|
|
+ # Thread anchoring — if the incoming message is inside a Chatto thread, we
|
|
|
# keep that thread by default; otherwise leave thread_id unset so
|
|
|
# replies land at the root.
|
|
|
- thread_id = payload.thread_root_event_id or None
|
|
|
- if not thread_id and room_kind != RoomKind.DM and self.chatto_config.auto_thread.value:
|
|
|
+ thread_id = payload.thread_root_event_id or None # we could also take "payload.room_id" but then, we're in a thread already.
|
|
|
+ if not thread_id and room_kind != RoomKind.DM:
|
|
|
thread_id = message.id
|
|
|
-
|
|
|
+
|
|
|
source = self.build_source(
|
|
|
chat_id=payload.room_id,
|
|
|
chat_name=self._room_names.get(message.room_id),
|
|
|
@@ -767,37 +769,33 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
role_authorized=True,
|
|
|
)
|
|
|
|
|
|
- msg_type = MessageType.COMMAND if (message_body.lstrip().startswith("/")) else MessageType.TEXT
|
|
|
-
|
|
|
- my_body = message_body.lstrip() if msg_type == MessageType.COMMAND else message_body
|
|
|
+ # prepare a MessageEvent
|
|
|
+ message_event = MessageEvent(
|
|
|
+ text=message_body,
|
|
|
+ source=source,
|
|
|
+ message_id=message.id,
|
|
|
+ timestamp=message.created_at or datetime.now(timezone.utc),
|
|
|
+ raw_message=message,
|
|
|
+ reply_to_message_id=message.in_reply_to,
|
|
|
+ )
|
|
|
+ if message_event.is_command():
|
|
|
+ message_event.message_type = MessageType.COMMAND
|
|
|
|
|
|
# Attachments — download and hand the local cache paths to the gateway,
|
|
|
# which runs vision enrichment / document extraction off media_urls.
|
|
|
- media_urls, media_types, media_kinds = await self._cache_attachments(
|
|
|
+ message_event.media_urls, message_event.media_types, media_kinds = await self._cache_attachments(
|
|
|
payload.room_id, attachments,
|
|
|
)
|
|
|
- if media_kinds and msg_type != MessageType.COMMAND:
|
|
|
+ if media_kinds:
|
|
|
# Same precedence as the Teams/Signal adapters: document-context
|
|
|
# injection gates strictly on DOCUMENT, image handling keys off the
|
|
|
# per-path image/* MIME regardless of message_type.
|
|
|
- msg_type = self._message_type_for_media_kinds(media_kinds)
|
|
|
+ message_event.message_type = self._message_type_for_media_kinds(media_kinds)
|
|
|
+ else:
|
|
|
+ message_event.message_type = MessageType.TEXT
|
|
|
|
|
|
- message_event = MessageEvent(
|
|
|
- text=my_body,
|
|
|
- source=source,
|
|
|
- message_id=message.id,
|
|
|
- message_type=msg_type,
|
|
|
- media_urls=media_urls,
|
|
|
- media_types=media_types,
|
|
|
- timestamp=message.created_at or datetime.now(timezone.utc),
|
|
|
- raw_message=message,
|
|
|
- reply_to_message_id=message.id,
|
|
|
- reply_to_text=message_body,
|
|
|
- reply_to_author_id=user.id,
|
|
|
- reply_to_author_name=user.login,
|
|
|
- )
|
|
|
- logger.info("Chatto: Dispatching MessageEvent to Hermes: %s", message_event)
|
|
|
|
|
|
+ logger.info("Chatto: Dispatching MessageEvent to Hermes: %s", message_event)
|
|
|
await self.handle_message(message_event)
|
|
|
return
|
|
|
|
|
|
@@ -1934,9 +1932,9 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
async def hermes_standalone_sender_fn(
|
|
|
- pconfig,
|
|
|
- chat_id,
|
|
|
- message,
|
|
|
+ pconfig: PlatformConfig,
|
|
|
+ chat_id: str,
|
|
|
+ message: str,
|
|
|
*,
|
|
|
thread_id=None,
|
|
|
media_files=None,
|
|
|
@@ -1974,14 +1972,14 @@ async def hermes_standalone_sender_fn(
|
|
|
try:
|
|
|
kwargs: Dict[str, Any] = {}
|
|
|
if chatto_config.auto_thread.value and thread_id:
|
|
|
- kwargs["in_reply_to"] = thread_id
|
|
|
+ kwargs["thread_root_event_id"] = thread_id
|
|
|
if media_files and media_files.get("attachment_asset_ids"):
|
|
|
kwargs["attachment_asset_ids"] = list(media_files["attachment_asset_ids"])
|
|
|
try:
|
|
|
posted = await client.post_message(chat_id, message, **kwargs)
|
|
|
except Exception as exc:
|
|
|
return SendResult(success=False, error=str(exc))
|
|
|
- return SendResult(success=True, message_id=getattr(posted, "id", "") or None)
|
|
|
+ return SendResult(success=True, message_id=posted.id, raw_response=posted)
|
|
|
finally:
|
|
|
try:
|
|
|
await client.close()
|
|
|
@@ -2019,7 +2017,7 @@ def hermes_check_fn() -> bool:
|
|
|
"""Check if Chatto is configured and dependencies are available.
|
|
|
Add real logic?! Or just .. there are no dependencies.. always return true. Really. Docs suck."""
|
|
|
try:
|
|
|
- import chattolib.client # noqa: F401 — vendored dependency probe
|
|
|
+ from .vendor.common.chattolib import client # noqa: F401 — vendored dependency probe
|
|
|
return True
|
|
|
except ImportError:
|
|
|
return False
|
|
|
@@ -2100,7 +2098,7 @@ def hermes_env_enablement_fn() -> Optional[dict]:
|
|
|
# Seed keys must be the config.yaml "extra" keys (config_key), NOT the env
|
|
|
# var names — ChattoConfiguration reads extra[config_key].
|
|
|
seed: Dict[str, Any] = {
|
|
|
- ChattoConfiguration.base_url.config_key: (
|
|
|
+ ChattoConfiguration.base_url.field_name: (
|
|
|
os.getenv(ChattoConfiguration.base_url.env_name) or ChattoClient.DEFAULT_BASE_URL
|
|
|
).strip(),
|
|
|
}
|