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
JavaScript
"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;