From 324f3102639d75eb116a11001cd24017c33e31dc Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:24:53 -0700 Subject: [PATCH] refactor(photon): reuse the /send-poll primitive from #43665 for poll clarify Follow-up to the #48194 pick: it was written before #43665 landed and re-added its own /send-poll sidecar route and poll import. Collapse the duplicates: - keep #43665's /send-poll route (>=2 trimmed string options) as the single sidecar implementation; drop #48194's variant - drop the duplicated poll import in the sidecar destructure - make adapter.send_poll() a thin wrapper over _sidecar_send_poll(), the one /send-poll client (shared with the poll-backed clarify path), and align its validation to the sidecar's >=2-options contract --- plugins/platforms/photon/adapter.py | 26 +++++++------------- plugins/platforms/photon/sidecar/index.mjs | 28 ++-------------------- 2 files changed, 11 insertions(+), 43 deletions(-) diff --git a/plugins/platforms/photon/adapter.py b/plugins/platforms/photon/adapter.py index 1f19c017a424..b8539c6a41a7 100644 --- a/plugins/platforms/photon/adapter.py +++ b/plugins/platforms/photon/adapter.py @@ -1631,21 +1631,13 @@ class PhotonAdapter(BasePlatformAdapter): options: list[str], metadata: Optional[Dict[str, Any]] = None, ) -> SendResult: - """Send a native iMessage poll through Photon Spectrum.""" - choices = [str(option).strip() for option in options if str(option).strip()] - if len(choices) < 2: - return SendResult( - success=False, - error="Photon polls require at least two non-empty options", - ) - try: - data = await self._sidecar_call( - "/send-poll", - {"spaceId": chat_id, "title": title.strip(), "options": choices}, - ) - except Exception as e: - return SendResult(success=False, error=str(e)) - return SendResult(success=True, message_id=data.get("messageId")) + """Send a native iMessage poll through Photon Spectrum. + + Thin public wrapper over :meth:`_sidecar_send_poll`, the single + implementation of the sidecar's ``/send-poll`` primitive (also used + by the poll-backed clarify path). + """ + return await self._sidecar_send_poll(chat_id, title, list(options or [])) async def send_effect( self, @@ -2022,8 +2014,8 @@ class PhotonAdapter(BasePlatformAdapter): opts = [str(o).strip() for o in (options or []) if str(o).strip()] if not title or not title.strip(): return SendResult(success=False, error="poll title is required") - if not opts: - return SendResult(success=False, error="poll needs at least one option") + if len(opts) < 2: + return SendResult(success=False, error="poll needs at least two options") body: Dict[str, Any] = { "spaceId": space_id, "title": title.strip()[: self.MAX_MESSAGE_LENGTH], diff --git a/plugins/platforms/photon/sidecar/index.mjs b/plugins/platforms/photon/sidecar/index.mjs index 35b7bb6522cd..7491bf214be2 100644 --- a/plugins/platforms/photon/sidecar/index.mjs +++ b/plugins/platforms/photon/sidecar/index.mjs @@ -33,14 +33,12 @@ // "reactionId": "..." | null (restart-recovery fallback)} // - POST /send-poll -> {"ok": true, "messageId": "..."} // body: {"spaceId": "...", "title": "...", "options": ["...", "..."]} +// Sends a native poll (orange iMessage poll bubble). A tap streams +// back inbound as a `poll_option` event ({title, selected}). // - POST /send-effect -> {"ok": true, "messageId": "..."} // body: {"spaceId": "...", "text": "...", "effect": "confetti" | ...} // - POST /typing -> {"ok": true} // body: {"spaceId": "...", "state": "start" | "stop"} -// - POST /send-poll -> {"ok": true, "messageId": "..."} -// body: {"spaceId": "...", "title": "...", "options": ["A", "B", ...]} -// Sends a native poll (orange iMessage poll bubble). A tap streams -// back inbound as a `poll_option` event ({title, selected}). // - POST /shutdown -> {"ok": true}; then process exits // // On SIGINT/SIGTERM the sidecar calls `app.stop()` (3s graceful) before @@ -257,7 +255,6 @@ try { text: spectrumText, markdown: spectrumMarkdown, typing: spectrumTyping, - poll: spectrumPoll, } = await import("spectrum-ts")); ({ imessage, effect: imessageEffect } = await import("spectrum-ts/providers/imessage")); } catch (e) { @@ -858,27 +855,6 @@ const server = http.createServer(async (req, res) => { } return ok(res, { messageId: result?.id || null }); } - if (req.url === "/send-poll") { - const { spaceId, title, options } = body || {}; - if (!spaceId || typeof title !== "string" || !title) { - return badRequest(res, "spaceId and title are required"); - } - if (!Array.isArray(options) || options.length < 1) { - return badRequest(res, "options must be a non-empty array"); - } - // spectrum-ts' poll() builder accepts string options; it degrades to a - // numbered text list on platforms without native polls (so the gateway - // text-intercept still resolves the clarify there). - const opts = options - .map((o) => (typeof o === "string" ? o : o?.title)) - .filter((o) => typeof o === "string" && o); - if (!opts.length) { - return badRequest(res, "options must contain at least one string"); - } - const space = await resolveSpace(spaceId); - const result = await space.send(spectrumPoll(title, ...opts)); - return ok(res, { messageId: result?.id || null }); - } if (req.url === "/react") { const { spaceId, messageId, emoji } = body || {}; if (!spaceId || !messageId || typeof emoji !== "string" || !emoji) {