---
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_message_send_test.go"
file_path: "functions/infra/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.
