5693 lines
178 KiB
JavaScript
5693 lines
178 KiB
JavaScript
function matchFilter(filter) {
|
|
const parsed = parse(filter);
|
|
const predicate = compile(parsed);
|
|
return (ctx)=>predicate(ctx);
|
|
}
|
|
function parse(filter) {
|
|
return Array.isArray(filter) ? filter.map((q)=>q.split(":")) : [
|
|
filter.split(":")
|
|
];
|
|
}
|
|
function compile(parsed) {
|
|
const preprocessed = parsed.flatMap((q)=>check(q, preprocess(q)));
|
|
const ltree = treeify(preprocessed);
|
|
const predicate = arborist(ltree);
|
|
return (ctx)=>!!predicate(ctx.update, ctx);
|
|
}
|
|
function preprocess(filter) {
|
|
const valid = UPDATE_KEYS;
|
|
const expanded = [
|
|
filter
|
|
].flatMap((q)=>{
|
|
const [l1, l2, l3] = q;
|
|
if (!(l1 in L1_SHORTCUTS)) return [
|
|
q
|
|
];
|
|
if (!l1 && !l2 && !l3) return [
|
|
q
|
|
];
|
|
const targets = L1_SHORTCUTS[l1];
|
|
const expanded = targets.map((s)=>[
|
|
s,
|
|
l2,
|
|
l3
|
|
]);
|
|
if (l2 === undefined) return expanded;
|
|
if (l2 in L2_SHORTCUTS && (l2 || l3)) return expanded;
|
|
return expanded.filter(([s])=>!!valid[s]?.[l2]);
|
|
}).flatMap((q)=>{
|
|
const [l1, l2, l3] = q;
|
|
if (!(l2 in L2_SHORTCUTS)) return [
|
|
q
|
|
];
|
|
if (!l2 && !l3) return [
|
|
q
|
|
];
|
|
const targets = L2_SHORTCUTS[l2];
|
|
const expanded = targets.map((s)=>[
|
|
l1,
|
|
s,
|
|
l3
|
|
]);
|
|
if (l3 === undefined) return expanded;
|
|
return expanded.filter(([, s])=>!!valid[l1]?.[s]?.[l3]);
|
|
});
|
|
if (expanded.length === 0) {
|
|
throw new Error(`Shortcuts in '${filter.join(":")}' do not expand to any valid filter query`);
|
|
}
|
|
return expanded;
|
|
}
|
|
function check(original, preprocessed) {
|
|
if (preprocessed.length === 0) throw new Error("Empty filter query given");
|
|
const errors = preprocessed.map(checkOne).filter((r)=>r !== true);
|
|
if (errors.length === 0) return preprocessed;
|
|
else if (errors.length === 1) throw new Error(errors[0]);
|
|
else {
|
|
throw new Error(`Invalid filter query '${original.join(":")}'. There are ${errors.length} errors after expanding the contained shortcuts: ${errors.join("; ")}`);
|
|
}
|
|
}
|
|
function checkOne(filter) {
|
|
const [l1, l2, l3, ...n] = filter;
|
|
if (l1 === undefined) return "Empty filter query given";
|
|
if (!(l1 in UPDATE_KEYS)) {
|
|
const permitted = Object.keys(UPDATE_KEYS);
|
|
return `Invalid L1 filter '${l1}' given in '${filter.join(":")}'. \
|
|
Permitted values are: ${permitted.map((k)=>`'${k}'`).join(", ")}.`;
|
|
}
|
|
if (l2 === undefined) return true;
|
|
const l1Obj = UPDATE_KEYS[l1];
|
|
if (!(l2 in l1Obj)) {
|
|
const permitted = Object.keys(l1Obj);
|
|
return `Invalid L2 filter '${l2}' given in '${filter.join(":")}'. \
|
|
Permitted values are: ${permitted.map((k)=>`'${k}'`).join(", ")}.`;
|
|
}
|
|
if (l3 === undefined) return true;
|
|
const l2Obj = l1Obj[l2];
|
|
if (!(l3 in l2Obj)) {
|
|
const permitted = Object.keys(l2Obj);
|
|
return `Invalid L3 filter '${l3}' given in '${filter.join(":")}'. ${permitted.length === 0 ? `No further filtering is possible after '${l1}:${l2}'.` : `Permitted values are: ${permitted.map((k)=>`'${k}'`).join(", ")}.`}`;
|
|
}
|
|
if (n.length === 0) return true;
|
|
return `Cannot filter further than three levels, ':${n.join(":")}' is invalid!`;
|
|
}
|
|
function treeify(paths) {
|
|
const tree = {};
|
|
for (const [l1, l2, l3] of paths){
|
|
const subtree = tree[l1] ??= {};
|
|
if (l2 !== undefined) {
|
|
const set = subtree[l2] ??= new Set();
|
|
if (l3 !== undefined) set.add(l3);
|
|
}
|
|
}
|
|
return tree;
|
|
}
|
|
function or(left, right) {
|
|
return (obj, ctx)=>left(obj, ctx) || right(obj, ctx);
|
|
}
|
|
function concat(get, test) {
|
|
return (obj, ctx)=>{
|
|
const nextObj = get(obj, ctx);
|
|
return nextObj && test(nextObj, ctx);
|
|
};
|
|
}
|
|
function leaf(pred) {
|
|
return (obj, ctx)=>pred(obj, ctx) != null;
|
|
}
|
|
function arborist(tree) {
|
|
const l1Predicates = Object.entries(tree).map(([l1, subtree])=>{
|
|
const l1Pred = (obj)=>obj[l1];
|
|
const l2Predicates = Object.entries(subtree).map(([l2, set])=>{
|
|
const l2Pred = (obj)=>obj[l2];
|
|
const l3Predicates = Array.from(set).map((l3)=>{
|
|
const l3Pred = l3 === "me" ? (obj, ctx)=>{
|
|
const me = ctx.me.id;
|
|
return testMaybeArray(obj, (u)=>u.id === me);
|
|
} : (obj)=>testMaybeArray(obj, (e)=>e[l3] || e.type === l3);
|
|
return l3Pred;
|
|
});
|
|
return l3Predicates.length === 0 ? leaf(l2Pred) : concat(l2Pred, l3Predicates.reduce(or));
|
|
});
|
|
return l2Predicates.length === 0 ? leaf(l1Pred) : concat(l1Pred, l2Predicates.reduce(or));
|
|
});
|
|
if (l1Predicates.length === 0) {
|
|
throw new Error("Cannot create filter function for empty query");
|
|
}
|
|
return l1Predicates.reduce(or);
|
|
}
|
|
function testMaybeArray(t, pred) {
|
|
const p = (x)=>x != null && pred(x);
|
|
return Array.isArray(t) ? t.some(p) : p(t);
|
|
}
|
|
const ENTITY_KEYS = {
|
|
mention: {},
|
|
hashtag: {},
|
|
cashtag: {},
|
|
bot_command: {},
|
|
url: {},
|
|
email: {},
|
|
phone_number: {},
|
|
bold: {},
|
|
italic: {},
|
|
underline: {},
|
|
strikethrough: {},
|
|
spoiler: {},
|
|
code: {},
|
|
pre: {},
|
|
text_link: {},
|
|
text_mention: {},
|
|
custom_emoji: {}
|
|
};
|
|
const USER_KEYS = {
|
|
me: {},
|
|
is_bot: {},
|
|
is_premium: {},
|
|
added_to_attachment_menu: {}
|
|
};
|
|
const STICKER_KEYS = {
|
|
is_video: {},
|
|
is_animated: {},
|
|
premium_animation: {}
|
|
};
|
|
const EDITABLE_MESSAGE_KEYS = {
|
|
text: {},
|
|
animation: {},
|
|
audio: {},
|
|
document: {},
|
|
photo: {},
|
|
video: {},
|
|
game: {},
|
|
location: {},
|
|
entities: ENTITY_KEYS,
|
|
caption_entities: ENTITY_KEYS,
|
|
has_media_spoiler: {},
|
|
caption: {}
|
|
};
|
|
const COMMON_MESSAGE_KEYS = {
|
|
...EDITABLE_MESSAGE_KEYS,
|
|
sticker: STICKER_KEYS,
|
|
video_note: {},
|
|
voice: {},
|
|
contact: {},
|
|
dice: {},
|
|
poll: {},
|
|
venue: {},
|
|
new_chat_title: {},
|
|
new_chat_photo: {},
|
|
delete_chat_photo: {},
|
|
message_auto_delete_timer_changed: {},
|
|
pinned_message: {},
|
|
invoice: {},
|
|
proximity_alert_triggered: {},
|
|
video_chat_scheduled: {},
|
|
video_chat_started: {},
|
|
video_chat_ended: {},
|
|
video_chat_participants_invited: {},
|
|
web_app_data: {},
|
|
forward_date: {},
|
|
is_topic_message: {},
|
|
is_automatic_forward: {}
|
|
};
|
|
const MESSAGE_KEYS = {
|
|
...COMMON_MESSAGE_KEYS,
|
|
new_chat_members: USER_KEYS,
|
|
left_chat_member: USER_KEYS,
|
|
group_chat_created: {},
|
|
supergroup_chat_created: {},
|
|
migrate_to_chat_id: {},
|
|
migrate_from_chat_id: {},
|
|
successful_payment: {},
|
|
user_shared: {},
|
|
chat_shared: {},
|
|
connected_website: {},
|
|
write_access_allowed: {},
|
|
passport_data: {},
|
|
forum_topic_created: {},
|
|
forum_topic_edited: {
|
|
name: {},
|
|
icon_custom_emoji_id: {}
|
|
},
|
|
forum_topic_closed: {},
|
|
forum_topic_reopened: {},
|
|
general_forum_topic_hidden: {},
|
|
general_forum_topic_unhidden: {}
|
|
};
|
|
const CHANNEL_POST_KEYS = {
|
|
...COMMON_MESSAGE_KEYS,
|
|
channel_chat_created: {}
|
|
};
|
|
const CALLBACK_QUERY_KEYS = {
|
|
data: {},
|
|
game_short_name: {}
|
|
};
|
|
const CHAT_MEMBER_UPDATED_KEYS = {
|
|
from: USER_KEYS
|
|
};
|
|
const UPDATE_KEYS = {
|
|
message: MESSAGE_KEYS,
|
|
edited_message: MESSAGE_KEYS,
|
|
channel_post: CHANNEL_POST_KEYS,
|
|
edited_channel_post: CHANNEL_POST_KEYS,
|
|
inline_query: {},
|
|
chosen_inline_result: {},
|
|
callback_query: CALLBACK_QUERY_KEYS,
|
|
shipping_query: {},
|
|
pre_checkout_query: {},
|
|
poll: {},
|
|
poll_answer: {},
|
|
my_chat_member: CHAT_MEMBER_UPDATED_KEYS,
|
|
chat_member: CHAT_MEMBER_UPDATED_KEYS,
|
|
chat_join_request: {}
|
|
};
|
|
const L1_SHORTCUTS = {
|
|
"": [
|
|
"message",
|
|
"channel_post"
|
|
],
|
|
msg: [
|
|
"message",
|
|
"channel_post"
|
|
],
|
|
edit: [
|
|
"edited_message",
|
|
"edited_channel_post"
|
|
]
|
|
};
|
|
const L2_SHORTCUTS = {
|
|
"": [
|
|
"entities",
|
|
"caption_entities"
|
|
],
|
|
media: [
|
|
"photo",
|
|
"video"
|
|
],
|
|
file: [
|
|
"photo",
|
|
"animation",
|
|
"audio",
|
|
"document",
|
|
"video",
|
|
"video_note",
|
|
"voice",
|
|
"sticker"
|
|
]
|
|
};
|
|
const checker = {
|
|
filterQuery (filter) {
|
|
const pred = matchFilter(filter);
|
|
return (ctx)=>pred(ctx);
|
|
},
|
|
text (trigger) {
|
|
const hasText = checker.filterQuery([
|
|
":text",
|
|
":caption"
|
|
]);
|
|
const trg = triggerFn(trigger);
|
|
return (ctx)=>{
|
|
if (!hasText(ctx)) return false;
|
|
const msg = ctx.message ?? ctx.channelPost;
|
|
const txt = msg.text ?? msg.caption;
|
|
return match(ctx, txt, trg);
|
|
};
|
|
},
|
|
command (command) {
|
|
const hasEntities = checker.filterQuery(":entities:bot_command");
|
|
const atCommands = new Set();
|
|
const noAtCommands = new Set();
|
|
toArray(command).forEach((cmd)=>{
|
|
if (cmd.startsWith("/")) {
|
|
throw new Error(`Do not include '/' when registering command handlers (use '${cmd.substring(1)}' not '${cmd}')`);
|
|
}
|
|
const set = cmd.indexOf("@") === -1 ? noAtCommands : atCommands;
|
|
set.add(cmd);
|
|
});
|
|
return (ctx)=>{
|
|
if (!hasEntities(ctx)) return false;
|
|
const msg = ctx.message ?? ctx.channelPost;
|
|
const txt = msg.text ?? msg.caption;
|
|
return msg.entities.some((e)=>{
|
|
if (e.type !== "bot_command") return false;
|
|
if (e.offset !== 0) return false;
|
|
const cmd = txt.substring(1, e.length);
|
|
if (noAtCommands.has(cmd) || atCommands.has(cmd)) {
|
|
ctx.match = txt.substring(cmd.length + 1).trimStart();
|
|
return true;
|
|
}
|
|
const index = cmd.indexOf("@");
|
|
if (index === -1) return false;
|
|
const atTarget = cmd.substring(index + 1);
|
|
if (atTarget !== ctx.me.username) return false;
|
|
const atCommand = cmd.substring(0, index);
|
|
if (noAtCommands.has(atCommand)) {
|
|
ctx.match = txt.substring(cmd.length + 1).trimStart();
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
};
|
|
},
|
|
chatType (chatType) {
|
|
const set = new Set(toArray(chatType));
|
|
return (ctx)=>ctx.chat?.type !== undefined && set.has(ctx.chat.type);
|
|
},
|
|
callbackQuery (trigger) {
|
|
const hasCallbackQuery = checker.filterQuery("callback_query:data");
|
|
const trg = triggerFn(trigger);
|
|
return (ctx)=>hasCallbackQuery(ctx) && match(ctx, ctx.callbackQuery.data, trg);
|
|
},
|
|
gameQuery (trigger) {
|
|
const hasGameQuery = checker.filterQuery("callback_query:game_short_name");
|
|
const trg = triggerFn(trigger);
|
|
return (ctx)=>hasGameQuery(ctx) && match(ctx, ctx.callbackQuery.game_short_name, trg);
|
|
},
|
|
inlineQuery (trigger) {
|
|
const hasInlineQuery = checker.filterQuery("inline_query");
|
|
const trg = triggerFn(trigger);
|
|
return (ctx)=>hasInlineQuery(ctx) && match(ctx, ctx.inlineQuery.query, trg);
|
|
},
|
|
chosenInlineResult (trigger) {
|
|
const hasChosenInlineResult = checker.filterQuery("chosen_inline_result");
|
|
const trg = triggerFn(trigger);
|
|
return (ctx)=>hasChosenInlineResult(ctx) && match(ctx, ctx.chosenInlineResult.result_id, trg);
|
|
}
|
|
};
|
|
class Context {
|
|
match;
|
|
constructor(update, api, me){
|
|
this.update = update;
|
|
this.api = api;
|
|
this.me = me;
|
|
}
|
|
get message() {
|
|
return this.update.message;
|
|
}
|
|
get editedMessage() {
|
|
return this.update.edited_message;
|
|
}
|
|
get channelPost() {
|
|
return this.update.channel_post;
|
|
}
|
|
get editedChannelPost() {
|
|
return this.update.edited_channel_post;
|
|
}
|
|
get inlineQuery() {
|
|
return this.update.inline_query;
|
|
}
|
|
get chosenInlineResult() {
|
|
return this.update.chosen_inline_result;
|
|
}
|
|
get callbackQuery() {
|
|
return this.update.callback_query;
|
|
}
|
|
get shippingQuery() {
|
|
return this.update.shipping_query;
|
|
}
|
|
get preCheckoutQuery() {
|
|
return this.update.pre_checkout_query;
|
|
}
|
|
get poll() {
|
|
return this.update.poll;
|
|
}
|
|
get pollAnswer() {
|
|
return this.update.poll_answer;
|
|
}
|
|
get myChatMember() {
|
|
return this.update.my_chat_member;
|
|
}
|
|
get chatMember() {
|
|
return this.update.chat_member;
|
|
}
|
|
get chatJoinRequest() {
|
|
return this.update.chat_join_request;
|
|
}
|
|
get msg() {
|
|
return this.message ?? this.editedMessage ?? this.callbackQuery?.message ?? this.channelPost ?? this.editedChannelPost;
|
|
}
|
|
get chat() {
|
|
return (this.msg ?? this.myChatMember ?? this.chatMember ?? this.chatJoinRequest)?.chat;
|
|
}
|
|
get senderChat() {
|
|
return this.msg?.sender_chat;
|
|
}
|
|
get from() {
|
|
return (this.callbackQuery ?? this.inlineQuery ?? this.shippingQuery ?? this.preCheckoutQuery ?? this.chosenInlineResult ?? this.msg ?? this.myChatMember ?? this.chatMember ?? this.chatJoinRequest)?.from;
|
|
}
|
|
get inlineMessageId() {
|
|
return this.callbackQuery?.inline_message_id ?? this.chosenInlineResult?.inline_message_id;
|
|
}
|
|
entities(types) {
|
|
const message = this.msg;
|
|
if (message === undefined) return [];
|
|
const text = message.text ?? message.caption;
|
|
if (text === undefined) return [];
|
|
let entities = message.entities ?? message.caption_entities;
|
|
if (entities === undefined) return [];
|
|
if (types !== undefined) {
|
|
const filters = new Set(toArray(types));
|
|
entities = entities.filter((entity)=>filters.has(entity.type));
|
|
}
|
|
return entities.map((entity)=>({
|
|
...entity,
|
|
text: text.substring(entity.offset, entity.offset + entity.length)
|
|
}));
|
|
}
|
|
static has = checker;
|
|
has(filter) {
|
|
return Context.has.filterQuery(filter)(this);
|
|
}
|
|
hasText(trigger) {
|
|
return Context.has.text(trigger)(this);
|
|
}
|
|
hasCommand(command) {
|
|
return Context.has.command(command)(this);
|
|
}
|
|
hasChatType(chatType) {
|
|
return Context.has.chatType(chatType)(this);
|
|
}
|
|
hasCallbackQuery(trigger) {
|
|
return Context.has.callbackQuery(trigger)(this);
|
|
}
|
|
hasGameQuery(trigger) {
|
|
return Context.has.gameQuery(trigger)(this);
|
|
}
|
|
hasInlineQuery(trigger) {
|
|
return Context.has.inlineQuery(trigger)(this);
|
|
}
|
|
hasChosenInlineResult(trigger) {
|
|
return Context.has.chosenInlineResult(trigger)(this);
|
|
}
|
|
reply(text, other, signal) {
|
|
return this.api.sendMessage(orThrow(this.chat, "sendMessage").id, text, other, signal);
|
|
}
|
|
forwardMessage(chat_id, other, signal) {
|
|
return this.api.forwardMessage(chat_id, orThrow(this.chat, "forwardMessage").id, orThrow(this.msg, "forwardMessage").message_id, other, signal);
|
|
}
|
|
copyMessage(chat_id, other, signal) {
|
|
return this.api.copyMessage(chat_id, orThrow(this.chat, "copyMessage").id, orThrow(this.msg, "copyMessage").message_id, other, signal);
|
|
}
|
|
replyWithPhoto(photo, other, signal) {
|
|
return this.api.sendPhoto(orThrow(this.chat, "sendPhoto").id, photo, other, signal);
|
|
}
|
|
replyWithAudio(audio, other, signal) {
|
|
return this.api.sendAudio(orThrow(this.chat, "sendAudio").id, audio, other, signal);
|
|
}
|
|
replyWithDocument(document1, other, signal) {
|
|
return this.api.sendDocument(orThrow(this.chat, "sendDocument").id, document1, other, signal);
|
|
}
|
|
replyWithVideo(video, other, signal) {
|
|
return this.api.sendVideo(orThrow(this.chat, "sendVideo").id, video, other, signal);
|
|
}
|
|
replyWithAnimation(animation, other, signal) {
|
|
return this.api.sendAnimation(orThrow(this.chat, "sendAnimation").id, animation, other, signal);
|
|
}
|
|
replyWithVoice(voice, other, signal) {
|
|
return this.api.sendVoice(orThrow(this.chat, "sendVoice").id, voice, other, signal);
|
|
}
|
|
replyWithVideoNote(video_note, other, signal) {
|
|
return this.api.sendVideoNote(orThrow(this.chat, "sendVideoNote").id, video_note, other, signal);
|
|
}
|
|
replyWithMediaGroup(media, other, signal) {
|
|
return this.api.sendMediaGroup(orThrow(this.chat, "sendMediaGroup").id, media, other, signal);
|
|
}
|
|
replyWithLocation(latitude, longitude, other, signal) {
|
|
return this.api.sendLocation(orThrow(this.chat, "sendLocation").id, latitude, longitude, other, signal);
|
|
}
|
|
editMessageLiveLocation(latitude, longitude, other, signal) {
|
|
const inlineId = this.inlineMessageId;
|
|
return inlineId !== undefined ? this.api.editMessageLiveLocationInline(inlineId, latitude, longitude, other) : this.api.editMessageLiveLocation(orThrow(this.chat, "editMessageLiveLocation").id, orThrow(this.msg, "editMessageLiveLocation").message_id, latitude, longitude, other, signal);
|
|
}
|
|
stopMessageLiveLocation(other, signal) {
|
|
const inlineId = this.inlineMessageId;
|
|
return inlineId !== undefined ? this.api.stopMessageLiveLocationInline(inlineId, other) : this.api.stopMessageLiveLocation(orThrow(this.chat, "stopMessageLiveLocation").id, orThrow(this.msg, "stopMessageLiveLocation").message_id, other, signal);
|
|
}
|
|
replyWithVenue(latitude, longitude, title, address, other, signal) {
|
|
return this.api.sendVenue(orThrow(this.chat, "sendVenue").id, latitude, longitude, title, address, other, signal);
|
|
}
|
|
replyWithContact(phone_number, first_name, other, signal) {
|
|
return this.api.sendContact(orThrow(this.chat, "sendContact").id, phone_number, first_name, other, signal);
|
|
}
|
|
replyWithPoll(question, options, other, signal) {
|
|
return this.api.sendPoll(orThrow(this.chat, "sendPoll").id, question, options, other, signal);
|
|
}
|
|
replyWithDice(emoji, other, signal) {
|
|
return this.api.sendDice(orThrow(this.chat, "sendDice").id, emoji, other, signal);
|
|
}
|
|
replyWithChatAction(action, other, signal) {
|
|
return this.api.sendChatAction(orThrow(this.chat, "sendChatAction").id, action, other, signal);
|
|
}
|
|
getUserProfilePhotos(other, signal) {
|
|
return this.api.getUserProfilePhotos(orThrow(this.from, "getUserProfilePhotos").id, other, signal);
|
|
}
|
|
getFile(signal) {
|
|
const m = orThrow(this.msg, "getFile");
|
|
const file = m.photo !== undefined ? m.photo[m.photo.length - 1] : m.animation ?? m.audio ?? m.document ?? m.video ?? m.video_note ?? m.voice ?? m.sticker;
|
|
return this.api.getFile(orThrow(file, "getFile").file_id, signal);
|
|
}
|
|
kickAuthor(...args) {
|
|
return this.banAuthor(...args);
|
|
}
|
|
banAuthor(other, signal) {
|
|
return this.api.banChatMember(orThrow(this.chat, "banAuthor").id, orThrow(this.from, "banAuthor").id, other, signal);
|
|
}
|
|
kickChatMember(...args) {
|
|
return this.banChatMember(...args);
|
|
}
|
|
banChatMember(user_id, other, signal) {
|
|
return this.api.banChatMember(orThrow(this.chat, "banChatMember").id, user_id, other, signal);
|
|
}
|
|
unbanChatMember(user_id, other, signal) {
|
|
return this.api.unbanChatMember(orThrow(this.chat, "unbanChatMember").id, user_id, other, signal);
|
|
}
|
|
restrictAuthor(permissions, other, signal) {
|
|
return this.api.restrictChatMember(orThrow(this.chat, "restrictAuthor").id, orThrow(this.from, "restrictAuthor").id, permissions, other, signal);
|
|
}
|
|
restrictChatMember(user_id, permissions, other, signal) {
|
|
return this.api.restrictChatMember(orThrow(this.chat, "restrictChatMember").id, user_id, permissions, other, signal);
|
|
}
|
|
promoteAuthor(other, signal) {
|
|
return this.api.promoteChatMember(orThrow(this.chat, "promoteAuthor").id, orThrow(this.from, "promoteAuthor").id, other, signal);
|
|
}
|
|
promoteChatMember(user_id, other, signal) {
|
|
return this.api.promoteChatMember(orThrow(this.chat, "promoteChatMember").id, user_id, other, signal);
|
|
}
|
|
setChatAdministratorAuthorCustomTitle(custom_title, signal) {
|
|
return this.api.setChatAdministratorCustomTitle(orThrow(this.chat, "setChatAdministratorAuthorCustomTitle").id, orThrow(this.from, "setChatAdministratorAuthorCustomTitle").id, custom_title, signal);
|
|
}
|
|
setChatAdministratorCustomTitle(user_id, custom_title, signal) {
|
|
return this.api.setChatAdministratorCustomTitle(orThrow(this.chat, "setChatAdministratorCustomTitle").id, user_id, custom_title, signal);
|
|
}
|
|
banChatSenderChat(sender_chat_id, signal) {
|
|
return this.api.banChatSenderChat(orThrow(this.chat, "banChatSenderChat").id, sender_chat_id, signal);
|
|
}
|
|
unbanChatSenderChat(sender_chat_id, signal) {
|
|
return this.api.unbanChatSenderChat(orThrow(this.chat, "unbanChatSenderChat").id, sender_chat_id, signal);
|
|
}
|
|
setChatPermissions(permissions, signal) {
|
|
return this.api.setChatPermissions(orThrow(this.chat, "setChatPermissions").id, permissions, signal);
|
|
}
|
|
exportChatInviteLink(signal) {
|
|
return this.api.exportChatInviteLink(orThrow(this.chat, "exportChatInviteLink").id, signal);
|
|
}
|
|
createChatInviteLink(other, signal) {
|
|
return this.api.createChatInviteLink(orThrow(this.chat, "createChatInviteLink").id, other, signal);
|
|
}
|
|
editChatInviteLink(invite_link, other, signal) {
|
|
return this.api.editChatInviteLink(orThrow(this.chat, "editChatInviteLink").id, invite_link, other, signal);
|
|
}
|
|
revokeChatInviteLink(invite_link, signal) {
|
|
return this.api.revokeChatInviteLink(orThrow(this.chat, "editChatInviteLink").id, invite_link, signal);
|
|
}
|
|
approveChatJoinRequest(user_id, signal) {
|
|
return this.api.approveChatJoinRequest(orThrow(this.chat, "approveChatJoinRequest").id, user_id, signal);
|
|
}
|
|
declineChatJoinRequest(user_id, signal) {
|
|
return this.api.declineChatJoinRequest(orThrow(this.chat, "declineChatJoinRequest").id, user_id, signal);
|
|
}
|
|
setChatPhoto(photo, signal) {
|
|
return this.api.setChatPhoto(orThrow(this.chat, "setChatPhoto").id, photo, signal);
|
|
}
|
|
deleteChatPhoto(signal) {
|
|
return this.api.deleteChatPhoto(orThrow(this.chat, "deleteChatPhoto").id, signal);
|
|
}
|
|
setChatTitle(title, signal) {
|
|
return this.api.setChatTitle(orThrow(this.chat, "setChatTitle").id, title, signal);
|
|
}
|
|
setChatDescription(description, signal) {
|
|
return this.api.setChatDescription(orThrow(this.chat, "setChatDescription").id, description, signal);
|
|
}
|
|
pinChatMessage(message_id, other, signal) {
|
|
return this.api.pinChatMessage(orThrow(this.chat, "pinChatMessage").id, message_id, other, signal);
|
|
}
|
|
unpinChatMessage(message_id, signal) {
|
|
return this.api.unpinChatMessage(orThrow(this.chat, "unpinChatMessage").id, message_id, signal);
|
|
}
|
|
unpinAllChatMessages(signal) {
|
|
return this.api.unpinAllChatMessages(orThrow(this.chat, "unpinAllChatMessages").id, signal);
|
|
}
|
|
leaveChat(signal) {
|
|
return this.api.leaveChat(orThrow(this.chat, "leaveChat").id, signal);
|
|
}
|
|
getChat(signal) {
|
|
return this.api.getChat(orThrow(this.chat, "getChat").id, signal);
|
|
}
|
|
getChatAdministrators(signal) {
|
|
return this.api.getChatAdministrators(orThrow(this.chat, "getChatAdministrators").id, signal);
|
|
}
|
|
getChatMembersCount(...args) {
|
|
return this.getChatMemberCount(...args);
|
|
}
|
|
getChatMemberCount(signal) {
|
|
return this.api.getChatMemberCount(orThrow(this.chat, "getChatMemberCount").id, signal);
|
|
}
|
|
getAuthor(signal) {
|
|
return this.api.getChatMember(orThrow(this.chat, "getAuthor").id, orThrow(this.from, "getAuthor").id, signal);
|
|
}
|
|
getChatMember(user_id, signal) {
|
|
return this.api.getChatMember(orThrow(this.chat, "getChatMember").id, user_id, signal);
|
|
}
|
|
setChatStickerSet(sticker_set_name, signal) {
|
|
return this.api.setChatStickerSet(orThrow(this.chat, "setChatStickerSet").id, sticker_set_name, signal);
|
|
}
|
|
deleteChatStickerSet(signal) {
|
|
return this.api.deleteChatStickerSet(orThrow(this.chat, "deleteChatStickerSet").id, signal);
|
|
}
|
|
createForumTopic(name, other, signal) {
|
|
return this.api.createForumTopic(orThrow(this.chat, "createForumTopic").id, name, other, signal);
|
|
}
|
|
editForumTopic(other, signal) {
|
|
const message = orThrow(this.msg, "editForumTopic");
|
|
const thread = orThrow(message.message_thread_id, "editForumTopic");
|
|
return this.api.editForumTopic(message.chat.id, thread, other, signal);
|
|
}
|
|
closeForumTopic(signal) {
|
|
const message = orThrow(this.msg, "closeForumTopic");
|
|
const thread = orThrow(message.message_thread_id, "closeForumTopic");
|
|
return this.api.closeForumTopic(message.chat.id, thread, signal);
|
|
}
|
|
reopenForumTopic(signal) {
|
|
const message = orThrow(this.msg, "reopenForumTopic");
|
|
const thread = orThrow(message.message_thread_id, "reopenForumTopic");
|
|
return this.api.reopenForumTopic(message.chat.id, thread, signal);
|
|
}
|
|
deleteForumTopic(signal) {
|
|
const message = orThrow(this.msg, "deleteForumTopic");
|
|
const thread = orThrow(message.message_thread_id, "deleteForumTopic");
|
|
return this.api.deleteForumTopic(message.chat.id, thread, signal);
|
|
}
|
|
unpinAllForumTopicMessages(signal) {
|
|
const message = orThrow(this.msg, "unpinAllForumTopicMessages");
|
|
const thread = orThrow(message.message_thread_id, "unpinAllForumTopicMessages");
|
|
return this.api.unpinAllForumTopicMessages(message.chat.id, thread, signal);
|
|
}
|
|
editGeneralForumTopic(name, signal) {
|
|
return this.api.editGeneralForumTopic(orThrow(this.chat, "editGeneralForumTopic").id, name, signal);
|
|
}
|
|
closeGeneralForumTopic(signal) {
|
|
return this.api.closeGeneralForumTopic(orThrow(this.chat, "closeGeneralForumTopic").id, signal);
|
|
}
|
|
reopenGeneralForumTopic(signal) {
|
|
return this.api.reopenGeneralForumTopic(orThrow(this.chat, "reopenGeneralForumTopic").id, signal);
|
|
}
|
|
hideGeneralForumTopic(signal) {
|
|
return this.api.hideGeneralForumTopic(orThrow(this.chat, "hideGeneralForumTopic").id, signal);
|
|
}
|
|
unhideGeneralForumTopic(signal) {
|
|
return this.api.unhideGeneralForumTopic(orThrow(this.chat, "unhideGeneralForumTopic").id, signal);
|
|
}
|
|
answerCallbackQuery(other, signal) {
|
|
return this.api.answerCallbackQuery(orThrow(this.callbackQuery, "answerCallbackQuery").id, typeof other === "string" ? {
|
|
text: other
|
|
} : other, signal);
|
|
}
|
|
setChatMenuButton(other, signal) {
|
|
return this.api.setChatMenuButton(other, signal);
|
|
}
|
|
getChatMenuButton(other, signal) {
|
|
return this.api.getChatMenuButton(other, signal);
|
|
}
|
|
setMyDefaultAdministratorRights(other, signal) {
|
|
return this.api.setMyDefaultAdministratorRights(other, signal);
|
|
}
|
|
getMyDefaultAdministratorRights(other, signal) {
|
|
return this.api.getMyDefaultAdministratorRights(other, signal);
|
|
}
|
|
editMessageText(text, other, signal) {
|
|
const inlineId = this.inlineMessageId;
|
|
return inlineId !== undefined ? this.api.editMessageTextInline(inlineId, text, other) : this.api.editMessageText(orThrow(this.chat, "editMessageText").id, orThrow(this.msg, "editMessageText").message_id, text, other, signal);
|
|
}
|
|
editMessageCaption(other, signal) {
|
|
const inlineId = this.inlineMessageId;
|
|
return inlineId !== undefined ? this.api.editMessageCaptionInline(inlineId, other) : this.api.editMessageCaption(orThrow(this.chat, "editMessageCaption").id, orThrow(this.msg, "editMessageCaption").message_id, other, signal);
|
|
}
|
|
editMessageMedia(media, other, signal) {
|
|
const inlineId = this.inlineMessageId;
|
|
return inlineId !== undefined ? this.api.editMessageMediaInline(inlineId, media, other) : this.api.editMessageMedia(orThrow(this.chat, "editMessageMedia").id, orThrow(this.msg, "editMessageMedia").message_id, media, other, signal);
|
|
}
|
|
editMessageReplyMarkup(other, signal) {
|
|
const inlineId = this.inlineMessageId;
|
|
return inlineId !== undefined ? this.api.editMessageReplyMarkupInline(inlineId, other) : this.api.editMessageReplyMarkup(orThrow(this.chat, "editMessageReplyMarkup").id, orThrow(this.msg, "editMessageReplyMarkup").message_id, other, signal);
|
|
}
|
|
stopPoll(other, signal) {
|
|
return this.api.stopPoll(orThrow(this.chat, "stopPoll").id, orThrow(this.msg, "stopPoll").message_id, other, signal);
|
|
}
|
|
deleteMessage(signal) {
|
|
return this.api.deleteMessage(orThrow(this.chat, "deleteMessage").id, orThrow(this.msg, "deleteMessage").message_id, signal);
|
|
}
|
|
replyWithSticker(sticker, other, signal) {
|
|
return this.api.sendSticker(orThrow(this.chat, "sendSticker").id, sticker, other, signal);
|
|
}
|
|
getCustomEmojiStickers(signal) {
|
|
return this.api.getCustomEmojiStickers((this.msg?.entities ?? []).filter((e)=>e.type === "custom_emoji").map((e)=>e.custom_emoji_id), signal);
|
|
}
|
|
answerInlineQuery(results, other, signal) {
|
|
return this.api.answerInlineQuery(orThrow(this.inlineQuery, "answerInlineQuery").id, results, other, signal);
|
|
}
|
|
replyWithInvoice(title, description, payload, provider_token, currency, prices, other, signal) {
|
|
return this.api.sendInvoice(orThrow(this.chat, "sendInvoice").id, title, description, payload, provider_token, currency, prices, other, signal);
|
|
}
|
|
answerShippingQuery(ok, other, signal) {
|
|
return this.api.answerShippingQuery(orThrow(this.shippingQuery, "answerShippingQuery").id, ok, other, signal);
|
|
}
|
|
answerPreCheckoutQuery(ok, other, signal) {
|
|
return this.api.answerPreCheckoutQuery(orThrow(this.preCheckoutQuery, "answerPreCheckoutQuery").id, ok, typeof other === "string" ? {
|
|
error_message: other
|
|
} : other, signal);
|
|
}
|
|
setPassportDataErrors(errors, signal) {
|
|
return this.api.setPassportDataErrors(orThrow(this.from, "setPassportDataErrors").id, errors, signal);
|
|
}
|
|
replyWithGame(game_short_name, other, signal) {
|
|
return this.api.sendGame(orThrow(this.chat, "sendGame").id, game_short_name, other, signal);
|
|
}
|
|
update;
|
|
api;
|
|
me;
|
|
}
|
|
function orThrow(value, method) {
|
|
if (value === undefined) {
|
|
throw new Error(`Missing information for API call to ${method}`);
|
|
}
|
|
return value;
|
|
}
|
|
function triggerFn(trigger) {
|
|
return toArray(trigger).map((t)=>typeof t === "string" ? (txt)=>txt === t ? t : null : (txt)=>txt.match(t));
|
|
}
|
|
function match(ctx, content, triggers) {
|
|
for (const t of triggers){
|
|
const res = t(content);
|
|
if (res) {
|
|
ctx.match = res;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
function toArray(e) {
|
|
return Array.isArray(e) ? e : [
|
|
e
|
|
];
|
|
}
|
|
class BotError extends Error {
|
|
constructor(error, ctx){
|
|
super(generateBotErrorMessage(error));
|
|
this.error = error;
|
|
this.ctx = ctx;
|
|
this.name = "BotError";
|
|
if (error instanceof Error) this.stack = error.stack;
|
|
}
|
|
error;
|
|
ctx;
|
|
}
|
|
function generateBotErrorMessage(error) {
|
|
let msg;
|
|
if (error instanceof Error) {
|
|
msg = `${error.name} in middleware: ${error.message}`;
|
|
} else {
|
|
const type = typeof error;
|
|
msg = `Non-error value of type ${type} thrown in middleware`;
|
|
switch(type){
|
|
case "bigint":
|
|
case "boolean":
|
|
case "number":
|
|
case "symbol":
|
|
msg += `: ${error}`;
|
|
break;
|
|
case "string":
|
|
msg += `: ${String(error).substring(0, 50)}`;
|
|
break;
|
|
default:
|
|
msg += "!";
|
|
break;
|
|
}
|
|
}
|
|
return msg;
|
|
}
|
|
function flatten(mw) {
|
|
return typeof mw === "function" ? mw : (ctx, next)=>mw.middleware()(ctx, next);
|
|
}
|
|
function concat1(first, andThen) {
|
|
return async (ctx, next)=>{
|
|
let nextCalled = false;
|
|
await first(ctx, async ()=>{
|
|
if (nextCalled) throw new Error("`next` already called before!");
|
|
else nextCalled = true;
|
|
await andThen(ctx, next);
|
|
});
|
|
};
|
|
}
|
|
function pass(_ctx, next) {
|
|
return next();
|
|
}
|
|
const leaf1 = ()=>Promise.resolve();
|
|
async function run(middleware, ctx) {
|
|
await middleware(ctx, leaf1);
|
|
}
|
|
class Composer {
|
|
handler;
|
|
constructor(...middleware){
|
|
this.handler = middleware.length === 0 ? pass : middleware.map(flatten).reduce(concat1);
|
|
}
|
|
middleware() {
|
|
return this.handler;
|
|
}
|
|
use(...middleware) {
|
|
const composer = new Composer(...middleware);
|
|
this.handler = concat1(this.handler, flatten(composer));
|
|
return composer;
|
|
}
|
|
on(filter, ...middleware) {
|
|
return this.filter(Context.has.filterQuery(filter), ...middleware);
|
|
}
|
|
hears(trigger, ...middleware) {
|
|
return this.filter(Context.has.text(trigger), ...middleware);
|
|
}
|
|
command(command, ...middleware) {
|
|
return this.filter(Context.has.command(command), ...middleware);
|
|
}
|
|
chatType(chatType, ...middleware) {
|
|
return this.filter(Context.has.chatType(chatType), ...middleware);
|
|
}
|
|
callbackQuery(trigger, ...middleware) {
|
|
return this.filter(Context.has.callbackQuery(trigger), ...middleware);
|
|
}
|
|
gameQuery(trigger, ...middleware) {
|
|
return this.filter(Context.has.gameQuery(trigger), ...middleware);
|
|
}
|
|
inlineQuery(trigger, ...middleware) {
|
|
return this.filter(Context.has.inlineQuery(trigger), ...middleware);
|
|
}
|
|
chosenInlineResult(resultId, ...middleware) {
|
|
return this.filter(Context.has.chosenInlineResult(resultId), ...middleware);
|
|
}
|
|
filter(predicate, ...middleware) {
|
|
const composer = new Composer(...middleware);
|
|
this.branch(predicate, composer, pass);
|
|
return composer;
|
|
}
|
|
drop(predicate, ...middleware) {
|
|
return this.filter(async (ctx)=>!await predicate(ctx), ...middleware);
|
|
}
|
|
fork(...middleware) {
|
|
const composer = new Composer(...middleware);
|
|
const fork = flatten(composer);
|
|
this.use((ctx, next)=>Promise.all([
|
|
next(),
|
|
run(fork, ctx)
|
|
]));
|
|
return composer;
|
|
}
|
|
lazy(middlewareFactory) {
|
|
return this.use(async (ctx, next)=>{
|
|
const middleware = await middlewareFactory(ctx);
|
|
const arr = Array.isArray(middleware) ? middleware : [
|
|
middleware
|
|
];
|
|
await flatten(new Composer(...arr))(ctx, next);
|
|
});
|
|
}
|
|
route(router, routeHandlers, fallback = pass) {
|
|
return this.lazy(async (ctx)=>{
|
|
const route = await router(ctx);
|
|
return (route === undefined || !routeHandlers[route] ? fallback : routeHandlers[route]) ?? [];
|
|
});
|
|
}
|
|
branch(predicate, trueMiddleware, falseMiddleware) {
|
|
return this.lazy(async (ctx)=>await predicate(ctx) ? trueMiddleware : falseMiddleware);
|
|
}
|
|
errorBoundary(errorHandler, ...middleware) {
|
|
const composer = new Composer(...middleware);
|
|
const bound = flatten(composer);
|
|
this.use(async (ctx, next)=>{
|
|
let nextCalled = false;
|
|
const cont = ()=>(nextCalled = true, Promise.resolve());
|
|
try {
|
|
await bound(ctx, cont);
|
|
} catch (err) {
|
|
nextCalled = false;
|
|
await errorHandler(new BotError(err, ctx), cont);
|
|
}
|
|
if (nextCalled) await next();
|
|
});
|
|
return composer;
|
|
}
|
|
}
|
|
var s = 1e3;
|
|
var m = s * 60;
|
|
var h = m * 60;
|
|
var d = h * 24;
|
|
var w = d * 7;
|
|
var y = d * 365.25;
|
|
var ms = function(val, options) {
|
|
options = options || {};
|
|
var type = typeof val;
|
|
if (type === "string" && val.length > 0) {
|
|
return parse1(val);
|
|
} else if (type === "number" && isFinite(val)) {
|
|
return options.long ? fmtLong(val) : fmtShort(val);
|
|
}
|
|
throw new Error("val is not a non-empty string or a valid number. val=" + JSON.stringify(val));
|
|
};
|
|
function parse1(str) {
|
|
str = String(str);
|
|
if (str.length > 100) {
|
|
return;
|
|
}
|
|
var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(str);
|
|
if (!match) {
|
|
return;
|
|
}
|
|
var n = parseFloat(match[1]);
|
|
var type = (match[2] || "ms").toLowerCase();
|
|
switch(type){
|
|
case "years":
|
|
case "year":
|
|
case "yrs":
|
|
case "yr":
|
|
case "y":
|
|
return n * y;
|
|
case "weeks":
|
|
case "week":
|
|
case "w":
|
|
return n * w;
|
|
case "days":
|
|
case "day":
|
|
case "d":
|
|
return n * d;
|
|
case "hours":
|
|
case "hour":
|
|
case "hrs":
|
|
case "hr":
|
|
case "h":
|
|
return n * h;
|
|
case "minutes":
|
|
case "minute":
|
|
case "mins":
|
|
case "min":
|
|
case "m":
|
|
return n * m;
|
|
case "seconds":
|
|
case "second":
|
|
case "secs":
|
|
case "sec":
|
|
case "s":
|
|
return n * s;
|
|
case "milliseconds":
|
|
case "millisecond":
|
|
case "msecs":
|
|
case "msec":
|
|
case "ms":
|
|
return n;
|
|
default:
|
|
return void 0;
|
|
}
|
|
}
|
|
function fmtShort(ms2) {
|
|
var msAbs = Math.abs(ms2);
|
|
if (msAbs >= d) {
|
|
return Math.round(ms2 / d) + "d";
|
|
}
|
|
if (msAbs >= h) {
|
|
return Math.round(ms2 / h) + "h";
|
|
}
|
|
if (msAbs >= m) {
|
|
return Math.round(ms2 / m) + "m";
|
|
}
|
|
if (msAbs >= s) {
|
|
return Math.round(ms2 / s) + "s";
|
|
}
|
|
return ms2 + "ms";
|
|
}
|
|
function fmtLong(ms2) {
|
|
var msAbs = Math.abs(ms2);
|
|
if (msAbs >= d) {
|
|
return plural(ms2, msAbs, d, "day");
|
|
}
|
|
if (msAbs >= h) {
|
|
return plural(ms2, msAbs, h, "hour");
|
|
}
|
|
if (msAbs >= m) {
|
|
return plural(ms2, msAbs, m, "minute");
|
|
}
|
|
if (msAbs >= s) {
|
|
return plural(ms2, msAbs, s, "second");
|
|
}
|
|
return ms2 + " ms";
|
|
}
|
|
function plural(ms2, msAbs, n, name) {
|
|
var isPlural = msAbs >= n * 1.5;
|
|
return Math.round(ms2 / n) + " " + name + (isPlural ? "s" : "");
|
|
}
|
|
function defaultSetTimout() {
|
|
throw new Error("setTimeout has not been defined");
|
|
}
|
|
function defaultClearTimeout() {
|
|
throw new Error("clearTimeout has not been defined");
|
|
}
|
|
var cachedSetTimeout = defaultSetTimout;
|
|
var cachedClearTimeout = defaultClearTimeout;
|
|
var globalContext;
|
|
if (typeof window !== "undefined") {
|
|
globalContext = window;
|
|
} else if (typeof self !== "undefined") {
|
|
globalContext = self;
|
|
} else {
|
|
globalContext = {};
|
|
}
|
|
if (typeof globalContext.setTimeout === "function") {
|
|
cachedSetTimeout = setTimeout;
|
|
}
|
|
if (typeof globalContext.clearTimeout === "function") {
|
|
cachedClearTimeout = clearTimeout;
|
|
}
|
|
function runTimeout(fun) {
|
|
if (cachedSetTimeout === setTimeout) {
|
|
return setTimeout(fun, 0);
|
|
}
|
|
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
|
cachedSetTimeout = setTimeout;
|
|
return setTimeout(fun, 0);
|
|
}
|
|
try {
|
|
return cachedSetTimeout(fun, 0);
|
|
} catch (e) {
|
|
try {
|
|
return cachedSetTimeout.call(null, fun, 0);
|
|
} catch (e2) {
|
|
return cachedSetTimeout.call(this, fun, 0);
|
|
}
|
|
}
|
|
}
|
|
function runClearTimeout(marker) {
|
|
if (cachedClearTimeout === clearTimeout) {
|
|
return clearTimeout(marker);
|
|
}
|
|
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
|
cachedClearTimeout = clearTimeout;
|
|
return clearTimeout(marker);
|
|
}
|
|
try {
|
|
return cachedClearTimeout(marker);
|
|
} catch (e) {
|
|
try {
|
|
return cachedClearTimeout.call(null, marker);
|
|
} catch (e2) {
|
|
return cachedClearTimeout.call(this, marker);
|
|
}
|
|
}
|
|
}
|
|
var queue = [];
|
|
var draining = false;
|
|
var currentQueue;
|
|
var queueIndex = -1;
|
|
function cleanUpNextTick() {
|
|
if (!draining || !currentQueue) {
|
|
return;
|
|
}
|
|
draining = false;
|
|
if (currentQueue.length) {
|
|
queue = currentQueue.concat(queue);
|
|
} else {
|
|
queueIndex = -1;
|
|
}
|
|
if (queue.length) {
|
|
drainQueue();
|
|
}
|
|
}
|
|
function drainQueue() {
|
|
if (draining) {
|
|
return;
|
|
}
|
|
var timeout = runTimeout(cleanUpNextTick);
|
|
draining = true;
|
|
var len = queue.length;
|
|
while(len){
|
|
currentQueue = queue;
|
|
queue = [];
|
|
while(++queueIndex < len){
|
|
if (currentQueue) {
|
|
currentQueue[queueIndex].run();
|
|
}
|
|
}
|
|
queueIndex = -1;
|
|
len = queue.length;
|
|
}
|
|
currentQueue = null;
|
|
draining = false;
|
|
runClearTimeout(timeout);
|
|
}
|
|
function nextTick(fun) {
|
|
var args = new Array(arguments.length - 1);
|
|
if (arguments.length > 1) {
|
|
for(var i = 1; i < arguments.length; i++){
|
|
args[i - 1] = arguments[i];
|
|
}
|
|
}
|
|
queue.push(new Item(fun, args));
|
|
if (queue.length === 1 && !draining) {
|
|
runTimeout(drainQueue);
|
|
}
|
|
}
|
|
function Item(fun, array) {
|
|
this.fun = fun;
|
|
this.array = array;
|
|
}
|
|
Item.prototype.run = function() {
|
|
this.fun.apply(null, this.array);
|
|
};
|
|
var title = "browser";
|
|
var platform = "browser";
|
|
var browser = true;
|
|
var argv = [];
|
|
var version = "";
|
|
var versions = {};
|
|
var release = {};
|
|
var config = {};
|
|
function noop() {}
|
|
var on = noop;
|
|
var addListener = noop;
|
|
var once = noop;
|
|
var off = noop;
|
|
var removeListener = noop;
|
|
var removeAllListeners = noop;
|
|
var emit = noop;
|
|
function binding(name) {
|
|
throw new Error("process.binding is not supported");
|
|
}
|
|
function cwd() {
|
|
return "/";
|
|
}
|
|
function chdir(dir) {
|
|
throw new Error("process.chdir is not supported");
|
|
}
|
|
function umask() {
|
|
return 0;
|
|
}
|
|
var performance = globalContext.performance || {};
|
|
var performanceNow = performance.now || performance.mozNow || performance.msNow || performance.oNow || performance.webkitNow || function() {
|
|
return new Date().getTime();
|
|
};
|
|
function hrtime(previousTimestamp) {
|
|
var clocktime = performanceNow.call(performance) * 1e-3;
|
|
var seconds = Math.floor(clocktime);
|
|
var nanoseconds = Math.floor(clocktime % 1 * 1e9);
|
|
if (previousTimestamp) {
|
|
seconds = seconds - previousTimestamp[0];
|
|
nanoseconds = nanoseconds - previousTimestamp[1];
|
|
if (nanoseconds < 0) {
|
|
seconds--;
|
|
nanoseconds += 1e9;
|
|
}
|
|
}
|
|
return [
|
|
seconds,
|
|
nanoseconds
|
|
];
|
|
}
|
|
var startTime = new Date();
|
|
function uptime() {
|
|
var currentTime = new Date();
|
|
var dif = currentTime - startTime;
|
|
return dif / 1e3;
|
|
}
|
|
var process = {
|
|
nextTick,
|
|
title,
|
|
browser,
|
|
env: {
|
|
NODE_ENV: "production"
|
|
},
|
|
argv,
|
|
version,
|
|
versions,
|
|
on,
|
|
addListener,
|
|
once,
|
|
off,
|
|
removeListener,
|
|
removeAllListeners,
|
|
emit,
|
|
binding,
|
|
cwd,
|
|
chdir,
|
|
umask,
|
|
hrtime,
|
|
platform,
|
|
release,
|
|
config,
|
|
uptime
|
|
};
|
|
function createCommonjsModule(fn, basedir, module) {
|
|
return module = {
|
|
path: basedir,
|
|
exports: {},
|
|
require: function(path, base) {
|
|
return commonjsRequire(path, base === void 0 || base === null ? module.path : base);
|
|
}
|
|
}, fn(module, module.exports), module.exports;
|
|
}
|
|
function commonjsRequire() {
|
|
throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs");
|
|
}
|
|
function setup(env) {
|
|
createDebug.debug = createDebug;
|
|
createDebug.default = createDebug;
|
|
createDebug.coerce = coerce;
|
|
createDebug.disable = disable;
|
|
createDebug.enable = enable;
|
|
createDebug.enabled = enabled;
|
|
createDebug.humanize = ms;
|
|
createDebug.destroy = destroy2;
|
|
Object.keys(env).forEach((key)=>{
|
|
createDebug[key] = env[key];
|
|
});
|
|
createDebug.names = [];
|
|
createDebug.skips = [];
|
|
createDebug.formatters = {};
|
|
function selectColor(namespace) {
|
|
let hash = 0;
|
|
for(let i = 0; i < namespace.length; i++){
|
|
hash = (hash << 5) - hash + namespace.charCodeAt(i);
|
|
hash |= 0;
|
|
}
|
|
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
|
|
}
|
|
createDebug.selectColor = selectColor;
|
|
function createDebug(namespace) {
|
|
let prevTime;
|
|
let enableOverride = null;
|
|
let namespacesCache;
|
|
let enabledCache;
|
|
function debug(...args) {
|
|
if (!debug.enabled) {
|
|
return;
|
|
}
|
|
const self2 = debug;
|
|
const curr = Number(new Date());
|
|
const ms2 = curr - (prevTime || curr);
|
|
self2.diff = ms2;
|
|
self2.prev = prevTime;
|
|
self2.curr = curr;
|
|
prevTime = curr;
|
|
args[0] = createDebug.coerce(args[0]);
|
|
if (typeof args[0] !== "string") {
|
|
args.unshift("%O");
|
|
}
|
|
let index = 0;
|
|
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format)=>{
|
|
if (match === "%%") {
|
|
return "%";
|
|
}
|
|
index++;
|
|
const formatter = createDebug.formatters[format];
|
|
if (typeof formatter === "function") {
|
|
const val = args[index];
|
|
match = formatter.call(self2, val);
|
|
args.splice(index, 1);
|
|
index--;
|
|
}
|
|
return match;
|
|
});
|
|
createDebug.formatArgs.call(self2, args);
|
|
const logFn = self2.log || createDebug.log;
|
|
logFn.apply(self2, args);
|
|
}
|
|
debug.namespace = namespace;
|
|
debug.useColors = createDebug.useColors();
|
|
debug.color = createDebug.selectColor(namespace);
|
|
debug.extend = extend;
|
|
debug.destroy = createDebug.destroy;
|
|
Object.defineProperty(debug, "enabled", {
|
|
enumerable: true,
|
|
configurable: false,
|
|
get: ()=>{
|
|
if (enableOverride !== null) {
|
|
return enableOverride;
|
|
}
|
|
if (namespacesCache !== createDebug.namespaces) {
|
|
namespacesCache = createDebug.namespaces;
|
|
enabledCache = createDebug.enabled(namespace);
|
|
}
|
|
return enabledCache;
|
|
},
|
|
set: (v)=>{
|
|
enableOverride = v;
|
|
}
|
|
});
|
|
if (typeof createDebug.init === "function") {
|
|
createDebug.init(debug);
|
|
}
|
|
return debug;
|
|
}
|
|
function extend(namespace, delimiter) {
|
|
const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace);
|
|
newDebug.log = this.log;
|
|
return newDebug;
|
|
}
|
|
function enable(namespaces) {
|
|
createDebug.save(namespaces);
|
|
createDebug.namespaces = namespaces;
|
|
createDebug.names = [];
|
|
createDebug.skips = [];
|
|
let i;
|
|
const split = (typeof namespaces === "string" ? namespaces : "").split(/[\s,]+/);
|
|
const len = split.length;
|
|
for(i = 0; i < len; i++){
|
|
if (!split[i]) {
|
|
continue;
|
|
}
|
|
namespaces = split[i].replace(/\*/g, ".*?");
|
|
if (namespaces[0] === "-") {
|
|
createDebug.skips.push(new RegExp("^" + namespaces.slice(1) + "$"));
|
|
} else {
|
|
createDebug.names.push(new RegExp("^" + namespaces + "$"));
|
|
}
|
|
}
|
|
}
|
|
function disable() {
|
|
const namespaces = [
|
|
...createDebug.names.map(toNamespace),
|
|
...createDebug.skips.map(toNamespace).map((namespace)=>"-" + namespace)
|
|
].join(",");
|
|
createDebug.enable("");
|
|
return namespaces;
|
|
}
|
|
function enabled(name) {
|
|
if (name[name.length - 1] === "*") {
|
|
return true;
|
|
}
|
|
let i;
|
|
let len;
|
|
for(i = 0, len = createDebug.skips.length; i < len; i++){
|
|
if (createDebug.skips[i].test(name)) {
|
|
return false;
|
|
}
|
|
}
|
|
for(i = 0, len = createDebug.names.length; i < len; i++){
|
|
if (createDebug.names[i].test(name)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
function toNamespace(regexp) {
|
|
return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, "*");
|
|
}
|
|
function coerce(val) {
|
|
if (val instanceof Error) {
|
|
return val.stack || val.message;
|
|
}
|
|
return val;
|
|
}
|
|
function destroy2() {
|
|
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
|
|
}
|
|
createDebug.enable(createDebug.load());
|
|
return createDebug;
|
|
}
|
|
var common = setup;
|
|
var browser$1 = createCommonjsModule(function(module, exports) {
|
|
exports.formatArgs = formatArgs2;
|
|
exports.save = save2;
|
|
exports.load = load2;
|
|
exports.useColors = useColors2;
|
|
exports.storage = localstorage();
|
|
exports.destroy = (()=>{
|
|
let warned = false;
|
|
return ()=>{
|
|
if (!warned) {
|
|
warned = true;
|
|
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
|
|
}
|
|
};
|
|
})();
|
|
exports.colors = [
|
|
"#0000CC",
|
|
"#0000FF",
|
|
"#0033CC",
|
|
"#0033FF",
|
|
"#0066CC",
|
|
"#0066FF",
|
|
"#0099CC",
|
|
"#0099FF",
|
|
"#00CC00",
|
|
"#00CC33",
|
|
"#00CC66",
|
|
"#00CC99",
|
|
"#00CCCC",
|
|
"#00CCFF",
|
|
"#3300CC",
|
|
"#3300FF",
|
|
"#3333CC",
|
|
"#3333FF",
|
|
"#3366CC",
|
|
"#3366FF",
|
|
"#3399CC",
|
|
"#3399FF",
|
|
"#33CC00",
|
|
"#33CC33",
|
|
"#33CC66",
|
|
"#33CC99",
|
|
"#33CCCC",
|
|
"#33CCFF",
|
|
"#6600CC",
|
|
"#6600FF",
|
|
"#6633CC",
|
|
"#6633FF",
|
|
"#66CC00",
|
|
"#66CC33",
|
|
"#9900CC",
|
|
"#9900FF",
|
|
"#9933CC",
|
|
"#9933FF",
|
|
"#99CC00",
|
|
"#99CC33",
|
|
"#CC0000",
|
|
"#CC0033",
|
|
"#CC0066",
|
|
"#CC0099",
|
|
"#CC00CC",
|
|
"#CC00FF",
|
|
"#CC3300",
|
|
"#CC3333",
|
|
"#CC3366",
|
|
"#CC3399",
|
|
"#CC33CC",
|
|
"#CC33FF",
|
|
"#CC6600",
|
|
"#CC6633",
|
|
"#CC9900",
|
|
"#CC9933",
|
|
"#CCCC00",
|
|
"#CCCC33",
|
|
"#FF0000",
|
|
"#FF0033",
|
|
"#FF0066",
|
|
"#FF0099",
|
|
"#FF00CC",
|
|
"#FF00FF",
|
|
"#FF3300",
|
|
"#FF3333",
|
|
"#FF3366",
|
|
"#FF3399",
|
|
"#FF33CC",
|
|
"#FF33FF",
|
|
"#FF6600",
|
|
"#FF6633",
|
|
"#FF9900",
|
|
"#FF9933",
|
|
"#FFCC00",
|
|
"#FFCC33"
|
|
];
|
|
function useColors2() {
|
|
if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) {
|
|
return true;
|
|
}
|
|
if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
|
return false;
|
|
}
|
|
return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
|
|
}
|
|
function formatArgs2(args) {
|
|
args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module.exports.humanize(this.diff);
|
|
if (!this.useColors) {
|
|
return;
|
|
}
|
|
const c = "color: " + this.color;
|
|
args.splice(1, 0, c, "color: inherit");
|
|
let index = 0;
|
|
let lastC = 0;
|
|
args[0].replace(/%[a-zA-Z%]/g, (match)=>{
|
|
if (match === "%%") {
|
|
return;
|
|
}
|
|
index++;
|
|
if (match === "%c") {
|
|
lastC = index;
|
|
}
|
|
});
|
|
args.splice(lastC, 0, c);
|
|
}
|
|
exports.log = console.debug || console.log || (()=>{});
|
|
function save2(namespaces) {
|
|
try {
|
|
if (namespaces) {
|
|
exports.storage.setItem("debug", namespaces);
|
|
} else {
|
|
exports.storage.removeItem("debug");
|
|
}
|
|
} catch (error) {}
|
|
}
|
|
function load2() {
|
|
let r;
|
|
try {
|
|
r = exports.storage.getItem("debug");
|
|
} catch (error) {}
|
|
if (!r && typeof process !== "undefined" && "env" in process) {
|
|
r = process.env.DEBUG;
|
|
}
|
|
return r;
|
|
}
|
|
function localstorage() {
|
|
try {
|
|
return localStorage;
|
|
} catch (error) {}
|
|
}
|
|
module.exports = common(exports);
|
|
const { formatters } = module.exports;
|
|
formatters.j = function(v) {
|
|
try {
|
|
return JSON.stringify(v);
|
|
} catch (error) {
|
|
return "[UnexpectedJSONParseError]: " + error.message;
|
|
}
|
|
};
|
|
});
|
|
browser$1.colors;
|
|
browser$1.destroy;
|
|
browser$1.formatArgs;
|
|
browser$1.load;
|
|
browser$1.log;
|
|
browser$1.save;
|
|
browser$1.storage;
|
|
browser$1.useColors;
|
|
class DenoStdInternalError extends Error {
|
|
constructor(message){
|
|
super(message);
|
|
this.name = "DenoStdInternalError";
|
|
}
|
|
}
|
|
function assert(expr, msg = "") {
|
|
if (!expr) {
|
|
throw new DenoStdInternalError(msg);
|
|
}
|
|
}
|
|
function copy(src, dst, off = 0) {
|
|
off = Math.max(0, Math.min(off, dst.byteLength));
|
|
const dstBytesAvailable = dst.byteLength - off;
|
|
if (src.byteLength > dstBytesAvailable) {
|
|
src = src.subarray(0, dstBytesAvailable);
|
|
}
|
|
dst.set(src, off);
|
|
return src.byteLength;
|
|
}
|
|
const MAX_SIZE = 2 ** 32 - 2;
|
|
class Buffer1 {
|
|
#buf;
|
|
#off = 0;
|
|
#readable = new ReadableStream({
|
|
type: "bytes",
|
|
pull: (controller)=>{
|
|
const view = new Uint8Array(controller.byobRequest.view.buffer);
|
|
if (this.empty()) {
|
|
this.reset();
|
|
controller.close();
|
|
controller.byobRequest.respond(0);
|
|
return;
|
|
}
|
|
const nread = copy(this.#buf.subarray(this.#off), view);
|
|
this.#off += nread;
|
|
controller.byobRequest.respond(nread);
|
|
},
|
|
autoAllocateChunkSize: 16_640
|
|
});
|
|
get readable() {
|
|
return this.#readable;
|
|
}
|
|
#writable = new WritableStream({
|
|
write: (chunk)=>{
|
|
const m = this.#grow(chunk.byteLength);
|
|
copy(chunk, this.#buf, m);
|
|
}
|
|
});
|
|
get writable() {
|
|
return this.#writable;
|
|
}
|
|
constructor(ab){
|
|
this.#buf = ab === undefined ? new Uint8Array(0) : new Uint8Array(ab);
|
|
}
|
|
bytes(options = {
|
|
copy: true
|
|
}) {
|
|
if (options.copy === false) return this.#buf.subarray(this.#off);
|
|
return this.#buf.slice(this.#off);
|
|
}
|
|
empty() {
|
|
return this.#buf.byteLength <= this.#off;
|
|
}
|
|
get length() {
|
|
return this.#buf.byteLength - this.#off;
|
|
}
|
|
get capacity() {
|
|
return this.#buf.buffer.byteLength;
|
|
}
|
|
truncate(n) {
|
|
if (n === 0) {
|
|
this.reset();
|
|
return;
|
|
}
|
|
if (n < 0 || n > this.length) {
|
|
throw Error("bytes.Buffer: truncation out of range");
|
|
}
|
|
this.#reslice(this.#off + n);
|
|
}
|
|
reset() {
|
|
this.#reslice(0);
|
|
this.#off = 0;
|
|
}
|
|
#tryGrowByReslice(n) {
|
|
const l = this.#buf.byteLength;
|
|
if (n <= this.capacity - l) {
|
|
this.#reslice(l + n);
|
|
return l;
|
|
}
|
|
return -1;
|
|
}
|
|
#reslice(len) {
|
|
assert(len <= this.#buf.buffer.byteLength);
|
|
this.#buf = new Uint8Array(this.#buf.buffer, 0, len);
|
|
}
|
|
#grow(n1) {
|
|
const m = this.length;
|
|
if (m === 0 && this.#off !== 0) {
|
|
this.reset();
|
|
}
|
|
const i = this.#tryGrowByReslice(n1);
|
|
if (i >= 0) {
|
|
return i;
|
|
}
|
|
const c = this.capacity;
|
|
if (n1 <= Math.floor(c / 2) - m) {
|
|
copy(this.#buf.subarray(this.#off), this.#buf);
|
|
} else if (c + n1 > MAX_SIZE) {
|
|
throw new Error("The buffer cannot be grown beyond the maximum size.");
|
|
} else {
|
|
const buf = new Uint8Array(Math.min(2 * c + n1, MAX_SIZE));
|
|
copy(this.#buf.subarray(this.#off), buf);
|
|
this.#buf = buf;
|
|
}
|
|
this.#off = 0;
|
|
this.#reslice(Math.min(m + n1, MAX_SIZE));
|
|
return m;
|
|
}
|
|
grow(n) {
|
|
if (n < 0) {
|
|
throw Error("Buffer.grow: negative count");
|
|
}
|
|
const m = this.#grow(n);
|
|
this.#reslice(m);
|
|
}
|
|
}
|
|
function createLPS(pat) {
|
|
const lps = new Uint8Array(pat.length);
|
|
lps[0] = 0;
|
|
let prefixEnd = 0;
|
|
let i = 1;
|
|
while(i < lps.length){
|
|
if (pat[i] == pat[prefixEnd]) {
|
|
prefixEnd++;
|
|
lps[i] = prefixEnd;
|
|
i++;
|
|
} else if (prefixEnd === 0) {
|
|
lps[i] = 0;
|
|
i++;
|
|
} else {
|
|
prefixEnd = lps[prefixEnd - 1];
|
|
}
|
|
}
|
|
return lps;
|
|
}
|
|
class BytesList {
|
|
#len = 0;
|
|
#chunks = [];
|
|
constructor(){}
|
|
size() {
|
|
return this.#len;
|
|
}
|
|
add(value, start = 0, end = value.byteLength) {
|
|
if (value.byteLength === 0 || end - start === 0) {
|
|
return;
|
|
}
|
|
checkRange(start, end, value.byteLength);
|
|
this.#chunks.push({
|
|
value,
|
|
end,
|
|
start,
|
|
offset: this.#len
|
|
});
|
|
this.#len += end - start;
|
|
}
|
|
shift(n) {
|
|
if (n === 0) {
|
|
return;
|
|
}
|
|
if (this.#len <= n) {
|
|
this.#chunks = [];
|
|
this.#len = 0;
|
|
return;
|
|
}
|
|
const idx = this.getChunkIndex(n);
|
|
this.#chunks.splice(0, idx);
|
|
const [chunk] = this.#chunks;
|
|
if (chunk) {
|
|
const diff = n - chunk.offset;
|
|
chunk.start += diff;
|
|
}
|
|
let offset = 0;
|
|
for (const chunk of this.#chunks){
|
|
chunk.offset = offset;
|
|
offset += chunk.end - chunk.start;
|
|
}
|
|
this.#len = offset;
|
|
}
|
|
getChunkIndex(pos) {
|
|
let max = this.#chunks.length;
|
|
let min = 0;
|
|
while(true){
|
|
const i = min + Math.floor((max - min) / 2);
|
|
if (i < 0 || this.#chunks.length <= i) {
|
|
return -1;
|
|
}
|
|
const { offset , start , end } = this.#chunks[i];
|
|
const len = end - start;
|
|
if (offset <= pos && pos < offset + len) {
|
|
return i;
|
|
} else if (offset + len <= pos) {
|
|
min = i + 1;
|
|
} else {
|
|
max = i - 1;
|
|
}
|
|
}
|
|
}
|
|
get(i) {
|
|
if (i < 0 || this.#len <= i) {
|
|
throw new Error("out of range");
|
|
}
|
|
const idx = this.getChunkIndex(i);
|
|
const { value , offset , start } = this.#chunks[idx];
|
|
return value[start + i - offset];
|
|
}
|
|
*iterator(start = 0) {
|
|
const startIdx = this.getChunkIndex(start);
|
|
if (startIdx < 0) return;
|
|
const first = this.#chunks[startIdx];
|
|
let firstOffset = start - first.offset;
|
|
for(let i = startIdx; i < this.#chunks.length; i++){
|
|
const chunk = this.#chunks[i];
|
|
for(let j = chunk.start + firstOffset; j < chunk.end; j++){
|
|
yield chunk.value[j];
|
|
}
|
|
firstOffset = 0;
|
|
}
|
|
}
|
|
slice(start, end = this.#len) {
|
|
if (end === start) {
|
|
return new Uint8Array();
|
|
}
|
|
checkRange(start, end, this.#len);
|
|
const result = new Uint8Array(end - start);
|
|
const startIdx = this.getChunkIndex(start);
|
|
const endIdx = this.getChunkIndex(end - 1);
|
|
let written = 0;
|
|
for(let i = startIdx; i <= endIdx; i++){
|
|
const { value: chunkValue , start: chunkStart , end: chunkEnd , offset: chunkOffset } = this.#chunks[i];
|
|
const readStart = chunkStart + (i === startIdx ? start - chunkOffset : 0);
|
|
const readEnd = i === endIdx ? end - chunkOffset + chunkStart : chunkEnd;
|
|
const len = readEnd - readStart;
|
|
result.set(chunkValue.subarray(readStart, readEnd), written);
|
|
written += len;
|
|
}
|
|
return result;
|
|
}
|
|
concat() {
|
|
const result = new Uint8Array(this.#len);
|
|
let sum = 0;
|
|
for (const { value , start , end } of this.#chunks){
|
|
result.set(value.subarray(start, end), sum);
|
|
sum += end - start;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
function checkRange(start, end, len) {
|
|
if (start < 0 || len < start || end < 0 || len < end || end < start) {
|
|
throw new Error("invalid range");
|
|
}
|
|
}
|
|
class DelimiterStream extends TransformStream {
|
|
#bufs = new BytesList();
|
|
#delimiter;
|
|
#inspectIndex = 0;
|
|
#matchIndex = 0;
|
|
#delimLen;
|
|
#delimLPS;
|
|
#disp;
|
|
constructor(delimiter, options){
|
|
super({
|
|
transform: (chunk, controller)=>{
|
|
this.#handle(chunk, controller);
|
|
},
|
|
flush: (controller)=>{
|
|
controller.enqueue(this.#bufs.concat());
|
|
}
|
|
});
|
|
this.#delimiter = delimiter;
|
|
this.#delimLen = delimiter.length;
|
|
this.#delimLPS = createLPS(delimiter);
|
|
this.#disp = options?.disposition ?? "discard";
|
|
}
|
|
#handle(chunk, controller) {
|
|
this.#bufs.add(chunk);
|
|
let localIndex = 0;
|
|
while(this.#inspectIndex < this.#bufs.size()){
|
|
if (chunk[localIndex] === this.#delimiter[this.#matchIndex]) {
|
|
this.#inspectIndex++;
|
|
localIndex++;
|
|
this.#matchIndex++;
|
|
if (this.#matchIndex === this.#delimLen) {
|
|
const start = this.#inspectIndex - this.#delimLen;
|
|
const end = this.#disp == "suffix" ? this.#inspectIndex : start;
|
|
const copy = this.#bufs.slice(0, end);
|
|
controller.enqueue(copy);
|
|
const shift = this.#disp == "prefix" ? start : this.#inspectIndex;
|
|
this.#bufs.shift(shift);
|
|
this.#inspectIndex = this.#disp == "prefix" ? this.#delimLen : 0;
|
|
this.#matchIndex = 0;
|
|
}
|
|
} else {
|
|
if (this.#matchIndex === 0) {
|
|
this.#inspectIndex++;
|
|
localIndex++;
|
|
} else {
|
|
this.#matchIndex = this.#delimLPS[this.#matchIndex - 1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function readableStreamFromIterable(iterable) {
|
|
const iterator = iterable[Symbol.asyncIterator]?.() ?? iterable[Symbol.iterator]?.();
|
|
return new ReadableStream({
|
|
async pull (controller) {
|
|
const { value , done } = await iterator.next();
|
|
if (done) {
|
|
controller.close();
|
|
} else {
|
|
controller.enqueue(value);
|
|
}
|
|
},
|
|
async cancel (reason) {
|
|
if (typeof iterator.throw == "function") {
|
|
try {
|
|
await iterator.throw(reason);
|
|
} catch {}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
const baseFetchConfig = (_apiRoot)=>({});
|
|
const defaultAdapter = "cloudflare";
|
|
class GrammyError extends Error {
|
|
ok;
|
|
error_code;
|
|
description;
|
|
parameters;
|
|
constructor(message, err, method, payload){
|
|
super(`${message} (${err.error_code}: ${err.description})`);
|
|
this.method = method;
|
|
this.payload = payload;
|
|
this.ok = false;
|
|
this.name = "GrammyError";
|
|
this.error_code = err.error_code;
|
|
this.description = err.description;
|
|
this.parameters = err.parameters ?? {};
|
|
}
|
|
method;
|
|
payload;
|
|
}
|
|
function toGrammyError(err, method, payload) {
|
|
return new GrammyError(`Call to '${method}' failed!`, err, method, payload);
|
|
}
|
|
class HttpError extends Error {
|
|
constructor(message, error){
|
|
super(message);
|
|
this.error = error;
|
|
this.name = "HttpError";
|
|
}
|
|
error;
|
|
}
|
|
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);
|
|
};
|
|
}
|
|
const osType = (()=>{
|
|
const { Deno } = globalThis;
|
|
if (typeof Deno?.build?.os === "string") {
|
|
return Deno.build.os;
|
|
}
|
|
const { navigator: navigator1 } = globalThis;
|
|
if (navigator1?.appVersion?.includes?.("Win")) {
|
|
return "windows";
|
|
}
|
|
return "linux";
|
|
})();
|
|
const isWindows = osType === "windows";
|
|
const CHAR_FORWARD_SLASH = 47;
|
|
function assertPath(path) {
|
|
if (typeof path !== "string") {
|
|
throw new TypeError(`Path must be a string. Received ${JSON.stringify(path)}`);
|
|
}
|
|
}
|
|
function isPosixPathSeparator(code) {
|
|
return code === 47;
|
|
}
|
|
function isPathSeparator(code) {
|
|
return isPosixPathSeparator(code) || code === 92;
|
|
}
|
|
function isWindowsDeviceRoot(code) {
|
|
return code >= 97 && code <= 122 || code >= 65 && code <= 90;
|
|
}
|
|
function normalizeString(path, allowAboveRoot, separator, isPathSeparator) {
|
|
let res = "";
|
|
let lastSegmentLength = 0;
|
|
let lastSlash = -1;
|
|
let dots = 0;
|
|
let code;
|
|
for(let i = 0, len = path.length; i <= len; ++i){
|
|
if (i < len) code = path.charCodeAt(i);
|
|
else if (isPathSeparator(code)) break;
|
|
else code = CHAR_FORWARD_SLASH;
|
|
if (isPathSeparator(code)) {
|
|
if (lastSlash === i - 1 || dots === 1) {} else if (lastSlash !== i - 1 && dots === 2) {
|
|
if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46) {
|
|
if (res.length > 2) {
|
|
const lastSlashIndex = res.lastIndexOf(separator);
|
|
if (lastSlashIndex === -1) {
|
|
res = "";
|
|
lastSegmentLength = 0;
|
|
} else {
|
|
res = res.slice(0, lastSlashIndex);
|
|
lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
|
|
}
|
|
lastSlash = i;
|
|
dots = 0;
|
|
continue;
|
|
} else if (res.length === 2 || res.length === 1) {
|
|
res = "";
|
|
lastSegmentLength = 0;
|
|
lastSlash = i;
|
|
dots = 0;
|
|
continue;
|
|
}
|
|
}
|
|
if (allowAboveRoot) {
|
|
if (res.length > 0) res += `${separator}..`;
|
|
else res = "..";
|
|
lastSegmentLength = 2;
|
|
}
|
|
} else {
|
|
if (res.length > 0) res += separator + path.slice(lastSlash + 1, i);
|
|
else res = path.slice(lastSlash + 1, i);
|
|
lastSegmentLength = i - lastSlash - 1;
|
|
}
|
|
lastSlash = i;
|
|
dots = 0;
|
|
} else if (code === 46 && dots !== -1) {
|
|
++dots;
|
|
} else {
|
|
dots = -1;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
function _format(sep, pathObject) {
|
|
const dir = pathObject.dir || pathObject.root;
|
|
const base = pathObject.base || (pathObject.name || "") + (pathObject.ext || "");
|
|
if (!dir) return base;
|
|
if (base === sep) return dir;
|
|
if (dir === pathObject.root) return dir + base;
|
|
return dir + sep + base;
|
|
}
|
|
const WHITESPACE_ENCODINGS = {
|
|
"\u0009": "%09",
|
|
"\u000A": "%0A",
|
|
"\u000B": "%0B",
|
|
"\u000C": "%0C",
|
|
"\u000D": "%0D",
|
|
"\u0020": "%20"
|
|
};
|
|
function encodeWhitespace(string) {
|
|
return string.replaceAll(/[\s]/g, (c)=>{
|
|
return WHITESPACE_ENCODINGS[c] ?? c;
|
|
});
|
|
}
|
|
function lastPathSegment(path, isSep, start = 0) {
|
|
let matchedNonSeparator = false;
|
|
let end = path.length;
|
|
for(let i = path.length - 1; i >= start; --i){
|
|
if (isSep(path.charCodeAt(i))) {
|
|
if (matchedNonSeparator) {
|
|
start = i + 1;
|
|
break;
|
|
}
|
|
} else if (!matchedNonSeparator) {
|
|
matchedNonSeparator = true;
|
|
end = i + 1;
|
|
}
|
|
}
|
|
return path.slice(start, end);
|
|
}
|
|
function stripTrailingSeparators(segment, isSep) {
|
|
if (segment.length <= 1) {
|
|
return segment;
|
|
}
|
|
let end = segment.length;
|
|
for(let i = segment.length - 1; i > 0; i--){
|
|
if (isSep(segment.charCodeAt(i))) {
|
|
end = i;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return segment.slice(0, end);
|
|
}
|
|
function stripSuffix(name, suffix) {
|
|
if (suffix.length >= name.length) {
|
|
return name;
|
|
}
|
|
const lenDiff = name.length - suffix.length;
|
|
for(let i = suffix.length - 1; i >= 0; --i){
|
|
if (name.charCodeAt(lenDiff + i) !== suffix.charCodeAt(i)) {
|
|
return name;
|
|
}
|
|
}
|
|
return name.slice(0, -suffix.length);
|
|
}
|
|
const sep = "\\";
|
|
const delimiter = ";";
|
|
function resolve(...pathSegments) {
|
|
let resolvedDevice = "";
|
|
let resolvedTail = "";
|
|
let resolvedAbsolute = false;
|
|
for(let i = pathSegments.length - 1; i >= -1; i--){
|
|
let path;
|
|
const { Deno } = globalThis;
|
|
if (i >= 0) {
|
|
path = pathSegments[i];
|
|
} else if (!resolvedDevice) {
|
|
if (typeof Deno?.cwd !== "function") {
|
|
throw new TypeError("Resolved a drive-letter-less path without a CWD.");
|
|
}
|
|
path = Deno.cwd();
|
|
} else {
|
|
if (typeof Deno?.env?.get !== "function" || typeof Deno?.cwd !== "function") {
|
|
throw new TypeError("Resolved a relative path without a CWD.");
|
|
}
|
|
path = Deno.cwd();
|
|
if (path === undefined || path.slice(0, 3).toLowerCase() !== `${resolvedDevice.toLowerCase()}\\`) {
|
|
path = `${resolvedDevice}\\`;
|
|
}
|
|
}
|
|
assertPath(path);
|
|
const len = path.length;
|
|
if (len === 0) continue;
|
|
let rootEnd = 0;
|
|
let device = "";
|
|
let isAbsolute = false;
|
|
const code = path.charCodeAt(0);
|
|
if (len > 1) {
|
|
if (isPathSeparator(code)) {
|
|
isAbsolute = true;
|
|
if (isPathSeparator(path.charCodeAt(1))) {
|
|
let j = 2;
|
|
let last = j;
|
|
for(; j < len; ++j){
|
|
if (isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j < len && j !== last) {
|
|
const firstPart = path.slice(last, j);
|
|
last = j;
|
|
for(; j < len; ++j){
|
|
if (!isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j < len && j !== last) {
|
|
last = j;
|
|
for(; j < len; ++j){
|
|
if (isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j === len) {
|
|
device = `\\\\${firstPart}\\${path.slice(last)}`;
|
|
rootEnd = j;
|
|
} else if (j !== last) {
|
|
device = `\\\\${firstPart}\\${path.slice(last, j)}`;
|
|
rootEnd = j;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
rootEnd = 1;
|
|
}
|
|
} else if (isWindowsDeviceRoot(code)) {
|
|
if (path.charCodeAt(1) === 58) {
|
|
device = path.slice(0, 2);
|
|
rootEnd = 2;
|
|
if (len > 2) {
|
|
if (isPathSeparator(path.charCodeAt(2))) {
|
|
isAbsolute = true;
|
|
rootEnd = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (isPathSeparator(code)) {
|
|
rootEnd = 1;
|
|
isAbsolute = true;
|
|
}
|
|
if (device.length > 0 && resolvedDevice.length > 0 && device.toLowerCase() !== resolvedDevice.toLowerCase()) {
|
|
continue;
|
|
}
|
|
if (resolvedDevice.length === 0 && device.length > 0) {
|
|
resolvedDevice = device;
|
|
}
|
|
if (!resolvedAbsolute) {
|
|
resolvedTail = `${path.slice(rootEnd)}\\${resolvedTail}`;
|
|
resolvedAbsolute = isAbsolute;
|
|
}
|
|
if (resolvedAbsolute && resolvedDevice.length > 0) break;
|
|
}
|
|
resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, "\\", isPathSeparator);
|
|
return resolvedDevice + (resolvedAbsolute ? "\\" : "") + resolvedTail || ".";
|
|
}
|
|
function normalize(path) {
|
|
assertPath(path);
|
|
const len = path.length;
|
|
if (len === 0) return ".";
|
|
let rootEnd = 0;
|
|
let device;
|
|
let isAbsolute = false;
|
|
const code = path.charCodeAt(0);
|
|
if (len > 1) {
|
|
if (isPathSeparator(code)) {
|
|
isAbsolute = true;
|
|
if (isPathSeparator(path.charCodeAt(1))) {
|
|
let j = 2;
|
|
let last = j;
|
|
for(; j < len; ++j){
|
|
if (isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j < len && j !== last) {
|
|
const firstPart = path.slice(last, j);
|
|
last = j;
|
|
for(; j < len; ++j){
|
|
if (!isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j < len && j !== last) {
|
|
last = j;
|
|
for(; j < len; ++j){
|
|
if (isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j === len) {
|
|
return `\\\\${firstPart}\\${path.slice(last)}\\`;
|
|
} else if (j !== last) {
|
|
device = `\\\\${firstPart}\\${path.slice(last, j)}`;
|
|
rootEnd = j;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
rootEnd = 1;
|
|
}
|
|
} else if (isWindowsDeviceRoot(code)) {
|
|
if (path.charCodeAt(1) === 58) {
|
|
device = path.slice(0, 2);
|
|
rootEnd = 2;
|
|
if (len > 2) {
|
|
if (isPathSeparator(path.charCodeAt(2))) {
|
|
isAbsolute = true;
|
|
rootEnd = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (isPathSeparator(code)) {
|
|
return "\\";
|
|
}
|
|
let tail;
|
|
if (rootEnd < len) {
|
|
tail = normalizeString(path.slice(rootEnd), !isAbsolute, "\\", isPathSeparator);
|
|
} else {
|
|
tail = "";
|
|
}
|
|
if (tail.length === 0 && !isAbsolute) tail = ".";
|
|
if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) {
|
|
tail += "\\";
|
|
}
|
|
if (device === undefined) {
|
|
if (isAbsolute) {
|
|
if (tail.length > 0) return `\\${tail}`;
|
|
else return "\\";
|
|
} else if (tail.length > 0) {
|
|
return tail;
|
|
} else {
|
|
return "";
|
|
}
|
|
} else if (isAbsolute) {
|
|
if (tail.length > 0) return `${device}\\${tail}`;
|
|
else return `${device}\\`;
|
|
} else if (tail.length > 0) {
|
|
return device + tail;
|
|
} else {
|
|
return device;
|
|
}
|
|
}
|
|
function isAbsolute(path) {
|
|
assertPath(path);
|
|
const len = path.length;
|
|
if (len === 0) return false;
|
|
const code = path.charCodeAt(0);
|
|
if (isPathSeparator(code)) {
|
|
return true;
|
|
} else if (isWindowsDeviceRoot(code)) {
|
|
if (len > 2 && path.charCodeAt(1) === 58) {
|
|
if (isPathSeparator(path.charCodeAt(2))) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
function join(...paths) {
|
|
const pathsCount = paths.length;
|
|
if (pathsCount === 0) return ".";
|
|
let joined;
|
|
let firstPart = null;
|
|
for(let i = 0; i < pathsCount; ++i){
|
|
const path = paths[i];
|
|
assertPath(path);
|
|
if (path.length > 0) {
|
|
if (joined === undefined) joined = firstPart = path;
|
|
else joined += `\\${path}`;
|
|
}
|
|
}
|
|
if (joined === undefined) return ".";
|
|
let needsReplace = true;
|
|
let slashCount = 0;
|
|
assert(firstPart != null);
|
|
if (isPathSeparator(firstPart.charCodeAt(0))) {
|
|
++slashCount;
|
|
const firstLen = firstPart.length;
|
|
if (firstLen > 1) {
|
|
if (isPathSeparator(firstPart.charCodeAt(1))) {
|
|
++slashCount;
|
|
if (firstLen > 2) {
|
|
if (isPathSeparator(firstPart.charCodeAt(2))) ++slashCount;
|
|
else {
|
|
needsReplace = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (needsReplace) {
|
|
for(; slashCount < joined.length; ++slashCount){
|
|
if (!isPathSeparator(joined.charCodeAt(slashCount))) break;
|
|
}
|
|
if (slashCount >= 2) joined = `\\${joined.slice(slashCount)}`;
|
|
}
|
|
return normalize(joined);
|
|
}
|
|
function relative(from, to) {
|
|
assertPath(from);
|
|
assertPath(to);
|
|
if (from === to) return "";
|
|
const fromOrig = resolve(from);
|
|
const toOrig = resolve(to);
|
|
if (fromOrig === toOrig) return "";
|
|
from = fromOrig.toLowerCase();
|
|
to = toOrig.toLowerCase();
|
|
if (from === to) return "";
|
|
let fromStart = 0;
|
|
let fromEnd = from.length;
|
|
for(; fromStart < fromEnd; ++fromStart){
|
|
if (from.charCodeAt(fromStart) !== 92) break;
|
|
}
|
|
for(; fromEnd - 1 > fromStart; --fromEnd){
|
|
if (from.charCodeAt(fromEnd - 1) !== 92) break;
|
|
}
|
|
const fromLen = fromEnd - fromStart;
|
|
let toStart = 0;
|
|
let toEnd = to.length;
|
|
for(; toStart < toEnd; ++toStart){
|
|
if (to.charCodeAt(toStart) !== 92) break;
|
|
}
|
|
for(; toEnd - 1 > toStart; --toEnd){
|
|
if (to.charCodeAt(toEnd - 1) !== 92) break;
|
|
}
|
|
const toLen = toEnd - toStart;
|
|
const length = fromLen < toLen ? fromLen : toLen;
|
|
let lastCommonSep = -1;
|
|
let i = 0;
|
|
for(; i <= length; ++i){
|
|
if (i === length) {
|
|
if (toLen > length) {
|
|
if (to.charCodeAt(toStart + i) === 92) {
|
|
return toOrig.slice(toStart + i + 1);
|
|
} else if (i === 2) {
|
|
return toOrig.slice(toStart + i);
|
|
}
|
|
}
|
|
if (fromLen > length) {
|
|
if (from.charCodeAt(fromStart + i) === 92) {
|
|
lastCommonSep = i;
|
|
} else if (i === 2) {
|
|
lastCommonSep = 3;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
const fromCode = from.charCodeAt(fromStart + i);
|
|
const toCode = to.charCodeAt(toStart + i);
|
|
if (fromCode !== toCode) break;
|
|
else if (fromCode === 92) lastCommonSep = i;
|
|
}
|
|
if (i !== length && lastCommonSep === -1) {
|
|
return toOrig;
|
|
}
|
|
let out = "";
|
|
if (lastCommonSep === -1) lastCommonSep = 0;
|
|
for(i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i){
|
|
if (i === fromEnd || from.charCodeAt(i) === 92) {
|
|
if (out.length === 0) out += "..";
|
|
else out += "\\..";
|
|
}
|
|
}
|
|
if (out.length > 0) {
|
|
return out + toOrig.slice(toStart + lastCommonSep, toEnd);
|
|
} else {
|
|
toStart += lastCommonSep;
|
|
if (toOrig.charCodeAt(toStart) === 92) ++toStart;
|
|
return toOrig.slice(toStart, toEnd);
|
|
}
|
|
}
|
|
function toNamespacedPath(path) {
|
|
if (typeof path !== "string") return path;
|
|
if (path.length === 0) return "";
|
|
const resolvedPath = resolve(path);
|
|
if (resolvedPath.length >= 3) {
|
|
if (resolvedPath.charCodeAt(0) === 92) {
|
|
if (resolvedPath.charCodeAt(1) === 92) {
|
|
const code = resolvedPath.charCodeAt(2);
|
|
if (code !== 63 && code !== 46) {
|
|
return `\\\\?\\UNC\\${resolvedPath.slice(2)}`;
|
|
}
|
|
}
|
|
} else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0))) {
|
|
if (resolvedPath.charCodeAt(1) === 58 && resolvedPath.charCodeAt(2) === 92) {
|
|
return `\\\\?\\${resolvedPath}`;
|
|
}
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
function dirname(path) {
|
|
assertPath(path);
|
|
const len = path.length;
|
|
if (len === 0) return ".";
|
|
let rootEnd = -1;
|
|
let end = -1;
|
|
let matchedSlash = true;
|
|
let offset = 0;
|
|
const code = path.charCodeAt(0);
|
|
if (len > 1) {
|
|
if (isPathSeparator(code)) {
|
|
rootEnd = offset = 1;
|
|
if (isPathSeparator(path.charCodeAt(1))) {
|
|
let j = 2;
|
|
let last = j;
|
|
for(; j < len; ++j){
|
|
if (isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j < len && j !== last) {
|
|
last = j;
|
|
for(; j < len; ++j){
|
|
if (!isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j < len && j !== last) {
|
|
last = j;
|
|
for(; j < len; ++j){
|
|
if (isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j === len) {
|
|
return path;
|
|
}
|
|
if (j !== last) {
|
|
rootEnd = offset = j + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (isWindowsDeviceRoot(code)) {
|
|
if (path.charCodeAt(1) === 58) {
|
|
rootEnd = offset = 2;
|
|
if (len > 2) {
|
|
if (isPathSeparator(path.charCodeAt(2))) rootEnd = offset = 3;
|
|
}
|
|
}
|
|
}
|
|
} else if (isPathSeparator(code)) {
|
|
return path;
|
|
}
|
|
for(let i = len - 1; i >= offset; --i){
|
|
if (isPathSeparator(path.charCodeAt(i))) {
|
|
if (!matchedSlash) {
|
|
end = i;
|
|
break;
|
|
}
|
|
} else {
|
|
matchedSlash = false;
|
|
}
|
|
}
|
|
if (end === -1) {
|
|
if (rootEnd === -1) return ".";
|
|
else end = rootEnd;
|
|
}
|
|
return stripTrailingSeparators(path.slice(0, end), isPosixPathSeparator);
|
|
}
|
|
function basename(path, suffix = "") {
|
|
assertPath(path);
|
|
if (path.length === 0) return path;
|
|
if (typeof suffix !== "string") {
|
|
throw new TypeError(`Suffix must be a string. Received ${JSON.stringify(suffix)}`);
|
|
}
|
|
let start = 0;
|
|
if (path.length >= 2) {
|
|
const drive = path.charCodeAt(0);
|
|
if (isWindowsDeviceRoot(drive)) {
|
|
if (path.charCodeAt(1) === 58) start = 2;
|
|
}
|
|
}
|
|
const lastSegment = lastPathSegment(path, isPathSeparator, start);
|
|
const strippedSegment = stripTrailingSeparators(lastSegment, isPathSeparator);
|
|
return suffix ? stripSuffix(strippedSegment, suffix) : strippedSegment;
|
|
}
|
|
function extname(path) {
|
|
assertPath(path);
|
|
let start = 0;
|
|
let startDot = -1;
|
|
let startPart = 0;
|
|
let end = -1;
|
|
let matchedSlash = true;
|
|
let preDotState = 0;
|
|
if (path.length >= 2 && path.charCodeAt(1) === 58 && isWindowsDeviceRoot(path.charCodeAt(0))) {
|
|
start = startPart = 2;
|
|
}
|
|
for(let i = path.length - 1; i >= start; --i){
|
|
const code = path.charCodeAt(i);
|
|
if (isPathSeparator(code)) {
|
|
if (!matchedSlash) {
|
|
startPart = i + 1;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if (end === -1) {
|
|
matchedSlash = false;
|
|
end = i + 1;
|
|
}
|
|
if (code === 46) {
|
|
if (startDot === -1) startDot = i;
|
|
else if (preDotState !== 1) preDotState = 1;
|
|
} else if (startDot !== -1) {
|
|
preDotState = -1;
|
|
}
|
|
}
|
|
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
|
|
return "";
|
|
}
|
|
return path.slice(startDot, end);
|
|
}
|
|
function format(pathObject) {
|
|
if (pathObject === null || typeof pathObject !== "object") {
|
|
throw new TypeError(`The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`);
|
|
}
|
|
return _format("\\", pathObject);
|
|
}
|
|
function parse2(path) {
|
|
assertPath(path);
|
|
const ret = {
|
|
root: "",
|
|
dir: "",
|
|
base: "",
|
|
ext: "",
|
|
name: ""
|
|
};
|
|
const len = path.length;
|
|
if (len === 0) return ret;
|
|
let rootEnd = 0;
|
|
let code = path.charCodeAt(0);
|
|
if (len > 1) {
|
|
if (isPathSeparator(code)) {
|
|
rootEnd = 1;
|
|
if (isPathSeparator(path.charCodeAt(1))) {
|
|
let j = 2;
|
|
let last = j;
|
|
for(; j < len; ++j){
|
|
if (isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j < len && j !== last) {
|
|
last = j;
|
|
for(; j < len; ++j){
|
|
if (!isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j < len && j !== last) {
|
|
last = j;
|
|
for(; j < len; ++j){
|
|
if (isPathSeparator(path.charCodeAt(j))) break;
|
|
}
|
|
if (j === len) {
|
|
rootEnd = j;
|
|
} else if (j !== last) {
|
|
rootEnd = j + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (isWindowsDeviceRoot(code)) {
|
|
if (path.charCodeAt(1) === 58) {
|
|
rootEnd = 2;
|
|
if (len > 2) {
|
|
if (isPathSeparator(path.charCodeAt(2))) {
|
|
if (len === 3) {
|
|
ret.root = ret.dir = path;
|
|
ret.base = "\\";
|
|
return ret;
|
|
}
|
|
rootEnd = 3;
|
|
}
|
|
} else {
|
|
ret.root = ret.dir = path;
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
} else if (isPathSeparator(code)) {
|
|
ret.root = ret.dir = path;
|
|
ret.base = "\\";
|
|
return ret;
|
|
}
|
|
if (rootEnd > 0) ret.root = path.slice(0, rootEnd);
|
|
let startDot = -1;
|
|
let startPart = rootEnd;
|
|
let end = -1;
|
|
let matchedSlash = true;
|
|
let i = path.length - 1;
|
|
let preDotState = 0;
|
|
for(; i >= rootEnd; --i){
|
|
code = path.charCodeAt(i);
|
|
if (isPathSeparator(code)) {
|
|
if (!matchedSlash) {
|
|
startPart = i + 1;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if (end === -1) {
|
|
matchedSlash = false;
|
|
end = i + 1;
|
|
}
|
|
if (code === 46) {
|
|
if (startDot === -1) startDot = i;
|
|
else if (preDotState !== 1) preDotState = 1;
|
|
} else if (startDot !== -1) {
|
|
preDotState = -1;
|
|
}
|
|
}
|
|
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
|
|
if (end !== -1) {
|
|
ret.base = ret.name = path.slice(startPart, end);
|
|
}
|
|
} else {
|
|
ret.name = path.slice(startPart, startDot);
|
|
ret.base = path.slice(startPart, end);
|
|
ret.ext = path.slice(startDot, end);
|
|
}
|
|
ret.base = ret.base || "\\";
|
|
if (startPart > 0 && startPart !== rootEnd) {
|
|
ret.dir = path.slice(0, startPart - 1);
|
|
} else ret.dir = ret.root;
|
|
return ret;
|
|
}
|
|
function fromFileUrl(url) {
|
|
url = url instanceof URL ? url : new URL(url);
|
|
if (url.protocol != "file:") {
|
|
throw new TypeError("Must be a file URL.");
|
|
}
|
|
let path = decodeURIComponent(url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25")).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\");
|
|
if (url.hostname != "") {
|
|
path = `\\\\${url.hostname}${path}`;
|
|
}
|
|
return path;
|
|
}
|
|
function toFileUrl(path) {
|
|
if (!isAbsolute(path)) {
|
|
throw new TypeError("Must be an absolute path.");
|
|
}
|
|
const [, hostname, pathname] = path.match(/^(?:[/\\]{2}([^/\\]+)(?=[/\\](?:[^/\\]|$)))?(.*)/);
|
|
const url = new URL("file:///");
|
|
url.pathname = encodeWhitespace(pathname.replace(/%/g, "%25"));
|
|
if (hostname != null && hostname != "localhost") {
|
|
url.hostname = hostname;
|
|
if (!url.hostname) {
|
|
throw new TypeError("Invalid hostname.");
|
|
}
|
|
}
|
|
return url;
|
|
}
|
|
const mod = {
|
|
sep: sep,
|
|
delimiter: delimiter,
|
|
resolve: resolve,
|
|
normalize: normalize,
|
|
isAbsolute: isAbsolute,
|
|
join: join,
|
|
relative: relative,
|
|
toNamespacedPath: toNamespacedPath,
|
|
dirname: dirname,
|
|
basename: basename,
|
|
extname: extname,
|
|
format: format,
|
|
parse: parse2,
|
|
fromFileUrl: fromFileUrl,
|
|
toFileUrl: toFileUrl
|
|
};
|
|
const sep1 = "/";
|
|
const delimiter1 = ":";
|
|
function resolve1(...pathSegments) {
|
|
let resolvedPath = "";
|
|
let resolvedAbsolute = false;
|
|
for(let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--){
|
|
let path;
|
|
if (i >= 0) path = pathSegments[i];
|
|
else {
|
|
const { Deno } = globalThis;
|
|
if (typeof Deno?.cwd !== "function") {
|
|
throw new TypeError("Resolved a relative path without a CWD.");
|
|
}
|
|
path = Deno.cwd();
|
|
}
|
|
assertPath(path);
|
|
if (path.length === 0) {
|
|
continue;
|
|
}
|
|
resolvedPath = `${path}/${resolvedPath}`;
|
|
resolvedAbsolute = isPosixPathSeparator(path.charCodeAt(0));
|
|
}
|
|
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, "/", isPosixPathSeparator);
|
|
if (resolvedAbsolute) {
|
|
if (resolvedPath.length > 0) return `/${resolvedPath}`;
|
|
else return "/";
|
|
} else if (resolvedPath.length > 0) return resolvedPath;
|
|
else return ".";
|
|
}
|
|
function normalize1(path) {
|
|
assertPath(path);
|
|
if (path.length === 0) return ".";
|
|
const isAbsolute = isPosixPathSeparator(path.charCodeAt(0));
|
|
const trailingSeparator = isPosixPathSeparator(path.charCodeAt(path.length - 1));
|
|
path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator);
|
|
if (path.length === 0 && !isAbsolute) path = ".";
|
|
if (path.length > 0 && trailingSeparator) path += "/";
|
|
if (isAbsolute) return `/${path}`;
|
|
return path;
|
|
}
|
|
function isAbsolute1(path) {
|
|
assertPath(path);
|
|
return path.length > 0 && isPosixPathSeparator(path.charCodeAt(0));
|
|
}
|
|
function join1(...paths) {
|
|
if (paths.length === 0) return ".";
|
|
let joined;
|
|
for(let i = 0, len = paths.length; i < len; ++i){
|
|
const path = paths[i];
|
|
assertPath(path);
|
|
if (path.length > 0) {
|
|
if (!joined) joined = path;
|
|
else joined += `/${path}`;
|
|
}
|
|
}
|
|
if (!joined) return ".";
|
|
return normalize1(joined);
|
|
}
|
|
function relative1(from, to) {
|
|
assertPath(from);
|
|
assertPath(to);
|
|
if (from === to) return "";
|
|
from = resolve1(from);
|
|
to = resolve1(to);
|
|
if (from === to) return "";
|
|
let fromStart = 1;
|
|
const fromEnd = from.length;
|
|
for(; fromStart < fromEnd; ++fromStart){
|
|
if (!isPosixPathSeparator(from.charCodeAt(fromStart))) break;
|
|
}
|
|
const fromLen = fromEnd - fromStart;
|
|
let toStart = 1;
|
|
const toEnd = to.length;
|
|
for(; toStart < toEnd; ++toStart){
|
|
if (!isPosixPathSeparator(to.charCodeAt(toStart))) break;
|
|
}
|
|
const toLen = toEnd - toStart;
|
|
const length = fromLen < toLen ? fromLen : toLen;
|
|
let lastCommonSep = -1;
|
|
let i = 0;
|
|
for(; i <= length; ++i){
|
|
if (i === length) {
|
|
if (toLen > length) {
|
|
if (isPosixPathSeparator(to.charCodeAt(toStart + i))) {
|
|
return to.slice(toStart + i + 1);
|
|
} else if (i === 0) {
|
|
return to.slice(toStart + i);
|
|
}
|
|
} else if (fromLen > length) {
|
|
if (isPosixPathSeparator(from.charCodeAt(fromStart + i))) {
|
|
lastCommonSep = i;
|
|
} else if (i === 0) {
|
|
lastCommonSep = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
const fromCode = from.charCodeAt(fromStart + i);
|
|
const toCode = to.charCodeAt(toStart + i);
|
|
if (fromCode !== toCode) break;
|
|
else if (isPosixPathSeparator(fromCode)) lastCommonSep = i;
|
|
}
|
|
let out = "";
|
|
for(i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i){
|
|
if (i === fromEnd || isPosixPathSeparator(from.charCodeAt(i))) {
|
|
if (out.length === 0) out += "..";
|
|
else out += "/..";
|
|
}
|
|
}
|
|
if (out.length > 0) return out + to.slice(toStart + lastCommonSep);
|
|
else {
|
|
toStart += lastCommonSep;
|
|
if (isPosixPathSeparator(to.charCodeAt(toStart))) ++toStart;
|
|
return to.slice(toStart);
|
|
}
|
|
}
|
|
function toNamespacedPath1(path) {
|
|
return path;
|
|
}
|
|
function dirname1(path) {
|
|
if (path.length === 0) return ".";
|
|
let end = -1;
|
|
let matchedNonSeparator = false;
|
|
for(let i = path.length - 1; i >= 1; --i){
|
|
if (isPosixPathSeparator(path.charCodeAt(i))) {
|
|
if (matchedNonSeparator) {
|
|
end = i;
|
|
break;
|
|
}
|
|
} else {
|
|
matchedNonSeparator = true;
|
|
}
|
|
}
|
|
if (end === -1) {
|
|
return isPosixPathSeparator(path.charCodeAt(0)) ? "/" : ".";
|
|
}
|
|
return stripTrailingSeparators(path.slice(0, end), isPosixPathSeparator);
|
|
}
|
|
function basename1(path, suffix = "") {
|
|
assertPath(path);
|
|
if (path.length === 0) return path;
|
|
if (typeof suffix !== "string") {
|
|
throw new TypeError(`Suffix must be a string. Received ${JSON.stringify(suffix)}`);
|
|
}
|
|
const lastSegment = lastPathSegment(path, isPosixPathSeparator);
|
|
const strippedSegment = stripTrailingSeparators(lastSegment, isPosixPathSeparator);
|
|
return suffix ? stripSuffix(strippedSegment, suffix) : strippedSegment;
|
|
}
|
|
function extname1(path) {
|
|
assertPath(path);
|
|
let startDot = -1;
|
|
let startPart = 0;
|
|
let end = -1;
|
|
let matchedSlash = true;
|
|
let preDotState = 0;
|
|
for(let i = path.length - 1; i >= 0; --i){
|
|
const code = path.charCodeAt(i);
|
|
if (isPosixPathSeparator(code)) {
|
|
if (!matchedSlash) {
|
|
startPart = i + 1;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if (end === -1) {
|
|
matchedSlash = false;
|
|
end = i + 1;
|
|
}
|
|
if (code === 46) {
|
|
if (startDot === -1) startDot = i;
|
|
else if (preDotState !== 1) preDotState = 1;
|
|
} else if (startDot !== -1) {
|
|
preDotState = -1;
|
|
}
|
|
}
|
|
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
|
|
return "";
|
|
}
|
|
return path.slice(startDot, end);
|
|
}
|
|
function format1(pathObject) {
|
|
if (pathObject === null || typeof pathObject !== "object") {
|
|
throw new TypeError(`The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`);
|
|
}
|
|
return _format("/", pathObject);
|
|
}
|
|
function parse3(path) {
|
|
assertPath(path);
|
|
const ret = {
|
|
root: "",
|
|
dir: "",
|
|
base: "",
|
|
ext: "",
|
|
name: ""
|
|
};
|
|
if (path.length === 0) return ret;
|
|
const isAbsolute = isPosixPathSeparator(path.charCodeAt(0));
|
|
let start;
|
|
if (isAbsolute) {
|
|
ret.root = "/";
|
|
start = 1;
|
|
} else {
|
|
start = 0;
|
|
}
|
|
let startDot = -1;
|
|
let startPart = 0;
|
|
let end = -1;
|
|
let matchedSlash = true;
|
|
let i = path.length - 1;
|
|
let preDotState = 0;
|
|
for(; i >= start; --i){
|
|
const code = path.charCodeAt(i);
|
|
if (isPosixPathSeparator(code)) {
|
|
if (!matchedSlash) {
|
|
startPart = i + 1;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if (end === -1) {
|
|
matchedSlash = false;
|
|
end = i + 1;
|
|
}
|
|
if (code === 46) {
|
|
if (startDot === -1) startDot = i;
|
|
else if (preDotState !== 1) preDotState = 1;
|
|
} else if (startDot !== -1) {
|
|
preDotState = -1;
|
|
}
|
|
}
|
|
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
|
|
if (end !== -1) {
|
|
if (startPart === 0 && isAbsolute) {
|
|
ret.base = ret.name = path.slice(1, end);
|
|
} else {
|
|
ret.base = ret.name = path.slice(startPart, end);
|
|
}
|
|
}
|
|
ret.base = ret.base || "/";
|
|
} else {
|
|
if (startPart === 0 && isAbsolute) {
|
|
ret.name = path.slice(1, startDot);
|
|
ret.base = path.slice(1, end);
|
|
} else {
|
|
ret.name = path.slice(startPart, startDot);
|
|
ret.base = path.slice(startPart, end);
|
|
}
|
|
ret.ext = path.slice(startDot, end);
|
|
}
|
|
if (startPart > 0) {
|
|
ret.dir = stripTrailingSeparators(path.slice(0, startPart - 1), isPosixPathSeparator);
|
|
} else if (isAbsolute) ret.dir = "/";
|
|
return ret;
|
|
}
|
|
function fromFileUrl1(url) {
|
|
url = url instanceof URL ? url : new URL(url);
|
|
if (url.protocol != "file:") {
|
|
throw new TypeError("Must be a file URL.");
|
|
}
|
|
return decodeURIComponent(url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25"));
|
|
}
|
|
function toFileUrl1(path) {
|
|
if (!isAbsolute1(path)) {
|
|
throw new TypeError("Must be an absolute path.");
|
|
}
|
|
const url = new URL("file:///");
|
|
url.pathname = encodeWhitespace(path.replace(/%/g, "%25").replace(/\\/g, "%5C"));
|
|
return url;
|
|
}
|
|
const mod1 = {
|
|
sep: sep1,
|
|
delimiter: delimiter1,
|
|
resolve: resolve1,
|
|
normalize: normalize1,
|
|
isAbsolute: isAbsolute1,
|
|
join: join1,
|
|
relative: relative1,
|
|
toNamespacedPath: toNamespacedPath1,
|
|
dirname: dirname1,
|
|
basename: basename1,
|
|
extname: extname1,
|
|
format: format1,
|
|
parse: parse3,
|
|
fromFileUrl: fromFileUrl1,
|
|
toFileUrl: toFileUrl1
|
|
};
|
|
const path = isWindows ? mod : mod1;
|
|
const { join: join2 , normalize: normalize2 } = path;
|
|
const path1 = isWindows ? mod : mod1;
|
|
const { basename: basename2 , delimiter: delimiter2 , dirname: dirname2 , extname: extname2 , format: format2 , fromFileUrl: fromFileUrl2 , isAbsolute: isAbsolute2 , join: join3 , normalize: normalize3 , parse: parse4 , relative: relative2 , resolve: resolve2 , toFileUrl: toFileUrl2 , toNamespacedPath: toNamespacedPath2 } = path1;
|
|
class InputFile {
|
|
consumed = false;
|
|
fileData;
|
|
filename;
|
|
constructor(file, filename){
|
|
this.fileData = file;
|
|
filename ??= this.guessFilename(file);
|
|
this.filename = filename;
|
|
}
|
|
guessFilename(file) {
|
|
if (typeof file === "string") return basename2(file);
|
|
if (typeof file !== "object") return undefined;
|
|
if ("url" in file) return basename2(file.url);
|
|
if (!(file instanceof URL)) return undefined;
|
|
return basename2(file.pathname) || basename2(file.hostname);
|
|
}
|
|
toRaw() {
|
|
if (this.consumed) {
|
|
throw new Error("Cannot reuse InputFile data source!");
|
|
}
|
|
const data = this.fileData;
|
|
if (data instanceof Blob) return data.stream();
|
|
if (data instanceof URL) return fetchFile(data);
|
|
if ("url" in data) return fetchFile(data.url);
|
|
if (!(data instanceof Uint8Array)) this.consumed = true;
|
|
return data;
|
|
}
|
|
}
|
|
async function* fetchFile(url) {
|
|
const { body } = await fetch(url);
|
|
if (body === null) {
|
|
throw new Error(`Download failed, no response body from '${url}'`);
|
|
}
|
|
yield* body;
|
|
}
|
|
function requiresFormDataUpload(payload) {
|
|
return payload instanceof InputFile || typeof payload === "object" && payload !== null && Object.values(payload).some((v)=>Array.isArray(v) ? v.some(requiresFormDataUpload) : v instanceof InputFile || requiresFormDataUpload(v));
|
|
}
|
|
function str(value) {
|
|
return JSON.stringify(value, (_, v)=>v ?? undefined);
|
|
}
|
|
function createJsonPayload(payload) {
|
|
return {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/json",
|
|
connection: "keep-alive"
|
|
},
|
|
body: str(payload)
|
|
};
|
|
}
|
|
async function* protectItr(itr, onError) {
|
|
try {
|
|
yield* itr;
|
|
} catch (err) {
|
|
onError(err);
|
|
}
|
|
}
|
|
function createFormDataPayload(payload, onError) {
|
|
const boundary = createBoundary();
|
|
const itr = payloadToMultipartItr(payload, boundary);
|
|
const safeItr = protectItr(itr, onError);
|
|
const stream = readableStreamFromIterable(safeItr);
|
|
return {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": `multipart/form-data; boundary=${boundary}`,
|
|
connection: "keep-alive"
|
|
},
|
|
body: stream
|
|
};
|
|
}
|
|
function createBoundary() {
|
|
return "----------" + randomId(32);
|
|
}
|
|
function randomId(length = 16) {
|
|
return Array.from(Array(length)).map(()=>Math.random().toString(36)[2] || 0).join("");
|
|
}
|
|
const enc = new TextEncoder();
|
|
async function* payloadToMultipartItr(payload, boundary) {
|
|
const files = extractFiles(payload);
|
|
yield enc.encode(`--${boundary}\r\n`);
|
|
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;
|
|
}
|
|
for (const { id , origin , file } of files){
|
|
if (!first) yield separator;
|
|
yield* filePart(id, origin, file);
|
|
first = false;
|
|
}
|
|
yield enc.encode(`\r\n--${boundary}--\r\n`);
|
|
}
|
|
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 InputFile) {
|
|
const id = randomId();
|
|
Object.assign(value, {
|
|
[k]: `attach://${id}`
|
|
});
|
|
const origin = k === "media" && "type" in value && typeof value.type === "string" ? value.type : k;
|
|
return {
|
|
id,
|
|
origin,
|
|
file: v
|
|
};
|
|
} else return extractFiles(v);
|
|
});
|
|
}
|
|
function valuePart(key, value) {
|
|
return enc.encode(`content-disposition:form-data;name="${key}"\r\n\r\n${value}`);
|
|
}
|
|
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;
|
|
}
|
|
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";
|
|
}
|
|
}
|
|
const debug = browser$1("grammy:core");
|
|
function concatTransformer(prev, trans) {
|
|
return (method, payload, signal)=>trans(prev, method, payload, signal);
|
|
}
|
|
class ApiClient {
|
|
options;
|
|
hasUsedWebhookReply;
|
|
installedTransformers;
|
|
constructor(token, options = {}, webhookReplyEnvelope = {}){
|
|
this.token = token;
|
|
this.webhookReplyEnvelope = webhookReplyEnvelope;
|
|
this.hasUsedWebhookReply = false;
|
|
this.installedTransformers = [];
|
|
this.call = async (method, p, signal)=>{
|
|
const payload = p ?? {};
|
|
debug(`Calling ${method}`);
|
|
const opts = this.options;
|
|
const formDataRequired = requiresFormDataUpload(payload);
|
|
if (this.webhookReplyEnvelope.send !== undefined && !this.hasUsedWebhookReply && !formDataRequired && opts.canUseWebhookReply(method)) {
|
|
this.hasUsedWebhookReply = true;
|
|
const config = createJsonPayload({
|
|
...payload,
|
|
method
|
|
});
|
|
await this.webhookReplyEnvelope.send(config.body);
|
|
return {
|
|
ok: true,
|
|
result: true
|
|
};
|
|
}
|
|
const controller = createAbortControllerFromSignal(signal);
|
|
const timeout = createTimeout(controller, opts.timeoutSeconds, method);
|
|
const streamErr = createStreamError(controller);
|
|
const url = opts.buildUrl(opts.apiRoot, this.token, method);
|
|
const config = formDataRequired ? createFormDataPayload(payload, (err)=>streamErr.catch(err)) : createJsonPayload(payload);
|
|
const sig = controller.signal;
|
|
const options = {
|
|
...opts.baseFetchConfig,
|
|
signal: sig,
|
|
...config
|
|
};
|
|
const successPromise = fetch(url instanceof URL ? url.href : url, options).catch(toHttpError(method, opts.sensitiveLogs));
|
|
const operations = [
|
|
successPromise,
|
|
streamErr.promise,
|
|
timeout.promise
|
|
];
|
|
try {
|
|
const res = await Promise.race(operations);
|
|
return await res.json();
|
|
} finally{
|
|
if (timeout.handle !== undefined) clearTimeout(timeout.handle);
|
|
}
|
|
};
|
|
const apiRoot = options.apiRoot ?? "https://api.telegram.org";
|
|
this.options = {
|
|
apiRoot,
|
|
buildUrl: options.buildUrl ?? ((root, token, method)=>`${root}/bot${token}/${method}`),
|
|
timeoutSeconds: options.timeoutSeconds ?? 500,
|
|
baseFetchConfig: {
|
|
...baseFetchConfig(apiRoot),
|
|
...options.baseFetchConfig
|
|
},
|
|
canUseWebhookReply: options.canUseWebhookReply ?? (()=>false),
|
|
sensitiveLogs: options.sensitiveLogs ?? 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}')`);
|
|
}
|
|
}
|
|
call;
|
|
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 toGrammyError(data, method, payload);
|
|
}
|
|
token;
|
|
webhookReplyEnvelope;
|
|
}
|
|
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;
|
|
}
|
|
const proxyMethods = {
|
|
set () {
|
|
return false;
|
|
},
|
|
defineProperty () {
|
|
return false;
|
|
},
|
|
deleteProperty () {
|
|
return false;
|
|
},
|
|
ownKeys () {
|
|
return [];
|
|
}
|
|
};
|
|
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
|
|
};
|
|
}
|
|
function createStreamError(abortController) {
|
|
let onError = (err)=>{
|
|
throw err;
|
|
};
|
|
const promise = new Promise((_, reject)=>{
|
|
onError = (err)=>{
|
|
reject(err);
|
|
abortController.abort();
|
|
};
|
|
});
|
|
return {
|
|
promise,
|
|
catch: onError
|
|
};
|
|
}
|
|
function createAbortControllerFromSignal(signal) {
|
|
const abortController = new 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
|
|
};
|
|
}
|
|
class Api {
|
|
raw;
|
|
config;
|
|
constructor(token, config, webhookReplyEnvelope){
|
|
const { raw , use , installedTransformers } = createRawApi(token, config, webhookReplyEnvelope);
|
|
this.raw = raw;
|
|
this.config = {
|
|
use,
|
|
installedTransformers: ()=>[
|
|
...installedTransformers
|
|
]
|
|
};
|
|
}
|
|
getUpdates(other, signal) {
|
|
return this.raw.getUpdates({
|
|
...other
|
|
}, signal);
|
|
}
|
|
setWebhook(url, other, signal) {
|
|
return this.raw.setWebhook({
|
|
url,
|
|
...other
|
|
}, signal);
|
|
}
|
|
deleteWebhook(other, signal) {
|
|
return this.raw.deleteWebhook({
|
|
...other
|
|
}, signal);
|
|
}
|
|
getWebhookInfo(signal) {
|
|
return this.raw.getWebhookInfo(signal);
|
|
}
|
|
getMe(signal) {
|
|
return this.raw.getMe(signal);
|
|
}
|
|
logOut(signal) {
|
|
return this.raw.logOut(signal);
|
|
}
|
|
close(signal) {
|
|
return this.raw.close(signal);
|
|
}
|
|
sendMessage(chat_id, text, other, signal) {
|
|
return this.raw.sendMessage({
|
|
chat_id,
|
|
text,
|
|
...other
|
|
}, signal);
|
|
}
|
|
forwardMessage(chat_id, from_chat_id, message_id, other, signal) {
|
|
return this.raw.forwardMessage({
|
|
chat_id,
|
|
from_chat_id,
|
|
message_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
copyMessage(chat_id, from_chat_id, message_id, other, signal) {
|
|
return this.raw.copyMessage({
|
|
chat_id,
|
|
from_chat_id,
|
|
message_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendPhoto(chat_id, photo, other, signal) {
|
|
return this.raw.sendPhoto({
|
|
chat_id,
|
|
photo,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendAudio(chat_id, audio, other, signal) {
|
|
return this.raw.sendAudio({
|
|
chat_id,
|
|
audio,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendDocument(chat_id, document1, other, signal) {
|
|
return this.raw.sendDocument({
|
|
chat_id,
|
|
document: document1,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendVideo(chat_id, video, other, signal) {
|
|
return this.raw.sendVideo({
|
|
chat_id,
|
|
video,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendAnimation(chat_id, animation, other, signal) {
|
|
return this.raw.sendAnimation({
|
|
chat_id,
|
|
animation,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendVoice(chat_id, voice, other, signal) {
|
|
return this.raw.sendVoice({
|
|
chat_id,
|
|
voice,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendVideoNote(chat_id, video_note, other, signal) {
|
|
return this.raw.sendVideoNote({
|
|
chat_id,
|
|
video_note,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendMediaGroup(chat_id, media, other, signal) {
|
|
return this.raw.sendMediaGroup({
|
|
chat_id,
|
|
media,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendLocation(chat_id, latitude, longitude, other, signal) {
|
|
return this.raw.sendLocation({
|
|
chat_id,
|
|
latitude,
|
|
longitude,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editMessageLiveLocation(chat_id, message_id, latitude, longitude, other, signal) {
|
|
return this.raw.editMessageLiveLocation({
|
|
chat_id,
|
|
message_id,
|
|
latitude,
|
|
longitude,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editMessageLiveLocationInline(inline_message_id, latitude, longitude, other, signal) {
|
|
return this.raw.editMessageLiveLocation({
|
|
inline_message_id,
|
|
latitude,
|
|
longitude,
|
|
...other
|
|
}, signal);
|
|
}
|
|
stopMessageLiveLocation(chat_id, message_id, other, signal) {
|
|
return this.raw.stopMessageLiveLocation({
|
|
chat_id,
|
|
message_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
stopMessageLiveLocationInline(inline_message_id, other, signal) {
|
|
return this.raw.stopMessageLiveLocation({
|
|
inline_message_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendVenue(chat_id, latitude, longitude, title, address, other, signal) {
|
|
return this.raw.sendVenue({
|
|
chat_id,
|
|
latitude,
|
|
longitude,
|
|
title,
|
|
address,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendContact(chat_id, phone_number, first_name, other, signal) {
|
|
return this.raw.sendContact({
|
|
chat_id,
|
|
phone_number,
|
|
first_name,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendPoll(chat_id, question, options, other, signal) {
|
|
return this.raw.sendPoll({
|
|
chat_id,
|
|
question,
|
|
options,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendDice(chat_id, emoji, other, signal) {
|
|
return this.raw.sendDice({
|
|
chat_id,
|
|
emoji,
|
|
...other
|
|
}, signal);
|
|
}
|
|
sendChatAction(chat_id, action, other, signal) {
|
|
return this.raw.sendChatAction({
|
|
chat_id,
|
|
action,
|
|
...other
|
|
}, signal);
|
|
}
|
|
getUserProfilePhotos(user_id, other, signal) {
|
|
return this.raw.getUserProfilePhotos({
|
|
user_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
getFile(file_id, signal) {
|
|
return this.raw.getFile({
|
|
file_id
|
|
}, signal);
|
|
}
|
|
kickChatMember(...args) {
|
|
return this.banChatMember(...args);
|
|
}
|
|
banChatMember(chat_id, user_id, other, signal) {
|
|
return this.raw.banChatMember({
|
|
chat_id,
|
|
user_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
unbanChatMember(chat_id, user_id, other, signal) {
|
|
return this.raw.unbanChatMember({
|
|
chat_id,
|
|
user_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
restrictChatMember(chat_id, user_id, permissions, other, signal) {
|
|
return this.raw.restrictChatMember({
|
|
chat_id,
|
|
user_id,
|
|
permissions,
|
|
...other
|
|
}, signal);
|
|
}
|
|
promoteChatMember(chat_id, user_id, other, signal) {
|
|
return this.raw.promoteChatMember({
|
|
chat_id,
|
|
user_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
setChatAdministratorCustomTitle(chat_id, user_id, custom_title, signal) {
|
|
return this.raw.setChatAdministratorCustomTitle({
|
|
chat_id,
|
|
user_id,
|
|
custom_title
|
|
}, signal);
|
|
}
|
|
banChatSenderChat(chat_id, sender_chat_id, signal) {
|
|
return this.raw.banChatSenderChat({
|
|
chat_id,
|
|
sender_chat_id
|
|
}, signal);
|
|
}
|
|
unbanChatSenderChat(chat_id, sender_chat_id, signal) {
|
|
return this.raw.unbanChatSenderChat({
|
|
chat_id,
|
|
sender_chat_id
|
|
}, signal);
|
|
}
|
|
setChatPermissions(chat_id, permissions, signal) {
|
|
return this.raw.setChatPermissions({
|
|
chat_id,
|
|
permissions
|
|
}, signal);
|
|
}
|
|
exportChatInviteLink(chat_id, signal) {
|
|
return this.raw.exportChatInviteLink({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
createChatInviteLink(chat_id, other, signal) {
|
|
return this.raw.createChatInviteLink({
|
|
chat_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editChatInviteLink(chat_id, invite_link, other, signal) {
|
|
return this.raw.editChatInviteLink({
|
|
chat_id,
|
|
invite_link,
|
|
...other
|
|
}, signal);
|
|
}
|
|
revokeChatInviteLink(chat_id, invite_link, signal) {
|
|
return this.raw.revokeChatInviteLink({
|
|
chat_id,
|
|
invite_link
|
|
}, signal);
|
|
}
|
|
approveChatJoinRequest(chat_id, user_id, signal) {
|
|
return this.raw.approveChatJoinRequest({
|
|
chat_id,
|
|
user_id
|
|
}, signal);
|
|
}
|
|
declineChatJoinRequest(chat_id, user_id, signal) {
|
|
return this.raw.declineChatJoinRequest({
|
|
chat_id,
|
|
user_id
|
|
}, signal);
|
|
}
|
|
setChatPhoto(chat_id, photo, signal) {
|
|
return this.raw.setChatPhoto({
|
|
chat_id,
|
|
photo
|
|
}, signal);
|
|
}
|
|
deleteChatPhoto(chat_id, signal) {
|
|
return this.raw.deleteChatPhoto({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
setChatTitle(chat_id, title, signal) {
|
|
return this.raw.setChatTitle({
|
|
chat_id,
|
|
title
|
|
}, signal);
|
|
}
|
|
setChatDescription(chat_id, description, signal) {
|
|
return this.raw.setChatDescription({
|
|
chat_id,
|
|
description
|
|
}, signal);
|
|
}
|
|
pinChatMessage(chat_id, message_id, other, signal) {
|
|
return this.raw.pinChatMessage({
|
|
chat_id,
|
|
message_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
unpinChatMessage(chat_id, message_id, signal) {
|
|
return this.raw.unpinChatMessage({
|
|
chat_id,
|
|
message_id
|
|
}, signal);
|
|
}
|
|
unpinAllChatMessages(chat_id, signal) {
|
|
return this.raw.unpinAllChatMessages({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
leaveChat(chat_id, signal) {
|
|
return this.raw.leaveChat({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
getChat(chat_id, signal) {
|
|
return this.raw.getChat({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
getChatAdministrators(chat_id, signal) {
|
|
return this.raw.getChatAdministrators({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
getChatMembersCount(...args) {
|
|
return this.getChatMemberCount(...args);
|
|
}
|
|
getChatMemberCount(chat_id, signal) {
|
|
return this.raw.getChatMemberCount({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
getChatMember(chat_id, user_id, signal) {
|
|
return this.raw.getChatMember({
|
|
chat_id,
|
|
user_id
|
|
}, signal);
|
|
}
|
|
setChatStickerSet(chat_id, sticker_set_name, signal) {
|
|
return this.raw.setChatStickerSet({
|
|
chat_id,
|
|
sticker_set_name
|
|
}, signal);
|
|
}
|
|
deleteChatStickerSet(chat_id, signal) {
|
|
return this.raw.deleteChatStickerSet({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
getForumTopicIconStickers(signal) {
|
|
return this.raw.getForumTopicIconStickers(signal);
|
|
}
|
|
createForumTopic(chat_id, name, other, signal) {
|
|
return this.raw.createForumTopic({
|
|
chat_id,
|
|
name,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editForumTopic(chat_id, message_thread_id, other, signal) {
|
|
return this.raw.editForumTopic({
|
|
chat_id,
|
|
message_thread_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
closeForumTopic(chat_id, message_thread_id, signal) {
|
|
return this.raw.closeForumTopic({
|
|
chat_id,
|
|
message_thread_id
|
|
}, signal);
|
|
}
|
|
reopenForumTopic(chat_id, message_thread_id, signal) {
|
|
return this.raw.reopenForumTopic({
|
|
chat_id,
|
|
message_thread_id
|
|
}, signal);
|
|
}
|
|
deleteForumTopic(chat_id, message_thread_id, signal) {
|
|
return this.raw.deleteForumTopic({
|
|
chat_id,
|
|
message_thread_id
|
|
}, signal);
|
|
}
|
|
unpinAllForumTopicMessages(chat_id, message_thread_id, signal) {
|
|
return this.raw.unpinAllForumTopicMessages({
|
|
chat_id,
|
|
message_thread_id
|
|
}, signal);
|
|
}
|
|
editGeneralForumTopic(chat_id, name, signal) {
|
|
return this.raw.editGeneralForumTopic({
|
|
chat_id,
|
|
name
|
|
}, signal);
|
|
}
|
|
closeGeneralForumTopic(chat_id, signal) {
|
|
return this.raw.closeGeneralForumTopic({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
reopenGeneralForumTopic(chat_id, signal) {
|
|
return this.raw.reopenGeneralForumTopic({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
hideGeneralForumTopic(chat_id, signal) {
|
|
return this.raw.hideGeneralForumTopic({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
unhideGeneralForumTopic(chat_id, signal) {
|
|
return this.raw.unhideGeneralForumTopic({
|
|
chat_id
|
|
}, signal);
|
|
}
|
|
answerCallbackQuery(callback_query_id, other, signal) {
|
|
return this.raw.answerCallbackQuery({
|
|
callback_query_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
setMyName(name, other, signal) {
|
|
return this.raw.setMyName({
|
|
name,
|
|
...other
|
|
}, signal);
|
|
}
|
|
getMyName(other, signal) {
|
|
return this.raw.getMyName(other ?? {}, signal);
|
|
}
|
|
setMyCommands(commands, other, signal) {
|
|
return this.raw.setMyCommands({
|
|
commands,
|
|
...other
|
|
}, signal);
|
|
}
|
|
deleteMyCommands(other, signal) {
|
|
return this.raw.deleteMyCommands({
|
|
...other
|
|
}, signal);
|
|
}
|
|
getMyCommands(other, signal) {
|
|
return this.raw.getMyCommands({
|
|
...other
|
|
}, signal);
|
|
}
|
|
setMyDescription(description, other, signal) {
|
|
return this.raw.setMyDescription({
|
|
description,
|
|
...other
|
|
}, signal);
|
|
}
|
|
getMyDescription(other, signal) {
|
|
return this.raw.getMyDescription({
|
|
...other
|
|
}, signal);
|
|
}
|
|
setMyShortDescription(short_description, other, signal) {
|
|
return this.raw.setMyShortDescription({
|
|
short_description,
|
|
...other
|
|
}, signal);
|
|
}
|
|
getMyShortDescription(other, signal) {
|
|
return this.raw.getMyShortDescription({
|
|
...other
|
|
}, signal);
|
|
}
|
|
setChatMenuButton(other, signal) {
|
|
return this.raw.setChatMenuButton({
|
|
...other
|
|
}, signal);
|
|
}
|
|
getChatMenuButton(other, signal) {
|
|
return this.raw.getChatMenuButton({
|
|
...other
|
|
}, signal);
|
|
}
|
|
setMyDefaultAdministratorRights(other, signal) {
|
|
return this.raw.setMyDefaultAdministratorRights({
|
|
...other
|
|
}, signal);
|
|
}
|
|
getMyDefaultAdministratorRights(other, signal) {
|
|
return this.raw.getMyDefaultAdministratorRights({
|
|
...other
|
|
}, signal);
|
|
}
|
|
editMessageText(chat_id, message_id, text, other, signal) {
|
|
return this.raw.editMessageText({
|
|
chat_id,
|
|
message_id,
|
|
text,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editMessageTextInline(inline_message_id, text, other, signal) {
|
|
return this.raw.editMessageText({
|
|
inline_message_id,
|
|
text,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editMessageCaption(chat_id, message_id, other, signal) {
|
|
return this.raw.editMessageCaption({
|
|
chat_id,
|
|
message_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editMessageCaptionInline(inline_message_id, other, signal) {
|
|
return this.raw.editMessageCaption({
|
|
inline_message_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editMessageMedia(chat_id, message_id, media, other, signal) {
|
|
return this.raw.editMessageMedia({
|
|
chat_id,
|
|
message_id,
|
|
media,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editMessageMediaInline(inline_message_id, media, other, signal) {
|
|
return this.raw.editMessageMedia({
|
|
inline_message_id,
|
|
media,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editMessageReplyMarkup(chat_id, message_id, other, signal) {
|
|
return this.raw.editMessageReplyMarkup({
|
|
chat_id,
|
|
message_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
editMessageReplyMarkupInline(inline_message_id, other, signal) {
|
|
return this.raw.editMessageReplyMarkup({
|
|
inline_message_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
stopPoll(chat_id, message_id, other, signal) {
|
|
return this.raw.stopPoll({
|
|
chat_id,
|
|
message_id,
|
|
...other
|
|
}, signal);
|
|
}
|
|
deleteMessage(chat_id, message_id, signal) {
|
|
return this.raw.deleteMessage({
|
|
chat_id,
|
|
message_id
|
|
}, signal);
|
|
}
|
|
sendSticker(chat_id, sticker, other, signal) {
|
|
return this.raw.sendSticker({
|
|
chat_id,
|
|
sticker,
|
|
...other
|
|
}, signal);
|
|
}
|
|
getStickerSet(name, signal) {
|
|
return this.raw.getStickerSet({
|
|
name
|
|
}, signal);
|
|
}
|
|
getCustomEmojiStickers(custom_emoji_ids, signal) {
|
|
return this.raw.getCustomEmojiStickers({
|
|
custom_emoji_ids
|
|
}, signal);
|
|
}
|
|
uploadStickerFile(user_id, sticker_format, sticker, signal) {
|
|
return this.raw.uploadStickerFile({
|
|
user_id,
|
|
sticker_format,
|
|
sticker
|
|
}, signal);
|
|
}
|
|
createNewStickerSet(user_id, name, title, stickers, sticker_format, other, signal) {
|
|
return this.raw.createNewStickerSet({
|
|
user_id,
|
|
name,
|
|
title,
|
|
stickers,
|
|
sticker_format,
|
|
...other
|
|
}, signal);
|
|
}
|
|
addStickerToSet(user_id, name, sticker, signal) {
|
|
return this.raw.addStickerToSet({
|
|
user_id,
|
|
name,
|
|
sticker
|
|
}, signal);
|
|
}
|
|
setStickerPositionInSet(sticker, position, signal) {
|
|
return this.raw.setStickerPositionInSet({
|
|
sticker,
|
|
position
|
|
}, signal);
|
|
}
|
|
deleteStickerFromSet(sticker, signal) {
|
|
return this.raw.deleteStickerFromSet({
|
|
sticker
|
|
}, signal);
|
|
}
|
|
setStickerEmojiList(sticker, emoji_list, signal) {
|
|
return this.raw.setStickerEmojiList({
|
|
sticker,
|
|
emoji_list
|
|
}, signal);
|
|
}
|
|
setStickerKeywords(sticker, keywords, signal) {
|
|
return this.raw.setStickerKeywords({
|
|
sticker,
|
|
keywords
|
|
}, signal);
|
|
}
|
|
setStickerMaskPosition(sticker, mask_position, signal) {
|
|
return this.raw.setStickerMaskPosition({
|
|
sticker,
|
|
mask_position
|
|
}, signal);
|
|
}
|
|
setStickerSetTitle(name, title, signal) {
|
|
return this.raw.setStickerSetTitle({
|
|
name,
|
|
title
|
|
}, signal);
|
|
}
|
|
deleteStickerSet(name, signal) {
|
|
return this.raw.deleteStickerSet({
|
|
name
|
|
}, signal);
|
|
}
|
|
setStickerSetThumbnail(name, user_id, thumbnail, signal) {
|
|
return this.raw.setStickerSetThumbnail({
|
|
name,
|
|
user_id,
|
|
thumbnail
|
|
}, signal);
|
|
}
|
|
setCustomEmojiStickerSetThumbnail(name, custom_emoji_id, signal) {
|
|
return this.raw.setCustomEmojiStickerSetThumbnail({
|
|
name,
|
|
custom_emoji_id
|
|
}, signal);
|
|
}
|
|
answerInlineQuery(inline_query_id, results, other, signal) {
|
|
return this.raw.answerInlineQuery({
|
|
inline_query_id,
|
|
results,
|
|
...other
|
|
}, signal);
|
|
}
|
|
answerWebAppQuery(web_app_query_id, result, signal) {
|
|
return this.raw.answerWebAppQuery({
|
|
web_app_query_id,
|
|
result
|
|
}, signal);
|
|
}
|
|
sendInvoice(chat_id, title, description, payload, provider_token, currency, prices, other, signal) {
|
|
return this.raw.sendInvoice({
|
|
chat_id,
|
|
title,
|
|
description,
|
|
payload,
|
|
provider_token,
|
|
currency,
|
|
prices,
|
|
...other
|
|
}, signal);
|
|
}
|
|
createInvoiceLink(title, description, payload, provider_token, currency, prices, other, signal) {
|
|
return this.raw.createInvoiceLink({
|
|
title,
|
|
description,
|
|
payload,
|
|
provider_token,
|
|
currency,
|
|
prices,
|
|
...other
|
|
}, signal);
|
|
}
|
|
answerShippingQuery(shipping_query_id, ok, other, signal) {
|
|
return this.raw.answerShippingQuery({
|
|
shipping_query_id,
|
|
ok,
|
|
...other
|
|
}, signal);
|
|
}
|
|
answerPreCheckoutQuery(pre_checkout_query_id, ok, other, signal) {
|
|
return this.raw.answerPreCheckoutQuery({
|
|
pre_checkout_query_id,
|
|
ok,
|
|
...other
|
|
}, signal);
|
|
}
|
|
setPassportDataErrors(user_id, errors, signal) {
|
|
return this.raw.setPassportDataErrors({
|
|
user_id,
|
|
errors
|
|
}, signal);
|
|
}
|
|
sendGame(chat_id, game_short_name, other, signal) {
|
|
return this.raw.sendGame({
|
|
chat_id,
|
|
game_short_name,
|
|
...other
|
|
}, signal);
|
|
}
|
|
setGameScore(chat_id, message_id, user_id, score, other, signal) {
|
|
return this.raw.setGameScore({
|
|
chat_id,
|
|
message_id,
|
|
user_id,
|
|
score,
|
|
...other
|
|
}, signal);
|
|
}
|
|
setGameScoreInline(inline_message_id, user_id, score, other, signal) {
|
|
return this.raw.setGameScore({
|
|
inline_message_id,
|
|
user_id,
|
|
score,
|
|
...other
|
|
}, signal);
|
|
}
|
|
getGameHighScores(chat_id, message_id, user_id, signal) {
|
|
return this.raw.getGameHighScores({
|
|
chat_id,
|
|
message_id,
|
|
user_id
|
|
}, signal);
|
|
}
|
|
getGameHighScoresInline(inline_message_id, user_id, signal) {
|
|
return this.raw.getGameHighScores({
|
|
inline_message_id,
|
|
user_id
|
|
}, signal);
|
|
}
|
|
}
|
|
const debug1 = browser$1("grammy:bot");
|
|
const debugWarn = browser$1("grammy:warn");
|
|
const debugErr = browser$1("grammy:error");
|
|
const DEFAULT_UPDATE_TYPES = [
|
|
"message",
|
|
"edited_message",
|
|
"channel_post",
|
|
"edited_channel_post",
|
|
"inline_query",
|
|
"chosen_inline_result",
|
|
"callback_query",
|
|
"shipping_query",
|
|
"pre_checkout_query",
|
|
"poll",
|
|
"poll_answer",
|
|
"my_chat_member",
|
|
"chat_join_request"
|
|
];
|
|
class Bot extends Composer {
|
|
pollingRunning;
|
|
pollingAbortController;
|
|
lastTriedUpdateId;
|
|
api;
|
|
me;
|
|
mePromise;
|
|
clientConfig;
|
|
ContextConstructor;
|
|
observedUpdateTypes;
|
|
errorHandler;
|
|
constructor(token, config){
|
|
super();
|
|
this.token = token;
|
|
this.pollingRunning = false;
|
|
this.lastTriedUpdateId = 0;
|
|
this.observedUpdateTypes = new Set();
|
|
this.errorHandler = async (err)=>{
|
|
console.error("Error in middleware while handling update", err.ctx?.update?.update_id, err.error);
|
|
console.error("No error handler was set!");
|
|
console.error("Set your own error handler with `bot.catch = ...`");
|
|
if (this.pollingRunning) {
|
|
console.error("Stopping bot");
|
|
await this.stop();
|
|
}
|
|
throw err;
|
|
};
|
|
if (!token) throw new Error("Empty token!");
|
|
this.me = config?.botInfo;
|
|
this.clientConfig = config?.client;
|
|
this.ContextConstructor = config?.ContextConstructor ?? Context;
|
|
this.api = new Api(token, this.clientConfig);
|
|
}
|
|
set botInfo(botInfo) {
|
|
this.me = botInfo;
|
|
}
|
|
get botInfo() {
|
|
if (this.me === undefined) {
|
|
throw new Error("Bot information unavailable! Make sure to call `await bot.init()` before accessing `bot.botInfo`!");
|
|
}
|
|
return this.me;
|
|
}
|
|
on(filter, ...middleware) {
|
|
for (const [u] of parse(filter).flatMap(preprocess)){
|
|
this.observedUpdateTypes.add(u);
|
|
}
|
|
return super.on(filter, ...middleware);
|
|
}
|
|
isInited() {
|
|
return this.me !== undefined;
|
|
}
|
|
async init(signal) {
|
|
if (!this.isInited()) {
|
|
debug1("Initializing bot");
|
|
this.mePromise ??= withRetries(()=>this.api.getMe(signal), signal);
|
|
let me;
|
|
try {
|
|
me = await this.mePromise;
|
|
} finally{
|
|
this.mePromise = undefined;
|
|
}
|
|
if (this.me === undefined) this.me = me;
|
|
else debug1("Bot info was set by now, will not overwrite");
|
|
}
|
|
debug1(`I am ${this.me.username}!`);
|
|
}
|
|
async handleUpdates(updates) {
|
|
for (const update of updates){
|
|
this.lastTriedUpdateId = update.update_id;
|
|
try {
|
|
await this.handleUpdate(update);
|
|
} catch (err) {
|
|
if (err instanceof BotError) {
|
|
await this.errorHandler(err);
|
|
} else {
|
|
console.error("FATAL: grammY unable to handle:", err);
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
async handleUpdate(update, webhookReplyEnvelope) {
|
|
if (this.me === undefined) {
|
|
throw new Error("Bot not initialized! Either call `await bot.init()`, \
|
|
or directly set the `botInfo` option in the `Bot` constructor to specify \
|
|
a known bot info object.");
|
|
}
|
|
debug1(`Processing update ${update.update_id}`);
|
|
const api = new Api(this.token, this.clientConfig, webhookReplyEnvelope);
|
|
const t = this.api.config.installedTransformers();
|
|
if (t.length > 0) api.config.use(...t);
|
|
const ctx = new this.ContextConstructor(update, api, this.me);
|
|
try {
|
|
await run(this.middleware(), ctx);
|
|
} catch (err) {
|
|
debugErr(`Error in middleware for update ${update.update_id}`);
|
|
throw new BotError(err, ctx);
|
|
}
|
|
}
|
|
async start(options) {
|
|
if (!this.isInited()) {
|
|
await this.init(this.pollingAbortController?.signal);
|
|
}
|
|
if (this.pollingRunning) {
|
|
debug1("Simple long polling already running!");
|
|
return;
|
|
} else {
|
|
this.pollingRunning = true;
|
|
this.pollingAbortController = new AbortController();
|
|
}
|
|
await withRetries(()=>this.api.deleteWebhook({
|
|
drop_pending_updates: options?.drop_pending_updates
|
|
}, this.pollingAbortController?.signal), this.pollingAbortController?.signal);
|
|
await options?.onStart?.(this.botInfo);
|
|
if (!this.pollingRunning) return;
|
|
validateAllowedUpdates(this.observedUpdateTypes, options?.allowed_updates);
|
|
this.use = noUseFunction;
|
|
debug1("Starting simple long polling");
|
|
await this.loop(options);
|
|
debug1("Middleware is done running");
|
|
}
|
|
async stop() {
|
|
if (this.pollingRunning) {
|
|
debug1("Stopping bot, saving update offset");
|
|
this.pollingRunning = false;
|
|
this.pollingAbortController?.abort();
|
|
const offset = this.lastTriedUpdateId + 1;
|
|
await this.api.getUpdates({
|
|
offset,
|
|
limit: 1
|
|
}).finally(()=>this.pollingAbortController = undefined);
|
|
} else {
|
|
debug1("Bot is not running!");
|
|
}
|
|
}
|
|
catch(errorHandler) {
|
|
this.errorHandler = errorHandler;
|
|
}
|
|
async loop(options) {
|
|
const limit = options?.limit;
|
|
const timeout = options?.timeout ?? 30;
|
|
let allowed_updates = options?.allowed_updates ?? [];
|
|
while(this.pollingRunning){
|
|
const updates = await this.fetchUpdates({
|
|
limit,
|
|
timeout,
|
|
allowed_updates
|
|
});
|
|
if (updates === undefined) break;
|
|
await this.handleUpdates(updates);
|
|
allowed_updates = undefined;
|
|
}
|
|
}
|
|
async fetchUpdates({ limit , timeout , allowed_updates }) {
|
|
const offset = this.lastTriedUpdateId + 1;
|
|
let updates = undefined;
|
|
do {
|
|
try {
|
|
updates = await this.api.getUpdates({
|
|
offset,
|
|
limit,
|
|
timeout,
|
|
allowed_updates
|
|
}, this.pollingAbortController?.signal);
|
|
} catch (error) {
|
|
await this.handlePollingError(error);
|
|
}
|
|
}while (updates === undefined && this.pollingRunning)
|
|
return updates;
|
|
}
|
|
async handlePollingError(error) {
|
|
if (!this.pollingRunning) {
|
|
debug1("Pending getUpdates request cancelled");
|
|
return;
|
|
}
|
|
let sleepSeconds = 3;
|
|
if (error instanceof GrammyError) {
|
|
debugErr(error.message);
|
|
if (error.error_code === 401) {
|
|
debugErr("Make sure you are using the bot token you obtained from @BotFather (https://t.me/BotFather).");
|
|
throw error;
|
|
} else if (error.error_code === 409) {
|
|
debugErr("Consider revoking the bot token if you believe that no other instance is running.");
|
|
throw error;
|
|
} else if (error.error_code === 429) {
|
|
debugErr("Bot API server is closing.");
|
|
sleepSeconds = error.parameters.retry_after ?? sleepSeconds;
|
|
}
|
|
} else debugErr(error);
|
|
debugErr(`Call to getUpdates failed, retrying in ${sleepSeconds} seconds ...`);
|
|
await sleep(sleepSeconds);
|
|
}
|
|
token;
|
|
}
|
|
async function withRetries(task, signal) {
|
|
const INITIAL_DELAY = 50;
|
|
let lastDelay = 50;
|
|
async function handleError(error) {
|
|
let delay = false;
|
|
let strategy = "rethrow";
|
|
if (error instanceof HttpError) {
|
|
delay = true;
|
|
strategy = "retry";
|
|
} else if (error instanceof GrammyError) {
|
|
if (error.error_code >= 500) {
|
|
delay = true;
|
|
strategy = "retry";
|
|
} else if (error.error_code === 429) {
|
|
const retryAfter = error.parameters.retry_after;
|
|
if (typeof retryAfter === "number") {
|
|
await sleep(retryAfter, signal);
|
|
lastDelay = INITIAL_DELAY;
|
|
} else {
|
|
delay = true;
|
|
}
|
|
strategy = "retry";
|
|
}
|
|
}
|
|
if (delay) {
|
|
if (lastDelay !== 50) {
|
|
await sleep(lastDelay, signal);
|
|
}
|
|
const TWENTY_MINUTES = 20 * 60 * 1000;
|
|
lastDelay = Math.min(TWENTY_MINUTES, 2 * lastDelay);
|
|
}
|
|
return strategy;
|
|
}
|
|
let result = {
|
|
ok: false
|
|
};
|
|
while(!result.ok){
|
|
try {
|
|
result = {
|
|
ok: true,
|
|
value: await task()
|
|
};
|
|
} catch (error) {
|
|
debugErr(error);
|
|
const strategy = await handleError(error);
|
|
switch(strategy){
|
|
case "retry":
|
|
continue;
|
|
case "rethrow":
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
return result.value;
|
|
}
|
|
async function sleep(seconds, signal) {
|
|
let handle;
|
|
let reject;
|
|
function abort() {
|
|
reject?.(new Error("Aborted delay"));
|
|
if (handle !== undefined) clearTimeout(handle);
|
|
}
|
|
try {
|
|
await new Promise((res, rej)=>{
|
|
reject = rej;
|
|
if (signal?.aborted) {
|
|
abort();
|
|
return;
|
|
}
|
|
signal?.addEventListener("abort", abort);
|
|
handle = setTimeout(res, 1000 * seconds);
|
|
});
|
|
} finally{
|
|
signal?.removeEventListener("abort", abort);
|
|
}
|
|
}
|
|
function validateAllowedUpdates(updates, allowed = DEFAULT_UPDATE_TYPES) {
|
|
const impossible = Array.from(updates).filter((u)=>!allowed.includes(u));
|
|
if (impossible.length > 0) {
|
|
debugWarn(`You registered listeners for the following update types, \
|
|
but you did not specify them in \`allowed_updates\` \
|
|
so they may not be received: ${impossible.map((u)=>`'${u}'`).join(", ")}`);
|
|
}
|
|
}
|
|
function noUseFunction() {
|
|
throw new Error(`It looks like you are registering more listeners \
|
|
on your bot from within other listeners! This means that every time your bot \
|
|
handles a message like this one, new listeners will be added. This list grows until \
|
|
your machine crashes, so grammY throws this error to tell you that you should \
|
|
probably do things a bit differently. If you're unsure how to resolve this problem, \
|
|
you can ask in the group chat: https://telegram.me/grammyjs
|
|
|
|
On the other hand, if you actually know what you're doing and you do need to install \
|
|
further middleware while your bot is running, consider installing a composer \
|
|
instance on your bot, and in turn augment the composer after the fact. This way, \
|
|
you can circumvent this protection against memory leaks.`);
|
|
}
|
|
const ALL_UPDATE_TYPES = [
|
|
...DEFAULT_UPDATE_TYPES,
|
|
"chat_member"
|
|
];
|
|
const ALL_CHAT_PERMISSIONS = {
|
|
can_send_messages: true,
|
|
can_send_audios: true,
|
|
can_send_documents: true,
|
|
can_send_photos: true,
|
|
can_send_videos: true,
|
|
can_send_video_notes: true,
|
|
can_send_voice_notes: true,
|
|
can_send_polls: true,
|
|
can_send_other_messages: true,
|
|
can_add_web_page_previews: true,
|
|
can_change_info: true,
|
|
can_invite_users: true,
|
|
can_pin_messages: true,
|
|
can_manage_topics: true
|
|
};
|
|
const API_CONSTANTS = Object.freeze({
|
|
ALL_UPDATE_TYPES,
|
|
ALL_CHAT_PERMISSIONS
|
|
});
|
|
export { API_CONSTANTS as API_CONSTANTS };
|
|
function inputMessage(queryTemplate) {
|
|
return {
|
|
...queryTemplate,
|
|
...inputMessageMethods(queryTemplate)
|
|
};
|
|
}
|
|
function inputMessageMethods(queryTemplate) {
|
|
return {
|
|
text (message_text, options = {}) {
|
|
const content = {
|
|
message_text,
|
|
...options
|
|
};
|
|
return {
|
|
...queryTemplate,
|
|
input_message_content: content
|
|
};
|
|
},
|
|
location (latitude, longitude, options = {}) {
|
|
const content = {
|
|
latitude,
|
|
longitude,
|
|
...options
|
|
};
|
|
return {
|
|
...queryTemplate,
|
|
input_message_content: content
|
|
};
|
|
},
|
|
venue (title, latitude, longitude, address, options) {
|
|
const content = {
|
|
title,
|
|
latitude,
|
|
longitude,
|
|
address,
|
|
...options
|
|
};
|
|
return {
|
|
...queryTemplate,
|
|
input_message_content: content
|
|
};
|
|
},
|
|
contact (first_name, phone_number, options = {}) {
|
|
const content = {
|
|
first_name,
|
|
phone_number,
|
|
...options
|
|
};
|
|
return {
|
|
...queryTemplate,
|
|
input_message_content: content
|
|
};
|
|
},
|
|
invoice (title, description, payload, provider_token, currency, prices, options = {}) {
|
|
const content = {
|
|
title,
|
|
description,
|
|
payload,
|
|
provider_token,
|
|
currency,
|
|
prices,
|
|
...options
|
|
};
|
|
return {
|
|
...queryTemplate,
|
|
input_message_content: content
|
|
};
|
|
}
|
|
};
|
|
}
|
|
const InlineQueryResultBuilder = {
|
|
article (id, title, options = {}) {
|
|
return inputMessageMethods({
|
|
type: "article",
|
|
id,
|
|
title,
|
|
...options
|
|
});
|
|
},
|
|
audio (id, title, audio_url, options = {}) {
|
|
return inputMessage({
|
|
type: "audio",
|
|
id,
|
|
title,
|
|
audio_url: typeof audio_url === "string" ? audio_url : audio_url.href,
|
|
...options
|
|
});
|
|
},
|
|
audioCached (id, audio_file_id, options = {}) {
|
|
return inputMessage({
|
|
type: "audio",
|
|
id,
|
|
audio_file_id,
|
|
...options
|
|
});
|
|
},
|
|
contact (id, phone_number, first_name, options = {}) {
|
|
return inputMessage({
|
|
type: "contact",
|
|
id,
|
|
phone_number,
|
|
first_name,
|
|
...options
|
|
});
|
|
},
|
|
documentPdf (id, title, document_url, options = {}) {
|
|
return inputMessage({
|
|
type: "document",
|
|
mime_type: "application/pdf",
|
|
id,
|
|
title,
|
|
document_url: typeof document_url === "string" ? document_url : document_url.href,
|
|
...options
|
|
});
|
|
},
|
|
documentZip (id, title, document_url, options = {}) {
|
|
return inputMessage({
|
|
type: "document",
|
|
mime_type: "application/zip",
|
|
id,
|
|
title,
|
|
document_url: typeof document_url === "string" ? document_url : document_url.href,
|
|
...options
|
|
});
|
|
},
|
|
documentCached (id, title, document_file_id, options = {}) {
|
|
return inputMessage({
|
|
type: "document",
|
|
id,
|
|
title,
|
|
document_file_id,
|
|
...options
|
|
});
|
|
},
|
|
game (id, game_short_name, options = {}) {
|
|
return {
|
|
type: "game",
|
|
id,
|
|
game_short_name,
|
|
...options
|
|
};
|
|
},
|
|
gif (id, gif_url, thumbnail_url, options = {}) {
|
|
return inputMessage({
|
|
type: "gif",
|
|
id,
|
|
gif_url: typeof gif_url === "string" ? gif_url : gif_url.href,
|
|
thumbnail_url: typeof thumbnail_url === "string" ? thumbnail_url : thumbnail_url.href,
|
|
...options
|
|
});
|
|
},
|
|
gifCached (id, gif_file_id, options = {}) {
|
|
return inputMessage({
|
|
type: "gif",
|
|
id,
|
|
gif_file_id,
|
|
...options
|
|
});
|
|
},
|
|
location (id, title, latitude, longitude, options = {}) {
|
|
return inputMessage({
|
|
type: "location",
|
|
id,
|
|
title,
|
|
latitude,
|
|
longitude,
|
|
...options
|
|
});
|
|
},
|
|
mpeg4gif (id, mpeg4_url, thumbnail_url, options = {}) {
|
|
return inputMessage({
|
|
type: "mpeg4_gif",
|
|
id,
|
|
mpeg4_url: typeof mpeg4_url === "string" ? mpeg4_url : mpeg4_url.href,
|
|
thumbnail_url: typeof thumbnail_url === "string" ? thumbnail_url : thumbnail_url.href,
|
|
...options
|
|
});
|
|
},
|
|
mpeg4gifCached (id, mpeg4_file_id, options = {}) {
|
|
return inputMessage({
|
|
type: "mpeg4_gif",
|
|
id,
|
|
mpeg4_file_id,
|
|
...options
|
|
});
|
|
},
|
|
photo (id, photo_url, options = {
|
|
thumbnail_url: typeof photo_url === "string" ? photo_url : photo_url.href
|
|
}) {
|
|
return inputMessage({
|
|
type: "photo",
|
|
id,
|
|
photo_url: typeof photo_url === "string" ? photo_url : photo_url.href,
|
|
...options
|
|
});
|
|
},
|
|
photoCached (id, photo_file_id, options = {}) {
|
|
return inputMessage({
|
|
type: "photo",
|
|
id,
|
|
photo_file_id,
|
|
...options
|
|
});
|
|
},
|
|
stickerCached (id, sticker_file_id, options = {}) {
|
|
return inputMessage({
|
|
type: "sticker",
|
|
id,
|
|
sticker_file_id,
|
|
...options
|
|
});
|
|
},
|
|
venue (id, title, latitude, longitude, address, options = {}) {
|
|
return inputMessage({
|
|
type: "venue",
|
|
id,
|
|
title,
|
|
latitude,
|
|
longitude,
|
|
address,
|
|
...options
|
|
});
|
|
},
|
|
videoHtml (id, title, video_url, thumbnail_url, options = {}) {
|
|
return inputMessageMethods({
|
|
type: "video",
|
|
mime_type: "text/html",
|
|
id,
|
|
title,
|
|
video_url: typeof video_url === "string" ? video_url : video_url.href,
|
|
thumbnail_url: typeof thumbnail_url === "string" ? thumbnail_url : thumbnail_url.href,
|
|
...options
|
|
});
|
|
},
|
|
videoMp4 (id, title, video_url, thumbnail_url, options = {}) {
|
|
return inputMessage({
|
|
type: "video",
|
|
mime_type: "video/mp4",
|
|
id,
|
|
title,
|
|
video_url: typeof video_url === "string" ? video_url : video_url.href,
|
|
thumbnail_url: typeof thumbnail_url === "string" ? thumbnail_url : thumbnail_url.href,
|
|
...options
|
|
});
|
|
},
|
|
videoCached (id, title, video_file_id, options = {}) {
|
|
return inputMessage({
|
|
type: "video",
|
|
id,
|
|
title,
|
|
video_file_id,
|
|
...options
|
|
});
|
|
},
|
|
voice (id, title, voice_url, options = {}) {
|
|
return inputMessage({
|
|
type: "voice",
|
|
id,
|
|
title,
|
|
voice_url: typeof voice_url === "string" ? voice_url : voice_url.href,
|
|
...options
|
|
});
|
|
},
|
|
voiceCached (id, title, voice_file_id, options = {}) {
|
|
return inputMessage({
|
|
type: "voice",
|
|
id,
|
|
title,
|
|
voice_file_id,
|
|
...options
|
|
});
|
|
}
|
|
};
|
|
export { InlineQueryResultBuilder as InlineQueryResultBuilder };
|
|
const InputMediaBuilder = {
|
|
photo (media, options = {}) {
|
|
return {
|
|
type: "photo",
|
|
media,
|
|
...options
|
|
};
|
|
},
|
|
video (media, options = {}) {
|
|
return {
|
|
type: "video",
|
|
media,
|
|
...options
|
|
};
|
|
},
|
|
animation (media, options = {}) {
|
|
return {
|
|
type: "animation",
|
|
media,
|
|
...options
|
|
};
|
|
},
|
|
audio (media, options = {}) {
|
|
return {
|
|
type: "audio",
|
|
media,
|
|
...options
|
|
};
|
|
},
|
|
document (media, options = {}) {
|
|
return {
|
|
type: "document",
|
|
media,
|
|
...options
|
|
};
|
|
}
|
|
};
|
|
export { InputMediaBuilder as InputMediaBuilder };
|
|
class Keyboard {
|
|
is_persistent;
|
|
selective;
|
|
one_time_keyboard;
|
|
resize_keyboard;
|
|
input_field_placeholder;
|
|
constructor(keyboard = [
|
|
[]
|
|
]){
|
|
this.keyboard = keyboard;
|
|
}
|
|
add(...buttons) {
|
|
this.keyboard[this.keyboard.length - 1]?.push(...buttons);
|
|
return this;
|
|
}
|
|
row(...buttons) {
|
|
this.keyboard.push(buttons);
|
|
return this;
|
|
}
|
|
text(text) {
|
|
return this.add(Keyboard.text(text));
|
|
}
|
|
static text(text) {
|
|
return {
|
|
text
|
|
};
|
|
}
|
|
requestUser(text, requestId, options = {}) {
|
|
return this.add(Keyboard.requestUser(text, requestId, options));
|
|
}
|
|
static requestUser(text, requestId, options = {}) {
|
|
return {
|
|
text,
|
|
request_user: {
|
|
request_id: requestId,
|
|
...options
|
|
}
|
|
};
|
|
}
|
|
requestChat(text, requestId, options = {
|
|
chat_is_channel: false
|
|
}) {
|
|
return this.add(Keyboard.requestChat(text, requestId, options));
|
|
}
|
|
static requestChat(text, requestId, options = {
|
|
chat_is_channel: false
|
|
}) {
|
|
return {
|
|
text,
|
|
request_chat: {
|
|
request_id: requestId,
|
|
...options
|
|
}
|
|
};
|
|
}
|
|
requestContact(text) {
|
|
return this.add(Keyboard.requestContact(text));
|
|
}
|
|
static requestContact(text) {
|
|
return {
|
|
text,
|
|
request_contact: true
|
|
};
|
|
}
|
|
requestLocation(text) {
|
|
return this.add(Keyboard.requestLocation(text));
|
|
}
|
|
static requestLocation(text) {
|
|
return {
|
|
text,
|
|
request_location: true
|
|
};
|
|
}
|
|
requestPoll(text, type) {
|
|
return this.add(Keyboard.requestPoll(text, type));
|
|
}
|
|
static requestPoll(text, type) {
|
|
return {
|
|
text,
|
|
request_poll: {
|
|
type
|
|
}
|
|
};
|
|
}
|
|
webApp(text, url) {
|
|
return this.add(Keyboard.webApp(text, url));
|
|
}
|
|
static webApp(text, url) {
|
|
return {
|
|
text,
|
|
web_app: {
|
|
url
|
|
}
|
|
};
|
|
}
|
|
persistent(isEnabled = true) {
|
|
this.is_persistent = isEnabled;
|
|
return this;
|
|
}
|
|
selected(isEnabled = true) {
|
|
this.selective = isEnabled;
|
|
return this;
|
|
}
|
|
oneTime(isEnabled = true) {
|
|
this.one_time_keyboard = isEnabled;
|
|
return this;
|
|
}
|
|
resized(isEnabled = true) {
|
|
this.resize_keyboard = isEnabled;
|
|
return this;
|
|
}
|
|
placeholder(value) {
|
|
this.input_field_placeholder = value;
|
|
return this;
|
|
}
|
|
toTransposed() {
|
|
const original = this.keyboard;
|
|
const transposed = transpose(original);
|
|
return this.clone(transposed);
|
|
}
|
|
toFlowed(columns, options = {}) {
|
|
const original = this.keyboard;
|
|
const flowed = reflow(original, columns, options);
|
|
return this.clone(flowed);
|
|
}
|
|
clone(keyboard = this.keyboard) {
|
|
const clone = new Keyboard(keyboard.map((row)=>row.slice()));
|
|
clone.is_persistent = this.is_persistent;
|
|
clone.selective = this.selective;
|
|
clone.one_time_keyboard = this.one_time_keyboard;
|
|
clone.resize_keyboard = this.resize_keyboard;
|
|
clone.input_field_placeholder = this.input_field_placeholder;
|
|
return clone;
|
|
}
|
|
append(...sources) {
|
|
for (const source of sources){
|
|
const keyboard = Keyboard.from(source);
|
|
this.keyboard.push(...keyboard.keyboard.map((row)=>row.slice()));
|
|
}
|
|
return this;
|
|
}
|
|
build() {
|
|
return this.keyboard;
|
|
}
|
|
static from(source) {
|
|
if (source instanceof Keyboard) return source.clone();
|
|
function toButton(btn) {
|
|
return typeof btn === "string" ? Keyboard.text(btn) : btn;
|
|
}
|
|
return new Keyboard(source.map((row)=>row.map(toButton)));
|
|
}
|
|
keyboard;
|
|
}
|
|
class InlineKeyboard {
|
|
constructor(inline_keyboard = [
|
|
[]
|
|
]){
|
|
this.inline_keyboard = inline_keyboard;
|
|
}
|
|
add(...buttons) {
|
|
this.inline_keyboard[this.inline_keyboard.length - 1]?.push(...buttons);
|
|
return this;
|
|
}
|
|
row(...buttons) {
|
|
this.inline_keyboard.push(buttons);
|
|
return this;
|
|
}
|
|
url(text, url) {
|
|
return this.add(InlineKeyboard.url(text, url));
|
|
}
|
|
static url(text, url) {
|
|
return {
|
|
text,
|
|
url
|
|
};
|
|
}
|
|
text(text, data = text) {
|
|
return this.add(InlineKeyboard.text(text, data));
|
|
}
|
|
static text(text, data = text) {
|
|
return {
|
|
text,
|
|
callback_data: data
|
|
};
|
|
}
|
|
webApp(text, url) {
|
|
return this.add(InlineKeyboard.webApp(text, url));
|
|
}
|
|
static webApp(text, url) {
|
|
return {
|
|
text,
|
|
web_app: {
|
|
url
|
|
}
|
|
};
|
|
}
|
|
login(text, loginUrl) {
|
|
return this.add(InlineKeyboard.login(text, loginUrl));
|
|
}
|
|
static login(text, loginUrl) {
|
|
return {
|
|
text,
|
|
login_url: typeof loginUrl === "string" ? {
|
|
url: loginUrl
|
|
} : loginUrl
|
|
};
|
|
}
|
|
switchInline(text, query = "") {
|
|
return this.add(InlineKeyboard.switchInline(text, query));
|
|
}
|
|
static switchInline(text, query = "") {
|
|
return {
|
|
text,
|
|
switch_inline_query: query
|
|
};
|
|
}
|
|
switchInlineCurrent(text, query = "") {
|
|
return this.add(InlineKeyboard.switchInlineCurrent(text, query));
|
|
}
|
|
static switchInlineCurrent(text, query = "") {
|
|
return {
|
|
text,
|
|
switch_inline_query_current_chat: query
|
|
};
|
|
}
|
|
switchInlineChosen(text, query = {}) {
|
|
return this.add(InlineKeyboard.switchInlineChosen(text, query));
|
|
}
|
|
static switchInlineChosen(text, query = {}) {
|
|
return {
|
|
text,
|
|
switch_inline_query_chosen_chat: query
|
|
};
|
|
}
|
|
game(text) {
|
|
return this.add(InlineKeyboard.game(text));
|
|
}
|
|
static game(text) {
|
|
return {
|
|
text,
|
|
callback_game: {}
|
|
};
|
|
}
|
|
pay(text) {
|
|
return this.add(InlineKeyboard.pay(text));
|
|
}
|
|
static pay(text) {
|
|
return {
|
|
text,
|
|
pay: true
|
|
};
|
|
}
|
|
toTransposed() {
|
|
const original = this.inline_keyboard;
|
|
const transposed = transpose(original);
|
|
return new InlineKeyboard(transposed);
|
|
}
|
|
toFlowed(columns, options = {}) {
|
|
const original = this.inline_keyboard;
|
|
const flowed = reflow(original, columns, options);
|
|
return new InlineKeyboard(flowed);
|
|
}
|
|
clone() {
|
|
return new InlineKeyboard(this.inline_keyboard.map((row)=>row.slice()));
|
|
}
|
|
append(...sources) {
|
|
for (const source of sources){
|
|
const keyboard = InlineKeyboard.from(source);
|
|
this.inline_keyboard.push(...keyboard.inline_keyboard.map((row)=>row.slice()));
|
|
}
|
|
return this;
|
|
}
|
|
static from(source) {
|
|
if (source instanceof InlineKeyboard) return source.clone();
|
|
return new InlineKeyboard(source.map((row)=>row.slice()));
|
|
}
|
|
inline_keyboard;
|
|
}
|
|
function transpose(grid) {
|
|
const transposed = [];
|
|
for(let i = 0; i < grid.length; i++){
|
|
const row = grid[i];
|
|
for(let j = 0; j < row.length; j++){
|
|
const button = row[j];
|
|
(transposed[j] ??= []).push(button);
|
|
}
|
|
}
|
|
return transposed;
|
|
}
|
|
function reflow(grid, columns, { fillLastRow =false }) {
|
|
let first = columns;
|
|
if (fillLastRow) {
|
|
const buttonCount = grid.map((row)=>row.length).reduce((a, b)=>a + b, 0);
|
|
first = buttonCount % columns;
|
|
}
|
|
const reflowed = [];
|
|
for (const row of grid){
|
|
for (const button of row){
|
|
const at = Math.max(0, reflowed.length - 1);
|
|
const max = at === 0 ? first : columns;
|
|
let next = reflowed[at] ??= [];
|
|
if (next.length === max) {
|
|
next = [];
|
|
reflowed.push(next);
|
|
}
|
|
next.push(button);
|
|
}
|
|
}
|
|
return reflowed;
|
|
}
|
|
export { Keyboard as Keyboard };
|
|
export { InlineKeyboard as InlineKeyboard };
|
|
const debug2 = browser$1("grammy:session");
|
|
function session(options = {}) {
|
|
return options.type === "multi" ? strictMultiSession(options) : strictSingleSession(options);
|
|
}
|
|
function strictSingleSession(options) {
|
|
const { initial , storage , getSessionKey , custom } = fillDefaults(options);
|
|
return async (ctx, next)=>{
|
|
const propSession = new PropertySession(storage, ctx, "session", initial);
|
|
const key = await getSessionKey(ctx);
|
|
await propSession.init(key, {
|
|
custom,
|
|
lazy: false
|
|
});
|
|
await next();
|
|
await propSession.finish();
|
|
};
|
|
}
|
|
function strictMultiSession(options) {
|
|
const props = Object.keys(options).filter((k)=>k !== "type");
|
|
const defaults = Object.fromEntries(props.map((prop)=>[
|
|
prop,
|
|
fillDefaults(options[prop])
|
|
]));
|
|
return async (ctx, next)=>{
|
|
ctx.session = {};
|
|
const propSessions = await Promise.all(props.map(async (prop)=>{
|
|
const { initial , storage , getSessionKey , custom } = defaults[prop];
|
|
const s = new PropertySession(storage, ctx.session, prop, initial);
|
|
const key = await getSessionKey(ctx);
|
|
await s.init(key, {
|
|
custom,
|
|
lazy: false
|
|
});
|
|
return s;
|
|
}));
|
|
await next();
|
|
if (ctx.session == null) propSessions.forEach((s)=>s.delete());
|
|
await Promise.all(propSessions.map((s)=>s.finish()));
|
|
};
|
|
}
|
|
function lazySession(options = {}) {
|
|
if (options.type !== undefined && options.type !== "single") {
|
|
throw new Error("Cannot use lazy multi sessions!");
|
|
}
|
|
const { initial , storage , getSessionKey , custom } = fillDefaults(options);
|
|
return async (ctx, next)=>{
|
|
const propSession = new PropertySession(storage, ctx, "session", initial);
|
|
const key = await getSessionKey(ctx);
|
|
await propSession.init(key, {
|
|
custom,
|
|
lazy: true
|
|
});
|
|
await next();
|
|
await propSession.finish();
|
|
};
|
|
}
|
|
class PropertySession {
|
|
key;
|
|
value;
|
|
promise;
|
|
fetching;
|
|
read;
|
|
wrote;
|
|
constructor(storage, obj, prop, initial){
|
|
this.storage = storage;
|
|
this.obj = obj;
|
|
this.prop = prop;
|
|
this.initial = initial;
|
|
this.fetching = false;
|
|
this.read = false;
|
|
this.wrote = false;
|
|
}
|
|
load() {
|
|
if (this.key === undefined) {
|
|
return;
|
|
}
|
|
if (this.wrote) {
|
|
return;
|
|
}
|
|
if (this.promise === undefined) {
|
|
this.fetching = true;
|
|
this.promise = Promise.resolve(this.storage.read(this.key)).then((val)=>{
|
|
this.fetching = false;
|
|
if (this.wrote) {
|
|
return this.value;
|
|
}
|
|
if (val !== undefined) {
|
|
this.value = val;
|
|
return val;
|
|
}
|
|
val = this.initial?.();
|
|
if (val !== undefined) {
|
|
this.wrote = true;
|
|
this.value = val;
|
|
}
|
|
return val;
|
|
});
|
|
}
|
|
return this.promise;
|
|
}
|
|
async init(key, opts) {
|
|
this.key = key;
|
|
if (!opts.lazy) await this.load();
|
|
Object.defineProperty(this.obj, this.prop, {
|
|
enumerable: true,
|
|
get: ()=>{
|
|
if (key === undefined) {
|
|
const msg = undef("access", opts);
|
|
throw new Error(msg);
|
|
}
|
|
this.read = true;
|
|
if (!opts.lazy || this.wrote) return this.value;
|
|
this.load();
|
|
return this.fetching ? this.promise : this.value;
|
|
},
|
|
set: (v)=>{
|
|
if (key === undefined) {
|
|
const msg = undef("assign", opts);
|
|
throw new Error(msg);
|
|
}
|
|
this.wrote = true;
|
|
this.fetching = false;
|
|
this.value = v;
|
|
}
|
|
});
|
|
}
|
|
delete() {
|
|
Object.assign(this.obj, {
|
|
[this.prop]: undefined
|
|
});
|
|
}
|
|
async finish() {
|
|
if (this.key !== undefined) {
|
|
if (this.read) await this.load();
|
|
if (this.read || this.wrote) {
|
|
const value = await this.value;
|
|
if (value == null) await this.storage.delete(this.key);
|
|
else await this.storage.write(this.key, value);
|
|
}
|
|
}
|
|
}
|
|
storage;
|
|
obj;
|
|
prop;
|
|
initial;
|
|
}
|
|
function fillDefaults(opts = {}) {
|
|
let { getSessionKey =defaultGetSessionKey , initial , storage } = opts;
|
|
if (storage == null) {
|
|
debug2("Storing session data in memory, all data will be lost when the bot restarts.");
|
|
storage = new MemorySessionStorage();
|
|
}
|
|
const custom = getSessionKey !== defaultGetSessionKey;
|
|
return {
|
|
initial,
|
|
storage,
|
|
getSessionKey,
|
|
custom
|
|
};
|
|
}
|
|
function defaultGetSessionKey(ctx) {
|
|
return ctx.chat?.id.toString();
|
|
}
|
|
function undef(op, opts) {
|
|
const { lazy =false , custom } = opts;
|
|
const reason = custom ? "the custom `getSessionKey` function returned undefined for this update" : "this update does not belong to a chat, so the session key is undefined";
|
|
return `Cannot ${op} ${lazy ? "lazy " : ""}session data because ${reason}!`;
|
|
}
|
|
function isEnhance(value) {
|
|
return value === undefined || typeof value === "object" && value !== null && "__d" in value;
|
|
}
|
|
function enhanceStorage(options) {
|
|
let { storage , millisecondsToLive , migrations } = options;
|
|
storage = compatStorage(storage);
|
|
if (millisecondsToLive !== undefined) {
|
|
storage = timeoutStorage(storage, millisecondsToLive);
|
|
}
|
|
if (migrations !== undefined) {
|
|
storage = migrationStorage(storage, migrations);
|
|
}
|
|
return wrapStorage(storage);
|
|
}
|
|
function compatStorage(storage) {
|
|
return {
|
|
read: async (k)=>{
|
|
const v = await storage.read(k);
|
|
return isEnhance(v) ? v : {
|
|
__d: v
|
|
};
|
|
},
|
|
write: (k, v)=>storage.write(k, v),
|
|
delete: (k)=>storage.delete(k)
|
|
};
|
|
}
|
|
function timeoutStorage(storage, millisecondsToLive) {
|
|
const ttlStorage = {
|
|
read: async (k)=>{
|
|
const value = await storage.read(k);
|
|
if (value === undefined) return undefined;
|
|
if (value.e === undefined) {
|
|
await ttlStorage.write(k, value);
|
|
return value;
|
|
}
|
|
if (value.e < Date.now()) {
|
|
await ttlStorage.delete(k);
|
|
return undefined;
|
|
}
|
|
return value;
|
|
},
|
|
write: async (k, v)=>{
|
|
v.e = addExpiryDate(v, millisecondsToLive).expires;
|
|
await storage.write(k, v);
|
|
},
|
|
delete: (k)=>storage.delete(k)
|
|
};
|
|
return ttlStorage;
|
|
}
|
|
function migrationStorage(storage, migrations) {
|
|
const versions = Object.keys(migrations).map((v)=>parseInt(v)).sort((a, b)=>a - b);
|
|
const count = versions.length;
|
|
if (count === 0) throw new Error("No migrations given!");
|
|
const earliest = versions[0];
|
|
const last = count - 1;
|
|
const latest = versions[last];
|
|
const index = new Map();
|
|
versions.forEach((v, i)=>index.set(v, i));
|
|
function nextAfter(current) {
|
|
let i = last;
|
|
while(current <= versions[i])i--;
|
|
return i;
|
|
}
|
|
return {
|
|
read: async (k)=>{
|
|
const val = await storage.read(k);
|
|
if (val === undefined) return val;
|
|
let { __d: value , v: current = earliest - 1 } = val;
|
|
let i = 1 + (index.get(current) ?? nextAfter(current));
|
|
for(; i < count; i++)value = migrations[versions[i]](value);
|
|
return {
|
|
...val,
|
|
v: latest,
|
|
__d: value
|
|
};
|
|
},
|
|
write: (k, v)=>storage.write(k, {
|
|
v: latest,
|
|
...v
|
|
}),
|
|
delete: (k)=>storage.delete(k)
|
|
};
|
|
}
|
|
function wrapStorage(storage) {
|
|
return {
|
|
read: (k)=>Promise.resolve(storage.read(k)).then((v)=>v?.__d),
|
|
write: (k, v)=>storage.write(k, {
|
|
__d: v
|
|
}),
|
|
delete: (k)=>storage.delete(k)
|
|
};
|
|
}
|
|
class MemorySessionStorage {
|
|
storage;
|
|
constructor(timeToLive){
|
|
this.timeToLive = timeToLive;
|
|
this.storage = new Map();
|
|
}
|
|
read(key) {
|
|
const value = this.storage.get(key);
|
|
if (value === undefined) return undefined;
|
|
if (value.expires !== undefined && value.expires < Date.now()) {
|
|
this.delete(key);
|
|
return undefined;
|
|
}
|
|
return value.session;
|
|
}
|
|
readAll() {
|
|
return this.readAllValues();
|
|
}
|
|
readAllKeys() {
|
|
return Array.from(this.storage.keys());
|
|
}
|
|
readAllValues() {
|
|
return Array.from(this.storage.keys()).map((key)=>this.read(key)).filter((value)=>value !== undefined);
|
|
}
|
|
readAllEntries() {
|
|
return Array.from(this.storage.keys()).map((key)=>[
|
|
key,
|
|
this.read(key)
|
|
]).filter((pair)=>pair[1] !== undefined);
|
|
}
|
|
has(key) {
|
|
return this.storage.has(key);
|
|
}
|
|
write(key, value) {
|
|
this.storage.set(key, addExpiryDate(value, this.timeToLive));
|
|
}
|
|
delete(key) {
|
|
this.storage.delete(key);
|
|
}
|
|
timeToLive;
|
|
}
|
|
function addExpiryDate(value, ttl) {
|
|
if (ttl !== undefined && ttl < Infinity) {
|
|
const now = Date.now();
|
|
return {
|
|
session: value,
|
|
expires: now + ttl
|
|
};
|
|
} else {
|
|
return {
|
|
session: value
|
|
};
|
|
}
|
|
}
|
|
export { session as session };
|
|
export { lazySession as lazySession };
|
|
export { enhanceStorage as enhanceStorage };
|
|
export { MemorySessionStorage as MemorySessionStorage };
|
|
const SECRET_HEADER = "X-Telegram-Bot-Api-Secret-Token";
|
|
const SECRET_HEADER_LOWERCASE = SECRET_HEADER.toLowerCase();
|
|
const WRONG_TOKEN_ERROR = "secret token is wrong";
|
|
const ok = ()=>new Response(null, {
|
|
status: 200
|
|
});
|
|
const okJson = (json)=>new Response(json, {
|
|
status: 200,
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
}
|
|
});
|
|
const unauthorized = ()=>new Response('"unauthorized"', {
|
|
status: 401,
|
|
statusText: WRONG_TOKEN_ERROR
|
|
});
|
|
const express = (req, res)=>({
|
|
update: Promise.resolve(req.body),
|
|
header: req.header(SECRET_HEADER),
|
|
end: ()=>res.end(),
|
|
respond: (json)=>{
|
|
res.set("Content-Type", "application/json");
|
|
res.send(json);
|
|
},
|
|
unauthorized: ()=>{
|
|
res.status(401).send(WRONG_TOKEN_ERROR);
|
|
}
|
|
});
|
|
const koa = (ctx)=>({
|
|
update: Promise.resolve(ctx.request.body),
|
|
header: ctx.get(SECRET_HEADER),
|
|
end: ()=>{
|
|
ctx.body = "";
|
|
},
|
|
respond: (json)=>{
|
|
ctx.set("Content-Type", "application/json");
|
|
ctx.response.body = json;
|
|
},
|
|
unauthorized: ()=>{
|
|
ctx.status = 401;
|
|
}
|
|
});
|
|
const fastify = (req, reply)=>({
|
|
update: Promise.resolve(req.body),
|
|
header: req.headers[SECRET_HEADER_LOWERCASE],
|
|
end: ()=>reply.status(200).send(),
|
|
respond: (json)=>reply.send(json),
|
|
unauthorized: ()=>reply.code(401).send(WRONG_TOKEN_ERROR)
|
|
});
|
|
const serveHttp = (requestEvent)=>({
|
|
update: requestEvent.request.json(),
|
|
header: requestEvent.request.headers.get(SECRET_HEADER) || undefined,
|
|
end: ()=>requestEvent.respondWith(ok()),
|
|
respond: (json)=>requestEvent.respondWith(okJson(json)),
|
|
unauthorized: ()=>requestEvent.respondWith(unauthorized())
|
|
});
|
|
const stdHttp = (req)=>{
|
|
let resolveResponse;
|
|
return {
|
|
update: req.json(),
|
|
header: req.headers.get(SECRET_HEADER) || undefined,
|
|
end: ()=>{
|
|
if (resolveResponse) resolveResponse(ok());
|
|
},
|
|
respond: (json)=>{
|
|
if (resolveResponse) resolveResponse(okJson(json));
|
|
},
|
|
unauthorized: ()=>{
|
|
if (resolveResponse) resolveResponse(unauthorized());
|
|
},
|
|
handlerReturn: new Promise((resolve)=>{
|
|
resolveResponse = resolve;
|
|
})
|
|
};
|
|
};
|
|
const oak = (ctx)=>({
|
|
update: ctx.request.body({
|
|
type: "json"
|
|
}).value,
|
|
header: ctx.request.headers.get(SECRET_HEADER) || undefined,
|
|
end: ()=>{
|
|
ctx.response.status = 200;
|
|
},
|
|
respond: (json)=>{
|
|
ctx.response.type = "json";
|
|
ctx.response.body = json;
|
|
},
|
|
unauthorized: ()=>{
|
|
ctx.response.status = 401;
|
|
}
|
|
});
|
|
const http = (req, res)=>{
|
|
const secretHeaderFromRequest = req.headers[SECRET_HEADER_LOWERCASE];
|
|
return {
|
|
update: new Promise((resolve, reject)=>{
|
|
const chunks = [];
|
|
req.on("data", (chunk)=>chunks.push(chunk)).once("end", ()=>{
|
|
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
resolve(JSON.parse(raw));
|
|
}).once("error", reject);
|
|
}),
|
|
header: Array.isArray(secretHeaderFromRequest) ? secretHeaderFromRequest[0] : secretHeaderFromRequest,
|
|
end: ()=>res.end(),
|
|
respond: (json)=>res.writeHead(200, {
|
|
"Content-Type": "application/json"
|
|
}).end(json),
|
|
unauthorized: ()=>res.writeHead(401).end(WRONG_TOKEN_ERROR)
|
|
};
|
|
};
|
|
const awsLambda = (event, _context, callback)=>({
|
|
update: JSON.parse(event.body),
|
|
header: event.headers[SECRET_HEADER],
|
|
end: ()=>callback(null, {
|
|
statusCode: 200
|
|
}),
|
|
respond: (json)=>callback(null, {
|
|
statusCode: 200,
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: json
|
|
}),
|
|
unauthorized: ()=>callback(null, {
|
|
statusCode: 401
|
|
})
|
|
});
|
|
const awsLambdaAsync = (event, _context)=>{
|
|
let resolveResponse;
|
|
return {
|
|
update: JSON.parse(event.body),
|
|
header: event.headers[SECRET_HEADER],
|
|
end: ()=>resolveResponse({
|
|
statusCode: 200
|
|
}),
|
|
respond: (json)=>resolveResponse({
|
|
statusCode: 200,
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: json
|
|
}),
|
|
unauthorized: ()=>resolveResponse({
|
|
statusCode: 401
|
|
}),
|
|
handlerReturn: new Promise((resolve)=>{
|
|
resolveResponse = resolve;
|
|
})
|
|
};
|
|
};
|
|
const azure = (context, req)=>({
|
|
update: Promise.resolve(req.body),
|
|
header: context.res.headers[SECRET_HEADER],
|
|
end: ()=>context.res = {
|
|
status: 200,
|
|
body: ""
|
|
},
|
|
respond: (json)=>{
|
|
context.res.set("Content-Type", "application/json");
|
|
context.res.send(json);
|
|
},
|
|
unauthorized: ()=>{
|
|
context.res.send(401, WRONG_TOKEN_ERROR);
|
|
}
|
|
});
|
|
const nextJs = (req, res)=>({
|
|
update: Promise.resolve(req.body),
|
|
header: req.headers[SECRET_HEADER_LOWERCASE],
|
|
end: ()=>res.end(),
|
|
respond: (json)=>res.status(200).json(json),
|
|
unauthorized: ()=>res.status(401).send(WRONG_TOKEN_ERROR)
|
|
});
|
|
const sveltekit = ({ request })=>{
|
|
let resolveResponse;
|
|
return {
|
|
update: Promise.resolve(request.json()),
|
|
header: request.headers.get(SECRET_HEADER) || undefined,
|
|
end: ()=>{
|
|
if (resolveResponse) resolveResponse(ok());
|
|
},
|
|
respond: (json)=>{
|
|
if (resolveResponse) resolveResponse(okJson(json));
|
|
},
|
|
unauthorized: ()=>{
|
|
if (resolveResponse) resolveResponse(unauthorized());
|
|
},
|
|
handlerReturn: new Promise((resolve)=>{
|
|
resolveResponse = resolve;
|
|
})
|
|
};
|
|
};
|
|
const cloudflare = (event)=>{
|
|
let resolveResponse;
|
|
event.respondWith(new Promise((resolve)=>{
|
|
resolveResponse = resolve;
|
|
}));
|
|
return {
|
|
update: event.request.json(),
|
|
header: event.request.headers.get(SECRET_HEADER) || undefined,
|
|
end: ()=>{
|
|
resolveResponse(ok());
|
|
},
|
|
respond: (json)=>{
|
|
resolveResponse(okJson(json));
|
|
},
|
|
unauthorized: ()=>{
|
|
resolveResponse(unauthorized());
|
|
}
|
|
};
|
|
};
|
|
const cloudflareModule = (request)=>{
|
|
let resolveResponse;
|
|
return {
|
|
update: request.json(),
|
|
header: request.headers.get(SECRET_HEADER) || undefined,
|
|
end: ()=>{
|
|
resolveResponse(ok());
|
|
},
|
|
respond: (json)=>{
|
|
resolveResponse(okJson(json));
|
|
},
|
|
unauthorized: ()=>{
|
|
resolveResponse(unauthorized());
|
|
},
|
|
handlerReturn: new Promise((resolve)=>{
|
|
resolveResponse = resolve;
|
|
})
|
|
};
|
|
};
|
|
const hono = (ctx)=>{
|
|
let resolveResponse;
|
|
return {
|
|
update: ctx.req.json(),
|
|
header: ctx.req.headers.get(SECRET_HEADER) || undefined,
|
|
end: ()=>{
|
|
resolveResponse(ctx.body());
|
|
},
|
|
respond: (json)=>{
|
|
ctx.header('Content-Type", "application/json');
|
|
resolveResponse(ctx.body(json));
|
|
},
|
|
unauthorized: ()=>{
|
|
ctx.status(401);
|
|
ctx.statusText(WRONG_TOKEN_ERROR);
|
|
resolveResponse(ctx.body());
|
|
},
|
|
handlerReturn: new Promise((resolve)=>{
|
|
resolveResponse = resolve;
|
|
})
|
|
};
|
|
};
|
|
const worktop = (req, res)=>({
|
|
update: Promise.resolve(req.body.json()),
|
|
header: req.headers.get(SECRET_HEADER),
|
|
end: ()=>res.end(),
|
|
respond: (json)=>res.send(200, json),
|
|
unauthorized: ()=>res.send(401, WRONG_TOKEN_ERROR)
|
|
});
|
|
const adapters = {
|
|
express,
|
|
koa,
|
|
fastify,
|
|
serveHttp,
|
|
"std/http": stdHttp,
|
|
oak,
|
|
http,
|
|
https: http,
|
|
"aws-lambda": awsLambda,
|
|
"aws-lambda-async": awsLambdaAsync,
|
|
azure,
|
|
"next-js": nextJs,
|
|
sveltekit,
|
|
cloudflare,
|
|
"cloudflare-mod": cloudflareModule,
|
|
hono,
|
|
worktop
|
|
};
|
|
const debugErr1 = browser$1("grammy:error");
|
|
const callbackAdapter = (update, callback, header, unauthorized = ()=>callback('"unauthorized"'))=>({
|
|
update: Promise.resolve(update),
|
|
respond: callback,
|
|
header,
|
|
unauthorized
|
|
});
|
|
const adapters1 = {
|
|
...adapters,
|
|
callback: callbackAdapter
|
|
};
|
|
function webhookCallback(bot, adapter = defaultAdapter, onTimeout = "throw", timeoutMilliseconds = 10_000, secretToken) {
|
|
const { onTimeout: timeout = "throw" , timeoutMilliseconds: ms = 10_000 , secretToken: token } = typeof onTimeout === "object" ? onTimeout : {
|
|
onTimeout,
|
|
timeoutMilliseconds,
|
|
secretToken
|
|
};
|
|
let initialized = false;
|
|
const server = typeof adapter === "string" ? adapters1[adapter] : adapter;
|
|
return async (...args)=>{
|
|
const { update , respond , unauthorized , end , handlerReturn , header } = server(...args);
|
|
if (!initialized) {
|
|
await bot.init();
|
|
initialized = true;
|
|
}
|
|
if (header !== token) {
|
|
await unauthorized();
|
|
console.log(handlerReturn);
|
|
return handlerReturn;
|
|
}
|
|
let usedWebhookReply = false;
|
|
const webhookReplyEnvelope = {
|
|
async send (json) {
|
|
usedWebhookReply = true;
|
|
await respond(json);
|
|
}
|
|
};
|
|
await timeoutIfNecessary(bot.handleUpdate(await update, webhookReplyEnvelope), typeof timeout === "function" ? ()=>timeout(...args) : timeout, ms);
|
|
if (!usedWebhookReply) end?.();
|
|
return handlerReturn;
|
|
};
|
|
}
|
|
function timeoutIfNecessary(task, onTimeout, timeout) {
|
|
if (timeout === Infinity) return task;
|
|
return new Promise((resolve, reject)=>{
|
|
const handle = setTimeout(()=>{
|
|
if (onTimeout === "throw") {
|
|
reject(new Error(`Request timed out after ${timeout} ms`));
|
|
} else {
|
|
if (typeof onTimeout === "function") onTimeout();
|
|
resolve();
|
|
}
|
|
const now = Date.now();
|
|
task.finally(()=>{
|
|
const diff = Date.now() - now;
|
|
debugErr1(`Request completed ${diff} ms after timeout!`);
|
|
});
|
|
}, timeout);
|
|
task.then(resolve).catch(reject).finally(()=>clearTimeout(handle));
|
|
});
|
|
}
|
|
export { webhookCallback as webhookCallback };
|
|
export { Bot as Bot, BotError as BotError };
|
|
export { InputFile as InputFile };
|
|
export { Context as Context };
|
|
export { Composer as Composer };
|
|
export { matchFilter as matchFilter };
|
|
export { Api as Api };
|
|
export { GrammyError as GrammyError, HttpError as HttpError };
|