feat(slack): announce new #support threads in Slack - #5
Merged
Merged
Conversation
Ruff 0.16 formats Python inside markdown code blocks, and the pin is `ruff>=0.4.0` with no upper bound, so `ruff format --check .` started failing on main as soon as ruff updated. Exclude rather than reformat: bot/docs/knowledge/ mirrors the remote docs API. It is the fallback for DOCS_USE_REMOTE=remote and the baseline the shadow-mode hash diff compares against, so reformatting it would report permanent false drift. The exclusion is narrow — markdown outside the knowledge base still gets formatted, and `ruff check` never looked at markdown to begin with. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Post a Slack message through an Incoming Webhook whenever a thread is created in one of the channels listed in SUPPORT_CHANNEL_IDS. The message carries the thread title as a link, the author, the channel, and a preview of the opening message. Works for both text and forum channels. Design notes: - Incoming Webhook over a bot token: one env var, no OAuth or scopes. The destination channel is fixed in the Slack app. - A new SUPPORT_CHANNEL_IDS rather than reusing ALLOWED_CHANNELS, which answers a different question (where the bot replies, not what it reports). - httpx, not aiohttp: httpx is an explicit dependency; aiohttp only comes in transitively through discord.py. - discord.py only dispatches on_thread_create for newly created threads, so gateway re-joins cannot double-notify and no dedupe is needed. - THREAD_CREATE can outrun a forum post's opening message, which surfaces as NotFound, so that case gets one retry. Forbidden gets none. - User-controlled fields are HTML-escaped so a thread title cannot forge a Slack link, and 5xx/429 are retried while other 4xx are not. Also drop httpx to WARNING: it logs every request at INFO with the full URL, and the webhook secret lives in that URL, so the default would write the secret to the container logs on every notification. With SLACK_WEBHOOK_URL unset the notifier is a no-op and the bot behaves exactly as before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Qué hace
Cada vez que se abre un hilo en un canal listado en
SUPPORT_CHANNEL_IDS(p. ej.#support), el bot publica un aviso en Slack vía Incoming Webhook con título del hilo (link clicable), autor, canal y preview del mensaje inicial. Funciona con canales de texto y de foro.Con
SLACK_WEBHOOK_URLvacío el notifier es un no-op y el bot se comporta exactamente como hoy.Decisiones
chat.postMessage: una env var, sin OAuth ni scopes. El canal destino se fija en la app de Slack.SUPPORT_CHANNEL_IDSnueva en vez de reusarALLOWED_CHANNELS, que responde a otra pregunta (dónde responde el bot, no de qué avisa).httpx, noaiohttp:httpxes dependencia explícita;aiohttpsólo entra transitivamente por discord.py.&,<,>en todo campo de usuario, así un título de hilo no puede forjar un link de Slack.Dos detalles de discord.py
on_thread_createsólo se despacha connewly_created, así que las reconexiones del gateway no duplican avisos y no hace falta deduplicar.THREAD_CREATEpuede adelantarse al mensaje inicial de un post de foro, y ese race se manifiesta comoNotFound. Por eso hay un reintento enNotFoundy ninguno enForbidden.Seguridad: el secreto del webhook en los logs
bot/config.pypone el root logger enINFOyhttpxheredaba, así que logueaba cada request con la URL completa. Como el secreto del webhook va en el path, en producción cada hilo de#supporthabría escrito ese secreto en los logs del contenedor.httpxbaja aWARNING; hay un test que falla si alguien lo revierte.Salió al hacer una prueba real contra un webhook; los tests usan
MockTransporty nunca pasan por ese logger.CI roto en
main(primer commit, independiente)ruff format --check .fallaba enmain: ruff 0.16 formatea el Python dentro de bloques markdown y el pin esruff>=0.4.0sin techo. Se arregla excluyendo, no reformateando:bot/docs/knowledge/es un mirror del API remoto de docs —fallback deDOCS_USE_REMOTE=remotey línea base del diff deshadow— así que reformatearlo reportaría drift falso a perpetuidad.Verificación
owner/parentaNone) y que un fallo de Slack no propague al event loop.ruff checkyruff format --checklimpios.Antes de mergear
El
.envdel servidor no lo toca el deploy. Hay que añadir a mano en el nodo, antes del merge:🤖 Generated with Claude Code