UNPKG

nstg

Version:

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

797 lines (796 loc) 32.5 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 __extends = (this && this.__extends) || (function () { var 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 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 = 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 nsapi_1 = require("nsapi"); /** * Represents the action associated with a recipient command. */ var Action; (function (Action) { /** * Add the recipients to the current group. */ Action[Action["Add"] = 1] = "Add"; /** * Remove the recipients from the current group. */ Action[Action["Remove"] = 2] = "Remove"; /** * Remove all recipients from the current group that are not in this * list of recipients. */ Action[Action["Limit"] = 3] = "Limit"; })(Action = exports.Action || (exports.Action = {})); /** * Error thrown during TRL parsing. */ var ParseError = /** @class */ (function (_super) { __extends(ParseError, _super); /** * Initializes a new instance of the ParseError class. * * @param message The message associated with the error. * @param position A set of one-based indices that identify the location of * the command that caused the error within the original * TRL string. */ function ParseError(message, position) { var _this = _super.call(this, message) || this; _this.message = message; _this.position = position; return _this; } return ParseError; }(Error)); exports.ParseError = ParseError; /** * Parses and evaluates a TRL string using the specified API. * * @param api The specified API. * @param trl The specified TRL string. * @param refreshOverrideCache Rules for when to override API caching when * re-evaluating a TRL string in continuous mode. * * @return A promise returning the nations represented by the specified TRL * string. */ function getRecipients(api, trl, refreshOverrideCache) { if (refreshOverrideCache === void 0) { refreshOverrideCache = {}; } return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, evaluateTrl(api, parseTrl(trl), refreshOverrideCache)]; case 1: return [2 /*return*/, _a.sent()]; } }); }); } exports.getRecipients = getRecipients; /** * Throws an error if the specified TRL string is not valid. */ function validateTrl(trl) { parseGroup({ s: "(" + trl.trim() + ")" }, []); } exports.validateTrl = validateTrl; /** * Parses the specified TRL string into a list of recipient commands. */ function parseTrl(trl) { return parseGroup({ s: "(" + trl.trim() + ")" }, []); } /** * Parses the specified group string into a list of recipient commands. * * @param cxt The current parse context. * @param position The position of the specified group. * * @return A list of recipient commands. */ function parseGroup(cxt, position) { cxt.s = cxt.s.trim(); if (cxt.s.charAt(0) !== "(") { throw new ParseError("Expected '(' character", position); } cxt.s = cxt.s.substring(1); cxt.s = cxt.s.trim(); var commands = []; for (var i = 1; cxt.s.charAt(0) !== ")"; i++) { commands.push(parseCommand(cxt, position.concat([i]))); cxt.s = cxt.s.trim(); } cxt.s = cxt.s.substring(1); cxt.s = cxt.s.trim(); if (commands.length === 0) { throw new ParseError("Group must contain at least one" + " command", position); } return commands; } /** * Parses the specified command string into a recipient command. * * @param cxt The current parse context. * @param position The position of the specified group. * * @return A recipient command. */ function parseCommand(cxt, position) { cxt.s = cxt.s.trim(); var action = Action.Add; if (cxt.s.charAt(0) === "+" || cxt.s.charAt(0) === "-" || cxt.s.charAt(0) === "/") { if (cxt.s.charAt(0) === "-") { action = Action.Remove; } else if (cxt.s.charAt(0) === "/") { action = Action.Limit; } cxt.s = cxt.s.substring(1); } cxt.s = cxt.s.trim(); var recipients; if (cxt.s.charAt(0) === "(") { recipients = parseGroup(cxt, position); } else { recipients = parsePrimitive(cxt, position); } cxt.s = cxt.s.trim(); if (cxt.s.charAt(0) !== ";") { throw new ParseError("Expected ';' character", position); } cxt.s = cxt.s.substring(1); return { action: action, recipients: recipients, position: position }; } /** * Parses the specified primitive string into a recipient primitive. * * @param cxt The current parse context. * @param position The position of the specified group. * * @return A recipient primitive. */ function parsePrimitive(cxt, position) { cxt.s = cxt.s.trim(); var category; var match = cxt.s.match(/^(nations|regions|tags|wa|new|refounded|census|categories)/); if (match) { category = match[0]; cxt.s = cxt.s.substring(category.length); } else { throw new ParseError("Unrecognized category name", position); } cxt.s = cxt.s.trim(); if (cxt.s.charAt(0) !== "[") { throw new ParseError("Expected '[' character", position); } cxt.s = cxt.s.substring(1); if (cxt.s.indexOf("]") === -1) { throw new ParseError("List of arguments must be terminated by" + " ']' character", position); } var args = cxt.s .substring(0, cxt.s.indexOf("]")) .split(",") .map(function (str) { return str.trim(); }); if (category === "wa" && (args.length !== 1 || (args[0] !== "members" && args[0] !== "delegates"))) { throw new ParseError("Argument for 'wa' not 'members' or" + " 'delegates'", position); } if (category === "new" && (args.length !== 1 || isNaN(parseInt(args[0])))) { throw new ParseError("Argument for 'new' not a number", position); } if (category === "refounded" && (args.length !== 1 || isNaN(parseInt(args[0])))) { throw new ParseError("Argument for 'new' not a number", position); } if (category === "census" && (args.length !== 3 || isNaN(parseInt(args[0])) || isNaN(parseInt(args[1])) || isNaN(parseInt(args[2])))) { throw new ParseError("Arguments for 'census' not three" + " integers", position); } cxt.s = cxt.s.substring(cxt.s.indexOf("]") + 1); return { category: category, args: args }; } /** * Evaluates the specified recipient commands using the specified API. * * @param api The specified API. * @param commands The specified recipient commands. * @param refreshOverrideCache Rules for when to override API caching when * re-evaluating a TRL string in continuous mode. * * @return A promise returning the list of nations that the specified TRL string * evaluates to. */ function evaluateTrl(api, commands, refreshOverrideCache) { return __awaiter(this, void 0, void 0, function () { var nations; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, evaluateGroup(api, commands, refreshOverrideCache)]; case 1: nations = _a.sent(); return [2 /*return*/, nations.filter(function (nation, index) { return nations.indexOf(nation) === index; })]; } }); }); } exports.evaluateTrl = evaluateTrl; /** * Evaluates the specified group recipient commands using the specified API. * * @param api The specified API. * @param commands The specified recipient commands. * @param refreshOverrideCache Rules for when to override API caching when * re-evaluating a TRL string in continuous mode. * * @return A promise returning the list of nations that the specified commands * evaluate to. */ function evaluateGroup(api, commands, refreshOverrideCache) { return __awaiter(this, void 0, void 0, function () { var nations, _i, commands_1, command; return __generator(this, function (_a) { switch (_a.label) { case 0: nations = []; _i = 0, commands_1 = commands; _a.label = 1; case 1: if (!(_i < commands_1.length)) return [3 /*break*/, 4]; command = commands_1[_i]; return [4 /*yield*/, evaluateCommand(api, command, nations, refreshOverrideCache)]; case 2: nations = _a.sent(); _a.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/, nations]; } }); }); } /** * Evaluates the specified recipient command using the specified API. * * @param api The specified API. * @param command The specified recipient command. * @param nations The list of nations in the current group. * @param refreshOverrideCache Rules for when to override API caching when * re-evaluating a TRL string in continuous mode. * * @return A promise returning a new list of nations for the current group. */ function evaluateCommand(api, command, nations, refreshOverrideCache) { return __awaiter(this, void 0, void 0, function () { var newNations; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!(command.recipients instanceof Array)) return [3 /*break*/, 2]; return [4 /*yield*/, evaluateGroup(api, command.recipients, refreshOverrideCache)]; case 1: newNations = _a.sent(); return [2 /*return*/, evaluateAction(command.action, nations, newNations)]; case 2: return [4 /*yield*/, evaluatePrimitive(api, command.recipients, command.action, nations, refreshOverrideCache)]; case 3: return [2 /*return*/, _a.sent()]; } }); }); } /** * Evaluates the specified primitive with the specified action using the * specified API. * * @param api The specified API. * @param primitive The specified primitive. * @param action The specified action. * @param nations The nations associated with the current group. * @param refreshOverrideCache Rules for when to override API caching when * re-evaluating a TRL string in continuous mode. * * @return A promise returning a new list of nations for the current group. */ function evaluatePrimitive(api, primitive, action, nations, refreshOverrideCache) { return __awaiter(this, void 0, void 0, function () { var newNations, _a, count, count, newNations_1, _b, _i, nations_1, nation, category, err_1, _c, nations_2, nation, score, err_2; return __generator(this, function (_d) { switch (_d.label) { case 0: if (!(primitive.category === "nations" || primitive.category === "regions" || primitive.category === "tags" || primitive.category === "wa" || primitive.category === "new" || primitive.category === "refounded")) return [3 /*break*/, 18]; newNations = []; _a = primitive.category; switch (_a) { case "nations": return [3 /*break*/, 1]; case "regions": return [3 /*break*/, 3]; case "tags": return [3 /*break*/, 5]; case "wa": return [3 /*break*/, 7]; case "new": return [3 /*break*/, 12]; case "refounded": return [3 /*break*/, 14]; } return [3 /*break*/, 16]; case 1: return [4 /*yield*/, getNations(primitive.args)]; case 2: newNations = _d.sent(); return [3 /*break*/, 17]; case 3: return [4 /*yield*/, getRegions(api, primitive.args, refreshOverrideCache.overrideRegions)]; case 4: newNations = _d.sent(); return [3 /*break*/, 17]; case 5: return [4 /*yield*/, getTags(api, primitive.args, refreshOverrideCache.overrideTags)]; case 6: newNations = _d.sent(); return [3 /*break*/, 17]; case 7: if (!(primitive.args[0] === "members")) return [3 /*break*/, 9]; return [4 /*yield*/, getWorldAssemblyMembers(api, refreshOverrideCache.overrideWa)]; case 8: newNations = _d.sent(); return [3 /*break*/, 11]; case 9: return [4 /*yield*/, getWorldAssemblyDelegates(api, refreshOverrideCache.overrideWa)]; case 10: newNations = _d.sent(); _d.label = 11; case 11: return [3 /*break*/, 17]; case 12: count = parseInt(primitive.args[0]); return [4 /*yield*/, getNewNations(api, count, refreshOverrideCache.overrideNew)]; case 13: newNations = _d.sent(); return [3 /*break*/, 17]; case 14: count = parseInt(primitive.args[0]); return [4 /*yield*/, getRefoundedNations(api, count, refreshOverrideCache.overrideRefounded)]; case 15: newNations = _d.sent(); return [3 /*break*/, 17]; case 16: throw new Error("Unexpected category"); case 17: return [2 /*return*/, evaluateAction(action, nations, newNations)]; case 18: newNations_1 = []; _b = primitive.category; switch (_b) { case "categories": return [3 /*break*/, 19]; case "census": return [3 /*break*/, 26]; } return [3 /*break*/, 33]; case 19: _i = 0, nations_1 = nations; _d.label = 20; case 20: if (!(_i < nations_1.length)) return [3 /*break*/, 25]; nation = nations_1[_i]; _d.label = 21; case 21: _d.trys.push([21, 23, , 24]); return [4 /*yield*/, getCategory(api, nation, refreshOverrideCache.overrideCategories)]; case 22: category = _d.sent(); if (primitive.args.indexOf(category) !== -1) { newNations_1.push(nation); } return [3 /*break*/, 24]; case 23: err_1 = _d.sent(); if (err_1 instanceof nsapi_1.ApiError) { if (err_1.responseText && err_1.responseText .indexOf("Unknown nation \"" + nation + "\".")) { return [3 /*break*/, 24]; } } throw err_1; case 24: _i++; return [3 /*break*/, 20]; case 25: return [3 /*break*/, 34]; case 26: _c = 0, nations_2 = nations; _d.label = 27; case 27: if (!(_c < nations_2.length)) return [3 /*break*/, 32]; nation = nations_2[_c]; _d.label = 28; case 28: _d.trys.push([28, 30, , 31]); return [4 /*yield*/, getCensusScore(api, nation, primitive.args[0], refreshOverrideCache.overrideCensus)]; case 29: score = _d.sent(); if (score >= parseFloat(primitive.args[1]) && score <= parseFloat(primitive.args[2])) { newNations_1.push(nation); } return [3 /*break*/, 31]; case 30: err_2 = _d.sent(); if (err_2 instanceof nsapi_1.ApiError) { if (err_2.responseText && err_2.responseText .indexOf("Unknown nation \"" + nation + "\".")) { return [3 /*break*/, 31]; } } throw err_2; case 31: _c++; return [3 /*break*/, 27]; case 32: return [3 /*break*/, 34]; case 33: throw new Error("Unexpected category"); case 34: switch (action) { case Action.Remove: return [2 /*return*/, nations.filter(function (item) { return newNations_1.indexOf(item) === -1; })]; case Action.Limit: return [2 /*return*/, newNations_1]; default: throw new Error("Unexpected action"); } _d.label = 35; case 35: return [2 /*return*/]; } }); }); } /** * Evaluates the specified action with a list of the current nations in the * group and a list of the nations associated with the most recently processed * command. * * @param action The specified action. * @param nations The current nations in the group. * @param newNations The nations associated with the current command. * * @return The new list of nations for the current group. */ function evaluateAction(action, nations, newNations) { switch (action) { case Action.Add: return nations.concat(newNations); case Action.Remove: return nations.filter(function (item) { return newNations.indexOf(item) === -1; }); case Action.Limit: return nations.filter(function (item) { return newNations.indexOf(item) !== -1; }); default: throw new Error("Unrecognized action"); } } /** * Gets a list of nations. * * @param args The list of nations in raw form. * * @return A promise returning the list of nations. */ function getNations(args) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, args.map(toId)]; }); }); } /** * Gets the nations in a list of regions using the specified API. * * @param api The specified API. * @param regions The list of regions. * @param overrideCache Whether or not to override the cache for this request. * * @return A promise returning the list of nations. */ function getRegions(api, regions, overrideCache) { if (overrideCache === void 0) { overrideCache = false; } return __awaiter(this, void 0, void 0, function () { var nations, _i, regions_1, region, data, nationsInRegions, _a, nationsInRegions_1, nationsInRegion; return __generator(this, function (_b) { switch (_b.label) { case 0: nations = []; _i = 0, regions_1 = regions; _b.label = 1; case 1: if (!(_i < regions_1.length)) return [3 /*break*/, 4]; region = regions_1[_i]; return [4 /*yield*/, api.regionRequest(region, ["nations"], undefined, overrideCache)]; case 2: data = _b.sent(); nationsInRegions = data["nations"].split(":").map(toId); for (_a = 0, nationsInRegions_1 = nationsInRegions; _a < nationsInRegions_1.length; _a++) { nationsInRegion = nationsInRegions_1[_a]; nations = nations.concat(nationsInRegion); } _b.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/, nations]; } }); }); } /** * Gets the nations in all regions with the specified tags using the specified * API. * * @param api The specified API. * @param args The list of tags. * @param overrideCache Whether or not to override the cache for this request. * * @return A promise returning the list of nations. */ function getTags(api, args, overrideCache) { if (overrideCache === void 0) { overrideCache = false; } return __awaiter(this, void 0, void 0, function () { var data, regions; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, api.worldRequest(["regionsbytag"], { tags: args.join(",") }, overrideCache)]; case 1: data = _a.sent(); regions = data["regions"].split(","); return [2 /*return*/, getRegions(api, regions, overrideCache)]; } }); }); } /** * Gets all World Assembly member states using the specified API. * * @param api The specified API. * @param overrideCache Whether or not to override the cache for this request. * * @return A promise returning the list of nations. */ function getWorldAssemblyMembers(api, overrideCache) { if (overrideCache === void 0) { overrideCache = false; } return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, api.worldAssemblyRequest(nsapi_1.WorldAssemblyCouncil.GeneralAssembly, ["members"], undefined, overrideCache)]; case 1: data = _a.sent(); return [2 /*return*/, data["members"].split(",").map(toId)]; } }); }); } /** * Gets all World Assembly delegates using the specified API. * * @param api The specified API. * @param overrideCache Whether or not to override the cache for this request. * * @return A promise returning the list of nations. */ function getWorldAssemblyDelegates(api, overrideCache) { if (overrideCache === void 0) { overrideCache = false; } return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, api.worldAssemblyRequest(nsapi_1.WorldAssemblyCouncil.GeneralAssembly, ["delegates"], undefined, overrideCache)]; case 1: data = _a.sent(); return [2 /*return*/, data["delegates"].split(",").map(toId)]; } }); }); } /** * Gets the specified number of new nations using the specified API. * * @param api The specified API. * @param count The number of new nations to retrieve. * @param overrideCache Whether or not to override the cache for this request. * * @return A promise returning the list of nations. */ function getNewNations(api, count, overrideCache) { if (overrideCache === void 0) { overrideCache = false; } return __awaiter(this, void 0, void 0, function () { var data, event; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, api.worldRequest(["happenings"], { filter: "founding", limit: String(count) }, overrideCache)]; case 1: data = _a.sent(); event = data["happenings"]["event"]; if (!(event instanceof Array)) { event = [event]; } return [2 /*return*/, event .filter(function (event) { return event["text"].indexOf("was founded") !== -1; }) .map(function (event) { var start = event["text"].indexOf("@@") + 2; if (start !== -1) { var end = start + event["text"].substring(start) .indexOf("@@"); if (end !== -1) { return event["text"].substring(start, end); } } return ""; }) .filter(function (nation) { return nation !== ""; })]; } }); }); } /** * Gets the specified number of refounded nations using the specified API. * * @param api The specified API. * @param count The number of refounded nations to retrieve. * @param overrideCache Whether or not to override the cache for this request. * * @return A promise returning the list of nations. */ function getRefoundedNations(api, count, overrideCache) { if (overrideCache === void 0) { overrideCache = false; } return __awaiter(this, void 0, void 0, function () { var data, event; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, api.worldRequest(["happenings"], { filter: "founding", limit: String(count) }, overrideCache)]; case 1: data = _a.sent(); event = data["happenings"]["event"]; if (!(event instanceof Array)) { event = [event]; } return [2 /*return*/, event .filter(function (event) { return event["text"].indexOf("was refounded") !== -1; }) .map(function (event) { var start = event["text"].indexOf("@@") + 2; if (start !== -1) { var end = start + event["text"].substring(start) .indexOf("@@"); if (end !== -1) { return event["text"].substring(start, end); } } return ""; }) .filter(function (nation) { return nation !== ""; })]; } }); }); } /** * Gets the category of the specified nation using the specified API. * * @param api The specified API. * @param nation The specified nation. * @param overrideCache Whether or not to override the cache for this request. * * @return A promise returning the list of nations. */ function getCategory(api, nation, overrideCache) { if (overrideCache === void 0) { overrideCache = false; } return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, api.nationRequest(nation, ["category"], {}, undefined, overrideCache)]; case 1: data = _a.sent(); return [2 /*return*/, data["category"]]; } }); }); } /** * Gets the score for the specified census ID for the specified nation using * the specified API. * * @param api The specified API. * @param nation The specified nation. * @param censusId The specified census ID. * @param overrideCache Whether or not to override the cache for this request. * * @return A promise returning the list of nations. */ function getCensusScore(api, nation, censusId, overrideCache) { if (overrideCache === void 0) { overrideCache = false; } return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, api.nationRequest(nation, ["census"], { scale: censusId }, undefined, overrideCache)]; case 1: data = _a.sent(); return [2 /*return*/, data["census"]["scale"]["score"]]; } }); }); } /** * Converts nation names to a fixed form: all lowercase, with spaces replaced * with underscores. * * @param nation The nation name to convert. * * @return The converted nation name. */ function toId(nation) { return nation.replace("_", " ") .trim() .toLowerCase() .replace(" ", "_"); }