refactor(infra): split de drivers pesados a subpaquetes + fix TestSSEHandler

Mueve duckdb_open, clickhouse_open, postgres_open, matrix_* y keyring_token_store
del paquete monolitico functions/infra a subpaquetes propios
(functions/infra/{duckdb,clickhouse,postgres,matrix,keyring}). El paquete infra ya
no importa los drivers (go-duckdb, clickhouse-go, pgx, mautrix, go-keyring), por lo
que las apps que solo usan funciones ligeras (process, cron, http, sqlite) dejan de
arrastrarlos. Reduccion de binarios: dag_engine 72->10MB, registry_api 70->8.7MB,
services_api 70->9MB, call_monitor 68->6.6MB, sqlite_api 70->8.9MB.

Los IDs del registry se mantienen estables (domain: infra en frontmatter). Se
preservan los build tags goolm/libolm de matrix_crypto_init.

Tambien corrige TestSSEHandler: el test leia el body con un unico Read() que con
HTTP chunked solo capturaba el primer evento; ahora usa io.ReadAll hasta EOF.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 23:48:59 +02:00
parent 4ad4e7093a
commit 4bce095964
25 changed files with 55 additions and 51 deletions
@@ -0,0 +1,99 @@
---
name: matrix_message_send
kind: function
lang: go
domain: infra
version: "0.1.0"
purity: impure
signature: |
func MatrixSendText(ctx context.Context, client *mautrix.Client, roomID id.RoomID, body string) (id.EventID, error)
func MatrixSendMarkdown(ctx context.Context, client *mautrix.Client, roomID id.RoomID, markdown string) (id.EventID, error)
func MatrixSendReply(ctx context.Context, client *mautrix.Client, roomID id.RoomID, replyTo id.EventID, body string) (id.EventID, error)
func MatrixEditMessage(ctx context.Context, client *mautrix.Client, roomID id.RoomID, eventID id.EventID, newBody string) (id.EventID, error)
func MatrixSendReaction(ctx context.Context, client *mautrix.Client, roomID id.RoomID, targetEventID id.EventID, key string) (id.EventID, error)
description: "Envía mensajes Matrix con todas las variantes del compositor: texto plain, markdown con HTML sanitizado, reply con m.in_reply_to, edit (m.replace) y reaction (m.annotation). Si el room es E2EE y client.Crypto está configurado via matrix_crypto_init, mautrix cifra automáticamente."
tags: [matrix, mautrix, send, message, markdown, reply, edit, reaction, infra, matrix-mas]
params:
- name: ctx
desc: "Context para cancelación y timeout de la petición HTTP a Synapse."
- name: client
desc: "*mautrix.Client autenticado. Debe tener AccessToken, UserID y DeviceID. Si es nil, error inmediato."
- name: roomID
desc: "ID del room Matrix destino. Formato: !xxx:server."
- name: body / markdown / newBody
desc: "Contenido del mensaje. Para MatrixSendMarkdown se parsea con goldmark y se sanitiza con bluemonday UGCPolicy."
- name: replyTo / eventID / targetEventID
desc: "ID del evento referenciado (para reply, edit y reaction)."
- name: key
desc: "Emoji unicode raw para reaction (ej. '👍'). No shortcodes (:thumbsup:)."
output: "id.EventID del evento enviado por Synapse + error. El EventID permite referenciar el mensaje para edits, replies o reactions posteriores."
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports:
- "context"
- "bytes"
- "fmt"
- "github.com/microcosm-cc/bluemonday"
- "github.com/yuin/goldmark"
- "maunium.net/go/mautrix"
- "maunium.net/go/mautrix/event"
- "maunium.net/go/mautrix/id"
tested: true
tests:
- "SendText body correcto y EventID parseado"
- "SendMarkdown bold convierte a HTML strong y sanitiza script"
- "SendReply m.relates_to m.in_reply_to presente"
- "EditMessage rel_type m.replace y m.new_content"
- "SendReaction tipo m.reaction con m.annotation y key"
- "SendText client nil devuelve error"
- "SendMarkdown client nil devuelve error"
- "SendReply client nil devuelve error"
- "EditMessage client nil devuelve error"
- "SendReaction client nil devuelve error"
test_file_path: "functions/infra/matrix/matrix_message_send_test.go"
file_path: "functions/infra/matrix/matrix_message_send.go"
---
## Ejemplo
```go
import (
"context"
infra "fn-registry/functions/infra"
"maunium.net/go/mautrix/id"
)
ctx := context.Background()
roomID := id.RoomID("!abc123:organic-machine.com")
// Texto plain
evID, err := infra.MatrixSendText(ctx, client, roomID, "Hola")
// Markdown: **bold**, `code`, > quote -> HTML sanitizado
evID, err = infra.MatrixSendMarkdown(ctx, client, roomID, "**bold** + `code`")
// Reply a un evento existente
evID, err = infra.MatrixSendReply(ctx, client, roomID, id.EventID("$orig:server"), "Si, totalmente")
// Edit de un mensaje ya enviado
evID, err = infra.MatrixEditMessage(ctx, client, roomID, id.EventID("$msg:server"), "texto corregido")
// Reaction emoji
evID, err = infra.MatrixSendReaction(ctx, client, roomID, id.EventID("$msg:server"), "👍")
```
## Cuando usarla
Llamar desde el compositor del cliente Matrix (`matrix_client_pc`) tras inicializar el cliente con `matrix_client_init`. Si el room es E2EE, llamar primero a `matrix_crypto_init` para que `client.Crypto` esté configurado — el cifrado es transparente, no requiere código extra en estas funciones.
## Gotchas
- **Markdown sanitization**: goldmark puede emitir tags HTML arbitrarios si el input los contiene. Esta función aplica `bluemonday.UGCPolicy()` + allowlist extra (`details`, `summary`, `code`, `pre`). Tags fuera de la allowlist como `<script>`, `<iframe>`, `<style>` son eliminados. El texto interno puede quedar como texto plano.
- **Edits sobre mensajes cifrados**: mautrix-go cifra el `m.new_content` también. Receivers que no tengan acceso a la session megolm no verán el edit — verán el mensaje original.
- **Reactions** son evento separado `m.reaction`, NO `m.room.message`. Algunos clientes Matrix viejos las ignoran. No se cifran aunque el room sea E2EE (limitación de mautrix-go).
- **Reply quote v0.1.0**: esta función NO inserta el texto del mensaje original en el body. Es responsabilidad del caller construir la cita si la necesita. v0.2.0 podría hacer fetch del original via state cache.
- **Edit racing**: si dos edits llegan al mismo tiempo al servidor, gana el de timestamp mayor (regla Matrix server-side). No hay protección contra races en esta función.
- **client nil**: todas las funciones validan `client != nil` y retornan error inmediato. No hacen validación del formato de `roomID` — Synapse responderá con error si es inválido.