twitch2ma
Version:
Twitch chat bot that runs commands on the MA GrandMA2 using Telnet.
203 lines • 8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SACNCorruptError = exports.SACNError = exports.SACNStopped = exports.SACNLost = exports.SACNReceiving = exports.SACNWaiting = exports.SACNStatus = void 0;
const sacn_1 = require("sacn");
const typed_event_emitter_1 = require("@d-fischer/typed-event-emitter");
const SACNUniverse_1 = require("./SACNUniverse");
const sentry_1 = require("../sentry");
const _ = require("lodash");
class SACNPermission extends typed_event_emitter_1.EventEmitter {
constructor(config) {
super();
this.onStatus = this.registerEvent();
this.onError = this.registerEvent();
this.universes = [];
this.config = config;
this.running = false;
}
check(permissionCollector, runtimeInformation, additionalRuntimeInformation) {
if (runtimeInformation.instructions) {
let sacn = runtimeInformation.instructions.sacn;
if (sacn) {
let universe = this.universes[sacn.universe];
if (universe && (universe.status !== SACNUniverse_1.UniverseStatus.Valid || universe.data[sacn.channel - 1] < 255)) {
permissionCollector.denyPermission("sacn", `@${runtimeInformation.userName}, ${runtimeInformation.config.sacn.lockMessage}`);
}
}
}
}
start() {
if (!this.running) {
for (const universe of SACNPermission.getUniverses(this.config)) {
this.universes[universe] = new SACNUniverse_1.SACNUniverse(universe);
}
let universes = SACNPermission.getUniverses(this.config);
if (universes.length > 0) {
let receiverOptions = {
universes: universes,
reuseAddr: true
};
if (this.config.sacn && _.isString(this.config.sacn.interface)) {
_.set(receiverOptions, "iface", this.config.sacn.interface);
}
this.sACNReceiver = new sacn_1.Receiver(receiverOptions);
this.sACNReceiver.on("packet", (packet) => {
this.universes[packet.universe].data = [...packet.payloadAsBuffer];
});
this.sACNReceiver.on("PacketCorruption", error => {
this.emit(this.onError, new SACNCorruptError());
this.stop().catch(error => sentry_1.default(error));
sentry_1.default(error);
});
this.sACNReceiver.on("error", (error) => {
// @ts-ignore
if (error.code) {
// @ts-ignore
switch (error.code) {
case "EADDRNOTAVAIL":
case "ENODEV":
this.emit(this.onError, new SACNError(`sACN error: Have you configured the right interface IP?`));
break;
case "ERR_SOCKET_DGRAM_NOT_RUNNING":
this.emit(this.onError, new SACNError("sACN error: UDP subsystem not running!"));
break;
default:
this.emit(this.onError, error);
break;
}
}
else {
this.emit(this.onError, error);
}
this.stop().catch(error => sentry_1.default(error));
});
this.emit(this.onStatus, new SACNWaiting(universes));
this.watchdogTimeout = setInterval(() => this.watchdog(), this.config.sacn.timeout);
this.watchdogTimeout.unref();
this.running = true;
}
}
return Promise.resolve();
}
stop() {
let stopChain = Promise.resolve();
if (this.sACNReceiver && this.running) {
stopChain = stopChain
.then(() => new Promise(resolve => {
this.sACNReceiver.close(() => {
resolve();
});
}))
.catch(error => sentry_1.default(error));
}
_.attempt(() => clearInterval(this.watchdogTimeout));
return stopChain
.then(() => this.running = false)
.then(() => this.emit(this.onStatus, new SACNStopped()));
}
reloadConfig(config) {
if (!_.isEqual(this.config.sacn, config.sacn)
|| !_.isEqual(SACNPermission.getUniverses(this.config), SACNPermission.getUniverses(config))) {
return this.stop()
.then(() => this.config = config)
.then(() => this.start());
}
return Promise.resolve();
}
watchdog() {
let lastValidTime = new Date().getTime() - this.config.sacn.timeout * 1000;
let receiving = [];
let lost = [];
for (const universe of this.universes) {
if (!universe) {
continue;
}
if (lastValidTime <= universe.lastReceived &&
(universe.watchdogStatus == SACNUniverse_1.UniverseStatus.NeverReceived || universe.watchdogStatus == SACNUniverse_1.UniverseStatus.Expired)) {
receiving.push(universe.universe);
universe.watchdogStatus = SACNUniverse_1.UniverseStatus.Valid;
}
if (lastValidTime > universe.lastReceived && universe.status == SACNUniverse_1.UniverseStatus.Valid) {
lost.push(universe.universe);
universe.watchdogStatus = SACNUniverse_1.UniverseStatus.Expired;
}
}
if (receiving.length > 0) {
this.emit(this.onStatus, new SACNReceiving(receiving));
}
if (lost.length > 0) {
this.emit(this.onStatus, new SACNLost(lost));
}
}
withOnStatusHandler(handler) {
this.onStatus(handler);
return this;
}
withOnErrorHandler(handler) {
this.onError(handler);
return this;
}
static getUniverses(config) {
function findAndPush(sacn) {
if (sacn && !_.find(universes, universe => universe === sacn.universe)) {
universes.push(sacn.universe);
}
}
let universes = new Array();
for (const command of config.commands) {
findAndPush(command.sacn);
for (const parameter of command.parameters) {
findAndPush(parameter.sacn);
}
}
return universes;
}
emit(event, ...args) {
try {
super.emit(event, ...args);
}
catch (error) {
sentry_1.default(error);
}
}
}
exports.default = SACNPermission;
class SACNStatus {
constructor(universes) {
this.universes = universes;
}
}
exports.SACNStatus = SACNStatus;
class SACNWaiting extends SACNStatus {
}
exports.SACNWaiting = SACNWaiting;
class SACNReceiving extends SACNStatus {
}
exports.SACNReceiving = SACNReceiving;
class SACNLost extends SACNStatus {
}
exports.SACNLost = SACNLost;
class SACNStopped extends SACNStatus {
constructor() {
super(null);
}
}
exports.SACNStopped = SACNStopped;
class SACNError extends Error {
constructor(message) {
super(message);
Object.setPrototypeOf(this, SACNError.prototype);
this.name = SACNError.name;
}
}
exports.SACNError = SACNError;
class SACNCorruptError extends SACNError {
constructor() {
super("sACN error: Unexpected packet received. Are you using the \"final\" " +
"protocol version in your MA sACN settings?");
Object.setPrototypeOf(this, SACNCorruptError.prototype);
this.name = SACNCorruptError.name;
}
}
exports.SACNCorruptError = SACNCorruptError;
//# sourceMappingURL=SACNPermission.js.map