124 lines
6.9 KiB
Markdown
124 lines
6.9 KiB
Markdown
# PR#91: An Agent Marks a Matrix Message with a Reaction
|
|
|
|
Version: minor
|
|
|
|
> **WARNING:** This document describes only the change applied in this PR.
|
|
> It may already be outdated once the next PR is merged.
|
|
> Historic PR documentation is not maintained along with new PRs; treat it as a snapshot.
|
|
|
|
## Problem
|
|
|
|
The user announces pull requests of hs.hsadmin.ng for review in the Matrix room "HSAdmin NG".
|
|
An agent merges the approved ones and must then mark each announcement:
|
|
✅️ when the pull request is approved, ☑️ in addition when it is merged.
|
|
The room is end-to-end encrypted; werkregie has a verified device of the user's account, but only reads.
|
|
|
|
## Non-Goals
|
|
|
|
- No messages, replies or edits; only a reaction (`m.reaction`, `rel_type: m.annotation`).
|
|
- No removal of a reaction.
|
|
- No reaction to messages of other users.
|
|
- No HTTP endpoint for it; the command line only.
|
|
- No reaction through the running relay; see "Follow-up PRs".
|
|
|
|
## Specification
|
|
|
|
### Feature: `werkregie-dashboard matrix react` marks the newest own message that contains a text
|
|
|
|
#### Scenario#91.01: an announcement gets the reaction
|
|
|
|
… so that the room shows which pull request is approved.
|
|
|
|
- **Given** the user's newest own message in the room "HSAdmin NG" that contains `/pulls/329`, without a reaction ✅️ of the user
|
|
- **When** an agent runs `werkregie-dashboard matrix react --room "HSAdmin NG" --contains /pulls/329 ✅️`
|
|
- **Then** the message has the reaction ✅️ of the user, and the command prints `REACTED` with the event IDs
|
|
|
|
##### Verified by
|
|
|
|
- [MatrixReactionsTest "the target is the newest own message that contains the text"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactionsTest.kt)
|
|
- [MatrixReactorTest "a message gives its text, an edit none"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactorTest.kt)
|
|
- [MatrixReactorTest "an encrypted event gives what its cleartext shows, a decrypted one its content"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactorTest.kt)
|
|
- [MainTest "matrix react takes room, text and key, in any order of the options"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/MainTest.kt)
|
|
|
|
#### Scenario#91.02: a reaction that is there already stays single
|
|
|
|
… so that an agent can repeat the command after a failure.
|
|
|
|
- **Given** the message has a reaction ✅️ of the user, with or without the variation selector U+FE0F
|
|
- **When** the agent runs the same command again
|
|
- **Then** nothing is sent, and the command prints `PRESENT`
|
|
|
|
##### Verified by
|
|
|
|
- [MatrixReactionsTest "an own reaction with the key, with or without U+FE0F, is present"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactionsTest.kt)
|
|
- [MatrixReactionsTest "a reaction of another user, with another key or to another message is not present"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactionsTest.kt)
|
|
- [MatrixReactorTest "a reaction gives the message it annotates and its key"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactorTest.kt)
|
|
- [MatrixReactorTest "a reaction in the outbox counts as the user's, by its event ID once it is sent"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactorTest.kt)
|
|
|
|
#### Scenario#91.03: no matching message
|
|
|
|
… so that a wrong text does not mark another message.
|
|
|
|
- **Given** no own message among the last 500 events of the room contains the text, e.g. only `/pulls/3290` for `/pulls/329`
|
|
- **When** the agent runs the command
|
|
- **Then** nothing is sent, and the command prints `NO-MESSAGE` and ends with exit code 1
|
|
|
|
##### Verified by
|
|
|
|
- [MatrixReactionsTest "without an own message that contains the text there is no target"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactionsTest.kt)
|
|
- [MatrixReactionsTest "each outcome prints one line, and only a missing message ends with exit code 1"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactionsTest.kt)
|
|
|
|
#### Scenario#91.04: no reaction while the relay syncs
|
|
|
|
… so that two clients never use the device's store and keys at once.
|
|
|
|
- **Given** the relay of the machine holds `sync.lock` and runs the Matrix client
|
|
- **When** the agent runs the command
|
|
- **Then** nothing is sent, and the command says to stop the relay first and ends with exit code 3
|
|
|
|
##### Verified by
|
|
|
|
- [MatrixReactorTest "while another process syncs, the command refuses and says how to stop the relay"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MatrixReactorTest.kt)
|
|
|
|
## Solution
|
|
|
|
- `MatrixReactions` decides, free of the client library: the room by its ID or name,
|
|
the newest own message whose text contains `--contains`, not followed by a letter or digit,
|
|
and whether an own reaction with the key is there already; U+FE0F does not count, since clients differ in it.
|
|
- `MatrixReactor` takes `sync.lock`, opens the device's client, waits for the first sync,
|
|
reads the last 500 events of the room backwards, decrypted where the device has the key, and sends `m.reaction`.
|
|
Trixnity encrypts it in an encrypted room.
|
|
A reaction of an earlier run that still waits in the client's outbox counts as present, so it is not sent twice.
|
|
- Exit codes: 0 for `REACTED` and `PRESENT`, 1 for `NO-MESSAGE`, 2 for a wrong call, 3 for a failure, as `watch-pr`.
|
|
- No token or key is printed: the command prints event IDs only, an error its message.
|
|
|
|
Why the command takes `sync.lock` rather than going through the running relay:
|
|
the relay holds the lock as long as it runs, and only `MatrixSource` has its client.
|
|
A control socket in `MatrixSource` would make `pitestPartial` mutate all of its methods;
|
|
`MatrixSource.message` alone has a Mutation-CRAP of 812, far above the limit 15 of a branch.
|
|
So `MatrixSource` needs its refactoring first, in a PR of its own.
|
|
Until then the agent stops the relay for the reactions and starts it again;
|
|
the dashboard then misses the relay's report for that time and says so.
|
|
A relay takes the lock only with its first fetch, a few seconds after its start;
|
|
until then `matrix react` gets the lock, too.
|
|
|
|
Checked live in the user's test room, with the relay stopped:
|
|
`REACTED`, then `PRESENT` for the same call; `NO-MESSAGE` with exit code 1 for a room found by its name.
|
|
A call takes about 50 s, mostly the first sync since the relay last ran.
|
|
|
|
## Release Notes
|
|
|
|
- `werkregie-dashboard matrix react --room RAUM --contains TEXT EMOJI` marks the newest own message with a reaction;
|
|
a running relay or local mode stops for it.
|
|
|
|
## Open Questions
|
|
|
|
None.
|
|
|
|
## Follow-up PRs
|
|
|
|
1. A refactoring of `MatrixSource`: each of its methods at a Mutation-CRAP of at most 15,
|
|
the limit of `pitestPartial` for the classes a branch changes.
|
|
2. Then the relay that holds `sync.lock` sends the reaction for `matrix react` through a control socket;
|
|
the relay no longer stops for it.
|