const Emitter = require("events").EventEmitter;
const fs = require("fs");
const path = require("path");
const request = require("request-promise");
const url = require("url");
const Socket = require("ws");
const zlib = require("zlib");
const DEBUG = false;
/**
* Class representing the API.
*
* Properties and functions begining with an underscore are low level objects exposed for
* users who know what they are doing and *should generally not be used*.
*
* They are provided and documented for some specific situations like when you need to extent the API.
*
* For information, tedivm has make a good summary of screeps server api detail on
* [python-screeps]( https://github.com/screepers/python-screeps/blob/master/docs/Endpoints.md)
* @extends EventEmitter
*/
class API extends Emitter {
/**
* Event broadcasted every tick (if subscribed) with user CPU and memory data.
* @event API#cpu
* @property {object} messages
* @property {array} messages.log - Lines shown in console (like if printed by `console.log()`)
* @property {array} messages.results - Array of console results
* @example
* // Script:
* api.on("cpu", (data) => console.log(JSON.stringify(data)));
* @example
* // Results:
* { "cpu": 32, "memory": 126435 }
*/
/**
* Event broadcasted every tick (if subscribed) with exectued commands and results.
* @event API#console
* @property {number} cpu - CPU used last tick
* @property {number} memory - Memory currently used
* @example
* // Script:
* api.on("console", (data) => console.log(JSON.stringify(data)));
* @example
* // Results: after executing `Game.time` in game:
* { "messages": { "log": [], "results": ["16746996"] } }
* // (`Game.time` does not show any log in the console, which is why `messages.log` is empty)
*/
/**
* Account email.
* @type {string}
*/
get _email() { return this.data.email; }
set _email(v) { this.data.email = v; }
/**
* Account password.
* @type {string}
*/
get _password() { throw new Error("Getting password is not allowed"); }
set _password(v) { this.data.password = v; }
/**
* Game server address including protocole and port.
* @type {string}
*/
// Game server
get _server() { return this.data.server; }
set _server(v) { this.data.server = v; }
/**
* Authorization token (sent after authentication by the server).
* @type {string}
*/
get _token() { return this.data.token; }
set _token(v) { this.data.token = v; }
/**
* API constructor. <br>
* Required the user credentials in order to connect to the server (but connection occurs later on).
* @constructor
* @param {string} email - Account email address
* @param {string} password - Account password
* @param {string} [server=http://screeps.com] - Server adress (ex: `http://127.0.0.1:21025`)
*/
constructor(email, password, server) {
super();
// Check parameters
if(email === undefined || password === undefined) {
throw new Error("Email and password are requried");
}
// Save authentication data
this.data = {
email: email,
password: password,
server: server || "https://screeps.com",
token: null, // authentication token
user: null // user data used for web sockets
};
// Create websocket pointer
this.socket = null;
}
/**
* Authenticate with the server in HTTP and get/save session token.
* @example <caption>Initialization and authententication:</caption>
* let API = require("screeps-js-api");
* new API("user@maiL.com", "password");
* api.authenticate();
* @return {Promise} Return a promise eventually resolved with autorization token given by the server.
*/
_authenticate() {
if(DEBUG) console.log("[Debug] Authenticating...");
return this._req("/api/auth/signin", "POST", {
email: this.data.email,
password: this.data.password
}).then((body) => {
if(body.token === undefined) throw new Error("No authorization token in authentication response.");
this.data.token = body.token;
return this.data.token;
});
}
/**
* Send HTTP request to provided endpoint.
* Return a promise eventually resolved with response body.
* @private
* @param {string} endpoint Endpoint path begining with "/" (like "/api/auth/signin")
* @param {string} method Request HTTP mathode (GET or POST, defaults to GET)
* @param {Object} params Parameters of the request (either in the POST body or in url for GETs)
* @return {Promise} Promise eventually resolved when message is sent.
*/
_req(endpoint, method, params) {
if(this.data.token === null && ! endpoint.match("auth/signin")) {
return this._authenticate().then(() => this._req(endpoint, method, params));
}
if(DEBUG) console.log(`[Debug] ${method} request to ${endpoint}...`);
return request({
body: (method === "POST") ? params : undefined,
headers: {
"Content-Type": "application/json; charset=utf-8",
"X-Token": this.data.token,
"X-Username": this.data.token
},
json: true,
method: method || "GET",
qs: (method === "GET") ? params : undefined,
resolveWithFullResponse: true,
simple: false,
uri: url.resolve(this.data.server, endpoint),
}).then((res) => {
if(res.statusCode === 401) {
// Unauthorized: token expired or incorrect login
this.data.token = null;
if(endpoint.match("auth/signin")) throw new Error("Unauthorized, email or password is probably incorrect");
else return this._req(endpoint, method, params);
} else if(res.statusCode === 200) {
return this._parseHttp(res.body);
} else {
throw new Error(`Error, server answered with HTTP ${res.statusCode} error code: ${JSON.stringify(res.body)}`);
}
});
}
/**
* Parse HTTP response.
* Return a promise eventually resolved with response data as JS object.
* @private
* @param {Object} body HTTP body as read by JSON.stringify()
* @return {Promise} Promise eventually resolved when parsing is finished.
*/
_parseHttp(body) {
if(body.error) throw new Error(`The server returned an error: ${body.error}`);
if(typeof body.data === "string" && body.data.slice(0, 3) === "gz:") {
return this._unzip(body.data.slice(3)).then((data) => ({ data: JSON.parse(data) }));
} else {
return Promise.resolve(body);
}
}
/**
* Uncompressed message using GZip or deflate algorithms.
* @private
* @param {string} message - The message to unzip.
* @return {Promise} Promise eventually resolved with uncompressed contents.
*/
_unzip(message) {
return new Promise((resolve, reject) => {
let buf = new Buffer(message, "base64");
// 1f8b is GZip magic number (https://en.wikipedia.org/wiki/Gzip)
if(buf.toString("hex").substr(0,4) === "1f8b") {
zlib.gunzip(buf, (err, data) => {
if(err) reject(err);
else resolve(data.toString());
});
} else {
zlib.inflate(buf, (err, data) => {
if(err) reject(err);
else resolve(data.toString());
});
}
});
}
/**
* Connect to server web socket.
* @example
* api._connect();
* @return {Promise} Promise eventually resolved when connection is confirmed and user authenticated.
*/
_connect() {
let p = Promise.resolve();
if(this.socket !== null) return p;
if(this.data.token === null) p = p.then(() => this._authenticate());
return p.then(() => new Promise((resolve, reject) => {
if(DEBUG) console.log("[Debug] Connecting socket...");
// Open socket
let server = this.data.server.replace(/^http/, "ws");
this.socket = new Socket(`${server}/socket/websocket`);
// Open event
this.socket.on("open", () => {
Promise.resolve()
.then(() => this._send(`gzip on`))
.then(() => this._send(`auth ${this.data.token}`))
.catch((err) => reject(err));
});
// Message event
this.socket.on("message", (msg) => {
if(msg.match(/auth ok/i)) resolve("ok");
if(msg.match(/auth failed/i)) reject(new Error("Socket authentication failed"));
this._parseSocket(msg);
});
// Error event
this.socket.on("error", (err) => {
this.emit("error", `Socket error: ${err}`);
});
// Close event
this.socket.on("close", (code, reason) => {
this.socket = null;
this.emit("error", `Socket closed (${code}): ${reason}`);
});
}));
}
/**
* Disconnect the web socket (if connected).
* @example
* api._disconnect();
* @return {Promise} Promise eventually resolved when disconnection is confirmed.
*/
_disconnect() {
let p = Promise.resolve();
if(this.socket === null) return p;
return p.then(() => new Promise((resolve, reject) => {
if(DEBUG) console.log("[Debug] Disconnecting socket...");
// Remove existing listeners
this.socket.removeAllListeners();
// Listen for actual close
this.socket.once("close", (code, reason) => resolve());
// Initiate closing handshake.
this.socket.close();
this.socket = null;
}));
}
/**
* Send a message via web socket. <br>
* /!\ Do not check if the socket is connected first.
* @private
* @param {string} message The message to send
* @return {Promise} Promise eventually resolved when message is sent.
*/
_send(message) {
return new Promise((resolve, reject) => {
this.socket.send(message, (err) => {
if(err) reject(err);
else resolve();
});
});
}
/**
* Parse a socket message.
* - Emit a "raw_message" event with message plain text.
* - Emit an event of type 'endpoint' with data when applicable.
* @private
* @param {string} message The message to parse
* @return {Promise} Promise eventually resolved when parsing is finished.
*/
_parseSocket(message) {
let p = Promise.resolve(message);
// Unzip message
if(message.slice(0, 3) === "gz:") {
p = this._unzip(body.data.slice(3)).then((data) => ({ data: JSON.parse(data) }));
}
return p.then((message) => {
this.emit("raw_message", message);
// Actually parse message
let [endpoint, data] = JSON.parse(message);
endpoint = endpoint.replace(/user:[a-z0-9]+\//i, ""); // remove leading user id
this.emit(endpoint, data);
}).catch(() => { /* Not an error, but message is not valid */ });
}
/**
* Subscribe to the given socket path ("/console", "/memory", "/rom"...).
* @example
* api._subscribe.("/code");
* @param {string} path The path with a leading "/"
* @return {Promise} Promise eventually resolved when message is sent.
*/
_subscribe(path) {
let p = Promise.resolve();
if(this.socket === null) p = p.then(() => this._connect());
if(this.data.user === null) p = p.then(() => this.user.me());
return p.then(() => {
if(DEBUG) console.log(`[Debug] Subscribing to user:${this.data.user._id}${path}...`);
if(this.data.user === null) throw new Error("Invalid user data while trying to subscribe");
let request = `subscribe user:${this.data.user._id}${path}`;
return this._send(request);
});
}
/**
* Provide methods to get and set user code (see `code.push()` and `code.subscribe()` functions).
* @type {Object}
*/
get code() {
return {
/**
* Push code to the given branch on the server. <br>
* - All files are supposed to be UTF-8 encoded. <br>
* - Does not read folders, only files. <br>
* Query the `/api/user/code` server endpoint.
* @alias code.push
* @memberof! API#
* @example
* let api = new API(email, password, server);
* api.code.push(["file_1.js", "file_2.js"], "simulation");
* @param {array<string>} files - An array of file name to commit (any other file will be deleted on the server).
* @param {string} branch - The name of the branch to push code to.
* @return {Promise} Promise eventually resolved when upload finishes.
*/
push: (files, branch) => {
let p = files.map((file) => new Promise((resolve, reject) => {
fs.readFile(file, "utf-8", (err, contents) => {
if(err) reject(err);
else resolve([file, contents]);
});
}));
return Promise.all(p).then((files) => {
let data = { branch: branch, modules: {} };
for(let [file, contents] of files) {
let name = path.basename(file, ".js");
data.modules[name] = contents;
}
return this._req("/api/user/code", "POST", data);
});
},
/**
* Subscribe to the `/code` endpoint. <br>
* Api will then emit events when code is changed on the server.
* @alias code.subscribe
* @memberof! API#
* @example
* let api = new API(email, password, server);
* api.on("console", (data) => console.log(JSON.stringify(data)));
* api.code.subscribe().then(() => console.log("ok"));
* @fires API#code
* @return {Promise} Promise eventually resolved once message sent.
*/
subscribe: () => this._subscribe("/code")
};
}
/**
* Provide methods to execute and console commands and get results (see `console.execute()` and `console.subscribe()` functions).
* @type {Object}
*/
get console() {
return {
/**
* Send a console command to execute. <br>
* Query the `/api/user/console` server endpoint.
* @alias console.execute
* @memberof! API#
* @example
* let api = new API(email, password, server);
* api.console.execute("console.log('Hello world')");
* @param {string} command - The command to send (as a string).
* @return {Promise} Promise eventually resolved when command is sent.
*/
execute: (command) => this._req("/api/user/console", "POST", { expression: command }),
/**
* Subscribe to the `/console` endpoint. <br>
* Api will then emit events every tick with executed commands (even out of the API) and results.
* @alias console.subscribe
* @memberof! API#
* @example
* let api = new API(email, password, server);
* api.on("console", (data) => console.log(JSON.stringify(data)));
* api.console.subscribe().then(() => console.log("ok"));
* @fires API#console
* @return {Promise} Promise eventually resolved once message sent.
*/
subscribe: () => this._subscribe("/console")
};
}
/**
* Provide methods to access leaderboard statistics (see `leaderboard.xxx()` functions).
* @type {Object}
*/
get leaderboard() {
return {
/**
* Get a list of the game seasons (one season = one month). <br>
* Query the `/api/leaderboard/seasons` server endpoint.
* @alias leaderboard.seasons
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.leaderboard.seasons().then(JSON.stringify).then(console.log);
* @example
* // Results:
* [
* {"_id":"2017-01","name":"January 2017","date":"2017-01-01T00:00:03.681Z"},
* {"_id":"2016-12","name":"December 2016","date":"2016-12-01T00:00:05.105Z"},
* // ...
* @return {Promise} Promise eventually resolved with the seasons list.
*/
seasons: () => this._req("/api/leaderboard/seasons", "GET").then((body) => body.seasons),
/**
* Get user statistics for the given season. <br>
* Query the `/api/leaderboard/find` server endpoint.
* @alias leaderboard.find
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.leaderboard.find(USER_NAME, "2017-01").then(JSON.stringify).then(console.log);
* @example
* // Results:
* { "ok": 1, "_id":"586846a0ec0e632daa907bfb", season": "2017-01",
* "user":"00000000000000000000000", "score":1855700, "rank": 883 }
* @param {string} username - User name to look at.
* @param {string} season - Season in the following format: `YYYY-MM`.
* @return {Promise} Promise eventually resolved with the user stats.
*/
find: (username, season) => {
return this._req("/api/leaderboard/find", "GET", { mode: "world", username, season })
.then((body) => body);
},
/**
* Get the list of top players.
* Query the `/api/leaderboard/list` server endpoint.
* @alias leaderboard.list
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.leaderboard.list(10, 100, "2017-01").then(JSON.stringify).then(console.log);
* @example
* // Results:
* [ { "_id": "586846a0ec0e632daa907724", "season":"2017-01",
* "user":"5569aa587401fd4155440afa","score":34059838,"rank":100 },
* { "_id": "586846a0ec0e632daa9076d9", "season":"2017-01",
* "user":"579a8ce3ee9e0fb54b18cc4d", "score":33968274,"rank":101 },
* // ... (up to rank 110)
* @param {number} limit - Number of players to show.
* @param {number} offset - Offset to start at.
* @param {string} season - Season in the following format: `YYYY-MM`.
* @return {Promise} Promise eventually resolved with the list.
*/
list: (limit, offset, season) => {
return this._req("/api/leaderboard/list", "GET", { mode: "world", limit, offset, season })
.then((body) => body.list);
},
};
}
/**
* Provide methods to get game data time (see `time.get()` function).
* @type {Object}
*/
get time() {
return {
/**
* Get game time in ticks. <br>
* Query the `/api/game/time` server endpoint.
* @alias time.get
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.time.get().then(console.log);
* @example
* // Results:
* 16746363
* @return {Promise} Promise eventually resolved with the time as a number of ticks.
*/
get: () => this._req("/api/game/time", "GET").then((body) => body.time),
};
}
/**
* Provide methods to access memory (see `memory.get()` and `memory.set()` functions).
* @type {Object}
*/
get memory() {
return {
/**
* Get memory for requested path. <br>
* Query the `/api/user/memory` server endpoint.
* @alias memory.get
* @memberof! API#
* @example
* let api = new API(email, password, server);
* api.memory.get("stats.room.E1N1").then(JSON.stringify).then(console.log);
* @param {string} [path] - Memory path to get.
* @return {Promise} Promise eventually resolved with the memory contents.
*/
get: (path) => this._req("/api/user/memory", "GET", { path }).then((body) => body.data),
/**
* Set memory for requested path. <br>
* _Autenticate user if needed._ <br>
* Query the `/api/user/memory` server endpoint.
* @alias memory.set
* @memberof! API#
* @example
* let api = new API(email, password, server);
* api.memory.set("stats.room.E1N1", undefined).then(JSON.stringify).then(console.log);
* @param {string} path - Memory path to set.
* @param {Object|undefined} value - Object to set (or undefined to delete the path).
* @return {Promise} Promise eventually resolved once done.
*/
set: (path, value) => this._req("/api/user/memory", "POST", { path, value }).then((body) => body.data)
};
}
/**
* Provide methods to access market data and statistics (see `market.xxx()` functions).
* @type {Object}
*/
get market() {
return {
/**
* Get number of market order for each resourceType. <br>
* Query the `/api/game/market/orders-index` server endpoint.
* @alias market.index
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.market.index().then(JSON.stringify).then(console.log);
* @example
* // Results:
* [ { _id: 'GH2O', count: 1 },
* { _id: 'LH2O', count: 2 },
* { _id: 'XUHO2', count: 24 },
* // ...
* @return {Promise} Promise eventually resolved with an array of orders count (see example).
*/
index: () => this._req("/api/game/market/orders-index", "GET").then((body) => body.list),
/**
* Get the lsit of market orders for given resourceType. <br>
* Query the `/api/game/market/orders` server endpoint.
* @alias market.orders
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.market.orders("energy").then(JSON.stringify).then(console.log);
* @example
* // Results:
* [ { _id: '581c5a8c580572317d776c98', type: 'buy', remainingAmount: 259,
* amount: 259, price: 0.04, roomName: 'E1S54' },
* { _id: '581c5a8c580572317d776c9c', type: 'buy', amount: 592,
* remainingAmount: 592, price: 0.04, roomName: 'E3S54' },
* // ...
* @param {ResourceType} resourceType - Reserouce type to query (one of the game RESOURCE_* values).
* @return {Promise} Promise eventually resolved with an array of orders.
*/
orders: (resourceType) => this._req("/api/game/market/orders", "GET", { resourceType }).then((body) => body.list),
/**
* Get market statistics for given resourceType. <br>
* Query the `/api/game/market/stats` server endpoint.
* @alias market.stats
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.market.stats("energy").then(JSON.stringify).then(console.log);
* @example
* // Results:
* [ { _id: '587a7b197401dbc73aa28fb1', resourceType: 'energy', date: '2017-01-14',
* transactions: 2003, volume: 6407767, avgPrice: 0.06, stddevPrice: 0.02 },
* { _id: '587a7b197401dbc73aa28fb0', resourceType: 'energy', date: '2017-01-13',
* transactions: 3149, volume: 9359438, avgPrice: 0.06, stddevPrice: 0.02 },
* // ...
* @param {ResourceType} resourceType - Reserouce type to query (one of the game RESOURCE_* values).
* @return {Promise} Promise eventually resolved with an array of stats (one per day).
*/
stats: (resourceType) => this._req("/api/game/market/stats", "GET", { resourceType }).then((body) => body.stats),
/**
* Get user transaction history. <br>
* Query the `/api/user/money-history` server endpoint.
* @alias market.history
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.market.history().then(JSON.stringify).then(console.log);
* @example
* // Results:
* [ { _id: '5879547305128b021d4ee1d5', date: '2017-01-13T22:28:03.006Z', tick: '16726004',
* user: '000000000000000000000000', type: 'market.sell', balance: 64650, change: 450,
* market: { resourceType: 'O', roomName: 'W30N0', targetRoomName: 'W78N36',
* price: 0.45, npc: true, amount: 1000 } },
* { _id: '58794ed205128b021d4acfcf', date: '2017-01-13T22:04:02.260Z', tick: '16725625',
* user: '000000000000000000000000', type: 'market.sell', balance: 64200, change: 450,
* market: { resourceType: 'O', roomName: 'W60N0', targetRoomName: 'W78N36',
* price: 0.45, npc: true, amount: 1000 } },
* // ...
* @return {Promise} Return a promise eventually resolved with the list of transactions.
*/
history: () => this._req("/api/user/money-history", "GET").then((body) => body.list)
};
}
/**
* Provide methods to get user data and statistics (see `user.xxx()` functions).
* @type {Object}
*/
get user() {
return {
/**
* Get generic user information like subscribtion, authentication, stats, etc. <br>
* Query the `/api/auth/me` server endpoint.
* @alias user.me
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.user.me().then(JSON.stringify).then(console.log);
* @example
* // Results:
* { ok: 1, _id: '000000000000000000000000',
* email: 'xxxxx@xxxxx.xxx', username: 'xxxxxx', password: true,
* subscription: true, promoPeriodUntil: 0000000000000, subscriptionTokens: 0, credits: 0,
* badge: { type: 00, color1: '#000000', color2: '#000000', color3: '#000000', param: 0, flip: false },
* lastRespawnDate: 0000000000000, cpu: 000, gcl: 0000000, money: 000000,
* steam: { id: '00000000000000000', displayName: 'xxxxxx', ownership: [ 000000 ] } }
* @return {Promise} Promise eventually resolved with user data (see example).
*/
me: () => {
if(DEBUG) console.log("[Debug] Geting user data (/auth/me)...");
if(this.data.user === null) return this._req("/api/auth/me", "GET").then((me) => this.data.user = me);
else return Promise.resolve(this.data.user);
},
/**
* Subscribe to the `/cpu` endpoint. <br>
* Api will then emit events every tick with CPU and memory usage. <br>
* @alias user.subscribe
* @memberof! API#
* @example
* let api = new API(email, password, server);
* api.on("cpu", (data) => console.log(JSON.stringify(data)));
* api.user.subscribe();
* @fires API#cpu
* @return {Promise} Promise eventually resolved once message sent.
*/
subscribe: () => this._subscribe("/cpu"),
/**
* Get user global statistics. <br>
* Query the `/api/user/overview` server endpoint.
* @alias user.overview
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.user.overview("energyHarvested", 8).then(JSON.stringify).then(console.log);
* @example
* // Results:
* { "W78N36":[
* {"value":1704, "endTime":3092527},
* {"value":1368, "endTime":3092528}
* // ... (8 times)
* ] }
* @param {StatName} statName - Name of the stat to get (ex: energyHarvested).
* @param {number} [interval = 8] - Time interval for aggregating stats (8, 180 or 1440, nothing else).
* @return {Promise} Promise eventually resolved with requested stats.
*/
overview: (statName, interval) =>
this._req("/api/user/overview", "GET", { interval: interval || 8, statName })
.then((body) => body.stats),
/**
* Get general information for another user by name. <br>
* Query the `/api/user/find` server endpoint.
* @alias user.findByName
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.user.findByName("user_name").then(JSON.stringify).then(console.log);
* @example
* // Results:
* { "_id":"000000000000000000000000", "steam": { "id":"00000000000000000" },
* "username":"xxxxxx", "gcl":000000,
* "badge":{ "type":00, "color1":"#000000", "color2":"#000000", "color3":"#000000", "param":0, "flip":false } }
* @param {string} username - User name to look for (not case sensitive).
* @return {Promise} Promise eventually resolved with requested user data.
*/
findByName: (username) => this._req("/api/user/find", "GET", { username }).then((body) => body.user),
/**
* Get general information for another user by id. <br>
* Query the `/api/user/find` server endpoint.
* @alias user.findById
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.user.findById("user_id").then(JSON.stringify).then(console.log);
* @example
* // Results:
* { "_id":"000000000000000000000000", "steam": { "id":"00000000000000000" },
* "username":"xxxxxx", "gcl":000000,
* "badge":{ "type":00, "color1":"#000000", "color2":"#000000", "color3":"#000000", "param":0, "flip":false } }
* @param {string} id - User id to look for.
* @return {Promise} Promise eventually resolved with requested user data.
*/
findById: (id) => this._req("/api/user/find", "GET", { id }).then((body) => body.user),
/**
* Get the list of owned rooms for given user id.
* Query the `/api/user/rooms` server endpoint.
* @alias user.rooms
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.user.rooms("user_id").then(JSON.stringify).then(console.log);
* @example
* // Results:
* ["W78N36"]
* @param {string} id - User id to look for.
* @return {Promise} Promise eventually resolved with the room list.
*/
rooms: (id) => this._req("/api/user/rooms", "GET", { id }).then((body) => body.rooms),
/**
* Get statistics for the given rooms name. <br>
* Query the `/api/game/map-stats` server endpoint.
* @alias user.roomsStats
* @memberof! API#
* @example
* // Script:
* let api = new API(email, password, server);
* api.user.roomsStats(["W78N36"], undefined, 8).then(JSON.stringify).then(console.log);
* @example
* // Results:
* { "W78N36": {
* "status": "normal", "novice": TIME_BEFORE_NOVICE_ENDS,
* "own": { "user": USER_ID, "level": RCL_LEVEL }
* } }
* @param {Array<string>} rooms - Array of rooms name.
* @param {StatName} statName - ?
* @param {number} [interval = 8] - Time interval for aggregating stats (8, 180 or 1440, nothing else).
* @return {Promise} Promise eventually resolved with requested stats.
*/
roomsStats: (rooms, statName, interval) => {
return this._req("/api/game/map-stats", "POST", { rooms, statName: `${statName} ${interval || 8}` })
.then((body) => body);
}
};
}
}
module.exports = API;