handle reactions in encrypted rooms

This commit is contained in:
Aine
2023-09-29 16:37:46 +03:00
parent bebfa6df92
commit 53655261fe
7 changed files with 78 additions and 25 deletions

View File

@@ -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

View File

@@ -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")
}
}