package roomserver import ( "context" "encoding/json" "fmt" "github.com/dariuszjaniak/warp/server/internal/matrix/auth" "github.com/dariuszjaniak/warp/server/internal/matrix/event" ) // PushRoom is what push rule evaluation needs to know about a room: who is // in it under what name, what it is called, and who may notify all of it. // // It is read from the room's current state rather than the state at the // event being evaluated. The push server reads it moments after the event // lands, when the two differ only if somebody joined or renamed themselves // in between, and reading the state at every event would cost a state // resolution per message per room member. type PushRoom struct { RoomID string // Members maps each joined user to their display name in the room, // empty when they have not set one. Members map[string]string // Name is m.room.name, and CanonicalAlias m.room.canonical_alias's // alias: what a push to a phone calls the room. Name string CanonicalAlias string powerLevels *auth.PowerLevels } // SenderMayNotify reports whether a user has the power a notification key // requires — "room", for an @room mention. func (p PushRoom) SenderMayNotify(userID, key string) bool { if p.powerLevels == nil { return false } return p.powerLevels.UserLevel(userID) >= p.powerLevels.NotificationLevel(key) } // PushRoomFor reads a room for the push server. // // It is a server-side read with no user to check against: the push server // evaluates events on behalf of the room's members, every one of whom // could read the same state. func (r *API) PushRoomFor(ctx context.Context, roomID string) (PushRoom, error) { room, rules, err := r.roomAndRules(ctx, roomID) if err != nil { return PushRoom{}, err } st, err := r.currentState(ctx, room, rules) if err != nil { return PushRoom{}, err } out := PushRoom{RoomID: roomID, Members: map[string]string{}} for key, e := range st { if key.Type != event.TypeMember { continue } if membership, _ := e.Membership(); membership != event.MembershipJoin { continue } var content struct { DisplayName string `json:"displayname"` } _ = json.Unmarshal(e.Content(), &content) out.Members[key.StateKey] = content.DisplayName } if e := st.Get("m.room.name", ""); e != nil { var content struct { Name string `json:"name"` } _ = json.Unmarshal(e.Content(), &content) out.Name = content.Name } if e := st.Get("m.room.canonical_alias", ""); e != nil { var content struct { Alias string `json:"alias"` } _ = json.Unmarshal(e.Content(), &content) out.CanonicalAlias = content.Alias } if create := st.Create(); create != nil { pl, err := auth.ResolvePowerLevels(st.PowerLevels(), create, rules) if err != nil { return PushRoom{}, fmt.Errorf("%w: %v", ErrRejected, err) } out.powerLevels = pl } return out, nil }