Skip to content

feat(slack): mirror support thread messages into the Slack thread - #7

Draft
crstian19 wants to merge 1 commit into
fix/slack-webhook-validationfrom
feat/slack-thread-mirroring
Draft

crstian19 wants to merge 1 commit into
fix/slack-webhook-validationfrom
feat/slack-thread-mirroring

Conversation

@crstian19

Copy link
Copy Markdown
Contributor

Apilado sobre #6. Al mergear aquel, la base pasa sola a main.

Qué hace

Cada mensaje humano de un hilo vigilado se replica como respuesta bajo el anuncio en Slack, de modo que la conversación se sigue entera desde ahí.

Por qué hace falta el bot token

Las respuestas en hilo se direccionan con el ts del mensaje padre, y un Incoming Webhook responde ok sin devolverlo nunca. La doc de Slack lo dice explícitamente:

"Since the initial webhook request does not return the message timestamp, you must retrieve it separately using methods like the Events API […] or the conversations.history method."

chat.postMessage lo devuelve directamente, así que la migración no es preferencia sino requisito.

Piezas

  • SlackApiClientchat.postMessage con bot token. Lee el ok/error dentro del cuerpo 200 en lugar de fiarse del status (la Web API reporta los fallos ahí), no reintenta errores permanentes como not_in_channel, y serializa los envíos con ~1 s de separación para respetar el límite por canal.
  • bot/thread_map.py — persiste discord_thread_id → slack_ts en SQLite dentro del volumen de Docker, para que un redespliegue no deje huérfanos los hilos vivos. Purga a los 30 días.
  • El webhook sigue como respaldo — sin bot token el bot continúa avisando de hilos nuevos, sólo que sin espejo. Desplegar esto sin el token no es una regresión.

Qué no se replica

Los mensajes de bots (las respuestas del LLM son largas; Slack lleva la conversación humana) y el mensaje inicial de un post de foro, que ya es el cuerpo del anuncio.

Verificación

  • 90 tests (23 nuevos): persistencia del mapeo tras reabrir la BD, purga por antigüedad, thread_ts en las respuestas, errores dentro de un 200, reintento sólo en ratelimited, escapado, adjuntos, y los cuatro casos que se descartan.
  • End-to-end dentro del contenedor contra un Slack simulado: anuncio → ts guardado → respuesta colgada del hilo, con el mensaje del bot y el inicial correctamente ignorados.
  • ruff check y ruff format --check limpios.

Configuración nueva

SLACK_BOT_TOKEN=xoxb-…      # scope chat:write, app invitada al canal (/invite @app)
SLACK_CHANNEL_ID=C…         # el destino ya no viene dentro de la URL

🤖 Generated with Claude Code

Every human message posted in a tracked Discord thread is echoed as a reply
under the Slack announcement, so the conversation can be followed from Slack.

This needs the Web API, not the webhook. Threaded replies are addressed by
the parent message's ts, and an Incoming Webhook answers "ok" without ever
returning one — Slack's own docs point to the Events API or
conversations.history to recover it. chat.postMessage returns it directly.

- SlackApiClient posts through chat.postMessage with a bot token, reads the
  ok/error field inside the 200 body rather than trusting the status, and
  serialises posts ~1s apart to respect the per-channel rate limit.
- SlackThreadMap persists discord_thread_id -> slack_ts in SQLite inside the
  Docker volume, so a redeploy does not orphan live threads. Purged at 30d.
- The webhook stays as a fallback: with no bot token the bot still announces
  threads, just without mirroring, so a deploy without the token is not a
  regression.

Skipped when mirroring: bots (the bot's own LLM answers are long, and Slack
should carry the human conversation) and a forum post's opening message,
which is already the body of the announcement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@crstian19
crstian19 marked this pull request as draft August 20, 2026 13:40
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.

2 participants