Add switch for enable/disable room

This commit is contained in:
Kevin Kandlbinder 2022-09-05 15:41:04 +02:00
parent ddefe682fa
commit d89a85dfb4
17 changed files with 554 additions and 8 deletions

8
.idea/misc.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SwUserDefinedSpecifications">
<option name="specTypeByUrl">
<map />
</option>
</component>
</project>

View file

@ -152,6 +152,7 @@ type ComplexityRoot struct {
Room struct {
Active func(childComplexity int) int
AdminPowerLevel func(childComplexity int) int
Deactivated func(childComplexity int) int
Debug func(childComplexity int) int
HashCheckerConfig func(childComplexity int) int
ID func(childComplexity int) int
@ -808,6 +809,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Room.AdminPowerLevel(childComplexity), true
case "Room.deactivated":
if e.complexity.Room.Deactivated == nil {
break
}
return e.complexity.Room.Deactivated(childComplexity), true
case "Room.debug":
if e.complexity.Room.Debug == nil {
break
@ -1079,6 +1087,7 @@ type HashCheckerConfig {
type Room {
id: ID!
active: Boolean!
deactivated: Boolean!
name: String!
roomId: String!
debug: Boolean!
@ -1341,6 +1350,7 @@ input RemoveMXID {
input RoomConfigUpdate {
id: ID!
deactivate: Boolean
debug: Boolean
adminPowerLevel: Int
hashChecker: HashCheckerConfigUpdate
@ -3995,6 +4005,8 @@ func (ec *executionContext) fieldContext_Mutation_reconfigureRoom(ctx context.Co
return ec.fieldContext_Room_id(ctx, field)
case "active":
return ec.fieldContext_Room_active(ctx, field)
case "deactivated":
return ec.fieldContext_Room_deactivated(ctx, field)
case "name":
return ec.fieldContext_Room_name(ctx, field)
case "roomId":
@ -4086,6 +4098,8 @@ func (ec *executionContext) fieldContext_Mutation_subscribeToList(ctx context.Co
return ec.fieldContext_Room_id(ctx, field)
case "active":
return ec.fieldContext_Room_active(ctx, field)
case "deactivated":
return ec.fieldContext_Room_deactivated(ctx, field)
case "name":
return ec.fieldContext_Room_name(ctx, field)
case "roomId":
@ -4177,6 +4191,8 @@ func (ec *executionContext) fieldContext_Mutation_unsubscribeFromList(ctx contex
return ec.fieldContext_Room_id(ctx, field)
case "active":
return ec.fieldContext_Room_active(ctx, field)
case "deactivated":
return ec.fieldContext_Room_deactivated(ctx, field)
case "name":
return ec.fieldContext_Room_name(ctx, field)
case "roomId":
@ -5380,6 +5396,8 @@ func (ec *executionContext) fieldContext_Query_room(ctx context.Context, field g
return ec.fieldContext_Room_id(ctx, field)
case "active":
return ec.fieldContext_Room_active(ctx, field)
case "deactivated":
return ec.fieldContext_Room_deactivated(ctx, field)
case "name":
return ec.fieldContext_Room_name(ctx, field)
case "roomId":
@ -5958,6 +5976,50 @@ func (ec *executionContext) fieldContext_Room_active(ctx context.Context, field
return fc, nil
}
func (ec *executionContext) _Room_deactivated(ctx context.Context, field graphql.CollectedField, obj *model.Room) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Room_deactivated(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Deactivated, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Room_deactivated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Room",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Boolean does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Room_name(ctx context.Context, field graphql.CollectedField, obj *model.Room) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Room_name(ctx, field)
if err != nil {
@ -6333,6 +6395,8 @@ func (ec *executionContext) fieldContext_RoomEdge_node(ctx context.Context, fiel
return ec.fieldContext_Room_id(ctx, field)
case "active":
return ec.fieldContext_Room_active(ctx, field)
case "deactivated":
return ec.fieldContext_Room_deactivated(ctx, field)
case "name":
return ec.fieldContext_Room_name(ctx, field)
case "roomId":
@ -9437,7 +9501,7 @@ func (ec *executionContext) unmarshalInputRoomConfigUpdate(ctx context.Context,
asMap[k] = v
}
fieldsInOrder := [...]string{"id", "debug", "adminPowerLevel", "hashChecker"}
fieldsInOrder := [...]string{"id", "deactivate", "debug", "adminPowerLevel", "hashChecker"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@ -9452,6 +9516,14 @@ func (ec *executionContext) unmarshalInputRoomConfigUpdate(ctx context.Context,
if err != nil {
return it, err
}
case "deactivate":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("deactivate"))
it.Deactivate, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
if err != nil {
return it, err
}
case "debug":
var err error
@ -10807,6 +10879,13 @@ func (ec *executionContext) _Room(ctx context.Context, sel ast.SelectionSet, obj
out.Values[i] = ec._Room_active(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "deactivated":
out.Values[i] = ec._Room_deactivated(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}

View file

@ -164,6 +164,7 @@ type RemoveMxid struct {
type RoomConfigUpdate struct {
ID string `json:"id"`
Deactivate *bool `json:"deactivate"`
Debug *bool `json:"debug"`
AdminPowerLevel *int `json:"adminPowerLevel"`
HashChecker *HashCheckerConfigUpdate `json:"hashChecker"`

View file

@ -5,6 +5,7 @@ import "github.com/Unkn0wnCat/matrix-veles/internal/config"
type Room struct {
ID string `json:"id"`
Active bool `json:"active"`
Deactivated bool `json:"deactivated"`
Name string `json:"name"`
RoomID string `json:"roomId"`
Debug bool `json:"debug"`
@ -28,7 +29,8 @@ func MakeRoom(room *config.RoomConfig) *Room {
return &Room{
ID: room.ID.Hex(),
Name: room.Name,
Active: room.Active,
Active: room.Active && !room.Deactivate,
Deactivated: room.Deactivate,
RoomID: room.RoomID,
Debug: room.Debug,
AdminPowerLevel: room.AdminPowerLevel,

View file

@ -46,6 +46,7 @@ type HashCheckerConfig {
type Room {
id: ID!
active: Boolean!
deactivated: Boolean!
name: String!
roomId: String!
debug: Boolean!
@ -308,6 +309,7 @@ input RemoveMXID {
input RoomConfigUpdate {
id: ID!
deactivate: Boolean
debug: Boolean
adminPowerLevel: Int
hashChecker: HashCheckerConfigUpdate

View file

@ -7,7 +7,6 @@ import (
"context"
"errors"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"math/big"
"strings"
"time"
@ -19,6 +18,7 @@ import (
model2 "github.com/Unkn0wnCat/matrix-veles/internal/db/model"
jwt "github.com/golang-jwt/jwt/v4"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@ -477,6 +477,10 @@ func (r *mutationResolver) ReconfigureRoom(ctx context.Context, input model.Room
rConfig.Debug = *input.Debug
}
if input.Deactivate != nil {
rConfig.Deactivate = *input.Deactivate
}
if input.HashChecker != nil {
if input.HashChecker.HashCheckMode != nil {
newMode := uint8(0)

View file

@ -80,6 +80,12 @@ func handleHashing(content *event.MessageEventContent, evt *event.Event, matrixC
// Fetch room configuration for adjusting behaviour
roomConfig := config.GetRoomConfig(evt.RoomID.String())
if !roomConfig.Active || roomConfig.Deactivate {
rcSpan.End()
span.AddEvent("Room is deactivated, returning")
return
}
rcSpan.End()
defer filesProcessed.Inc()

View file

@ -29,6 +29,9 @@ type RoomConfig struct {
// Active tells if the bot is active in this room (Set to false on leave/kick/ban)
Active bool `yaml:"active" bson:"active"`
// Deactivate can be set by an admin to disable the bot for a room
Deactivate bool `yaml:"deactivate" bson:"deactivate"`
// Name is fetched regularly from the room state
Name string `yaml:"name" bson:"name"`

View file

@ -46,6 +46,7 @@ type HashCheckerConfig {
type Room {
id: ID!
active: Boolean!
deactivated: Boolean!
name: String!
roomId: String!
debug: Boolean!
@ -308,6 +309,7 @@ input RemoveMXID {
input RoomConfigUpdate {
id: ID!
deactivate: Boolean
debug: Boolean
adminPowerLevel: Int
hashChecker: HashCheckerConfigUpdate

View file

@ -1,3 +1,67 @@
/*
Improved screen reader only CSS class
@author Gaël Poupard
@note Based on Yahoo!'s technique
@author Thierry Koblentz
@see https://developer.yahoo.com/blogs/ydn/clip-hidden-content-better-accessibility-53456.html
* 1.
@note `clip` is deprecated but works everywhere
@see https://developer.mozilla.org/en-US/docs/Web/CSS/clip
* 2.
@note `clip-path` is the future-proof version, but not very well supported yet
@see https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path
@see http://caniuse.com/#search=clip-path
@author Yvain Liechti
@see https://twitter.com/ryuran78/status/778943389819604992
* 3.
@note preventing text to be condensed
author J. Renée Beach
@see https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe
@note Drupal 8 goes with word-wrap: normal instead
@see https://www.drupal.org/node/2045151
@see http://cgit.drupalcode.org/drupal/commit/?id=5b847ea
* 4.
@note !important is important
@note Obviously you wanna hide something
@author Harry Roberts
@see https://csswizardry.com/2016/05/the-importance-of-important/
*/
.srOnly, .srOnlyFocusable {
border: 0 !important;
clip: rect(1px, 1px, 1px, 1px) !important; /* 1 */
-webkit-clip-path: inset(50%) !important;
clip-path: inset(50%) !important; /* 2 */
height: 1px !important;
margin: -1px !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important;
white-space: nowrap !important; /* 3 */
}
/*
Use in conjunction with .sr-only to only display content when it's focused.
@note Useful for skip links
@see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
@note Based on a HTML5 Boilerplate technique, included in Bootstrap
@note Fixed a bug with position: static on iOS 10.0.2 + VoiceOver
@author Sylvain Pigeard
@see https://github.com/twbs/bootstrap/issues/20732
*/
.srOnlyFocusable:focus,
.srOnlyFocusable:active {
clip: auto !important;
-webkit-clip-path: none !important;
clip-path: none !important;
height: auto !important;
margin: auto !important;
overflow: visible !important;
width: auto !important;
white-space: normal !important;
}
@mixin badges {
.badge {
padding: 2px var(--veles-layout-padding-slim);

View file

@ -0,0 +1,78 @@
@import "../../globals";
.toggle {
display: inline-flex;
flex-wrap: wrap;
align-items: center;
position: relative;
margin-bottom: 1em;
cursor: pointer;
gap: 1ch;
.toggleInput {
position: absolute;
opacity: 0;
width: 100%;
height: 100%;
}
.toggleDisplay {
--offset: 0.25em;
--diameter: 1.8em;
display: inline-flex;
align-items: center;
justify-content: space-around;
box-sizing: content-box;
width: calc(var(--diameter) * 2 + var(--offset) * 2);
height: calc(var(--diameter) + var(--offset) * 2);
border: 0;
position: relative;
border-radius: var(--veles-layout-border-radius);
background-color: var(--veles-color-background);
transition: 250ms;
&::before {
content: "";
z-index: 2;
position: absolute;
top: 50%;
left: var(--offset);
box-sizing: border-box;
width: var(--diameter);
height: var(--diameter);
border: 0.1em solid rgb(0 0 0 / 0.2);
border-radius: var(--veles-layout-border-radius);
background-color: white;
transform: translate(0, -50%);
will-change: transform;
transition: inherit;
}
}
&:focus .toggleDisplay, .toggleInput:focus + .toggleDisplay {
outline: 2px solid var(--veles-color-accent);
outline: 2px auto -webkit-focus-ring-color;
outline-offset: 2px;
}
&[disabled] .toggleDisplay,
.toggleInput:disabled + .toggleDisplay {
opacity: 0.6;
filter: grayscale(40%);
cursor: not-allowed;
}
&:focus, &:focus:not(:focus-visible) .toggleDisplay, .toggleInput:focus:not(:focus-visible) + .toggleDisplay {
outline: none;
}
&[aria-pressed="true"] .toggleDisplay,
.toggleInput:checked + .toggleDisplay {
background-color: var(--veles-color-accent);
&::before {
transform: translate(100%, -50%);
}
}
}

View file

@ -0,0 +1,24 @@
import React from "react";
import styles from "./ToggleButton.module.scss";
type ToggleButtonProps = {
name: string
label: string
labelSrOnly?: boolean
onChange?: React.ChangeEventHandler<HTMLInputElement>
disabled?: boolean
checked?: boolean
}
const ToggleButton = (props: ToggleButtonProps) => {
return <label className={styles.toggle} htmlFor={props.name}>
<input type='checkbox' name={props.name} id={props.name} className={styles.toggleInput} onChange={props.onChange} disabled={props.disabled} checked={props.checked} />
<span className={styles.toggleDisplay} hidden>
<span className={styles.spacer}/>I<span className={styles.spacer}/>O<span className={styles.spacer}/>
</span>
<span className={props.labelSrOnly ? styles.srOnly : ""}>{props.label}</span>
</label>;
}
export default ToggleButton

View file

@ -0,0 +1,6 @@
@import "../../../globals";
.title {
display: flex;
gap: var(--veles-layout-padding)
}

View file

@ -4,6 +4,10 @@ import {graphql} from "babel-plugin-relay/macro";
import {RoomDetailQuery, RoomDetailQuery$variables} from "./__generated__/RoomDetailQuery.graphql";
import {DisposeFn} from "relay-runtime";
import {useParams} from "react-router-dom";
import ToggleButton from "../../form_components/ToggleButton";
import styles from "./RoomDetail.module.scss";
import {useReconfigureRoomMutation} from "../../../mutations/ReconfigureRoomMutation";
type Props = {
initialQueryRef: PreloadedQuery<RoomDetailQuery> | null | undefined,
@ -16,12 +20,17 @@ type PropsFinal = {
}
const RoomDetailInner = ({initialQueryRef}: PropsFinal) => {
const [reconfigureRoom, reconfiguringRoom] = useReconfigureRoomMutation();
const data = usePreloadedQuery(
graphql`
query RoomDetailQuery($id: ID) {
room(id:$id) {
id
active
deactivated
adminPowerLevel
debug
name
@ -38,7 +47,20 @@ const RoomDetailInner = ({initialQueryRef}: PropsFinal) => {
)
return <>
<h1>Room Detail: {data.room?.name}</h1>
<span className={styles.title}>
<h1>{data.room?.name}</h1>
<ToggleButton name={"activeSwitch"} label={"Activate"} labelSrOnly={true} onChange={(ev) => {
reconfigureRoom({
variables: {
reconfigureInput: {
id: data.room?.id!,
deactivate: !ev.currentTarget.checked
}
}
})
}} disabled={reconfiguringRoom || ((data.room || false) && !data.room.active && !data.room.deactivated)} checked={data.room?.active}/>
</span>
<pre>{JSON.stringify(data, null, 2)}</pre>
</>

View file

@ -1,5 +1,5 @@
/**
* @generated SignedSource<<9574fe27c6b52038cbfdd4009ccdf08c>>
* @generated SignedSource<<58fc708823f0e42ade18c16f92c3b0d2>>
* @lightSyntaxTransform
* @nogrep
*/
@ -17,6 +17,7 @@ export type RoomDetailQuery$data = {
readonly room: {
readonly id: string;
readonly active: boolean;
readonly deactivated: boolean;
readonly adminPowerLevel: number;
readonly debug: boolean;
readonly name: string;
@ -70,6 +71,13 @@ v1 = [
"name": "active",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "deactivated",
"storageKey": null
},
{
"alias": null,
"args": null,
@ -152,16 +160,16 @@ return {
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "91fa63463870ca57128248af37619c8f",
"cacheID": "397376ecc93b3b405d66d01b7fd4de21",
"id": null,
"metadata": {},
"name": "RoomDetailQuery",
"operationKind": "query",
"text": "query RoomDetailQuery(\n $id: ID\n) {\n room(id: $id) {\n id\n active\n adminPowerLevel\n debug\n name\n roomId\n hashCheckerConfig {\n chatNotice\n hashCheckMode\n subscribedLists\n }\n }\n}\n"
"text": "query RoomDetailQuery(\n $id: ID\n) {\n room(id: $id) {\n id\n active\n deactivated\n adminPowerLevel\n debug\n name\n roomId\n hashCheckerConfig {\n chatNotice\n hashCheckMode\n subscribedLists\n }\n }\n}\n"
}
};
})();
(node as any).hash = "a825cd16719edd1498f1b34bef67b414";
(node as any).hash = "338b222f1e379335edc6847c401f52f5";
export default node;

View file

@ -0,0 +1,51 @@
import {
commitMutation
} from "relay-runtime";
import {graphql} from "babel-plugin-relay/macro";
import RelayEnvironment from "../RelayEnvironment";
import {
ReconfigureRoomMutation as ReconfigureRoomMutationType,
ReconfigureRoomMutation$data,
ReconfigureRoomMutation$variables
} from "./__generated__/ReconfigureRoomMutation.graphql";
import {useMutation} from "react-relay";
const mutation = graphql`
mutation ReconfigureRoomMutation($reconfigureInput: RoomConfigUpdate!) {
reconfigureRoom(input: $reconfigureInput) {
id
active
deactivated
name
roomId
debug
adminPowerLevel
hashCheckerConfig {
chatNotice
hashCheckMode
subscribedLists
}
}
}
`
export const useReconfigureRoomMutation = () => useMutation<ReconfigureRoomMutationType>(mutation)
const ReconfigureRoomMutation = (input: ReconfigureRoomMutation$variables): Promise<ReconfigureRoomMutation$data> => {
return new Promise<ReconfigureRoomMutation$data>((resolve, reject) => {
const variables: ReconfigureRoomMutation$variables = input
commitMutation<ReconfigureRoomMutationType>(
RelayEnvironment({}),
{
mutation: mutation,
variables,
onCompleted: resolve,
onError: reject
}
)
})
}
export default ReconfigureRoomMutation

View file

@ -0,0 +1,186 @@
/**
* @generated SignedSource<<c6a0a35dc80ab80f3fc8e42c6fe8a0f6>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest, Mutation } from 'relay-runtime';
export type HashCheckerMode = "NOTICE" | "DELETE" | "MUTE" | "BAN" | "%future added value";
export type RoomConfigUpdate = {
id: string;
deactivate?: boolean | null;
debug?: boolean | null;
adminPowerLevel?: number | null;
hashChecker?: HashCheckerConfigUpdate | null;
};
export type HashCheckerConfigUpdate = {
chatNotice?: boolean | null;
hashCheckMode?: HashCheckerMode | null;
};
export type ReconfigureRoomMutation$variables = {
reconfigureInput: RoomConfigUpdate;
};
export type ReconfigureRoomMutation$data = {
readonly reconfigureRoom: {
readonly id: string;
readonly active: boolean;
readonly deactivated: boolean;
readonly name: string;
readonly roomId: string;
readonly debug: boolean;
readonly adminPowerLevel: number;
readonly hashCheckerConfig: {
readonly chatNotice: boolean;
readonly hashCheckMode: HashCheckerMode;
readonly subscribedLists: ReadonlyArray<string> | null;
};
};
};
export type ReconfigureRoomMutation = {
variables: ReconfigureRoomMutation$variables;
response: ReconfigureRoomMutation$data;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "reconfigureInput"
}
],
v1 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "reconfigureInput"
}
],
"concreteType": "Room",
"kind": "LinkedField",
"name": "reconfigureRoom",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "active",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "deactivated",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "roomId",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "debug",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "adminPowerLevel",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "HashCheckerConfig",
"kind": "LinkedField",
"name": "hashCheckerConfig",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "chatNotice",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hashCheckMode",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "subscribedLists",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ReconfigureRoomMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ReconfigureRoomMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "4186d7d18c6230e79d890ca0043dc104",
"id": null,
"metadata": {},
"name": "ReconfigureRoomMutation",
"operationKind": "mutation",
"text": "mutation ReconfigureRoomMutation(\n $reconfigureInput: RoomConfigUpdate!\n) {\n reconfigureRoom(input: $reconfigureInput) {\n id\n active\n deactivated\n name\n roomId\n debug\n adminPowerLevel\n hashCheckerConfig {\n chatNotice\n hashCheckMode\n subscribedLists\n }\n }\n}\n"
}
};
})();
(node as any).hash = "e4ccf905922a56c92f44336d58c96a53";
export default node;