twitch-chat-bot
Version:
an attempt to provide a generic, but highly-configurable platform for developers intending to create Twitch chat bots in Node.js
380 lines (308 loc) • 7.64 kB
JavaScript
/**
* twitch-chat-bot
*
* Copyright (c) 2020 WildcardSearch
*/
const TwitchChatBotModule = require("../../lib/twitch-chat-bot-module.js");
const {
errorCategories, errorCodes, warningCodes,
} = require("./error-codes.js");
class StreamTimer_TwitchChatBotModule extends TwitchChatBotModule
{
id = "timer";
/**
* register database fields &
* error codes
*
* @return void
*/
install()
{
this.db.registerField([{
key: "live",
type: "boolean",
}, {
key: "livetime",
type: "number",
}, {
key: "lastlive",
type: "number",
}]);
this.errorHandler.registerCategories(errorCategories);
this.errorHandler.registerWarnings(warningCodes);
this.errorHandler.registerCodes(errorCodes);
this.polyglot.extend(require(`../../locales/${this.bot.locale}/timer.json`));
}
/**
* setup; add commands; get stream info and calculate/retrieve live time; start the live timer if applicable
*
* @return void
*/
init()
{
this.live = false;
this.liveTimestamp = null;
this.liveFunctions = [];
this.liveTimer = null;
const now = Date.now();
this.bot.registerEvent("live-time-update");
this.bot.registerEvent("live", true);
this.bot.isLive = this.isLive;
this.commandCenter.addCommand([{
key: "livetime",
description: this.polyglot.t("timer.commands.live_time.description"),
aliases: [ "lt" ],
parser: this.parseLiveTimeCommand.bind(this),
}, {
key: "golive",
description: this.polyglot.t("timer.commands.go_live.description"),
permissionLevel: this.permissions.permMap["PERMISSIONS_STREAMER"],
parser: this.parseGoLiveCommand.bind(this),
}]);
if (typeof this.bot.streamData !== "object" ||
typeof this.bot.streamData.livetime === "undefined" ||
typeof this.bot.streamData.live === "undefined" ||
this.bot.streamData.livetime === 0) {
this.calculateLiveTime();
return;
}
this.setLiveTimestamp(this.bot.streamData.livetime);
let msTillLive = this.liveTimestamp-now;
if (this.bot.streamData.live ||
msTillLive <= 0) {
this.forceLive();
return;
}
this.liveTimer = setTimeout(this.goLive.bind(this), msTillLive);
}
/**
* parser: !golive
*
* @param Object
* @return void
*/
parseGoLiveCommand(options)
{
const now = Date.now();
if (this.live === true) {
this.bot.sendMessage(this.polyglot.t("timer.commands.go_live.stream_already_live"));
return;
}
if (options.msgPieces.length === 1 ||
typeof options.msgPieces[1] !== "string" ||
options.msgPieces[1].length === 0) {
this.forceLive();
return;
}
this.setLiveTimeStampFromParams(options.msgPieces[1]);
let msTillLive = this.liveTimestamp-now;
this.liveTimer = setTimeout(this.goLive.bind(this), msTillLive);
this.db.updateStreamInfo({
live: 0,
livetime: this.liveTimestamp,
});
this.parseLiveTimeCommand();
}
/**
* initialize the timer
*
* @return void
*/
calculateLiveTime()
{
const now = Date.now();
if (this.options.timer.livetime === null) {
this.bot.log("cancelling live timer startup");
return;
}
this.setLiveTimeStampFromParams(this.options.timer.livetime);
let msTillLive = this.liveTimestamp-now;
this.liveTimer = setTimeout(this.goLive.bind(this), msTillLive);
this.db.updateStreamInfo({
live: 0,
livetime: this.liveTimestamp,
});
}
/**
* set the propety and fire the event
*
* @param String|Number
* @return void
*/
setLiveTimestamp(v)
{
this.liveTimestamp = v;
this.bot.fireEvent("live-time-update");
}
/**
* produce a live timestamp from provided options
*
* @param String|Number
* @return void
*/
setLiveTimeStampFromParams(lt = "top")
{
const
rDate = new Date(),
hrs = rDate.getHours();
let mins = rDate.getMinutes();
if (typeof lt === "string" &&
isNaN(parseInt(lt, 10)) === false) {
lt = parseInt(lt, 10);
}
switch(typeof lt) {
case "string":
switch(lt) {
case "top":
rDate.setHours(hrs+1);
rDate.setMinutes(0);
rDate.setSeconds(0);
rDate.setMilliseconds(0);
break;
case "bottom":
if (mins > 30) {
rDate.setHours(hrs+1);
rDate.setMinutes(30);
} else {
rDate.setMinutes(30);
}
rDate.setSeconds(0);
rDate.setMilliseconds(0);
break;
case "nextquarter":
if (mins > 45) {
rDate.setHours(hrs+1);
rDate.setMinutes(0);
} else {
if (mins > 30) {
rDate.setMinutes(45);
} else {
if (mins > 15) {
rDate.setMinutes(30);
} else {
rDate.setMinutes(15);
}
}
}
rDate.setSeconds(0);
rDate.setMilliseconds(0);
break;
}
break;
case "number":
let wait = parseInt(lt, 10),
hoursToAdd = 0;
// input is in minutes, so allow for large values
while (wait > (60-mins)) {
hoursToAdd++;
wait -= (60-mins);
mins = 0;
}
if (hoursToAdd > 0) {
rDate.setHours(hrs+hoursToAdd);
}
if (wait > 0) {
rDate.setMinutes(mins+wait);
}
break;
default:
this.errorHandler.warn("ERROR_TIMER_SET_LIVE_TS_BAD_INFO", arguments);
return;
}
this.setLiveTimestamp(rDate.getTime());
}
/**
* parser: !livetime
*
* @param Object
* @return void
*/
parseLiveTimeCommand(options)
{
let timeDescription = "";
if (this.live) {
timeDescription = this.bot.formatTimeStamp(Date.now() - this.liveTimestamp);
this.bot.sendMessage(this.polyglot.t("timer.commands.live_time.stream_time_since_live", {
"live_time_description": timeDescription.description || this.polyglot.t("timer.commands.live_time.unknown_amount_of_time"),
}));
return;
}
if (this.liveTimestamp === null) {
this.bot.sendMessage(this.polyglot.t("timer.commands.live_time.no_live_time_set"));
return;
}
timeDescription = this.bot.formatTimeStamp(this.liveTimestamp-Date.now());
this.bot.sendMessage(this.polyglot.t("timer.commands.live_time.stream_time_till_live", {
"time_description": timeDescription.description || this.polyglot.t("timer.commands.live_time.unknown_amount_of_time"),
}));
}
/**
* for crash recovery; force stream into live mode
*
* @param Boolean
* @return void
*/
forceLive()
{
this.clearLiveTimer();
this.goLive();
}
/**
* getter for live state
*
* @param Boolean
* @return void
*/
isLive()
{
return this.live;
}
/**
* go live
*
* @return void
*/
goLive()
{
let data = {
live: true,
lastlive: Date.now(),
};
if (typeof this.bot.streamData.livetime === "undefined" ||
isNaN(parseInt(this.bot.streamData.livetime, 10)) == true ||
parseInt(this.bot.streamData.livetime, 10) <= 0) {
this.liveTimestamp = data.livetime = Date.now();
}
this.live = true;
this.db.updateStreamInfo(data);
this.bot.fireEvent("live");
this.bot.on("chat", () => {
this.db.updateStreamInfo({
lastlive: Date.now(),
});
});
}
/**
* return the time stamp of going live
*
* @return void
*/
getLiveTime()
{
return this.liveTimestamp;
}
/**
* stop tracking the go live time
*
* @return void
*/
clearLiveTimer()
{
if (this.liveTimer === null) {
return;
}
clearTimeout(this.liveTimer);
this.liveTimer = null;
}
}
module.exports = StreamTimer_TwitchChatBotModule;