Skip to content

feat(nextcloud-talk): reactions, ack emoji, typing indicators, mention parsing#2

Open
iclem wants to merge 11 commits intomainfrom
feat/nextcloud-talk
Open

feat(nextcloud-talk): reactions, ack emoji, typing indicators, mention parsing#2
iclem wants to merge 11 commits intomainfrom
feat/nextcloud-talk

Conversation

@iclem
Copy link
Copy Markdown
Owner

@iclem iclem commented Apr 23, 2026

Summary

  • Parse structured Nextcloud Talk message bodies (JSON with {mentionN} placeholders) and extract explicit mention entries for reliable mention detection
  • Inbound reaction dispatch: Like/Dislike webhooks are decoded, access-checked, and forwarded to the agent as [Reaction] messages
  • Auto-react to acknowledged messages via resolveAckReaction (channel/account/global hierarchy + room-level override)
  • Typing indicator support via POST /bot/{token}/typing, opt-in per account or room
  • All outbound paths (message, reaction, typing) now go through fetchWithSsrFGuard + ssrfPolicyFromPrivateNetworkOptIn

Fixes included

  • P1: Typing indicator now routes through the SSRF guard (send-typing.ts)
  • P2: Reaction webhooks now pass through the replay guard (monitor-runtime.ts)
  • P2: handleNextcloudTalkInboundReaction honors dmPolicy: open for direct-room reactions
  • P2: Room-level ackReaction config is now consulted before the channel/account hierarchy

Test plan

  • pnpm test extensions/nextcloud-talk — 17 files, all pass
  • pnpm tsgo:prod — no errors
  • Manual: mention bot in a Nextcloud Talk group room → bot reacts with ack emoji and replies
  • Manual: react to a message → reaction forwarded to agent session

🤖 Generated with Claude Code

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b8a08c3606

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const result = await processNextcloudTalkReplayGuardedMessage({
replayGuard,
accountId: account.accountId,
message: reaction,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Namespace replay keys for reaction webhooks

This passes reactions through processNextcloudTalkReplayGuardedMessage using the same roomToken + messageId key shape used for normal message webhooks, so reactions collide with already-processed Create events and with other reactions on the same message. In practice, once a message webhook is committed, later Like/Dislike events for that message are treated as duplicates and dropped, which breaks reaction forwarding for common cases.

Useful? React with 👍 / 👎.

Comment on lines +104 to +105
kind: "group",
id: roomToken,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Route DM reactions with direct peer identity

The reaction handler always resolves routes as peer.kind: "group" keyed by roomToken, but DM inbound messages are routed as direct peers keyed by sender id in handleNextcloudTalkInbound. That mismatch means direct-room reactions are written to a different route/session than the originating DM conversation, so per-peer DM routing and session continuity can break (including reactions being handled by a different agent binding).

Useful? React with 👍 / 👎.

iclem and others added 11 commits April 23, 2026 22:31
- Export parseStructuredNextcloudTalkBody and resolveExplicitNextcloudTalkMention
- Rename mentionDebug -> mentionEntries and drop unused explicitMention field from
  the parsed body struct
- Remove verbose per-message debug log line from the hot path
- Add inbound.mentions.test.ts with 16 unit tests covering plain text pass-through,
  structured JSON extraction, malformed JSON fallback, and mention matching (id,
  mentionId, name, email local-part, case-insensitive, type guard)
…ge body

Nextcloud Talk's structured payload puts mention tokens like {mention0} or
{mention-user1} directly in the message string. Leaving them in effectiveBody
caused two problems:
- Command parsing (hasControlCommand, directives) would not see commands that
  follow an @-mention, e.g. "{mention0} /reset" -> "/reset" was never reached.
- A mention-only message ("{mention0}") produced a non-empty prompt dispatched
  to the agent, causing spurious replies.

Fix: strip all {key} placeholder tokens whose keys appear in parameters[], then
treat a structured body that is empty after stripping as a mention-only ping and
return early without dispatching.

Also add ParsedNextcloudTalkBody.structured flag to distinguish plain-text
fall-through from a successfully parsed JSON body.

Tests: 2 new cases (mention-only drop, command after mention), updated existing
assertions to reflect stripped text. 18 unit tests, 68 total, all passing.
- Outbound: wire sendReactionNextcloudTalk into a ChannelMessageActionAdapter
  (nextcloudTalkMessageActions) exposing the 'react' action on the message tool
- Inbound: extend webhook decoder to parse Like/Dislike events (Talk 21+) into
  NextcloudTalkInboundReaction; route through onReaction callback in
  monitorNextcloudTalkProvider; add handleNextcloudTalkInboundReaction stub
  (TODO: full dispatch on a future pass)
- Types: extend NextcloudTalkWebhookPayload with Like/Dislike types and
  optional content field; add NextcloudTalkInboundReaction type
- SDK: export jsonResult, readReactionParams, readStringParam from
  plugin-sdk/nextcloud-talk surface
- Tests: channel-actions.test.ts (2 tests), monitor.reaction.test.ts (3 tests)
  — all 73 nextcloud-talk tests pass
Adds an optional `ackReaction` config field (account- and room-level).
When set, the bot sends a reaction emoji to every received message that
passes all gate checks (best-effort; errors are logged, not thrown).
Room-level `ackReaction` takes precedence over account-level.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements typing indicator signaling so the bot shows a 'composing'
status while processing a message.

- Add NextcloudTalkTypingIndicator type and typingIndicator config option
  to NextcloudTalkRoomConfig and NextcloudTalkAccountConfig
- Add send-typing.ts: sendTypingNextcloudTalk() using the proposed bot
  typing endpoint POST /ocs/v2.php/apps/spreed/api/v1/bot/{token}/typing
  with HMAC-SHA256 auth (same pattern as sendMessageNextcloudTalk)
- Gracefully handles 404 (endpoint not yet available on server) with a
  one-time console.warn and continues without throwing
- Add resolveTypingIndicatorEnabled() for room-level override of
  account-level setting (room config takes precedence)
- Integrate into inbound.ts: typing=true before dispatch, typing=false
  after delivery or on dispatch error (fire-and-forget, non-blocking)
- Add config-schema.ts entries for typingIndicator on both room and
  account schemas
- Add 16 unit tests covering valid/error paths and config resolution

Feature is opt-in: typingIndicator defaults to false. Enable per-account
or override per-room with typingIndicator: true/false.
@iclem iclem force-pushed the feat/nextcloud-talk branch from b8a08c3 to aa2a646 Compare April 23, 2026 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant