|
@@ -72,7 +72,8 @@ try:
|
|
|
stream_events
|
|
stream_events
|
|
|
)
|
|
)
|
|
|
from chattolib.realtime_types import (
|
|
from chattolib.realtime_types import (
|
|
|
- MessagePostedPayload
|
|
|
|
|
|
|
+ MessagePostedPayload,
|
|
|
|
|
+ ReactionPayload,
|
|
|
)
|
|
)
|
|
|
from chattolib.types import (
|
|
from chattolib.types import (
|
|
|
PresenceStatus, RoomKind, User
|
|
PresenceStatus, RoomKind, User
|
|
@@ -614,6 +615,47 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
await self.handle_message(message_event)
|
|
await self.handle_message(message_event)
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
|
|
+ async def _forward_reaction(
|
|
|
|
|
+ self, event: RealtimeEvent, payload: ReactionPayload, *, removed: bool,
|
|
|
|
|
+ ) -> None:
|
|
|
|
|
+ """Forward a human reaction to the gateway's reaction hook surface.
|
|
|
|
|
+
|
|
|
|
|
+ The handler is registered by the gateway via ``set_reaction_handler``
|
|
|
|
|
+ and fans out as ``reaction:added`` / ``reaction:removed`` through the
|
|
|
|
|
+ HookRegistry. The dict shape mirrors the Slack adapter's — hook
|
|
|
|
|
+ consumers are written against that contract, not against a per-platform
|
|
|
|
|
+ one. Our own lifecycle reactions (👀/✅/❌) are dropped: forwarding them
|
|
|
|
|
+ would feed the agent its own markers.
|
|
|
|
|
+ """
|
|
|
|
|
+ actor_id = event.actor_id
|
|
|
|
|
+ if actor_id and self.me and actor_id == self.me.id:
|
|
|
|
|
+ return
|
|
|
|
|
+ if not payload.room_id or not payload.message_event_id or not actor_id:
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ handler = getattr(self, "_reaction_handler", None)
|
|
|
|
|
+ if handler is None:
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ action = "removed" if removed else "added"
|
|
|
|
|
+ try:
|
|
|
|
|
+ await handler(
|
|
|
|
|
+ {
|
|
|
|
|
+ "platform": ChattoConstants.PLATFORM_NAME,
|
|
|
|
|
+ "event_name": f"reaction:{action}",
|
|
|
|
|
+ "reaction": payload.emoji,
|
|
|
|
|
+ "user_id": actor_id,
|
|
|
|
|
+ "item_user_id": None,
|
|
|
|
|
+ "item_type": "message",
|
|
|
|
|
+ "channel_id": payload.room_id,
|
|
|
|
|
+ "message_ts": payload.message_event_id,
|
|
|
|
|
+ "event_ts": event.id,
|
|
|
|
|
+ "raw_event": event,
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ except Exception: # pragma: no cover - the hook contract is non-blocking
|
|
|
|
|
+ logger.debug("Chatto: reaction hook forwarding failed", exc_info=True)
|
|
|
|
|
+
|
|
|
async def _handle_realtime_event(self, event: RealtimeEvent) -> None:
|
|
async def _handle_realtime_event(self, event: RealtimeEvent) -> None:
|
|
|
if self._is_seen(event.id):
|
|
if self._is_seen(event.id):
|
|
|
return
|
|
return
|
|
@@ -633,10 +675,19 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
|
|
|
|
|
await self._dispatch_message_posted(event_payload)
|
|
await self._dispatch_message_posted(event_payload)
|
|
|
|
|
|
|
|
|
|
+ elif event.kind in ("reaction_added", "reaction_removed"):
|
|
|
|
|
+ reaction_payload = event.get(event.kind)
|
|
|
|
|
+ if reaction_payload is not None:
|
|
|
|
|
+ await self._forward_reaction(
|
|
|
|
|
+ event,
|
|
|
|
|
+ cast(ReactionPayload, reaction_payload),
|
|
|
|
|
+ removed=event.kind == "reaction_removed",
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
# confirmed:
|
|
# confirmed:
|
|
|
elif event.kind in ("mention_notification", "projection_event", "presence_changed", "notification_dismissed",
|
|
elif event.kind in ("mention_notification", "projection_event", "presence_changed", "notification_dismissed",
|
|
|
"room_marked_as_read", "user_typing", "notification_created", "new_direct_message_notification",
|
|
"room_marked_as_read", "user_typing", "notification_created", "new_direct_message_notification",
|
|
|
- 'reaction_removed', 'reaction_added'):
|
|
|
|
|
|
|
+ "message_edited", "message_retracted"):
|
|
|
logger.debug("Chatto: '%s' event received. Not yet implemented or not needed.", event.kind)
|
|
logger.debug("Chatto: '%s' event received. Not yet implemented or not needed.", event.kind)
|
|
|
else:
|
|
else:
|
|
|
logger.error("Chatto: unknown event kind: '%s'", event.kind)
|
|
logger.error("Chatto: unknown event kind: '%s'", event.kind)
|