UNPKG

nstg

Version:

Sends telegrams to a list of NationStates nations defined using a powerful query language called Telegram Recipient Language

643 lines (642 loc) 25.3 kB
"use strict"; /** * Copyright (C) 2016-2017 Auralia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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 = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [0, 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 trl_1 = require("./trl"); exports.ParseError = trl_1.ParseError; var clone = require("clone"); /** * Rules for when to override API caching when re-evaluating a TRL string * during a refresh. */ var RefreshOverrideCache = /** @class */ (function () { function RefreshOverrideCache() { } return RefreshOverrideCache; }()); exports.RefreshOverrideCache = RefreshOverrideCache; /** * Sends telegrams to a list of NationStates nations defined using a powerful * query language called Telegram Recipient Language. */ var NsTgApi = /** @class */ (function () { /** * Initializes a new instance of the TelegramApi class. * * @param api The NationStates API instance used by this API. Only this API * should use this instance. * @param clientKey The telegram client key used by this API. * @param refreshRateSecs The number of seconds between refreshes for a * telegram job with the refresh option enabled. * Defaults to 60. */ function NsTgApi(api, clientKey, refreshRateSecs) { if (refreshRateSecs === void 0) { refreshRateSecs = 60; } var _this = this; this._api = api; this._clientKey = clientKey; this._onJobStart = function () { /* Do nothing. */ }; this._onTgSuccess = function () { /* Do nothing. */ }; this._onTgFailure = function () { /* Do nothing. */ }; this._onJobComplete = function () { /* Do nothing. */ }; this._onNewRecipients = function () { /* Do nothing. */ }; this._tgJobs = {}; this._tgQueue = []; this._tgInterval = setInterval(function () { if (_this.tgInProgress || _this._tgQueue.length === 0 || _this._blockExistingTelegrams) { return; } var recipient = _this._tgQueue.shift(); _this._tgInProgress = true; _this.sendTelegramWithCallbacks(recipient); }, 0); this._tgInProgress = false; this._refreshRateSecs = refreshRateSecs; this._tgRefreshInterval = setInterval(function () { if (_this._blockNewTelegrams) { return; } var _loop_1 = function (jobId) { if (!_this._tgJobs.hasOwnProperty(jobId)) { return "continue"; } var job = _this._tgJobs[jobId]; if (!job.refresh || job.status.isComplete) { return "continue"; } trl_1.getRecipients(_this.api, job.trl, job.refreshOverrideCache) .then(function (nations) { var recipients = nations.filter(function (nation) { for (var _i = 0, _a = job.recipients; _i < _a.length; _i++) { var recipient = _a[_i]; if (recipient.nation === nation) { return false; } } return true; }).map(function (nation) { return Object.freeze({ nation: nation, jobId: jobId, status: {} }); }); for (var _i = 0, recipients_1 = recipients; _i < recipients_1.length; _i++) { var recipient = recipients_1[_i]; job.recipients.push(recipient); _this._tgQueue.push(recipient); } if (recipients.length > 0) { _this._onNewRecipients(job.id, recipients); } }); }; for (var jobId in _this._tgJobs) { _loop_1(jobId); } }, this.refreshRateSecs * 1000); this._blockExistingTelegrams = false; this._blockNewTelegrams = false; this._cleanup = false; this._jobIdCounter = 1; } Object.defineProperty(NsTgApi.prototype, "api", { /** * Gets the NationStates API instance used by this API. */ get: function () { return this._api; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "clientKey", { /** * Gets the telegram client key used by this API. */ get: function () { return this._clientKey; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "onJobStart", { /** * Gets the event handler called when the API begins sending telegrams for * a particular job. */ get: function () { return this._onJobStart; }, /** * Sets the event handler called when the API begins sending telegrams for * a particular job. * * @param onStart The new event handler. */ set: function (onStart) { this._onJobStart = onStart; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "onNewRecipients", { /** * Gets the event handler called when new recipients are added to a job. */ get: function () { return this._onNewRecipients; }, /** * Sets the event handler called when new recipients are added to a job. * * @param onNewRecipients The new event handler. */ set: function (onNewRecipients) { this._onNewRecipients = onNewRecipients; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "onTgSuccess", { /** * Gets the event handler called when the API successfully sends a telegram * to a recipient. */ get: function () { return this._onTgSuccess; }, /** * Sets the event handler called when the API successfully sends a telegram * to a recipient. * * @param onTgSuccess The new event handler. */ set: function (onTgSuccess) { this._onTgSuccess = onTgSuccess; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "onTgFailure", { /** * Gets the event handler called when the API fails to send a telegram * to a recipient. */ get: function () { return this._onTgFailure; }, /** * Sets the event handler called when the API fails to send a telegram * to a recipient. * * @param onTgFailure The new event handler. */ set: function (onTgFailure) { this._onTgFailure = onTgFailure; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "onJobComplete", { /** * Gets the event handler called when the API finishes sending telegrams for * a particular job. */ get: function () { return this._onJobComplete; }, /** * Sets the event handler called when the API finishes sending telegrams for * a particular job. * * @param onJobComplete The new event handler. */ set: function (onJobComplete) { this._onJobComplete = onJobComplete; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "refreshRateSecs", { /** * Gets the number of seconds between refreshes for a telegram job with the * refresh option enabled. */ get: function () { return this._refreshRateSecs; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "blockExistingTelegrams", { /** * Gets whether or not existing telegrams in the queue are blocked from * being sent. */ get: function () { return this._blockExistingTelegrams; }, /** * If set to true, blocks the API from sending any further telegrams. If * set to false, normal operation will resume. * * @param blockExistingTelegrams Whether or not existing telegrams in the * queue should be blocked from being sent. */ set: function (blockExistingTelegrams) { this._blockExistingTelegrams = blockExistingTelegrams; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "blockNewTelegrams", { /** * Gets whether or not new telegrams are blocked from being added to the * queue. */ get: function () { return this._blockNewTelegrams; }, /** * If set to true, prevents any new telegrams from being added to the queue. * If set to false, normal operation will resume. * * @param blockNewTelegrams Whether or not new telegrams should be blocked * from being added to the queue. */ set: function (blockNewTelegrams) { this._blockNewTelegrams = blockNewTelegrams; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "tgInProgress", { /** * Gets whether or not this API is currently sending telegrams. */ get: function () { return this._tgInProgress; }, enumerable: true, configurable: true }); Object.defineProperty(NsTgApi.prototype, "tgQueued", { /** * Gets whether or not telegrams are queued. */ get: function () { return this._tgQueue.length !== 0; }, enumerable: true, configurable: true }); /** * Cancels all requests in the API queue. */ NsTgApi.prototype.clearQueue = function () { while (this._tgQueue.length > 0) { var recipient = this._tgQueue.pop(); this.recipientFailure(recipient, new Error("API queue cleared")); } }; /** * Cancels all requests in the telegram queue and turns off the API * scheduler. * * After this function is called, no further telegrams can be sent using * this API instance, including telegrams currently in the queue. */ NsTgApi.prototype.cleanup = function () { clearInterval(this._tgInterval); clearInterval(this._tgRefreshInterval); this.clearQueue(); this._cleanup = true; }; /** * Gets the telegram job with the specified ID. * * @param id The telegram job ID. * * @return The telegram job with the specified ID. */ NsTgApi.prototype.getJob = function (id) { return this._tgJobs[id]; }; /** * Cancels the job with the specified ID. * * @param id The ID of the job to cancel. */ NsTgApi.prototype.cancelJob = function (id) { var job = this._tgJobs[id]; if (typeof job !== "undefined") { for (var _i = 0, _a = job.recipients; _i < _a.length; _i++) { var recipient = _a[_i]; if (typeof recipient.status.success === "undefined") { recipient.status.success = false; recipient.status.err = new Error("Job cancelled"); var index = this._tgQueue.indexOf(recipient); if (index !== -1) { this._tgQueue.splice(index, 1); } } } job.status.isComplete = true; this.onJobComplete(job.id); } }; /** * Throws an error if the specified TRL string is not valid. * * @param trl A TRL string. */ NsTgApi.validateTrl = function (trl) { return trl_1.validateTrl(trl); }; /** * Parses and evaluates a TRL string. * * @param trl A TRL string. * * @return A promise returning the nations represented by the specified TRL * string. */ NsTgApi.prototype.evaluateTrl = function (trl) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, trl_1.getRecipients(this.api, trl)]; case 1: return [2 /*return*/, _a.sent()]; } }); }); }; /** * Sends telegrams to the recipients defined by the specified template * recipient language string. * * @param trl The TRL string. * @param tgInfo Information about the telegram to send. * @param refresh Whether the list of recipients should be refreshed by * re-evaluating the TRL string at periodic intervals. * Defaults to false. * @param refreshOverrideCache Rules for when to override API caching when * re-evaluating a TRL string during a refresh. * By default, all primitives override caches * except categories and census. * @param dryRun Whether to not actually send any telegrams to the * specified recipients. Defaults to false. * * @return A promise returning the ID of the telegram job associated with * this request. */ NsTgApi.prototype.sendTelegramsTrl = function (trl, tgInfo, refresh, refreshOverrideCache, dryRun) { if (refresh === void 0) { refresh = false; } if (refreshOverrideCache === void 0) { refreshOverrideCache = { overrideRegions: true, overrideTags: true, overrideWa: true, overrideNew: true, overrideRefounded: true, overrideCategories: false, overrideCensus: false }; } if (dryRun === void 0) { dryRun = false; } return __awaiter(this, void 0, void 0, function () { var _tgInfo, job, _i, _a, recipient; return __generator(this, function (_b) { switch (_b.label) { case 0: if (this.blockNewTelegrams) { throw new Error("New telegram requests are being blocked"); } if (this._cleanup) { throw new Error("API is shut down"); } _tgInfo = Object.freeze(clone(tgInfo)); return [4 /*yield*/, this.createJob(trl, _tgInfo, refresh, refreshOverrideCache, dryRun)]; case 1: job = _b.sent(); this._tgJobs[job.id] = job; for (_i = 0, _a = job.recipients; _i < _a.length; _i++) { recipient = _a[_i]; this._tgQueue.push(recipient); } this.onJobStart(job.id); job.status.isStarted = true; return [2 /*return*/, job.id]; } }); }); }; /** * Creates a job with the specified parameters. * * @param trl The TRL string. * @param tgInfo Information about the telegram to send. * @param refresh Whether the list of recipients should be refreshed by * re-evaluating the TRL string at periodic intervals. * @param refreshOverrideCache Rules for when to override API caching when * re-evaluating a TRL string during a refresh. * @param dryRun Whether to not actually send any telegrams to the * specified recipients. * * @return A promise returning the created telegram job. */ NsTgApi.prototype.createJob = function (trl, tgInfo, refresh, refreshOverrideCache, dryRun) { return __awaiter(this, void 0, void 0, function () { var jobId, nations, recipients, _i, nations_1, nation; return __generator(this, function (_a) { switch (_a.label) { case 0: jobId = String(this._jobIdCounter++); return [4 /*yield*/, trl_1.getRecipients(this.api, trl)]; case 1: nations = _a.sent(); recipients = []; for (_i = 0, nations_1 = nations; _i < nations_1.length; _i++) { nation = nations_1[_i]; recipients.push(Object.freeze({ nation: nation, jobId: jobId, status: {} })); } if (recipients.length === 0 && !refresh) { throw new Error("No recipients in job"); } return [2 /*return*/, Object.freeze({ id: jobId, trl: trl, recipients: recipients, tgInfo: tgInfo, refresh: refresh, refreshOverrideCache: refreshOverrideCache, dryRun: dryRun, status: { isStarted: false, isComplete: false } })]; } }); }); }; /** * Sends a telegram to the specified recipient. * * @param recipient The specified recipient. */ NsTgApi.prototype.sendTelegramWithCallbacks = function (recipient) { var _this = this; this.sendTelegram(recipient) .then(function () { return _this.recipientSuccess(recipient); }) .catch(function (err) { return _this.recipientFailure(recipient, err); }); }; /** * Sends a telegram to the specified recipient. * * @param recipient The specified recipient. */ NsTgApi.prototype.sendTelegram = function (recipient) { return __awaiter(this, void 0, void 0, function () { var job, data, data; return __generator(this, function (_a) { switch (_a.label) { case 0: job = this.getJob(recipient.jobId); if (typeof job === "undefined") { throw new Error("Job does not exist"); } if (!job.tgInfo.skipIfRecruitBlocked) return [3 /*break*/, 2]; return [4 /*yield*/, this.api.nationRequest(recipient.nation, ["tgcanrecruit"])]; case 1: data = _a.sent(); if (data["tgcanrecruit"] !== 1) { throw new Error("Nation has blocked" + " recruitment telegrams"); } _a.label = 2; case 2: if (!job.tgInfo.skipIfCampaignBlocked) return [3 /*break*/, 4]; return [4 /*yield*/, this.api.nationRequest(recipient.nation, ["tgcancampaign"])]; case 3: data = _a.sent(); if (data["tgcancampaign"] !== 1) { throw new Error("Nation has blocked campaign" + " telegrams"); } _a.label = 4; case 4: if (job.dryRun) { return [2 /*return*/]; } return [4 /*yield*/, this.api.telegramRequest(this.clientKey, job.tgInfo.telegramId, job.tgInfo.telegramKey, recipient.nation, job.tgInfo.telegramType)]; case 5: return [2 /*return*/, _a.sent()]; } }); }); }; /** * Called when a telegram is sent successfully to the specified recipient. * * @param recipient The specified recipient. */ NsTgApi.prototype.recipientSuccess = function (recipient) { this._tgInProgress = false; recipient.status.success = true; this.onTgSuccess(recipient); var job = this.getJob(recipient.jobId); if (job) { this.jobComplete(job); } }; /** * Called when an attempt is made to send a telegram to the specified * recipient that fails. * * @param recipient The specified recipient. * @param err The error associated with the failure. */ NsTgApi.prototype.recipientFailure = function (recipient, err) { this._tgInProgress = false; recipient.status.success = false; recipient.status.err = err; this.onTgFailure(recipient); var job = this.getJob(recipient.jobId); if (job) { this.jobComplete(job); } }; /** * Called when a recipient entry in the queue is processed in order to * determine if a job is complete. * * @param job The job associated with the recipient. */ NsTgApi.prototype.jobComplete = function (job) { if (!job.refresh) { for (var _i = 0, _a = job.recipients; _i < _a.length; _i++) { var recipient = _a[_i]; if (typeof recipient.status.success === "undefined") { return; } } job.status.isComplete = true; this.onJobComplete(job.id); } }; return NsTgApi; }()); exports.NsTgApi = NsTgApi;