"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, TwitchStream: () => TwitchStream, 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 = `
`;
buf += ` `; buf += `${name}`; buf += ` | `;
buf += ` `; buf += `${videos} videos | ${subs} subscribers | ${views} video views `; buf += ``; buf += `${description.slice(0, 400).replace(/\n/g, " ")}${description.length > 400 ? "(...)" : ""} `; if (username) { buf += `PS username: ${username} |
`;
buf += ` `; buf += `${info.title}`; buf += ` | `;
buf += ` `;
buf += `${info.likes} likes | ${info.dislikes} dislikes | ${info.views} video views Video Description`; buf += ``; buf += `${info.description.slice(0, 400).replace(/\n/g, " ")}${info.description.length > 400 ? "(...)" : ""} | `;
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 id = this.getId(url);
const videoInfo = await this.getVideoData(id);
if (!videoInfo)
throw new Chat.ErrorMessage(`Video not found.`);
const num = baseRoom.nextGameNumber();
baseRoom.saveSettings();
const gameRoom = Rooms.createGameRoom(`video-watch-${num}`, import_lib.Utils.html`[Group Watch] ${title}`, {
isPrivate: "hidden"
});
const game = new GroupWatch(gameRoom, url, videoInfo);
gameRoom.game = game;
gameRoom.setParent(baseRoom);
return gameRoom;
}
}
const Twitch = new class {
constructor() {
this.linkRegex = /(https?:\/\/)?twitch.tv\/([A-Za-z0-9]+)/i;
}
async getChannel(channel) {
channel = toID(channel);
let res;
try {
res = await (0, import_lib.Net)(`https://api.twitch.tv/kraken/search/channels`).get({
headers: {
"Client-Id": Config.twitchKey,
"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 = `
`;
buf += ` `; buf += `${info.display_name}`; buf += ` | `;
buf += ` `; const created = new Date(info.created_at); buf += `${info.followers} subscribers | ${info.views} stream views | created ${Chat.toTimestamp(created).split(" ")[0]} `; buf += `Last seen playing ${info.game} (Status: ${info.status}) `; buf += ``; buf += `${info.description.slice(0, 400).replace(/\n/g, " ")}${info.description.length > 400 ? "..." : ""} `; buf += " |
`;
buf += `
${this.info.title}
`;
const id = YouTube.getId(this.url);
const url = `https://youtube.com/watch?v=${id}`;
buf += `
`.repeat(4);
buf += `
`;
buf += `Watching ${this.data.display_name} `;
buf += `
`;
buf += `${Chat.count(Object.keys(this.room.users).length, "users")} watching
`;
buf += `Playing: ${this.data.game}`;
return buf;
}
getStreamDisplay() {
let buf = `
`;
for (const curRoom of Rooms.rooms.values()) {
if (!curRoom.getGame(GroupWatch))
continue;
buf += ``;
}
this.runBroadcast();
this.sendReplyBox(buf);
}
},
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.`,
`/youtube groupwatch [link], [title] - Creates a group watch of the [url] with the given [title]. Requires % @ & #`,
`/youtube startwatch - Starts the group watch in the current room, if there is one. Requires % @ & #`,
`/youtube stopwatch - Ends the current group watch, if there is one in the current room. 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);
},
async watch(target, room, user) {
room = this.requireRoom();
if (!GROUPWATCH_ROOMS.includes(room.roomid)) {
throw new Chat.ErrorMessage(`You cannot use this command in this room.`);
}
this.checkCan("mute", null, room);
if (!toID(target)) {
return this.errorReply(`Invalid channel`);
}
const gameRoom = await TwitchStream.createStreamWatch(room, target);
user.joinRoom(gameRoom);
room.add(
`|uhtml|ts-${gameRoom.roomid}|`
).update();
},
start(target, room) {
room = this.requireRoom();
const stream = this.requireGame(TwitchStream);
stream.start();
},
stop(target, room, user) {
room = this.requireRoom();
const stream = this.requireGame(TwitchStream);
this.checkCan("mute", null, room);
stream.end();
}
}
};
const pages = {
async channels(args, user) {
const [type] = args;
if (!Config.youtubeKey)
return `Youtube is not configured.
`;
const titles = {
all: "All channels",
categories: "by category"
};
const title = titles[type] || "Usernames only";
this.title = `[Channels] ${title}`;
let buffer = `Channels in the YouTube database: (${title})`;
buffer += ` `;
buffer += `
`;
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 += `${cat}:
`;
for (const id of sorted[cat]) {
const channel = channels[id];
buffer += `${channel.name}
`;
buffer += await YouTube.generateChannelDisplay(id);
buffer += `
`;
}
}
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 += `${name}`;
buffer += ` (Channel ID: ${id})`;
if (username)
buffer += ` (PS name: ${username})`;
buffer += `
`;
buffer += await YouTube.generateChannelDisplay(id);
buffer += `
`;
}
break;
}
buffer += `