{ "version": 3, "sources": ["../../../server/rooms.ts"], "sourcesContent": ["/**\r\n * Rooms\r\n * Pokemon Showdown - http://pokemonshowdown.com/\r\n *\r\n * Every chat room and battle is a room, and what they do is done in\r\n * rooms.ts. There's also a global room which every user is in, and\r\n * handles miscellaneous things like welcoming the user.\r\n *\r\n * `Rooms.rooms` is the global table of all rooms, a `Map` of `RoomID:Room`.\r\n * Rooms should normally be accessed with `Rooms.get(roomid)`.\r\n *\r\n * All rooms extend `BasicRoom`, whose important properties like `.users`\r\n * and `.game` are documented near the the top of its class definition.\r\n *\r\n * @license MIT\r\n */\r\n\r\nconst ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'.split('');\r\n\r\nconst TIMEOUT_EMPTY_DEALLOCATE = 10 * 60 * 1000;\r\nconst TIMEOUT_INACTIVE_DEALLOCATE = 40 * 60 * 1000;\r\nconst REPORT_USER_STATS_INTERVAL = 10 * 60 * 1000;\r\nconst MAX_CHATROOM_ID_LENGTH = 225;\r\n\r\nconst CRASH_REPORT_THROTTLE = 60 * 60 * 1000;\r\n\r\nconst LAST_BATTLE_WRITE_THROTTLE = 10;\r\n\r\nconst RETRY_AFTER_LOGIN = null;\r\n\r\nimport {FS, Utils, Streams, PostgresDatabase} from '../lib';\r\nimport {RoomSection, RoomSections} from './chat-commands/room-settings';\r\nimport {QueuedHunt} from './chat-plugins/scavengers';\r\nimport {ScavengerGameTemplate} from './chat-plugins/scavenger-games';\r\nimport {RepeatedPhrase} from './chat-plugins/repeats';\r\nimport {\r\n\tPM as RoomBattlePM, RoomBattle, RoomBattlePlayer, RoomBattleTimer, RoomBattleOptions, PlayerIndex,\r\n} from \"./room-battle\";\r\nimport {RoomGame, SimpleRoomGame, RoomGamePlayer} from './room-game';\r\nimport {MinorActivity, MinorActivityData} from './room-minor-activity';\r\nimport {Roomlogs} from './roomlogs';\r\nimport * as crypto from 'crypto';\r\nimport {RoomAuth} from './user-groups';\r\nimport {PartialModlogEntry, mainModlog} from './modlog';\r\n\r\n/*********************************************************\r\n * the Room object.\r\n *********************************************************/\r\n\r\ninterface MuteEntry {\r\n\tuserid: ID;\r\n\ttime: number;\r\n\tguestNum: number;\r\n\tautoconfirmed: string;\r\n}\r\n\r\ninterface ChatRoomTable {\r\n\ttitle: string;\r\n\tdesc: string;\r\n\tuserCount: number;\r\n\tsection?: string;\r\n\tsubRooms?: string[];\r\n\tspotlight?: string;\r\n\tprivacy: RoomSettings['isPrivate'];\r\n}\r\n\r\ninterface ShowRequest {\r\n\tname: string;\r\n\tlink: string;\r\n\tcomment: string;\r\n\tdimensions?: [number, number, boolean];\r\n}\r\n\r\ninterface BattleRoomTable {\r\n\tp1?: string;\r\n\tp2?: string;\r\n\tminElo?: 'tour' | number;\r\n}\r\n\r\ninterface UserTable {\r\n\t[userid: string]: User;\r\n}\r\n\r\nexport interface RoomSettings {\r\n\ttitle: string;\r\n\tauth: {[userid: string]: GroupSymbol};\r\n\tcreationTime: number;\r\n\tsection?: RoomSection;\r\n\r\n\treadonly autojoin?: boolean;\r\n\taliases?: string[];\r\n\tbanwords?: string[];\r\n\tisPrivate?: PrivacySetting;\r\n\tmodjoin?: AuthLevel | true | null;\r\n\tmodchat?: AuthLevel | null;\r\n\tstaffRoom?: boolean;\r\n\tlanguage?: ID | false;\r\n\tslowchat?: number | false;\r\n\tevents?: {[k: string]: RoomEvent | RoomEventAlias | RoomEventCategory};\r\n\tfilterStretching?: boolean;\r\n\tfilterEmojis?: boolean;\r\n\tfilterCaps?: boolean;\r\n\tfilterLinks?: boolean;\r\n\tjeopardyDisabled?: boolean;\r\n\tmafiaDisabled?: boolean;\r\n\tunoDisabled?: boolean;\r\n\tblackjackDisabled?: boolean;\r\n\thangmanDisabled?: boolean;\r\n\tgameNumber?: number;\r\n\thighTraffic?: boolean;\r\n\tspotlight?: string;\r\n\tparentid?: string | null;\r\n\tdesc?: string | null;\r\n\tintroMessage?: string | null;\r\n\tstaffMessage?: string | null;\r\n\trulesLink?: string | null;\r\n\tdataCommandTierDisplay?: 'tiers' | 'doubles tiers' | 'National Dex tiers' | 'numbers';\r\n\trequestShowEnabled?: boolean | null;\r\n\tpermissions?: {[k: string]: GroupSymbol};\r\n\tminorActivity?: PollData | AnnouncementData;\r\n\tminorActivityQueue?: MinorActivityData[];\r\n\trepeats?: RepeatedPhrase[];\r\n\tautoModchat?: {\r\n\t\trank: GroupSymbol,\r\n\t\ttime: number,\r\n\t\t// stores previous modchat setting. if true, modchat was fully off\r\n\t\tactive: boolean | AuthLevel,\r\n\t};\r\n\ttournaments?: TournamentRoomSettings;\r\n\tdefaultFormat?: string;\r\n\r\n\tscavSettings?: AnyObject;\r\n\tscavQueue?: QueuedHunt[];\r\n\r\n\t// should not ever be saved because they're inapplicable to persistent rooms\r\n\t/** This includes groupchats, battles, and help-ticket rooms. */\r\n\tisPersonal?: boolean;\r\n\tisHelp?: boolean;\r\n\tnoLogTimes?: boolean;\r\n\tnoAutoTruncate?: boolean;\r\n\tisMultichannel?: boolean;\r\n}\r\n\r\nexport type MessageHandler = (room: BasicRoom, message: string) => void;\r\nexport type Room = GameRoom | ChatRoom;\r\nexport type PrivacySetting = boolean | 'hidden' | 'voice' | 'unlisted';\r\n\r\nimport type {AnnouncementData} from './chat-plugins/announcements';\r\nimport type {PollData} from './chat-plugins/poll';\r\nimport type {AutoResponder} from './chat-plugins/responder';\r\nimport type {RoomEvent, RoomEventAlias, RoomEventCategory} from './chat-plugins/room-events';\r\nimport type {Tournament, TournamentRoomSettings} from './tournaments/index';\r\n\r\nexport abstract class BasicRoom {\r\n\t/** to rename use room.rename */\r\n\treadonly roomid: RoomID;\r\n\ttitle: string;\r\n\treadonly type: 'chat' | 'battle';\r\n\treadonly users: UserTable;\r\n\t/**\r\n\t * Scrollback log. This is the log that's sent to users when\r\n\t * joining the room. Should roughly match what's on everyone's\r\n\t * screen.\r\n\t */\r\n\treadonly log: Roomlog;\r\n\t/**\r\n\t * The room's current RoomGame, if it exists. Each room can have 0 to 2\r\n\t * `RoomGame`s, and `this.game.room === this`.\r\n\t * Rooms may also have an additional game in `this.subGame`.\r\n\t * However, `subGame`s do not update `user.game`.\r\n\t */\r\n\tgame: RoomGame | null;\r\n\tsubGame: RoomGame | null;\r\n\t/**\r\n\t * The room's current battle. Battles are a type of RoomGame, so in battle\r\n\t * rooms (which can only be `GameRoom`s), `this.battle === this.game`.\r\n\t * In all other rooms, `this.battle` is `null`.\r\n\t */\r\n\tbattle: RoomBattle | null;\r\n\t/**\r\n\t * The game room's current tournament. If the room is a battle room whose\r\n\t * battle is part of a tournament, `this.tour === this.parent.game`.\r\n\t * In all other rooms, `this.tour` is `null`.\r\n\t */\r\n\ttour: Tournament | null;\r\n\r\n\tauth: RoomAuth;\r\n\t/** use `setParent` to set this */\r\n\treadonly parent: Room | null;\r\n\t/** use `subroom.setParent` to set this, or `clearSubRooms` to clear it */\r\n\treadonly subRooms: ReadonlyMap | null;\r\n\r\n\treadonly muteQueue: MuteEntry[];\r\n\tuserCount: number;\r\n\tactive: boolean;\r\n\tmuteTimer: NodeJS.Timer | null;\r\n\tmodchatTimer: NodeJS.Timer | null;\r\n\tlastUpdate: number;\r\n\tlastBroadcast: string;\r\n\tlastBroadcastTime: number;\r\n\tsettings: RoomSettings;\r\n\t/** If true, this room's settings will be saved in config/chatrooms.json, allowing it to stay past restarts. */\r\n\tpersist: boolean;\r\n\r\n\tscavgame: ScavengerGameTemplate | null;\r\n\tscavLeaderboard: AnyObject;\r\n\tresponder?: AutoResponder | null;\r\n\tprivacySetter?: Set | null;\r\n\thideReplay: boolean;\r\n\r\n\treportJoins: boolean;\r\n\tbatchJoins: number;\r\n\treportJoinsInterval: NodeJS.Timer | null;\r\n\r\n\tminorActivity: MinorActivity | null;\r\n\tminorActivityQueue: MinorActivityData[] | null;\r\n\tbanwordRegex: RegExp | true | null;\r\n\tlogUserStatsInterval: NodeJS.Timer | null;\r\n\texpireTimer: NodeJS.Timer | null;\r\n\tuserList: string;\r\n\tpendingApprovals: Map | null;\r\n\r\n\tmessagesSent: number;\r\n\t/**\r\n\t * These handlers will be invoked every n messages.\r\n\t * handler:number-of-messages map\r\n\t */\r\n\tnthMessageHandlers: Map;\r\n\r\n\tconstructor(roomid: RoomID, title?: string, options: Partial = {}) {\r\n\t\tthis.users = Object.create(null);\r\n\t\tthis.type = 'chat';\r\n\t\tthis.muteQueue = [];\r\n\r\n\t\tthis.battle = null;\r\n\t\tthis.game = null;\r\n\t\tthis.subGame = null;\r\n\t\tthis.tour = null;\r\n\r\n\t\tthis.roomid = roomid;\r\n\t\tthis.title = (title || roomid);\r\n\t\tthis.parent = null;\r\n\r\n\t\tthis.userCount = 0;\r\n\r\n\t\tthis.game = null;\r\n\t\tthis.active = false;\r\n\r\n\t\tthis.muteTimer = null;\r\n\r\n\t\tthis.lastUpdate = 0;\r\n\t\tthis.lastBroadcast = '';\r\n\t\tthis.lastBroadcastTime = 0;\r\n\r\n\t\t// room settings\r\n\r\n\t\tthis.settings = {\r\n\t\t\ttitle: this.title,\r\n\t\t\tauth: Object.create(null),\r\n\t\t\tcreationTime: Date.now(),\r\n\t\t};\r\n\t\tthis.persist = false;\r\n\t\tthis.hideReplay = false;\r\n\t\tthis.subRooms = null;\r\n\t\tthis.scavgame = null;\r\n\t\tthis.scavLeaderboard = {};\r\n\t\tthis.auth = new RoomAuth(this);\r\n\r\n\t\tthis.reportJoins = true;\r\n\t\tthis.batchJoins = 0;\r\n\t\tthis.reportJoinsInterval = null;\r\n\r\n\t\toptions.title = this.title;\r\n\t\tif (options.isHelp) options.noAutoTruncate = true;\r\n\t\tthis.reportJoins = !!(Config.reportjoins || options.isPersonal);\r\n\t\tthis.batchJoins = options.isPersonal ? 0 : Config.reportjoinsperiod || 0;\r\n\t\tif (!options.auth) options.auth = {};\r\n\r\n\t\tthis.log = Roomlogs.create(this, options);\r\n\r\n\t\tthis.banwordRegex = null;\r\n\r\n\t\tthis.settings = options as RoomSettings;\r\n\t\tif (!this.settings.creationTime) this.settings.creationTime = Date.now();\r\n\t\tthis.auth.load();\r\n\r\n\t\tif (!options.isPersonal) this.persist = true;\r\n\r\n\t\tthis.minorActivity = null;\r\n\t\tthis.minorActivityQueue = null;\r\n\t\tif (options.parentid) {\r\n\t\t\tthis.setParent(Rooms.get(options.parentid) || null);\r\n\t\t}\r\n\r\n\t\tthis.subRooms = null;\r\n\r\n\t\tthis.active = false;\r\n\t\tthis.muteTimer = null;\r\n\t\tthis.modchatTimer = null;\r\n\r\n\t\tthis.logUserStatsInterval = null;\r\n\t\tthis.expireTimer = null;\r\n\t\tif (Config.logchat) {\r\n\t\t\tthis.roomlog('NEW CHATROOM: ' + this.roomid);\r\n\t\t\tif (Config.loguserstats) {\r\n\t\t\t\tthis.logUserStatsInterval = setInterval(() => this.logUserStats(), Config.loguserstats);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.userList = '';\r\n\t\tif (this.batchJoins) {\r\n\t\t\tthis.userList = this.getUserList();\r\n\t\t}\r\n\t\tthis.pendingApprovals = null;\r\n\t\tthis.messagesSent = 0;\r\n\t\tthis.nthMessageHandlers = new Map();\r\n\t\tthis.tour = null;\r\n\t\tthis.game = null;\r\n\t\tthis.battle = null;\r\n\t\tthis.validateTitle(this.title, this.roomid);\r\n\t}\r\n\r\n\ttoString() {\r\n\t\treturn this.roomid;\r\n\t}\r\n\r\n\t/**\r\n\t * Send a room message to all users in the room, without recording it\r\n\t * in the scrollback log.\r\n\t */\r\n\tsend(message: string) {\r\n\t\tif (this.roomid !== 'lobby') message = '>' + this.roomid + '\\n' + message;\r\n\t\tif (this.userCount) Sockets.roomBroadcast(this.roomid, message);\r\n\t}\r\n\tsendMods(data: string) {\r\n\t\tthis.sendRankedUsers(data, '*');\r\n\t}\r\n\tsendRankedUsers(data: string, minRank: GroupSymbol = '+') {\r\n\t\tif (this.settings.staffRoom) {\r\n\t\t\tif (!this.log) throw new Error(`Staff room ${this.roomid} has no log`);\r\n\t\t\tthis.log.add(data);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tfor (const i in this.users) {\r\n\t\t\tconst user = this.users[i];\r\n\t\t\t// hardcoded for performance reasons (this is an inner loop)\r\n\t\t\tif (user.isStaff || this.auth.atLeast(user, minRank)) {\r\n\t\t\t\tuser.sendTo(this, data);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\t/**\r\n\t * Send a room message to a single user.\r\n\t */\r\n\tsendUser(user: User | Connection, message: string) {\r\n\t\tuser.sendTo(this, message);\r\n\t}\r\n\t/**\r\n\t * Add a room message to the room log, so it shows up in the room\r\n\t * for everyone, and appears in the scrollback for new users who\r\n\t * join.\r\n\t */\r\n\tadd(message: string) {\r\n\t\tthis.log.add(message);\r\n\t\treturn this;\r\n\t}\r\n\troomlog(message: string) {\r\n\t\tthis.log.roomlog(message);\r\n\t\treturn this;\r\n\t}\r\n\t/**\r\n\t * Writes an entry to the modlog for that room, and the global modlog if entry.isGlobal is true.\r\n\t */\r\n\tmodlog(entry: PartialModlogEntry) {\r\n\t\tconst override = this.tour ? `${this.roomid} tournament: ${this.tour.roomid}` : undefined;\r\n\t\tthis.log.modlog(entry, override);\r\n\t\treturn this;\r\n\t}\r\n\tuhtmlchange(name: string, message: string) {\r\n\t\tthis.log.uhtmlchange(name, message);\r\n\t}\r\n\tattributedUhtmlchange(user: User, name: string, message: string) {\r\n\t\tthis.log.attributedUhtmlchange(user, name, message);\r\n\t}\r\n\thideText(userids: ID[], lineCount = 0, hideRevealButton?: boolean) {\r\n\t\tconst cleared = this.log.clearText(userids, lineCount);\r\n\t\tfor (const userid of cleared) {\r\n\t\t\tthis.send(`|hidelines|${hideRevealButton ? 'delete' : 'hide'}|${userid}|${lineCount}`);\r\n\t\t}\r\n\t\tthis.update();\r\n\t}\r\n\t/**\r\n\t * Inserts (sanitized) HTML into the room log.\r\n\t */\r\n\taddRaw(message: string) {\r\n\t\treturn this.add('|raw|' + message);\r\n\t}\r\n\t/**\r\n\t * Inserts some text into the room log, attributed to user. The\r\n\t * attribution will not appear, and is used solely as a hint not to\r\n\t * highlight the user.\r\n\t */\r\n\taddByUser(user: User, text: string): this {\r\n\t\treturn this.add('|c|' + user.getIdentity(this) + '|/log ' + text);\r\n\t}\r\n\t/**\r\n\t * Like addByUser, but without logging\r\n\t */\r\n\tsendByUser(user: User | null, text: string) {\r\n\t\tthis.send('|c|' + (user ? user.getIdentity(this) : '&') + '|/log ' + text);\r\n\t}\r\n\t/**\r\n\t * Like addByUser, but sends to mods only.\r\n\t */\r\n\tsendModsByUser(user: User, text: string) {\r\n\t\tthis.sendMods('|c|' + user.getIdentity(this) + '|/log ' + text);\r\n\t}\r\n\tupdate() {\r\n\t\tif (!this.log.broadcastBuffer.length) return;\r\n\t\tif (this.reportJoinsInterval) {\r\n\t\t\tclearInterval(this.reportJoinsInterval);\r\n\t\t\tthis.reportJoinsInterval = null;\r\n\t\t\tthis.userList = this.getUserList();\r\n\t\t}\r\n\t\tthis.send(this.log.broadcastBuffer.join('\\n'));\r\n\t\tthis.log.broadcastBuffer = [];\r\n\t\tthis.log.truncate();\r\n\r\n\t\tthis.pokeExpireTimer();\r\n\t}\r\n\r\n\tgetUserList() {\r\n\t\tlet buffer = '';\r\n\t\tlet counter = 0;\r\n\t\tfor (const i in this.users) {\r\n\t\t\tif (!this.users[i].named) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tcounter++;\r\n\t\t\tbuffer += ',' + this.users[i].getIdentityWithStatus(this);\r\n\t\t}\r\n\t\tconst msg = `|users|${counter}${buffer}`;\r\n\t\treturn msg;\r\n\t}\r\n\r\n\tnextGameNumber() {\r\n\t\tconst gameNumber = (this.settings.gameNumber || 0) + 1;\r\n\t\tthis.settings.gameNumber = gameNumber;\r\n\t\tthis.saveSettings();\r\n\t\treturn gameNumber;\r\n\t}\r\n\r\n\t// mute handling\r\n\r\n\trunMuteTimer(forceReschedule = false) {\r\n\t\tif (forceReschedule && this.muteTimer) {\r\n\t\t\tclearTimeout(this.muteTimer);\r\n\t\t\tthis.muteTimer = null;\r\n\t\t}\r\n\t\tif (this.muteTimer || this.muteQueue.length === 0) return;\r\n\r\n\t\tconst timeUntilExpire = this.muteQueue[0].time - Date.now();\r\n\t\tif (timeUntilExpire <= 1000) { // one second of leeway\r\n\t\t\tthis.unmute(this.muteQueue[0].userid, \"Your mute in '\" + this.title + \"' has expired.\");\r\n\t\t\t// runMuteTimer() is called again in unmute() so this function instance should be closed\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tthis.muteTimer = setTimeout(() => {\r\n\t\t\tthis.muteTimer = null;\r\n\t\t\tthis.runMuteTimer(true);\r\n\t\t}, timeUntilExpire);\r\n\t}\r\n\tisMuted(user: User): ID | undefined {\r\n\t\tif (!user) return;\r\n\t\tif (this.muteQueue) {\r\n\t\t\tfor (const entry of this.muteQueue) {\r\n\t\t\t\tif (user.id === entry.userid ||\r\n\t\t\t\t\tuser.guestNum === entry.guestNum ||\r\n\t\t\t\t\t(user.autoconfirmed && user.autoconfirmed === entry.autoconfirmed)) {\r\n\t\t\t\t\tif (entry.time - Date.now() < 0) {\r\n\t\t\t\t\t\tthis.unmute(user.id);\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\treturn entry.userid;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (this.parent) return this.parent.isMuted(user);\r\n\t}\r\n\tgetMuteTime(user: User): number | undefined {\r\n\t\tconst userid = this.isMuted(user);\r\n\t\tif (!userid) return;\r\n\t\tfor (const entry of this.muteQueue) {\r\n\t\t\tif (userid === entry.userid) {\r\n\t\t\t\treturn entry.time - Date.now();\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (this.parent) return this.parent.getMuteTime(user);\r\n\t}\r\n\t// I think putting the `new` before the signature is confusing the linter\r\n\t// eslint-disable-next-line @typescript-eslint/type-annotation-spacing\r\n\tgetGame(constructor: new (...args: any[]) => T, subGame = false): T | null {\r\n\t\t// TODO: switch to `static readonly gameid` when all game files are TypeScripted\r\n\t\tif (subGame && this.subGame && this.subGame.constructor.name === constructor.name) return this.subGame as T;\r\n\t\tif (this.game && this.game.constructor.name === constructor.name) return this.game as T;\r\n\t\treturn null;\r\n\t}\r\n\tgetMinorActivity(constructor: new (...args: any[]) => T): T | null {\r\n\t\tif (this.minorActivity?.constructor.name === constructor.name) return this.minorActivity as T;\r\n\t\treturn null;\r\n\t}\r\n\tgetMinorActivityQueue(settings = false): MinorActivityData[] | null {\r\n\t\tconst usedQueue = settings ? this.settings.minorActivityQueue : this.minorActivityQueue;\r\n\t\tif (!usedQueue?.length) return null;\r\n\t\treturn usedQueue;\r\n\t}\r\n\tqueueMinorActivity(activity: MinorActivityData): void {\r\n\t\tif (!this.minorActivityQueue) this.minorActivityQueue = [];\r\n\t\tthis.minorActivityQueue.push(activity);\r\n\t\tthis.settings.minorActivityQueue = this.minorActivityQueue;\r\n\t}\r\n\tclearMinorActivityQueue(slot?: number, depth = 1) {\r\n\t\tif (!this.minorActivityQueue) return;\r\n\t\tif (slot === undefined) {\r\n\t\t\tthis.minorActivityQueue = null;\r\n\t\t\tdelete this.settings.minorActivityQueue;\r\n\t\t\tthis.saveSettings();\r\n\t\t} else {\r\n\t\t\tthis.minorActivityQueue.splice(slot, depth);\r\n\t\t\tthis.settings.minorActivityQueue = this.minorActivityQueue;\r\n\t\t\tthis.saveSettings();\r\n\t\t\tif (!this.minorActivityQueue.length) this.clearMinorActivityQueue();\r\n\t\t}\r\n\t}\r\n\tsetMinorActivity(activity: MinorActivity | null, noDisplay = false): void {\r\n\t\tthis.minorActivity?.endTimer();\r\n\t\tthis.minorActivity = activity;\r\n\t\tif (this.minorActivity) {\r\n\t\t\tthis.minorActivity.save();\r\n\t\t\tif (!noDisplay) this.minorActivity.display();\r\n\t\t} else {\r\n\t\t\tdelete this.settings.minorActivity;\r\n\t\t\tthis.saveSettings();\r\n\t\t}\r\n\t}\r\n\tsaveSettings() {\r\n\t\tif (!this.persist) return;\r\n\r\n\t\tif (!Rooms.global) return; // during initialization\r\n\r\n\t\tRooms.global.writeChatRoomData();\r\n\t}\r\n\tcheckModjoin(user: User) {\r\n\t\tif (user.id in this.users) return true;\r\n\t\tif (!this.settings.modjoin) return true;\r\n\t\t// users with a room rank can always join\r\n\t\tif (this.auth.has(user.id)) return true;\r\n\r\n\t\tconst modjoinSetting = this.settings.modjoin !== true ? this.settings.modjoin : this.settings.modchat;\r\n\t\tif (!modjoinSetting) return true;\r\n\t\tif (!Users.Auth.isAuthLevel(modjoinSetting)) {\r\n\t\t\tMonitor.error(`Invalid modjoin setting in ${this.roomid}: ${modjoinSetting}`);\r\n\t\t}\r\n\t\treturn (\r\n\t\t\tthis.auth.atLeast(user, modjoinSetting) || Users.globalAuth.atLeast(user, modjoinSetting)\r\n\t\t);\r\n\t}\r\n\tmute(user: User, setTime?: number) {\r\n\t\tconst userid = user.id;\r\n\r\n\t\tif (!setTime) setTime = 7 * 60000; // default time: 7 minutes\r\n\t\tif (setTime > 90 * 60000) setTime = 90 * 60000; // limit 90 minutes\r\n\r\n\t\t// If the user is already muted, the existing queue position for them should be removed\r\n\t\tif (this.isMuted(user)) this.unmute(userid);\r\n\r\n\t\t// Place the user in a queue for the unmute timer\r\n\t\tfor (let i = 0; i <= this.muteQueue.length; i++) {\r\n\t\t\tconst time = Date.now() + setTime;\r\n\t\t\tif (i === this.muteQueue.length || time < this.muteQueue[i].time) {\r\n\t\t\t\tconst entry = {\r\n\t\t\t\t\tuserid,\r\n\t\t\t\t\ttime,\r\n\t\t\t\t\tguestNum: user.guestNum,\r\n\t\t\t\t\tautoconfirmed: user.autoconfirmed,\r\n\t\t\t\t};\r\n\t\t\t\tthis.muteQueue.splice(i, 0, entry);\r\n\t\t\t\t// The timer needs to be switched to the new entry if it is to be unmuted\r\n\t\t\t\t// before the entry the timer is currently running for\r\n\t\t\t\tif (i === 0 && this.muteTimer) {\r\n\t\t\t\t\tclearTimeout(this.muteTimer);\r\n\t\t\t\t\tthis.muteTimer = null;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\tthis.runMuteTimer();\r\n\r\n\t\tuser.updateIdentity();\r\n\r\n\t\tif (!(this.settings.isPrivate === true || this.settings.isPersonal)) {\r\n\t\t\tvoid Punishments.monitorRoomPunishments(user);\r\n\t\t}\r\n\r\n\t\treturn userid;\r\n\t}\r\n\tunmute(userid: string, notifyText?: string) {\r\n\t\tlet successUserid = '';\r\n\t\tconst user = Users.get(userid);\r\n\t\tlet autoconfirmed = '';\r\n\t\tif (user) {\r\n\t\t\tuserid = user.id;\r\n\t\t\tautoconfirmed = user.autoconfirmed;\r\n\t\t}\r\n\r\n\t\tfor (const [i, entry] of this.muteQueue.entries()) {\r\n\t\t\tif (entry.userid === userid ||\r\n\t\t\t\t(user && entry.guestNum === user.guestNum) ||\r\n\t\t\t\t(autoconfirmed && entry.autoconfirmed === autoconfirmed)) {\r\n\t\t\t\tif (i === 0) {\r\n\t\t\t\t\tthis.muteQueue.splice(0, 1);\r\n\t\t\t\t\tthis.runMuteTimer(true);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthis.muteQueue.splice(i, 1);\r\n\t\t\t\t}\r\n\t\t\t\tsuccessUserid = entry.userid;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (user && successUserid && userid in this.users) {\r\n\t\t\tuser.updateIdentity();\r\n\t\t\tif (notifyText) user.popup(notifyText);\r\n\t\t}\r\n\t\treturn successUserid;\r\n\t}\r\n\r\n\tlogUserStats() {\r\n\t\tlet total = 0;\r\n\t\tlet guests = 0;\r\n\t\tconst groups: {[k: string]: number} = {};\r\n\t\tfor (const group of Config.groupsranking) {\r\n\t\t\tgroups[group] = 0;\r\n\t\t}\r\n\t\tfor (const i in this.users) {\r\n\t\t\tconst user = this.users[i];\r\n\t\t\t++total;\r\n\t\t\tif (!user.named) {\r\n\t\t\t\t++guests;\r\n\t\t\t}\r\n\t\t\t++groups[this.auth.get(user.id)];\r\n\t\t}\r\n\t\tlet entry = '|userstats|total:' + total + '|guests:' + guests;\r\n\t\tfor (const i in groups) {\r\n\t\t\tentry += '|' + i + ':' + groups[i];\r\n\t\t}\r\n\t\tthis.roomlog(entry);\r\n\t}\r\n\r\n\tpokeExpireTimer() {\r\n\t\tif (this.expireTimer) clearTimeout(this.expireTimer);\r\n\t\tif (this.settings.isPersonal) {\r\n\t\t\tthis.expireTimer = setTimeout(() => this.expire(), TIMEOUT_INACTIVE_DEALLOCATE);\r\n\t\t} else {\r\n\t\t\tthis.expireTimer = null;\r\n\t\t}\r\n\t}\r\n\texpire() {\r\n\t\tthis.send('|expire|');\r\n\t\tthis.destroy();\r\n\t}\r\n\treportJoin(type: 'j' | 'l' | 'n', entry: string, user: User) {\r\n\t\tconst canTalk = this.auth.atLeast(user, this.settings.modchat ?? 'unlocked') && !this.isMuted(user);\r\n\t\tif (this.reportJoins && (canTalk || this.auth.has(user.id))) {\r\n\t\t\tthis.add(`|${type}|${entry}`).update();\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tlet ucType = '';\r\n\t\tswitch (type) {\r\n\t\tcase 'j': ucType = 'J'; break;\r\n\t\tcase 'l': ucType = 'L'; break;\r\n\t\tcase 'n': ucType = 'N'; break;\r\n\t\t}\r\n\t\tentry = `|${ucType}|${entry}`;\r\n\t\tif (this.batchJoins) {\r\n\t\t\tthis.log.broadcastBuffer.push(entry);\r\n\r\n\t\t\tif (!this.reportJoinsInterval) {\r\n\t\t\t\tthis.reportJoinsInterval = setTimeout(\r\n\t\t\t\t\t() => this.update(), this.batchJoins\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tthis.send(entry);\r\n\t\t}\r\n\t\tthis.roomlog(entry);\r\n\t}\r\n\tgetIntroMessage(user: User) {\r\n\t\tlet message = Utils.html`\\n|raw|
You joined ${this.title}`;\r\n\t\tif (this.settings.modchat) {\r\n\t\t\tmessage += ` [${this.settings.modchat} or higher to talk]`;\r\n\t\t}\r\n\t\tif (this.settings.modjoin) {\r\n\t\t\tconst modjoin = this.settings.modjoin === true ? this.settings.modchat : this.settings.modjoin;\r\n\t\t\tmessage += ` [${modjoin} or higher to join]`;\r\n\t\t}\r\n\t\tif (this.settings.slowchat) {\r\n\t\t\tmessage += ` [Slowchat ${this.settings.slowchat}s]`;\r\n\t\t}\r\n\t\tmessage += `
`;\r\n\t\tif (this.settings.introMessage) {\r\n\t\t\tmessage += `\\n|raw|
` +\r\n\t\t\t\tthis.settings.introMessage.replace(/\\n/g, '') +\r\n\t\t\t\t`
`;\r\n\t\t}\r\n\t\tconst staffIntro = this.getStaffIntroMessage(user);\r\n\t\tif (staffIntro) message += `\\n${staffIntro}`;\r\n\t\treturn message;\r\n\t}\r\n\tgetStaffIntroMessage(user: User) {\r\n\t\tif (!user.can('mute', null, this)) return ``;\r\n\t\tlet message = ``;\r\n\t\tif (this.settings.staffMessage) {\r\n\t\t\tmessage += `\\n|raw|
(Staff intro:)
` +\r\n\t\t\t\tthis.settings.staffMessage.replace(/\\n/g, '') +\r\n\t\t\t\t`
`;\r\n\t\t}\r\n\t\tif (this.pendingApprovals?.size) {\r\n\t\t\tmessage += `\\n|raw|
`;\r\n\t\t\tmessage += `
(Pending media requests: ${this.pendingApprovals.size})`;\r\n\t\t\tfor (const [userid, entry] of this.pendingApprovals) {\r\n\t\t\t\tmessage += `
`;\r\n\t\t\t\tmessage += `Requester ID: ${userid}
`;\r\n\t\t\t\tif (entry.dimensions) {\r\n\t\t\t\t\tconst [width, height, resized] = entry.dimensions;\r\n\t\t\t\t\tmessage += `Link:

`;\r\n\t\t\t\t\tif (resized) message += `(Resized)
`;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tmessage += `Link:
Link
`;\r\n\t\t\t\t}\r\n\t\t\t\tmessage += `Comment: ${entry.comment ? entry.comment : 'None.'}
`;\r\n\t\t\t\tmessage += `` +\r\n\t\t\t\t`
`;\r\n\t\t\t\tmessage += `
`;\r\n\t\t\t}\r\n\t\t\tmessage += `
`;\r\n\t\t}\r\n\t\treturn message ? `|raw|${message}` : ``;\r\n\t}\r\n\tgetSubRooms(includeSecret = false) {\r\n\t\tif (!this.subRooms) return [];\r\n\t\treturn [...this.subRooms.values()].filter(\r\n\t\t\troom => includeSecret ? true : !room.settings.isPrivate && !room.settings.isPersonal\r\n\t\t);\r\n\t}\r\n\tvalidateTitle(newTitle: string, newID?: string) {\r\n\t\tif (!newID) newID = toID(newTitle);\r\n\t\t// `,` is a delimiter used by a lot of /commands\r\n\t\t// `|` and `[` are delimiters used by the protocol\r\n\t\t// `-` has special meaning in roomids\r\n\t\tif (newTitle.includes(',') || newTitle.includes('|')) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Room title \"${newTitle}\" can't contain any of: ,|`);\r\n\t\t}\r\n\t\tif ((!newID.includes('-') || newID.startsWith('groupchat-')) && newTitle.includes('-')) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Room title \"${newTitle}\" can't contain -`);\r\n\t\t}\r\n\t\tif (newID.length > MAX_CHATROOM_ID_LENGTH) throw new Chat.ErrorMessage(\"The given room title is too long.\");\r\n\t\tif (Rooms.search(newTitle)) throw new Chat.ErrorMessage(`The room '${newTitle}' already exists.`);\r\n\t}\r\n\tsetParent(room: Room | null) {\r\n\t\tif (this.parent === room) return;\r\n\r\n\t\tif (this.parent) {\r\n\t\t\t(this as any).parent.subRooms.delete(this.roomid);\r\n\t\t\tif (!this.parent.subRooms!.size) {\r\n\t\t\t\t(this as any).parent.subRooms = null;\r\n\t\t\t}\r\n\t\t}\r\n\t\t(this as any).parent = room;\r\n\t\tif (room) {\r\n\t\t\tif (!room.subRooms) {\r\n\t\t\t\t(room as any).subRooms = new Map();\r\n\t\t\t}\r\n\t\t\t(room as any).subRooms.set(this.roomid, this);\r\n\t\t\tthis.settings.parentid = room.roomid;\r\n\t\t} else {\r\n\t\t\tdelete this.settings.parentid;\r\n\t\t}\r\n\r\n\t\tthis.saveSettings();\r\n\r\n\t\tfor (const userid in this.users) {\r\n\t\t\tthis.users[userid].updateIdentity(this.roomid);\r\n\t\t}\r\n\t}\r\n\tclearSubRooms() {\r\n\t\tif (!this.subRooms) return;\r\n\t\tfor (const room of this.subRooms.values()) {\r\n\t\t\t(room as any).parent = null;\r\n\t\t}\r\n\t\t(this as any).subRooms = null;\r\n\r\n\t\t// this doesn't update parentid or subroom user symbols because it's\r\n\t\t// intended to be used for cleanup only\r\n\t}\r\n\tsetPrivate(privacy: PrivacySetting) {\r\n\t\tthis.settings.isPrivate = privacy;\r\n\t\tthis.saveSettings();\r\n\r\n\t\tif (privacy) {\r\n\t\t\tfor (const user of Object.values(this.users)) {\r\n\t\t\t\tif (!user.named) {\r\n\t\t\t\t\tuser.leaveRoom(this.roomid);\r\n\t\t\t\t\tuser.popup(`The room <<${this.roomid}>> has been made private; you must log in to be in private rooms.`);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (this.battle) {\r\n\t\t\tif (privacy) {\r\n\t\t\t\tif (this.roomid.endsWith('pw')) return true;\r\n\r\n\t\t\t\t// This is the same password generation approach as genPassword in the client replays.lib.php\r\n\t\t\t\t// but obviously will not match given mt_rand there uses a different RNG and seed.\r\n\t\t\t\tlet password = '';\r\n\t\t\t\tfor (let i = 0; i < 31; i++) password += ALPHABET[Math.floor(Math.random() * ALPHABET.length)];\r\n\r\n\t\t\t\tthis.rename(this.title, `${this.roomid}-${password}pw` as RoomID, true);\r\n\t\t\t} else {\r\n\t\t\t\tif (!this.roomid.endsWith('pw')) return true;\r\n\r\n\t\t\t\tconst lastDashIndex = this.roomid.lastIndexOf('-');\r\n\t\t\t\tif (lastDashIndex < 0) throw new Error(`invalid battle ID ${this.roomid}`);\r\n\r\n\t\t\t\tthis.rename(this.title, this.roomid.slice(0, lastDashIndex) as RoomID);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tvalidateSection(section: string) {\r\n\t\tconst target = toID(section);\r\n\t\tif (!RoomSections.sections.includes(target as any)) {\r\n\t\t\tthrow new Chat.ErrorMessage(`\"${target}\" is not a valid room section. Valid categories include: ${RoomSections.sections.join(', ')}`);\r\n\t\t}\r\n\t\treturn target as RoomSection;\r\n\t}\r\n\tsetSection(section?: string) {\r\n\t\tif (!this.persist) {\r\n\t\t\tthrow new Chat.ErrorMessage(`You cannot change the section of temporary rooms.`);\r\n\t\t}\r\n\t\tif (section) {\r\n\t\t\tconst validatedSection = this.validateSection(section);\r\n\t\t\tif (this.settings.isPrivate && [true, 'hidden'].includes(this.settings.isPrivate)) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(`Only public rooms can change their section.`);\r\n\t\t\t}\r\n\t\t\tconst oldSection = this.settings.section;\r\n\t\t\tif (oldSection === section) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(`${this.title}'s room section is already set to \"${RoomSections.sectionNames[oldSection]}\".`);\r\n\t\t\t}\r\n\t\t\tthis.settings.section = validatedSection;\r\n\t\t\tthis.saveSettings();\r\n\t\t\treturn validatedSection;\r\n\t\t}\r\n\t\tdelete this.settings.section;\r\n\t\tthis.saveSettings();\r\n\t\treturn undefined;\r\n\t}\r\n\r\n\t/**\r\n\t * Displays a warning popup to all non-staff users users in the room.\r\n\t * Returns a list of all the user IDs that were warned.\r\n\t */\r\n\twarnParticipants(message: string) {\r\n\t\tconst warned = Object.values(this.users).filter(u => !u.can('lock'));\r\n\t\tfor (const user of warned) {\r\n\t\t\tuser.popup(`|modal|${message}`);\r\n\t\t}\r\n\t\treturn warned;\r\n\t}\r\n\r\n\t/**\r\n\t * @param newID Add this param if the roomid is different from `toID(newTitle)`\r\n\t * @param noAlias Set this param to true to not redirect aliases and the room's old name to its new name.\r\n\t */\r\n\trename(newTitle: string, newID?: RoomID, noAlias?: boolean) {\r\n\t\tif (!newID) newID = toID(newTitle) as RoomID;\r\n\t\tthis.validateTitle(newTitle, newID);\r\n\t\tif (this.type === 'chat' && this.game) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Please finish your game (${this.game.title}) before renaming ${this.roomid}.`);\r\n\t\t}\r\n\t\tconst oldID = this.roomid;\r\n\t\t(this as any).roomid = newID;\r\n\t\tthis.title = newTitle;\r\n\t\tRooms.rooms.delete(oldID);\r\n\t\tRooms.rooms.set(newID, this as Room);\r\n\t\tif (this.battle && oldID) {\r\n\t\t\tfor (const player of this.battle.players) {\r\n\t\t\t\tif (player.invite) {\r\n\t\t\t\t\tconst chall = Ladders.challenges.searchByRoom(player.invite, oldID);\r\n\t\t\t\t\tif (chall) chall.roomid = this.roomid;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (oldID === 'lobby') {\r\n\t\t\tRooms.lobby = null;\r\n\t\t} else if (newID === 'lobby') {\r\n\t\t\tRooms.lobby = this as ChatRoom;\r\n\t\t}\r\n\r\n\t\tif (!noAlias) {\r\n\t\t\tfor (const [alias, roomid] of Rooms.aliases.entries()) {\r\n\t\t\t\tif (roomid === oldID) {\r\n\t\t\t\t\tRooms.aliases.set(alias, newID);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// add an alias from the old id\r\n\t\t\tRooms.aliases.set(oldID, newID);\r\n\t\t\tif (!this.settings.aliases) this.settings.aliases = [];\r\n\t\t\t// resolve an old (fixed) bug in /renameroom\r\n\t\t\tif (!this.settings.aliases.includes(oldID)) this.settings.aliases.push(oldID);\r\n\t\t} else {\r\n\t\t\t// clear aliases\r\n\t\t\tfor (const [alias, roomid] of Rooms.aliases.entries()) {\r\n\t\t\t\tif (roomid === oldID) {\r\n\t\t\t\t\tRooms.aliases.delete(alias);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tthis.settings.aliases = undefined;\r\n\t\t}\r\n\r\n\t\tthis.game?.renameRoom(newID);\r\n\r\n\t\tthis.saveSettings();\r\n\r\n\t\tfor (const user of Object.values(this.users)) {\r\n\t\t\tuser.moveConnections(oldID, newID);\r\n\t\t\tuser.send(`>${oldID}\\n|noinit|rename|${newID}|${newTitle}`);\r\n\t\t}\r\n\r\n\t\tif (this.parent && this.parent.subRooms) {\r\n\t\t\t(this as any).parent.subRooms.delete(oldID);\r\n\t\t\t(this as any).parent.subRooms.set(newID, this as ChatRoom);\r\n\t\t}\r\n\t\tif (this.subRooms) {\r\n\t\t\tfor (const subRoom of this.subRooms.values()) {\r\n\t\t\t\t(subRoom as any).parent = this as ChatRoom;\r\n\t\t\t\tsubRoom.settings.parentid = newID;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.settings.title = newTitle;\r\n\t\tthis.saveSettings();\r\n\r\n\t\tPunishments.renameRoom(oldID, newID);\r\n\r\n\t\tvoid this.log.rename(newID);\r\n\t}\r\n\r\n\tonConnect(user: User, connection: Connection) {\r\n\t\tconst userList = this.userList ? this.userList : this.getUserList();\r\n\t\tthis.sendUser(\r\n\t\t\tconnection,\r\n\t\t\t'|init|chat\\n|title|' + this.title + '\\n' + userList + '\\n' + this.log.getScrollback() + this.getIntroMessage(user)\r\n\t\t);\r\n\t\tthis.minorActivity?.onConnect?.(user, connection);\r\n\t\tthis.game?.onConnect?.(user, connection);\r\n\t}\r\n\tonJoin(user: User, connection: Connection) {\r\n\t\tif (!user) return false; // ???\r\n\t\tif (this.users[user.id]) return false;\r\n\r\n\t\tif (user.named) {\r\n\t\t\tthis.reportJoin('j', user.getIdentityWithStatus(this), user);\r\n\t\t}\r\n\r\n\t\tconst staffIntro = this.getStaffIntroMessage(user);\r\n\t\tif (staffIntro) this.sendUser(user, staffIntro);\r\n\r\n\t\tthis.users[user.id] = user;\r\n\t\tthis.userCount++;\r\n\t\tthis.checkAutoModchat(user);\r\n\r\n\t\tthis.minorActivity?.onConnect?.(user, connection);\r\n\t\tthis.game?.onJoin?.(user, connection);\r\n\t\tChat.runHandlers('onRoomJoin', this, user, connection);\r\n\t\treturn true;\r\n\t}\r\n\tonRename(user: User, oldid: ID, joining: boolean) {\r\n\t\tif (user.id === oldid) {\r\n\t\t\treturn this.onUpdateIdentity(user);\r\n\t\t}\r\n\t\tif (!this.users[oldid]) {\r\n\t\t\tMonitor.crashlog(new Error(`user ${oldid} not in room ${this.roomid}`));\r\n\t\t}\r\n\t\tif (this.users[user.id]) {\r\n\t\t\tMonitor.crashlog(new Error(`user ${user.id} already in room ${this.roomid}`));\r\n\t\t}\r\n\t\tdelete this.users[oldid];\r\n\t\tthis.users[user.id] = user;\r\n\t\tif (joining) {\r\n\t\t\tthis.reportJoin('j', user.getIdentityWithStatus(this), user);\r\n\t\t\tconst staffIntro = this.getStaffIntroMessage(user);\r\n\t\t\tif (staffIntro) this.sendUser(user, staffIntro);\r\n\t\t} else if (!user.named) {\r\n\t\t\tthis.reportJoin('l', oldid, user);\r\n\t\t} else {\r\n\t\t\tthis.reportJoin('n', user.getIdentityWithStatus(this) + '|' + oldid, user);\r\n\t\t}\r\n\t\tthis.minorActivity?.onRename?.(user, oldid, joining);\r\n\t\tthis.checkAutoModchat(user);\r\n\t\treturn true;\r\n\t}\r\n\t/**\r\n\t * onRename, but without a userid change\r\n\t */\r\n\tonUpdateIdentity(user: User) {\r\n\t\tif (user?.connected) {\r\n\t\t\tif (!this.users[user.id]) return false;\r\n\t\t\tif (user.named) {\r\n\t\t\t\tthis.reportJoin('n', user.getIdentityWithStatus(this) + '|' + user.id, user);\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn true;\r\n\t}\r\n\tonLeave(user: User) {\r\n\t\tif (!user) return false; // ...\r\n\r\n\t\tif (!(user.id in this.users)) {\r\n\t\t\tMonitor.crashlog(new Error(`user ${user.id} already left`));\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tdelete this.users[user.id];\r\n\t\tthis.userCount--;\r\n\r\n\t\tif (user.named) {\r\n\t\t\tthis.reportJoin('l', user.getIdentity(this), user);\r\n\t\t}\r\n\t\tif (this.game && this.game.onLeave) this.game.onLeave(user);\r\n\t\tthis.runAutoModchat();\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\trunAutoModchat() {\r\n\t\tif (!this.settings.autoModchat || this.settings.autoModchat.active) return;\r\n\t\t// they are staff and online\r\n\t\tconst staff = Object.values(this.users).filter(u => this.auth.atLeast(u, '%'));\r\n\t\tif (!staff.length) {\r\n\t\t\tconst {time} = this.settings.autoModchat;\r\n\t\t\tif (!time || time < 5) {\r\n\t\t\t\tthrow new Error(`Invalid time setting for automodchat (${Utils.visualize(this.settings.autoModchat)})`);\r\n\t\t\t}\r\n\t\t\tif (this.modchatTimer) clearTimeout(this.modchatTimer);\r\n\t\t\tthis.modchatTimer = setTimeout(() => {\r\n\t\t\t\tif (!this.settings.autoModchat) return;\r\n\t\t\t\tconst {rank} = this.settings.autoModchat;\r\n\t\t\t\tconst oldSetting = this.settings.modchat;\r\n\t\t\t\tthis.settings.modchat = rank;\r\n\t\t\t\tthis.add(\r\n\t\t\t\t\t// always gonna be minutes so we can just use the number directly lol\r\n\t\t\t\t\t`|raw|
This room has had no active staff for ${time} minutes,` +\r\n\t\t\t\t\t` and has had modchat set to ${rank}.
`\r\n\t\t\t\t).update();\r\n\t\t\t\tthis.modlog({\r\n\t\t\t\t\taction: 'AUTOMODCHAT ACTIVATE',\r\n\t\t\t\t});\r\n\t\t\t\t// automodchat will always exist\r\n\t\t\t\tthis.settings.autoModchat.active = oldSetting || true;\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t}, time * 60 * 1000);\r\n\t\t}\r\n\t}\r\n\r\n\tcheckAutoModchat(user: User) {\r\n\t\tif (user.can('mute', null, this, 'modchat')) {\r\n\t\t\tif (this.modchatTimer) {\r\n\t\t\t\tclearTimeout(this.modchatTimer);\r\n\t\t\t}\r\n\t\t\tif (this.settings.autoModchat?.active) {\r\n\t\t\t\tconst oldSetting = this.settings.autoModchat.active;\r\n\t\t\t\tif (typeof oldSetting === 'string') {\r\n\t\t\t\t\tthis.settings.modchat = oldSetting;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tdelete this.settings.modchat;\r\n\t\t\t\t}\r\n\t\t\t\tthis.settings.autoModchat.active = false;\r\n\t\t\t\tthis.saveSettings();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tdestroy(): void {\r\n\t\t// deallocate ourself\r\n\r\n\t\tif (this.battle && this.tour) {\r\n\t\t\t// resolve state of the tournament;\r\n\t\t\tif (!this.battle.ended) this.tour.onBattleWin(this as any as GameRoom, '');\r\n\t\t\tthis.tour = null;\r\n\t\t}\r\n\r\n\t\t// remove references to ourself\r\n\t\tfor (const i in this.users) {\r\n\t\t\tthis.users[i].leaveRoom(this as Room, null);\r\n\t\t\tdelete this.users[i];\r\n\t\t}\r\n\r\n\t\tthis.setParent(null);\r\n\t\tthis.clearSubRooms();\r\n\r\n\t\tChat.runHandlers('onRoomDestroy', this.roomid);\r\n\r\n\t\tRooms.global.deregisterChatRoom(this.roomid);\r\n\t\tRooms.global.delistChatRoom(this.roomid);\r\n\r\n\t\tif (this.settings.aliases) {\r\n\t\t\tfor (const alias of this.settings.aliases) {\r\n\t\t\t\tRooms.aliases.delete(alias);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (this.game) {\r\n\t\t\tthis.game.destroy();\r\n\t\t\tthis.game = null;\r\n\t\t\tthis.battle = null;\r\n\t\t}\r\n\t\tthis.active = false;\r\n\r\n\t\t// Ensure there aren't any pending messages that could restart the expire timer\r\n\t\tthis.update();\r\n\r\n\t\t// Clear any active timers for the room\r\n\t\tif (this.muteTimer) {\r\n\t\t\tclearTimeout(this.muteTimer);\r\n\t\t\tthis.muteTimer = null;\r\n\t\t}\r\n\t\tif (this.expireTimer) {\r\n\t\t\tclearTimeout(this.expireTimer);\r\n\t\t\tthis.expireTimer = null;\r\n\t\t}\r\n\t\tif (this.reportJoinsInterval) {\r\n\t\t\tclearInterval(this.reportJoinsInterval);\r\n\t\t}\r\n\t\tthis.reportJoinsInterval = null;\r\n\t\tif (this.logUserStatsInterval) {\r\n\t\t\tclearInterval(this.logUserStatsInterval);\r\n\t\t}\r\n\t\tthis.logUserStatsInterval = null;\r\n\r\n\t\tvoid this.log.destroy();\r\n\r\n\t\tRooms.rooms.delete(this.roomid);\r\n\t\tif (this.roomid === 'lobby') Rooms.lobby = null;\r\n\t}\r\n\ttr(strings: string | TemplateStringsArray, ...keys: any[]) {\r\n\t\treturn Chat.tr(this.settings.language || 'english' as ID, strings, ...keys);\r\n\t}\r\n}\r\n\r\nexport class GlobalRoomState {\r\n\treadonly settingsList: RoomSettings[];\r\n\treadonly chatRooms: ChatRoom[];\r\n\t/**\r\n\t * Rooms that users autojoin upon connecting\r\n\t */\r\n\treadonly autojoinList: RoomID[];\r\n\t/**\r\n\t * Rooms that users autojoin upon logging in\r\n\t */\r\n\treadonly modjoinedAutojoinList: RoomID[];\r\n\treadonly ladderIpLog: Streams.WriteStream;\r\n\treadonly reportUserStatsInterval: NodeJS.Timeout;\r\n\tlockdown: boolean | 'pre' | 'ddos';\r\n\tbattleCount: number;\r\n\tlastReportedCrash: number;\r\n\tlastBattle: number;\r\n\tlastWrittenBattle: number;\r\n\tmaxUsers: number;\r\n\tmaxUsersDate: number;\r\n\tformatList: string;\r\n\r\n\tconstructor() {\r\n\t\tthis.settingsList = [];\r\n\t\ttry {\r\n\t\t\tthis.settingsList = require(FS('config/chatrooms.json').path);\r\n\t\t\tif (!Array.isArray(this.settingsList)) this.settingsList = [];\r\n\t\t} catch {} // file doesn't exist [yet]\r\n\r\n\t\tif (!this.settingsList.length) {\r\n\t\t\tthis.settingsList = [{\r\n\t\t\t\ttitle: 'Lobby',\r\n\t\t\t\tauth: {},\r\n\t\t\t\tcreationTime: Date.now(),\r\n\t\t\t\tautojoin: true,\r\n\t\t\t\tsection: 'official',\r\n\t\t\t}, {\r\n\t\t\t\ttitle: 'Staff',\r\n\t\t\t\tauth: {},\r\n\t\t\t\tcreationTime: Date.now(),\r\n\t\t\t\tisPrivate: 'hidden',\r\n\t\t\t\tmodjoin: Users.SECTIONLEADER_SYMBOL,\r\n\t\t\t\tautojoin: true,\r\n\t\t\t}];\r\n\t\t}\r\n\r\n\t\tthis.chatRooms = [];\r\n\r\n\t\tthis.autojoinList = [];\r\n\t\tthis.modjoinedAutojoinList = [];\r\n\t\tfor (const [i, settings] of this.settingsList.entries()) {\r\n\t\t\tif (!settings?.title) {\r\n\t\t\t\tMonitor.warn(`ERROR: Room number ${i} has no data and could not be loaded.`);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif ((settings as any).staffAutojoin) {\r\n\t\t\t\t// convert old staffAutojoin format\r\n\t\t\t\tdelete (settings as any).staffAutojoin;\r\n\t\t\t\t(settings as any).autojoin = true;\r\n\t\t\t\tif (!settings.modjoin) settings.modjoin = '%';\r\n\t\t\t\tif (settings.isPrivate === true) settings.isPrivate = 'hidden';\r\n\t\t\t}\r\n\r\n\t\t\t// We're okay with assinging type `ID` to `RoomID` here\r\n\t\t\t// because the hyphens in chatrooms don't have any special\r\n\t\t\t// meaning, unlike in helptickets, groupchats, battles etc\r\n\t\t\t// where they are used for shared modlogs and the like\r\n\t\t\tconst id = toID(settings.title) as RoomID;\r\n\t\t\tMonitor.notice(\"RESTORE CHATROOM: \" + id);\r\n\t\t\tconst room = Rooms.createChatRoom(id, settings.title, settings);\r\n\t\t\tif (room.settings.aliases) {\r\n\t\t\t\tfor (const alias of room.settings.aliases) {\r\n\t\t\t\t\tRooms.aliases.set(alias, id);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tthis.chatRooms.push(room);\r\n\t\t\tif (room.settings.autojoin) {\r\n\t\t\t\tif (room.settings.modjoin) {\r\n\t\t\t\t\tthis.modjoinedAutojoinList.push(id);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthis.autojoinList.push(id);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tRooms.lobby = Rooms.rooms.get('lobby') as ChatRoom;\r\n\r\n\t\t// init battle room logging\r\n\t\tif (Config.logladderip) {\r\n\t\t\tthis.ladderIpLog = FS('logs/ladderip/ladderip.txt').createAppendStream();\r\n\t\t} else {\r\n\t\t\t// Prevent there from being two possible hidden classes an instance\r\n\t\t\t// of GlobalRoom can have.\r\n\t\t\tthis.ladderIpLog = new Streams.WriteStream({write() { return undefined; }});\r\n\t\t}\r\n\r\n\t\tthis.reportUserStatsInterval = setInterval(\r\n\t\t\t() => this.reportUserStats(),\r\n\t\t\tREPORT_USER_STATS_INTERVAL\r\n\t\t);\r\n\r\n\t\t// init users\r\n\t\tthis.maxUsers = 0;\r\n\t\tthis.maxUsersDate = 0;\r\n\t\tthis.lockdown = false;\r\n\r\n\t\tthis.battleCount = 0;\r\n\t\tthis.lastReportedCrash = 0;\r\n\r\n\t\tthis.formatList = '';\r\n\r\n\t\tlet lastBattle;\r\n\t\ttry {\r\n\t\t\tlastBattle = FS('logs/lastbattle.txt').readSync('utf8');\r\n\t\t} catch {}\r\n\t\tthis.lastBattle = Number(lastBattle) || 0;\r\n\t\tthis.lastWrittenBattle = this.lastBattle;\r\n\t\tvoid this.loadBattles();\r\n\t}\r\n\r\n\tasync saveBattles() {\r\n\t\tlet count = 0;\r\n\t\tif (!Config.usepostgres) return 0;\r\n\t\tconst logDatabase = new PostgresDatabase();\r\n\t\tawait logDatabase.ensureMigrated({\r\n\t\t\ttable: 'stored_battles',\r\n\t\t\tmigrationsFolder: 'databases/migrations/storedbattles',\r\n\t\t\tbaseSchemaFile: 'databases/schemas/stored-battles.sql',\r\n\t\t});\r\n\t\tfor (const room of Rooms.rooms.values()) {\r\n\t\t\tif (!room.battle || room.battle.ended) continue;\r\n\t\t\troom.battle.frozen = true;\r\n\t\t\tconst log = await room.battle.getLog();\r\n\t\t\tconst players: ID[] = room.battle.options.players || [];\r\n\t\t\tif (!players.length) {\r\n\t\t\t\tfor (const num of ['p1', 'p2', 'p3', 'p4'] as const) {\r\n\t\t\t\t\tif (room.battle[num]?.id) {\r\n\t\t\t\t\t\tplayers.push(room.battle[num].id);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (!players.length || !log?.length) continue; // shouldn't happen???\r\n\t\t\tconst timerData = {\r\n\t\t\t\t...room.battle.timer.settings,\r\n\t\t\t\tactive: !!room.battle.timer.timer || false,\r\n\t\t\t};\r\n\t\t\tawait logDatabase.query(\r\n\t\t\t\t`INSERT INTO stored_battles (roomid, input_log, players, title, rated, timer) VALUES ($1, $2, $3, $4, $5, $6)` +\r\n\t\t\t\t` ON CONFLICT (roomid) DO UPDATE ` +\r\n\t\t\t\t`SET input_log = EXCLUDED.input_log, players = EXCLUDED.players, title = EXCLUDED.title, rated = EXCLUDED.rated`,\r\n\t\t\t\t[room.roomid, log.join('\\n'), players, room.title, room.battle.rated, timerData]\r\n\t\t\t);\r\n\t\t\troom.battle.timer.stop();\r\n\t\t\tcount++;\r\n\t\t}\r\n\t\treturn count;\r\n\t}\r\n\r\n\tbattlesLoading?: boolean;\r\n\tasync loadBattles() {\r\n\t\tif (!Config.usepostgres) return;\r\n\t\tthis.battlesLoading = true;\r\n\t\tfor (const u of Users.users.values()) {\r\n\t\t\tu.send(\r\n\t\t\t\t`|pm|&|${u.getIdentity()}|/uhtml restartmsg,` +\r\n\t\t\t\t`
Your battles are currently being restored.
Please be patient as they load.
`\r\n\t\t\t);\r\n\t\t}\r\n\t\tconst logDatabase = new PostgresDatabase();\r\n\t\tconst query = `DELETE FROM stored_battles WHERE roomid IN (SELECT roomid FROM stored_battles LIMIT 1) RETURNING *`;\r\n\t\tfor await (const battle of logDatabase.stream(query)) {\r\n\t\t\tconst {input_log, players, roomid, title, rated, timer} = battle;\r\n\t\t\tconst [, formatid] = roomid.split('-');\r\n\t\t\tconst room = Rooms.createBattle({\r\n\t\t\t\tformat: formatid,\r\n\t\t\t\tinputLog: input_log,\r\n\t\t\t\troomid,\r\n\t\t\t\ttitle,\r\n\t\t\t\trated: Number(rated),\r\n\t\t\t\tplayers,\r\n\t\t\t\tdelayedStart: true,\r\n\t\t\t\tdelayedTimer: timer.active,\r\n\t\t\t\trestored: true,\r\n\t\t\t});\r\n\t\t\tif (!room || !room.battle) continue; // shouldn't happen???\r\n\t\t\troom.battle.started = true; // so that timer works\r\n\t\t\troom.battle.start();\r\n\t\t\tif (timer) { // json blob of settings\r\n\t\t\t\tObject.assign(room.battle.timer.settings, timer);\r\n\t\t\t}\r\n\t\t\tfor (const [i, p] of players.entries()) {\r\n\t\t\t\troom.auth.set(p, Users.PLAYER_SYMBOL);\r\n\t\t\t\tconst player = room.battle.players[i];\r\n\t\t\t\tplayer.id = p;\r\n\t\t\t\tplayer.name = p; // temp for if they get timed out before they connect\r\n\t\t\t\tconst u = Users.getExact(p);\r\n\t\t\t\tif (u) {\r\n\t\t\t\t\tthis.rejoinBattle(room, u, i);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tfor (const u of Users.users.values()) {\r\n\t\t\tu.send(`|pm|&|${u.getIdentity()}|/uhtmlchange restartmsg,`);\r\n\t\t}\r\n\t\tdelete this.battlesLoading;\r\n\t}\r\n\r\n\trejoinBattle(room: GameRoom, user: User, idx: number) {\r\n\t\tif (!room.battle) return;\r\n\t\t// we reuse these player objects explicitly so if\r\n\t\t// someone has already started the timer, their settings carry over (should reduce bugs)\r\n\t\t// and it's safe to do this first because we know these users were already in the battle\r\n\t\tlet player = room.battle.players[idx];\r\n\t\tif (!player) {\r\n\t\t\t// this can happen sometimes\r\n\t\t\tplayer = room.battle.players[idx] = new Rooms.RoomBattlePlayer(\r\n\t\t\t\tuser, room.battle, (idx + 1) as PlayerIndex\r\n\t\t\t);\r\n\t\t}\r\n\t\tplayer.id = user.id;\r\n\t\tplayer.name = user.name;\r\n\t\troom.battle.playerTable[user.id] = player;\r\n\t\tuser.joinRoom(room.roomid);\r\n\t\t// force update panel\r\n\t\troom.battle.onConnect(user);\r\n\t\tif (room.battle.options.delayedTimer && !room.battle.timer.timer) {\r\n\t\t\troom.battle.timer.start();\r\n\t\t}\r\n\t\tuser.send(`|pm|&|${user.getIdentity()}|/uhtmlchange restartmsg,`);\r\n\t}\r\n\r\n\tjoinOldBattles(user: User) {\r\n\t\tfor (const room of Rooms.rooms.values()) {\r\n\t\t\tconst battle = room.battle;\r\n\t\t\tif (!battle) continue;\r\n\t\t\tconst idx = battle.options.players?.indexOf(user.id);\r\n\t\t\tif (battle.ended) {\r\n\t\t\t\t// TODO: Do we want to rejoin the battle room here?\r\n\t\t\t\t// We might need to cache the join so it only happens once -\r\n\t\t\t\t// just running joinRoom would mean they join the room every refresh until the battle expires\r\n\r\n\t\t\t\t// user.joinRoom(room);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (typeof idx === 'number' && idx > -1) {\r\n\t\t\t\tthis.rejoinBattle(room, user, idx);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tmodlog(entry: PartialModlogEntry, overrideID?: string) {\r\n\t\tvoid Rooms.Modlog.write('global', entry, overrideID);\r\n\t}\r\n\r\n\twriteChatRoomData() {\r\n\t\tFS('config/chatrooms.json').writeUpdate(() => (\r\n\t\t\tJSON.stringify(this.settingsList)\r\n\t\t\t\t.replace(/\\{\"title\":/g, '\\n{\"title\":')\r\n\t\t\t\t.replace(/\\]$/, '\\n]')\r\n\t\t), {throttle: 5000});\r\n\t}\r\n\r\n\twriteNumRooms() {\r\n\t\tif (this.lockdown) {\r\n\t\t\tif (this.lastBattle === this.lastWrittenBattle) return;\r\n\t\t\tthis.lastWrittenBattle = this.lastBattle;\r\n\t\t} else {\r\n\t\t\t// batch writes so we don't have to write them every new battle\r\n\t\t\t// very probably premature optimization, considering by default we\r\n\t\t\t// write significantly larger log files every new battle\r\n\t\t\tif (this.lastBattle < this.lastWrittenBattle) return;\r\n\t\t\tthis.lastWrittenBattle = this.lastBattle + LAST_BATTLE_WRITE_THROTTLE;\r\n\t\t}\r\n\t\tFS('logs/lastbattle.txt').writeUpdate(\r\n\t\t\t() => `${this.lastWrittenBattle}`\r\n\t\t);\r\n\t}\r\n\r\n\treportUserStats() {\r\n\t\tif (this.maxUsersDate) {\r\n\t\t\tvoid LoginServer.request('updateuserstats', {\r\n\t\t\t\tdate: this.maxUsersDate,\r\n\t\t\t\tusers: this.maxUsers,\r\n\t\t\t});\r\n\t\t\tthis.maxUsersDate = 0;\r\n\t\t}\r\n\t\tvoid LoginServer.request('updateuserstats', {\r\n\t\t\tdate: Date.now(),\r\n\t\t\tusers: Users.onlineCount,\r\n\t\t});\r\n\t}\r\n\r\n\tget formatListText() {\r\n\t\tif (this.formatList) {\r\n\t\t\treturn this.formatList;\r\n\t\t}\r\n\t\tthis.formatList = '|formats' + (Ladders.formatsListPrefix || '');\r\n\t\tlet section = '';\r\n\t\tlet prevSection = '';\r\n\t\tlet curColumn = 1;\r\n\t\tfor (const format of Dex.formats.all()) {\r\n\t\t\tif (format.section) section = format.section;\r\n\t\t\tif (format.column) curColumn = format.column;\r\n\t\t\tif (!format.name) continue;\r\n\t\t\tif (!format.challengeShow && !format.searchShow && !format.tournamentShow) continue;\r\n\r\n\t\t\tif (section !== prevSection) {\r\n\t\t\t\tprevSection = section;\r\n\t\t\t\tthis.formatList += '|,' + curColumn + '|' + section;\r\n\t\t\t}\r\n\t\t\tthis.formatList += '|' + format.name;\r\n\t\t\tlet displayCode = 0;\r\n\t\t\tif (format.team) displayCode |= 1;\r\n\t\t\tif (format.searchShow) displayCode |= 2;\r\n\t\t\tif (format.challengeShow) displayCode |= 4;\r\n\t\t\tif (format.tournamentShow) displayCode |= 8;\r\n\t\t\tconst ruleTable = Dex.formats.getRuleTable(format);\r\n\t\t\tconst level = ruleTable.adjustLevel || ruleTable.adjustLevelDown || ruleTable.maxLevel;\r\n\t\t\tif (level === 50) displayCode |= 16;\r\n\t\t\tthis.formatList += ',' + displayCode.toString(16);\r\n\t\t}\r\n\t\treturn this.formatList;\r\n\t}\r\n\tget configRankList() {\r\n\t\tif (Config.nocustomgrouplist) return '';\r\n\r\n\t\t// putting the resultant object in Config would enable this to be run again should config.js be reloaded.\r\n\t\tif (Config.rankList) {\r\n\t\t\treturn Config.rankList;\r\n\t\t}\r\n\t\tconst rankList = [];\r\n\r\n\t\tfor (const rank in Config.groups) {\r\n\t\t\tif (!Config.groups[rank] || !rank) continue;\r\n\r\n\t\t\tconst tarGroup = Config.groups[rank];\r\n\t\t\tlet groupType = tarGroup.id === 'bot' || (!tarGroup.mute && !tarGroup.root) ?\r\n\t\t\t\t'normal' : (tarGroup.root || tarGroup.declare) ? 'leadership' : 'staff';\r\n\t\t\tif (tarGroup.id === 'sectionleader') groupType = 'staff';\r\n\r\n\t\t\trankList.push({\r\n\t\t\t\tsymbol: rank,\r\n\t\t\t\tname: (Config.groups[rank].name || null),\r\n\t\t\t\ttype: groupType}); // send the first character in the rank, incase they put a string several characters long\r\n\t\t}\r\n\r\n\t\tconst typeOrder = ['punishment', 'normal', 'staff', 'leadership'];\r\n\r\n\t\tUtils.sortBy(rankList, rank => -typeOrder.indexOf(rank.type));\r\n\r\n\t\t// add the punishment types at the very end.\r\n\t\tfor (const rank in Config.punishgroups) {\r\n\t\t\trankList.push({symbol: Config.punishgroups[rank].symbol, name: Config.punishgroups[rank].name, type: 'punishment'});\r\n\t\t}\r\n\r\n\t\tConfig.rankList = '|customgroups|' + JSON.stringify(rankList) + '\\n';\r\n\t\treturn Config.rankList;\r\n\t}\r\n\r\n\tgetBattles(/** formatfilter, elofilter, usernamefilter */ filter: string) {\r\n\t\tconst rooms: GameRoom[] = [];\r\n\t\tconst [formatFilter, eloFilterString, usernameFilter] = filter.split(',');\r\n\t\tconst eloFilter = +eloFilterString;\r\n\t\tfor (const room of Rooms.rooms.values()) {\r\n\t\t\tif (!room?.active || room.settings.isPrivate) continue;\r\n\t\t\tif (room.type !== 'battle') continue;\r\n\t\t\tif (formatFilter && formatFilter !== room.format) continue;\r\n\t\t\tif (eloFilter && (!room.rated || room.rated < eloFilter)) continue;\r\n\t\t\tif (usernameFilter && room.battle) {\r\n\t\t\t\tconst p1userid = room.battle.p1.id;\r\n\t\t\t\tconst p2userid = room.battle.p2.id;\r\n\t\t\t\tif (!p1userid || !p2userid) continue;\r\n\t\t\t\tif (!p1userid.startsWith(usernameFilter) && !p2userid.startsWith(usernameFilter)) continue;\r\n\t\t\t}\r\n\t\t\trooms.push(room);\r\n\t\t}\r\n\r\n\t\tconst roomTable: {[roomid: string]: BattleRoomTable} = {};\r\n\t\tfor (let i = rooms.length - 1; i >= rooms.length - 100 && i >= 0; i--) {\r\n\t\t\tconst room = rooms[i];\r\n\t\t\tconst roomData: BattleRoomTable = {};\r\n\t\t\tif (room.active && room.battle) {\r\n\t\t\t\tif (room.battle.p1) roomData.p1 = room.battle.p1.name;\r\n\t\t\t\tif (room.battle.p2) roomData.p2 = room.battle.p2.name;\r\n\t\t\t\tif (room.tour) roomData.minElo = 'tour';\r\n\t\t\t\tif (room.rated) roomData.minElo = Math.floor(room.rated);\r\n\t\t\t}\r\n\t\t\tif (!roomData.p1 || !roomData.p2) continue;\r\n\t\t\troomTable[room.roomid] = roomData;\r\n\t\t}\r\n\t\treturn roomTable;\r\n\t}\r\n\tgetRooms(user: User) {\r\n\t\tconst roomsData: {\r\n\t\t\tchat: ChatRoomTable[],\r\n\t\t\tsectionTitles: string[],\r\n\t\t\tuserCount: number,\r\n\t\t\tbattleCount: number,\r\n\t\t} = {\r\n\t\t\tchat: [],\r\n\t\t\tsectionTitles: Object.values(RoomSections.sectionNames),\r\n\t\t\tuserCount: Users.onlineCount,\r\n\t\t\tbattleCount: this.battleCount,\r\n\t\t};\r\n\t\tfor (const room of this.chatRooms) {\r\n\t\t\tif (!room) continue;\r\n\t\t\tif (room.parent) continue;\r\n\t\t\tif (\r\n\t\t\t\troom.settings.modjoin ||\r\n\t\t\t\t(room.settings.isPrivate && !(['hidden', 'voice'] as any).includes(room.settings.isPrivate)) ||\r\n\t\t\t\t(room.settings.isPrivate === 'voice' && user.tempGroup === ' ')\r\n\t\t\t) continue;\r\n\t\t\tconst roomData: ChatRoomTable = {\r\n\t\t\t\ttitle: room.title,\r\n\t\t\t\tdesc: room.settings.desc || '',\r\n\t\t\t\tuserCount: room.userCount,\r\n\t\t\t\tsection: room.settings.section ?\r\n\t\t\t\t\t(RoomSections.sectionNames[room.settings.section] || room.settings.section) : undefined,\r\n\t\t\t\tprivacy: !room.settings.isPrivate ? undefined : room.settings.isPrivate,\r\n\t\t\t};\r\n\t\t\tconst subrooms = room.getSubRooms().map(r => r.title);\r\n\t\t\tif (subrooms.length) roomData.subRooms = subrooms;\r\n\t\t\tif (room.settings.spotlight) roomData.spotlight = room.settings.spotlight;\r\n\r\n\t\t\troomsData.chat.push(roomData);\r\n\t\t}\r\n\t\treturn roomsData;\r\n\t}\r\n\tsendAll(message: string) {\r\n\t\tSockets.roomBroadcast('', message);\r\n\t}\r\n\taddChatRoom(title: string) {\r\n\t\tconst id = toID(title) as RoomID;\r\n\t\tif (['battles', 'rooms', 'ladder', 'teambuilder', 'home', 'all', 'public'].includes(id)) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tif (Rooms.rooms.has(id)) return false;\r\n\r\n\t\tconst settings = {\r\n\t\t\ttitle,\r\n\t\t\tauth: {},\r\n\t\t\tcreationTime: Date.now(),\r\n\t\t};\r\n\t\tconst room = Rooms.createChatRoom(id, title, settings);\r\n\t\tif (id === 'lobby') Rooms.lobby = room;\r\n\t\tthis.settingsList.push(settings);\r\n\t\tthis.chatRooms.push(room);\r\n\t\tthis.writeChatRoomData();\r\n\t\treturn true;\r\n\t}\r\n\r\n\tprepBattleRoom(format: string) {\r\n\t\t// console.log('BATTLE START BETWEEN: ' + p1.id + ' ' + p2.id);\r\n\t\tconst roomPrefix = `battle-${toID(Dex.formats.get(format).name)}-`;\r\n\t\tlet battleNum = this.lastBattle;\r\n\t\tlet roomid: RoomID;\r\n\t\tdo {\r\n\t\t\troomid = `${roomPrefix}${++battleNum}` as RoomID;\r\n\t\t} while (Rooms.rooms.has(roomid));\r\n\r\n\t\tthis.lastBattle = battleNum;\r\n\t\tthis.writeNumRooms();\r\n\t\treturn roomid;\r\n\t}\r\n\r\n\tonCreateBattleRoom(players: User[], room: GameRoom, options: AnyObject) {\r\n\t\tfor (const player of players) {\r\n\t\t\tif (player.statusType === 'idle') {\r\n\t\t\t\tplayer.setStatusType('online');\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (Config.reportbattles) {\r\n\t\t\tconst reportRoom = Rooms.get(Config.reportbattles === true ? 'lobby' : Config.reportbattles);\r\n\t\t\tif (reportRoom) {\r\n\t\t\t\tconst reportPlayers = players.map(p => p.getIdentity()).join('|');\r\n\t\t\t\treportRoom\r\n\t\t\t\t\t.add(`|b|${room.roomid}|${reportPlayers}`)\r\n\t\t\t\t\t.update();\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (Config.logladderip && options.rated) {\r\n\t\t\tconst ladderIpLogString = players.map(p => `${p.id}: ${p.latestIp}\\n`).join('');\r\n\t\t\tvoid this.ladderIpLog.write(ladderIpLogString);\r\n\t\t}\r\n\t\tfor (const player of players) {\r\n\t\t\tChat.runHandlers('onBattleStart', player, room);\r\n\t\t}\r\n\t}\r\n\r\n\tderegisterChatRoom(id: string) {\r\n\t\tid = toID(id);\r\n\t\tconst room = Rooms.get(id);\r\n\t\tif (!room) return false; // room doesn't exist\r\n\t\tif (!room.persist) return false; // room isn't registered\r\n\t\t// deregister from global settings\r\n\t\t// looping from the end is a pretty trivial optimization, but the\r\n\t\t// assumption is that more recently added rooms are more likely to\r\n\t\t// be deleted\r\n\t\tfor (let i = this.settingsList.length - 1; i >= 0; i--) {\r\n\t\t\tif (id === toID(this.settingsList[i].title)) {\r\n\t\t\t\tthis.settingsList.splice(i, 1);\r\n\t\t\t\tthis.writeChatRoomData();\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\troom.persist = false;\r\n\t\treturn true;\r\n\t}\r\n\tdelistChatRoom(id: RoomID) {\r\n\t\tid = toID(id) as RoomID;\r\n\t\tif (!Rooms.rooms.has(id)) return false; // room doesn't exist\r\n\t\tfor (let i = this.chatRooms.length - 1; i >= 0; i--) {\r\n\t\t\tif (id === this.chatRooms[i].roomid) {\r\n\t\t\t\tthis.chatRooms.splice(i, 1);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tremoveChatRoom(id: string) {\r\n\t\tid = toID(id);\r\n\t\tconst room = Rooms.get(id);\r\n\t\tif (!room) return false; // room doesn't exist\r\n\t\troom.destroy();\r\n\t\treturn true;\r\n\t}\r\n\tautojoinRooms(user: User, connection: Connection) {\r\n\t\t// we only autojoin regular rooms if the client requests it with /autojoin\r\n\t\t// note that this restriction doesn't apply to modjoined autojoin rooms\r\n\t\tlet includesLobby = false;\r\n\t\tfor (const roomName of this.autojoinList) {\r\n\t\t\tuser.joinRoom(roomName, connection);\r\n\t\t\tif (roomName === 'lobby') includesLobby = true;\r\n\t\t}\r\n\t\tif (!includesLobby && Config.serverid !== 'showdown') user.send(`>lobby\\n|deinit`);\r\n\t}\r\n\tcheckAutojoin(user: User, connection?: Connection) {\r\n\t\tif (!user.named) return;\r\n\t\tfor (let [i, roomid] of this.modjoinedAutojoinList.entries()) {\r\n\t\t\tconst room = Rooms.get(roomid);\r\n\t\t\tif (!room) {\r\n\t\t\t\tthis.modjoinedAutojoinList.splice(i, 1);\r\n\t\t\t\ti--;\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (room.checkModjoin(user)) {\r\n\t\t\t\tuser.joinRoom(room.roomid, connection);\r\n\t\t\t}\r\n\t\t}\r\n\t\tfor (const conn of user.connections) {\r\n\t\t\tif (conn.autojoins) {\r\n\t\t\t\tconst autojoins = conn.autojoins.split(',') as RoomID[];\r\n\t\t\t\tfor (const roomName of autojoins) {\r\n\t\t\t\t\tvoid user.tryJoinRoom(roomName, conn);\r\n\t\t\t\t}\r\n\t\t\t\tconn.autojoins = '';\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\thandleConnect(user: User, connection: Connection) {\r\n\t\tconnection.send(user.getUpdateuserText() + '\\n' + this.configRankList + this.formatListText);\r\n\t\tif (Users.users.size > this.maxUsers) {\r\n\t\t\tthis.maxUsers = Users.users.size;\r\n\t\t\tthis.maxUsersDate = Date.now();\r\n\t\t}\r\n\t\tif (this.battlesLoading) {\r\n\t\t\tconnection.send(\r\n\t\t\t\t`|pm|&|${user.getIdentity()}|/uhtml restartmsg,` +\r\n\t\t\t\t`
Your battles are currently being restored.
Please be patient as they load.
`\r\n\t\t\t);\r\n\t\t}\r\n\t}\r\n\tstartLockdown(err: Error | null = null, slow = false) {\r\n\t\tif (this.lockdown && err) return;\r\n\t\tconst devRoom = Rooms.get('development');\r\n\t\t// @ts-ignore\r\n\t\tconst stack = (err ? Utils.escapeHTML(err.stack).split(`\\n`).slice(0, 2).join(`
`) : ``);\r\n\t\tfor (const [id, curRoom] of Rooms.rooms) {\r\n\t\t\tif (err) {\r\n\t\t\t\tif (id === 'staff' || id === 'development' || (!devRoom && id === 'lobby')) {\r\n\t\t\t\t\tcurRoom.addRaw(`
The server needs to restart because of a crash: ${stack}
Please restart the server.
`);\r\n\t\t\t\t\tcurRoom.addRaw(`
You will not be able to start new battles until the server restarts.
`);\r\n\t\t\t\t\tcurRoom.update();\r\n\t\t\t\t} else {\r\n\t\t\t\t\tcurRoom.addRaw(`
The server needs to restart because of a crash.
No new battles can be started until the server is done restarting.
`).update();\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tcurRoom.addRaw(`
The server is restarting soon.
Please finish your battles quickly. No new battles can be started until the server resets in a few minutes.
`).update();\r\n\t\t\t}\r\n\t\t\tconst game = curRoom.game;\r\n\t\t\t// @ts-ignore TODO: revisit when game.timer is standardized\r\n\t\t\tif (!slow && game && game.timer && typeof game.timer.start === 'function' && !game.ended) {\r\n\t\t\t\t// @ts-ignore\r\n\t\t\t\tgame.timer.start();\r\n\t\t\t\tif (curRoom.settings.modchat !== '+') {\r\n\t\t\t\t\tcurRoom.settings.modchat = '+';\r\n\t\t\t\t\tcurRoom.addRaw(`
Moderated chat was set to +!
Only users of rank + and higher can talk.
`).update();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tfor (const user of Users.users.values()) {\r\n\t\t\tuser.send(`|pm|&|${user.tempGroup}${user.name}|/raw
The server is restarting soon.
Please finish your battles quickly. No new battles can be started until the server resets in a few minutes.
`);\r\n\t\t}\r\n\r\n\t\tthis.lockdown = true;\r\n\t\tthis.writeNumRooms();\r\n\t\tthis.lastReportedCrash = Date.now();\r\n\t}\r\n\tautomaticKillRequest() {\r\n\t\tconst notifyPlaces: RoomID[] = ['development', 'staff', 'upperstaff'];\r\n\t\tif (Config.autolockdown === undefined) Config.autolockdown = true; // on by default\r\n\r\n\t\tif (Config.autolockdown && Rooms.global.lockdown === true && Rooms.global.battleCount === 0) {\r\n\t\t\t// The server is in lockdown, the final battle has finished, and the option is set\r\n\t\t\t// so we will now automatically kill the server here if it is not updating.\r\n\t\t\tif (Monitor.updateServerLock) {\r\n\t\t\t\tthis.notifyRooms(\r\n\t\t\t\t\tnotifyPlaces,\r\n\t\t\t\t\t`|html|
Automatic server lockdown kill canceled.

The server tried to automatically kill itself upon the final battle finishing, but the server was updating while trying to kill itself.
`\r\n\t\t\t\t);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\r\n\t\t\t// final warning\r\n\t\t\tthis.notifyRooms(\r\n\t\t\t\tnotifyPlaces,\r\n\t\t\t\t`|html|
The server is about to automatically kill itself in 10 seconds.
`\r\n\t\t\t);\r\n\r\n\t\t\t// kill server in 10 seconds if it's still set to\r\n\t\t\tsetTimeout(() => {\r\n\t\t\t\tif (Config.autolockdown && Rooms.global.lockdown === true) {\r\n\t\t\t\t\t// finally kill the server\r\n\t\t\t\t\tprocess.exit();\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthis.notifyRooms(\r\n\t\t\t\t\t\tnotifyPlaces,\r\n\t\t\t\t\t\t`|html|
Automatic server lockdown kill canceled.

In the last final seconds, the automatic lockdown was manually disabled.
`\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\t\t\t}, 10 * 1000);\r\n\t\t}\r\n\t}\r\n\tnotifyRooms(rooms: RoomID[], message: string) {\r\n\t\tif (!rooms || !message) return;\r\n\t\tfor (const roomid of rooms) {\r\n\t\t\tconst curRoom = Rooms.get(roomid);\r\n\t\t\tif (curRoom) curRoom.add(message).update();\r\n\t\t}\r\n\t}\r\n\treportCrash(err: Error | string, crasher = \"The server\") {\r\n\t\tconst time = Date.now();\r\n\t\tif (time - this.lastReportedCrash < CRASH_REPORT_THROTTLE) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tthis.lastReportedCrash = time;\r\n\r\n\t\tconst stack = typeof err === 'string' ? err : err?.stack || err?.message || err?.name || '';\r\n\t\tconst [stackFirst, stackRest] = Utils.splitFirst(Utils.escapeHTML(stack), `
`);\r\n\t\tlet fullStack = `${crasher} crashed: ` + stackFirst;\r\n\t\tif (stackRest) fullStack = `
${fullStack}${stackRest}
`;\r\n\r\n\t\tlet crashMessage = `|html|
${fullStack}
`;\r\n\t\tlet privateCrashMessage = null;\r\n\r\n\t\tconst upperStaffRoom = Rooms.get('upperstaff');\r\n\r\n\t\tlet hasPrivateTerm = stack.includes('private');\r\n\t\tfor (const term of (Config.privatecrashterms || [])) {\r\n\t\t\tif (typeof term === 'string' ? stack.includes(term) : term.test(stack)) {\r\n\t\t\t\thasPrivateTerm = true;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (hasPrivateTerm) {\r\n\t\t\tif (upperStaffRoom) {\r\n\t\t\t\tprivateCrashMessage = crashMessage;\r\n\t\t\t\tcrashMessage = `|html|
${crasher} crashed in private code Read more
`;\r\n\t\t\t} else {\r\n\t\t\t\tcrashMessage = `|html|
${crasher} crashed in private code
`;\r\n\t\t\t}\r\n\t\t}\r\n\t\tconst devRoom = Rooms.get('development');\r\n\t\tif (devRoom) {\r\n\t\t\tdevRoom.add(crashMessage).update();\r\n\t\t} else {\r\n\t\t\tRooms.lobby?.add(crashMessage).update();\r\n\t\t\tRooms.get('staff')?.add(crashMessage).update();\r\n\t\t}\r\n\t\tif (privateCrashMessage) {\r\n\t\t\tupperStaffRoom!.add(privateCrashMessage).update();\r\n\t\t}\r\n\t}\r\n\t/**\r\n\t * Destroys personal rooms of a (punished) user\r\n\t * Returns a list of the user's remaining public auth\r\n\t */\r\n\tdestroyPersonalRooms(userid: ID) {\r\n\t\tconst roomauth = [];\r\n\t\tfor (const [id, curRoom] of Rooms.rooms) {\r\n\t\t\tif (curRoom.settings.isPersonal && curRoom.auth.get(userid) === Users.HOST_SYMBOL) {\r\n\t\t\t\tcurRoom.destroy();\r\n\t\t\t} else {\r\n\t\t\t\tif (curRoom.settings.isPrivate || curRoom.battle || !curRoom.persist) {\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (curRoom.auth.has(userid)) {\r\n\t\t\t\t\tlet oldGroup = curRoom.auth.get(userid) as string;\r\n\t\t\t\t\tif (oldGroup === ' ') oldGroup = 'whitelist in ';\r\n\t\t\t\t\troomauth.push(`${oldGroup}${id}`);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn roomauth;\r\n\t}\r\n}\r\n\r\nexport class ChatRoom extends BasicRoom {\r\n\t// This is not actually used, this is just a fake class to keep\r\n\t// TypeScript happy\r\n\tbattle = null;\r\n\tactive: false = false as const;\r\n\ttype: 'chat' = 'chat' as const;\r\n}\r\n\r\nexport class GameRoom extends BasicRoom {\r\n\tdeclare readonly type: 'battle';\r\n\treadonly format: string;\r\n\tp1: User | null;\r\n\tp2: User | null;\r\n\tp3: User | null;\r\n\tp4: User | null;\r\n\t/**\r\n\t * The lower player's rating, for searching purposes.\r\n\t * 0 for unrated battles. 1 for unknown ratings.\r\n\t */\r\n\trated: number;\r\n\tdeclare battle: RoomBattle | null;\r\n\tdeclare game: RoomGame;\r\n\tmodchatUser: string;\r\n\tconstructor(roomid: RoomID, title: string, options: Partial) {\r\n\t\toptions.noLogTimes = true;\r\n\t\toptions.noAutoTruncate = true;\r\n\t\toptions.isMultichannel = true;\r\n\t\tsuper(roomid, title, options);\r\n\t\tthis.reportJoins = !!Config.reportbattlejoins;\r\n\t\tthis.settings.modchat = (Config.battlemodchat || null);\r\n\r\n\t\tthis.type = 'battle';\r\n\r\n\t\tthis.format = options.format || '';\r\n\t\t// console.log(\"NEW BATTLE\");\r\n\r\n\t\tthis.tour = options.tour || null;\r\n\t\tthis.setParent((options as any).parent || (this.tour && this.tour.room) || null);\r\n\r\n\t\tthis.p1 = options.p1?.user || null;\r\n\t\tthis.p2 = options.p2?.user || null;\r\n\t\tthis.p3 = options.p3?.user || null;\r\n\t\tthis.p4 = options.p4?.user || null;\r\n\r\n\t\tthis.rated = options.rated === true ? 1 : options.rated || 0;\r\n\r\n\t\tthis.battle = null;\r\n\t\tthis.game = null!;\r\n\r\n\t\tthis.modchatUser = '';\r\n\r\n\t\tthis.active = false;\r\n\t}\r\n\t/**\r\n\t * - logNum = 0 : spectator log (no exact HP)\r\n\t * - logNum = 1, 2, 3, 4 : player log (exact HP for that player)\r\n\t * - logNum = -1 : debug log (exact HP for all players)\r\n\t */\r\n\tgetLog(channel: -1 | 0 | 1 | 2 | 3 | 4 = 0) {\r\n\t\treturn this.log.getScrollback(channel);\r\n\t}\r\n\tgetLogForUser(user: User) {\r\n\t\tif (!(user.id in this.game.playerTable)) return this.getLog();\r\n\t\t// @ts-ignore\r\n\t\treturn this.getLog(this.game.playerTable[user.id].num);\r\n\t}\r\n\tupdate(excludeUser: User | null = null) {\r\n\t\tif (!this.log.broadcastBuffer.length) return;\r\n\r\n\t\tif (this.userCount) {\r\n\t\t\tSockets.channelBroadcast(this.roomid, `>${this.roomid}\\n${this.log.broadcastBuffer.join('\\n')}`);\r\n\t\t}\r\n\t\tthis.log.broadcastBuffer = [];\r\n\r\n\t\tthis.pokeExpireTimer();\r\n\t}\r\n\tpokeExpireTimer() {\r\n\t\t// empty rooms time out after ten minutes\r\n\t\tif (!this.userCount) {\r\n\t\t\tif (this.expireTimer) clearTimeout(this.expireTimer);\r\n\t\t\tthis.expireTimer = setTimeout(() => this.expire(), TIMEOUT_EMPTY_DEALLOCATE);\r\n\t\t} else {\r\n\t\t\tif (this.expireTimer) clearTimeout(this.expireTimer);\r\n\t\t\tthis.expireTimer = setTimeout(() => this.expire(), TIMEOUT_INACTIVE_DEALLOCATE);\r\n\t\t}\r\n\t}\r\n\tsendPlayer(num: 0 | 1, message: string) {\r\n\t\tconst player = this.getPlayer(num);\r\n\t\tif (!player) return false;\r\n\t\tplayer.sendRoom(message);\r\n\t}\r\n\tgetPlayer(num: 0 | 1) {\r\n\t\t// @ts-ignore\r\n\t\treturn this.game['p' + (num + 1)];\r\n\t}\r\n\trequestModchat(user: User | null) {\r\n\t\tif (!user) {\r\n\t\t\tthis.modchatUser = '';\r\n\t\t\treturn;\r\n\t\t} else if (!this.modchatUser || this.modchatUser === user.id || this.auth.get(user.id) !== Users.PLAYER_SYMBOL) {\r\n\t\t\tthis.modchatUser = user.id;\r\n\t\t\treturn;\r\n\t\t} else {\r\n\t\t\treturn \"Modchat can only be changed by the user who turned it on, or by staff\";\r\n\t\t}\r\n\t}\r\n\tonConnect(user: User, connection: Connection) {\r\n\t\tthis.sendUser(connection, '|init|battle\\n|title|' + this.title + '\\n' + this.getLogForUser(user));\r\n\t\tif (this.game && this.game.onConnect) this.game.onConnect(user, connection);\r\n\t}\r\n\tonJoin(user: User, connection: Connection) {\r\n\t\tif (!user) return false; // ???\r\n\t\tif (this.users[user.id]) return false;\r\n\r\n\t\tif (user.named) {\r\n\t\t\tthis.reportJoin('j', user.getIdentityWithStatus(this), user);\r\n\t\t}\r\n\r\n\t\t// This is only here because of an issue with private logs not getting resent\r\n\t\t// when a user reloads on a battle and autojoins. This should be removed when that gets fixed.\r\n\t\tvoid (async () => {\r\n\t\t\tif (this.battle) {\r\n\t\t\t\tconst player = this.battle.playerTable[user.id];\r\n\t\t\t\tif (player && this.battle.players.every(curPlayer => curPlayer.wantsOpenTeamSheets)) {\r\n\t\t\t\t\tlet buf = '|uhtml|ots|';\r\n\t\t\t\t\tfor (const curPlayer of this.battle.players) {\r\n\t\t\t\t\t\tconst team = await this.battle.getTeam(curPlayer.id);\r\n\t\t\t\t\t\tif (!team) continue;\r\n\t\t\t\t\t\tbuf += Utils.html`
Open Team Sheet for ${curPlayer.name}${Teams.export(team, {hideStats: true})}
`;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tplayer.sendRoom(buf);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t})();\r\n\r\n\t\tthis.users[user.id] = user;\r\n\t\tthis.userCount++;\r\n\t\tthis.checkAutoModchat(user);\r\n\r\n\t\tthis.minorActivity?.onConnect?.(user, connection);\r\n\t\tthis.game?.onJoin?.(user, connection);\r\n\t\tChat.runHandlers('onRoomJoin', this, user, connection);\r\n\t\treturn true;\r\n\t}\r\n\t/**\r\n\t * Sends this room's replay to the connection to be uploaded to the replay\r\n\t * server. To be clear, the replay goes:\r\n\t *\r\n\t * PS server -> user -> loginserver\r\n\t *\r\n\t * NOT: PS server -> loginserver\r\n\t *\r\n\t * That's why this function requires a connection. For details, see the top\r\n\t * comment inside this function.\r\n\t */\r\n\tasync uploadReplay(user: User, connection: Connection, options?: 'forpunishment' | 'silent') {\r\n\t\t// The reason we don't upload directly to the loginserver, unlike every\r\n\t\t// other interaction with the loginserver, is because it takes so much\r\n\t\t// bandwidth that it can get identified as a DoS attack by PHP, Apache, or\r\n\t\t// Cloudflare, and blocked.\r\n\r\n\t\t// While I'm sure this is configurable, it's a huge pain, and getting it\r\n\t\t// wrong, especially while migrating infrastructure, leads to everything\r\n\t\t// being unusable and panic while we figure out how to unblock our servers\r\n\t\t// from each other. It's just easier to \"spread out\" the bandwidth.\r\n\r\n\t\t// TODO: My ideal long-term fix would be to just have a database (probably\r\n\t\t// Postgres) shared between client and server, acting as both the server's\r\n\t\t// battle logs as well as the client's replay database, which both client\r\n\t\t// and server have write access to.\r\n\r\n\t\tconst battle = this.battle;\r\n\t\tif (!battle) return;\r\n\r\n\t\t// retrieve spectator log (0) if there are privacy concerns\r\n\t\tconst format = Dex.formats.get(this.format, true);\r\n\r\n\t\t// custom games always show full details\r\n\t\t// random-team battles show full details if the battle is ended\r\n\t\t// otherwise, don't show full details\r\n\t\tlet hideDetails = !format.id.includes('customgame');\r\n\t\tif (format.team && battle.ended) hideDetails = false;\r\n\r\n\t\tconst data = this.getLog(hideDetails ? 0 : -1);\r\n\t\tconst datahash = crypto.createHash('md5').update(data.replace(/[^(\\x20-\\x7F)]+/g, '')).digest('hex');\r\n\t\tlet rating = 0;\r\n\t\tif (battle.ended && this.rated) rating = this.rated;\r\n\t\tconst {id, password} = this.getReplayData();\r\n\r\n\t\t// STEP 1: Directly tell the login server that a replay is coming\r\n\t\t// (also include all the data, including a hash of the replay itself,\r\n\t\t// so it can't be spoofed.)\r\n\r\n\t\tbattle.replaySaved = true;\r\n\t\tconst [success] = await LoginServer.request('prepreplay', {\r\n\t\t\tid: id,\r\n\t\t\tloghash: datahash,\r\n\t\t\tp1: battle.p1.name,\r\n\t\t\tp2: battle.p2.name,\r\n\t\t\tformat: format.id,\r\n\t\t\trating,\r\n\t\t\thidden: options === 'forpunishment' || (this as any).unlistReplay ?\r\n\t\t\t\t'2' : this.settings.isPrivate || this.hideReplay ? '1' : '',\r\n\t\t\tinputlog: battle.inputLog?.join('\\n') || null,\r\n\t\t});\r\n\t\tif (success?.errorip) {\r\n\t\t\tconnection.popup(`This server's request IP ${success.errorip} is not a registered server.`);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// STEP 2: Tell the user to upload the replay to the login server\r\n\r\n\t\tconnection.send('|queryresponse|savereplay|' + JSON.stringify({\r\n\t\t\tlog: data,\r\n\t\t\tid: id,\r\n\t\t\tpassword: password,\r\n\t\t\tsilent: options === 'forpunishment' || options === 'silent',\r\n\t\t}));\r\n\t}\r\n\r\n\tgetReplayData() {\r\n\t\tif (!this.roomid.endsWith('pw')) return {id: this.roomid.slice(7)};\r\n\t\tconst end = this.roomid.length - 2;\r\n\t\tconst lastHyphen = this.roomid.lastIndexOf('-', end);\r\n\t\treturn {id: this.roomid.slice(7, lastHyphen), password: this.roomid.slice(lastHyphen + 1, end)};\r\n\t}\r\n}\r\n\r\nfunction getRoom(roomid?: string | BasicRoom) {\r\n\tif (typeof roomid === 'string') return Rooms.rooms.get(roomid as RoomID);\r\n\treturn roomid as Room;\r\n}\r\n\r\nexport const Rooms = {\r\n\tModlog: mainModlog,\r\n\t/**\r\n\t * The main roomid:Room table. Please do not hold a reference to a\r\n\t * room long-term; just store the roomid and grab it from here (with\r\n\t * the Rooms.get(roomid) accessor) when necessary.\r\n\t */\r\n\trooms: new Map(),\r\n\taliases: new Map(),\r\n\r\n\tget: getRoom,\r\n\tsearch(name: string): Room | undefined {\r\n\t\treturn getRoom(name) || getRoom(toID(name)) || getRoom(Rooms.aliases.get(toID(name)));\r\n\t},\r\n\r\n\tcreateGameRoom(roomid: RoomID, title: string, options: Partial) {\r\n\t\tif (Rooms.rooms.has(roomid)) throw new Error(`Room ${roomid} already exists`);\r\n\t\tMonitor.debug(\"NEW BATTLE ROOM: \" + roomid);\r\n\t\tconst room = new GameRoom(roomid, title, options);\r\n\t\tRooms.rooms.set(roomid, room);\r\n\t\treturn room;\r\n\t},\r\n\tcreateChatRoom(roomid: RoomID, title: string, options: Partial) {\r\n\t\tif (Rooms.rooms.has(roomid)) throw new Error(`Room ${roomid} already exists`);\r\n\t\tconst room: ChatRoom = new (BasicRoom as any)(roomid, title, options);\r\n\t\tRooms.rooms.set(roomid, room);\r\n\t\treturn room;\r\n\t},\r\n\tcreateBattle(options: RoomBattleOptions & Partial) {\r\n\t\tconst players: User[] = [options.p1, options.p2, options.p3, options.p4]\r\n\t\t\t.filter(Boolean).map(player => player!.user);\r\n\t\tconst gameType = Dex.formats.get(options.format).gameType;\r\n\t\tif (gameType !== 'multi' && gameType !== 'freeforall') {\r\n\t\t\tif (players.length > 2) {\r\n\t\t\t\tthrow new Error(`Four players were provided, but the format is a two-player format.`);\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (new Set(players).size < players.length) {\r\n\t\t\tthrow new Error(`Players can't battle themselves`);\r\n\t\t}\r\n\r\n\t\tfor (const user of players) {\r\n\t\t\tLadders.cancelSearches(user);\r\n\t\t}\r\n\r\n\t\tif (Rooms.global.lockdown === true) {\r\n\t\t\tfor (const user of players) {\r\n\t\t\t\tuser.popup(\"The server is restarting. Battles will be available again in a few minutes.\");\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst p1Special = players.length ? players[0].battleSettings.special : undefined;\r\n\t\tlet mismatch = `\"${p1Special}\"`;\r\n\t\tfor (const user of players) {\r\n\t\t\tif (user.battleSettings.special !== p1Special) {\r\n\t\t\t\tmismatch += ` vs. \"${user.battleSettings.special}\"`;\r\n\t\t\t}\r\n\t\t\tuser.battleSettings.special = undefined;\r\n\t\t}\r\n\r\n\t\tif (mismatch !== `\"${p1Special}\"`) {\r\n\t\t\tfor (const user of players) {\r\n\t\t\t\tuser.popup(`Your special battle settings don't match: ${mismatch}`);\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t} else if (p1Special) {\r\n\t\t\toptions.ratedMessage = p1Special;\r\n\t\t}\r\n\r\n\t\tconst roomid = options.roomid || Rooms.global.prepBattleRoom(options.format);\r\n\t\t// options.rated is a number representing the lowest player rating, for searching purposes\r\n\t\t// options.rated < 0 or falsy means \"unrated\", and will be converted to 0 here\r\n\t\t// options.rated === true is converted to 1 (used in tests sometimes)\r\n\t\toptions.rated = Math.max(+options.rated! || 0, 0);\r\n\t\tconst p1 = players[0];\r\n\t\tconst p2 = players[1];\r\n\t\tconst p1name = p1 ? p1.name : \"Player 1\";\r\n\t\tconst p2name = p2 ? p2.name : \"Player 2\";\r\n\t\tlet roomTitle;\r\n\t\tif (gameType === 'multi') {\r\n\t\t\troomTitle = `Team ${p1name} vs. Team ${p2name}`;\r\n\t\t} else if (gameType === 'freeforall') {\r\n\t\t\t// p1 vs. p2 vs. p3 vs. p4 is too long of a title\r\n\t\t\troomTitle = `${p1name} and friends`;\r\n\t\t} else if (options.title) {\r\n\t\t\troomTitle = options.title;\r\n\t\t} else {\r\n\t\t\troomTitle = `${p1name} vs. ${p2name}`;\r\n\t\t}\r\n\t\toptions.isPersonal = true;\r\n\t\tconst room = Rooms.createGameRoom(roomid, roomTitle, options);\r\n\t\tconst battle = new Rooms.RoomBattle(room, options);\r\n\t\troom.game = battle;\r\n\t\tbattle.checkPrivacySettings(options);\r\n\r\n\t\tfor (const p of players) {\r\n\t\t\tif (p) {\r\n\t\t\t\tp.joinRoom(room);\r\n\t\t\t\tMonitor.countBattle(p.latestIp, p.name);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn room;\r\n\t},\r\n\r\n\tglobal: null! as GlobalRoomState,\r\n\tlobby: null as ChatRoom | null,\r\n\r\n\tBasicRoom,\r\n\tGlobalRoomState,\r\n\tGameRoom,\r\n\tChatRoom: BasicRoom as typeof ChatRoom,\r\n\r\n\tRoomGame,\r\n\tSimpleRoomGame,\r\n\tRoomGamePlayer,\r\n\r\n\tMinorActivity,\r\n\r\n\tRETRY_AFTER_LOGIN,\r\n\r\n\tRoomlogs,\r\n\r\n\tRoomBattle,\r\n\tRoomBattlePlayer,\r\n\tRoomBattleTimer,\r\n\tPM: RoomBattlePM,\r\n};\r\n"], "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BA,iBAAmD;AACnD,2BAAwC;AAIxC,yBAEO;AACP,uBAAuD;AACvD,iCAA+C;AAC/C,sBAAuB;AACvB,aAAwB;AACxB,yBAAuB;AACvB,oBAA6C;AA3C7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBA,MAAM,WAAW,uCAAuC,MAAM,EAAE;AAEhE,MAAM,2BAA2B,KAAK,KAAK;AAC3C,MAAM,8BAA8B,KAAK,KAAK;AAC9C,MAAM,6BAA6B,KAAK,KAAK;AAC7C,MAAM,yBAAyB;AAE/B,MAAM,wBAAwB,KAAK,KAAK;AAExC,MAAM,6BAA6B;AAEnC,MAAM,oBAAoB;AA6HnB,MAAe,UAAU;AAAA,EA4E/B,YAAY,QAAgB,OAAgB,UAAiC,CAAC,GAAG;AAChF,SAAK,QAAQ,uBAAO,OAAO,IAAI;AAC/B,SAAK,OAAO;AACZ,SAAK,YAAY,CAAC;AAElB,SAAK,SAAS;AACd,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,SAAK,OAAO;AAEZ,SAAK,SAAS;AACd,SAAK,QAAS,SAAS;AACvB,SAAK,SAAS;AAEd,SAAK,YAAY;AAEjB,SAAK,OAAO;AACZ,SAAK,SAAS;AAEd,SAAK,YAAY;AAEjB,SAAK,aAAa;AAClB,SAAK,gBAAgB;AACrB,SAAK,oBAAoB;AAIzB,SAAK,WAAW;AAAA,MACf,OAAO,KAAK;AAAA,MACZ,MAAM,uBAAO,OAAO,IAAI;AAAA,MACxB,cAAc,KAAK,IAAI;AAAA,IACxB;AACA,SAAK,UAAU;AACf,SAAK,aAAa;AAClB,SAAK,WAAW;AAChB,SAAK,WAAW;AAChB,SAAK,kBAAkB,CAAC;AACxB,SAAK,OAAO,IAAI,4BAAS,IAAI;AAE7B,SAAK,cAAc;AACnB,SAAK,aAAa;AAClB,SAAK,sBAAsB;AAE3B,YAAQ,QAAQ,KAAK;AACrB,QAAI,QAAQ;AAAQ,cAAQ,iBAAiB;AAC7C,SAAK,cAAc,CAAC,EAAE,OAAO,eAAe,QAAQ;AACpD,SAAK,aAAa,QAAQ,aAAa,IAAI,OAAO,qBAAqB;AACvE,QAAI,CAAC,QAAQ;AAAM,cAAQ,OAAO,CAAC;AAEnC,SAAK,MAAM,yBAAS,OAAO,MAAM,OAAO;AAExC,SAAK,eAAe;AAEpB,SAAK,WAAW;AAChB,QAAI,CAAC,KAAK,SAAS;AAAc,WAAK,SAAS,eAAe,KAAK,IAAI;AACvE,SAAK,KAAK,KAAK;AAEf,QAAI,CAAC,QAAQ;AAAY,WAAK,UAAU;AAExC,SAAK,gBAAgB;AACrB,SAAK,qBAAqB;AAC1B,QAAI,QAAQ,UAAU;AACrB,WAAK,UAAU,MAAM,IAAI,QAAQ,QAAQ,KAAK,IAAI;AAAA,IACnD;AAEA,SAAK,WAAW;AAEhB,SAAK,SAAS;AACd,SAAK,YAAY;AACjB,SAAK,eAAe;AAEpB,SAAK,uBAAuB;AAC5B,SAAK,cAAc;AACnB,QAAI,OAAO,SAAS;AACnB,WAAK,QAAQ,mBAAmB,KAAK,MAAM;AAC3C,UAAI,OAAO,cAAc;AACxB,aAAK,uBAAuB,YAAY,MAAM,KAAK,aAAa,GAAG,OAAO,YAAY;AAAA,MACvF;AAAA,IACD;AAEA,SAAK,WAAW;AAChB,QAAI,KAAK,YAAY;AACpB,WAAK,WAAW,KAAK,YAAY;AAAA,IAClC;AACA,SAAK,mBAAmB;AACxB,SAAK,eAAe;AACpB,SAAK,qBAAqB,oBAAI,IAAI;AAClC,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,cAAc,KAAK,OAAO,KAAK,MAAM;AAAA,EAC3C;AAAA,EAEA,WAAW;AACV,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,SAAiB;AACrB,QAAI,KAAK,WAAW;AAAS,gBAAU,MAAM,KAAK,SAAS,OAAO;AAClE,QAAI,KAAK;AAAW,cAAQ,cAAc,KAAK,QAAQ,OAAO;AAAA,EAC/D;AAAA,EACA,SAAS,MAAc;AACtB,SAAK,gBAAgB,MAAM,GAAG;AAAA,EAC/B;AAAA,EACA,gBAAgB,MAAc,UAAuB,KAAK;AACzD,QAAI,KAAK,SAAS,WAAW;AAC5B,UAAI,CAAC,KAAK;AAAK,cAAM,IAAI,MAAM,cAAc,KAAK,mBAAmB;AACrE,WAAK,IAAI,IAAI,IAAI;AACjB;AAAA,IACD;AAEA,eAAW,KAAK,KAAK,OAAO;AAC3B,YAAM,OAAO,KAAK,MAAM,CAAC;AAEzB,UAAI,KAAK,WAAW,KAAK,KAAK,QAAQ,MAAM,OAAO,GAAG;AACrD,aAAK,OAAO,MAAM,IAAI;AAAA,MACvB;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAIA,SAAS,MAAyB,SAAiB;AAClD,SAAK,OAAO,MAAM,OAAO;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,SAAiB;AACpB,SAAK,IAAI,IAAI,OAAO;AACpB,WAAO;AAAA,EACR;AAAA,EACA,QAAQ,SAAiB;AACxB,SAAK,IAAI,QAAQ,OAAO;AACxB,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAIA,OAAO,OAA2B;AACjC,UAAM,WAAW,KAAK,OAAO,GAAG,KAAK,sBAAsB,KAAK,KAAK,WAAW;AAChF,SAAK,IAAI,OAAO,OAAO,QAAQ;AAC/B,WAAO;AAAA,EACR;AAAA,EACA,YAAY,MAAc,SAAiB;AAC1C,SAAK,IAAI,YAAY,MAAM,OAAO;AAAA,EACnC;AAAA,EACA,sBAAsB,MAAY,MAAc,SAAiB;AAChE,SAAK,IAAI,sBAAsB,MAAM,MAAM,OAAO;AAAA,EACnD;AAAA,EACA,SAAS,SAAe,YAAY,GAAG,kBAA4B;AAClE,UAAM,UAAU,KAAK,IAAI,UAAU,SAAS,SAAS;AACrD,eAAW,UAAU,SAAS;AAC7B,WAAK,KAAK,cAAc,mBAAmB,WAAW,UAAU,UAAU,WAAW;AAAA,IACtF;AACA,SAAK,OAAO;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAIA,OAAO,SAAiB;AACvB,WAAO,KAAK,IAAI,UAAU,OAAO;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,MAAY,MAAoB;AACzC,WAAO,KAAK,IAAI,QAAQ,KAAK,YAAY,IAAI,IAAI,WAAW,IAAI;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA,EAIA,WAAW,MAAmB,MAAc;AAC3C,SAAK,KAAK,SAAS,OAAO,KAAK,YAAY,IAAI,IAAI,OAAO,WAAW,IAAI;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA,EAIA,eAAe,MAAY,MAAc;AACxC,SAAK,SAAS,QAAQ,KAAK,YAAY,IAAI,IAAI,WAAW,IAAI;AAAA,EAC/D;AAAA,EACA,SAAS;AACR,QAAI,CAAC,KAAK,IAAI,gBAAgB;AAAQ;AACtC,QAAI,KAAK,qBAAqB;AAC7B,oBAAc,KAAK,mBAAmB;AACtC,WAAK,sBAAsB;AAC3B,WAAK,WAAW,KAAK,YAAY;AAAA,IAClC;AACA,SAAK,KAAK,KAAK,IAAI,gBAAgB,KAAK,IAAI,CAAC;AAC7C,SAAK,IAAI,kBAAkB,CAAC;AAC5B,SAAK,IAAI,SAAS;AAElB,SAAK,gBAAgB;AAAA,EACtB;AAAA,EAEA,cAAc;AACb,QAAI,SAAS;AACb,QAAI,UAAU;AACd,eAAW,KAAK,KAAK,OAAO;AAC3B,UAAI,CAAC,KAAK,MAAM,CAAC,EAAE,OAAO;AACzB;AAAA,MACD;AACA;AACA,gBAAU,MAAM,KAAK,MAAM,CAAC,EAAE,sBAAsB,IAAI;AAAA,IACzD;AACA,UAAM,MAAM,UAAU,UAAU;AAChC,WAAO;AAAA,EACR;AAAA,EAEA,iBAAiB;AAChB,UAAM,cAAc,KAAK,SAAS,cAAc,KAAK;AACrD,SAAK,SAAS,aAAa;AAC3B,SAAK,aAAa;AAClB,WAAO;AAAA,EACR;AAAA;AAAA,EAIA,aAAa,kBAAkB,OAAO;AACrC,QAAI,mBAAmB,KAAK,WAAW;AACtC,mBAAa,KAAK,SAAS;AAC3B,WAAK,YAAY;AAAA,IAClB;AACA,QAAI,KAAK,aAAa,KAAK,UAAU,WAAW;AAAG;AAEnD,UAAM,kBAAkB,KAAK,UAAU,CAAC,EAAE,OAAO,KAAK,IAAI;AAC1D,QAAI,mBAAmB,KAAM;AAC5B,WAAK,OAAO,KAAK,UAAU,CAAC,EAAE,QAAQ,mBAAmB,KAAK,QAAQ,gBAAgB;AAEtF;AAAA,IACD;AACA,SAAK,YAAY,WAAW,MAAM;AACjC,WAAK,YAAY;AACjB,WAAK,aAAa,IAAI;AAAA,IACvB,GAAG,eAAe;AAAA,EACnB;AAAA,EACA,QAAQ,MAA4B;AACnC,QAAI,CAAC;AAAM;AACX,QAAI,KAAK,WAAW;AACnB,iBAAW,SAAS,KAAK,WAAW;AACnC,YAAI,KAAK,OAAO,MAAM,UACrB,KAAK,aAAa,MAAM,YACvB,KAAK,iBAAiB,KAAK,kBAAkB,MAAM,eAAgB;AACpE,cAAI,MAAM,OAAO,KAAK,IAAI,IAAI,GAAG;AAChC,iBAAK,OAAO,KAAK,EAAE;AACnB;AAAA,UACD,OAAO;AACN,mBAAO,MAAM;AAAA,UACd;AAAA,QACD;AAAA,MACD;AAAA,IACD;AACA,QAAI,KAAK;AAAQ,aAAO,KAAK,OAAO,QAAQ,IAAI;AAAA,EACjD;AAAA,EACA,YAAY,MAAgC;AAC3C,UAAM,SAAS,KAAK,QAAQ,IAAI;AAChC,QAAI,CAAC;AAAQ;AACb,eAAW,SAAS,KAAK,WAAW;AACnC,UAAI,WAAW,MAAM,QAAQ;AAC5B,eAAO,MAAM,OAAO,KAAK,IAAI;AAAA,MAC9B;AAAA,IACD;AACA,QAAI,KAAK;AAAQ,aAAO,KAAK,OAAO,YAAY,IAAI;AAAA,EACrD;AAAA;AAAA;AAAA,EAGA,QAA4B,aAAwC,UAAU,OAAiB;AAE9F,QAAI,WAAW,KAAK,WAAW,KAAK,QAAQ,YAAY,SAAS,YAAY;AAAM,aAAO,KAAK;AAC/F,QAAI,KAAK,QAAQ,KAAK,KAAK,YAAY,SAAS,YAAY;AAAM,aAAO,KAAK;AAC9E,WAAO;AAAA,EACR;AAAA,EACA,iBAA0C,aAAkD;AAC3F,QAAI,KAAK,eAAe,YAAY,SAAS,YAAY;AAAM,aAAO,KAAK;AAC3E,WAAO;AAAA,EACR;AAAA,EACA,sBAAsB,WAAW,OAAmC;AACnE,UAAM,YAAY,WAAW,KAAK,SAAS,qBAAqB,KAAK;AACrE,QAAI,CAAC,WAAW;AAAQ,aAAO;AAC/B,WAAO;AAAA,EACR;AAAA,EACA,mBAAmB,UAAmC;AACrD,QAAI,CAAC,KAAK;AAAoB,WAAK,qBAAqB,CAAC;AACzD,SAAK,mBAAmB,KAAK,QAAQ;AACrC,SAAK,SAAS,qBAAqB,KAAK;AAAA,EACzC;AAAA,EACA,wBAAwB,MAAe,QAAQ,GAAG;AACjD,QAAI,CAAC,KAAK;AAAoB;AAC9B,QAAI,SAAS,QAAW;AACvB,WAAK,qBAAqB;AAC1B,aAAO,KAAK,SAAS;AACrB,WAAK,aAAa;AAAA,IACnB,OAAO;AACN,WAAK,mBAAmB,OAAO,MAAM,KAAK;AAC1C,WAAK,SAAS,qBAAqB,KAAK;AACxC,WAAK,aAAa;AAClB,UAAI,CAAC,KAAK,mBAAmB;AAAQ,aAAK,wBAAwB;AAAA,IACnE;AAAA,EACD;AAAA,EACA,iBAAiB,UAAgC,YAAY,OAAa;AACzE,SAAK,eAAe,SAAS;AAC7B,SAAK,gBAAgB;AACrB,QAAI,KAAK,eAAe;AACvB,WAAK,cAAc,KAAK;AACxB,UAAI,CAAC;AAAW,aAAK,cAAc,QAAQ;AAAA,IAC5C,OAAO;AACN,aAAO,KAAK,SAAS;AACrB,WAAK,aAAa;AAAA,IACnB;AAAA,EACD;AAAA,EACA,eAAe;AACd,QAAI,CAAC,KAAK;AAAS;AAEnB,QAAI,CAAC,MAAM;AAAQ;AAEnB,UAAM,OAAO,kBAAkB;AAAA,EAChC;AAAA,EACA,aAAa,MAAY;AACxB,QAAI,KAAK,MAAM,KAAK;AAAO,aAAO;AAClC,QAAI,CAAC,KAAK,SAAS;AAAS,aAAO;AAEnC,QAAI,KAAK,KAAK,IAAI,KAAK,EAAE;AAAG,aAAO;AAEnC,UAAM,iBAAiB,KAAK,SAAS,YAAY,OAAO,KAAK,SAAS,UAAU,KAAK,SAAS;AAC9F,QAAI,CAAC;AAAgB,aAAO;AAC5B,QAAI,CAAC,MAAM,KAAK,YAAY,cAAc,GAAG;AAC5C,cAAQ,MAAM,8BAA8B,KAAK,WAAW,gBAAgB;AAAA,IAC7E;AACA,WACC,KAAK,KAAK,QAAQ,MAAM,cAAc,KAAK,MAAM,WAAW,QAAQ,MAAM,cAAc;AAAA,EAE1F;AAAA,EACA,KAAK,MAAY,SAAkB;AAClC,UAAM,SAAS,KAAK;AAEpB,QAAI,CAAC;AAAS,gBAAU,IAAI;AAC5B,QAAI,UAAU,KAAK;AAAO,gBAAU,KAAK;AAGzC,QAAI,KAAK,QAAQ,IAAI;AAAG,WAAK,OAAO,MAAM;AAG1C,aAAS,IAAI,GAAG,KAAK,KAAK,UAAU,QAAQ,KAAK;AAChD,YAAM,OAAO,KAAK,IAAI,IAAI;AAC1B,UAAI,MAAM,KAAK,UAAU,UAAU,OAAO,KAAK,UAAU,CAAC,EAAE,MAAM;AACjE,cAAM,QAAQ;AAAA,UACb;AAAA,UACA;AAAA,UACA,UAAU,KAAK;AAAA,UACf,eAAe,KAAK;AAAA,QACrB;AACA,aAAK,UAAU,OAAO,GAAG,GAAG,KAAK;AAGjC,YAAI,MAAM,KAAK,KAAK,WAAW;AAC9B,uBAAa,KAAK,SAAS;AAC3B,eAAK,YAAY;AAAA,QAClB;AACA;AAAA,MACD;AAAA,IACD;AACA,SAAK,aAAa;AAElB,SAAK,eAAe;AAEpB,QAAI,EAAE,KAAK,SAAS,cAAc,QAAQ,KAAK,SAAS,aAAa;AACpE,WAAK,YAAY,uBAAuB,IAAI;AAAA,IAC7C;AAEA,WAAO;AAAA,EACR;AAAA,EACA,OAAO,QAAgB,YAAqB;AAC3C,QAAI,gBAAgB;AACpB,UAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,QAAI,gBAAgB;AACpB,QAAI,MAAM;AACT,eAAS,KAAK;AACd,sBAAgB,KAAK;AAAA,IACtB;AAEA,eAAW,CAAC,GAAG,KAAK,KAAK,KAAK,UAAU,QAAQ,GAAG;AAClD,UAAI,MAAM,WAAW,UACnB,QAAQ,MAAM,aAAa,KAAK,YAChC,iBAAiB,MAAM,kBAAkB,eAAgB;AAC1D,YAAI,MAAM,GAAG;AACZ,eAAK,UAAU,OAAO,GAAG,CAAC;AAC1B,eAAK,aAAa,IAAI;AAAA,QACvB,OAAO;AACN,eAAK,UAAU,OAAO,GAAG,CAAC;AAAA,QAC3B;AACA,wBAAgB,MAAM;AACtB;AAAA,MACD;AAAA,IACD;AAEA,QAAI,QAAQ,iBAAiB,UAAU,KAAK,OAAO;AAClD,WAAK,eAAe;AACpB,UAAI;AAAY,aAAK,MAAM,UAAU;AAAA,IACtC;AACA,WAAO;AAAA,EACR;AAAA,EAEA,eAAe;AACd,QAAI,QAAQ;AACZ,QAAI,SAAS;AACb,UAAM,SAAgC,CAAC;AACvC,eAAW,SAAS,OAAO,eAAe;AACzC,aAAO,KAAK,IAAI;AAAA,IACjB;AACA,eAAW,KAAK,KAAK,OAAO;AAC3B,YAAM,OAAO,KAAK,MAAM,CAAC;AACzB,QAAE;AACF,UAAI,CAAC,KAAK,OAAO;AAChB,UAAE;AAAA,MACH;AACA,QAAE,OAAO,KAAK,KAAK,IAAI,KAAK,EAAE,CAAC;AAAA,IAChC;AACA,QAAI,QAAQ,sBAAsB,QAAQ,aAAa;AACvD,eAAW,KAAK,QAAQ;AACvB,eAAS,MAAM,IAAI,MAAM,OAAO,CAAC;AAAA,IAClC;AACA,SAAK,QAAQ,KAAK;AAAA,EACnB;AAAA,EAEA,kBAAkB;AACjB,QAAI,KAAK;AAAa,mBAAa,KAAK,WAAW;AACnD,QAAI,KAAK,SAAS,YAAY;AAC7B,WAAK,cAAc,WAAW,MAAM,KAAK,OAAO,GAAG,2BAA2B;AAAA,IAC/E,OAAO;AACN,WAAK,cAAc;AAAA,IACpB;AAAA,EACD;AAAA,EACA,SAAS;AACR,SAAK,KAAK,UAAU;AACpB,SAAK,QAAQ;AAAA,EACd;AAAA,EACA,WAAW,MAAuB,OAAe,MAAY;AAC5D,UAAM,UAAU,KAAK,KAAK,QAAQ,MAAM,KAAK,SAAS,WAAW,UAAU,KAAK,CAAC,KAAK,QAAQ,IAAI;AAClG,QAAI,KAAK,gBAAgB,WAAW,KAAK,KAAK,IAAI,KAAK,EAAE,IAAI;AAC5D,WAAK,IAAI,IAAI,QAAQ,OAAO,EAAE,OAAO;AACrC;AAAA,IACD;AACA,QAAI,SAAS;AACb,YAAQ,MAAM;AAAA,MACd,KAAK;AAAK,iBAAS;AAAK;AAAA,MACxB,KAAK;AAAK,iBAAS;AAAK;AAAA,MACxB,KAAK;AAAK,iBAAS;AAAK;AAAA,IACxB;AACA,YAAQ,IAAI,UAAU;AACtB,QAAI,KAAK,YAAY;AACpB,WAAK,IAAI,gBAAgB,KAAK,KAAK;AAEnC,UAAI,CAAC,KAAK,qBAAqB;AAC9B,aAAK,sBAAsB;AAAA,UAC1B,MAAM,KAAK,OAAO;AAAA,UAAG,KAAK;AAAA,QAC3B;AAAA,MACD;AAAA,IACD,OAAO;AACN,WAAK,KAAK,KAAK;AAAA,IAChB;AACA,SAAK,QAAQ,KAAK;AAAA,EACnB;AAAA,EACA,gBAAgB,MAAY;AAC3B,QAAI,UAAU,iBAAM,+CAA+C,KAAK;AACxE,QAAI,KAAK,SAAS,SAAS;AAC1B,iBAAW,KAAK,KAAK,SAAS;AAAA,IAC/B;AACA,QAAI,KAAK,SAAS,SAAS;AAC1B,YAAM,UAAU,KAAK,SAAS,YAAY,OAAO,KAAK,SAAS,UAAU,KAAK,SAAS;AACvF,iBAAW,KAAK;AAAA,IACjB;AACA,QAAI,KAAK,SAAS,UAAU;AAC3B,iBAAW,cAAc,KAAK,SAAS;AAAA,IACxC;AACA,eAAW;AACX,QAAI,KAAK,SAAS,cAAc;AAC/B,iBAAW;AAAA,mDAAuD,KAAK,SAAS,YAAY,aAAa,4BAA4B,QACpI,KAAK,SAAS,aAAa,QAAQ,OAAO,EAAE,IAC5C;AAAA,IACF;AACA,UAAM,aAAa,KAAK,qBAAqB,IAAI;AACjD,QAAI;AAAY,iBAAW;AAAA,EAAK;AAChC,WAAO;AAAA,EACR;AAAA,EACA,qBAAqB,MAAY;AAChC,QAAI,CAAC,KAAK,IAAI,QAAQ,MAAM,IAAI;AAAG,aAAO;AAC1C,QAAI,UAAU;AACd,QAAI,KAAK,SAAS,cAAc;AAC/B,iBAAW;AAAA,uDACV,KAAK,SAAS,aAAa,QAAQ,OAAO,EAAE,IAC5C;AAAA,IACF;AACA,QAAI,KAAK,kBAAkB,MAAM;AAChC,iBAAW;AAAA;AACX,iBAAW,mDAAmD,KAAK,iBAAiB;AACpF,iBAAW,CAAC,QAAQ,KAAK,KAAK,KAAK,kBAAkB;AACpD,mBAAW;AACX,mBAAW,kCAAkC;AAC7C,YAAI,MAAM,YAAY;AACrB,gBAAM,CAAC,OAAO,QAAQ,OAAO,IAAI,MAAM;AACvC,qBAAW,0CAA0C,MAAM,gBAAgB,kBAAkB;AAC7F,cAAI;AAAS,uBAAW;AAAA,QACzB,OAAO;AACN,qBAAW,yCAAyC,MAAM;AAAA,QAC3D;AACA,mBAAW,6BAA6B,MAAM,UAAU,MAAM,UAAU;AACxE,mBAAW,0DAA0D,+EACd;AACvD,mBAAW;AAAA,MACZ;AACA,iBAAW;AAAA,IACZ;AACA,WAAO,UAAU,QAAQ,YAAY;AAAA,EACtC;AAAA,EACA,YAAY,gBAAgB,OAAO;AAClC,QAAI,CAAC,KAAK;AAAU,aAAO,CAAC;AAC5B,WAAO,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE;AAAA,MAClC,UAAQ,gBAAgB,OAAO,CAAC,KAAK,SAAS,aAAa,CAAC,KAAK,SAAS;AAAA,IAC3E;AAAA,EACD;AAAA,EACA,cAAc,UAAkB,OAAgB;AAC/C,QAAI,CAAC;AAAO,cAAQ,KAAK,QAAQ;AAIjC,QAAI,SAAS,SAAS,GAAG,KAAK,SAAS,SAAS,GAAG,GAAG;AACrD,YAAM,IAAI,KAAK,aAAa,eAAe,oCAAoC;AAAA,IAChF;AACA,SAAK,CAAC,MAAM,SAAS,GAAG,KAAK,MAAM,WAAW,YAAY,MAAM,SAAS,SAAS,GAAG,GAAG;AACvF,YAAM,IAAI,KAAK,aAAa,eAAe,2BAA2B;AAAA,IACvE;AACA,QAAI,MAAM,SAAS;AAAwB,YAAM,IAAI,KAAK,aAAa,mCAAmC;AAC1G,QAAI,MAAM,OAAO,QAAQ;AAAG,YAAM,IAAI,KAAK,aAAa,aAAa,2BAA2B;AAAA,EACjG;AAAA,EACA,UAAU,MAAmB;AAC5B,QAAI,KAAK,WAAW;AAAM;AAE1B,QAAI,KAAK,QAAQ;AAChB,MAAC,KAAa,OAAO,SAAS,OAAO,KAAK,MAAM;AAChD,UAAI,CAAC,KAAK,OAAO,SAAU,MAAM;AAChC,QAAC,KAAa,OAAO,WAAW;AAAA,MACjC;AAAA,IACD;AACA,IAAC,KAAa,SAAS;AACvB,QAAI,MAAM;AACT,UAAI,CAAC,KAAK,UAAU;AACnB,QAAC,KAAa,WAAW,oBAAI,IAAI;AAAA,MAClC;AACA,MAAC,KAAa,SAAS,IAAI,KAAK,QAAQ,IAAI;AAC5C,WAAK,SAAS,WAAW,KAAK;AAAA,IAC/B,OAAO;AACN,aAAO,KAAK,SAAS;AAAA,IACtB;AAEA,SAAK,aAAa;AAElB,eAAW,UAAU,KAAK,OAAO;AAChC,WAAK,MAAM,MAAM,EAAE,eAAe,KAAK,MAAM;AAAA,IAC9C;AAAA,EACD;AAAA,EACA,gBAAgB;AACf,QAAI,CAAC,KAAK;AAAU;AACpB,eAAW,QAAQ,KAAK,SAAS,OAAO,GAAG;AAC1C,MAAC,KAAa,SAAS;AAAA,IACxB;AACA,IAAC,KAAa,WAAW;AAAA,EAI1B;AAAA,EACA,WAAW,SAAyB;AACnC,SAAK,SAAS,YAAY;AAC1B,SAAK,aAAa;AAElB,QAAI,SAAS;AACZ,iBAAW,QAAQ,OAAO,OAAO,KAAK,KAAK,GAAG;AAC7C,YAAI,CAAC,KAAK,OAAO;AAChB,eAAK,UAAU,KAAK,MAAM;AAC1B,eAAK,MAAM,cAAc,KAAK,yEAAyE;AAAA,QACxG;AAAA,MACD;AAAA,IACD;AAEA,QAAI,KAAK,QAAQ;AAChB,UAAI,SAAS;AACZ,YAAI,KAAK,OAAO,SAAS,IAAI;AAAG,iBAAO;AAIvC,YAAI,WAAW;AACf,iBAAS,IAAI,GAAG,IAAI,IAAI;AAAK,sBAAY,SAAS,KAAK,MAAM,KAAK,OAAO,IAAI,SAAS,MAAM,CAAC;AAE7F,aAAK,OAAO,KAAK,OAAO,GAAG,KAAK,UAAU,cAAwB,IAAI;AAAA,MACvE,OAAO;AACN,YAAI,CAAC,KAAK,OAAO,SAAS,IAAI;AAAG,iBAAO;AAExC,cAAM,gBAAgB,KAAK,OAAO,YAAY,GAAG;AACjD,YAAI,gBAAgB;AAAG,gBAAM,IAAI,MAAM,qBAAqB,KAAK,QAAQ;AAEzE,aAAK,OAAO,KAAK,OAAO,KAAK,OAAO,MAAM,GAAG,aAAa,CAAW;AAAA,MACtE;AAAA,IACD;AAAA,EACD;AAAA,EACA,gBAAgB,SAAiB;AAChC,UAAM,SAAS,KAAK,OAAO;AAC3B,QAAI,CAAC,kCAAa,SAAS,SAAS,MAAa,GAAG;AACnD,YAAM,IAAI,KAAK,aAAa,IAAI,kEAAkE,kCAAa,SAAS,KAAK,IAAI,GAAG;AAAA,IACrI;AACA,WAAO;AAAA,EACR;AAAA,EACA,WAAW,SAAkB;AAC5B,QAAI,CAAC,KAAK,SAAS;AAClB,YAAM,IAAI,KAAK,aAAa,mDAAmD;AAAA,IAChF;AACA,QAAI,SAAS;AACZ,YAAM,mBAAmB,KAAK,gBAAgB,OAAO;AACrD,UAAI,KAAK,SAAS,aAAa,CAAC,MAAM,QAAQ,EAAE,SAAS,KAAK,SAAS,SAAS,GAAG;AAClF,cAAM,IAAI,KAAK,aAAa,6CAA6C;AAAA,MAC1E;AACA,YAAM,aAAa,KAAK,SAAS;AACjC,UAAI,eAAe,SAAS;AAC3B,cAAM,IAAI,KAAK,aAAa,GAAG,KAAK,2CAA2C,kCAAa,aAAa,UAAU,KAAK;AAAA,MACzH;AACA,WAAK,SAAS,UAAU;AACxB,WAAK,aAAa;AAClB,aAAO;AAAA,IACR;AACA,WAAO,KAAK,SAAS;AACrB,SAAK,aAAa;AAClB,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBAAiB,SAAiB;AACjC,UAAM,SAAS,OAAO,OAAO,KAAK,KAAK,EAAE,OAAO,OAAK,CAAC,EAAE,IAAI,MAAM,CAAC;AACnE,eAAW,QAAQ,QAAQ;AAC1B,WAAK,MAAM,UAAU,SAAS;AAAA,IAC/B;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,UAAkB,OAAgB,SAAmB;AAC3D,QAAI,CAAC;AAAO,cAAQ,KAAK,QAAQ;AACjC,SAAK,cAAc,UAAU,KAAK;AAClC,QAAI,KAAK,SAAS,UAAU,KAAK,MAAM;AACtC,YAAM,IAAI,KAAK,aAAa,4BAA4B,KAAK,KAAK,0BAA0B,KAAK,SAAS;AAAA,IAC3G;AACA,UAAM,QAAQ,KAAK;AACnB,IAAC,KAAa,SAAS;AACvB,SAAK,QAAQ;AACb,UAAM,MAAM,OAAO,KAAK;AACxB,UAAM,MAAM,IAAI,OAAO,IAAY;AACnC,QAAI,KAAK,UAAU,OAAO;AACzB,iBAAW,UAAU,KAAK,OAAO,SAAS;AACzC,YAAI,OAAO,QAAQ;AAClB,gBAAM,QAAQ,QAAQ,WAAW,aAAa,OAAO,QAAQ,KAAK;AAClE,cAAI;AAAO,kBAAM,SAAS,KAAK;AAAA,QAChC;AAAA,MACD;AAAA,IACD;AAEA,QAAI,UAAU,SAAS;AACtB,YAAM,QAAQ;AAAA,IACf,WAAW,UAAU,SAAS;AAC7B,YAAM,QAAQ;AAAA,IACf;AAEA,QAAI,CAAC,SAAS;AACb,iBAAW,CAAC,OAAO,MAAM,KAAK,MAAM,QAAQ,QAAQ,GAAG;AACtD,YAAI,WAAW,OAAO;AACrB,gBAAM,QAAQ,IAAI,OAAO,KAAK;AAAA,QAC/B;AAAA,MACD;AAGA,YAAM,QAAQ,IAAI,OAAO,KAAK;AAC9B,UAAI,CAAC,KAAK,SAAS;AAAS,aAAK,SAAS,UAAU,CAAC;AAErD,UAAI,CAAC,KAAK,SAAS,QAAQ,SAAS,KAAK;AAAG,aAAK,SAAS,QAAQ,KAAK,KAAK;AAAA,IAC7E,OAAO;AAEN,iBAAW,CAAC,OAAO,MAAM,KAAK,MAAM,QAAQ,QAAQ,GAAG;AACtD,YAAI,WAAW,OAAO;AACrB,gBAAM,QAAQ,OAAO,KAAK;AAAA,QAC3B;AAAA,MACD;AACA,WAAK,SAAS,UAAU;AAAA,IACzB;AAEA,SAAK,MAAM,WAAW,KAAK;AAE3B,SAAK,aAAa;AAElB,eAAW,QAAQ,OAAO,OAAO,KAAK,KAAK,GAAG;AAC7C,WAAK,gBAAgB,OAAO,KAAK;AACjC,WAAK,KAAK,IAAI;AAAA,iBAAyB,SAAS,UAAU;AAAA,IAC3D;AAEA,QAAI,KAAK,UAAU,KAAK,OAAO,UAAU;AACxC,MAAC,KAAa,OAAO,SAAS,OAAO,KAAK;AAC1C,MAAC,KAAa,OAAO,SAAS,IAAI,OAAO,IAAgB;AAAA,IAC1D;AACA,QAAI,KAAK,UAAU;AAClB,iBAAW,WAAW,KAAK,SAAS,OAAO,GAAG;AAC7C,QAAC,QAAgB,SAAS;AAC1B,gBAAQ,SAAS,WAAW;AAAA,MAC7B;AAAA,IACD;AAEA,SAAK,SAAS,QAAQ;AACtB,SAAK,aAAa;AAElB,gBAAY,WAAW,OAAO,KAAK;AAEnC,SAAK,KAAK,IAAI,OAAO,KAAK;AAAA,EAC3B;AAAA,EAEA,UAAU,MAAY,YAAwB;AAC7C,UAAM,WAAW,KAAK,WAAW,KAAK,WAAW,KAAK,YAAY;AAClE,SAAK;AAAA,MACJ;AAAA,MACA,wBAAwB,KAAK,QAAQ,OAAO,WAAW,OAAO,KAAK,IAAI,cAAc,IAAI,KAAK,gBAAgB,IAAI;AAAA,IACnH;AACA,SAAK,eAAe,YAAY,MAAM,UAAU;AAChD,SAAK,MAAM,YAAY,MAAM,UAAU;AAAA,EACxC;AAAA,EACA,OAAO,MAAY,YAAwB;AAC1C,QAAI,CAAC;AAAM,aAAO;AAClB,QAAI,KAAK,MAAM,KAAK,EAAE;AAAG,aAAO;AAEhC,QAAI,KAAK,OAAO;AACf,WAAK,WAAW,KAAK,KAAK,sBAAsB,IAAI,GAAG,IAAI;AAAA,IAC5D;AAEA,UAAM,aAAa,KAAK,qBAAqB,IAAI;AACjD,QAAI;AAAY,WAAK,SAAS,MAAM,UAAU;AAE9C,SAAK,MAAM,KAAK,EAAE,IAAI;AACtB,SAAK;AACL,SAAK,iBAAiB,IAAI;AAE1B,SAAK,eAAe,YAAY,MAAM,UAAU;AAChD,SAAK,MAAM,SAAS,MAAM,UAAU;AACpC,SAAK,YAAY,cAAc,MAAM,MAAM,UAAU;AACrD,WAAO;AAAA,EACR;AAAA,EACA,SAAS,MAAY,OAAW,SAAkB;AACjD,QAAI,KAAK,OAAO,OAAO;AACtB,aAAO,KAAK,iBAAiB,IAAI;AAAA,IAClC;AACA,QAAI,CAAC,KAAK,MAAM,KAAK,GAAG;AACvB,cAAQ,SAAS,IAAI,MAAM,QAAQ,qBAAqB,KAAK,QAAQ,CAAC;AAAA,IACvE;AACA,QAAI,KAAK,MAAM,KAAK,EAAE,GAAG;AACxB,cAAQ,SAAS,IAAI,MAAM,QAAQ,KAAK,sBAAsB,KAAK,QAAQ,CAAC;AAAA,IAC7E;AACA,WAAO,KAAK,MAAM,KAAK;AACvB,SAAK,MAAM,KAAK,EAAE,IAAI;AACtB,QAAI,SAAS;AACZ,WAAK,WAAW,KAAK,KAAK,sBAAsB,IAAI,GAAG,IAAI;AAC3D,YAAM,aAAa,KAAK,qBAAqB,IAAI;AACjD,UAAI;AAAY,aAAK,SAAS,MAAM,UAAU;AAAA,IAC/C,WAAW,CAAC,KAAK,OAAO;AACvB,WAAK,WAAW,KAAK,OAAO,IAAI;AAAA,IACjC,OAAO;AACN,WAAK,WAAW,KAAK,KAAK,sBAAsB,IAAI,IAAI,MAAM,OAAO,IAAI;AAAA,IAC1E;AACA,SAAK,eAAe,WAAW,MAAM,OAAO,OAAO;AACnD,SAAK,iBAAiB,IAAI;AAC1B,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAIA,iBAAiB,MAAY;AAC5B,QAAI,MAAM,WAAW;AACpB,UAAI,CAAC,KAAK,MAAM,KAAK,EAAE;AAAG,eAAO;AACjC,UAAI,KAAK,OAAO;AACf,aAAK,WAAW,KAAK,KAAK,sBAAsB,IAAI,IAAI,MAAM,KAAK,IAAI,IAAI;AAAA,MAC5E;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EACA,QAAQ,MAAY;AACnB,QAAI,CAAC;AAAM,aAAO;AAElB,QAAI,EAAE,KAAK,MAAM,KAAK,QAAQ;AAC7B,cAAQ,SAAS,IAAI,MAAM,QAAQ,KAAK,iBAAiB,CAAC;AAC1D,aAAO;AAAA,IACR;AACA,WAAO,KAAK,MAAM,KAAK,EAAE;AACzB,SAAK;AAEL,QAAI,KAAK,OAAO;AACf,WAAK,WAAW,KAAK,KAAK,YAAY,IAAI,GAAG,IAAI;AAAA,IAClD;AACA,QAAI,KAAK,QAAQ,KAAK,KAAK;AAAS,WAAK,KAAK,QAAQ,IAAI;AAC1D,SAAK,eAAe;AAEpB,WAAO;AAAA,EACR;AAAA,EAEA,iBAAiB;AAChB,QAAI,CAAC,KAAK,SAAS,eAAe,KAAK,SAAS,YAAY;AAAQ;AAEpE,UAAM,QAAQ,OAAO,OAAO,KAAK,KAAK,EAAE,OAAO,OAAK,KAAK,KAAK,QAAQ,GAAG,GAAG,CAAC;AAC7E,QAAI,CAAC,MAAM,QAAQ;AAClB,YAAM,EAAC,KAAI,IAAI,KAAK,SAAS;AAC7B,UAAI,CAAC,QAAQ,OAAO,GAAG;AACtB,cAAM,IAAI,MAAM,yCAAyC,iBAAM,UAAU,KAAK,SAAS,WAAW,IAAI;AAAA,MACvG;AACA,UAAI,KAAK;AAAc,qBAAa,KAAK,YAAY;AACrD,WAAK,eAAe,WAAW,MAAM;AACpC,YAAI,CAAC,KAAK,SAAS;AAAa;AAChC,cAAM,EAAC,KAAI,IAAI,KAAK,SAAS;AAC7B,cAAM,aAAa,KAAK,SAAS;AACjC,aAAK,SAAS,UAAU;AACxB,aAAK;AAAA;AAAA,UAEJ,kFAAkF,4CACnD;AAAA,QAChC,EAAE,OAAO;AACT,aAAK,OAAO;AAAA,UACX,QAAQ;AAAA,QACT,CAAC;AAED,aAAK,SAAS,YAAY,SAAS,cAAc;AACjD,aAAK,aAAa;AAAA,MACnB,GAAG,OAAO,KAAK,GAAI;AAAA,IACpB;AAAA,EACD;AAAA,EAEA,iBAAiB,MAAY;AAC5B,QAAI,KAAK,IAAI,QAAQ,MAAM,MAAM,SAAS,GAAG;AAC5C,UAAI,KAAK,cAAc;AACtB,qBAAa,KAAK,YAAY;AAAA,MAC/B;AACA,UAAI,KAAK,SAAS,aAAa,QAAQ;AACtC,cAAM,aAAa,KAAK,SAAS,YAAY;AAC7C,YAAI,OAAO,eAAe,UAAU;AACnC,eAAK,SAAS,UAAU;AAAA,QACzB,OAAO;AACN,iBAAO,KAAK,SAAS;AAAA,QACtB;AACA,aAAK,SAAS,YAAY,SAAS;AACnC,aAAK,aAAa;AAAA,MACnB;AAAA,IACD;AAAA,EACD;AAAA,EAEA,UAAgB;AAGf,QAAI,KAAK,UAAU,KAAK,MAAM;AAE7B,UAAI,CAAC,KAAK,OAAO;AAAO,aAAK,KAAK,YAAY,MAAyB,EAAE;AACzE,WAAK,OAAO;AAAA,IACb;AAGA,eAAW,KAAK,KAAK,OAAO;AAC3B,WAAK,MAAM,CAAC,EAAE,UAAU,MAAc,IAAI;AAC1C,aAAO,KAAK,MAAM,CAAC;AAAA,IACpB;AAEA,SAAK,UAAU,IAAI;AACnB,SAAK,cAAc;AAEnB,SAAK,YAAY,iBAAiB,KAAK,MAAM;AAE7C,UAAM,OAAO,mBAAmB,KAAK,MAAM;AAC3C,UAAM,OAAO,eAAe,KAAK,MAAM;AAEvC,QAAI,KAAK,SAAS,SAAS;AAC1B,iBAAW,SAAS,KAAK,SAAS,SAAS;AAC1C,cAAM,QAAQ,OAAO,KAAK;AAAA,MAC3B;AAAA,IACD;AAEA,QAAI,KAAK,MAAM;AACd,WAAK,KAAK,QAAQ;AAClB,WAAK,OAAO;AACZ,WAAK,SAAS;AAAA,IACf;AACA,SAAK,SAAS;AAGd,SAAK,OAAO;AAGZ,QAAI,KAAK,WAAW;AACnB,mBAAa,KAAK,SAAS;AAC3B,WAAK,YAAY;AAAA,IAClB;AACA,QAAI,KAAK,aAAa;AACrB,mBAAa,KAAK,WAAW;AAC7B,WAAK,cAAc;AAAA,IACpB;AACA,QAAI,KAAK,qBAAqB;AAC7B,oBAAc,KAAK,mBAAmB;AAAA,IACvC;AACA,SAAK,sBAAsB;AAC3B,QAAI,KAAK,sBAAsB;AAC9B,oBAAc,KAAK,oBAAoB;AAAA,IACxC;AACA,SAAK,uBAAuB;AAE5B,SAAK,KAAK,IAAI,QAAQ;AAEtB,UAAM,MAAM,OAAO,KAAK,MAAM;AAC9B,QAAI,KAAK,WAAW;AAAS,YAAM,QAAQ;AAAA,EAC5C;AAAA,EACA,GAAG,YAA2C,MAAa;AAC1D,WAAO,KAAK,GAAG,KAAK,SAAS,YAAY,WAAiB,SAAS,GAAG,IAAI;AAAA,EAC3E;AACD;AAEO,MAAM,gBAAgB;AAAA,EAsB5B,cAAc;AACb,SAAK,eAAe,CAAC;AACrB,QAAI;AACH,WAAK,eAAe,YAAQ,eAAG,uBAAuB,EAAE,IAAI;AAC5D,UAAI,CAAC,MAAM,QAAQ,KAAK,YAAY;AAAG,aAAK,eAAe,CAAC;AAAA,IAC7D,QAAE;AAAA,IAAO;AAET,QAAI,CAAC,KAAK,aAAa,QAAQ;AAC9B,WAAK,eAAe,CAAC;AAAA,QACpB,OAAO;AAAA,QACP,MAAM,CAAC;AAAA,QACP,cAAc,KAAK,IAAI;AAAA,QACvB,UAAU;AAAA,QACV,SAAS;AAAA,MACV,GAAG;AAAA,QACF,OAAO;AAAA,QACP,MAAM,CAAC;AAAA,QACP,cAAc,KAAK,IAAI;AAAA,QACvB,WAAW;AAAA,QACX,SAAS,MAAM;AAAA,QACf,UAAU;AAAA,MACX,CAAC;AAAA,IACF;AAEA,SAAK,YAAY,CAAC;AAElB,SAAK,eAAe,CAAC;AACrB,SAAK,wBAAwB,CAAC;AAC9B,eAAW,CAAC,GAAG,QAAQ,KAAK,KAAK,aAAa,QAAQ,GAAG;AACxD,UAAI,CAAC,UAAU,OAAO;AACrB,gBAAQ,KAAK,sBAAsB,wCAAwC;AAC3E;AAAA,MACD;AACA,UAAK,SAAiB,eAAe;AAEpC,eAAQ,SAAiB;AACzB,QAAC,SAAiB,WAAW;AAC7B,YAAI,CAAC,SAAS;AAAS,mBAAS,UAAU;AAC1C,YAAI,SAAS,cAAc;AAAM,mBAAS,YAAY;AAAA,MACvD;AAMA,YAAM,KAAK,KAAK,SAAS,KAAK;AAC9B,cAAQ,OAAO,uBAAuB,EAAE;AACxC,YAAM,OAAO,MAAM,eAAe,IAAI,SAAS,OAAO,QAAQ;AAC9D,UAAI,KAAK,SAAS,SAAS;AAC1B,mBAAW,SAAS,KAAK,SAAS,SAAS;AAC1C,gBAAM,QAAQ,IAAI,OAAO,EAAE;AAAA,QAC5B;AAAA,MACD;AAEA,WAAK,UAAU,KAAK,IAAI;AACxB,UAAI,KAAK,SAAS,UAAU;AAC3B,YAAI,KAAK,SAAS,SAAS;AAC1B,eAAK,sBAAsB,KAAK,EAAE;AAAA,QACnC,OAAO;AACN,eAAK,aAAa,KAAK,EAAE;AAAA,QAC1B;AAAA,MACD;AAAA,IACD;AACA,UAAM,QAAQ,MAAM,MAAM,IAAI,OAAO;AAGrC,QAAI,OAAO,aAAa;AACvB,WAAK,kBAAc,eAAG,4BAA4B,EAAE,mBAAmB;AAAA,IACxE,OAAO;AAGN,WAAK,cAAc,IAAI,mBAAQ,YAAY,EAAC,QAAQ;AAAE,eAAO;AAAA,MAAW,EAAC,CAAC;AAAA,IAC3E;AAEA,SAAK,0BAA0B;AAAA,MAC9B,MAAM,KAAK,gBAAgB;AAAA,MAC3B;AAAA,IACD;AAGA,SAAK,WAAW;AAChB,SAAK,eAAe;AACpB,SAAK,WAAW;AAEhB,SAAK,cAAc;AACnB,SAAK,oBAAoB;AAEzB,SAAK,aAAa;AAElB,QAAI;AACJ,QAAI;AACH,uBAAa,eAAG,qBAAqB,EAAE,SAAS,MAAM;AAAA,IACvD,QAAE;AAAA,IAAO;AACT,SAAK,aAAa,OAAO,UAAU,KAAK;AACxC,SAAK,oBAAoB,KAAK;AAC9B,SAAK,KAAK,YAAY;AAAA,EACvB;AAAA,EAEA,MAAM,cAAc;AACnB,QAAI,QAAQ;AACZ,QAAI,CAAC,OAAO;AAAa,aAAO;AAChC,UAAM,cAAc,IAAI,4BAAiB;AACzC,UAAM,YAAY,eAAe;AAAA,MAChC,OAAO;AAAA,MACP,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,IACjB,CAAC;AACD,eAAW,QAAQ,MAAM,MAAM,OAAO,GAAG;AACxC,UAAI,CAAC,KAAK,UAAU,KAAK,OAAO;AAAO;AACvC,WAAK,OAAO,SAAS;AACrB,YAAM,MAAM,MAAM,KAAK,OAAO,OAAO;AACrC,YAAM,UAAgB,KAAK,OAAO,QAAQ,WAAW,CAAC;AACtD,UAAI,CAAC,QAAQ,QAAQ;AACpB,mBAAW,OAAO,CAAC,MAAM,MAAM,MAAM,IAAI,GAAY;AACpD,cAAI,KAAK,OAAO,GAAG,GAAG,IAAI;AACzB,oBAAQ,KAAK,KAAK,OAAO,GAAG,EAAE,EAAE;AAAA,UACjC;AAAA,QACD;AAAA,MACD;AACA,UAAI,CAAC,QAAQ,UAAU,CAAC,KAAK;AAAQ;AACrC,YAAM,YAAY;AAAA,QACjB,GAAG,KAAK,OAAO,MAAM;AAAA,QACrB,QAAQ,CAAC,CAAC,KAAK,OAAO,MAAM,SAAS;AAAA,MACtC;AACA,YAAM,YAAY;AAAA,QACjB;AAAA,QAGA,CAAC,KAAK,QAAQ,IAAI,KAAK,IAAI,GAAG,SAAS,KAAK,OAAO,KAAK,OAAO,OAAO,SAAS;AAAA,MAChF;AACA,WAAK,OAAO,MAAM,KAAK;AACvB;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EAGA,MAAM,cAAc;AACnB,QAAI,CAAC,OAAO;AAAa;AACzB,SAAK,iBAAiB;AACtB,eAAW,KAAK,MAAM,MAAM,OAAO,GAAG;AACrC,QAAE;AAAA,QACD,SAAS,EAAE,YAAY;AAAA,MAExB;AAAA,IACD;AACA,UAAM,cAAc,IAAI,4BAAiB;AACzC,UAAM,QAAQ;AACd,qBAAiB,UAAU,YAAY,OAAO,KAAK,GAAG;AACrD,YAAM,EAAC,WAAW,SAAS,QAAQ,OAAO,OAAO,MAAK,IAAI;AAC1D,YAAM,CAAC,EAAE,QAAQ,IAAI,OAAO,MAAM,GAAG;AACrC,YAAM,OAAO,MAAM,aAAa;AAAA,QAC/B,QAAQ;AAAA,QACR,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA,OAAO,OAAO,KAAK;AAAA,QACnB;AAAA,QACA,cAAc;AAAA,QACd,cAAc,MAAM;AAAA,QACpB,UAAU;AAAA,MACX,CAAC;AACD,UAAI,CAAC,QAAQ,CAAC,KAAK;AAAQ;AAC3B,WAAK,OAAO,UAAU;AACtB,WAAK,OAAO,MAAM;AAClB,UAAI,OAAO;AACV,eAAO,OAAO,KAAK,OAAO,MAAM,UAAU,KAAK;AAAA,MAChD;AACA,iBAAW,CAAC,GAAG,CAAC,KAAK,QAAQ,QAAQ,GAAG;AACvC,aAAK,KAAK,IAAI,GAAG,MAAM,aAAa;AACpC,cAAM,SAAS,KAAK,OAAO,QAAQ,CAAC;AACpC,eAAO,KAAK;AACZ,eAAO,OAAO;AACd,cAAM,IAAI,MAAM,SAAS,CAAC;AAC1B,YAAI,GAAG;AACN,eAAK,aAAa,MAAM,GAAG,CAAC;AAAA,QAC7B;AAAA,MACD;AAAA,IACD;AACA,eAAW,KAAK,MAAM,MAAM,OAAO,GAAG;AACrC,QAAE,KAAK,SAAS,EAAE,YAAY,4BAA4B;AAAA,IAC3D;AACA,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,aAAa,MAAgB,MAAY,KAAa;AACrD,QAAI,CAAC,KAAK;AAAQ;AAIlB,QAAI,SAAS,KAAK,OAAO,QAAQ,GAAG;AACpC,QAAI,CAAC,QAAQ;AAEZ,eAAS,KAAK,OAAO,QAAQ,GAAG,IAAI,IAAI,MAAM;AAAA,QAC7C;AAAA,QAAM,KAAK;AAAA,QAAS,MAAM;AAAA,MAC3B;AAAA,IACD;AACA,WAAO,KAAK,KAAK;AACjB,WAAO,OAAO,KAAK;AACnB,SAAK,OAAO,YAAY,KAAK,EAAE,IAAI;AACnC,SAAK,SAAS,KAAK,MAAM;AAEzB,SAAK,OAAO,UAAU,IAAI;AAC1B,QAAI,KAAK,OAAO,QAAQ,gBAAgB,CAAC,KAAK,OAAO,MAAM,OAAO;AACjE,WAAK,OAAO,MAAM,MAAM;AAAA,IACzB;AACA,SAAK,KAAK,SAAS,KAAK,YAAY,4BAA4B;AAAA,EACjE;AAAA,EAEA,eAAe,MAAY;AAC1B,eAAW,QAAQ,MAAM,MAAM,OAAO,GAAG;AACxC,YAAM,SAAS,KAAK;AACpB,UAAI,CAAC;AAAQ;AACb,YAAM,MAAM,OAAO,QAAQ,SAAS,QAAQ,KAAK,EAAE;AACnD,UAAI,OAAO,OAAO;AAMjB;AAAA,MACD;AACA,UAAI,OAAO,QAAQ,YAAY,MAAM,IAAI;AACxC,aAAK,aAAa,MAAM,MAAM,GAAG;AAAA,MAClC;AAAA,IACD;AAAA,EACD;AAAA,EAEA,OAAO,OAA2B,YAAqB;AACtD,SAAK,MAAM,OAAO,MAAM,UAAU,OAAO,UAAU;AAAA,EACpD;AAAA,EAEA,oBAAoB;AACnB,uBAAG,uBAAuB,EAAE,YAAY,MACvC,KAAK,UAAU,KAAK,YAAY,EAC9B,QAAQ,eAAe,aAAa,EACpC,QAAQ,OAAO,KAAK,GACpB,EAAC,UAAU,IAAI,CAAC;AAAA,EACpB;AAAA,EAEA,gBAAgB;AACf,QAAI,KAAK,UAAU;AAClB,UAAI,KAAK,eAAe,KAAK;AAAmB;AAChD,WAAK,oBAAoB,KAAK;AAAA,IAC/B,OAAO;AAIN,UAAI,KAAK,aAAa,KAAK;AAAmB;AAC9C,WAAK,oBAAoB,KAAK,aAAa;AAAA,IAC5C;AACA,uBAAG,qBAAqB,EAAE;AAAA,MACzB,MAAM,GAAG,KAAK;AAAA,IACf;AAAA,EACD;AAAA,EAEA,kBAAkB;AACjB,QAAI,KAAK,cAAc;AACtB,WAAK,YAAY,QAAQ,mBAAmB;AAAA,QAC3C,MAAM,KAAK;AAAA,QACX,OAAO,KAAK;AAAA,MACb,CAAC;AACD,WAAK,eAAe;AAAA,IACrB;AACA,SAAK,YAAY,QAAQ,mBAAmB;AAAA,MAC3C,MAAM,KAAK,IAAI;AAAA,MACf,OAAO,MAAM;AAAA,IACd,CAAC;AAAA,EACF;AAAA,EAEA,IAAI,iBAAiB;AACpB,QAAI,KAAK,YAAY;AACpB,aAAO,KAAK;AAAA,IACb;AACA,SAAK,aAAa,cAAc,QAAQ,qBAAqB;AAC7D,QAAI,UAAU;AACd,QAAI,cAAc;AAClB,QAAI,YAAY;AAChB,eAAW,UAAU,IAAI,QAAQ,IAAI,GAAG;AACvC,UAAI,OAAO;AAAS,kBAAU,OAAO;AACrC,UAAI,OAAO;AAAQ,oBAAY,OAAO;AACtC,UAAI,CAAC,OAAO;AAAM;AAClB,UAAI,CAAC,OAAO,iBAAiB,CAAC,OAAO,cAAc,CAAC,OAAO;AAAgB;AAE3E,UAAI,YAAY,aAAa;AAC5B,sBAAc;AACd,aAAK,cAAc,OAAO,YAAY,MAAM;AAAA,MAC7C;AACA,WAAK,cAAc,MAAM,OAAO;AAChC,UAAI,cAAc;AAClB,UAAI,OAAO;AAAM,uBAAe;AAChC,UAAI,OAAO;AAAY,uBAAe;AACtC,UAAI,OAAO;AAAe,uBAAe;AACzC,UAAI,OAAO;AAAgB,uBAAe;AAC1C,YAAM,YAAY,IAAI,QAAQ,aAAa,MAAM;AACjD,YAAM,QAAQ,UAAU,eAAe,UAAU,mBAAmB,UAAU;AAC9E,UAAI,UAAU;AAAI,uBAAe;AACjC,WAAK,cAAc,MAAM,YAAY,SAAS,EAAE;AAAA,IACjD;AACA,WAAO,KAAK;AAAA,EACb;AAAA,EACA,IAAI,iBAAiB;AACpB,QAAI,OAAO;AAAmB,aAAO;AAGrC,QAAI,OAAO,UAAU;AACpB,aAAO,OAAO;AAAA,IACf;AACA,UAAM,WAAW,CAAC;AAElB,eAAW,QAAQ,OAAO,QAAQ;AACjC,UAAI,CAAC,OAAO,OAAO,IAAI,KAAK,CAAC;AAAM;AAEnC,YAAM,WAAW,OAAO,OAAO,IAAI;AACnC,UAAI,YAAY,SAAS,OAAO,SAAU,CAAC,SAAS,QAAQ,CAAC,SAAS,OACrE,WAAY,SAAS,QAAQ,SAAS,UAAW,eAAe;AACjE,UAAI,SAAS,OAAO;AAAiB,oBAAY;AAEjD,eAAS,KAAK;AAAA,QACb,QAAQ;AAAA,QACR,MAAO,OAAO,OAAO,IAAI,EAAE,QAAQ;AAAA,QACnC,MAAM;AAAA,MAAS,CAAC;AAAA,IAClB;AAEA,UAAM,YAAY,CAAC,cAAc,UAAU,SAAS,YAAY;AAEhE,qBAAM,OAAO,UAAU,UAAQ,CAAC,UAAU,QAAQ,KAAK,IAAI,CAAC;AAG5D,eAAW,QAAQ,OAAO,cAAc;AACvC,eAAS,KAAK,EAAC,QAAQ,OAAO,aAAa,IAAI,EAAE,QAAQ,MAAM,OAAO,aAAa,IAAI,EAAE,MAAM,MAAM,aAAY,CAAC;AAAA,IACnH;AAEA,WAAO,WAAW,mBAAmB,KAAK,UAAU,QAAQ,IAAI;AAChE,WAAO,OAAO;AAAA,EACf;AAAA,EAEA,WAA0D,QAAgB;AACzE,UAAM,QAAoB,CAAC;AAC3B,UAAM,CAAC,cAAc,iBAAiB,cAAc,IAAI,OAAO,MAAM,GAAG;AACxE,UAAM,YAAY,CAAC;AACnB,eAAW,QAAQ,MAAM,MAAM,OAAO,GAAG;AACxC,UAAI,CAAC,MAAM,UAAU,KAAK,SAAS;AAAW;AAC9C,UAAI,KAAK,SAAS;AAAU;AAC5B,UAAI,gBAAgB,iBAAiB,KAAK;AAAQ;AAClD,UAAI,cAAc,CAAC,KAAK,SAAS,KAAK,QAAQ;AAAY;AAC1D,UAAI,kBAAkB,KAAK,QAAQ;AAClC,cAAM,WAAW,KAAK,OAAO,GAAG;AAChC,cAAM,WAAW,KAAK,OAAO,GAAG;AAChC,YAAI,CAAC,YAAY,CAAC;AAAU;AAC5B,YAAI,CAAC,SAAS,WAAW,cAAc,KAAK,CAAC,SAAS,WAAW,cAAc;AAAG;AAAA,MACnF;AACA,YAAM,KAAK,IAAI;AAAA,IAChB;AAEA,UAAM,YAAiD,CAAC;AACxD,aAAS,IAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,OAAO,KAAK,GAAG,KAAK;AACtE,YAAM,OAAO,MAAM,CAAC;AACpB,YAAM,WAA4B,CAAC;AACnC,UAAI,KAAK,UAAU,KAAK,QAAQ;AAC/B,YAAI,KAAK,OAAO;AAAI,mBAAS,KAAK,KAAK,OAAO,GAAG;AACjD,YAAI,KAAK,OAAO;AAAI,mBAAS,KAAK,KAAK,OAAO,GAAG;AACjD,YAAI,KAAK;AAAM,mBAAS,SAAS;AACjC,YAAI,KAAK;AAAO,mBAAS,SAAS,KAAK,MAAM,KAAK,KAAK;AAAA,MACxD;AACA,UAAI,CAAC,SAAS,MAAM,CAAC,SAAS;AAAI;AAClC,gBAAU,KAAK,MAAM,IAAI;AAAA,IAC1B;AACA,WAAO;AAAA,EACR;AAAA,EACA,SAAS,MAAY;AACpB,UAAM,YAKF;AAAA,MACH,MAAM,CAAC;AAAA,MACP,eAAe,OAAO,OAAO,kCAAa,YAAY;AAAA,MACtD,WAAW,MAAM;AAAA,MACjB,aAAa,KAAK;AAAA,IACnB;AACA,eAAW,QAAQ,KAAK,WAAW;AAClC,UAAI,CAAC;AAAM;AACX,UAAI,KAAK;AAAQ;AACjB,UACC,KAAK,SAAS,WACb,KAAK,SAAS,aAAa,CAAE,CAAC,UAAU,OAAO,EAAU,SAAS,KAAK,SAAS,SAAS,KACzF,KAAK,SAAS,cAAc,WAAW,KAAK,cAAc;AAC1D;AACF,YAAM,WAA0B;AAAA,QAC/B,OAAO,KAAK;AAAA,QACZ,MAAM,KAAK,SAAS,QAAQ;AAAA,QAC5B,WAAW,KAAK;AAAA,QAChB,SAAS,KAAK,SAAS,UACrB,kCAAa,aAAa,KAAK,SAAS,OAAO,KAAK,KAAK,SAAS,UAAW;AAAA,QAC/E,SAAS,CAAC,KAAK,SAAS,YAAY,SAAY,KAAK,SAAS;AAAA,MAC/D;AACA,YAAM,WAAW,KAAK,YAAY,EAAE,IAAI,OAAK,EAAE,KAAK;AACpD,UAAI,SAAS;AAAQ,iBAAS,WAAW;AACzC,UAAI,KAAK,SAAS;AAAW,iBAAS,YAAY,KAAK,SAAS;AAEhE,gBAAU,KAAK,KAAK,QAAQ;AAAA,IAC7B;AACA,WAAO;AAAA,EACR;AAAA,EACA,QAAQ,SAAiB;AACxB,YAAQ,cAAc,IAAI,OAAO;AAAA,EAClC;AAAA,EACA,YAAY,OAAe;AAC1B,UAAM,KAAK,KAAK,KAAK;AACrB,QAAI,CAAC,WAAW,SAAS,UAAU,eAAe,QAAQ,OAAO,QAAQ,EAAE,SAAS,EAAE,GAAG;AACxF,aAAO;AAAA,IACR;AACA,QAAI,MAAM,MAAM,IAAI,EAAE;AAAG,aAAO;AAEhC,UAAM,WAAW;AAAA,MAChB;AAAA,MACA,MAAM,CAAC;AAAA,MACP,cAAc,KAAK,IAAI;AAAA,IACxB;AACA,UAAM,OAAO,MAAM,eAAe,IAAI,OAAO,QAAQ;AACrD,QAAI,OAAO;AAAS,YAAM,QAAQ;AAClC,SAAK,aAAa,KAAK,QAAQ;AAC/B,SAAK,UAAU,KAAK,IAAI;AACxB,SAAK,kBAAkB;AACvB,WAAO;AAAA,EACR;AAAA,EAEA,eAAe,QAAgB;AAE9B,UAAM,aAAa,UAAU,KAAK,IAAI,QAAQ,IAAI,MAAM,EAAE,IAAI;AAC9D,QAAI,YAAY,KAAK;AACrB,QAAI;AACJ,OAAG;AACF,eAAS,GAAG,aAAa,EAAE;AAAA,IAC5B,SAAS,MAAM,MAAM,IAAI,MAAM;AAE/B,SAAK,aAAa;AAClB,SAAK,cAAc;AACnB,WAAO;AAAA,EACR;AAAA,EAEA,mBAAmB,SAAiB,MAAgB,SAAoB;AACvE,eAAW,UAAU,SAAS;AAC7B,UAAI,OAAO,eAAe,QAAQ;AACjC,eAAO,cAAc,QAAQ;AAAA,MAC9B;AAAA,IACD;AACA,QAAI,OAAO,eAAe;AACzB,YAAM,aAAa,MAAM,IAAI,OAAO,kBAAkB,OAAO,UAAU,OAAO,aAAa;AAC3F,UAAI,YAAY;AACf,cAAM,gBAAgB,QAAQ,IAAI,OAAK,EAAE,YAAY,CAAC,EAAE,KAAK,GAAG;AAChE,mBACE,IAAI,MAAM,KAAK,UAAU,eAAe,EACxC,OAAO;AAAA,MACV;AAAA,IACD;AACA,QAAI,OAAO,eAAe,QAAQ,OAAO;AACxC,YAAM,oBAAoB,QAAQ,IAAI,OAAK,GAAG,EAAE,OAAO,EAAE;AAAA,CAAY,EAAE,KAAK,EAAE;AAC9E,WAAK,KAAK,YAAY,MAAM,iBAAiB;AAAA,IAC9C;AACA,eAAW,UAAU,SAAS;AAC7B,WAAK,YAAY,iBAAiB,QAAQ,IAAI;AAAA,IAC/C;AAAA,EACD;AAAA,EAEA,mBAAmB,IAAY;AAC9B,SAAK,KAAK,EAAE;AACZ,UAAM,OAAO,MAAM,IAAI,EAAE;AACzB,QAAI,CAAC;AAAM,aAAO;AAClB,QAAI,CAAC,KAAK;AAAS,aAAO;AAK1B,aAAS,IAAI,KAAK,aAAa,SAAS,GAAG,KAAK,GAAG,KAAK;AACvD,UAAI,OAAO,KAAK,KAAK,aAAa,CAAC,EAAE,KAAK,GAAG;AAC5C,aAAK,aAAa,OAAO,GAAG,CAAC;AAC7B,aAAK,kBAAkB;AACvB;AAAA,MACD;AAAA,IACD;AACA,SAAK,UAAU;AACf,WAAO;AAAA,EACR;AAAA,EACA,eAAe,IAAY;AAC1B,SAAK,KAAK,EAAE;AACZ,QAAI,CAAC,MAAM,MAAM,IAAI,EAAE;AAAG,aAAO;AACjC,aAAS,IAAI,KAAK,UAAU,SAAS,GAAG,KAAK,GAAG,KAAK;AACpD,UAAI,OAAO,KAAK,UAAU,CAAC,EAAE,QAAQ;AACpC,aAAK,UAAU,OAAO,GAAG,CAAC;AAC1B;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA,EACA,eAAe,IAAY;AAC1B,SAAK,KAAK,EAAE;AACZ,UAAM,OAAO,MAAM,IAAI,EAAE;AACzB,QAAI,CAAC;AAAM,aAAO;AAClB,SAAK,QAAQ;AACb,WAAO;AAAA,EACR;AAAA,EACA,cAAc,MAAY,YAAwB;AAGjD,QAAI,gBAAgB;AACpB,eAAW,YAAY,KAAK,cAAc;AACzC,WAAK,SAAS,UAAU,UAAU;AAClC,UAAI,aAAa;AAAS,wBAAgB;AAAA,IAC3C;AACA,QAAI,CAAC,iBAAiB,OAAO,aAAa;AAAY,WAAK,KAAK;AAAA,QAAiB;AAAA,EAClF;AAAA,EACA,cAAc,MAAY,YAAyB;AAClD,QAAI,CAAC,KAAK;AAAO;AACjB,aAAS,CAAC,GAAG,MAAM,KAAK,KAAK,sBAAsB,QAAQ,GAAG;AAC7D,YAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,UAAI,CAAC,MAAM;AACV,aAAK,sBAAsB,OAAO,GAAG,CAAC;AACtC;AACA;AAAA,MACD;AACA,UAAI,KAAK,aAAa,IAAI,GAAG;AAC5B,aAAK,SAAS,KAAK,QAAQ,UAAU;AAAA,MACtC;AAAA,IACD;AACA,eAAW,QAAQ,KAAK,aAAa;AACpC,UAAI,KAAK,WAAW;AACnB,cAAM,YAAY,KAAK,UAAU,MAAM,GAAG;AAC1C,mBAAW,YAAY,WAAW;AACjC,eAAK,KAAK,YAAY,UAAU,IAAI;AAAA,QACrC;AACA,aAAK,YAAY;AAAA,MAClB;AAAA,IACD;AAAA,EACD;AAAA,EACA,cAAc,MAAY,YAAwB;AACjD,eAAW,KAAK,KAAK,kBAAkB,IAAI,OAAO,KAAK,iBAAiB,KAAK,cAAc;AAC3F,QAAI,MAAM,MAAM,OAAO,KAAK,UAAU;AACrC,WAAK,WAAW,MAAM,MAAM;AAC5B,WAAK,eAAe,KAAK,IAAI;AAAA,IAC9B;AACA,QAAI,KAAK,gBAAgB;AACxB,iBAAW;AAAA,QACV,SAAS,KAAK,YAAY;AAAA,MAE3B;AAAA,IACD;AAAA,EACD;AAAA,EACA,cAAc,MAAoB,MAAM,OAAO,OAAO;AACrD,QAAI,KAAK,YAAY;AAAK;AAC1B,UAAM,UAAU,MAAM,IAAI,aAAa;AAEvC,UAAM,QAAS,MAAM,iBAAM,WAAW,IAAI,KAAK,EAAE,MAAM;AAAA,CAAI,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,QAAQ,IAAI;AAC1F,eAAW,CAAC,IAAI,OAAO,KAAK,MAAM,OAAO;AACxC,UAAI,KAAK;AACR,YAAI,OAAO,WAAW,OAAO,iBAAkB,CAAC,WAAW,OAAO,SAAU;AAC3E,kBAAQ,OAAO,qFAAqF,6CAA6C;AACjJ,kBAAQ,OAAO,uGAAuG;AACtH,kBAAQ,OAAO;AAAA,QAChB,OAAO;AACN,kBAAQ,OAAO,iKAAiK,EAAE,OAAO;AAAA,QAC1L;AAAA,MACD,OAAO;AACN,gBAAQ,OAAO,yLAAyL,EAAE,OAAO;AAAA,MAClN;AACA,YAAM,OAAO,QAAQ;AAErB,UAAI,CAAC,QAAQ,QAAQ,KAAK,SAAS,OAAO,KAAK,MAAM,UAAU,cAAc,CAAC,KAAK,OAAO;AAEzF,aAAK,MAAM,MAAM;AACjB,YAAI,QAAQ,SAAS,YAAY,KAAK;AACrC,kBAAQ,SAAS,UAAU;AAC3B,kBAAQ,OAAO,qHAAqH,EAAE,OAAO;AAAA,QAC9I;AAAA,MACD;AAAA,IACD;AACA,eAAW,QAAQ,MAAM,MAAM,OAAO,GAAG;AACxC,WAAK,KAAK,SAAS,KAAK,YAAY,KAAK,mMAAmM;AAAA,IAC7O;AAEA,SAAK,WAAW;AAChB,SAAK,cAAc;AACnB,SAAK,oBAAoB,KAAK,IAAI;AAAA,EACnC;AAAA,EACA,uBAAuB;AACtB,UAAM,eAAyB,CAAC,eAAe,SAAS,YAAY;AACpE,QAAI,OAAO,iBAAiB;AAAW,aAAO,eAAe;AAE7D,QAAI,OAAO,gBAAgB,MAAM,OAAO,aAAa,QAAQ,MAAM,OAAO,gBAAgB,GAAG;AAG5F,UAAI,QAAQ,kBAAkB;AAC7B,aAAK;AAAA,UACJ;AAAA,UACA;AAAA,QACD;AACA;AAAA,MACD;AAGA,WAAK;AAAA,QACJ;AAAA,QACA;AAAA,MACD;AAGA,iBAAW,MAAM;AAChB,YAAI,OAAO,gBAAgB,MAAM,OAAO,aAAa,MAAM;AAE1D,kBAAQ,KAAK;AAAA,QACd,OAAO;AACN,eAAK;AAAA,YACJ;AAAA,YACA;AAAA,UACD;AAAA,QACD;AAAA,MACD,GAAG,KAAK,GAAI;AAAA,IACb;AAAA,EACD;AAAA,EACA,YAAY,OAAiB,SAAiB;AAC7C,QAAI,CAAC,SAAS,CAAC;AAAS;AACxB,eAAW,UAAU,OAAO;AAC3B,YAAM,UAAU,MAAM,IAAI,MAAM;AAChC,UAAI;AAAS,gBAAQ,IAAI,OAAO,EAAE,OAAO;AAAA,IAC1C;AAAA,EACD;AAAA,EACA,YAAY,KAAqB,UAAU,cAAc;AACxD,UAAM,OAAO,KAAK,IAAI;AACtB,QAAI,OAAO,KAAK,oBAAoB,uBAAuB;AAC1D;AAAA,IACD;AACA,SAAK,oBAAoB;AAEzB,UAAM,QAAQ,OAAO,QAAQ,WAAW,MAAM,KAAK,SAAS,KAAK,WAAW,KAAK,QAAQ;AACzF,UAAM,CAAC,YAAY,SAAS,IAAI,iBAAM,WAAW,iBAAM,WAAW,KAAK,GAAG,QAAQ;AAClF,QAAI,YAAY,MAAM,0BAA0B;AAChD,QAAI;AAAW,kBAAY,sCAAsC,sBAAsB;AAEvF,QAAI,eAAe,oCAAoC;AACvD,QAAI,sBAAsB;AAE1B,UAAM,iBAAiB,MAAM,IAAI,YAAY;AAE7C,QAAI,iBAAiB,MAAM,SAAS,SAAS;AAC7C,eAAW,QAAS,OAAO,qBAAqB,CAAC,GAAI;AACpD,UAAI,OAAO,SAAS,WAAW,MAAM,SAAS,IAAI,IAAI,KAAK,KAAK,KAAK,GAAG;AACvE,yBAAiB;AACjB;AAAA,MACD;AAAA,IACD;AAEA,QAAI,gBAAgB;AACnB,UAAI,gBAAgB;AACnB,8BAAsB;AACtB,uBAAe,uCAAuC;AAAA,MACvD,OAAO;AACN,uBAAe,uCAAuC;AAAA,MACvD;AAAA,IACD;AACA,UAAM,UAAU,MAAM,IAAI,aAAa;AACvC,QAAI,SAAS;AACZ,cAAQ,IAAI,YAAY,EAAE,OAAO;AAAA,IAClC,OAAO;AACN,YAAM,OAAO,IAAI,YAAY,EAAE,OAAO;AACtC,YAAM,IAAI,OAAO,GAAG,IAAI,YAAY,EAAE,OAAO;AAAA,IAC9C;AACA,QAAI,qBAAqB;AACxB,qBAAgB,IAAI,mBAAmB,EAAE,OAAO;AAAA,IACjD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB,QAAY;AAChC,UAAM,WAAW,CAAC;AAClB,eAAW,CAAC,IAAI,OAAO,KAAK,MAAM,OAAO;AACxC,UAAI,QAAQ,SAAS,cAAc,QAAQ,KAAK,IAAI,MAAM,MAAM,MAAM,aAAa;AAClF,gBAAQ,QAAQ;AAAA,MACjB,OAAO;AACN,YAAI,QAAQ,SAAS,aAAa,QAAQ,UAAU,CAAC,QAAQ,SAAS;AACrE;AAAA,QACD;AAEA,YAAI,QAAQ,KAAK,IAAI,MAAM,GAAG;AAC7B,cAAI,WAAW,QAAQ,KAAK,IAAI,MAAM;AACtC,cAAI,aAAa;AAAK,uBAAW;AACjC,mBAAS,KAAK,GAAG,WAAW,IAAI;AAAA,QACjC;AAAA,MACD;AAAA,IACD;AACA,WAAO;AAAA,EACR;AACD;AAEO,MAAM,iBAAiB,UAAU;AAAA,EAAjC;AAAA;AAGN;AAAA;AAAA,kBAAS;AACT,kBAAgB;AAChB,gBAAe;AAAA;AAChB;AAEO,MAAM,iBAAiB,UAAU;AAAA,EAevC,YAAY,QAAgB,OAAe,SAAoD;AAC9F,YAAQ,aAAa;AACrB,YAAQ,iBAAiB;AACzB,YAAQ,iBAAiB;AACzB,UAAM,QAAQ,OAAO,OAAO;AAC5B,SAAK,cAAc,CAAC,CAAC,OAAO;AAC5B,SAAK,SAAS,UAAW,OAAO,iBAAiB;AAEjD,SAAK,OAAO;AAEZ,SAAK,SAAS,QAAQ,UAAU;AAGhC,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,UAAW,QAAgB,UAAW,KAAK,QAAQ,KAAK,KAAK,QAAS,IAAI;AAE/E,SAAK,KAAK,QAAQ,IAAI,QAAQ;AAC9B,SAAK,KAAK,QAAQ,IAAI,QAAQ;AAC9B,SAAK,KAAK,QAAQ,IAAI,QAAQ;AAC9B,SAAK,KAAK,QAAQ,IAAI,QAAQ;AAE9B,SAAK,QAAQ,QAAQ,UAAU,OAAO,IAAI,QAAQ,SAAS;AAE3D,SAAK,SAAS;AACd,SAAK,OAAO;AAEZ,SAAK,cAAc;AAEnB,SAAK,SAAS;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,UAAkC,GAAG;AAC3C,WAAO,KAAK,IAAI,cAAc,OAAO;AAAA,EACtC;AAAA,EACA,cAAc,MAAY;AACzB,QAAI,EAAE,KAAK,MAAM,KAAK,KAAK;AAAc,aAAO,KAAK,OAAO;AAE5D,WAAO,KAAK,OAAO,KAAK,KAAK,YAAY,KAAK,EAAE,EAAE,GAAG;AAAA,EACtD;AAAA,EACA,OAAO,cAA2B,MAAM;AACvC,QAAI,CAAC,KAAK,IAAI,gBAAgB;AAAQ;AAEtC,QAAI,KAAK,WAAW;AACnB,cAAQ,iBAAiB,KAAK,QAAQ,IAAI,KAAK;AAAA,EAAW,KAAK,IAAI,gBAAgB,KAAK,IAAI,GAAG;AAAA,IAChG;AACA,SAAK,IAAI,kBAAkB,CAAC;AAE5B,SAAK,gBAAgB;AAAA,EACtB;AAAA,EACA,kBAAkB;AAEjB,QAAI,CAAC,KAAK,WAAW;AACpB,UAAI,KAAK;AAAa,qBAAa,KAAK,WAAW;AACnD,WAAK,cAAc,WAAW,MAAM,KAAK,OAAO,GAAG,wBAAwB;AAAA,IAC5E,OAAO;AACN,UAAI,KAAK;AAAa,qBAAa,KAAK,WAAW;AACnD,WAAK,cAAc,WAAW,MAAM,KAAK,OAAO,GAAG,2BAA2B;AAAA,IAC/E;AAAA,EACD;AAAA,EACA,WAAW,KAAY,SAAiB;AACvC,UAAM,SAAS,KAAK,UAAU,GAAG;AACjC,QAAI,CAAC;AAAQ,aAAO;AACpB,WAAO,SAAS,OAAO;AAAA,EACxB;AAAA,EACA,UAAU,KAAY;AAErB,WAAO,KAAK,KAAK,OAAO,MAAM,EAAE;AAAA,EACjC;AAAA,EACA,eAAe,MAAmB;AACjC,QAAI,CAAC,MAAM;AACV,WAAK,cAAc;AACnB;AAAA,IACD,WAAW,CAAC,KAAK,eAAe,KAAK,gBAAgB,KAAK,MAAM,KAAK,KAAK,IAAI,KAAK,EAAE,MAAM,MAAM,eAAe;AAC/G,WAAK,cAAc,KAAK;AACxB;AAAA,IACD,OAAO;AACN,aAAO;AAAA,IACR;AAAA,EACD;AAAA,EACA,UAAU,MAAY,YAAwB;AAC7C,SAAK,SAAS,YAAY,0BAA0B,KAAK,QAAQ,OAAO,KAAK,cAAc,IAAI,CAAC;AAChG,QAAI,KAAK,QAAQ,KAAK,KAAK;AAAW,WAAK,KAAK,UAAU,MAAM,UAAU;AAAA,EAC3E;AAAA,EACA,OAAO,MAAY,YAAwB;AAC1C,QAAI,CAAC;AAAM,aAAO;AAClB,QAAI,KAAK,MAAM,KAAK,EAAE;AAAG,aAAO;AAEhC,QAAI,KAAK,OAAO;AACf,WAAK,WAAW,KAAK,KAAK,sBAAsB,IAAI,GAAG,IAAI;AAAA,IAC5D;AAIA,UAAM,YAAY;AACjB,UAAI,KAAK,QAAQ;AAChB,cAAM,SAAS,KAAK,OAAO,YAAY,KAAK,EAAE;AAC9C,YAAI,UAAU,KAAK,OAAO,QAAQ,MAAM,eAAa,UAAU,mBAAmB,GAAG;AACpF,cAAI,MAAM;AACV,qBAAW,aAAa,KAAK,OAAO,SAAS;AAC5C,kBAAM,OAAO,MAAM,KAAK,OAAO,QAAQ,UAAU,EAAE;AACnD,gBAAI,CAAC;AAAM;AACX,mBAAO,iBAAM,yFAAyF,UAAU,iBAAiB,MAAM,OAAO,MAAM,EAAC,WAAW,KAAI,CAAC;AAAA,UACtK;AACA,iBAAO,SAAS,GAAG;AAAA,QACpB;AAAA,MACD;AAAA,IACD,GAAG;AAEH,SAAK,MAAM,KAAK,EAAE,IAAI;AACtB,SAAK;AACL,SAAK,iBAAiB,IAAI;AAE1B,SAAK,eAAe,YAAY,MAAM,UAAU;AAChD,SAAK,MAAM,SAAS,MAAM,UAAU;AACpC,SAAK,YAAY,cAAc,MAAM,MAAM,UAAU;AACrD,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,aAAa,MAAY,YAAwB,SAAsC;AAgB5F,UAAM,SAAS,KAAK;AACpB,QAAI,CAAC;AAAQ;AAGb,UAAM,SAAS,IAAI,QAAQ,IAAI,KAAK,QAAQ,IAAI;AAKhD,QAAI,cAAc,CAAC,OAAO,GAAG,SAAS,YAAY;AAClD,QAAI,OAAO,QAAQ,OAAO;AAAO,oBAAc;AAE/C,UAAM,OAAO,KAAK,OAAO,cAAc,IAAI,EAAE;AAC7C,UAAM,WAAW,OAAO,WAAW,KAAK,EAAE,OAAO,KAAK,QAAQ,oBAAoB,EAAE,CAAC,EAAE,OAAO,KAAK;AACnG,QAAI,SAAS;AACb,QAAI,OAAO,SAAS,KAAK;AAAO,eAAS,KAAK;AAC9C,UAAM,EAAC,IAAI,SAAQ,IAAI,KAAK,cAAc;AAM1C,WAAO,cAAc;AACrB,UAAM,CAAC,OAAO,IAAI,MAAM,YAAY,QAAQ,cAAc;AAAA,MACzD;AAAA,MACA,SAAS;AAAA,MACT,IAAI,OAAO,GAAG;AAAA,MACd,IAAI,OAAO,GAAG;AAAA,MACd,QAAQ,OAAO;AAAA,MACf;AAAA,MACA,QAAQ,YAAY,mBAAoB,KAAa,eACpD,MAAM,KAAK,SAAS,aAAa,KAAK,aAAa,MAAM;AAAA,MAC1D,UAAU,OAAO,UAAU,KAAK,IAAI,KAAK;AAAA,IAC1C,CAAC;AACD,QAAI,SAAS,SAAS;AACrB,iBAAW,MAAM,4BAA4B,QAAQ,qCAAqC;AAC1F;AAAA,IACD;AAIA,eAAW,KAAK,+BAA+B,KAAK,UAAU;AAAA,MAC7D,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,QAAQ,YAAY,mBAAmB,YAAY;AAAA,IACpD,CAAC,CAAC;AAAA,EACH;AAAA,EAEA,gBAAgB;AACf,QAAI,CAAC,KAAK,OAAO,SAAS,IAAI;AAAG,aAAO,EAAC,IAAI,KAAK,OAAO,MAAM,CAAC,EAAC;AACjE,UAAM,MAAM,KAAK,OAAO,SAAS;AACjC,UAAM,aAAa,KAAK,OAAO,YAAY,KAAK,GAAG;AACnD,WAAO,EAAC,IAAI,KAAK,OAAO,MAAM,GAAG,UAAU,GAAG,UAAU,KAAK,OAAO,MAAM,aAAa,GAAG,GAAG,EAAC;AAAA,EAC/F;AACD;AAEA,SAAS,QAAQ,QAA6B;AAC7C,MAAI,OAAO,WAAW;AAAU,WAAO,MAAM,MAAM,IAAI,MAAgB;AACvE,SAAO;AACR;AAEO,MAAM,QAAQ;AAAA,EACpB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,OAAO,oBAAI,IAAkB;AAAA,EAC7B,SAAS,oBAAI,IAAoB;AAAA,EAEjC,KAAK;AAAA,EACL,OAAO,MAAgC;AACtC,WAAO,QAAQ,IAAI,KAAK,QAAQ,KAAK,IAAI,CAAC,KAAK,QAAQ,MAAM,QAAQ,IAAI,KAAK,IAAI,CAAC,CAAC;AAAA,EACrF;AAAA,EAEA,eAAe,QAAgB,OAAe,SAAoD;AACjG,QAAI,MAAM,MAAM,IAAI,MAAM;AAAG,YAAM,IAAI,MAAM,QAAQ,uBAAuB;AAC5E,YAAQ,MAAM,sBAAsB,MAAM;AAC1C,UAAM,OAAO,IAAI,SAAS,QAAQ,OAAO,OAAO;AAChD,UAAM,MAAM,IAAI,QAAQ,IAAI;AAC5B,WAAO;AAAA,EACR;AAAA,EACA,eAAe,QAAgB,OAAe,SAAgC;AAC7E,QAAI,MAAM,MAAM,IAAI,MAAM;AAAG,YAAM,IAAI,MAAM,QAAQ,uBAAuB;AAC5E,UAAM,OAAiB,IAAK,UAAkB,QAAQ,OAAO,OAAO;AACpE,UAAM,MAAM,IAAI,QAAQ,IAAI;AAC5B,WAAO;AAAA,EACR;AAAA,EACA,aAAa,SAAoD;AAChE,UAAM,UAAkB,CAAC,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,EAAE,EACrE,OAAO,OAAO,EAAE,IAAI,YAAU,OAAQ,IAAI;AAC5C,UAAM,WAAW,IAAI,QAAQ,IAAI,QAAQ,MAAM,EAAE;AACjD,QAAI,aAAa,WAAW,aAAa,cAAc;AACtD,UAAI,QAAQ,SAAS,GAAG;AACvB,cAAM,IAAI,MAAM,oEAAoE;AAAA,MACrF;AAAA,IACD;AACA,QAAI,IAAI,IAAI,OAAO,EAAE,OAAO,QAAQ,QAAQ;AAC3C,YAAM,IAAI,MAAM,iCAAiC;AAAA,IAClD;AAEA,eAAW,QAAQ,SAAS;AAC3B,cAAQ,eAAe,IAAI;AAAA,IAC5B;AAEA,QAAI,MAAM,OAAO,aAAa,MAAM;AACnC,iBAAW,QAAQ,SAAS;AAC3B,aAAK,MAAM,6EAA6E;AAAA,MACzF;AACA;AAAA,IACD;AAEA,UAAM,YAAY,QAAQ,SAAS,QAAQ,CAAC,EAAE,eAAe,UAAU;AACvE,QAAI,WAAW,IAAI;AACnB,eAAW,QAAQ,SAAS;AAC3B,UAAI,KAAK,eAAe,YAAY,WAAW;AAC9C,oBAAY,SAAS,KAAK,eAAe;AAAA,MAC1C;AACA,WAAK,eAAe,UAAU;AAAA,IAC/B;AAEA,QAAI,aAAa,IAAI,cAAc;AAClC,iBAAW,QAAQ,SAAS;AAC3B,aAAK,MAAM,6CAA6C,UAAU;AAAA,MACnE;AACA;AAAA,IACD,WAAW,WAAW;AACrB,cAAQ,eAAe;AAAA,IACxB;AAEA,UAAM,SAAS,QAAQ,UAAU,MAAM,OAAO,eAAe,QAAQ,MAAM;AAI3E,YAAQ,QAAQ,KAAK,IAAI,CAAC,QAAQ,SAAU,GAAG,CAAC;AAChD,UAAM,KAAK,QAAQ,CAAC;AACpB,UAAM,KAAK,QAAQ,CAAC;AACpB,UAAM,SAAS,KAAK,GAAG,OAAO;AAC9B,UAAM,SAAS,KAAK,GAAG,OAAO;AAC9B,QAAI;AACJ,QAAI,aAAa,SAAS;AACzB,kBAAY,QAAQ,mBAAmB;AAAA,IACxC,WAAW,aAAa,cAAc;AAErC,kBAAY,GAAG;AAAA,IAChB,WAAW,QAAQ,OAAO;AACzB,kBAAY,QAAQ;AAAA,IACrB,OAAO;AACN,kBAAY,GAAG,cAAc;AAAA,IAC9B;AACA,YAAQ,aAAa;AACrB,UAAM,OAAO,MAAM,eAAe,QAAQ,WAAW,OAAO;AAC5D,UAAM,SAAS,IAAI,MAAM,WAAW,MAAM,OAAO;AACjD,SAAK,OAAO;AACZ,WAAO,qBAAqB,OAAO;AAEnC,eAAW,KAAK,SAAS;AACxB,UAAI,GAAG;AACN,UAAE,SAAS,IAAI;AACf,gBAAQ,YAAY,EAAE,UAAU,EAAE,IAAI;AAAA,MACvC;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA,EAEA,QAAQ;AAAA,EACR,OAAO;AAAA,EAEP;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EAEV;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA,IAAI,mBAAAA;AACL;", "names": ["RoomBattlePM"] }