const Package = require("../package.json");
const { EventEmitter } = require("events");
const RESTManager = require("./rest/RESTManager");
const UserStore = require("./storage/UserStore");
const ClanStore = require("./storage/ClanStore");
const { TypeError } = require("./errors");
/** Provides data from War Thunder */
class ThunderAPI extends EventEmitter {
/**
* Creates a new instance of ThunderAPI
* @param {ThunderAPIOptions} [options={}] The timeout in milliseconds to sweep the cache, defaults to 180000 milliseconds
*/
constructor(options = { cacheSweepInterval: 180000 }) {
super(options);
if (typeof options.cacheSweepInterval !== "number") throw new TypeError("INVALID_CONSTRUCTOR_OPTION", "cacheSweepInterval", "number");
/**
* The Request Handler for ThunderApi
* @type {RequestHandler}
* @private
*/
this.rest = new RESTManager();
/**
* A collection of cached profiles.
* The collection will be sweeped after the time passed
* @type {Collection<string,Profile>}
*/
this.users = new UserStore(this);
/**
* A collection of cached squadrons.
* The collection will be sweeped after the time passed
* @type {Collection<string,Clan>}
*/
this.clans = new ClanStore(this);
/**
* The time cache should be sweeped, in milliseconds
* @type {number}
* @readonly
*/
this.cacheSweepInterval = options.cacheSweepInterval;
/**
* The cached array of news items
* @type {?Array<NewsInfo>}
*/
this.lastNews = null;
/**
* Timeouts set by {@link ThunderAPI#setTimeout} that are still active
* @type {Set<Timeout>}
* @private
*/
this._timeouts = new Set();
/**
* Intervals set by {@link ThunderAPI#setInterval} that are still active
* @type {Set<Timeout>}
* @private
*/
this._intervals = new Set();
// Sweep cache every 180 seconds/time specified in constructor
this.setInterval(this.sweepCache.bind(this), this.cacheSweepInterval);
}
/**
* Get a player's profile
* @param {string} player The profile of the player to fetch
* @param {boolean} [cache=true] If it should get the player from the cache. Defaults to true
* @return {Promise<User>}
* @example
* // The following example gets the profile of the player
* // TheDutchy0412 and logs the squadron name
* // and the registration date
* ThunderAPI.getPlayer("TheDutchy0412")
* .then(profile => {
* console.log(profile.squadron);
* console.log(profile.registered);
* })
* .catch(err => console.error("Oh no, an error occurred!", err));
*/
fetchUser(player, cache = true) {
return this.users.fetch(player, cache);
}
/**
* Searches on the War Thunder wiki
* @param {string} query The query to search on
* @return {Promise<Wiki[]>}
*/
async searchWiki(query) {
if (typeof query !== "string") return Promise.reject(new TypeError("INVALID_QUERY", "query", "string"));
query = query.split(" ").join("+");
// eslint-disable-next-line max-len
const data = await this.rest.request("get", "/index.php", "wiki", { api: "wiki", query: { title: "Special:Search", search: query, fulltext: "Search" } })
.catch(error => Promise.reject(error));
return Promise.resolve(data);
}
/**
* Verifies the given player's in-game nickname
* @param {string} player The player to verify
* @return {Promise<boolean>}
*/
isUser(player) {
if (typeof player !== "string") return Promise.reject(new TypeError("INVALID_QUERY", "player", "string"));
if (this.users.has(player)) return Promise.resolve(true);
return this.fetchUser(player)
.then(() => true)
.catch(() => false);
}
/**
* Get's info about a squadron.
* <note>You must provide the **full** name of the squadron,
* e.g. "35th Gopnik nation battle group" instead of
* "GOPNK".</note>
* @param {string} name The **full** name of the squadron
* @param {boolean} [cache=true] If it should get the squadron from the cache, if cached. Defaults to true
* @return {Promise<Clan>}
* @example
* // The following example gets info about
* // the squadron 35th Gopnik nation battle group,
* // and logs the squadron description, and the
* // date this squadron was created
* ThunderAPI.getSquadron("35th Gopnik nation battle group")
* .then(data => {
* console.log(data.description);
* console.log(data.createdAt);
* })
* .catch(err => console.error("Oh no, an error occurred!", err));
*/
fetchClan(name, cache = true) {
return this.clans.fetch(name, cache);
}
/**
* Returns an array of news objects
* @param {boolean} [cache=true] If it should return the cached news object. Defaults to true
* @return {Promise<NewsInfo[]>}
*/
async getNews(cache) {
if (this.lastNews && cache) return Promise.resolve(this.lastNews);
const data = await this.rest.request("get", "/news3-en.html", "news")
.catch(error => Promise.reject(error));
this.lastNews = data;
return Promise.resolve(data);
}
/**
* Sweeps the cache
* @return {boolean} If the cache sweep was successful
*/
sweepCache() {
try {
this.users.clear();
this.clans.clear();
if (this.lastNews) this.lastNews = null;
return true;
} catch (err) {
return false;
}
}
/**
* Destroys all assets used by ThunderAPI.
*/
destroy() {
for (const t of this._timeouts) clearTimeout(t);
for (const i of this._intervals) clearInterval(i);
this._timeouts.clear();
this._intervals.clear();
}
/**
* Sets a timeout that will be automatically cancelled if the instance of ThunderAPI is destroyed.
* @param {Function} fn Function to execute
* @param {number} delay Time to wait before executing (in milliseconds)
* @param {...*} args Arguments for the function
* @returns {Timeout}
*/
setTimeout(fn, delay, ...args) {
const timeout = setTimeout(() => {
fn(...args);
this._timeouts.delete(timeout);
}, delay);
this._timeouts.add(timeout);
return timeout;
}
/**
* Clears a timeout.
* @param {Timeout} timeout Timeout to cancel
*/
clearTimeout(timeout) {
clearTimeout(timeout);
this._timeouts.delete(timeout);
}
/**
* Sets an interval that will be automatically cancelled if the client is destroyed.
* @param {Function} fn Function to execute
* @param {number} delay Time to wait between executions (in milliseconds)
* @param {...*} args Arguments for the function
* @returns {Timeout}
*/
setInterval(fn, delay, ...args) {
const interval = setInterval(fn, delay, ...args);
this._intervals.add(interval);
return interval;
}
/**
* Clears an interval.
* @param {Timeout} interval Interval to cancel
*/
clearInterval(interval) {
clearInterval(interval);
this._intervals.delete(interval);
}
}
module.exports = ThunderAPI;
module.exports.version = Package.version;
// Still exporting for backwards compatibility, probably remove it in the future!
module.exports.ThunderAPI = ThunderAPI;