1
0
Fork 0
forked from Mirrors/gomuks
nyxmuks/pkg/hicli/database/statestore.go

187 lines
6.7 KiB
Go

// Copyright (c) 2024 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package database
import (
"context"
"database/sql"
"errors"
"fmt"
"slices"
"go.mau.fi/util/dbutil"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/crypto"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
const (
getMembershipQuery = `
SELECT membership FROM current_state
WHERE room_id = $1 AND event_type = 'm.room.member' AND state_key = $2
`
getStateEventContentQuery = `
SELECT event.content FROM current_state cs
LEFT JOIN event ON event.rowid = cs.event_rowid
WHERE cs.room_id = $1 AND cs.event_type = $2 AND cs.state_key = $3
`
getRoomJoinedMembersQuery = `
SELECT state_key FROM current_state
WHERE room_id = $1 AND event_type = 'm.room.member' AND membership = 'join'
`
getRoomJoinedOrInvitedMembersQuery = `
SELECT state_key FROM current_state
WHERE room_id = $1 AND event_type = 'm.room.member' AND membership IN ('join', 'invite')
`
getHasFetchedMembersQuery = `
SELECT has_member_list FROM room WHERE room_id = $1
`
isRoomEncryptedQuery = `
SELECT room.encryption_event IS NOT NULL FROM room WHERE room_id = $1
`
getRoomEncryptionEventQuery = `
SELECT room.encryption_event FROM room WHERE room_id = $1
`
findSharedRoomsQuery = `
SELECT room_id FROM current_state
WHERE event_type = 'm.room.member' AND state_key = $1 AND membership = 'join'
`
)
type ClientStateStore struct {
*Database
}
var _ mautrix.StateStore = (*ClientStateStore)(nil)
var _ mautrix.StateStoreUpdater = (*ClientStateStore)(nil)
var _ crypto.StateStore = (*ClientStateStore)(nil)
func (c *ClientStateStore) IsInRoom(ctx context.Context, roomID id.RoomID, userID id.UserID) bool {
return c.IsMembership(ctx, roomID, userID, event.MembershipJoin)
}
func (c *ClientStateStore) IsInvited(ctx context.Context, roomID id.RoomID, userID id.UserID) bool {
return c.IsMembership(ctx, roomID, userID, event.MembershipInvite, event.MembershipJoin)
}
func (c *ClientStateStore) IsMembership(ctx context.Context, roomID id.RoomID, userID id.UserID, allowedMemberships ...event.Membership) bool {
var membership event.Membership
err := c.QueryRow(ctx, getMembershipQuery, roomID, userID).Scan(&membership)
if errors.Is(err, sql.ErrNoRows) {
err = nil
membership = event.MembershipLeave
}
return slices.Contains(allowedMemberships, membership)
}
func (c *ClientStateStore) GetMember(ctx context.Context, roomID id.RoomID, userID id.UserID) (*event.MemberEventContent, error) {
content, err := c.TryGetMember(ctx, roomID, userID)
if content == nil {
content = &event.MemberEventContent{Membership: event.MembershipLeave}
}
return content, err
}
func (c *ClientStateStore) TryGetMember(ctx context.Context, roomID id.RoomID, userID id.UserID) (content *event.MemberEventContent, err error) {
err = c.QueryRow(ctx, getStateEventContentQuery, roomID, event.StateMember.Type, userID).Scan(&dbutil.JSON{Data: &content})
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
return
}
func (c *ClientStateStore) IsConfusableName(ctx context.Context, roomID id.RoomID, currentUser id.UserID, name string) ([]id.UserID, error) {
//TODO implement me
panic("implement me")
}
func (c *ClientStateStore) GetPowerLevels(ctx context.Context, roomID id.RoomID) (content *event.PowerLevelsEventContent, err error) {
err = c.QueryRow(ctx, getStateEventContentQuery, roomID, event.StatePowerLevels.Type, "").Scan(&dbutil.JSON{Data: &content})
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
return
}
func (c *ClientStateStore) GetRoomJoinedMembers(ctx context.Context, roomID id.RoomID) ([]id.UserID, error) {
rows, err := c.Query(ctx, getRoomJoinedMembersQuery, roomID)
return dbutil.NewRowIterWithError(rows, dbutil.ScanSingleColumn[id.UserID], err).AsList()
}
func (c *ClientStateStore) GetRoomJoinedOrInvitedMembers(ctx context.Context, roomID id.RoomID) ([]id.UserID, error) {
rows, err := c.Query(ctx, getRoomJoinedOrInvitedMembersQuery, roomID)
return dbutil.NewRowIterWithError(rows, dbutil.ScanSingleColumn[id.UserID], err).AsList()
}
func (c *ClientStateStore) HasFetchedMembers(ctx context.Context, roomID id.RoomID) (hasFetched bool, err error) {
//err = c.QueryRow(ctx, getHasFetchedMembersQuery, roomID).Scan(&hasFetched)
//if errors.Is(err, sql.ErrNoRows) {
// err = nil
//}
//return
return false, fmt.Errorf("not implemented")
}
func (c *ClientStateStore) MarkMembersFetched(ctx context.Context, roomID id.RoomID) error {
return fmt.Errorf("not implemented")
}
func (c *ClientStateStore) GetAllMembers(ctx context.Context, roomID id.RoomID) (map[id.UserID]*event.MemberEventContent, error) {
return nil, fmt.Errorf("not implemented")
}
func (c *ClientStateStore) IsEncrypted(ctx context.Context, roomID id.RoomID) (isEncrypted bool, err error) {
err = c.QueryRow(ctx, isRoomEncryptedQuery, roomID).Scan(&isEncrypted)
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
return
}
func (c *ClientStateStore) GetEncryptionEvent(ctx context.Context, roomID id.RoomID) (content *event.EncryptionEventContent, err error) {
err = c.QueryRow(ctx, getRoomEncryptionEventQuery, roomID).
Scan(&dbutil.JSON{Data: &content})
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
return
}
func (c *ClientStateStore) FindSharedRooms(ctx context.Context, userID id.UserID) ([]id.RoomID, error) {
// TODO for multiuser support, this might need to filter by the local user's membership
rows, err := c.Query(ctx, findSharedRoomsQuery, userID)
return dbutil.NewRowIterWithError(rows, dbutil.ScanSingleColumn[id.RoomID], err).AsList()
}
// Update methods are all intentionally no-ops as the state store wants to have the full event
func (c *ClientStateStore) SetMembership(ctx context.Context, roomID id.RoomID, userID id.UserID, membership event.Membership) error {
return nil
}
func (c *ClientStateStore) SetMember(ctx context.Context, roomID id.RoomID, userID id.UserID, member *event.MemberEventContent) error {
return nil
}
func (c *ClientStateStore) ClearCachedMembers(ctx context.Context, roomID id.RoomID, memberships ...event.Membership) error {
return nil
}
func (c *ClientStateStore) SetPowerLevels(ctx context.Context, roomID id.RoomID, levels *event.PowerLevelsEventContent) error {
return nil
}
func (c *ClientStateStore) SetEncryptionEvent(ctx context.Context, roomID id.RoomID, content *event.EncryptionEventContent) error {
return nil
}
func (c *ClientStateStore) UpdateState(ctx context.Context, evt *event.Event) {}
func (c *ClientStateStore) ReplaceCachedMembers(ctx context.Context, roomID id.RoomID, evts []*event.Event, onlyMemberships ...event.Membership) error {
return nil
}