mirror of
https://github.com/Unkn0wnCat/matrix-veles.git
synced 2025-04-28 17:56:49 +02:00
api: Add Room query
This commit is contained in:
parent
0aa91fa99f
commit
cbd1aeb04a
8 changed files with 1639 additions and 6 deletions
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
39
graph/model/room.go
Normal 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,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue