Source: C:/Users/Shadow/Documents/screeps-js-api/src/api.js

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;