Repository navigation
Conversation
Emoji reactions are a Symphony Messaging feature that users rely on, but they are absent from the published Agent and Pod API specifications, so no generated client covers them and bots have had no way to participate. ReactionService calls the same pod endpoint the Symphony clients use, POST and GET on /maestro/reactions/v1/message, and is reachable as bdk.reactions(). It follows the precedent of MultiAttachmentsMessagesApi, which is hand written for an endpoint the generated code does not reach. Three behaviours the endpoint enforces, each confirmed against a pod: - Message ids must be padded standard base64. Ids are handed out URL-safe and unpadded elsewhere, so to_padded_base64 converts them and rejects anything that is not base64. - The emoji must be the character itself. Shortnames and empty strings come back as REACTIONS_INVALID_EMOJI, so an empty one is refused before the request is made. - Clearing a reaction omits the emoji field rather than sending an empty one, since an empty string is rejected and reacting twice with the same emoji is idempotent rather than a toggle. Reactions can be disabled per pod, and restricted in external or federated rooms, in which case these calls surface the pod's own error.
thibauult
left a comment
There was a problem hiding this comment.
Thanks for the contribution, @timkpaine, and for the thorough write-up of the wire format and the endpoint's behaviors. That's very helpful context.
Unfortunately, we can't accept this PR as it stands. /maestro/reactions/v1/message is an internal endpoint, not part of the public Symphony REST API (https://rest-api.symphony.com/). The BDKs only support the public API, because that's what we can maintain with backward-compatibility guarantees. Internal endpoints can change or disappear in any pod release without notice, and we'd have no way to protect BDK users from that.
We agree that reactions are a useful capability for bots. We'll raise it internally with the Symphony Messaging team to get these endpoints documented and exposed in the public API. We'll also follow up on finos/symphony-api-spec#232. We can't commit to a timeline.
Once the endpoints are published, we'd be glad to revisit this. At that point the service should be a thin wrapper over the client generated from the updated spec. The react/unreact/list_reactions interface and the id and emoji validation would carry over well.
In the meantime, I'd suggest keeping this as a draft, or closing it and reopening once the public endpoint exists. If you'd like to keep using it in the interim, it can live in your own fork or as a separate package, understanding it falls outside the BDK's compatibility guarantees.
Thanks again for the effort and for the detailed investigation.
What this adds
Emoji reactions on messages, as
bdk.reactions():Opened as a draft, because the endpoint it calls is not in the published API specification and maintainers may want a different shape, or may prefer to publish the endpoint first. Happy to rework or close it.
Why it is not generated
Reactions are a Symphony Messaging feature that users rely on, but neither
agent-api-public.yamlnorpod-api-public.yamlin finos/symphony-api-spec mentions them, so no generated client covers them and a bot currently has no way to participate.The capability exists server side. The Symphony web client reacts through
POST /maestro/reactions/v1/messageon the pod host, and that endpoint accepts a bot'ssessionTokenas-is. This service calls it directly, following the precedent ofMultiAttachmentsMessagesApi, which is hand written for an endpoint the generated code does not reach.I have also filed a request for a documented endpoint: finos/symphony-api-spec#232. If that lands, the right move is to point this service at the published route and drop the note below.
Wire format
Established by observing the web client and then confirming each behaviour against a pod with a bot session token:
Three behaviours the endpoint enforces, each handled here:
to_padded_base64converts them, and rejects anything that is not base64 rather than sending it.thumbsup, or an empty string, returns400 REACTIONS_INVALID_EMOJI, so an empty one is refused before the request is made.emojifield. An empty string is rejected, and reacting again with the same emoji is idempotent rather than a toggle, so omitting the field is what clears it.list_reactionstakes the id of the original message, which is what reactions attach to for an edited message, and returns{}rather than raising when there are none.Reactions can be disabled for a pod and restricted in external or federated rooms — the client gates on
isReactionsReadEnabled,isReactionsWriteEnabled, andisReactionsWriteExternalEnabled. This service does not try to pre-empt that; a pod that forbids the operation surfaces its own error.Conventions and testing
ReactionServiceis wired throughservice_factoryalongside the other services and reachable asbdk.reactions(). Nothing existing is modified beyond those two wiring points.Typing stays on
Optional/Dict/Listfor thepy39target.ruff format --check .andruff check .are clean repo-wide. Eight unit tests cover the id conversion, the empty-emoji refusal, thatunreactomits the field, the list mapping, and an empty response.Full suite: 567 passed. The two failures in
tests/core/config/bdk_config_loader_test.py::test_load_from_symphony_directoryreproduce on an unmodified checkout in my environment and are unrelated to this change.