fix: detectar thread en eventos E2EE via cache de eventos cifrados
Añade un tercer mecanismo de deteccion de thread en listener.go para cubrir el caso en que mautrix-go no propaga m.relates_to al payload descifrado. El problema ocurria cuando Element Web (matrix-js-sdk versiones antiguas) no incluia m.relates_to en el contenido exterior del evento m.room.encrypted. mautrix-go solo copia m.relates_to al payload descifrado si EncryptedEventContent.RelatesTo != nil, por lo que los dos mecanismos existentes (raw map + typed content) fallaban. La solucion registra un listener global (OnEvent) que captura m.relates_to del evento cifrado ANTES de que CryptoHelper lo descifre y re-despache (los listeners globales se ejecutan antes que los de tipo especifico segun DefaultSyncer.Dispatch). El valor se guarda en un sync.Map keyed por event ID y se consume con LoadAndDelete en el handler EventMessage.
This commit is contained in:
+45
-10
@@ -29,14 +29,15 @@ type MembershipNotifyFunc func(ctx context.Context, roomID, userID, membership s
|
||||
|
||||
// Listener attaches to a mautrix syncer and dispatches events to an EventHandler.
|
||||
type Listener struct {
|
||||
client *Client
|
||||
cfg config.MatrixCfg
|
||||
handler EventHandler
|
||||
logger *slog.Logger
|
||||
dmCache map[id.RoomID]bool
|
||||
mu sync.RWMutex
|
||||
interceptor InterceptFunc // if set and returns true, event is forwarded to orchestrator
|
||||
membershipNotify MembershipNotifyFunc // if set, called on all StateMember events
|
||||
client *Client
|
||||
cfg config.MatrixCfg
|
||||
handler EventHandler
|
||||
logger *slog.Logger
|
||||
dmCache map[id.RoomID]bool
|
||||
mu sync.RWMutex
|
||||
interceptor InterceptFunc // if set and returns true, event is forwarded to orchestrator
|
||||
membershipNotify MembershipNotifyFunc // if set, called on all StateMember events
|
||||
encryptedRelatesTo sync.Map // id.EventID → map[string]any: cache m.relates_to from encrypted events
|
||||
}
|
||||
|
||||
// NewListener creates a Listener for the given client.
|
||||
@@ -66,6 +67,22 @@ func (l *Listener) SetMembershipNotify(fn MembershipNotifyFunc) {
|
||||
func (l *Listener) Run(ctx context.Context) error {
|
||||
syncer := l.client.raw.Syncer.(*mautrix.DefaultSyncer)
|
||||
|
||||
// Cache m.relates_to from encrypted events BEFORE they are decrypted.
|
||||
// mautrix-go only copies m.relates_to into the decrypted content when
|
||||
// EncryptedEventContent.RelatesTo != nil (i.e. the outer event has it).
|
||||
// Older Element / matrix-js-sdk versions may not include it in the outer
|
||||
// event, so we store it here and use it as a fallback for thread detection.
|
||||
// Global listeners fire before type-specific ones, so this runs before
|
||||
// CryptoHelper decrypts and dispatches the event.EventMessage.
|
||||
syncer.OnEvent(func(ctx context.Context, evt *event.Event) {
|
||||
if evt.Type != event.EventEncrypted {
|
||||
return
|
||||
}
|
||||
if relatesTo, ok := evt.Content.Raw["m.relates_to"].(map[string]any); ok {
|
||||
l.encryptedRelatesTo.Store(evt.ID, relatesTo)
|
||||
}
|
||||
})
|
||||
|
||||
// Auto-join rooms when invited. Without this, the bot stays in "invited"
|
||||
// state and never receives m.room.message events.
|
||||
syncer.OnEventType(event.StateMember, func(ctx context.Context, evt *event.Event) {
|
||||
@@ -154,7 +171,10 @@ func (l *Listener) Run(ctx context.Context) error {
|
||||
msgCtx.EventID = evt.ID.String()
|
||||
|
||||
// Extract thread root from m.relates_to (Matrix thread support).
|
||||
// Two methods: raw map (fast) + typed content fallback (robust for E2EE).
|
||||
// Three methods in order of preference:
|
||||
// 1. Raw map from decrypted content (fast path)
|
||||
// 2. Typed parsed content (robust after E2EE decryption)
|
||||
// 3. Encrypted event cache (for older clients that omit outer m.relates_to)
|
||||
if l.cfg.Threads.Enabled {
|
||||
if relatesTo, ok := evt.Content.Raw["m.relates_to"].(map[string]any); ok {
|
||||
if relType, _ := relatesTo["rel_type"].(string); relType == "m.thread" {
|
||||
@@ -163,7 +183,7 @@ func (l *Listener) Run(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback: use typed content (more robust after E2EE decryption)
|
||||
// Fallback 2: use typed content (more robust after E2EE decryption)
|
||||
if msgCtx.ThreadID == "" {
|
||||
if msgContent, ok := evt.Content.Parsed.(*event.MessageEventContent); ok && msgContent.RelatesTo != nil {
|
||||
if threadParent := msgContent.RelatesTo.GetThreadParent(); threadParent != "" {
|
||||
@@ -172,6 +192,21 @@ func (l *Listener) Run(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback 3: encrypted event cache — for E2EE messages where the outer
|
||||
// m.relates_to was NOT propagated into the decrypted content by mautrix-go
|
||||
// (happens with older Element / matrix-js-sdk versions).
|
||||
if msgCtx.ThreadID == "" {
|
||||
if cached, found := l.encryptedRelatesTo.LoadAndDelete(evt.ID); found {
|
||||
if rt, ok := cached.(map[string]any); ok {
|
||||
if relType, _ := rt["rel_type"].(string); relType == "m.thread" {
|
||||
if threadRoot, _ := rt["event_id"].(string); threadRoot != "" {
|
||||
msgCtx.ThreadID = threadRoot
|
||||
l.logger.Debug("thread detected via encrypted event cache", "thread_id", msgCtx.ThreadID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l.logger.Debug("message parsed",
|
||||
|
||||
Reference in New Issue
Block a user