api: Add Room query

This commit is contained in:
Kevin Kandlbinder 2022-03-25 22:08:38 +01:00
parent 0aa91fa99f
commit cbd1aeb04a
8 changed files with 1639 additions and 6 deletions

File diff suppressed because it is too large Load diff

View file

@ -527,3 +527,79 @@ func ResolveComments(comments []*model2.DBComment, first *int, after *string) (*
Edges: nil,
}, nil
}
func buildDBRoomFilter(first *int, after *string, filter *model.RoomFilter /*, sort *model.UserSort*/, userId primitive.ObjectID) (*bson.M, *bson.M, *int64, error) {
compiledFilter := bson.M{}
compiledSort := bson.M{}
var filterBson *bson.M
var cursorBson *bson.M
limit := 25
/*if sort != nil {
if sort.ID != nil {
compiledSort["_id"] = buildSortRule(sort.ID)
}
if sort.Username != nil {
compiledSort["username"] = buildSortRule(sort.Username)
}
if sort.Admin != nil {
compiledSort["admin"] = buildSortRule(sort.Admin)
}
}*/
if first != nil {
limit = *first
}
if after != nil {
cursorBsonW := bson.M{}
afterID, err := primitive.ObjectIDFromHex(*after)
if err != nil {
return nil, nil, nil, err
}
cursorBsonW["_id"] = bson.M{"$gt": afterID}
cursorBson = &cursorBsonW
}
if filter != nil {
filterBsonW := bson.M{}
if filter.ID != nil {
filterBsonW["_id"] = *filter.ID
}
if filter.Debug != nil {
filterBsonW["debug"] = *filter.Debug
}
if filter.Active != nil {
filterBsonW["active"] = *filter.Active
}
if filter.CanEdit != nil && *filter.CanEdit == true {
filterBsonW["admins"] = userId
}
filterBson = &filterBsonW
}
if filterBson != nil && cursorBson != nil {
compiledFilter["$and"] = bson.A{*cursorBson, *filterBson}
}
if filterBson == nil && cursorBson != nil {
compiledFilter = *cursorBson
}
if filterBson != nil && cursorBson == nil {
compiledFilter = *filterBson
}
convLimit := int64(limit)
return &compiledFilter, &compiledSort, &convLimit, nil
}

View file

@ -85,6 +85,11 @@ type EntrySort struct {
AddedBy *SortRule `json:"addedBy"`
}
type HashCheckerConfigUpdate struct {
ChatNotice *bool `json:"chatNotice"`
HashCheckMode *HashCheckerMode `json:"hashCheckMode"`
}
type IDArrayFilter struct {
ContainsAll []*string `json:"containsAll"`
Length *int `json:"length"`
@ -152,6 +157,30 @@ type RemoveMxid struct {
Mxid string `json:"mxid"`
}
type RoomConfigUpdate struct {
Debug *bool `json:"debug"`
AdminPowerLevel *int `json:"adminPowerLevel"`
HashChecker *HashCheckerConfigUpdate `json:"hashChecker"`
}
type RoomConnection struct {
PageInfo *PageInfo `json:"pageInfo"`
Edges []*RoomEdge `json:"edges"`
SubscribedLists []string `json:"subscribedLists"`
}
type RoomEdge struct {
Node *Room `json:"node"`
Cursor string `json:"cursor"`
}
type RoomFilter struct {
ID *string `json:"id"`
Active *bool `json:"active"`
Debug *bool `json:"debug"`
CanEdit *bool `json:"canEdit"`
}
type SortRule struct {
Direction SortDirection `json:"direction"`
}
@ -203,6 +232,51 @@ type UserSort struct {
Admin *SortRule `json:"admin"`
}
type HashCheckerMode string
const (
HashCheckerModeNotice HashCheckerMode = "NOTICE"
HashCheckerModeDelete HashCheckerMode = "DELETE"
HashCheckerModeMute HashCheckerMode = "MUTE"
HashCheckerModeBan HashCheckerMode = "BAN"
)
var AllHashCheckerMode = []HashCheckerMode{
HashCheckerModeNotice,
HashCheckerModeDelete,
HashCheckerModeMute,
HashCheckerModeBan,
}
func (e HashCheckerMode) IsValid() bool {
switch e {
case HashCheckerModeNotice, HashCheckerModeDelete, HashCheckerModeMute, HashCheckerModeBan:
return true
}
return false
}
func (e HashCheckerMode) String() string {
return string(e)
}
func (e *HashCheckerMode) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = HashCheckerMode(str)
if !e.IsValid() {
return fmt.Errorf("%s is not a valid HashCheckerMode", str)
}
return nil
}
func (e HashCheckerMode) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}
type SortDirection string
const (

39
graph/model/room.go Normal file
View file

@ -0,0 +1,39 @@
package model
import "github.com/Unkn0wnCat/matrix-veles/internal/config"
type Room struct {
ID string `json:"id"`
Active bool `json:"active"`
RoomID string `json:"roomId"`
Debug bool `json:"debug"`
AdminPowerLevel int `json:"adminPowerLevel"`
HashCheckerConfig *HashCheckerConfig `json:"hashCheckerConfig"`
}
type HashCheckerConfig struct {
ChatNotice bool `json:"chatNotice"`
HashCheckMode HashCheckerMode `json:"hashCheckMode"`
SubscribedLists []string `json:"subscribedLists"`
}
func MakeRoom(room *config.RoomConfig) *Room {
var subscribed []string
for _, subId := range room.HashChecker.SubscribedLists {
subscribed = append(subscribed, subId.Hex())
}
return &Room{
ID: room.ID.String(),
Active: room.Active,
RoomID: room.RoomID,
Debug: room.Debug,
AdminPowerLevel: room.AdminPowerLevel,
HashCheckerConfig: &HashCheckerConfig{
ChatNotice: room.HashChecker.NoticeToChat,
HashCheckMode: AllHashCheckerMode[room.HashChecker.HashCheckMode],
SubscribedLists: subscribed,
},
}
}

View file

@ -7,7 +7,6 @@ scalar Time
directive @loggedIn on FIELD_DEFINITION
directive @hasRole(role: UserRole!) on FIELD_DEFINITION
directive @owner on FIELD_DEFINITION
directive @maintainer on FIELD_DEFINITION
enum UserRole {
ADMIN
@ -31,6 +30,38 @@ input SortRule {
direction: SortDirection!
}
enum HashCheckerMode {
NOTICE # Just post a notice
DELETE # Delete message, post notice
MUTE # Mute user, delete message, post notice
BAN # Ban user, delete message, post notice
}
type HashCheckerConfig {
chatNotice: Boolean!
hashCheckMode: HashCheckerMode!
}
type Room {
id: ID!
active: Boolean!
roomId: String!
debug: Boolean!
adminPowerLevel: Int!
hashCheckerConfig: HashCheckerConfig!
}
type RoomConnection {
pageInfo: PageInfo!
edges: [RoomEdge!]!
subscribedLists: [ID!]
}
type RoomEdge {
node: Room!
cursor: String!
}
type User {
id: ID!
@ -198,10 +229,19 @@ input EntrySort {
addedBy: SortRule
}
input RoomFilter {
id: ID
active: Boolean
debug: Boolean
canEdit: Boolean
}
type Query {
users(first: Int, after: String, filter: UserFilter, sort: UserSort): UserConnection @loggedIn
lists(first: Int, after: String, filter: ListFilter, sort: ListSort): ListConnection @loggedIn
entries(first: Int, after: String, filter: EntryFilter, sort: EntrySort): EntryConnection @loggedIn
rooms(first: Int, after: String, filter: RoomFilter): RoomConnection @loggedIn
user(id: ID, username: String): User @loggedIn
entry(id: ID, hashValue: String): Entry @loggedIn
@ -264,12 +304,25 @@ input RemoveMXID {
mxid: String!
}
input RoomConfigUpdate {
debug: Boolean
adminPowerLevel: Int
hashChecker: HashCheckerConfigUpdate
}
input HashCheckerConfigUpdate {
chatNotice: Boolean
hashCheckMode: HashCheckerMode
}
type Mutation {
login(input: Login!): String!
register(input: Register!): String! @hasRole(role: UNAUTHENTICATED)
addMXID(input: AddMXID!): User! @loggedIn
removeMXID(input: RemoveMXID!): User! @loggedIn
reconfigureRoom(input: RoomConfigUpdate!): Room! @loggedIn
createEntry(input: CreateEntry!): Entry! @loggedIn
commentEntry(input: CommentEntry!): Entry! @loggedIn
addToLists(input: AddToLists!): Entry! @loggedIn

View file

@ -13,6 +13,7 @@ import (
"github.com/Unkn0wnCat/matrix-veles/graph/generated"
"github.com/Unkn0wnCat/matrix-veles/graph/model"
"github.com/Unkn0wnCat/matrix-veles/internal/config"
"github.com/Unkn0wnCat/matrix-veles/internal/db"
model2 "github.com/Unkn0wnCat/matrix-veles/internal/db/model"
jwt "github.com/golang-jwt/jwt/v4"
@ -428,6 +429,10 @@ func (r *mutationResolver) RemoveMxid(ctx context.Context, input model.RemoveMxi
return model.MakeUser(user), nil
}
func (r *mutationResolver) ReconfigureRoom(ctx context.Context, input model.RoomConfigUpdate) (*model.Room, error) {
panic(fmt.Errorf("not implemented"))
}
func (r *mutationResolver) CreateEntry(ctx context.Context, input model.CreateEntry) (*model.Entry, error) {
user, err := GetUserFromContext(ctx)
if err != nil {
@ -900,6 +905,86 @@ func (r *queryResolver) Entries(ctx context.Context, first *int, after *string,
}, nil
}
func (r *queryResolver) Rooms(ctx context.Context, first *int, after *string, filter *model.RoomFilter) (*model.RoomConnection, error) {
userId, _ := GetUserIDFromContext(ctx)
if userId == nil {
userId = &primitive.ObjectID{}
}
dbFilter, dbSort, dbLimit, err := buildDBRoomFilter(first, after, filter, *userId)
if err != nil {
return nil, err
}
coll := db.Db.Collection(viper.GetString("bot.mongo.collection.rooms"))
newLimit := *dbLimit + 1
findOpts := options.FindOptions{
Limit: &newLimit,
Sort: *dbSort,
}
res, err := coll.Find(ctx, *dbFilter, &findOpts)
if err != nil {
return nil, errors.New("database error")
}
var rawEntries []config.RoomConfig
err = res.All(ctx, &rawEntries)
if err != nil {
return nil, errors.New("database error")
}
if len(rawEntries) == 0 {
return nil, errors.New("not found")
}
lastEntryI := len(rawEntries) - 1
if lastEntryI > 0 {
lastEntryI--
}
firstEntry := rawEntries[0]
lastEntry := rawEntries[lastEntryI]
isAfter := false
if after != nil {
isAfter = true
}
hasMore := false
if int64(len(rawEntries)) > *dbLimit {
hasMore = true
}
var edges []*model.RoomEdge
for i, rawRoom := range rawEntries {
if int64(i) == *dbLimit {
continue
}
edges = append(edges, &model.RoomEdge{
Node: model.MakeRoom(&rawRoom),
Cursor: rawRoom.ID.Hex(),
})
}
return &model.RoomConnection{
PageInfo: &model.PageInfo{
HasPreviousPage: isAfter,
HasNextPage: hasMore,
StartCursor: firstEntry.ID.Hex(),
EndCursor: lastEntry.ID.Hex(),
},
Edges: edges,
}, nil
}
func (r *queryResolver) User(ctx context.Context, id *string, username *string) (*model.User, error) {
if id != nil {
dbId, err := primitive.ObjectIDFromHex(*id)

View file

@ -100,6 +100,16 @@ func Run() {
// Set up async tasks
go startSync(matrixClient)
go doInitialUpdate(matrixClient)
go doAdminStateUpdate(matrixClient)
go func() {
ticker := time.NewTicker(5 * time.Minute)
for {
go doAdminStateUpdate(matrixClient)
<-ticker.C
}
}()
<-c
log.Printf("Shutting down...")
@ -178,6 +188,53 @@ func doInitialUpdate(matrixClient *mautrix.Client) {
config.RoomConfigInitialUpdate(resp.JoinedRooms, ctx)
}
func doAdminStateUpdate(matrixClient *mautrix.Client) {
ctx, span := tracer.Tracer.Start(tracer.Ctx, "admin_state_update")
defer span.End()
_, requestSpan := tracer.Tracer.Start(ctx, "request_joined_rooms")
resp, err := matrixClient.JoinedRooms()
if err != nil {
log.Printf("matrix-veles could not read joined rooms, something is horribly wrong")
log.Fatalln(err)
}
requestSpan.End()
for _, roomId := range resp.JoinedRooms {
_, processSpan := tracer.Tracer.Start(ctx, "process_room")
processSpan.SetAttributes(attribute.String("room_id", roomId.String()))
powerLevels, err := GetRoomPowerLevelState(matrixClient, roomId)
if err != nil {
log.Printf("Failed to get power levels for %s - %v", roomId.String(), err)
processSpan.RecordError(err)
processSpan.End()
continue
}
roomConfig := config.GetRoomConfig(roomId.String())
var admins []string
for user, powerLevel := range powerLevels.Users {
if powerLevel >= roomConfig.AdminPowerLevel {
admins = append(admins, user)
}
}
roomConfig = config.GetRoomConfig(roomId.String())
roomConfig.Admins = admins
err = config.SaveRoomConfig(&roomConfig)
if err != nil {
processSpan.RecordError(err)
processSpan.End()
continue
}
processSpan.End()
}
}
// handleMessageEvent wraps message handler taking the mautrix.Client and start timestamp as parameters
func handleMessageEvent(matrixClient *mautrix.Client, startTs int64) mautrix.EventHandler {
return func(source mautrix.EventSource, evt *event.Event) {

View file

@ -43,6 +43,8 @@ type RoomConfig struct {
// HashChecker contains configuration specific to the hash-checker
HashChecker HashCheckerConfig `bson:"hash_checker"`
Admins []string `bson:"admins"`
}
type HashCheckerConfig struct {