UNPKG

twitchonlinetracker

Version:

A library to help track streamers and fires an event if they go online.

352 lines (351 loc) 15.4 kB
'use strict'; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var dotenv = require("dotenv"); var axios_1 = require("axios"); var EventEmitter = require("events"); dotenv.config({ path: "./" + process.env.NODE_ENV + ".env" }); /** * Twitch Online Tracker * * @class TwitchOnlineTracker */ var TwitchOnlineTracker = /** @class */ (function (_super) { __extends(TwitchOnlineTracker, _super); /** *Creates an instance of TwitchOnlineTracker. * @param {TwitchOnlineTrackerOptions} options Options to pass * @memberof TwitchOnlineTracker */ function TwitchOnlineTracker(options) { var _this = _super.call(this) || this; _this.tracked = new Set(); _this._cachedStreamData = []; _this.options = options; if (_this.options.client_id === undefined || typeof _this.options.client_id !== 'string') { throw new Error('`client_id` must be set and a string for TwitchOnlineTracker to work.'); } if (_this.options.debug === undefined) { _this.options.debug = false; } if (_this.options.pollInterval === undefined) { _this.options.pollInterval = 30; } if (_this.options.track === undefined) { } else { _this.track(_this.options.track); } if (_this.options.start) { _this.start(); } return _this; } /** * Log something to console. * * @param {*} rest * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype.log = function () { var rest = []; for (var _i = 0; _i < arguments.length; _i++) { rest[_i] = arguments[_i]; } if (this.options.debug) console.log.apply(console, ['[twitchonlinetracker]'].concat(rest)); }; /** * Make a request on the Twitch Helix API. Used internally but can be used for something custom. * * @param {string} endpoint The endpoint, plus parameters. * @returns The response JSON data, unaltered from Twitch. * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype.api = function (endpoint) { return __awaiter(this, void 0, void 0, function () { var twitchApiBase, response, rv, err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); twitchApiBase = 'https://api.twitch.tv/helix/'; this.log("making a request: " + twitchApiBase + endpoint); return [4 /*yield*/, axios_1.default(twitchApiBase + endpoint, { headers: { 'Client-ID': this.options.client_id } })]; case 1: response = _a.sent(); rv = {}; if (response.data) { return [2 /*return*/, response.data]; } return [2 /*return*/, rv]; case 2: err_1 = _a.sent(); throw new Error(err_1); case 3: return [2 /*return*/]; } }); }); }; /** * Make a /users Twitch API request. * * Either `id` or `login` must be used. * * @param {UsersApiEndpointOptions} params The API parameters. * @returns The response JSON data, unaltered from Twitch. * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype.users = function (params) { return __awaiter(this, void 0, void 0, function () { var paramString_1, e_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); if (!params.id && !params.login) { throw new Error("Need login or id for Users endpoint."); } paramString_1 = ''; if (params.id) { params.id.forEach(function (id, idx) { paramString_1 += "id=" + id; if (idx < params.id.length) paramString_1 += '&'; }); } if (params.id && params.login) paramString_1 += '&'; if (params.login) { params.login.forEach(function (login, idx) { paramString_1 += "login=" + login + "&"; }); } paramString_1 = paramString_1.slice(0, -1); return [4 /*yield*/, this.api("users?" + paramString_1)]; case 1: return [2 /*return*/, _a.sent()]; case 2: e_1 = _a.sent(); throw new Error(e_1); case 3: return [2 /*return*/]; } }); }); }; /** * Make a /streams API request. * * @param {StreamsApiEndPointOptions} params The API parameters. * @returns The response JSON data, unaltered from Twitch. * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype.streams = function (params) { return __awaiter(this, void 0, void 0, function () { var paramString, param, join, e_2; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); paramString = ''; for (param in params) { if (Array.isArray(params[param])) { join = "&" + param + "="; paramString += param + "=" + params[param].join(join); paramString.slice(0, -(join.length)); } } return [4 /*yield*/, this.api("streams?" + paramString)]; case 1: return [2 /*return*/, _a.sent()]; case 2: e_2 = _a.sent(); throw new Error(e_2); case 3: return [2 /*return*/]; } }); }); }; /** * Begin tracking a stream * * @param {string[]} loginNames An array of login names of streamers * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype.track = function (loginNames) { var _this = this; this.log("tracking " + loginNames.join(', ')); loginNames.forEach(function (login) { _this.tracked.add(login.toLowerCase()); }); }; /** * Stop tracking a stream * * @param {string[]} loginNames An array of login names of streamers * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype.untrack = function (loginNames) { var _this = this; this.log("untracking " + loginNames.join(', ')); loginNames.forEach(function (login) { _this.tracked.delete(login.toLowerCase()); }); }; /** * Start making requests. * * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype.start = function () { var _this = this; this.log("starting to poll at " + this.options.pollInterval + "s intervals"); this._loopIntervalId = setInterval(function () { _this._loop(); }, this.options.pollInterval * 1000); return this; }; /** * Stops polling. * * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype.stop = function () { this.log('forcefully stopping polling'); clearInterval(this._loopIntervalId); this._loopIntervalId = 0; }; /** * The internal loop. * * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype._loop = function () { return __awaiter(this, void 0, void 0, function () { var _streamDataJson, streamRequestData_1, started, stopped, e_3; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 3, , 4]); if (!this.tracked.size) return [3 /*break*/, 2]; return [4 /*yield*/, this.streams({ user_login: Array.from(this.tracked) })]; case 1: _streamDataJson = _a.sent(); streamRequestData_1 = _streamDataJson; started = streamRequestData_1.data .filter(function (current) { return _this._cachedStreamData.filter(function (other) { return other.user_name === current.user_name; }).length == 0; }); stopped = this._cachedStreamData .filter(function (current) { return streamRequestData_1.data.filter(function (other) { return other.user_name === current.user_name; }).length == 0; }); if (started.length) { this.log(started.length + " new streams"); started.forEach(function (startedStream) { return _this._announce(startedStream); }); } if (stopped.length) { this.log(stopped.length + " stopped streams"); stopped.forEach(function (stoppedStream) { return _this._offline(stoppedStream.user_name); }); } this._cachedStreamData = streamRequestData_1.data; return [2 /*return*/, started]; case 2: return [3 /*break*/, 4]; case 3: e_3 = _a.sent(); // unauthorized if (e_3.message.includes('401')) { this.emit('error', Error('Twitch returned with an Unauthorized response. Your client_id probably wrong. Stopping.')); } else { this.emit('error', e_3); } this.stop(); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; /** * Emit an event when a stream starts * @fires TwitchOnlineTracker#started * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype._announce = function (streamData) { /** * @event TwitchOnlineTracker#live * @param {StreamData} The stream that has started */ this.emit('live', streamData); }; /** * Emit an event when a stream stops * @fires TwitchOnlineTracker#offline * @param {string} channelName the channel name of the stream that has stopped * @memberof TwitchOnlineTracker */ TwitchOnlineTracker.prototype._offline = function (channelName) { /** * @event TwitchOnlineTracker#offline * @param {string} The stream that has stopped */ this.emit('offline', channelName); }; return TwitchOnlineTracker; }(EventEmitter)); exports.TwitchOnlineTracker = TwitchOnlineTracker;