UNPKG

node-hoyolab

Version:

node-hoyolab is an unofficial API Wrapper library developed to integrate with the Hoyolab, which provides a more convenient way to access the Hoyolab API.

277 lines (276 loc) 9.47 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; var request_exports = {}; __export(request_exports, { HTTPRequest: () => HTTPRequest }); module.exports = __toCommonJS(request_exports); var import_https = require("https"); var import_zlib = require("zlib"); var import_error = require("../error"); var import_request = require("./request.helper"); var import_cache = require("../cache"); var import_crypto = require("crypto"); var import_language = require("../language"); class HTTPRequest { constructor(cookie) { /** * Query parameters for the request. */ __publicField(this, "params", {}); /** * Body of the request. */ __publicField(this, "body", {}); /** * The cache used for the request */ __publicField(this, "cache"); /* * Headers for the request. */ __publicField(this, "headers", { Accept: "application/json, text/plain, */*", "Content-Type": "application/json", "Accept-Encoding": "gzip, deflate, br", "sec-ch-ua": '"Chromium";v="112", "Microsoft Edge";v="112", "Not:A-Brand";v="99"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": '"Windows"', "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-site", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.46", "x-rpc-app_version": "1.5.0", "x-rpc-client_type": "5", "x-rpc-language": "en-us" }); /** * Flag indicating whether Dynamic Security is used. */ __publicField(this, "ds", false); /** * The number of request attempts made. */ __publicField(this, "retries", 1); __publicField(this, "http"); if (cookie) this.headers.Cookie = cookie.replace("ltokenV2", "ltoken_v2").replace("ltuidV2", "ltuid_v2"); this.cache = new import_cache.Cache(); } /** * Sets search parameters or query parameter. * * @param params - An object of query parameter to be set. * @returns Returns this Request object. */ setQueryParams(params) { this.params = { ...this.params, ...params }; return this; } /** * Set Body Parameter * * @param body - RequestBodyType as object containing the body parameters. * @returns This instance of Request object. */ setBody(data) { this.body = { ...this.body, ...data }; return this; } /** * Set Referer Headers * * @param url - The URL string of referer * @returns The updated Request instance. */ setReferer(url) { this.headers.Referer = url.toString(); this.headers.Origin = url.toString(); return this; } /** * Set Language * * @param lang Language Language that used for return of API (default: Language.ENGLISH). * @returns {this} */ setLang(lang) { this.headers["x-rpc-language"] = import_language.Language.parseLang(lang); return this; } /** * Set to used Dynamic Security or not * * @param flag boolean Flag indicating whether to use dynamic security or not (default: true). * @returns {this} The current Request instance. */ setDs(flag = true) { this.ds = flag; return this; } /** * Send the HTTP request. * * @param url - The URL to send the request to. * @param method - The HTTP method to use. Defaults to 'GET'. * @param ttl - The TTL value for the cached data in seconds. * @returns A Promise that resolves with the response data, or rejects with a NodeHoyoError if an error occurs. * @throws {NodeHoyoError} if an error occurs rejects with a NodeHoyoError */ async send(url, method = "GET", ttl = 60) { const fetch = (url2, method2) => { return new Promise((resolve, reject) => { const hostname = new URL(url2); const queryParams = new URLSearchParams(hostname.searchParams); Object.keys(this.params).forEach((val) => { var _a, _b; queryParams.append(val, (_b = (_a = this.params[val]) == null ? void 0 : _a.toString()) != null ? _b : ""); }); hostname.search = queryParams.toString(); const options = { method: method2, headers: this.headers }; const client = (0, import_https.request)(hostname, options, (res) => { if (res.statusCode === 429) { return resolve({ response: { data: null, message: "Too Many Request", retcode: 429 }, status: { code: 429, message: "Too Many Request" }, headers: res.headers, body: this.body, params: this.params }); } else if (res.statusCode && res.statusCode >= 400 && res.statusCode < 600) { reject( new import_error.NodeHoyoError( "HTTP ".concat(res.statusCode, ": ").concat(res.statusMessage), res.statusCode, { response: res.statusMessage, request: { params: this.params, body: this.body, headers: this.headers } } ) ); } const stream = []; res.on("data", (chunk) => { stream.push(chunk); }); res.on("end", () => { var _a, _b, _c, _d; let buffer = Buffer.concat(stream); const encoding = res.headers["content-encoding"]; if (encoding === "gzip") { buffer = (0, import_zlib.gunzipSync)(buffer); } else if (encoding === "deflate") { buffer = (0, import_zlib.inflateSync)(buffer); } else if (encoding === "br") { buffer = (0, import_zlib.brotliDecompressSync)(buffer); } const responseString = buffer.toString("utf8"); let response; if (res.headers["content-type"] === "application/json") { try { response = JSON.parse(responseString); resolve({ response: { data: (_a = response == null ? void 0 : response.data) != null ? _a : null, message: (_b = response == null ? void 0 : response.message) != null ? _b : "", retcode: (_c = response == null ? void 0 : response.retcode) != null ? _c : -1 }, status: { /* c8 ignore next */ code: (_d = res.statusCode) != null ? _d : -1, message: res.statusMessage }, headers: res.headers, body: this.body, params: this.params }); } catch (error) { reject( new import_error.NodeHoyoError("Failed to parse response body as JSON") ); } } else { reject( new import_error.NodeHoyoError( "Response Content-Type is not application/json" ) ); } }); res.on("error", (err) => { reject(new import_error.NodeHoyoError(err.message)); }); }); if (method2 === "POST") { client.write(JSON.stringify(this.body)); } client.end(); }); }; const cacheKey = (0, import_crypto.createHash)("md5").update( JSON.stringify({ url, method, body: this.body, params: this.params }) ).digest("hex"); const cachedResult = this.cache.get(cacheKey); if (cachedResult) { return cachedResult; } if (this.ds) { this.headers.DS = (0, import_request.generateDS)(); } const req = await fetch(url, method); if ([-1004, -2016, -500004, 429].includes(req.response.retcode) && this.retries <= 120) { this.retries++; await (0, import_request.delay)(1); return this.send(url, method); } this.retries = 1; this.body = {}; this.params = {}; this.cache.set(cacheKey, req, ttl); return req; } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { HTTPRequest });