--- name: matrix_sync_service kind: function lang: go domain: infra version: "0.1.0" purity: impure signature: "func MatrixSyncService(ctx context.Context, cfg MatrixSyncServiceConfig) (*MatrixSyncServiceHandle, error)" description: "Arranca el sync loop de mautrix contra Synapse en background con backoff exponencial, emite eventos Matrix normalizados via canal Go y expone funcion de stop idempotente." tags: [matrix, mautrix, sync, longpoll, reconnect, goroutine, channels, infra, matrix-mas] params: - name: ctx desc: "Context padre. Si se cancela, la goroutine sale limpiamente y cierra los channels." - name: cfg.Client desc: "*mautrix.Client ya inicializado con credenciales (HomeserverURL, AccessToken, UserID). Usar matrix_client_init para crearlo. Obligatorio." - name: cfg.InitialBackoffMS desc: "Milisegundos de espera inicial entre reintentos tras error de sync. Default: 1000 (1s)." - name: cfg.MaxBackoffMS desc: "Techo del backoff exponencial en ms. Default: 60000 (60s)." - name: cfg.ChannelBuffer desc: "Capacidad del buffer del canal Events. Si el consumer va lento y el buffer se llena, el sync se bloquea hasta que el consumer drene. Default: 256." output: "*MatrixSyncServiceHandle con Events <-chan MatrixSyncEvent (canal de eventos normalizados), Errors <-chan error (errores transitorios no fatales), Stop func() (cancela y cierra todo, idempotente)." uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: - "maunium.net/go/mautrix" - "maunium.net/go/mautrix/event" - "maunium.net/go/mautrix/id" tested: true tests: - "RecibeMensajeYStop" - "BackoffRecovery" - "Error401NoExit" - "StopIdempotente" - "CtxCancelCierraChannels" test_file_path: "functions/infra/matrix/matrix_sync_service_test.go" file_path: "functions/infra/matrix/matrix_sync_service.go" --- ## Ejemplo ```go ctx := context.Background() h, err := MatrixSyncService(ctx, MatrixSyncServiceConfig{ Client: client, // *mautrix.Client de matrix_client_init }) if err != nil { panic(err) } defer h.Stop() // Consumir errores transitorios en goroutine separada go func() { for e := range h.Errors { log.Println("matrix sync error:", e) } }() // Loop de eventos (bloquea hasta que h.Stop() se llame o ctx sea cancelado) for ev := range h.Events { fmt.Printf("[%s] %s: %s\n", ev.Type, ev.Sender, ev.Body) } ``` ## Cuando usarla Usar despues de `MatrixClientInit` (y opcionalmente `MatrixCryptoInit`) para recibir el stream de eventos de Matrix en tiempo real. Es el servicio long-running central de cualquier cliente Matrix: matrix_client_pc, admin_panel, bots, monitores. Un solo `MatrixSyncService` por client, durante toda la vida de la aplicacion. ## Gotchas - **Solo UN Sync por client**: dos goroutines llamando `SyncWithContext` simultaneamente sobre el mismo client rompe el `since` token y produce duplicados o perdidas. Esta funcion garantiza una sola goroutine de sync si es llamada una sola vez. NO llamar `MatrixSyncService` dos veces sobre el mismo `*mautrix.Client`. - **Crypto antes del Sync**: mensajes `m.room.encrypted` que llegan antes de inicializar `MatrixCryptoInit` quedan sin descifrar (emitidos con `Type:"encrypted"`, `Body:""`, `Raw:*event.Event`). Inicializar crypto siempre ANTES de llamar a esta funcion. - **Buffer de channel**: si el consumer no drena `Events` con suficiente rapidez, el sync se bloquea en el punto de emision. Synapse puede acumular deltas. Mantener el consumer rapido o aumentar `ChannelBuffer`. - **Errores fatales (401/M_UNKNOWN_TOKEN)**: no cierran el servicio automaticamente — se emiten a `Errors` y el servicio espera con backoff maximo. El caller decide llamar `Stop()` y re-autenticar. - **Stop idempotente**: llamar `Stop()` multiples veces es seguro; no causa panic. - **Build tag**: el paquete `infra` requiere `-tags goolm` para compilar tests sin libolm (dependencia C de la crypto de mautrix). Los tests usan `//go:build goolm`.