"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var youtube_exports = {};
__export(youtube_exports, {
  GroupWatch: () => GroupWatch,
  Twitch: () => Twitch,
  YouTube: () => YouTube,
  YoutubeInterface: () => YoutubeInterface,
  commands: () => commands,
  destroy: () => destroy,
  pages: () => pages,
  searchDataCache: () => searchDataCache,
  videoDataCache: () => videoDataCache
});
module.exports = __toCommonJS(youtube_exports);
var import_lib = require("../../lib");
const ROOT = "https://www.googleapis.com/youtube/v3/";
const STORAGE_PATH = "config/chat-plugins/youtube.json";
const GROUPWATCH_ROOMS = ["youtube", "pokemongames", "videogames", "smashbros", "pokemongo", "hindi"];
const videoDataCache = Chat.oldPlugins.youtube?.videoDataCache || /* @__PURE__ */ new Map();
const searchDataCache = Chat.oldPlugins.youtube?.searchDataCache || /* @__PURE__ */ new Map();
function loadData() {
  const raw = JSON.parse((0, import_lib.FS)(STORAGE_PATH).readIfExistsSync() || "{}");
  if (!(raw.channels && raw.categories)) {
    const data = {};
    data.channels = raw;
    data.categories = [];
    (0, import_lib.FS)(STORAGE_PATH).writeUpdate(() => JSON.stringify(data));
    return data;
  }
  return raw;
}
const channelData = loadData();
class YoutubeInterface {
  constructor(data) {
    this.linkRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)(\/|$)/i;
    this.data = data ? data : { categories: [], channels: {} };
    this.interval = null;
    this.intervalTime = 0;
    if (data?.intervalTime) {
      this.runInterval(`${data.intervalTime}`);
    }
  }
  async getChannelData(link, username) {
    if (!Config.youtubeKey) {
      throw new Chat.ErrorMessage(`This server does not support YouTube commands. If you're the owner, you can enable them by setting up Config.youtubekey.`);
    }
    const id = this.getId(link);
    const raw = await (0, import_lib.Net)(`${ROOT}channels`).get({
      query: { part: "snippet,statistics", id, key: Config.youtubeKey }
    });
    const res = JSON.parse(raw);
    if (!res?.items || res.items.length < 1) {
      throw new Chat.ErrorMessage(`Channel not found.`);
    }
    const data = res.items[0];
    const cache = {
      name: data.snippet.title,
      description: data.snippet.description,
      url: data.snippet.customUrl,
      icon: data.snippet.thumbnails.medium.url,
      videos: Number(data.statistics.videoCount),
      subs: Number(data.statistics.subscriberCount),
      views: Number(data.statistics.viewCount),
      username
    };
    this.data.channels[id] = { ...cache };
    this.save();
    return cache;
  }
  async generateChannelDisplay(link) {
    const id = this.getId(link);
    const { name, description, icon, videos, subs, views, username } = await this.get(id);
    let buf = `<div class="infobox"><table style="margin:0px;"><tr>`;
    buf += `<td style="margin:5px;padding:5px;min-width:175px;max-width:160px;text-align:center;border-bottom:0px;">`;
    buf += `<div style="padding:5px;background:white;border:1px solid black;margin:auto;max-width:100px;max-height:100px;">`;
    buf += `<a href="${ROOT}channel/${id}"><img src="${icon}" width=100px height=100px/></a>`;
    buf += `</div><p style="margin:5px 0px 4px 0px;word-wrap:break-word;">`;
    buf += `<a style="font-weight:bold;color:#c70000;font-size:12pt;" href="https://www.youtube.com/channel/${id}">${name}</a>`;
    buf += `</p></td><td style="padding: 0px 25px;font-size:10pt;background:rgb(220,20,60);width:100%;border-bottom:0px;vertical-align:top;">`;
    buf += `<p style="padding: 5px;border-radius:8px;color:white;font-weight:bold;text-align:center;">`;
    buf += `${videos} videos | ${subs} subscribers | ${views} video views</p>`;
    buf += `<p style="margin-left: 5px; font-size:9pt;color:white;">`;
    buf += `${description.slice(0, 400).replace(/\n/g, " ")}${description.length > 400 ? "(...)" : ""}</p>`;
    if (username) {
      buf += `<p style="text-align:left;font-style:italic;color:white;">PS username: ${username}</p></td></tr></table></div>`;
    } else {
      buf += "</td></tr></table></div>";
    }
    return buf;
  }
  randChannel(cat) {
    let channels = Object.keys(this.data.channels);
    if (channels.length < 1) {
      throw new Chat.ErrorMessage(`There are no channels in the database.`);
    }
    if (cat) {
      cat = toID(cat);
      const categoryIDs = this.data.categories.map(toID);
      if (!categoryIDs.includes(cat)) {
        throw new Chat.ErrorMessage(`Invalid category.`);
      }
      channels = channels.filter((id2) => {
        const channel = this.data.channels[id2];
        return channel.category && toID(channel.category) === cat;
      });
    }
    const id = import_lib.Utils.shuffle(channels)[0];
    return this.generateChannelDisplay(id);
  }
  get(id, username) {
    if (!(id in this.data.channels))
      return this.getChannelData(id, username);
    return Promise.resolve({ ...this.data.channels[id] });
  }
  async getVideoData(id) {
    const cached = videoDataCache.get(id);
    if (cached)
      return cached;
    let raw;
    try {
      raw = await (0, import_lib.Net)(`${ROOT}videos`).get({
        query: { part: "snippet,statistics", id, key: Config.youtubeKey }
      });
    } catch (e) {
      throw new Chat.ErrorMessage(`Failed to retrieve video data: ${e.message}.`);
    }
    const res = JSON.parse(raw);
    if (!res?.items || res.items.length < 1)
      return null;
    const video = res.items[0];
    const data = {
      title: video.snippet.title,
      id,
      date: new Date(video.snippet.publishedAt).toString(),
      description: video.snippet.description,
      channelTitle: video.snippet.channelTitle,
      channelUrl: video.snippet.channelId,
      views: video.statistics.viewCount,
      thumbnail: video.snippet.thumbnails.default.url,
      likes: video.statistics.likeCount,
      dislikes: video.statistics.dislikeCount
    };
    videoDataCache.set(id, data);
    return data;
  }
  channelSearch(search) {
    let channel;
    if (this.data.channels[search]) {
      channel = search;
    } else {
      for (const id of Object.keys(this.data.channels)) {
        const name = toID(this.data.channels[id].name);
        const username = this.data.channels[id].username;
        if (name === toID(search) || username && toID(username) === toID(search)) {
          channel = id;
          break;
        }
      }
    }
    return channel;
  }
  getId(link) {
    let id = "";
    if (!link)
      throw new Chat.ErrorMessage("You must provide a YouTube link.");
    if (this.data.channels[link])
      return link;
    if (!link.includes("channel/")) {
      if (link.includes("youtube")) {
        id = link.split("v=")[1] || "";
      } else if (link.includes("youtu.be")) {
        id = link.split("/")[3] || "";
      } else {
        throw new Chat.ErrorMessage("Invalid YouTube channel link.");
      }
    } else {
      id = link.split("channel/")[1] || "";
    }
    if (id.includes("&"))
      id = id.split("&")[0];
    if (id.includes("?"))
      id = id.split("?")[0];
    return id;
  }
  async generateVideoDisplay(link, fullInfo = false, broadcasting = false) {
    if (!Config.youtubeKey) {
      throw new Chat.ErrorMessage(`This server does not support YouTube commands. If you're the owner, you can enable them by setting up Config.youtubekey.`);
    }
    const id = this.getId(link);
    const info = await this.getVideoData(id);
    if (!info)
      throw new Chat.ErrorMessage(`Video not found.`);
    if (!fullInfo) {
      let buf2 = `<b>${info.title}</b> `;
      buf2 += `(<a class="subtle" href="https://youtube.com/channel/${info.channelUrl}">${info.channelTitle}</a>)<br />`;
      if (broadcasting) {
        buf2 += `<button class="button" name="send" value="/show https://www.youtube.com/watch?v=${id}">`;
        buf2 += `Click to view "${info.title}"</button>`;
      } else {
        buf2 += `<youtube src="https://www.youtube.com/embed/${id}" />`;
      }
      return buf2;
    }
    let buf = `<table style="margin:0px;"><tr>`;
    buf += `<td style="margin:5px;padding:5px;min-width:175px;max-width:160px;text-align:center;border-bottom:0px;">`;
    buf += `<div style="padding:5px;background:#b0b0b0;border:1px solid black;margin:auto;max-width:100px;max-height:100px;">`;
    buf += `<a href="${ROOT}channel/${id}"><img src="${info.thumbnail}" width=100px height=100px/></a>`;
    buf += `</div><p style="margin:5px 0px 4px 0px;word-wrap:break-word;">`;
    buf += `<a style="font-weight:bold;color:#c70000;font-size:12pt;" href="https://www.youtube.com/watch?v=${id}">${info.title}</a>`;
    buf += `</p></td><td style="padding: 0px 25px;font-size:10pt;max-width:100px;background:`;
    buf += `#white;width:100%;border-bottom:0px;vertical-align:top;">`;
    buf += `<p style="background: #e22828; padding: 5px;border-radius:8px;color:white;font-weight:bold;text-align:center;">`;
    buf += `${info.likes} likes | ${info.dislikes} dislikes | ${info.views} video views<br><br>`;
    buf += `<small>Published on ${info.date} | ID: ${id}</small><br>Uploaded by: ${info.channelTitle}</p>`;
    buf += `<br><details><summary>Video Description</p></summary>`;
    buf += `<p style="background: #e22828;max-width:500px;padding: 5px;border-radius:8px;color:white;font-weight:bold;text-align:center;">`;
    buf += `<i>${info.description.slice(0, 400).replace(/\n/g, " ")}${info.description.length > 400 ? "(...)" : ""}</p><i></details></td>`;
    return buf;
  }
  save() {
    return (0, import_lib.FS)(STORAGE_PATH).writeUpdate(() => JSON.stringify(this.data));
  }
  async searchVideo(name, limit) {
    const cached = searchDataCache.get(toID(name));
    if (cached) {
      return cached.slice(0, limit);
    }
    const raw = await (0, import_lib.Net)(`${ROOT}search`).get({
      query: {
        part: "snippet",
        q: name,
        key: Config.youtubeKey,
        order: "relevance"
      }
    });
    const result = JSON.parse(raw);
    const resultArray = result.items?.map((item) => item?.id?.videoId).filter(Boolean);
    searchDataCache.set(toID(name), resultArray);
    return resultArray.slice(0, limit);
  }
  async searchChannel(name, limit = 10) {
    const raw = await (0, import_lib.Net)(`${ROOT}search`).get({
      query: {
        part: "snippet",
        q: name,
        type: "channel",
        key: Config.youtubeKey,
        order: "relevance",
        maxResults: limit
      }
    });
    const result = JSON.parse(raw);
    return result?.items.map((item) => item?.snippet?.channelId);
  }
  runInterval(time) {
    let interval = Number(time);
    if (interval < 10)
      throw new Chat.ErrorMessage(`${interval} is too low - set it above 10 minutes.`);
    this.intervalTime = interval;
    this.data.intervalTime = interval;
    interval = interval * 60 * 1e3;
    if (this.interval)
      clearInterval(this.interval);
    this.interval = setInterval(() => {
      void (async () => {
        const room = Rooms.get("youtube");
        if (!room)
          return;
        const res = await YouTube.randChannel();
        room.add(`|html|${res}`).update();
      })();
    }, interval);
    return this.interval;
  }
  async createGroupWatch(url, baseRoom, title) {
    const videoInfo = await this.getGroupwatchData(url);
    const num = baseRoom.nextGameNumber();
    baseRoom.saveSettings();
    return new GroupWatch(baseRoom, num, url, title, videoInfo);
  }
  async getGroupwatchData(url) {
    if (!Chat.isLink(url)) {
      throw new Chat.ErrorMessage("Invalid URL: " + url);
    }
    const urlData = new URL(url);
    const host = urlData.hostname;
    let videoInfo;
    if (["youtu.be", "www.youtube.com"].includes(host)) {
      const id = this.getId(url);
      const data = await this.getVideoData(id);
      if (!data)
        throw new Chat.ErrorMessage(`Video not found.`);
      videoInfo = Object.assign(data, { groupwatchType: "youtube" });
    } else if (host === "www.twitch.tv") {
      const data = await Twitch.getChannel(urlData.pathname.slice(1));
      if (!data)
        throw new Chat.ErrorMessage(`Channel not found`);
      videoInfo = Object.assign(data, { groupwatchType: "twitch" });
    } else {
      throw new Chat.ErrorMessage(`Invalid URL: must be either a Youtube or Twitch link.`);
    }
    return videoInfo;
  }
}
const Twitch = new class {
  constructor() {
    this.linkRegex = /(https?:\/\/)?twitch.tv\/([A-Za-z0-9]+)/i;
  }
  async getChannel(channel) {
    if (!Config.twitchKey || typeof Config.twitchKey !== "object") {
      throw new Chat.ErrorMessage(`Twitch is not enabled.`);
    }
    channel = toID(channel);
    let res;
    try {
      res = await (0, import_lib.Net)(`https://api.twitch.tv/helix/search/channels`).get({
        headers: {
          "Authorization": `Bearer ${Config.twitchKey.key}`,
          "Client-Id": Config.twitchKey.id,
          "Content-Type": "application/json",
          "Accept": "application/vnd.twitchtv.v5+json"
        },
        query: { query: channel }
      });
    } catch (e) {
      throw new Chat.ErrorMessage(`Error retrieving twitch channel: ${e.message}`);
    }
    const data = JSON.parse(res);
    import_lib.Utils.sortBy(data.channels, (c) => -c.followers);
    return data?.channels?.[0];
  }
  visualizeChannel(info) {
    let buf = `<div class="infobox"><table style="margin:0px;"><tr>`;
    buf += `<td style="margin:5px;padding:5px;min-width:175px;max-width:160px;text-align:center;border-bottom:0px;">`;
    buf += `<div style="padding:5px;background:white;border:1px solid black;margin:auto;max-width:100px;max-height:100px;">`;
    buf += `<a href="${info.url}"><img src="${info.logo}" width=100px height=100px/></a>`;
    buf += `</div><p style="margin:5px 0px 4px 0px;word-wrap:break-word;">`;
    buf += `<a style="font-weight:bold;color:#6441a5;font-size:12pt;" href="${info.logo}">${info.display_name}</a>`;
    buf += `</p></td><td style="padding: 0px 25px;font-size:10pt;background:rgb(100, 65, 164);width:100%;border-bottom:0px;vertical-align:top;">`;
    buf += `<p style="padding: 5px;border-radius:8px;color:white;font-size:15px;font-weight:bold;text-align:center;">`;
    const created = new Date(info.created_at);
    buf += `${info.followers} subscribers | ${info.views} stream views | created ${Chat.toTimestamp(created).split(" ")[0]}</p>`;
    buf += `<p style="color:white;font-size:10px">Last seen playing ${info.game} (Status: ${info.status})</p>`;
    buf += `<hr /><p style="margin-left: 5px; font-size:9pt;color:white;">`;
    buf += `${info.description.slice(0, 400).replace(/\n/g, " ")}${info.description.length > 400 ? "..." : ""}</p>`;
    buf += "</td></tr></table></div>";
    return buf;
  }
}();
const _GroupWatch = class extends Rooms.SimpleRoomGame {
  constructor(room, num, url, title, videoInfo) {
    super(room);
    this.gameid = "groupwatch";
    this.started = null;
    this.title = title;
    this.id = `${room.roomid}-${num}`;
    _GroupWatch.groupwatches.set(this.id, this);
    this.url = url;
    this.info = videoInfo;
  }
  onJoin(user) {
    const hints = this.hints();
    for (const hint of hints) {
      user.sendTo(this.room.roomid, `|html|${hint}`);
    }
  }
  start() {
    if (this.started)
      throw new Chat.ErrorMessage(`We've already started.`);
    this.started = Date.now();
    this.update();
  }
  hints() {
    const title = this.info.groupwatchType === "youtube" ? this.info.title : this.info.display_name;
    const hints = [
      `To watch, all you need to do is click play on the video once staff have started it!`,
      `We are currently watching: <a href="${this.url}">${title}</a>`
    ];
    if (this.started && this.info.groupwatchType === "youtube") {
      const diff = Date.now() - this.started;
      hints.push(`Video is currently at ${Chat.toDurationString(diff)} (${Math.floor(diff / 1e3)} seconds)`);
    }
    return hints;
  }
  getStatsDisplay() {
    if (this.info.groupwatchType === "twitch") {
      let buf = `<p style="background: #6441a5; padding: 5px;border-radius:8px;color:white;font-weight:bold;text-align:center;">`;
      buf += `<strong>Watching <a href="${this.info.url}" class="subtle">${this.info.display_name}</strong><br />`;
      buf += `${Chat.count(Object.keys(this.room.users).length, "users")} watching<br />`;
      buf += `<strong>Playing: ${this.info.game}`;
      return buf;
    }
    let controlsHTML = `<h3>${this.info.title}</h3>`;
    controlsHTML += `<div class="infobox"><b>Channel:</b> `;
    controlsHTML += `<a href="https://www.youtube.com/channel/${this.info.channelUrl}">${this.info.channelTitle}</a><br />`;
    controlsHTML += `<b>Likes:</b> ${this.info.likes} | <b>Dislikes:</b> ${this.info.dislikes}<br />`;
    controlsHTML += `<b>Uploaded:</b> <time>${new Date(this.info.date).toISOString()}</time><br />`;
    controlsHTML += `<details><summary>Description</summary>${this.info.description.replace(/\n/ig, "<br />")}</details>`;
    controlsHTML += `</div>`;
    return controlsHTML;
  }
  getVideoDisplay() {
    if (this.info.groupwatchType === "twitch") {
      let buf2 = `<p style="background: #6441a5; padding: 5px;border-radius:8px;color:white;font-weight:bold;text-align:center;">`;
      buf2 += `<twitch src="${this.info.url}" width="600" height="330" />`;
      return buf2;
    }
    let buf = `<p style="background: #e22828; padding: 5px;border-radius:8px;color:white;font-weight:bold;text-align:center;">`;
    buf += `<br /><br /><b>${this.info.title}</b><br />`;
    const id = YouTube.getId(this.url);
    const url = `https://youtube.com/watch?v=${id}`;
    let addendum = "";
    if (this.started) {
      const diff = Date.now() - this.started;
      addendum = `&start=${Math.floor(diff / 1e3)}`;
    }
    buf += `<youtube src="${url}${addendum}"></youtube>`;
    buf += `<br />`.repeat(4);
    buf += `</p>`;
    return buf;
  }
  display() {
    return import_lib.Utils.html`<center><div class="pad"><strong>${this.room.title} Groupwatch - ${this.title}</strong><br /><br />` + `<p>${this.started ? this.getVideoDisplay() : ""}</p><hr /><p>${this.started ? this.getStatsDisplay() : "<i>Waiting to start the video...</i>"}</p><p>${this.hints().join("<br />")}</p>`;
  }
  update() {
    for (const user of Object.values(this.room.users)) {
      for (const conn of user.connections) {
        if (conn.openPages?.has(`groupwatch-${this.id}`)) {
          void Chat.parse(`/j view-groupwatch-${this.id}`, this.room, user, conn);
        }
      }
    }
  }
  async changeVideo(url) {
    const info = await YouTube.getGroupwatchData(url);
    if (!info)
      throw new Chat.ErrorMessage(`Could not retrieve data for URL ${url}`);
    this.url = url;
    this.started = Date.now();
    this.info = info;
    this.update();
  }
  destroy() {
    _GroupWatch.groupwatches.delete(this.id);
    this.room.game = null;
    this.room = null;
  }
};
let GroupWatch = _GroupWatch;
GroupWatch.groupwatches = /* @__PURE__ */ new Map();
const YouTube = new YoutubeInterface(channelData);
function destroy() {
  if (YouTube.interval)
    clearInterval(YouTube.interval);
}
const commands = {
  async randchannel(target, room, user) {
    room = this.requireRoom("youtube");
    if (Object.keys(YouTube.data.channels).length < 1)
      return this.errorReply(`No channels in the database.`);
    target = toID(target);
    this.runBroadcast();
    const data = await YouTube.randChannel(target);
    return this.sendReply(`|html|${data}`);
  },
  randchannelhelp: [`/randchannel - View data of a random channel from the YouTube database.`],
  yt: "youtube",
  youtube: {
    async addchannel(target, room, user) {
      room = this.requireRoom("youtube");
      this.checkCan("mute", null, room);
      const [id, name] = target.split(",").map((t) => t.trim());
      if (!id)
        return this.errorReply("Specify a channel ID.");
      await YouTube.getChannelData(id, name);
      this.modlog("ADDCHANNEL", null, `${id} ${name ? `username: ${name}` : ""}`);
      return this.privateModAction(
        `${user.name} added channel with id ${id} ${name ? `and username (${name}) ` : ""} to the random channel pool.`
      );
    },
    addchannelhelp: [`/addchannel - Add channel data to the YouTube database. Requires: % @ #`],
    removechannel(target, room, user) {
      room = this.requireRoom("youtube");
      this.checkCan("mute", null, room);
      const id = YouTube.channelSearch(target);
      if (!id)
        return this.errorReply(`Channel with ID or name ${target} not found.`);
      delete YouTube.data.channels[id];
      YouTube.save();
      this.privateModAction(`${user.name} deleted channel with ID or name ${target}.`);
      return this.modlog(`REMOVECHANNEL`, null, id);
    },
    removechannelhelp: [`/youtube removechannel - Delete channel data from the YouTube database. Requires: % @ #`],
    async channel(target, room, user) {
      room = this.requireRoom("youtube");
      const channel = YouTube.channelSearch(target);
      if (!channel)
        return this.errorReply(`No channels with ID or name ${target} found.`);
      const data = await YouTube.generateChannelDisplay(channel);
      this.runBroadcast();
      return this.sendReply(`|html|${data}`);
    },
    channelhelp: [
      "/youtube channel - View the data of a specified channel. Can be either channel ID or channel name."
    ],
    async video(target, room, user) {
      room = this.requireRoom("youtube");
      this.checkCan("mute", null, room);
      const buffer = await YouTube.generateVideoDisplay(target, true);
      this.runBroadcast();
      this.sendReplyBox(buffer);
    },
    channels(target, room, user) {
      target = toID(target);
      return this.parse(`/j view-channels${target ? `-${target}` : ""}`);
    },
    help(target, room, user) {
      return this.parse("/help youtube");
    },
    categories() {
      return this.parse(`/j view-channels-categories`);
    },
    update(target, room, user) {
      room = this.requireRoom("youtube");
      this.checkCan("mute", null, room);
      const [channel, name] = target.split(",");
      const id = YouTube.channelSearch(channel);
      if (!id)
        return this.errorReply(`Channel ${channel} is not in the database.`);
      YouTube.data.channels[id].username = name;
      this.modlog(`UPDATECHANNEL`, null, name);
      this.privateModAction(`${user.name} updated channel ${id}'s username to ${name}.`);
      YouTube.save();
    },
    interval: "repeat",
    repeat(target, room, user) {
      room = this.requireRoom("youtube");
      this.checkCan("declare", null, room);
      if (!target) {
        if (!YouTube.interval)
          return this.errorReply(`The YouTube plugin is not currently running an interval.`);
        return this.sendReply(`Interval is currently set to ${Chat.toDurationString(YouTube.intervalTime * 60 * 1e3)}.`);
      }
      if (this.meansNo(target)) {
        if (!YouTube.interval)
          return this.errorReply(`The interval is not currently running`);
        clearInterval(YouTube.interval);
        delete YouTube.data.intervalTime;
        YouTube.save();
        this.privateModAction(`${user.name} turned off the YouTube interval`);
        return this.modlog(`YOUTUBE INTERVAL`, null, "OFF");
      }
      if (Object.keys(channelData).length < 1)
        return this.errorReply(`No channels in the database.`);
      if (isNaN(parseInt(target)))
        return this.errorReply(`Specify a number (in minutes) for the interval.`);
      YouTube.runInterval(target);
      YouTube.save();
      this.privateModAction(`${user.name} set a randchannel interval to ${target} minutes`);
      return this.modlog(`CHANNELINTERVAL`, null, `${target} minutes`);
    },
    addcategory(target, room, user) {
      room = this.requireRoom("youtube");
      this.checkCan("mute", null, room);
      const categoryID = toID(target);
      if (!categoryID)
        return this.parse(`/help youtube`);
      if (YouTube.data.categories.map(toID).includes(categoryID)) {
        return this.errorReply(`This category is already added. To change it, remove it and re-add it.`);
      }
      YouTube.data.categories.push(target);
      this.modlog(`YOUTUBE ADDCATEGORY`, null, target);
      this.privateModAction(`${user.name} added category '${target}' to the categories list.`);
      YouTube.save();
    },
    removecategory(target, room, user) {
      room = this.requireRoom("youtube");
      this.checkCan("mute", null, room);
      const categoryID = toID(target);
      if (!categoryID)
        return this.parse(`/help youtube`);
      const index = YouTube.data.categories.indexOf(target);
      if (index < 0) {
        return this.errorReply(`${target} is not a valid category.`);
      }
      for (const id in YouTube.data.channels) {
        const channel = YouTube.data.channels[id];
        if (channel.category === target)
          delete YouTube.data.channels[id].category;
      }
      YouTube.save();
      this.privateModAction(`${user.name} removed the category '${target}' from the category list.`);
      this.modlog(`YOUTUBE REMOVECATEGORY`, null, target);
    },
    setcategory(target, room, user) {
      room = this.requireRoom("youtube");
      this.checkCan("mute", null, room);
      target = target.trim();
      const [category, id] = import_lib.Utils.splitFirst(target, ",").map((item) => item.trim());
      if (!target || !category || !id) {
        return this.parse("/help youtube");
      }
      if (!YouTube.data.categories.includes(category)) {
        return this.errorReply(`Invalid category.`);
      }
      const name = YouTube.channelSearch(id);
      if (!name)
        return this.errorReply(`Invalid channel.`);
      const channel = YouTube.data.channels[name];
      YouTube.data.channels[name].category = category;
      YouTube.save();
      this.modlog(`YOUTUBE SETCATEGORY`, null, `${id}: to category ${category}`);
      this.privateModAction(`${user.name} set the channel ${channel.name}'s category to '${category}'.`);
    },
    decategorize(target, room, user) {
      room = this.requireRoom("youtube");
      this.checkCan("mute", null, room);
      target = target.trim();
      if (!target) {
        return this.parse("/help youtube");
      }
      const name = YouTube.channelSearch(target);
      if (!name)
        return this.errorReply(`Invalid channel.`);
      const channel = YouTube.data.channels[name];
      const category = channel.category;
      if (!category)
        return this.errorReply(`That channel does not have a category.`);
      delete channel.category;
      YouTube.save();
      this.modlog(`YOUTUBE DECATEGORIZE`, null, target);
      this.privateModAction(`${user.name} removed the channel ${channel.name} from the category ${category}.`);
    }
  },
  youtubehelp: [
    `YouTube commands:`,
    `/randchannel [optional category]- View data of a random channel from the YouTube database. If a category is given, the random channel will be in the  given category.`,
    `/youtube addchannel [channel] - Add channel data to the YouTube database. Requires: % @ #`,
    `/youtube removechannel [channel]- Delete channel data from the YouTube database. Requires: % @ #`,
    `/youtube channel [channel] - View the data of a specified channel. Can be either channel ID or channel name.`,
    `/youtube video [video] - View data of a specified video. Can be either channel ID or channel name.`,
    `/youtube update [channel], [name] - sets a channel's PS username to [name]. Requires: % @ #`,
    `/youtube repeat [time] - Sets an interval for [time] minutes, showing a random channel each time. Requires: # &`,
    `/youtube addcategory [name] - Adds the [category] to the channel category list. Requires: @ # &`,
    `/youtube removecategory [name] - Removes the [category] from the channel category list. Requires: @ # &`,
    `/youtube setcategory [category], [channel name] - Sets the category for [channel] to [category]. Requires: @ # &`,
    `/youtube decategorize [channel name] - Removes the category for the [channel], if there is one. Requires: @ # &`,
    `/youtube categores - View all channels sorted by category.`
  ],
  groupwatch: {
    async create(target, room, user) {
      room = this.requireRoom();
      if (!GROUPWATCH_ROOMS.includes(room.roomid)) {
        return this.errorReply(`This room is not allowed to use the groupwatch function.`);
      }
      this.checkCan("mute", null, room);
      const [url, title] = import_lib.Utils.splitFirst(target, ",").map((p) => p.trim());
      if (!url || !title)
        return this.errorReply(`You must specify a video to watch and a title for the group watch.`);
      const game = await YouTube.createGroupWatch(url, room, title);
      this.modlog(`YOUTUBE GROUPWATCH`, null, `${url} (${title})`);
      room.add(
        `|uhtml|${game.id}|<button class="button" name="send" value="/j view-groupwatch-${game.id}">Join the ongoing group watch!</button>`
      );
      room.send(`|tempnotify|youtube|New groupwatch - ${title}!`);
      this.update();
    },
    end(target, room, user) {
      room = this.requireRoom();
      this.checkCan("mute", null, room);
      const game = this.requireGame(GroupWatch);
      this.modlog(`GROUPWATCH END`);
      this.add(`|uhtmlchange|${game.id}|`);
      game.destroy();
    },
    start(target, room, user) {
      room = this.requireRoom();
      this.checkCan("mute", null, room);
      const game = this.requireGame(GroupWatch);
      game.start();
      game.update();
    },
    async edit(target, room, user) {
      room = this.requireRoom();
      this.checkCan("mute", null, room);
      const game = this.requireGame(GroupWatch);
      await game.changeVideo(target);
    },
    list() {
      let buf = `<strong>Ongoing groupwatches:</strong><br />`;
      for (const curRoom of Rooms.rooms.values()) {
        if (!curRoom.getGame(GroupWatch))
          continue;
        buf += `<button class="button" name="send" value="/j ${curRoom.roomid}">${curRoom.title}</button>`;
      }
      this.runBroadcast();
      this.sendReplyBox(buf);
    }
  },
  groupwatchhelp: [
    `/groupwatch create [link],[title] - create a groupwatch for the given Youtube or Twitch [link] with the [title]. Requires: % @ & #`,
    `/groupwatch end - End the current room's groupwatch, if one exists. Requires: % @ & #`,
    `/groupwatch start - Begin playback for the current groupwatch. Requires: % @ & #`,
    `/groupwatch edit [link] - Change the current groupwatch, if one exists, to be viewing the given [link]. Requires: % @ & #`
  ],
  twitch: {
    async channel(target, room, user) {
      room = this.requireRoom("youtube");
      if (!Config.twitchKey)
        return this.errorReply(`Twitch is not configured`);
      const data = await Twitch.getChannel(target);
      if (!data)
        return this.errorReply(`Channel not found`);
      const html = Twitch.visualizeChannel(data);
      this.runBroadcast();
      return this.sendReplyBox(html);
    }
  }
};
const pages = {
  async channels(args, user) {
    const [type] = args;
    if (!Config.youtubeKey)
      return `<h2>Youtube is not configured.</h2>`;
    const titles = {
      all: "All channels",
      categories: "by category"
    };
    const title = titles[type] || "Usernames only";
    this.title = `[Channels] ${title}`;
    let buffer = `<div class="pad"><h4>Channels in the YouTube database: (${title})`;
    buffer += ` <button class="button" name="send" value="/join view-channels-${type}" style="float: right">Refresh</button>`;
    buffer += `</h4><hr />`;
    switch (toID(type)) {
      case "categories":
        if (!YouTube.data.categories.length) {
          return this.errorReply(`There are currently no categories in the Youtube channel database.`);
        }
        const sorted = {};
        const channels = YouTube.data.channels;
        for (const [id, channel] of Object.entries(channels)) {
          const category = channel.category || "No category";
          if (!sorted[category]) {
            sorted[category] = [];
          }
          sorted[category].push(id);
        }
        for (const cat in sorted) {
          buffer += `<h3>${cat}:</h3>`;
          for (const id of sorted[cat]) {
            const channel = channels[id];
            buffer += `<details><summary>${channel.name}</summary>`;
            buffer += await YouTube.generateChannelDisplay(id);
            buffer += `</details><br />`;
          }
        }
        break;
      default:
        for (const id of import_lib.Utils.shuffle(Object.keys(YouTube.data.channels))) {
          const { name, username } = await YouTube.get(id);
          if (toID(type) !== "all" && !username)
            continue;
          buffer += `<details><summary>${name}`;
          buffer += `<small><i> (Channel ID: ${id})</i></small>`;
          if (username)
            buffer += ` <small>(PS name: ${username})</small>`;
          buffer += `</summary>`;
          buffer += await YouTube.generateChannelDisplay(id);
          buffer += `</details><hr/ >`;
        }
        break;
    }
    buffer += `</div>`;
    return buffer;
  },
  groupwatch(query, user, connection) {
    if (!user.named)
      return Rooms.RETRY_AFTER_LOGIN;
    const [roomid, num] = query;
    const watch = GroupWatch.groupwatches.get(`${roomid}-${num}`);
    if (!watch)
      return this.errorReply(`Groupwatch ${roomid}-${num} not found.`);
    this.title = `[Groupwatch] ${watch.title}`;
    return watch.display();
  }
};
//# sourceMappingURL=youtube.js.map
