UNPKG

twitch2ma

Version:

Twitch chat bot that runs commands on the MA GrandMA2 using Telnet.

245 lines โ€ข 10.5 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.exitWithError = exports.loadConfig = exports.attachEventHandlers = exports.notifyUpdate = exports.main = void 0; const commander_1 = require("commander"); const twitch_1 = require("twitch"); const Twitch2Ma_1 = require("./Twitch2Ma"); const Config_1 = require("./Config"); const CommandSocket_1 = require("./CommandSocket"); const Logger_1 = require("./Logger"); const sentry_1 = require("./sentry"); const axios_1 = require("axios"); const Fs = require("fs"); const YAML = require("yaml"); const _ = require("lodash"); const chalk = require("chalk"); const ipc = require("node-ipc"); const childProcess = require("child_process"); const semverGt = require('semver/functions/gt'); const packageInformation = require("../../package.json"); // TODO Improve: let logger = new Logger_1.Logger(); let commandSocket = new CommandSocket_1.CommandSocket(); let globalTwitch2Ma; let globalConfigFile; function main() { process.on("SIGINT", () => exit(0)); axios_1.default.get("https://registry.npmjs.org/twitch2ma") .then(response => notifyUpdate(response.data["dist-tags"].latest)) .catch((error) => sentry_1.default(error, () => logger.warning("Could not get update information!"))) .then(init); } exports.main = main; function init() { let program = new commander_1.Command() .name(packageInformation.name) .description(packageInformation.description) .version(packageInformation.version, "-v, --version", "output the current version"); program.command("start", { isDefault: true }) .description("start twitch2ma bot") .arguments("[configFile]") .option("-d, --detach", "detach the bot from the terminal and run as daemon") .option("-l, --logfile <logfile>", "log to logfile") .action((configFile, cmd) => { loadConfig(configFile) .then(config => new Twitch2Ma_1.default(config)) .then(twitch2Ma => { if (cmd.detach) { return commandSocket.checkSocketExists().then(() => startChildProcess()); } else { if (cmd.logfile) { logger.setLogfile(cmd.logfile); } globalTwitch2Ma = twitch2Ma; return attachEventHandlers(twitch2Ma) .then(twitch2Ma => twitch2Ma.start()) .then(() => openCommandSocket()); } }) // @ts-ignore .catch(Config_1.ConfigError, Twitch2Ma_1.TelnetError, CommandSocket_1.SocketError, error => exitWithError(error)) .catch(twitch_1.InvalidTokenError, () => exitWithError(new Error("Twitch error: Access token invalid!"))) .catch(twitch_1.HTTPStatusCodeError, error => { if (error.statusCode === 500) { return exitWithError(new Error("Twitch error: Twitch seems to be broken at the moment (see " + "https://status.twitch.tv for status) ๐Ÿ˜•")); } else { throw error; } }) .catch(error => sentry_1.default(error, exitWithError)); }); program.command("stop") .description("stop twitch2ma bot") .action(() => emitSocketEvent("exit")); program.command("reloadConfig [configFile]") .description("reload running twitch2ma instance with a new config") .action(configFile => emitSocketEvent("reloadConfig", configFile)); program.parse(process.argv); } function startChildProcess() { for (let i = 0; i < process.argv.length; i++) { if (process.argv[i] === "-d" || process.argv[i] === "--detach") { process.argv[i] = ""; } } process.argv[0] = ""; let subProcess = childProcess.spawn(process.argv0, process.argv, { stdio: "ignore", shell: true, detached: true, windowsHide: true }); if (subProcess) { subProcess.unref(); logger.confirm("twitch2ma starting detached!"); } else { throw new Error("twitch2ma could not start detached!"); } } function emitSocketEvent(event, payload) { ipc.config.appspace = "twitch2ma."; ipc.config.silent = true; ipc.connectTo("main", () => { ipc.of.main.on("connect", () => { ipc.of.main.emit(event, payload); ipc.disconnect("main"); }); ipc.of.main.on("error", () => { ipc.disconnect("main"); logger.error("Could not connect to socket: Is twitch2ma running?"); process.exit(1); }); }); } function openCommandSocket() { commandSocket.onError(error => exitWithError(error)); commandSocket.onExitCommand(() => { logger.socketMessage("Exit command received!"); exit(0); }); commandSocket.onReloadConfigCommand((newConfigFile) => { if (!_.isString(newConfigFile)) { newConfigFile = globalConfigFile; } logger.socketMessage(`Reload config command received (${newConfigFile})!`); loadConfig(newConfigFile) .then(config => globalTwitch2Ma.reloadConfig(config)) .catch(error => sentry_1.default(error, () => logger.error(`Reloading config failed: ${error.message}`))); }); return commandSocket.start(); } function notifyUpdate(latestVersion) { if (semverGt(latestVersion, packageInformation.version)) { console.log(chalk `๐Ÿ”” {blue A new version of ${packageInformation.name} is available!} ` + chalk `{blue (current: {bold ${packageInformation.version}}, new: {bold ${latestVersion}})}`); } } exports.notifyUpdate = notifyUpdate; function attachEventHandlers(twitch2Ma) { return __awaiter(this, void 0, void 0, function* () { twitch2Ma.onTelnetConnected((host, user) => logger.confirm(chalk `Telnet connected to {bold ${user}:***@${host}:30000}.`)); twitch2Ma.onTwitchConnected(channel => logger.confirm(chalk `Twitch connected to {bold #${channel}}.`)); twitch2Ma.onCommandExecuted((channel, user, chatCommand, parameterName, consoleCommand) => { parameterName = _.isString(parameterName) ? ` ${parameterName}` : ""; logger.channelMessage(channel, chalk `๐Ÿ’ก User {bold ${user}} executed {bold.blue !${chatCommand}${parameterName}}` + (_.isString(consoleCommand) ? chalk ` ({magenta ${consoleCommand}}) on the desk.` : '.')); }); twitch2Ma.onHelpExecuted((channel, user, helpCommand) => { if (_.isString(helpCommand)) { logger.channelMessage(channel, chalk `๐Ÿคจ User {bold ${user}} got help for {bold.blue !${helpCommand}}.`); } else { logger.channelMessage(channel, chalk `๐Ÿ“– User {bold ${user}} listed available commands.`); } }); twitch2Ma.onGodMode((channel, user, reason) => logger.channelMessage(channel, chalk `๐Ÿ’ช User {bold ${user}} activated {bold.inverse god mode } because: ${reason}.`)); twitch2Ma.onPermissionDenied((channel, user, command, reason) => logger.channelMessage(channel, chalk `โœ‹ User {bold ${user}} tried to run {bold.blue ${command}} but permissions were denied by ${reason}.`)); twitch2Ma.onNotice(message => logger.notice(message)); twitch2Ma.onConfigReloaded(() => logger.confirm(`Config reloaded.`)); twitch2Ma.onError(error => exitWithError(error)); return twitch2Ma; }); } exports.attachEventHandlers = attachEventHandlers; function loadConfig(configFile) { return __awaiter(this, void 0, void 0, function* () { let configFileIsDefault = !_.isString(configFile); if (configFileIsDefault) { configFile = "config.json"; } let rawConfigFile; try { rawConfigFile = Fs.readFileSync(configFile, { encoding: "utf-8" }); } catch (error) { if (error.code === "ENOENT") { throw new Config_1.ConfigError(`Could not open config file ${configFile}!`); } else { throw error; } } let rawConfigObject; try { rawConfigObject = JSON.parse(rawConfigFile); } catch (error) { try { rawConfigObject = YAML.parse(rawConfigFile); } catch (ignored) { throw new Config_1.ConfigError(`Config file ${configFile} is not a valid JSON or YAML file!`); } } globalConfigFile = configFile; return new Config_1.Config(rawConfigObject); }); } exports.loadConfig = loadConfig; function exit(statusCode) { return __awaiter(this, void 0, void 0, function* () { let stopChain = Promise.resolve(); if (globalTwitch2Ma) { stopChain = stopChain .then(() => globalTwitch2Ma.stop()) .catch(err => { sentry_1.default(err); process.exit(1); }); } return stopChain .then(() => console.log(chalk `\n{bold Thank you for using twitch2ma} โค๏ธ`)) .then(() => logger.end()) .then(() => { if (commandSocket) { commandSocket.stop(); } }) .finally(() => process.exit(statusCode)); }); } function exitWithError(err) { return __awaiter(this, void 0, void 0, function* () { logger.error((err.message.slice(-1).match(/[!?]/) ? err.message : err.message + "!") + " Exiting..."); logger.end(); if (commandSocket) { commandSocket.stop(); } return exit(1); }); } exports.exitWithError = exitWithError; //# sourceMappingURL=index.js.map