initial commit
This commit is contained in:
1377
node_modules/grammy/out/core/api.d.ts
generated
vendored
Normal file
1377
node_modules/grammy/out/core/api.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1596
node_modules/grammy/out/core/api.js
generated
vendored
Normal file
1596
node_modules/grammy/out/core/api.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
163
node_modules/grammy/out/core/client.d.ts
generated
vendored
Normal file
163
node_modules/grammy/out/core/client.d.ts
generated
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
/// <reference types="node-fetch" />
|
||||
import { type ApiMethods as Telegram, type ApiResponse, type Opts } from "../types.js";
|
||||
export type Methods<R extends RawApi> = string & keyof R;
|
||||
/**
|
||||
* Represents the raw Telegram Bot API with all methods specified 1:1 as
|
||||
* documented on the website (https://core.telegram.org/bots/api).
|
||||
*
|
||||
* Every method takes an optional `AbortSignal` object that allows to cancel the
|
||||
* API call if desired.
|
||||
*/
|
||||
export type RawApi = {
|
||||
[M in keyof Telegram]: Parameters<Telegram[M]>[0] extends undefined ? (signal?: AbortSignal) => Promise<ReturnType<Telegram[M]>> : (args: Opts<M>, signal?: AbortSignal) => Promise<ReturnType<Telegram[M]>>;
|
||||
};
|
||||
export type Payload<M extends Methods<R>, R extends RawApi> = M extends unknown ? R[M] extends (signal?: AbortSignal) => unknown ? {} : R[M] extends (args: any, signal?: AbortSignal) => unknown ? Parameters<R[M]>[0] : never : never;
|
||||
/**
|
||||
* Small utility interface that abstracts from webhook reply calls of different
|
||||
* web frameworks.
|
||||
*/
|
||||
export interface WebhookReplyEnvelope {
|
||||
send?: (payload: string) => void | Promise<void>;
|
||||
}
|
||||
/**
|
||||
* Type of a function that can perform an API call. Used for Transformers.
|
||||
*/
|
||||
export type ApiCallFn<R extends RawApi = RawApi> = <M extends Methods<R>>(method: M, payload: Payload<M, R>, signal?: AbortSignal) => Promise<ApiResponse<ApiCallResult<M, R>>>;
|
||||
type ApiCallResult<M extends Methods<R>, R extends RawApi> = R[M] extends (...args: unknown[]) => unknown ? Awaited<ReturnType<R[M]>> : never;
|
||||
/**
|
||||
* API call transformers are functions that can access and modify the method and
|
||||
* payload of an API call on the fly. This can be useful if you want to
|
||||
* implement rate limiting or other things against the Telegram Bot API.
|
||||
*
|
||||
* Confer the grammY
|
||||
* [documentation](https://grammy.dev/advanced/transformers.html) to read more
|
||||
* about how to use transformers.
|
||||
*/
|
||||
export type Transformer<R extends RawApi = RawApi> = <M extends Methods<R>>(prev: ApiCallFn<R>, method: M, payload: Payload<M, R>, signal?: AbortSignal) => Promise<ApiResponse<ApiCallResult<M, R>>>;
|
||||
export type TransformerConsumer<R extends RawApi = RawApi> = TransformableApi<R>["use"];
|
||||
/**
|
||||
* A transformable API enhances the `RawApi` type by transformers.
|
||||
*/
|
||||
export interface TransformableApi<R extends RawApi = RawApi> {
|
||||
/**
|
||||
* Access to the raw API that the transformers will be installed on.
|
||||
*/
|
||||
raw: R;
|
||||
/**
|
||||
* Can be used to register any number of transformers on the API.
|
||||
*/
|
||||
use: (...transformers: Transformer<R>[]) => this;
|
||||
/**
|
||||
* Returns a readonly list or the currently installed transformers. The list
|
||||
* is sorted by time of installation where index 0 represents the
|
||||
* transformer that was installed first.
|
||||
*/
|
||||
installedTransformers: Transformer<R>[];
|
||||
}
|
||||
/**
|
||||
* Options to pass to the API client that eventually connects to the Telegram
|
||||
* Bot API server and makes the HTTP requests.
|
||||
*/
|
||||
export interface ApiClientOptions {
|
||||
/**
|
||||
* Root URL of the Telegram Bot API server. Default:
|
||||
* https://api.telegram.org
|
||||
*/
|
||||
apiRoot?: string;
|
||||
/**
|
||||
* URL builder function for API calls. Can be used to modify which API
|
||||
* server should be called.
|
||||
*
|
||||
* @param root The URL that was passed in `apiRoot`, or its default value
|
||||
* @param token The bot's token that was passed when creating the bot
|
||||
* @param method The API method to be called, e.g. `getMe`
|
||||
* @returns The URL that will be fetched during the API call
|
||||
*/
|
||||
buildUrl?: (root: string, token: string, method: string) => string | URL;
|
||||
/**
|
||||
* Maximum number of seconds that a request to the Bot API server may take.
|
||||
* If a request has not completed before this time has elapsed, grammY
|
||||
* aborts the request and errors. Without such a timeout, networking issues
|
||||
* may cause your bot to leave open a connection indefinitely, which may
|
||||
* effectively make your bot freeze.
|
||||
*
|
||||
* You probably do not have to care about this option. In rare cases, you
|
||||
* may want to adjust it if you are transferring large files via slow
|
||||
* connections to your own Bot API server.
|
||||
*
|
||||
* The default number of seconds is `500`, which corresponds to 8 minutes
|
||||
* and 20 seconds. Note that this is also the value that is hard-coded in
|
||||
* the official Bot API server, so you cannot perform any successful
|
||||
* requests that exceed this time frame (even if you would allow it in
|
||||
* grammY). Setting this option to higher than the default only makes sense
|
||||
* with a custom Bot API server.
|
||||
*/
|
||||
timeoutSeconds?: number;
|
||||
/**
|
||||
* If the bot is running on webhooks, as soon as the bot receives an update
|
||||
* from Telegram, it is possible to make up to one API call in the response
|
||||
* to the webhook request. As a benefit, this saves your bot from making up
|
||||
* to one HTTP request per update. However, there are a number of drawbacks
|
||||
* to using this:
|
||||
* 1) You will not be able to handle potential errors of the respective API
|
||||
* call. This includes rate limiting errors, so sent messages can be
|
||||
* swallowed by the Bot API server and there is no way to detect if a
|
||||
* message was actually sent or not.
|
||||
* 2) More importantly, you also won't have access to the response object,
|
||||
* so e.g. calling `sendMessage` will not give you access to the message
|
||||
* you sent.
|
||||
* 3) Furthermore, it is not possible to cancel the request. The
|
||||
* `AbortSignal` will be disregarded.
|
||||
* 4) Note also that the types in grammY do not reflect the consequences of
|
||||
* a performed webhook callback! For instance, they indicate that you
|
||||
* always receive a response object, so it is your own responsibility to
|
||||
* make sure you're not screwing up while using this minor performance
|
||||
* optimization.
|
||||
*
|
||||
* With this warning out of the way, here is what you can do with the
|
||||
* `canUseWebhookReply` option: it can be used to pass a function that
|
||||
* determines whether to use webhook reply for the given method. It will
|
||||
* only be invoked if the payload can be sent as JSON. It will not be
|
||||
* invoked again for a given update after it returned `true`, indicating
|
||||
* that the API call should be performed as a webhook send. In other words,
|
||||
* subsequent API calls (during the same update) will always perform their
|
||||
* own HTTP requests.
|
||||
*
|
||||
* @param method The method to call
|
||||
*/
|
||||
canUseWebhookReply?: (method: string) => boolean;
|
||||
/**
|
||||
* Base configuration for `fetch` calls. Specify any additional parameters
|
||||
* to use when fetching a method of the Telegram Bot API. Default: `{
|
||||
* compress: true }` (Node), `{}` (Deno)
|
||||
*/
|
||||
baseFetchConfig?: Omit<NonNullable<Parameters<typeof fetch>[1]>, "method" | "headers" | "body">;
|
||||
/**
|
||||
* When the network connection is unreliable and some API requests fail
|
||||
* because of that, grammY will throw errors that tell you exactly which
|
||||
* requests failed. However, the error messages do not disclose the fetched
|
||||
* URL as it contains your bot's token. Logging it may lead to token leaks.
|
||||
*
|
||||
* If you are sure that no logs are ever posted in Telegram chats, GitHub
|
||||
* issues, or otherwise shared, you can set this option to `true` in order
|
||||
* to obtain more detailed logs that may help you debug your bot. The
|
||||
* default value is `false`, meaning that the bot token is not logged.
|
||||
*/
|
||||
sensitiveLogs?: boolean;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export declare function createRawApi<R extends RawApi>(token: string, options?: ApiClientOptions, webhookReplyEnvelope?: WebhookReplyEnvelope): TransformableApi<R>;
|
||||
import { AbortSignal, fetch } from "./../shim.node.js";
|
||||
export {};
|
180
node_modules/grammy/out/core/client.js
generated
vendored
Normal file
180
node_modules/grammy/out/core/client.js
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
"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");
|
53
node_modules/grammy/out/core/error.d.ts
generated
vendored
Normal file
53
node_modules/grammy/out/core/error.d.ts
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
import { type ApiError, type ResponseParameters } from "../types.js";
|
||||
/**
|
||||
* This class represents errors that are thrown by grammY because the Telegram
|
||||
* Bot API responded with an error.
|
||||
*
|
||||
* Instances of this class hold the information that the Telegram backend
|
||||
* returned.
|
||||
*
|
||||
* If this error is thrown, grammY could successfully communicate with the
|
||||
* Telegram Bot API servers, however, an error code was returned for the
|
||||
* respective method call.
|
||||
*/
|
||||
export declare class GrammyError extends Error implements ApiError {
|
||||
/** The called method name which caused this error to be thrown. */
|
||||
readonly method: string;
|
||||
/** The payload that was passed when calling the method. */
|
||||
readonly payload: Record<string, unknown>;
|
||||
/** Flag that this request was unsuccessful. Always `false`. */
|
||||
readonly ok: false;
|
||||
/** An integer holding Telegram's error code. Subject to change. */
|
||||
readonly error_code: number;
|
||||
/** A human-readable description of the error. */
|
||||
readonly description: string;
|
||||
/** Further parameters that may help to automatically handle the error. */
|
||||
readonly parameters: ResponseParameters;
|
||||
constructor(message: string, err: ApiError,
|
||||
/** The called method name which caused this error to be thrown. */
|
||||
method: string,
|
||||
/** The payload that was passed when calling the method. */
|
||||
payload: Record<string, unknown>);
|
||||
}
|
||||
export declare function toGrammyError(err: ApiError, method: string, payload: Record<string, unknown>): GrammyError;
|
||||
/**
|
||||
* This class represents errors that are thrown by grammY because an HTTP call
|
||||
* to the Telegram Bot API failed.
|
||||
*
|
||||
* Instances of this class hold the error object that was created because the
|
||||
* fetch call failed. It can be inspected to determine why exactly the network
|
||||
* request failed.
|
||||
*
|
||||
* If an [API transformer
|
||||
* function](https://grammy.dev/advanced/transformers.html) throws an error,
|
||||
* grammY will regard this as if the network request failed. The contained error
|
||||
* will then be the error that was thrown by the transformer function.
|
||||
*/
|
||||
export declare class HttpError extends Error {
|
||||
/** The thrown error object. */
|
||||
readonly error: unknown;
|
||||
constructor(message: string,
|
||||
/** The thrown error object. */
|
||||
error: unknown);
|
||||
}
|
||||
export declare function toHttpError(method: string, sensitiveLogs: boolean): (err: unknown) => never;
|
77
node_modules/grammy/out/core/error.js
generated
vendored
Normal file
77
node_modules/grammy/out/core/error.js
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.toHttpError = exports.HttpError = exports.toGrammyError = exports.GrammyError = void 0;
|
||||
/**
|
||||
* This class represents errors that are thrown by grammY because the Telegram
|
||||
* Bot API responded with an error.
|
||||
*
|
||||
* Instances of this class hold the information that the Telegram backend
|
||||
* returned.
|
||||
*
|
||||
* If this error is thrown, grammY could successfully communicate with the
|
||||
* Telegram Bot API servers, however, an error code was returned for the
|
||||
* respective method call.
|
||||
*/
|
||||
class GrammyError extends Error {
|
||||
constructor(message, err,
|
||||
/** The called method name which caused this error to be thrown. */
|
||||
method,
|
||||
/** The payload that was passed when calling the method. */
|
||||
payload) {
|
||||
var _a;
|
||||
super(`${message} (${err.error_code}: ${err.description})`);
|
||||
this.method = method;
|
||||
this.payload = payload;
|
||||
/** Flag that this request was unsuccessful. Always `false`. */
|
||||
this.ok = false;
|
||||
this.name = "GrammyError";
|
||||
this.error_code = err.error_code;
|
||||
this.description = err.description;
|
||||
this.parameters = (_a = err.parameters) !== null && _a !== void 0 ? _a : {};
|
||||
}
|
||||
}
|
||||
exports.GrammyError = GrammyError;
|
||||
function toGrammyError(err, method, payload) {
|
||||
return new GrammyError(`Call to '${method}' failed!`, err, method, payload);
|
||||
}
|
||||
exports.toGrammyError = toGrammyError;
|
||||
/**
|
||||
* This class represents errors that are thrown by grammY because an HTTP call
|
||||
* to the Telegram Bot API failed.
|
||||
*
|
||||
* Instances of this class hold the error object that was created because the
|
||||
* fetch call failed. It can be inspected to determine why exactly the network
|
||||
* request failed.
|
||||
*
|
||||
* If an [API transformer
|
||||
* function](https://grammy.dev/advanced/transformers.html) throws an error,
|
||||
* grammY will regard this as if the network request failed. The contained error
|
||||
* will then be the error that was thrown by the transformer function.
|
||||
*/
|
||||
class HttpError extends Error {
|
||||
constructor(message,
|
||||
/** The thrown error object. */
|
||||
error) {
|
||||
super(message);
|
||||
this.error = error;
|
||||
this.name = "HttpError";
|
||||
}
|
||||
}
|
||||
exports.HttpError = HttpError;
|
||||
function isTelegramError(err) {
|
||||
return (typeof err === "object" &&
|
||||
err !== null &&
|
||||
"status" in err &&
|
||||
"statusText" in err);
|
||||
}
|
||||
function toHttpError(method, sensitiveLogs) {
|
||||
return (err) => {
|
||||
let msg = `Network request for '${method}' failed!`;
|
||||
if (isTelegramError(err))
|
||||
msg += ` (${err.status}: ${err.statusText})`;
|
||||
if (sensitiveLogs && err instanceof Error)
|
||||
msg += ` ${err.message}`;
|
||||
throw new HttpError(msg, err);
|
||||
};
|
||||
}
|
||||
exports.toHttpError = toHttpError;
|
40
node_modules/grammy/out/core/payload.d.ts
generated
vendored
Normal file
40
node_modules/grammy/out/core/payload.d.ts
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/// <reference types="node" />
|
||||
/**
|
||||
* Determines for a given payload if it may be sent as JSON, or if it has to be
|
||||
* uploaded via multipart/form-data. Returns `true` in the latter case and
|
||||
* `false` in the former.
|
||||
*
|
||||
* @param payload The payload to analyze
|
||||
*/
|
||||
export declare function requiresFormDataUpload(payload: unknown): boolean;
|
||||
/**
|
||||
* Turns a payload into an options object that can be passed to a `fetch` call
|
||||
* by setting the necessary headers and method. May only be called for payloads
|
||||
* `P` that let `requiresFormDataUpload(P)` return `false`.
|
||||
*
|
||||
* @param payload The payload to wrap
|
||||
*/
|
||||
export declare function createJsonPayload(payload: Record<string, unknown>): {
|
||||
method: string;
|
||||
headers: {
|
||||
"content-type": string;
|
||||
connection: string;
|
||||
};
|
||||
body: string;
|
||||
};
|
||||
/**
|
||||
* Turns a payload into an options object that can be passed to a `fetch` call
|
||||
* by setting the necessary headers and method. Note that this method creates a
|
||||
* multipart/form-data stream under the hood. If possible, a JSON payload should
|
||||
* be created instead for performance reasons.
|
||||
*
|
||||
* @param payload The payload to wrap
|
||||
*/
|
||||
export declare function createFormDataPayload(payload: Record<string, unknown>, onError: (err: unknown) => void): {
|
||||
method: string;
|
||||
headers: {
|
||||
"content-type": string;
|
||||
connection: string;
|
||||
};
|
||||
body: import("stream").Readable;
|
||||
};
|
200
node_modules/grammy/out/core/payload.js
generated
vendored
Normal file
200
node_modules/grammy/out/core/payload.js
generated
vendored
Normal file
@ -0,0 +1,200 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createFormDataPayload = exports.createJsonPayload = exports.requiresFormDataUpload = void 0;
|
||||
const platform_node_js_1 = require("../platform.node.js");
|
||||
const types_js_1 = require("../types.js");
|
||||
// === Payload types (JSON vs. form data)
|
||||
/**
|
||||
* Determines for a given payload if it may be sent as JSON, or if it has to be
|
||||
* uploaded via multipart/form-data. Returns `true` in the latter case and
|
||||
* `false` in the former.
|
||||
*
|
||||
* @param payload The payload to analyze
|
||||
*/
|
||||
function requiresFormDataUpload(payload) {
|
||||
return payload instanceof types_js_1.InputFile || (typeof payload === "object" &&
|
||||
payload !== null &&
|
||||
Object.values(payload).some((v) => Array.isArray(v)
|
||||
? v.some(requiresFormDataUpload)
|
||||
: v instanceof types_js_1.InputFile || requiresFormDataUpload(v)));
|
||||
}
|
||||
exports.requiresFormDataUpload = requiresFormDataUpload;
|
||||
/**
|
||||
* Calls `JSON.stringify` but removes `null` values from objects before
|
||||
* serialization
|
||||
*
|
||||
* @param value value
|
||||
* @returns stringified value
|
||||
*/
|
||||
function str(value) {
|
||||
return JSON.stringify(value, (_, v) => v !== null && v !== void 0 ? v : undefined);
|
||||
}
|
||||
/**
|
||||
* Turns a payload into an options object that can be passed to a `fetch` call
|
||||
* by setting the necessary headers and method. May only be called for payloads
|
||||
* `P` that let `requiresFormDataUpload(P)` return `false`.
|
||||
*
|
||||
* @param payload The payload to wrap
|
||||
*/
|
||||
function createJsonPayload(payload) {
|
||||
return {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
connection: "keep-alive",
|
||||
},
|
||||
body: str(payload),
|
||||
};
|
||||
}
|
||||
exports.createJsonPayload = createJsonPayload;
|
||||
async function* protectItr(itr, onError) {
|
||||
try {
|
||||
yield* itr;
|
||||
}
|
||||
catch (err) {
|
||||
onError(err);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Turns a payload into an options object that can be passed to a `fetch` call
|
||||
* by setting the necessary headers and method. Note that this method creates a
|
||||
* multipart/form-data stream under the hood. If possible, a JSON payload should
|
||||
* be created instead for performance reasons.
|
||||
*
|
||||
* @param payload The payload to wrap
|
||||
*/
|
||||
function createFormDataPayload(payload, onError) {
|
||||
const boundary = createBoundary();
|
||||
const itr = payloadToMultipartItr(payload, boundary);
|
||||
const safeItr = protectItr(itr, onError);
|
||||
const stream = (0, platform_node_js_1.itrToStream)(safeItr);
|
||||
return {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": `multipart/form-data; boundary=${boundary}`,
|
||||
connection: "keep-alive",
|
||||
},
|
||||
body: stream,
|
||||
};
|
||||
}
|
||||
exports.createFormDataPayload = createFormDataPayload;
|
||||
// === Form data creation
|
||||
function createBoundary() {
|
||||
// Taken from Deno std lib
|
||||
return "----------" + randomId(32);
|
||||
}
|
||||
function randomId(length = 16) {
|
||||
return Array.from(Array(length))
|
||||
.map(() => Math.random().toString(36)[2] || 0)
|
||||
.join("");
|
||||
}
|
||||
const enc = new TextEncoder();
|
||||
/**
|
||||
* Takes a payload object and produces a valid multipart/form-data stream. The
|
||||
* stream is an iterator of `Uint8Array` objects. You also need to specify the
|
||||
* boundary string that was used in the Content-Type header of the HTTP request.
|
||||
*
|
||||
* @param payload a payload object
|
||||
* @param boundary the boundary string to use between the parts
|
||||
*/
|
||||
async function* payloadToMultipartItr(payload, boundary) {
|
||||
const files = extractFiles(payload);
|
||||
// Start multipart/form-data protocol
|
||||
yield enc.encode(`--${boundary}\r\n`);
|
||||
// Send all payload fields
|
||||
const separator = enc.encode(`\r\n--${boundary}\r\n`);
|
||||
let first = true;
|
||||
for (const [key, value] of Object.entries(payload)) {
|
||||
if (value == null)
|
||||
continue;
|
||||
if (!first)
|
||||
yield separator;
|
||||
yield valuePart(key, typeof value === "object" ? str(value) : value);
|
||||
first = false;
|
||||
}
|
||||
// Send all files
|
||||
for (const { id, origin, file } of files) {
|
||||
if (!first)
|
||||
yield separator;
|
||||
yield* filePart(id, origin, file);
|
||||
first = false;
|
||||
}
|
||||
// End multipart/form-data protocol
|
||||
yield enc.encode(`\r\n--${boundary}--\r\n`);
|
||||
}
|
||||
/**
|
||||
* Replaces all instances of `InputFile` in a given payload by attach://
|
||||
* strings. This alters the passed object. After calling this method, the
|
||||
* payload object can be stringified.
|
||||
*
|
||||
* Returns a list of `InputFile` instances along with the random identifiers
|
||||
* that were used in the corresponding attach:// strings, as well as the origin
|
||||
* keys of the original payload object.
|
||||
*
|
||||
* @param value a payload object, or a part of it
|
||||
* @param key the origin key of the payload object, if a part of it is passed
|
||||
* @returns the cleaned payload object
|
||||
*/
|
||||
function extractFiles(value) {
|
||||
if (typeof value !== "object" || value === null)
|
||||
return [];
|
||||
return Object.entries(value).flatMap(([k, v]) => {
|
||||
if (Array.isArray(v))
|
||||
return v.flatMap((p) => extractFiles(p));
|
||||
else if (v instanceof types_js_1.InputFile) {
|
||||
const id = randomId();
|
||||
// Overwrite `InputFile` instance with attach:// string
|
||||
Object.assign(value, { [k]: `attach://${id}` });
|
||||
const origin = k === "media" &&
|
||||
"type" in value && typeof value.type === "string"
|
||||
? value.type // use `type` for `InputMedia*`
|
||||
: k; // use property key otherwise
|
||||
return { id, origin, file: v };
|
||||
}
|
||||
else
|
||||
return extractFiles(v);
|
||||
});
|
||||
}
|
||||
/** Turns a regular value into a `Uint8Array` */
|
||||
function valuePart(key, value) {
|
||||
return enc.encode(`content-disposition:form-data;name="${key}"\r\n\r\n${value}`);
|
||||
}
|
||||
/** Turns an InputFile into a generator of `Uint8Array`s */
|
||||
async function* filePart(id, origin, input) {
|
||||
const filename = input.filename || `${origin}.${getExt(origin)}`;
|
||||
if (filename.includes("\r") || filename.includes("\n")) {
|
||||
throw new Error(`File paths cannot contain carriage-return (\\r) \
|
||||
or newline (\\n) characters! Filename for property '${origin}' was:
|
||||
"""
|
||||
${filename}
|
||||
"""`);
|
||||
}
|
||||
yield enc.encode(`content-disposition:form-data;name="${id}";filename=${filename}\r\ncontent-type:application/octet-stream\r\n\r\n`);
|
||||
const data = await input.toRaw();
|
||||
if (data instanceof Uint8Array)
|
||||
yield data;
|
||||
else
|
||||
yield* data;
|
||||
}
|
||||
/** Returns the default file extension for an API property name */
|
||||
function getExt(key) {
|
||||
switch (key) {
|
||||
case "certificate":
|
||||
return "pem";
|
||||
case "photo":
|
||||
case "thumbnail":
|
||||
return "jpg";
|
||||
case "voice":
|
||||
return "ogg";
|
||||
case "audio":
|
||||
return "mp3";
|
||||
case "animation":
|
||||
case "video":
|
||||
case "video_note":
|
||||
return "mp4";
|
||||
case "sticker":
|
||||
return "webp";
|
||||
default:
|
||||
return "dat";
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user