Skip to content

Extract reactions, and stop dropping message relations - #20

Merged
TroyHernandez merged 1 commit into
mainfrom
feat/reaction-extractor
Aug 5, 2026
Merged

TroyHernandez merged 1 commit into
mainfrom
feat/reaction-extractor

Conversation

@TroyHernandez

Copy link
Copy Markdown
Contributor

Two gaps in the same timeline walk. Both block chat_react() and chat_poll()$reactions in chat.api (phase 1b).

mx_extract_reactions()

Returns every m.reaction in a sync as (room_id, event_id, sender, is_self, target_event_id, key, ts).

The reaction's own event id and the id it annotates are separate fields. Conflating them is the obvious mistake here and the one a caller cannot detect afterwards — both are event ids and both are plausible.

Self reactions are kept and tagged, the same way mx_extract_text_events() keeps the client's own messages: a consumer that wants only other people's filters on the flag, and one tracking which reactions it has already placed needs them.

This is not mx_extract_reaction_verdict(). That one answers a single approve/deny question about a single event, with the key semantics baked in — which keys mean yes, which mean no. Those belong to whoever is asking, not here. It stays for callers who want it; this reports keys and leaves their meaning alone.

Only additions are reported. Removing a reaction is an m.room.redaction of the m.reaction event, which is not an m.reaction and so does not appear. A consumer that has to notice un-reactions reads redactions itself. Documented rather than left to be found out.

An m.reaction with no annotation relation, no target, or no key is not a reaction to anything and is skipped. rel_type is checked rather than assumed.

relates_to on text events

mx_extract_text_events() dropped content$m.relates_to, which is what distinguishes a threaded reply (rel_type of m.thread) from a rich reply (an m.in_reply_to with no rel_type) from an edit (m.replace). Without it every caller sees all four as an ordinary message — which is why chat.api reports thread_replies = FALSE.

Passed through verbatim rather than normalized: which of those a caller cares about is its own business, and a normalized shape would have to pick.

Bundled with the reactions change because it is the same walk over the same events, and because the alternative is a second trip through mx.client → chat.api → drat → corteza for three lines.

Testing

183 assertions. Three mutations run, all caught: dropping the relation, accepting any rel_type, and reporting the target id as the reaction's own id.

R CMD check --as-cran clean but for the days-since-last-update NOTE.

Downstream

chat.api 1b consumes this: chat_react() plus a chat_reaction() record on chat_poll(). Nothing depends on it yet, so this merges on its own.

Two gaps in the same walk, both blocking chat.api's reaction verb.

mx_extract_reactions() reports every m.reaction in a sync:
(room_id, event_id, sender, is_self, target_event_id, key, ts). The
event's own id and the id it annotates are separate fields, since
conflating them is the obvious mistake and the one a caller cannot detect
afterwards.

mx_extract_reaction_verdict() already existed and is not this. It answers
one approve/deny question about one event with the key semantics baked
in -- which keys mean yes, which mean no -- and those belong to whoever
is asking. It stays for callers who want that; this one reports keys and
leaves their meaning alone.

Only additions are reported. Removing a reaction is an m.room.redaction
of the m.reaction event, which is not an m.reaction, so a consumer that
has to notice un-reactions reads redactions itself. Documented rather
than left to be discovered.

And mx_extract_text_events() now passes m.relates_to through. That field
is what tells a threaded reply (rel_type m.thread) from a rich reply (an
m.in_reply_to with no rel_type) from an edit (m.replace), and dropping it
left every caller unable to tell any of them from an ordinary message --
which is why chat.api reports thread_replies = FALSE. Passed verbatim
rather than interpreted: which of those a caller cares about is its own
business.

Bundled because it is the same walk over the same events, and because
the alternative is a second trip through mx.client -> chat.api -> drat ->
corteza for three lines.

183 assertions, three mutations, all caught.
@TroyHernandez
TroyHernandez merged commit 3d8e1e7 into main Aug 5, 2026
2 checks passed
@TroyHernandez
TroyHernandez deleted the feat/reaction-extractor branch August 5, 2026 23:25
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