@twitchfy/chatbot
Version:
A powerful node module to make your own Twitch ChatBot
302 lines (301 loc) • 11.1 kB
JavaScript
"use strict";
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/ban-types */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChatBot = void 0;
const eventsub_1 = require("@twitchfy/eventsub");
const helix_1 = require("@twitchfy/helix");
const path_1 = require("path");
const BaseChannel_1 = require("./BaseChannel");
const Collection_1 = require("./Collection");
const ChatBotUser_1 = require("./ChatBotUser");
const EventHandler_1 = require("./EventHandler");
const CommandHandler_1 = require("./CommandHandler");
const Stream_1 = require("./Stream");
const Clip_1 = require("./Clip");
const ChatRoom_1 = require("./ChatRoom");
const managers_1 = require("./managers");
const managers_2 = require("./managers");
const managers_3 = require("./managers");
const enums_1 = require("../enums");
const util_1 = require("../util");
/**
* Represents a chatbot.
*/
class ChatBot {
/**
* The client Id of the Twitch's application.
*/
clientId;
/**
* The client secret of the Twitch's application.
*/
clientSecret;
/**
* A Collection of the profiles of the joined channels.
*/
profiles;
/**
* The manager of the chatbot channels.
*/
channels;
/**
* The handlers of resources of the chatbot.
* @private
*/
handlers;
/**
* A Collection of the chatbot commands.
*/
commands;
/**
* A Collection of the chatbot events.
*/
events;
/**
* The message manager of the chatbot.
*/
messages;
/**
* The ban manager of the chatbot.
*/
bans;
/**
* The user manager of the chatbot.
*/
users;
/**
* The timeout manager of the chatbot.
*/
timeouts;
/**
* The warning manager of the chatbot.
*/
warns;
/**
* The chatters manager of the chatbot.
*/
chatters;
/**
* The Twitch user of the chatbot.
*/
user;
/**
* The user Id of the chatbot.
*/
userId;
/**
* The operator to separate the options in the command.
*/
optionOperator;
/**
* The EventSub connection type used by the chatbot.
*/
connectionType;
/**
* The EventSub connection of the chatbot.
*/
eventsub;
/**
* The Helix client of the chatbot.
*/
helixClient;
/**
* The options of the chatbot.
*/
options;
/**
* The prefix callback used to handle command prefixes.
* @protected
*/
__prefix;
/**
* Creates a new instance of the chatbot.
* @param options The options to build up the chatbot.
*/
constructor(options) {
this.options = options;
this.clientId = options.clientId;
this.clientSecret = options.clientSecret;
this.profiles = new Collection_1.Collection();
this.channels = new managers_3.ChannelManager(this);
this.messages = new managers_3.ChatBotMessageManager(this);
this.bans = new managers_3.ChatBotBanManager(this);
this.timeouts = new managers_3.ChatBotTimeoutManager(this);
this.users = new managers_3.ChatBotUserManager(this);
this.warns = new managers_2.ChatBotWarnsManager(this);
this.chatters = new managers_1.ChatBotChatterManager(this);
this.handlers = {
commands: new CommandHandler_1.CommandHandler((0, path_1.join)(process.cwd(), options.paths.output, options.paths.commands || '.')),
events: new EventHandler_1.EventHandler((0, path_1.join)(process.cwd(), options.paths.output, options.paths.events || '.'))
};
this.commands = new Collection_1.Collection();
this.events = new Collection_1.Collection();
this.connectionType = options.connectionType;
this.optionOperator = options.optionOperator ?? '-';
this.helixClient = new helix_1.HelixClient({ ...options, ...options.helix });
this.__prefix = options.prefix ?? (() => ['!']);
this.eventsub = this.createEventSubConnection(options.connectionType, options);
const fn = util_1.handleSubscriptionReload.bind(this);
// @ts-expect-error
this.eventsub.on(eventsub_1.Events.SubscriptionReload, fn);
}
/**
* Creates the EventSub connection of the chatbot.
* @param type The type of the EventSub connection.
* @param options The options to build up the EventSub connection.
* @returns The EventSub connection of the chatbot.
* @private
*/
createEventSubConnection(type, options) {
switch (type) {
case enums_1.EventSubConnection.WebSocket:
{
const { clientId, clientSecret, userToken, eventsub } = options;
this.helixClient.preferedToken = 'user';
// @ts-expect-error
return new eventsub_1.WebSocketConnection({ clientId, clientSecret, userToken, ...eventsub });
}
break;
case enums_1.EventSubConnection.Webhook:
{
const { clientId, clientSecret, eventsub } = options;
this.helixClient.setAppToken(eventsub.appToken);
// @ts-expect-error
return new eventsub_1.WebhookConnection({ clientId, clientSecret, ...eventsub }, eventsub.server);
}
break;
case enums_1.EventSubConnection.Conduit: {
const { clientId, clientSecret, eventsub } = options;
this.helixClient.setAppToken(eventsub.appToken);
// @ts-expect-error
return new eventsub_1.Conduit({ clientId, clientSecret, ...eventsub });
}
}
// @ts-expect-error
return;
}
/**
* Start the chatbot.
* @param options The start options to start the chatbot.
* @returns The current instance of the chatbot. When the promise is resolved the chatbot has been completly started.
*/
async start(options) {
if (this.options.paths.commands) {
const commands = await this.handlers.commands.load();
for (const commandClass of commands) {
const command = new commandClass();
if (!command.name || !command.run)
continue;
this.commands.set(command.name, command);
}
}
if (this.options.paths.events) {
const events = await this.handlers.events.load();
for (const data of events) {
if (!data.event || !data.run)
continue;
this.events.set(data.event, data);
}
}
const listener = util_1.handleEvent.bind(this);
// @ts-expect-error
this.eventsub.on(eventsub_1.Events.SubscriptionMessage, listener);
const tokenInfo = await this.helixClient.getUserToken(false).catch(async (err) => {
if (this.helixClient.userToken?.refresh) {
//@ts-expect-error
const token = await this.helixClient.refreshToken(this.helixClient.userToken).catch(() => {
throw err;
});
this.helixClient.setUserToken(token);
return await this.helixClient.getUserToken(false);
}
throw err;
});
this.userId = tokenInfo.user_id;
this.user = new ChatBotUser_1.ChatBotUser(this, await this.helixClient.getUser(this.userId));
// @ts-expect-error
await ((this.eventsub instanceof eventsub_1.WebhookConnection || this.eventsub instanceof eventsub_1.Conduit) ? this.eventsub.start(options?.port, options?.callback) : this.eventsub.connect());
const readyEvent = this.events.get('ChatBotReady');
if (readyEvent)
readyEvent.run(this, undefined);
return this;
}
/**
* Get Twitch streams from the API.
* @param options The options to filter the streams.
* @param number The number of streams to fetch. This number could vary from the result due to API reasons.
* @returns An array containing the streams fetched from the API. If there isn't any stream founded, it will return a nullish value.
*/
async streams(options, number) {
if (typeof options === 'number') {
number = options;
options = {};
}
const streams = await this.helixClient.getStreams(options, { pages: number ? number / 100 : Infinity });
if (!streams.length)
return null;
return streams.map(stream => new Stream_1.Stream(this, stream));
}
/**
* Fetches a Twitch stream from the API.
* @param options The options for filter the stream.
* @returns A stream fetched from the API or null if the stream wasn't founded.
*/
async stream(options) {
const stream = await this.helixClient.getStream(options);
return stream ? new Stream_1.Stream(this, stream) : null;
}
/**
* Get Twitch clips from the API.
* @param options The options to filter the clips.
* @returns An array containing the clips fetched from the API. If there isn't any clip founded, it will return a nullish value.
*/
async clips(options) {
const data = await this.helixClient.getClips(options);
if (!data.length)
return null;
return data.map(clip => new Clip_1.Clip(this, clip));
}
/**
* Get a specific Twitch clip from the API.
* @param options The options to filter the clip.
* @returns The clip fetched from the API or null if the clip wasn't founded.
*/
async clip(options) {
const data = await this.helixClient.getClip(options);
if (!data)
return null;
return new Clip_1.Clip(this, data);
}
/**
* Checks whether the chatbot is moderator in a specific channel.
* @param channelId The Id of the channel to check.
* @returns A boolean indicating whether the chatbot is moderator in the channel.
*/
async isModerator(channelId) {
const data = await this.moderatedChannels();
return data.some((x) => x.id === channelId);
}
/**
* Get the moderated channels of the chatbot.
* @returns An array containing the moderated channels of the chatbot.
*/
async moderatedChannels() {
const data = await this.helixClient.getModeratedChannels(this.userId);
return data.map((x) => new BaseChannel_1.BaseChannel(this, x, new ChatRoom_1.ChatRoom(this, x)));
}
/**
* The user token of the chatbot Twitch account to make requests.
*/
get userToken() {
return this.helixClient.userToken;
}
/**
* The app token of the Twitch application. The value is null if the chatbot is using a WebSocket EventSub connection.
*/
get appToken() {
return this.helixClient.appToken ?? null;
}
}
exports.ChatBot = ChatBot;