{ "version": 3, "sources": ["../../../../server/chat-plugins/youtube.ts"], "sourcesContent": ["/**\r\n * Youtube room chat-plugin.\r\n * Supports adding channels and selecting a random channel.\r\n * Also supports showing video data on request.\r\n * Written by Mia, with some design concepts from bumbadadabum.\r\n * @author mia-pi-git\r\n */\r\n\r\nimport {Utils, FS, Net} from '../../lib';\r\n\r\nconst ROOT = 'https://www.googleapis.com/youtube/v3/';\r\nconst STORAGE_PATH = 'config/chat-plugins/youtube.json';\r\nconst GROUPWATCH_ROOMS = ['youtube', 'pokemongames', 'videogames', 'smashbros', 'pokemongo', 'hindi'];\r\n\r\nexport const videoDataCache: Map = Chat.oldPlugins.youtube?.videoDataCache || new Map();\r\nexport const searchDataCache: Map = Chat.oldPlugins.youtube?.searchDataCache || new Map();\r\n\r\ninterface ChannelEntry {\r\n\tname: string;\r\n\tdescription: string;\r\n\turl: string;\r\n\ticon: string;\r\n\tvideos: number;\r\n\tsubs: number;\r\n\tviews: number;\r\n\tusername?: string;\r\n\tcategory?: string;\r\n}\r\n\r\nexport interface VideoData {\r\n\tid: string;\r\n\ttitle: string;\r\n\tdate: string;\r\n\tdescription: string;\r\n\tchannelTitle: string;\r\n\tchannelUrl: string;\r\n\tviews: number;\r\n\tthumbnail: string;\r\n\tlikes: number;\r\n\tdislikes: number;\r\n}\r\n\r\ninterface TwitchChannel {\r\n\tstatus: string;\r\n\tdisplay_name: string;\r\n\tname: string;\r\n\tlanguage: string;\r\n\tcreated_at: string;\r\n\tlogo: string;\r\n\tviews: number;\r\n\tfollowers: number;\r\n\tvideo_banner: string;\r\n\turl: string;\r\n\tgame: string;\r\n\tdescription: string;\r\n\tupdated_at: string;\r\n}\r\n\r\ninterface ChannelData {\r\n\tchannels: {[k: string]: ChannelEntry};\r\n\tcategories: string[];\r\n\tintervalTime?: number;\r\n}\r\n\r\nfunction loadData() {\r\n\tconst raw: AnyObject = JSON.parse(FS(STORAGE_PATH).readIfExistsSync() || \"{}\");\r\n\tif (!(raw.channels && raw.categories)) { // hasn't been converted to new format\r\n\t\tconst data: Partial = {};\r\n\t\tdata.channels = raw;\r\n\t\tdata.categories = [];\r\n\t\t// re-save into new format\r\n\t\tFS(STORAGE_PATH).writeUpdate(() => JSON.stringify(data));\r\n\t\treturn data as ChannelData;\r\n\t}\r\n\treturn raw as ChannelData;\r\n}\r\n\r\nconst channelData: ChannelData = loadData();\r\n\r\nexport class YoutubeInterface {\r\n\tinterval: NodeJS.Timer | null;\r\n\tintervalTime: number;\r\n\tdata: ChannelData;\r\n\tlinkRegex = /^(https?:\\/\\/)?(www\\.)?(youtube\\.com|youtu\\.be)(\\/|$)/i;\r\n\tconstructor(data?: ChannelData) {\r\n\t\tthis.data = data ? data : {categories: [], channels: {}};\r\n\t\tthis.interval = null;\r\n\t\tthis.intervalTime = 0;\r\n\t\tif (data?.intervalTime) {\r\n\t\t\tthis.runInterval(`${data.intervalTime}`);\r\n\t\t}\r\n\t}\r\n\tasync getChannelData(link: string, username?: string) {\r\n\t\tif (!Config.youtubeKey) {\r\n\t\t\tthrow new Chat.ErrorMessage(`This server does not support YouTube commands. If you're the owner, you can enable them by setting up Config.youtubekey.`);\r\n\t\t}\r\n\t\tconst id = this.getId(link);\r\n\t\tconst raw = await Net(`${ROOT}channels`).get({\r\n\t\t\tquery: {part: 'snippet,statistics', id, key: Config.youtubeKey},\r\n\t\t});\r\n\t\tconst res = JSON.parse(raw);\r\n\t\tif (!res?.items || res.items.length < 1) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Channel not found.`);\r\n\t\t}\r\n\t\tconst data = res.items[0];\r\n\t\tconst cache: ChannelEntry = {\r\n\t\t\tname: data.snippet.title,\r\n\t\t\tdescription: data.snippet.description,\r\n\t\t\turl: data.snippet.customUrl,\r\n\t\t\ticon: data.snippet.thumbnails.medium.url,\r\n\t\t\tvideos: Number(data.statistics.videoCount),\r\n\t\t\tsubs: Number(data.statistics.subscriberCount),\r\n\t\t\tviews: Number(data.statistics.viewCount),\r\n\t\t\tusername: username,\r\n\t\t};\r\n\t\tthis.data.channels[id] = {...cache};\r\n\t\tthis.save();\r\n\t\treturn cache;\r\n\t}\r\n\tasync generateChannelDisplay(link: string) {\r\n\t\tconst id = this.getId(link);\r\n\t\tconst {name, description, icon, videos, subs, views, username} = await this.get(id);\r\n\t\t// credits bumbadadabum for most of the html\r\n\t\tlet buf = `
`;\r\n\t\tbuf += `
`;\r\n\t\tbuf += `
`;\r\n\t\tbuf += ``;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `${name}`;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `${videos} videos | ${subs} subscribers | ${views} video views

`;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `${description.slice(0, 400).replace(/\\n/g, ' ')}${description.length > 400 ? '(...)' : ''}

`;\r\n\t\tif (username) {\r\n\t\t\tbuf += `

PS username: ${username}

`;\r\n\t\t} else {\r\n\t\t\tbuf += '';\r\n\t\t}\r\n\t\treturn buf;\r\n\t}\r\n\trandChannel(cat?: string) {\r\n\t\tlet channels = Object.keys(this.data.channels);\r\n\t\tif (channels.length < 1) {\r\n\t\t\tthrow new Chat.ErrorMessage(`There are no channels in the database.`);\r\n\t\t}\r\n\t\tif (cat) {\r\n\t\t\tcat = toID(cat);\r\n\t\t\tconst categoryIDs = this.data.categories.map(toID);\r\n\t\t\tif (!categoryIDs.includes(cat as ID)) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(`Invalid category.`);\r\n\t\t\t}\r\n\t\t\tchannels = channels.filter(id => {\r\n\t\t\t\tconst channel = this.data.channels[id];\r\n\t\t\t\treturn channel.category && toID(channel.category) === cat;\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tconst id = Utils.shuffle(channels)[0];\r\n\t\treturn this.generateChannelDisplay(id);\r\n\t}\r\n\tget(id: string, username?: string): Promise {\r\n\t\tif (!(id in this.data.channels)) return this.getChannelData(id, username);\r\n\t\treturn Promise.resolve({...this.data.channels[id]});\r\n\t}\r\n\tasync getVideoData(id: string): Promise {\r\n\t\tconst cached = videoDataCache.get(id);\r\n\t\tif (cached) return cached;\r\n\t\tlet raw;\r\n\t\ttry {\r\n\t\t\traw = await Net(`${ROOT}videos`).get({\r\n\t\t\t\tquery: {part: 'snippet,statistics', id, key: Config.youtubeKey},\r\n\t\t\t});\r\n\t\t} catch (e: any) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Failed to retrieve video data: ${e.message}.`);\r\n\t\t}\r\n\t\tconst res = JSON.parse(raw);\r\n\t\tif (!res?.items || res.items.length < 1) return null;\r\n\t\tconst video = res.items[0];\r\n\t\tconst data: VideoData = {\r\n\t\t\ttitle: video.snippet.title,\r\n\t\t\tid,\r\n\t\t\tdate: new Date(video.snippet.publishedAt).toString(),\r\n\t\t\tdescription: video.snippet.description,\r\n\t\t\tchannelTitle: video.snippet.channelTitle,\r\n\t\t\tchannelUrl: video.snippet.channelId,\r\n\t\t\tviews: video.statistics.viewCount,\r\n\t\t\tthumbnail: video.snippet.thumbnails.default.url,\r\n\t\t\tlikes: video.statistics.likeCount,\r\n\t\t\tdislikes: video.statistics.dislikeCount,\r\n\t\t};\r\n\t\tvideoDataCache.set(id, data);\r\n\t\treturn data;\r\n\t}\r\n\tchannelSearch(search: string) {\r\n\t\tlet channel;\r\n\t\tif (this.data.channels[search]) {\r\n\t\t\tchannel = search;\r\n\t\t} else {\r\n\t\t\tfor (const id of Object.keys(this.data.channels)) {\r\n\t\t\t\tconst name = toID(this.data.channels[id].name);\r\n\t\t\t\tconst username = this.data.channels[id].username;\r\n\t\t\t\tif (name === toID(search) || username && toID(username) === toID(search)) {\r\n\t\t\t\t\tchannel = id;\r\n\t\t\t\t\tbreak; // don't iterate through everything once a match is found\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn channel;\r\n\t}\r\n\tgetId(link: string) {\r\n\t\tlet id = '';\r\n\t\tif (!link) throw new Chat.ErrorMessage('You must provide a YouTube link.');\r\n\t\tif (this.data.channels[link]) return link;\r\n\t\tif (!link.includes('channel/')) {\r\n\t\t\tif (link.includes('youtube')) {\r\n\t\t\t\tid = link.split('v=')[1] || '';\r\n\t\t\t} else if (link.includes('youtu.be')) {\r\n\t\t\t\tid = link.split('/')[3] || '';\r\n\t\t\t} else {\r\n\t\t\t\tthrow new Chat.ErrorMessage('Invalid YouTube channel link.');\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tid = link.split('channel/')[1] || '';\r\n\t\t}\r\n\t\tif (id.includes('&')) id = id.split('&')[0];\r\n\t\tif (id.includes('?')) id = id.split('?')[0];\r\n\t\treturn id;\r\n\t}\r\n\tasync generateVideoDisplay(link: string, fullInfo = false) {\r\n\t\tif (!Config.youtubeKey) {\r\n\t\t\tthrow new Chat.ErrorMessage(`This server does not support YouTube commands. If you're the owner, you can enable them by setting up Config.youtubekey.`);\r\n\t\t}\r\n\t\tconst id = this.getId(link);\r\n\t\tconst info = await this.getVideoData(id);\r\n\t\tif (!info) throw new Chat.ErrorMessage(`Video not found.`);\r\n\t\tif (!fullInfo) {\r\n\t\t\tlet buf = `${info.title} `;\r\n\t\t\tbuf += `(${info.channelTitle})
`;\r\n\t\t\tbuf += ``;\r\n\t\t\treturn buf;\r\n\t\t}\r\n\t\tlet buf = ``;\r\n\t\tbuf += ``;\r\n\t\treturn buf;\r\n\t}\r\n\tsave() {\r\n\t\treturn FS(STORAGE_PATH).writeUpdate(() => JSON.stringify(this.data));\r\n\t}\r\n\tasync searchVideo(name: string, limit?: number): Promise {\r\n\t\tconst cached = searchDataCache.get(toID(name));\r\n\t\tif (cached) {\r\n\t\t\treturn cached.slice(0, limit);\r\n\t\t}\r\n\t\tconst raw = await Net(`${ROOT}search`).get({\r\n\t\t\tquery: {\r\n\t\t\t\tpart: 'snippet', q: name,\r\n\t\t\t\tkey: Config.youtubeKey, order: 'relevance',\r\n\t\t\t},\r\n\t\t});\r\n\t\tconst result = JSON.parse(raw);\r\n\t\tconst resultArray = result.items?.map((item: AnyObject) => item?.id?.videoId).filter(Boolean);\r\n\t\tsearchDataCache.set(toID(name), resultArray);\r\n\t\treturn resultArray.slice(0, limit);\r\n\t}\r\n\tasync searchChannel(name: string, limit = 10): Promise {\r\n\t\tconst raw = await Net(`${ROOT}search`).get({\r\n\t\t\tquery: {\r\n\t\t\t\tpart: 'snippet', q: name, type: 'channel',\r\n\t\t\t\tkey: Config.youtubeKey, order: 'relevance', maxResults: limit,\r\n\t\t\t},\r\n\t\t});\r\n\t\tconst result = JSON.parse(raw);\r\n\t\treturn result?.items.map((item: AnyObject) => item?.snippet?.channelId);\r\n\t}\r\n\trunInterval(time: string) {\r\n\t\tlet interval = Number(time);\r\n\t\tif (interval < 10) throw new Chat.ErrorMessage(`${interval} is too low - set it above 10 minutes.`);\r\n\t\tthis.intervalTime = interval;\r\n\t\tthis.data.intervalTime = interval;\r\n\t\tinterval = interval * 60 * 1000;\r\n\t\tif (this.interval) clearInterval(this.interval);\r\n\t\tthis.interval = setInterval(() => {\r\n\t\t\tvoid (async () => {\r\n\t\t\t\tconst room = Rooms.get('youtube');\r\n\t\t\t\tif (!room) return; // do nothing if the room doesn't exist anymore\r\n\t\t\t\tconst res = await YouTube.randChannel();\r\n\t\t\t\troom.add(`|html|${res}`).update();\r\n\t\t\t})();\r\n\t\t}, interval);\r\n\t\treturn this.interval;\r\n\t}\r\n\tasync createGroupWatch(url: string, baseRoom: Room, title: string) {\r\n\t\tconst id = this.getId(url);\r\n\t\tconst videoInfo = await this.getVideoData(id);\r\n\t\tif (!videoInfo) throw new Chat.ErrorMessage(`Video not found.`);\r\n\t\tconst num = baseRoom.nextGameNumber();\r\n\t\tbaseRoom.saveSettings();\r\n\t\tconst gameRoom = Rooms.createGameRoom(`video-watch-${num}` as RoomID, Utils.html`[Group Watch] ${title}`, {\r\n\t\t\tisPrivate: 'hidden',\r\n\t\t});\r\n\t\tconst game = new GroupWatch(gameRoom, url, videoInfo);\r\n\t\tgameRoom.game = game;\r\n\t\tgameRoom.setParent(baseRoom);\r\n\t\treturn gameRoom;\r\n\t}\r\n}\r\n\r\nexport const Twitch = new class {\r\n\tlinkRegex = /(https?:\\/\\/)?twitch.tv\\/([A-Za-z0-9]+)/i;\r\n\tasync getChannel(channel: string): Promise {\r\n\t\tchannel = toID(channel);\r\n\t\tlet res;\r\n\t\ttry {\r\n\t\t\tres = await Net(`https://api.twitch.tv/kraken/search/channels`).get({\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'Client-Id': Config.twitchKey,\r\n\t\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t\t\t'Accept': \"application/vnd.twitchtv.v5+json\",\r\n\t\t\t\t},\r\n\t\t\t\tquery: {query: channel},\r\n\t\t\t});\r\n\t\t} catch (e: any) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Error retrieving twitch channel: ${e.message}`);\r\n\t\t}\r\n\t\tconst data = JSON.parse(res);\r\n\t\tUtils.sortBy(data.channels as AnyObject[], c => -c.followers);\r\n\t\treturn data?.channels?.[0] as TwitchChannel | undefined;\r\n\t}\r\n\tvisualizeChannel(info: TwitchChannel) {\r\n\t\tlet buf = `
`;\r\n\t\tbuf += `
`;\r\n\t\tbuf += ``;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `${info.title}`;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `${info.likes} likes | ${info.dislikes} dislikes | ${info.views} video views

`;\r\n\t\tbuf += `Published on ${info.date} | ID: ${id}
Uploaded by: ${info.channelTitle}

`;\r\n\t\tbuf += `
Video Description

`;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `${info.description.slice(0, 400).replace(/\\n/g, ' ')}${info.description.length > 400 ? '(...)' : ''}

`;\r\n\t\tbuf += `
`;\r\n\t\tbuf += `
`;\r\n\t\tbuf += ``;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `${info.display_name}`;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `

`;\r\n\t\tconst created = new Date(info.created_at);\r\n\t\tbuf += `${info.followers} subscribers | ${info.views} stream views | created ${Chat.toTimestamp(created).split(' ')[0]}

`;\r\n\t\tbuf += `

Last seen playing ${info.game} (Status: ${info.status})

`;\r\n\t\tbuf += `

`;\r\n\t\tbuf += `${info.description.slice(0, 400).replace(/\\n/g, ' ')}${info.description.length > 400 ? '...' : ''}

`;\r\n\t\tbuf += '
';\r\n\t\treturn buf;\r\n\t}\r\n};\r\n\r\nexport class GroupWatch extends Rooms.SimpleRoomGame {\r\n\turl: string;\r\n\tinfo: VideoData;\r\n\tstarted: number | null = null;\r\n\tconstructor(room: Room, url: string, videoInfo: VideoData) {\r\n\t\tsuper(room);\r\n\t\tthis.url = url;\r\n\t\tthis.info = videoInfo;\r\n\t\tthis.controls(`

Waiting to start the video...

`);\r\n\t}\r\n\tonJoin(user: User) {\r\n\t\tconst hints = this.hints();\r\n\t\tfor (const hint of hints) {\r\n\t\t\tuser.sendTo(this.room.roomid, `|html|${hint}`);\r\n\t\t}\r\n\t}\r\n\tstart() {\r\n\t\tif (this.started) throw new Chat.ErrorMessage(`We've already started.`);\r\n\t\tthis.controls(this.getStatsDisplay());\r\n\t\tthis.field(this.getVideoDisplay());\r\n\t\tthis.started = Date.now();\r\n\t\tthis.add(`|html|

Group Watch!

`);\r\n\t}\r\n\thints() {\r\n\t\tconst hints = [\r\n\t\t\t`To watch, all you need to do is click play on the video once staff have started it!`,\r\n\t\t\t`We are currently watching: ${this.info.title}`,\r\n\t\t];\r\n\t\tif (this.started) {\r\n\t\t\tconst diff = Date.now() - this.started;\r\n\t\t\thints.push(`Video is currently at ${Chat.toDurationString(diff)} (${diff / 1000} seconds)`);\r\n\t\t}\r\n\t\treturn hints;\r\n\t}\r\n\tgetStatsDisplay() {\r\n\t\tlet controlsHTML = `

${this.info.title}

`;\r\n\t\tcontrolsHTML += `
Channel: `;\r\n\t\tcontrolsHTML += `${this.info.channelTitle}
`;\r\n\t\tcontrolsHTML += `Likes: ${this.info.likes} | Dislikes: ${this.info.dislikes}
`;\r\n\t\tcontrolsHTML += `Uploaded: ${Chat.toTimestamp(new Date(this.info.date))}
`;\r\n\t\tcontrolsHTML += `
Description${this.info.description.replace(/\\n/ig, '
')}
`;\r\n\t\tcontrolsHTML += `
`;\r\n\t\treturn controlsHTML;\r\n\t}\r\n\tgetVideoDisplay() {\r\n\t\tlet buf = `

`;\r\n\t\tbuf += `

${this.info.title}
`;\r\n\t\tconst id = YouTube.getId(this.url);\r\n\t\tconst url = `https://youtube.com/watch?v=${id}`;\r\n\t\tbuf += ``;\r\n\t\tbuf += `
`.repeat(4);\r\n\t\tbuf += `

`;\r\n\t\treturn buf;\r\n\t}\r\n\tcontrols(html: string) {\r\n\t\tthis.add(`|controlshtml|
${html}
`);\r\n\t}\r\n\tfield(html: string) {\r\n\t\tthis.add(`|fieldhtml|${html}`);\r\n\t}\r\n\tadd(buf: string) {\r\n\t\tthis.room.add(buf).update();\r\n\t}\r\n\tdestroy() {\r\n\t\tthis.controls(`The group watch has ended.`);\r\n\t\tlet endBuf = `
`;\r\n\t\tendBuf += this.getStatsDisplay();\r\n\t\tendBuf += `
Thanks for watching!
`;\r\n\t\tthis.field(endBuf);\r\n\t\tthis.room = null!;\r\n\t}\r\n}\r\n\r\nexport class TwitchStream extends Rooms.SimpleRoomGame {\r\n\tstarted = false;\r\n\tdata: TwitchChannel;\r\n\tconstructor(room: Room, data: TwitchChannel) {\r\n\t\tsuper(room);\r\n\t\tthis.data = data;\r\n\t}\r\n\tstatic async createStreamWatch(room: Room, channel: string) {\r\n\t\tif ([...Rooms.rooms.values()].some(\r\n\t\t\tr => r.roomid.startsWith(`twitch-`) && r.parent?.roomid === room?.roomid\r\n\t\t)) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Twitch watch already in progress for this room.`);\r\n\t\t}\r\n\t\tconst data = await Twitch.getChannel(channel);\r\n\t\tif (!data) throw new Chat.ErrorMessage(`Channel not found`);\r\n\t\tconst watchRoom = Rooms.createGameRoom(\r\n\t\t\t`twitch-stream-watch-${room.nextGameNumber()}` as RoomID,\r\n\t\t\tUtils.html`[Twitch Watch] ${data.display_name}`,\r\n\t\t\t{isPrivate: 'hidden'}\r\n\t\t);\r\n\t\troom.saveSettings();\r\n\t\twatchRoom.setParent(room);\r\n\t\tconst stream = new TwitchStream(watchRoom, data);\r\n\t\twatchRoom.game = stream;\r\n\t\treturn watchRoom;\r\n\t}\r\n\tfield(buf: string) {\r\n\t\tthis.add(`|fieldhtml|${buf}`);\r\n\t}\r\n\tcontrols(buf: string) {\r\n\t\tthis.add(`|controlshtml|${buf}`);\r\n\t}\r\n\tadd(data: string) {\r\n\t\treturn this.room.add(data).update();\r\n\t}\r\n\tonJoin(user: User) {\r\n\t\tif (!user.named) return;\r\n\t\tthis.controls(this.getControlsDisplay());\r\n\t}\r\n\tonLeave(user: User) {\r\n\t\tif (!user.named) return;\r\n\t\tthis.controls(this.getControlsDisplay());\r\n\t}\r\n\tstart() {\r\n\t\tif (this.started) {\r\n\t\t\tthrow new Chat.ErrorMessage(`Stream already started`);\r\n\t\t}\r\n\t\tthis.controls(this.getControlsDisplay());\r\n\t\tthis.field(this.getStreamDisplay());\r\n\t\tthis.add(`|html|

The stream watch has started!

`);\r\n\t\tthis.started = true;\r\n\t}\r\n\tend() {\r\n\t\tthis.field('');\r\n\t\tthis.controls(`

Stream watch ended

`);\r\n\t\tthis.room.parent?.add(`|uhtmlchange|ts-${this.room.roomid}|`);\r\n\t\tthis.add(`|expire|Stream ended`);\r\n\t\tthis.room.destroy();\r\n\t}\r\n\tgetControlsDisplay() {\r\n\t\tlet buf = `

`;\r\n\t\tbuf += `Watching ${this.data.display_name}
`;\r\n\t\tbuf += `${Chat.count(Object.keys(this.room.users).length, 'users')} watching
`;\r\n\t\tbuf += `Playing: ${this.data.game}`;\r\n\t\treturn buf;\r\n\t}\r\n\tgetStreamDisplay() {\r\n\t\tlet buf = `

`;\r\n\t\tbuf += ``;\r\n\t\treturn buf;\r\n\t}\r\n}\r\n\r\nexport const YouTube = new YoutubeInterface(channelData);\r\n\r\nexport function destroy() {\r\n\tif (YouTube.interval) clearInterval(YouTube.interval);\r\n}\r\n\r\nexport const commands: Chat.ChatCommands = {\r\n\tasync randchannel(target, room, user) {\r\n\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\tif (Object.keys(YouTube.data.channels).length < 1) return this.errorReply(`No channels in the database.`);\r\n\t\ttarget = toID(target);\r\n\t\tthis.runBroadcast();\r\n\t\tconst data = await YouTube.randChannel(target);\r\n\t\treturn this.sendReply(`|html|${data}`);\r\n\t},\r\n\trandchannelhelp: [`/randchannel - View data of a random channel from the YouTube database.`],\r\n\r\n\tyt: 'youtube',\r\n\tyoutube: {\r\n\t\tasync addchannel(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tconst [id, name] = target.split(',').map(t => t.trim());\r\n\t\t\tif (!id) return this.errorReply('Specify a channel ID.');\r\n\t\t\tawait YouTube.getChannelData(id, name);\r\n\t\t\tthis.modlog('ADDCHANNEL', null, `${id} ${name ? `username: ${name}` : ''}`);\r\n\t\t\treturn this.privateModAction(\r\n\t\t\t\t`${user.name} added channel with id ${id} ${name ? `and username (${name}) ` : ''} to the random channel pool.`\r\n\t\t\t);\r\n\t\t},\r\n\t\taddchannelhelp: [`/addchannel - Add channel data to the YouTube database. Requires: % @ #`],\r\n\r\n\t\tremovechannel(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tconst id = YouTube.channelSearch(target);\r\n\t\t\tif (!id) return this.errorReply(`Channel with ID or name ${target} not found.`);\r\n\t\t\tdelete YouTube.data.channels[id];\r\n\t\t\tYouTube.save();\r\n\t\t\tthis.privateModAction(`${user.name} deleted channel with ID or name ${target}.`);\r\n\t\t\treturn this.modlog(`REMOVECHANNEL`, null, id);\r\n\t\t},\r\n\t\tremovechannelhelp: [`/youtube removechannel - Delete channel data from the YouTube database. Requires: % @ #`],\r\n\r\n\t\tasync channel(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tconst channel = YouTube.channelSearch(target);\r\n\t\t\tif (!channel) return this.errorReply(`No channels with ID or name ${target} found.`);\r\n\t\t\tconst data = await YouTube.generateChannelDisplay(channel);\r\n\t\t\tthis.runBroadcast();\r\n\t\t\treturn this.sendReply(`|html|${data}`);\r\n\t\t},\r\n\t\tchannelhelp: [\r\n\t\t\t'/youtube channel - View the data of a specified channel. Can be either channel ID or channel name.',\r\n\t\t],\r\n\t\tasync video(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tconst buffer = await YouTube.generateVideoDisplay(target, true);\r\n\t\t\tthis.runBroadcast();\r\n\t\t\tthis.sendReplyBox(buffer);\r\n\t\t},\r\n\r\n\t\tchannels(target, room, user) {\r\n\t\t\ttarget = toID(target);\r\n\t\t\treturn this.parse(`/j view-channels${target ? `-${target}` : ''}`);\r\n\t\t},\r\n\t\thelp(target, room, user) {\r\n\t\t\treturn this.parse('/help youtube');\r\n\t\t},\r\n\r\n\t\tcategories() {\r\n\t\t\treturn this.parse(`/j view-channels-categories`);\r\n\t\t},\r\n\r\n\t\tupdate(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tconst [channel, name] = target.split(',');\r\n\t\t\tconst id = YouTube.channelSearch(channel);\r\n\t\t\tif (!id) return this.errorReply(`Channel ${channel} is not in the database.`);\r\n\t\t\tYouTube.data.channels[id].username = name;\r\n\t\t\tthis.modlog(`UPDATECHANNEL`, null, name);\r\n\t\t\tthis.privateModAction(`${user.name} updated channel ${id}'s username to ${name}.`);\r\n\t\t\tYouTube.save();\r\n\t\t},\r\n\t\tinterval: 'repeat',\r\n\t\trepeat(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tthis.checkCan('declare', null, room);\r\n\t\t\tif (!target) {\r\n\t\t\t\tif (!YouTube.interval) return this.errorReply(`The YouTube plugin is not currently running an interval.`);\r\n\t\t\t\treturn this.sendReply(`Interval is currently set to ${Chat.toDurationString(YouTube.intervalTime * 60 * 1000)}.`);\r\n\t\t\t}\r\n\t\t\tif (this.meansNo(target)) {\r\n\t\t\t\tif (!YouTube.interval) return this.errorReply(`The interval is not currently running`);\r\n\t\t\t\tclearInterval(YouTube.interval);\r\n\t\t\t\tdelete YouTube.data.intervalTime;\r\n\t\t\t\tYouTube.save();\r\n\t\t\t\tthis.privateModAction(`${user.name} turned off the YouTube interval`);\r\n\t\t\t\treturn this.modlog(`YOUTUBE INTERVAL`, null, 'OFF');\r\n\t\t\t}\r\n\t\t\tif (Object.keys(channelData).length < 1) return this.errorReply(`No channels in the database.`);\r\n\t\t\tif (isNaN(parseInt(target))) return this.errorReply(`Specify a number (in minutes) for the interval.`);\r\n\t\t\tYouTube.runInterval(target);\r\n\t\t\tYouTube.save();\r\n\t\t\tthis.privateModAction(`${user.name} set a randchannel interval to ${target} minutes`);\r\n\t\t\treturn this.modlog(`CHANNELINTERVAL`, null, `${target} minutes`);\r\n\t\t},\r\n\t\taddcategory(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tconst categoryID = toID(target);\r\n\t\t\tif (!categoryID) return this.parse(`/help youtube`);\r\n\t\t\tif (YouTube.data.categories.map(toID).includes(categoryID)) {\r\n\t\t\t\treturn this.errorReply(`This category is already added. To change it, remove it and re-add it.`);\r\n\t\t\t}\r\n\t\t\tYouTube.data.categories.push(target);\r\n\t\t\tthis.modlog(`YOUTUBE ADDCATEGORY`, null, target);\r\n\t\t\tthis.privateModAction(`${user.name} added category '${target}' to the categories list.`);\r\n\t\t\tYouTube.save();\r\n\t\t},\r\n\t\tremovecategory(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tconst categoryID = toID(target);\r\n\t\t\tif (!categoryID) return this.parse(`/help youtube`);\r\n\t\t\tconst index = YouTube.data.categories.indexOf(target);\r\n\t\t\tif (index < 0) {\r\n\t\t\t\treturn this.errorReply(`${target} is not a valid category.`);\r\n\t\t\t}\r\n\t\t\tfor (const id in YouTube.data.channels) {\r\n\t\t\t\tconst channel = YouTube.data.channels[id];\r\n\t\t\t\tif (channel.category === target) delete YouTube.data.channels[id].category;\r\n\t\t\t}\r\n\t\t\tYouTube.save();\r\n\t\t\tthis.privateModAction(`${user.name} removed the category '${target}' from the category list.`);\r\n\t\t\tthis.modlog(`YOUTUBE REMOVECATEGORY`, null, target);\r\n\t\t},\r\n\t\tsetcategory(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\ttarget = target.trim();\r\n\t\t\tconst [category, id] = Utils.splitFirst(target, ',').map(item => item.trim());\r\n\t\t\tif (!target || !category || !id) {\r\n\t\t\t\treturn this.parse('/help youtube');\r\n\t\t\t}\r\n\t\t\tif (!YouTube.data.categories.includes(category)) {\r\n\t\t\t\treturn this.errorReply(`Invalid category.`);\r\n\t\t\t}\r\n\t\t\tconst name = YouTube.channelSearch(id);\r\n\t\t\tif (!name) return this.errorReply(`Invalid channel.`);\r\n\t\t\tconst channel = YouTube.data.channels[name];\r\n\t\t\tYouTube.data.channels[name].category = category;\r\n\t\t\tYouTube.save();\r\n\t\t\tthis.modlog(`YOUTUBE SETCATEGORY`, null, `${id}: to category ${category}`);\r\n\t\t\tthis.privateModAction(`${user.name} set the channel ${channel.name}'s category to '${category}'.`);\r\n\t\t},\r\n\t\tdecategorize(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\ttarget = target.trim();\r\n\t\t\tif (!target) {\r\n\t\t\t\treturn this.parse('/help youtube');\r\n\t\t\t}\r\n\t\t\tconst name = YouTube.channelSearch(target);\r\n\t\t\tif (!name) return this.errorReply(`Invalid channel.`);\r\n\t\t\tconst channel = YouTube.data.channels[name];\r\n\t\t\tconst category = channel.category;\r\n\t\t\tif (!category) return this.errorReply(`That channel does not have a category.`);\r\n\t\t\tdelete channel.category;\r\n\t\t\tYouTube.save();\r\n\t\t\tthis.modlog(`YOUTUBE DECATEGORIZE`, null, target);\r\n\t\t\tthis.privateModAction(`${user.name} removed the channel ${channel.name} from the category ${category}.`);\r\n\t\t},\r\n\t\tasync groupwatch(target, room, user) {\r\n\t\t\troom = this.requireRoom();\r\n\t\t\tif (!GROUPWATCH_ROOMS.includes(room.roomid)) {\r\n\t\t\t\treturn this.errorReply(`This room is not allowed to use the groupwatch function.`);\r\n\t\t\t}\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tconst [url, title] = Utils.splitFirst(target, ',').map(p => p.trim());\r\n\t\t\tif (!url || !title) return this.errorReply(`You must specify a video to watch and a title for the group watch.`);\r\n\t\t\tconst gameRoom = await YouTube.createGroupWatch(url, room, title);\r\n\t\t\tthis.modlog(`YOUTUBE GROUPWATCH`, null, `${url} (${title})`);\r\n\t\t\troom.add(\r\n\t\t\t\t`|uhtml|${gameRoom.roomid}|` +\r\n\t\t\t\t``\r\n\t\t\t);\r\n\t\t\troom.send(`|tempnotify|youtube|New groupwatch - ${title}!`);\r\n\t\t\tthis.update();\r\n\t\t\tuser.joinRoom(gameRoom);\r\n\t\t},\r\n\t\tendwatch(target, room, user) {\r\n\t\t\troom = this.requireRoom();\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tthis.requireGame(GroupWatch);\r\n\t\t\troom.parent!.modlog({action: `GROUPWATCH END`, loggedBy: user.id});\r\n\t\t\troom.parent!.add(`|uhtmlchange|${room.roomid}|`).update();\r\n\t\t\troom.destroy();\r\n\t\t},\r\n\t\tstartwatch: 'beginwatch',\r\n\t\tbeginwatch(target, room, user) {\r\n\t\t\troom = this.requireRoom();\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tconst game = this.requireGame(GroupWatch);\r\n\t\t\tgame.start();\r\n\t\t},\r\n\t\tgroupwatches() {\r\n\t\t\tlet buf = `Ongoing groupwatches:
`;\r\n\t\t\tfor (const curRoom of Rooms.rooms.values()) {\r\n\t\t\t\tif (!curRoom.getGame(GroupWatch)) continue;\r\n\t\t\t\tbuf += ``;\r\n\t\t\t}\r\n\t\t\tthis.runBroadcast();\r\n\t\t\tthis.sendReplyBox(buf);\r\n\t\t},\r\n\t},\r\n\tyoutubehelp: [\r\n\t\t`YouTube commands:`,\r\n\t\t`/randchannel [optional category]- View data of a random channel from the YouTube database.` +\r\n\t\t\t` If a category is given, the random channel will be in the given category.`,\r\n\t\t`/youtube addchannel [channel] - Add channel data to the YouTube database. Requires: % @ #`,\r\n\t\t`/youtube removechannel [channel]- Delete channel data from the YouTube database. Requires: % @ #`,\r\n\t\t`/youtube channel [channel] - View the data of a specified channel. Can be either channel ID or channel name.`,\r\n\t\t`/youtube video [video] - View data of a specified video. Can be either channel ID or channel name.`,\r\n\t\t`/youtube update [channel], [name] - sets a channel's PS username to [name]. Requires: % @ #`,\r\n\t\t`/youtube repeat [time] - Sets an interval for [time] minutes, showing a random channel each time. Requires: # &`,\r\n\t\t`/youtube addcategory [name] - Adds the [category] to the channel category list. Requires: @ # &`,\r\n\t\t`/youtube removecategory [name] - Removes the [category] from the channel category list. Requires: @ # &`,\r\n\t\t`/youtube setcategory [category], [channel name] - Sets the category for [channel] to [category]. Requires: @ # &`,\r\n\t\t`/youtube decategorize [channel name] - Removes the category for the [channel], if there is one. Requires: @ # &`,\r\n\t\t`/youtube categores - View all channels sorted by category.`,\r\n\t\t`/youtube groupwatch [link], [title] - Creates a group watch of the [url] with the given [title]. Requires % @ & #`,\r\n\t\t`/youtube startwatch - Starts the group watch in the current room, if there is one. Requires % @ & #`,\r\n\t\t`/youtube stopwatch - Ends the current group watch, if there is one in the current room. Requires % @ & #`,\r\n\t],\r\n\ttwitch: {\r\n\t\tasync channel(target, room, user) {\r\n\t\t\troom = this.requireRoom('youtube' as RoomID);\r\n\t\t\tif (!Config.twitchKey) return this.errorReply(`Twitch is not configured`);\r\n\t\t\tconst data = await Twitch.getChannel(target);\r\n\t\t\tif (!data) return this.errorReply(`Channel not found`);\r\n\t\t\tconst html = Twitch.visualizeChannel(data);\r\n\t\t\tthis.runBroadcast();\r\n\t\t\treturn this.sendReplyBox(html);\r\n\t\t},\r\n\t\tasync watch(target, room, user) {\r\n\t\t\troom = this.requireRoom();\r\n\t\t\tif (!GROUPWATCH_ROOMS.includes(room.roomid)) {\r\n\t\t\t\tthrow new Chat.ErrorMessage(`You cannot use this command in this room.`);\r\n\t\t\t}\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tif (!toID(target)) {\r\n\t\t\t\treturn this.errorReply(`Invalid channel`);\r\n\t\t\t}\r\n\t\t\tconst gameRoom = await TwitchStream.createStreamWatch(room, target);\r\n\t\t\tuser.joinRoom(gameRoom);\r\n\t\t\troom.add(\r\n\t\t\t\t`|uhtml|ts-${gameRoom.roomid}|` +\r\n\t\t\t\t``\r\n\t\t\t).update();\r\n\t\t},\r\n\t\tstart(target, room) {\r\n\t\t\troom = this.requireRoom();\r\n\t\t\tconst stream = this.requireGame(TwitchStream);\r\n\t\t\tstream.start();\r\n\t\t},\r\n\t\tstop(target, room, user) {\r\n\t\t\troom = this.requireRoom();\r\n\t\t\tconst stream = this.requireGame(TwitchStream);\r\n\t\t\tthis.checkCan('mute', null, room);\r\n\t\t\tstream.end();\r\n\t\t},\r\n\t},\r\n};\r\n\r\nexport const pages: Chat.PageTable = {\r\n\tasync channels(args, user) {\r\n\t\tconst [type] = args;\r\n\t\tif (!Config.youtubeKey) return `

Youtube is not configured.

`;\r\n\t\tconst titles: {[k: string]: string} = {\r\n\t\t\tall: 'All channels',\r\n\t\t\tcategories: 'by category',\r\n\t\t};\r\n\t\tconst title = titles[type] || 'Usernames only';\r\n\t\tthis.title = `[Channels] ${title}`;\r\n\t\tlet buffer = `

Channels in the YouTube database: (${title})`;\r\n\t\tbuffer += ` `;\r\n\t\tbuffer += `


`;\r\n\t\tswitch (toID(type)) {\r\n\t\tcase 'categories':\r\n\t\t\tif (!YouTube.data.categories.length) {\r\n\t\t\t\treturn this.errorReply(`There are currently no categories in the Youtube channel database.`);\r\n\t\t\t}\r\n\t\t\tconst sorted: {[k: string]: string[]} = {};\r\n\t\t\tconst channels = YouTube.data.channels;\r\n\t\t\tfor (const [id, channel] of Object.entries(channels)) {\r\n\t\t\t\tconst category = channel.category || \"No category\";\r\n\t\t\t\tif (!sorted[category]) {\r\n\t\t\t\t\tsorted[category] = [];\r\n\t\t\t\t}\r\n\t\t\t\tsorted[category].push(id);\r\n\t\t\t}\r\n\t\t\tfor (const cat in sorted) {\r\n\t\t\t\tbuffer += `

${cat}:

`;\r\n\t\t\t\tfor (const id of sorted[cat]) {\r\n\t\t\t\t\tconst channel = channels[id];\r\n\t\t\t\t\tbuffer += `
${channel.name}`;\r\n\t\t\t\t\tbuffer += await YouTube.generateChannelDisplay(id);\r\n\t\t\t\t\tbuffer += `

`;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tfor (const id of Utils.shuffle(Object.keys(YouTube.data.channels))) {\r\n\t\t\t\tconst {name, username} = await YouTube.get(id);\r\n\t\t\t\tif (toID(type) !== 'all' && !username) continue;\r\n\t\t\t\tbuffer += `
${name}`;\r\n\t\t\t\tbuffer += ` (Channel ID: ${id})`;\r\n\t\t\t\tif (username) buffer += ` (PS name: ${username})`;\r\n\t\t\t\tbuffer += ``;\r\n\t\t\t\tbuffer += await YouTube.generateChannelDisplay(id);\r\n\t\t\t\tbuffer += `

`;\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\t\tbuffer += `
`;\r\n\t\treturn buffer;\r\n\t},\r\n};\r\n"], "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,iBAA6B;AAE7B,MAAM,OAAO;AACb,MAAM,eAAe;AACrB,MAAM,mBAAmB,CAAC,WAAW,gBAAgB,cAAc,aAAa,aAAa,OAAO;AAE7F,MAAM,iBAAyC,KAAK,WAAW,SAAS,kBAAkB,oBAAI,IAAI;AAClG,MAAM,kBAAyC,KAAK,WAAW,SAAS,mBAAmB,oBAAI,IAAI;AAiD1G,SAAS,WAAW;AACnB,QAAM,MAAiB,KAAK,UAAM,eAAG,YAAY,EAAE,iBAAiB,KAAK,IAAI;AAC7E,MAAI,EAAE,IAAI,YAAY,IAAI,aAAa;AACtC,UAAM,OAA6B,CAAC;AACpC,SAAK,WAAW;AAChB,SAAK,aAAa,CAAC;AAEnB,uBAAG,YAAY,EAAE,YAAY,MAAM,KAAK,UAAU,IAAI,CAAC;AACvD,WAAO;AAAA,EACR;AACA,SAAO;AACR;AAEA,MAAM,cAA2B,SAAS;AAEnC,MAAM,iBAAiB;AAAA,EAK7B,YAAY,MAAoB;AADhC,qBAAY;AAEX,SAAK,OAAO,OAAO,OAAO,EAAC,YAAY,CAAC,GAAG,UAAU,CAAC,EAAC;AACvD,SAAK,WAAW;AAChB,SAAK,eAAe;AACpB,QAAI,MAAM,cAAc;AACvB,WAAK,YAAY,GAAG,KAAK,cAAc;AAAA,IACxC;AAAA,EACD;AAAA,EACA,MAAM,eAAe,MAAc,UAAmB;AACrD,QAAI,CAAC,OAAO,YAAY;AACvB,YAAM,IAAI,KAAK,aAAa,0HAA0H;AAAA,IACvJ;AACA,UAAM,KAAK,KAAK,MAAM,IAAI;AAC1B,UAAM,MAAM,UAAM,gBAAI,GAAG,cAAc,EAAE,IAAI;AAAA,MAC5C,OAAO,EAAC,MAAM,sBAAsB,IAAI,KAAK,OAAO,WAAU;AAAA,IAC/D,CAAC;AACD,UAAM,MAAM,KAAK,MAAM,GAAG;AAC1B,QAAI,CAAC,KAAK,SAAS,IAAI,MAAM,SAAS,GAAG;AACxC,YAAM,IAAI,KAAK,aAAa,oBAAoB;AAAA,IACjD;AACA,UAAM,OAAO,IAAI,MAAM,CAAC;AACxB,UAAM,QAAsB;AAAA,MAC3B,MAAM,KAAK,QAAQ;AAAA,MACnB,aAAa,KAAK,QAAQ;AAAA,MAC1B,KAAK,KAAK,QAAQ;AAAA,MAClB,MAAM,KAAK,QAAQ,WAAW,OAAO;AAAA,MACrC,QAAQ,OAAO,KAAK,WAAW,UAAU;AAAA,MACzC,MAAM,OAAO,KAAK,WAAW,eAAe;AAAA,MAC5C,OAAO,OAAO,KAAK,WAAW,SAAS;AAAA,MACvC;AAAA,IACD;AACA,SAAK,KAAK,SAAS,EAAE,IAAI,EAAC,GAAG,MAAK;AAClC,SAAK,KAAK;AACV,WAAO;AAAA,EACR;AAAA,EACA,MAAM,uBAAuB,MAAc;AAC1C,UAAM,KAAK,KAAK,MAAM,IAAI;AAC1B,UAAM,EAAC,MAAM,aAAa,MAAM,QAAQ,MAAM,OAAO,SAAQ,IAAI,MAAM,KAAK,IAAI,EAAE;AAElF,QAAI,MAAM;AACV,WAAO;AACP,WAAO;AACP,WAAO,YAAY,eAAe,iBAAiB;AACnD,WAAO;AACP,WAAO,mGAAmG,OAAO;AACjH,WAAO;AACP,WAAO;AACP,WAAO,GAAG,mBAAmB,sBAAsB;AACnD,WAAO;AACP,WAAO,GAAG,YAAY,MAAM,GAAG,GAAG,EAAE,QAAQ,OAAO,GAAG,IAAI,YAAY,SAAS,MAAM,UAAU;AAC/F,QAAI,UAAU;AACb,aAAO,0EAA0E;AAAA,IAClF,OAAO;AACN,aAAO;AAAA,IACR;AACA,WAAO;AAAA,EACR;AAAA,EACA,YAAY,KAAc;AACzB,QAAI,WAAW,OAAO,KAAK,KAAK,KAAK,QAAQ;AAC7C,QAAI,SAAS,SAAS,GAAG;AACxB,YAAM,IAAI,KAAK,aAAa,wCAAwC;AAAA,IACrE;AACA,QAAI,KAAK;AACR,YAAM,KAAK,GAAG;AACd,YAAM,cAAc,KAAK,KAAK,WAAW,IAAI,IAAI;AACjD,UAAI,CAAC,YAAY,SAAS,GAAS,GAAG;AACrC,cAAM,IAAI,KAAK,aAAa,mBAAmB;AAAA,MAChD;AACA,iBAAW,SAAS,OAAO,CAAAA,QAAM;AAChC,cAAM,UAAU,KAAK,KAAK,SAASA,GAAE;AACrC,eAAO,QAAQ,YAAY,KAAK,QAAQ,QAAQ,MAAM;AAAA,MACvD,CAAC;AAAA,IACF;AAEA,UAAM,KAAK,iBAAM,QAAQ,QAAQ,EAAE,CAAC;AACpC,WAAO,KAAK,uBAAuB,EAAE;AAAA,EACtC;AAAA,EACA,IAAI,IAAY,UAA0C;AACzD,QAAI,EAAE,MAAM,KAAK,KAAK;AAAW,aAAO,KAAK,eAAe,IAAI,QAAQ;AACxE,WAAO,QAAQ,QAAQ,EAAC,GAAG,KAAK,KAAK,SAAS,EAAE,EAAC,CAAC;AAAA,EACnD;AAAA,EACA,MAAM,aAAa,IAAuC;AACzD,UAAM,SAAS,eAAe,IAAI,EAAE;AACpC,QAAI;AAAQ,aAAO;AACnB,QAAI;AACJ,QAAI;AACH,YAAM,UAAM,gBAAI,GAAG,YAAY,EAAE,IAAI;AAAA,QACpC,OAAO,EAAC,MAAM,sBAAsB,IAAI,KAAK,OAAO,WAAU;AAAA,MAC/D,CAAC;AAAA,IACF,SAAS,GAAP;AACD,YAAM,IAAI,KAAK,aAAa,kCAAkC,EAAE,UAAU;AAAA,IAC3E;AACA,UAAM,MAAM,KAAK,MAAM,GAAG;AAC1B,QAAI,CAAC,KAAK,SAAS,IAAI,MAAM,SAAS;AAAG,aAAO;AAChD,UAAM,QAAQ,IAAI,MAAM,CAAC;AACzB,UAAM,OAAkB;AAAA,MACvB,OAAO,MAAM,QAAQ;AAAA,MACrB;AAAA,MACA,MAAM,IAAI,KAAK,MAAM,QAAQ,WAAW,EAAE,SAAS;AAAA,MACnD,aAAa,MAAM,QAAQ;AAAA,MAC3B,cAAc,MAAM,QAAQ;AAAA,MAC5B,YAAY,MAAM,QAAQ;AAAA,MAC1B,OAAO,MAAM,WAAW;AAAA,MACxB,WAAW,MAAM,QAAQ,WAAW,QAAQ;AAAA,MAC5C,OAAO,MAAM,WAAW;AAAA,MACxB,UAAU,MAAM,WAAW;AAAA,IAC5B;AACA,mBAAe,IAAI,IAAI,IAAI;AAC3B,WAAO;AAAA,EACR;AAAA,EACA,cAAc,QAAgB;AAC7B,QAAI;AACJ,QAAI,KAAK,KAAK,SAAS,MAAM,GAAG;AAC/B,gBAAU;AAAA,IACX,OAAO;AACN,iBAAW,MAAM,OAAO,KAAK,KAAK,KAAK,QAAQ,GAAG;AACjD,cAAM,OAAO,KAAK,KAAK,KAAK,SAAS,EAAE,EAAE,IAAI;AAC7C,cAAM,WAAW,KAAK,KAAK,SAAS,EAAE,EAAE;AACxC,YAAI,SAAS,KAAK,MAAM,KAAK,YAAY,KAAK,QAAQ,MAAM,KAAK,MAAM,GAAG;AACzE,oBAAU;AACV;AAAA,QACD;AAAA,MACD;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EACA,MAAM,MAAc;AACnB,QAAI,KAAK;AACT,QAAI,CAAC;AAAM,YAAM,IAAI,KAAK,aAAa,kCAAkC;AACzE,QAAI,KAAK,KAAK,SAAS,IAAI;AAAG,aAAO;AACrC,QAAI,CAAC,KAAK,SAAS,UAAU,GAAG;AAC/B,UAAI,KAAK,SAAS,SAAS,GAAG;AAC7B,aAAK,KAAK,MAAM,IAAI,EAAE,CAAC,KAAK;AAAA,MAC7B,WAAW,KAAK,SAAS,UAAU,GAAG;AACrC,aAAK,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK;AAAA,MAC5B,OAAO;AACN,cAAM,IAAI,KAAK,aAAa,+BAA+B;AAAA,MAC5D;AAAA,IACD,OAAO;AACN,WAAK,KAAK,MAAM,UAAU,EAAE,CAAC,KAAK;AAAA,IACnC;AACA,QAAI,GAAG,SAAS,GAAG;AAAG,WAAK,GAAG,MAAM,GAAG,EAAE,CAAC;AAC1C,QAAI,GAAG,SAAS,GAAG;AAAG,WAAK,GAAG,MAAM,GAAG,EAAE,CAAC;AAC1C,WAAO;AAAA,EACR;AAAA,EACA,MAAM,qBAAqB,MAAc,WAAW,OAAO;AAC1D,QAAI,CAAC,OAAO,YAAY;AACvB,YAAM,IAAI,KAAK,aAAa,0HAA0H;AAAA,IACvJ;AACA,UAAM,KAAK,KAAK,MAAM,IAAI;AAC1B,UAAM,OAAO,MAAM,KAAK,aAAa,EAAE;AACvC,QAAI,CAAC;AAAM,YAAM,IAAI,KAAK,aAAa,kBAAkB;AACzD,QAAI,CAAC,UAAU;AACd,UAAIC,OAAM,MAAM,KAAK;AACrB,MAAAA,QAAO,wDAAwD,KAAK,eAAe,KAAK;AACxF,MAAAA,QAAO,+CAA+C;AACtD,aAAOA;AAAA,IACR;AACA,QAAI,MAAM;AACV,WAAO;AACP,WAAO;AACP,WAAO,YAAY,eAAe,iBAAiB,KAAK;AACxD,WAAO;AACP,WAAO,mGAAmG,OAAO,KAAK;AACtH,WAAO;AACP,WAAO;AACP,WAAO;AACP,WAAO,GAAG,KAAK,iBAAiB,KAAK,uBAAuB,KAAK;AACjE,WAAO,uBAAuB,KAAK,cAAc,8BAA8B,KAAK;AACpF,WAAO;AACP,WAAO;AACP,WAAO,MAAM,KAAK,YAAY,MAAM,GAAG,GAAG,EAAE,QAAQ,OAAO,GAAG,IAAI,KAAK,YAAY,SAAS,MAAM,UAAU;AAC5G,WAAO;AAAA,EACR;AAAA,EACA,OAAO;AACN,eAAO,eAAG,YAAY,EAAE,YAAY,MAAM,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,EACpE;AAAA,EACA,MAAM,YAAY,MAAc,OAA+C;AAC9E,UAAM,SAAS,gBAAgB,IAAI,KAAK,IAAI,CAAC;AAC7C,QAAI,QAAQ;AACX,aAAO,OAAO,MAAM,GAAG,KAAK;AAAA,IAC7B;AACA,UAAM,MAAM,UAAM,gBAAI,GAAG,YAAY,EAAE,IAAI;AAAA,MAC1C,OAAO;AAAA,QACN,MAAM;AAAA,QAAW,GAAG;AAAA,QACpB,KAAK,OAAO;AAAA,QAAY,OAAO;AAAA,MAChC;AAAA,IACD,CAAC;AACD,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAM,cAAc,OAAO,OAAO,IAAI,CAAC,SAAoB,MAAM,IAAI,OAAO,EAAE,OAAO,OAAO;AAC5F,oBAAgB,IAAI,KAAK,IAAI,GAAG,WAAW;AAC3C,WAAO,YAAY,MAAM,GAAG,KAAK;AAAA,EAClC;AAAA,EACA,MAAM,cAAc,MAAc,QAAQ,IAAmC;AAC5E,UAAM,MAAM,UAAM,gBAAI,GAAG,YAAY,EAAE,IAAI;AAAA,MAC1C,OAAO;AAAA,QACN,MAAM;AAAA,QAAW,GAAG;AAAA,QAAM,MAAM;AAAA,QAChC,KAAK,OAAO;AAAA,QAAY,OAAO;AAAA,QAAa,YAAY;AAAA,MACzD;AAAA,IACD,CAAC;AACD,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO,QAAQ,MAAM,IAAI,CAAC,SAAoB,MAAM,SAAS,SAAS;AAAA,EACvE;AAAA,EACA,YAAY,MAAc;AACzB,QAAI,WAAW,OAAO,IAAI;AAC1B,QAAI,WAAW;AAAI,YAAM,IAAI,KAAK,aAAa,GAAG,gDAAgD;AAClG,SAAK,eAAe;AACpB,SAAK,KAAK,eAAe;AACzB,eAAW,WAAW,KAAK;AAC3B,QAAI,KAAK;AAAU,oBAAc,KAAK,QAAQ;AAC9C,SAAK,WAAW,YAAY,MAAM;AACjC,YAAM,YAAY;AACjB,cAAM,OAAO,MAAM,IAAI,SAAS;AAChC,YAAI,CAAC;AAAM;AACX,cAAM,MAAM,MAAM,QAAQ,YAAY;AACtC,aAAK,IAAI,SAAS,KAAK,EAAE,OAAO;AAAA,MACjC,GAAG;AAAA,IACJ,GAAG,QAAQ;AACX,WAAO,KAAK;AAAA,EACb;AAAA,EACA,MAAM,iBAAiB,KAAa,UAAgB,OAAe;AAClE,UAAM,KAAK,KAAK,MAAM,GAAG;AACzB,UAAM,YAAY,MAAM,KAAK,aAAa,EAAE;AAC5C,QAAI,CAAC;AAAW,YAAM,IAAI,KAAK,aAAa,kBAAkB;AAC9D,UAAM,MAAM,SAAS,eAAe;AACpC,aAAS,aAAa;AACtB,UAAM,WAAW,MAAM,eAAe,eAAe,OAAiB,iBAAM,qBAAqB,SAAS;AAAA,MACzG,WAAW;AAAA,IACZ,CAAC;AACD,UAAM,OAAO,IAAI,WAAW,UAAU,KAAK,SAAS;AACpD,aAAS,OAAO;AAChB,aAAS,UAAU,QAAQ;AAC3B,WAAO;AAAA,EACR;AACD;AAEO,MAAM,SAAS,IAAI,MAAM;AAAA,EAAN;AACzB,qBAAY;AAAA;AAAA,EACZ,MAAM,WAAW,SAAqD;AACrE,cAAU,KAAK,OAAO;AACtB,QAAI;AACJ,QAAI;AACH,YAAM,UAAM,gBAAI,8CAA8C,EAAE,IAAI;AAAA,QACnE,SAAS;AAAA,UACR,aAAa,OAAO;AAAA,UACpB,gBAAgB;AAAA,UAChB,UAAU;AAAA,QACX;AAAA,QACA,OAAO,EAAC,OAAO,QAAO;AAAA,MACvB,CAAC;AAAA,IACF,SAAS,GAAP;AACD,YAAM,IAAI,KAAK,aAAa,oCAAoC,EAAE,SAAS;AAAA,IAC5E;AACA,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,qBAAM,OAAO,KAAK,UAAyB,OAAK,CAAC,EAAE,SAAS;AAC5D,WAAO,MAAM,WAAW,CAAC;AAAA,EAC1B;AAAA,EACA,iBAAiB,MAAqB;AACrC,QAAI,MAAM;AACV,WAAO;AACP,WAAO;AACP,WAAO,YAAY,KAAK,kBAAkB,KAAK;AAC/C,WAAO;AACP,WAAO,mEAAmE,KAAK,SAAS,KAAK;AAC7F,WAAO;AACP,WAAO;AACP,UAAM,UAAU,IAAI,KAAK,KAAK,UAAU;AACxC,WAAO,GAAG,KAAK,2BAA2B,KAAK,gCAAgC,KAAK,YAAY,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;AACrH,WAAO,2DAA2D,KAAK,iBAAiB,KAAK;AAC7F,WAAO;AACP,WAAO,GAAG,KAAK,YAAY,MAAM,GAAG,GAAG,EAAE,QAAQ,OAAO,GAAG,IAAI,KAAK,YAAY,SAAS,MAAM,QAAQ;AACvG,WAAO;AACP,WAAO;AAAA,EACR;AACD;AAEO,MAAM,mBAAmB,MAAM,eAAe;AAAA,EAIpD,YAAY,MAAY,KAAa,WAAsB;AAC1D,UAAM,IAAI;AAFX,mBAAyB;AAGxB,SAAK,MAAM;AACX,SAAK,OAAO;AACZ,SAAK,SAAS,+CAA+C;AAAA,EAC9D;AAAA,EACA,OAAO,MAAY;AAClB,UAAM,QAAQ,KAAK,MAAM;AACzB,eAAW,QAAQ,OAAO;AACzB,WAAK,OAAO,KAAK,KAAK,QAAQ,SAAS,MAAM;AAAA,IAC9C;AAAA,EACD;AAAA,EACA,QAAQ;AACP,QAAI,KAAK;AAAS,YAAM,IAAI,KAAK,aAAa,wBAAwB;AACtE,SAAK,SAAS,KAAK,gBAAgB,CAAC;AACpC,SAAK,MAAM,KAAK,gBAAgB,CAAC;AACjC,SAAK,UAAU,KAAK,IAAI;AACxB,SAAK,IAAI,6BAA6B;AAAA,EACvC;AAAA,EACA,QAAQ;AACP,UAAM,QAAQ;AAAA,MACb;AAAA,MACA,uCAAuC,KAAK,QAAQ,KAAK,KAAK;AAAA,IAC/D;AACA,QAAI,KAAK,SAAS;AACjB,YAAM,OAAO,KAAK,IAAI,IAAI,KAAK;AAC/B,YAAM,KAAK,yBAAyB,KAAK,iBAAiB,IAAI,MAAM,OAAO,cAAe;AAAA,IAC3F;AACA,WAAO;AAAA,EACR;AAAA,EACA,kBAAkB;AACjB,QAAI,eAAe,OAAO,KAAK,KAAK;AACpC,oBAAgB;AAChB,oBAAgB,4CAA4C,KAAK,KAAK,eAAe,KAAK,KAAK;AAC/F,oBAAgB,iBAAiB,KAAK,KAAK,4BAA4B,KAAK,KAAK;AACjF,oBAAgB,oBAAoB,KAAK,YAAY,IAAI,KAAK,KAAK,KAAK,IAAI,CAAC;AAC7E,oBAAgB,0CAA0C,KAAK,KAAK,YAAY,QAAQ,QAAQ,QAAQ;AACxG,oBAAgB;AAChB,WAAO;AAAA,EACR;AAAA,EACA,kBAAkB;AACjB,QAAI,MAAM;AACV,WAAO,kBAAkB,KAAK,KAAK;AACnC,UAAM,KAAK,QAAQ,MAAM,KAAK,GAAG;AACjC,UAAM,MAAM,+BAA+B;AAC3C,WAAO,iBAAiB;AACxB,WAAO,SAAS,OAAO,CAAC;AACxB,WAAO;AACP,WAAO;AAAA,EACR;AAAA,EACA,SAAS,MAAc;AACtB,SAAK,IAAI,yBAAyB,eAAe;AAAA,EAClD;AAAA,EACA,MAAM,MAAc;AACnB,SAAK,IAAI,cAAc,MAAM;AAAA,EAC9B;AAAA,EACA,IAAI,KAAa;AAChB,SAAK,KAAK,IAAI,GAAG,EAAE,OAAO;AAAA,EAC3B;AAAA,EACA,UAAU;AACT,SAAK,SAAS,mCAAmC;AACjD,QAAI,SAAS;AACb,cAAU,KAAK,gBAAgB;AAC/B,cAAU;AACV,SAAK,MAAM,MAAM;AACjB,SAAK,OAAO;AAAA,EACb;AACD;AAEO,MAAM,qBAAqB,MAAM,eAAe;AAAA,EAGtD,YAAY,MAAY,MAAqB;AAC5C,UAAM,IAAI;AAHX,mBAAU;AAIT,SAAK,OAAO;AAAA,EACb;AAAA,EACA,aAAa,kBAAkB,MAAY,SAAiB;AAC3D,QAAI,CAAC,GAAG,MAAM,MAAM,OAAO,CAAC,EAAE;AAAA,MAC7B,OAAK,EAAE,OAAO,WAAW,SAAS,KAAK,EAAE,QAAQ,WAAW,MAAM;AAAA,IACnE,GAAG;AACF,YAAM,IAAI,KAAK,aAAa,iDAAiD;AAAA,IAC9E;AACA,UAAM,OAAO,MAAM,OAAO,WAAW,OAAO;AAC5C,QAAI,CAAC;AAAM,YAAM,IAAI,KAAK,aAAa,mBAAmB;AAC1D,UAAM,YAAY,MAAM;AAAA,MACvB,uBAAuB,KAAK,eAAe;AAAA,MAC3C,iBAAM,sBAAsB,KAAK;AAAA,MACjC,EAAC,WAAW,SAAQ;AAAA,IACrB;AACA,SAAK,aAAa;AAClB,cAAU,UAAU,IAAI;AACxB,UAAM,SAAS,IAAI,aAAa,WAAW,IAAI;AAC/C,cAAU,OAAO;AACjB,WAAO;AAAA,EACR;AAAA,EACA,MAAM,KAAa;AAClB,SAAK,IAAI,cAAc,KAAK;AAAA,EAC7B;AAAA,EACA,SAAS,KAAa;AACrB,SAAK,IAAI,iBAAiB,KAAK;AAAA,EAChC;AAAA,EACA,IAAI,MAAc;AACjB,WAAO,KAAK,KAAK,IAAI,IAAI,EAAE,OAAO;AAAA,EACnC;AAAA,EACA,OAAO,MAAY;AAClB,QAAI,CAAC,KAAK;AAAO;AACjB,SAAK,SAAS,KAAK,mBAAmB,CAAC;AAAA,EACxC;AAAA,EACA,QAAQ,MAAY;AACnB,QAAI,CAAC,KAAK;AAAO;AACjB,SAAK,SAAS,KAAK,mBAAmB,CAAC;AAAA,EACxC;AAAA,EACA,QAAQ;AACP,QAAI,KAAK,SAAS;AACjB,YAAM,IAAI,KAAK,aAAa,wBAAwB;AAAA,IACrD;AACA,SAAK,SAAS,KAAK,mBAAmB,CAAC;AACvC,SAAK,MAAM,KAAK,iBAAiB,CAAC;AAClC,SAAK,IAAI,8CAA8C;AACvD,SAAK,UAAU;AAAA,EAChB;AAAA,EACA,MAAM;AACL,SAAK,MAAM,EAAE;AACb,SAAK,SAAS,8CAA8C;AAC5D,SAAK,KAAK,QAAQ,IAAI,mBAAmB,KAAK,KAAK,SAAS;AAC5D,SAAK,IAAI,sBAAsB;AAC/B,SAAK,KAAK,QAAQ;AAAA,EACnB;AAAA,EACA,qBAAqB;AACpB,QAAI,MAAM;AACV,WAAO,6BAA6B,KAAK,KAAK,uBAAuB,KAAK,KAAK;AAC/E,WAAO,GAAG,KAAK,MAAM,OAAO,KAAK,KAAK,KAAK,KAAK,EAAE,QAAQ,OAAO;AACjE,WAAO,oBAAoB,KAAK,KAAK;AACrC,WAAO;AAAA,EACR;AAAA,EACA,mBAAmB;AAClB,QAAI,MAAM;AACV,WAAO,gBAAgB,KAAK,KAAK;AACjC,WAAO;AAAA,EACR;AACD;AAEO,MAAM,UAAU,IAAI,iBAAiB,WAAW;AAEhD,SAAS,UAAU;AACzB,MAAI,QAAQ;AAAU,kBAAc,QAAQ,QAAQ;AACrD;AAEO,MAAM,WAA8B;AAAA,EAC1C,MAAM,YAAY,QAAQ,MAAM,MAAM;AACrC,WAAO,KAAK,YAAY,SAAmB;AAC3C,QAAI,OAAO,KAAK,QAAQ,KAAK,QAAQ,EAAE,SAAS;AAAG,aAAO,KAAK,WAAW,8BAA8B;AACxG,aAAS,KAAK,MAAM;AACpB,SAAK,aAAa;AAClB,UAAM,OAAO,MAAM,QAAQ,YAAY,MAAM;AAC7C,WAAO,KAAK,UAAU,SAAS,MAAM;AAAA,EACtC;AAAA,EACA,iBAAiB,CAAC,yEAAyE;AAAA,EAE3F,IAAI;AAAA,EACJ,SAAS;AAAA,IACR,MAAM,WAAW,QAAQ,MAAM,MAAM;AACpC,aAAO,KAAK,YAAY,SAAmB;AAC3C,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,YAAM,CAAC,IAAI,IAAI,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC;AACtD,UAAI,CAAC;AAAI,eAAO,KAAK,WAAW,uBAAuB;AACvD,YAAM,QAAQ,eAAe,IAAI,IAAI;AACrC,WAAK,OAAO,cAAc,MAAM,GAAG,MAAM,OAAO,aAAa,SAAS,IAAI;AAC1E,aAAO,KAAK;AAAA,QACX,GAAG,KAAK,8BAA8B,MAAM,OAAO,iBAAiB,WAAW;AAAA,MAChF;AAAA,IACD;AAAA,IACA,gBAAgB,CAAC,yEAAyE;AAAA,IAE1F,cAAc,QAAQ,MAAM,MAAM;AACjC,aAAO,KAAK,YAAY,SAAmB;AAC3C,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,YAAM,KAAK,QAAQ,cAAc,MAAM;AACvC,UAAI,CAAC;AAAI,eAAO,KAAK,WAAW,2BAA2B,mBAAmB;AAC9E,aAAO,QAAQ,KAAK,SAAS,EAAE;AAC/B,cAAQ,KAAK;AACb,WAAK,iBAAiB,GAAG,KAAK,wCAAwC,SAAS;AAC/E,aAAO,KAAK,OAAO,iBAAiB,MAAM,EAAE;AAAA,IAC7C;AAAA,IACA,mBAAmB,CAAC,yFAAyF;AAAA,IAE7G,MAAM,QAAQ,QAAQ,MAAM,MAAM;AACjC,aAAO,KAAK,YAAY,SAAmB;AAC3C,YAAM,UAAU,QAAQ,cAAc,MAAM;AAC5C,UAAI,CAAC;AAAS,eAAO,KAAK,WAAW,+BAA+B,eAAe;AACnF,YAAM,OAAO,MAAM,QAAQ,uBAAuB,OAAO;AACzD,WAAK,aAAa;AAClB,aAAO,KAAK,UAAU,SAAS,MAAM;AAAA,IACtC;AAAA,IACA,aAAa;AAAA,MACZ;AAAA,IACD;AAAA,IACA,MAAM,MAAM,QAAQ,MAAM,MAAM;AAC/B,aAAO,KAAK,YAAY,SAAmB;AAC3C,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,YAAM,SAAS,MAAM,QAAQ,qBAAqB,QAAQ,IAAI;AAC9D,WAAK,aAAa;AAClB,WAAK,aAAa,MAAM;AAAA,IACzB;AAAA,IAEA,SAAS,QAAQ,MAAM,MAAM;AAC5B,eAAS,KAAK,MAAM;AACpB,aAAO,KAAK,MAAM,mBAAmB,SAAS,IAAI,WAAW,IAAI;AAAA,IAClE;AAAA,IACA,KAAK,QAAQ,MAAM,MAAM;AACxB,aAAO,KAAK,MAAM,eAAe;AAAA,IAClC;AAAA,IAEA,aAAa;AACZ,aAAO,KAAK,MAAM,6BAA6B;AAAA,IAChD;AAAA,IAEA,OAAO,QAAQ,MAAM,MAAM;AAC1B,aAAO,KAAK,YAAY,SAAmB;AAC3C,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,YAAM,CAAC,SAAS,IAAI,IAAI,OAAO,MAAM,GAAG;AACxC,YAAM,KAAK,QAAQ,cAAc,OAAO;AACxC,UAAI,CAAC;AAAI,eAAO,KAAK,WAAW,WAAW,iCAAiC;AAC5E,cAAQ,KAAK,SAAS,EAAE,EAAE,WAAW;AACrC,WAAK,OAAO,iBAAiB,MAAM,IAAI;AACvC,WAAK,iBAAiB,GAAG,KAAK,wBAAwB,oBAAoB,OAAO;AACjF,cAAQ,KAAK;AAAA,IACd;AAAA,IACA,UAAU;AAAA,IACV,OAAO,QAAQ,MAAM,MAAM;AAC1B,aAAO,KAAK,YAAY,SAAmB;AAC3C,WAAK,SAAS,WAAW,MAAM,IAAI;AACnC,UAAI,CAAC,QAAQ;AACZ,YAAI,CAAC,QAAQ;AAAU,iBAAO,KAAK,WAAW,0DAA0D;AACxG,eAAO,KAAK,UAAU,gCAAgC,KAAK,iBAAiB,QAAQ,eAAe,KAAK,GAAI,IAAI;AAAA,MACjH;AACA,UAAI,KAAK,QAAQ,MAAM,GAAG;AACzB,YAAI,CAAC,QAAQ;AAAU,iBAAO,KAAK,WAAW,uCAAuC;AACrF,sBAAc,QAAQ,QAAQ;AAC9B,eAAO,QAAQ,KAAK;AACpB,gBAAQ,KAAK;AACb,aAAK,iBAAiB,GAAG,KAAK,sCAAsC;AACpE,eAAO,KAAK,OAAO,oBAAoB,MAAM,KAAK;AAAA,MACnD;AACA,UAAI,OAAO,KAAK,WAAW,EAAE,SAAS;AAAG,eAAO,KAAK,WAAW,8BAA8B;AAC9F,UAAI,MAAM,SAAS,MAAM,CAAC;AAAG,eAAO,KAAK,WAAW,iDAAiD;AACrG,cAAQ,YAAY,MAAM;AAC1B,cAAQ,KAAK;AACb,WAAK,iBAAiB,GAAG,KAAK,sCAAsC,gBAAgB;AACpF,aAAO,KAAK,OAAO,mBAAmB,MAAM,GAAG,gBAAgB;AAAA,IAChE;AAAA,IACA,YAAY,QAAQ,MAAM,MAAM;AAC/B,aAAO,KAAK,YAAY,SAAmB;AAC3C,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,YAAM,aAAa,KAAK,MAAM;AAC9B,UAAI,CAAC;AAAY,eAAO,KAAK,MAAM,eAAe;AAClD,UAAI,QAAQ,KAAK,WAAW,IAAI,IAAI,EAAE,SAAS,UAAU,GAAG;AAC3D,eAAO,KAAK,WAAW,wEAAwE;AAAA,MAChG;AACA,cAAQ,KAAK,WAAW,KAAK,MAAM;AACnC,WAAK,OAAO,uBAAuB,MAAM,MAAM;AAC/C,WAAK,iBAAiB,GAAG,KAAK,wBAAwB,iCAAiC;AACvF,cAAQ,KAAK;AAAA,IACd;AAAA,IACA,eAAe,QAAQ,MAAM,MAAM;AAClC,aAAO,KAAK,YAAY,SAAmB;AAC3C,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,YAAM,aAAa,KAAK,MAAM;AAC9B,UAAI,CAAC;AAAY,eAAO,KAAK,MAAM,eAAe;AAClD,YAAM,QAAQ,QAAQ,KAAK,WAAW,QAAQ,MAAM;AACpD,UAAI,QAAQ,GAAG;AACd,eAAO,KAAK,WAAW,GAAG,iCAAiC;AAAA,MAC5D;AACA,iBAAW,MAAM,QAAQ,KAAK,UAAU;AACvC,cAAM,UAAU,QAAQ,KAAK,SAAS,EAAE;AACxC,YAAI,QAAQ,aAAa;AAAQ,iBAAO,QAAQ,KAAK,SAAS,EAAE,EAAE;AAAA,MACnE;AACA,cAAQ,KAAK;AACb,WAAK,iBAAiB,GAAG,KAAK,8BAA8B,iCAAiC;AAC7F,WAAK,OAAO,0BAA0B,MAAM,MAAM;AAAA,IACnD;AAAA,IACA,YAAY,QAAQ,MAAM,MAAM;AAC/B,aAAO,KAAK,YAAY,SAAmB;AAC3C,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,eAAS,OAAO,KAAK;AACrB,YAAM,CAAC,UAAU,EAAE,IAAI,iBAAM,WAAW,QAAQ,GAAG,EAAE,IAAI,UAAQ,KAAK,KAAK,CAAC;AAC5E,UAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI;AAChC,eAAO,KAAK,MAAM,eAAe;AAAA,MAClC;AACA,UAAI,CAAC,QAAQ,KAAK,WAAW,SAAS,QAAQ,GAAG;AAChD,eAAO,KAAK,WAAW,mBAAmB;AAAA,MAC3C;AACA,YAAM,OAAO,QAAQ,cAAc,EAAE;AACrC,UAAI,CAAC;AAAM,eAAO,KAAK,WAAW,kBAAkB;AACpD,YAAM,UAAU,QAAQ,KAAK,SAAS,IAAI;AAC1C,cAAQ,KAAK,SAAS,IAAI,EAAE,WAAW;AACvC,cAAQ,KAAK;AACb,WAAK,OAAO,uBAAuB,MAAM,GAAG,mBAAmB,UAAU;AACzE,WAAK,iBAAiB,GAAG,KAAK,wBAAwB,QAAQ,uBAAuB,YAAY;AAAA,IAClG;AAAA,IACA,aAAa,QAAQ,MAAM,MAAM;AAChC,aAAO,KAAK,YAAY,SAAmB;AAC3C,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,eAAS,OAAO,KAAK;AACrB,UAAI,CAAC,QAAQ;AACZ,eAAO,KAAK,MAAM,eAAe;AAAA,MAClC;AACA,YAAM,OAAO,QAAQ,cAAc,MAAM;AACzC,UAAI,CAAC;AAAM,eAAO,KAAK,WAAW,kBAAkB;AACpD,YAAM,UAAU,QAAQ,KAAK,SAAS,IAAI;AAC1C,YAAM,WAAW,QAAQ;AACzB,UAAI,CAAC;AAAU,eAAO,KAAK,WAAW,wCAAwC;AAC9E,aAAO,QAAQ;AACf,cAAQ,KAAK;AACb,WAAK,OAAO,wBAAwB,MAAM,MAAM;AAChD,WAAK,iBAAiB,GAAG,KAAK,4BAA4B,QAAQ,0BAA0B,WAAW;AAAA,IACxG;AAAA,IACA,MAAM,WAAW,QAAQ,MAAM,MAAM;AACpC,aAAO,KAAK,YAAY;AACxB,UAAI,CAAC,iBAAiB,SAAS,KAAK,MAAM,GAAG;AAC5C,eAAO,KAAK,WAAW,0DAA0D;AAAA,MAClF;AACA,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,YAAM,CAAC,KAAK,KAAK,IAAI,iBAAM,WAAW,QAAQ,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC;AACpE,UAAI,CAAC,OAAO,CAAC;AAAO,eAAO,KAAK,WAAW,oEAAoE;AAC/G,YAAM,WAAW,MAAM,QAAQ,iBAAiB,KAAK,MAAM,KAAK;AAChE,WAAK,OAAO,sBAAsB,MAAM,GAAG,QAAQ,QAAQ;AAC3D,WAAK;AAAA,QACJ,UAAU,SAAS,uDAC6B,SAAS;AAAA,MAC1D;AACA,WAAK,KAAK,wCAAwC,QAAQ;AAC1D,WAAK,OAAO;AACZ,WAAK,SAAS,QAAQ;AAAA,IACvB;AAAA,IACA,SAAS,QAAQ,MAAM,MAAM;AAC5B,aAAO,KAAK,YAAY;AACxB,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,WAAK,YAAY,UAAU;AAC3B,WAAK,OAAQ,OAAO,EAAC,QAAQ,kBAAkB,UAAU,KAAK,GAAE,CAAC;AACjE,WAAK,OAAQ,IAAI,gBAAgB,KAAK,SAAS,EAAE,OAAO;AACxD,WAAK,QAAQ;AAAA,IACd;AAAA,IACA,YAAY;AAAA,IACZ,WAAW,QAAQ,MAAM,MAAM;AAC9B,aAAO,KAAK,YAAY;AACxB,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,YAAM,OAAO,KAAK,YAAY,UAAU;AACxC,WAAK,MAAM;AAAA,IACZ;AAAA,IACA,eAAe;AACd,UAAI,MAAM;AACV,iBAAW,WAAW,MAAM,MAAM,OAAO,GAAG;AAC3C,YAAI,CAAC,QAAQ,QAAQ,UAAU;AAAG;AAClC,eAAO,gDAAgD,QAAQ,WAAW,QAAQ;AAAA,MACnF;AACA,WAAK,aAAa;AAClB,WAAK,aAAa,GAAG;AAAA,IACtB;AAAA,EACD;AAAA,EACA,aAAa;AAAA,IACZ;AAAA,IACA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAAA,EACA,QAAQ;AAAA,IACP,MAAM,QAAQ,QAAQ,MAAM,MAAM;AACjC,aAAO,KAAK,YAAY,SAAmB;AAC3C,UAAI,CAAC,OAAO;AAAW,eAAO,KAAK,WAAW,0BAA0B;AACxE,YAAM,OAAO,MAAM,OAAO,WAAW,MAAM;AAC3C,UAAI,CAAC;AAAM,eAAO,KAAK,WAAW,mBAAmB;AACrD,YAAM,OAAO,OAAO,iBAAiB,IAAI;AACzC,WAAK,aAAa;AAClB,aAAO,KAAK,aAAa,IAAI;AAAA,IAC9B;AAAA,IACA,MAAM,MAAM,QAAQ,MAAM,MAAM;AAC/B,aAAO,KAAK,YAAY;AACxB,UAAI,CAAC,iBAAiB,SAAS,KAAK,MAAM,GAAG;AAC5C,cAAM,IAAI,KAAK,aAAa,2CAA2C;AAAA,MACxE;AACA,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,UAAI,CAAC,KAAK,MAAM,GAAG;AAClB,eAAO,KAAK,WAAW,iBAAiB;AAAA,MACzC;AACA,YAAM,WAAW,MAAM,aAAa,kBAAkB,MAAM,MAAM;AAClE,WAAK,SAAS,QAAQ;AACtB,WAAK;AAAA,QACJ,aAAa,SAAS,uDAC0B,SAAS;AAAA,MAC1D,EAAE,OAAO;AAAA,IACV;AAAA,IACA,MAAM,QAAQ,MAAM;AACnB,aAAO,KAAK,YAAY;AACxB,YAAM,SAAS,KAAK,YAAY,YAAY;AAC5C,aAAO,MAAM;AAAA,IACd;AAAA,IACA,KAAK,QAAQ,MAAM,MAAM;AACxB,aAAO,KAAK,YAAY;AACxB,YAAM,SAAS,KAAK,YAAY,YAAY;AAC5C,WAAK,SAAS,QAAQ,MAAM,IAAI;AAChC,aAAO,IAAI;AAAA,IACZ;AAAA,EACD;AACD;AAEO,MAAM,QAAwB;AAAA,EACpC,MAAM,SAAS,MAAM,MAAM;AAC1B,UAAM,CAAC,IAAI,IAAI;AACf,QAAI,CAAC,OAAO;AAAY,aAAO;AAC/B,UAAM,SAAgC;AAAA,MACrC,KAAK;AAAA,MACL,YAAY;AAAA,IACb;AACA,UAAM,QAAQ,OAAO,IAAI,KAAK;AAC9B,SAAK,QAAQ,cAAc;AAC3B,QAAI,SAAS,2DAA2D;AACxE,cAAU,kEAAkE;AAC5E,cAAU;AACV,YAAQ,KAAK,IAAI,GAAG;AAAA,MACpB,KAAK;AACJ,YAAI,CAAC,QAAQ,KAAK,WAAW,QAAQ;AACpC,iBAAO,KAAK,WAAW,oEAAoE;AAAA,QAC5F;AACA,cAAM,SAAkC,CAAC;AACzC,cAAM,WAAW,QAAQ,KAAK;AAC9B,mBAAW,CAAC,IAAI,OAAO,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACrD,gBAAM,WAAW,QAAQ,YAAY;AACrC,cAAI,CAAC,OAAO,QAAQ,GAAG;AACtB,mBAAO,QAAQ,IAAI,CAAC;AAAA,UACrB;AACA,iBAAO,QAAQ,EAAE,KAAK,EAAE;AAAA,QACzB;AACA,mBAAW,OAAO,QAAQ;AACzB,oBAAU,OAAO;AACjB,qBAAW,MAAM,OAAO,GAAG,GAAG;AAC7B,kBAAM,UAAU,SAAS,EAAE;AAC3B,sBAAU,qBAAqB,QAAQ;AACvC,sBAAU,MAAM,QAAQ,uBAAuB,EAAE;AACjD,sBAAU;AAAA,UACX;AAAA,QACD;AACA;AAAA,MACD;AACC,mBAAW,MAAM,iBAAM,QAAQ,OAAO,KAAK,QAAQ,KAAK,QAAQ,CAAC,GAAG;AACnE,gBAAM,EAAC,MAAM,SAAQ,IAAI,MAAM,QAAQ,IAAI,EAAE;AAC7C,cAAI,KAAK,IAAI,MAAM,SAAS,CAAC;AAAU;AACvC,oBAAU,qBAAqB;AAC/B,oBAAU,2BAA2B;AACrC,cAAI;AAAU,sBAAU,qBAAqB;AAC7C,oBAAU;AACV,oBAAU,MAAM,QAAQ,uBAAuB,EAAE;AACjD,oBAAU;AAAA,QACX;AACA;AAAA,IACD;AACA,cAAU;AACV,WAAO;AAAA,EACR;AACD;", "names": ["id", "buf"] }