mirror of
https://github.com/a-sync/game-server-watcher.git
synced 2026-07-24 06:33:35 -04:00
181 lines
7.3 KiB
JavaScript
181 lines
7.3 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.createRawApi = void 0;
|
|
const platform_node_js_1 = require("../platform.node.js");
|
|
const error_js_1 = require("./error.js");
|
|
const payload_js_1 = require("./payload.js");
|
|
const debug = (0, platform_node_js_1.debug)("grammy:core");
|
|
// Transformer base functions
|
|
function concatTransformer(prev, trans) {
|
|
return (method, payload, signal) => trans(prev, method, payload, signal);
|
|
}
|
|
class ApiClient {
|
|
constructor(token, options = {}, webhookReplyEnvelope = {}) {
|
|
var _a, _b, _c, _d, _e;
|
|
this.token = token;
|
|
this.webhookReplyEnvelope = webhookReplyEnvelope;
|
|
this.hasUsedWebhookReply = false;
|
|
this.installedTransformers = [];
|
|
this.call = async (method, p, signal) => {
|
|
const payload = p !== null && p !== void 0 ? p : {};
|
|
debug(`Calling ${method}`);
|
|
// General config
|
|
const opts = this.options;
|
|
const formDataRequired = (0, payload_js_1.requiresFormDataUpload)(payload);
|
|
// Short-circuit on webhook reply
|
|
if (this.webhookReplyEnvelope.send !== undefined &&
|
|
!this.hasUsedWebhookReply &&
|
|
!formDataRequired &&
|
|
opts.canUseWebhookReply(method)) {
|
|
this.hasUsedWebhookReply = true;
|
|
const config = (0, payload_js_1.createJsonPayload)({ ...payload, method });
|
|
await this.webhookReplyEnvelope.send(config.body);
|
|
return { ok: true, result: true };
|
|
}
|
|
// Handle timeouts and errors in the underlying form-data stream
|
|
const controller = createAbortControllerFromSignal(signal);
|
|
const timeout = createTimeout(controller, opts.timeoutSeconds, method);
|
|
const streamErr = createStreamError(controller);
|
|
// Build request URL and config
|
|
const url = opts.buildUrl(opts.apiRoot, this.token, method);
|
|
const config = formDataRequired
|
|
? (0, payload_js_1.createFormDataPayload)(payload, (err) => streamErr.catch(err))
|
|
: (0, payload_js_1.createJsonPayload)(payload);
|
|
const sig = controller.signal;
|
|
const options = { ...opts.baseFetchConfig, signal: sig, ...config };
|
|
// Perform fetch call, and handle networking errors
|
|
const successPromise = (0, shim_node_js_1.fetch)(url instanceof URL ? url.href : url, options).catch((0, error_js_1.toHttpError)(method, opts.sensitiveLogs));
|
|
// Those are the three possible outcomes of the fetch call:
|
|
const operations = [successPromise, streamErr.promise, timeout.promise];
|
|
// Wait for result
|
|
try {
|
|
const res = await Promise.race(operations);
|
|
return await res.json();
|
|
}
|
|
finally {
|
|
if (timeout.handle !== undefined)
|
|
clearTimeout(timeout.handle);
|
|
}
|
|
};
|
|
const apiRoot = (_a = options.apiRoot) !== null && _a !== void 0 ? _a : "https://api.telegram.org";
|
|
this.options = {
|
|
apiRoot,
|
|
buildUrl: (_b = options.buildUrl) !== null && _b !== void 0 ? _b : ((root, token, method) => `${root}/bot${token}/${method}`),
|
|
timeoutSeconds: (_c = options.timeoutSeconds) !== null && _c !== void 0 ? _c : 500,
|
|
baseFetchConfig: {
|
|
...(0, platform_node_js_1.baseFetchConfig)(apiRoot),
|
|
...options.baseFetchConfig,
|
|
},
|
|
canUseWebhookReply: (_d = options.canUseWebhookReply) !== null && _d !== void 0 ? _d : (() => false),
|
|
sensitiveLogs: (_e = options.sensitiveLogs) !== null && _e !== void 0 ? _e : false,
|
|
};
|
|
if (this.options.apiRoot.endsWith("/")) {
|
|
throw new Error(`Remove the trailing '/' from the 'apiRoot' option (use '${this.options.apiRoot.substring(0, this.options.apiRoot.length - 1)}' instead of '${this.options.apiRoot}')`);
|
|
}
|
|
}
|
|
use(...transformers) {
|
|
this.call = transformers.reduce(concatTransformer, this.call);
|
|
this.installedTransformers.push(...transformers);
|
|
return this;
|
|
}
|
|
async callApi(method, payload, signal) {
|
|
const data = await this.call(method, payload, signal);
|
|
if (data.ok)
|
|
return data.result;
|
|
else
|
|
throw (0, error_js_1.toGrammyError)(data, method, payload);
|
|
}
|
|
}
|
|
/**
|
|
* Creates a new transformable API, i.e. an object that lets you perform raw API
|
|
* calls to the Telegram Bot API server but pass the calls through a stack of
|
|
* transformers before. This will create a new API client instance under the
|
|
* hood that will be used to connect to the Telegram servers. You therefore need
|
|
* to pass the bot token. In addition, you may pass API client options as well
|
|
* as a webhook reply envelope that allows the client to perform up to one HTTP
|
|
* request in response to a webhook call if this is desired.
|
|
*
|
|
* @param token The bot's token
|
|
* @param options A number of options to pass to the created API client
|
|
* @param webhookReplyEnvelope The webhook reply envelope that will be used
|
|
*/
|
|
function createRawApi(token, options, webhookReplyEnvelope) {
|
|
const client = new ApiClient(token, options, webhookReplyEnvelope);
|
|
const proxyHandler = {
|
|
get(_, m) {
|
|
return m === "toJSON"
|
|
? "__internal"
|
|
: client.callApi.bind(client, m);
|
|
},
|
|
...proxyMethods,
|
|
};
|
|
const raw = new Proxy({}, proxyHandler);
|
|
const installedTransformers = client.installedTransformers;
|
|
const api = {
|
|
raw,
|
|
installedTransformers,
|
|
use: (...t) => {
|
|
client.use(...t);
|
|
return api;
|
|
},
|
|
};
|
|
return api;
|
|
}
|
|
exports.createRawApi = createRawApi;
|
|
const proxyMethods = {
|
|
set() {
|
|
return false;
|
|
},
|
|
defineProperty() {
|
|
return false;
|
|
},
|
|
deleteProperty() {
|
|
return false;
|
|
},
|
|
ownKeys() {
|
|
return [];
|
|
},
|
|
};
|
|
/** Creates a timeout error which aborts a given controller */
|
|
function createTimeout(controller, seconds, method) {
|
|
let handle = undefined;
|
|
const promise = new Promise((_, reject) => {
|
|
handle = setTimeout(() => {
|
|
const msg = `Request to '${method}' timed out after ${seconds} seconds`;
|
|
reject(new Error(msg));
|
|
controller.abort();
|
|
}, 1000 * seconds);
|
|
});
|
|
return { promise, handle };
|
|
}
|
|
/** Creates a stream error which abort a given controller */
|
|
function createStreamError(abortController) {
|
|
let onError = (err) => {
|
|
// Re-throw by default, but will be overwritten immediately
|
|
throw err;
|
|
};
|
|
const promise = new Promise((_, reject) => {
|
|
onError = (err) => {
|
|
reject(err);
|
|
abortController.abort();
|
|
};
|
|
});
|
|
return { promise, catch: onError };
|
|
}
|
|
function createAbortControllerFromSignal(signal) {
|
|
const abortController = new shim_node_js_1.AbortController();
|
|
if (signal === undefined)
|
|
return abortController;
|
|
const sig = signal;
|
|
function abort() {
|
|
abortController.abort();
|
|
sig.removeEventListener("abort", abort);
|
|
}
|
|
if (sig.aborted)
|
|
abort();
|
|
else
|
|
sig.addEventListener("abort", abort);
|
|
return { abort, signal: abortController.signal };
|
|
}
|
|
const shim_node_js_1 = require("../shim.node.js");
|