diff --git a/bot/email.go b/bot/email.go index 059eb06..9a6e7d7 100644 --- a/bot/email.go +++ b/bot/email.go @@ -445,7 +445,7 @@ func (b *Bot) getParentEvent(evt *event.Event) (id.EventID, *event.Event) { b.log.Error().Err(err).Msg("cannot get parent event") return threadID, nil } - linkpearl.ParseContent(parentEvt, parentEvt.Type, b.log) + linkpearl.ParseContent(parentEvt, b.log) if !b.lp.GetMachine().StateStore.IsEncrypted(evt.RoomID) { return threadID, parentEvt diff --git a/bot/reaction.go b/bot/reaction.go index d931227..0ae40f6 100644 --- a/bot/reaction.go +++ b/bot/reaction.go @@ -4,7 +4,6 @@ import ( "context" "gitlab.com/etke.cc/linkpearl" - "maunium.net/go/mautrix/event" ) var supportedReactions = map[string]string{ @@ -28,9 +27,16 @@ func (b *Bot) handleReaction(ctx context.Context) { b.Error(ctx, "cannot find event %s: %v", srcID, err) return } + linkpearl.ParseContent(srcEvt, b.log) + if b.lp.GetMachine().StateStore.IsEncrypted(evt.RoomID) { + decrypted, derr := b.lp.GetClient().Crypto.Decrypt(srcEvt) + if derr == nil { + srcEvt = decrypted + } + } threadID := linkpearl.EventParent(srcID, srcEvt.Content.AsMessage()) ctx = threadIDToContext(ctx, threadID) - linkpearl.ParseContent(evt, event.EventMessage, b.log) + linkpearl.ParseContent(evt, b.log) switch action { case commandSpamlistAdd: diff --git a/go.mod b/go.mod index 4e4afd9..677fa12 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( gitlab.com/etke.cc/go/secgen v1.1.1 gitlab.com/etke.cc/go/trysmtp v1.1.3 gitlab.com/etke.cc/go/validator v1.0.6 - gitlab.com/etke.cc/linkpearl v0.0.0-20230928120707-1e99315dc616 + gitlab.com/etke.cc/linkpearl v0.0.0-20230929133006-84554ee97edb golang.org/x/exp v0.0.0-20230905200255-921286631fa9 maunium.net/go/mautrix v0.16.1 ) diff --git a/go.sum b/go.sum index e5b649b..8acb538 100644 --- a/go.sum +++ b/go.sum @@ -111,8 +111,8 @@ gitlab.com/etke.cc/go/trysmtp v1.1.3 h1:e2EHond77onMaecqCg6mWumffTSEf+ycgj88nbee gitlab.com/etke.cc/go/trysmtp v1.1.3/go.mod h1:lOO7tTdAE0a3ETV3wN3GJ7I1Tqewu7YTpPWaOmTteV0= gitlab.com/etke.cc/go/validator v1.0.6 h1:w0Muxf9Pqw7xvF7NaaswE6d7r9U3nB2t2l5PnFMrecQ= gitlab.com/etke.cc/go/validator v1.0.6/go.mod h1:Id0SxRj0J3IPhiKlj0w1plxVLZfHlkwipn7HfRZsDts= -gitlab.com/etke.cc/linkpearl v0.0.0-20230928120707-1e99315dc616 h1:Gvhmq84VmAJN1xRzRBK79XJVObAvVcx9Q3s6K+Zo644= -gitlab.com/etke.cc/linkpearl v0.0.0-20230928120707-1e99315dc616/go.mod h1:IZ0TE+ZnIdJLb538owDMxhtpWH7blfW+oR7e5XRXxNY= +gitlab.com/etke.cc/linkpearl v0.0.0-20230929133006-84554ee97edb h1:QoabJtxrQg7P2kuxwOp3iU6uKoep+5QQAtMomajXcpA= +gitlab.com/etke.cc/linkpearl v0.0.0-20230929133006-84554ee97edb/go.mod h1:IZ0TE+ZnIdJLb538owDMxhtpWH7blfW+oR7e5XRXxNY= go.mau.fi/util v0.1.0 h1:BwIFWIOEeO7lsiI2eWKFkWTfc5yQmoe+0FYyOFVyaoE= go.mau.fi/util v0.1.0/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84= golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= diff --git a/vendor/gitlab.com/etke.cc/linkpearl/sync.go b/vendor/gitlab.com/etke.cc/linkpearl/sync.go index edcdeed..da43491 100644 --- a/vendor/gitlab.com/etke.cc/linkpearl/sync.go +++ b/vendor/gitlab.com/etke.cc/linkpearl/sync.go @@ -1,6 +1,7 @@ package linkpearl import ( + "strings" "time" "maunium.net/go/mautrix" @@ -65,14 +66,22 @@ func (l *Linkpearl) onInvite(evt *event.Event) { l.tryLeave(evt.RoomID, 0) } +// TODO: https://spec.matrix.org/v1.8/client-server-api/#post_matrixclientv3joinroomidoralias +// endpoint supports server_name param and tells "The servers to attempt to join the room through. One of the servers must be participating in the room.", +// meaning you can specify more than 1 server. It is not clear, what format should be used "example.com,example.org", or "example.com example.org", or whatever else. +// Moreover, it is not clear if the following values can be used together with that field: l.api.UserID.Homeserver() and evt.Sender.Homeserver() func (l *Linkpearl) tryJoin(roomID id.RoomID, retry int) { if retry >= l.maxretries { return } - _, err := l.api.JoinRoomByID(roomID) + _, err := l.api.JoinRoom(roomID.String(), "", nil) + err = UnwrapError(err) if err != nil { l.log.Error().Err(err).Str("roomID", roomID.String()).Msg("cannot join room") + if strings.HasPrefix(err.Error(), "403") { // no permission to join, no need to retry + return + } time.Sleep(5 * time.Second) l.log.Error().Err(err).Str("roomID", roomID.String()).Int("retry", retry+1).Msg("trying to join again") l.tryJoin(roomID, retry+1) @@ -85,6 +94,7 @@ func (l *Linkpearl) tryLeave(roomID id.RoomID, retry int) { } _, err := l.api.LeaveRoom(roomID) + err = UnwrapError(err) if err != nil { l.log.Error().Err(err).Str("roomID", roomID.String()).Msg("cannot leave room") time.Sleep(5 * time.Second) @@ -99,6 +109,7 @@ func (l *Linkpearl) onEmpty(evt *event.Event) { } members, err := l.api.StateStore.GetRoomJoinedOrInvitedMembers(evt.RoomID) + err = UnwrapError(err) if err != nil { l.log.Error().Err(err).Str("roomID", evt.RoomID.String()).Msg("cannot get joined or invited members") return diff --git a/vendor/gitlab.com/etke.cc/linkpearl/utils.go b/vendor/gitlab.com/etke.cc/linkpearl/utils.go index 2e699f7..ba28ce7 100644 --- a/vendor/gitlab.com/etke.cc/linkpearl/utils.go +++ b/vendor/gitlab.com/etke.cc/linkpearl/utils.go @@ -7,6 +7,17 @@ import ( "maunium.net/go/mautrix/id" ) +// EventRelatesTo uses evt as source for EventParent() and RelatesTo() +func EventRelatesTo(evt *event.Event) *event.RelatesTo { + ParseContent(evt, nil) + relatable, ok := evt.Content.Parsed.(event.Relatable) + if !ok { + return nil + } + + return RelatesTo(EventParent(evt.ID, relatable)) +} + // RelatesTo returns relation object of a matrix event (either threads with reply-to fallback or plain reply-to) func RelatesTo(parentID id.EventID, noThreads ...bool) *event.RelatesTo { if parentID == "" { @@ -36,34 +47,58 @@ func RelatesTo(parentID id.EventID, noThreads ...bool) *event.RelatesTo { } } -// EventParent returns parent event ID (either from thread or from reply-to relation) -func EventParent(currentID id.EventID, content *event.MessageEventContent) id.EventID { - if content == nil { - return currentID +// GetParent is nil-safe version of evt.Content.AsMessage().RelatesTo.(GetThreadParent()|GetReplyTo()) +func GetParent(evt *event.Event) id.EventID { + ParseContent(evt, nil) + content, ok := evt.Content.Parsed.(event.Relatable) + if !ok { + return "" } - relation := content.OptionalGetRelatesTo() + relation := content.GetRelatesTo() if relation == nil { - return currentID + return "" } - threadParent := relation.GetThreadParent() - if threadParent != "" { - return threadParent + if parentID := relation.GetThreadParent(); parentID != "" { + return parentID + } + if parentID := relation.GetReplyTo(); parentID != "" { + return parentID } - replyParent := relation.GetReplyTo() - if replyParent != "" { - return replyParent + return "" +} + +// EventParent returns parent event ID (either from thread or from reply-to relation), like GetRelatesTo(), but with content and default return value +func EventParent(currentID id.EventID, content event.Relatable) id.EventID { + if parentID := GetParent(&event.Event{Content: event.Content{Parsed: content}}); parentID != "" { + return parentID } return currentID } +// EventContains checks if raw event content contains specified field with specified values +func EventContains[T comparable](evt *event.Event, field string, value T) bool { + if evt.Content.Raw == nil { + return false + } + if EventField[T](&evt.Content, field) != value { + return false + } + + return true +} + // EventField returns field value from raw event content -func EventField[T any](content *event.Content, field string) T { +func EventField[T comparable](content *event.Content, field string) T { var zero T - raw := content.Raw[field] + raw, ok := content.Raw[field] + if !ok { + return zero + } + if raw == nil { return zero } @@ -76,12 +111,13 @@ func EventField[T any](content *event.Content, field string) T { return v } -func ParseContent(evt *event.Event, eventType event.Type, log *zerolog.Logger) { +// ParseContent parses event content according to evt.Type +func ParseContent(evt *event.Event, log *zerolog.Logger) { if evt.Content.Parsed != nil { return } - perr := evt.Content.ParseRaw(eventType) - if perr != nil { + perr := evt.Content.ParseRaw(evt.Type) + if perr != nil && log != nil { log.Error().Err(perr).Msg("cannot parse event content") } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 5b590b4..ec286fa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -144,7 +144,7 @@ gitlab.com/etke.cc/go/trysmtp # gitlab.com/etke.cc/go/validator v1.0.6 ## explicit; go 1.18 gitlab.com/etke.cc/go/validator -# gitlab.com/etke.cc/linkpearl v0.0.0-20230928120707-1e99315dc616 +# gitlab.com/etke.cc/linkpearl v0.0.0-20230929133006-84554ee97edb ## explicit; go 1.18 gitlab.com/etke.cc/linkpearl # go.mau.fi/util v0.1.0