diff --git a/add_user.ts b/add_user.ts index 4aa9499..497c136 100644 --- a/add_user.ts +++ b/add_user.ts @@ -30,7 +30,7 @@ export async function addUser(apiId: number, apiHash: string): Promise { const id = "user" + crypto.randomUUID(); ClientManager.createKvPath(); const storage = new StorageDenoKV(path.join(ClientManager.KV_PATH, id)); - const client = new Client(storage, apiId, apiHash); + const client = new Client({ storage, apiId, apiHash }); await client.start({ phone: () => prompt("Phone number:")!, diff --git a/allowed_methods.ts b/allowed_methods.ts index 448cc29..3e209c8 100644 --- a/allowed_methods.ts +++ b/allowed_methods.ts @@ -107,6 +107,14 @@ export const ALLOWED_METHODS = [ "removeStoryFromHighlights", "blockUser", "unblockUser", + "downloadLiveStreamChunk", + "getLiveStreamChannels", + "getVideoChat", + "joinLiveStream", + "joinVideoChat", + "leaveVideoChat", + "scheduleVideoChat", + "startVideoChat", ] as const; export type AllowedMethod = (typeof ALLOWED_METHODS)[number]; diff --git a/benchmarks/disallowed_functions_bench.ts b/benchmarks/disallowed_functions_bench.ts new file mode 100644 index 0000000..37e4b48 --- /dev/null +++ b/benchmarks/disallowed_functions_bench.ts @@ -0,0 +1,48 @@ +/** + * MTKruto Server + * Copyright (C) 2024 Roj + * + * This file is part of MTKruto Server. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { functions } from "mtkruto/mod.ts"; +import { ALLOWED_METHODS } from "../allowed_methods.ts"; +import { isFunctionDisallowed } from "../disallowed_functions.ts"; + +Deno.bench("isFunctionDisallowed(ping)", () => { + isFunctionDisallowed(new functions.ping({ ping_id: 0n })); +}); + +Deno.bench("isFunctionDisallowed(Allowed Method)", () => { + isFunctionDisallowed(ALLOWED_METHODS[0]); +}); + +Deno.bench("isFunctionDisallowed(MTProto Function)", () => { + isFunctionDisallowed( + new functions.req_DH_params({ + nonce: 0n, + encrypted_data: new Uint8Array(), + p: new Uint8Array(), + q: new Uint8Array(), + public_key_fingerprint: 0n, + server_nonce: 0n, + }), + ); +}); + +Deno.bench("isFunctionDisallowed(ALLOWED_METHODS)", () => { + ALLOWED_METHODS.map(isFunctionDisallowed); +}); diff --git a/benchmarks/params_bench.ts b/benchmarks/params_bench.ts new file mode 100644 index 0000000..0bef34f --- /dev/null +++ b/benchmarks/params_bench.ts @@ -0,0 +1,56 @@ +/** + * MTKruto Server + * Copyright (C) 2024 Roj + * + * This file is part of MTKruto Server. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { parseFormDataParams, parseGetParams } from "../params.ts"; + +const formData = new FormData(); +const params = new URLSearchParams(); + +const args = [ + JSON.stringify(1), + "MTKruto", + false, + null, + [1, 2, 3, 4, 5, "", null, false], + { _: [1, 2, 3, 4, 5, "", null, false] }, +]; +const kwargs = [ + ["a", "b"], + ["c", "d"], +]; +for (let i = 0; i < 2; ++i) { + for (const arg of args.map((v) => JSON.stringify(v))) { + params.set(arg, ""); + formData.append("_", arg); + } + for (const [k, v] of kwargs) { + params.set(k, JSON.stringify(v)); + } +} +formData.append("_", new Blob([new Uint8Array(1024)])); +formData.append("_", JSON.stringify(Object.fromEntries(kwargs))); + +Deno.bench("parseFormDataParams", () => { + parseFormDataParams(formData); +}); + +Deno.bench("parseGetParams", () => { + parseGetParams(params); +}); diff --git a/benchmarks/responses_bench.ts b/benchmarks/responses_bench.ts new file mode 100644 index 0000000..27da1a0 --- /dev/null +++ b/benchmarks/responses_bench.ts @@ -0,0 +1,49 @@ +/** + * MTKruto Server + * Copyright (C) 2024 Roj + * + * This file is part of MTKruto Server. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { assertArgCount } from "../responses.ts"; + +Deno.bench("assertArgCount(2, 2)", () => { + assertArgCount(2, 2); +}); + +Deno.bench("assertArgCount(1, 2)", () => { + try { + assertArgCount(1, 2); + } catch { + // + } +}); + +Deno.bench("assertArgCount(0, 1)", () => { + try { + assertArgCount(0, 1); + } catch { + // + } +}); + +Deno.bench("assertArgCount(1, 0)", () => { + try { + assertArgCount(1, 0); + } catch { + // + } +}); diff --git a/benchmarks/transform_bench.ts b/benchmarks/transform_bench.ts new file mode 100644 index 0000000..c057fa8 --- /dev/null +++ b/benchmarks/transform_bench.ts @@ -0,0 +1,40 @@ +/** + * MTKruto Server + * Copyright (C) 2024 Roj + * + * This file is part of MTKruto Server. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { transform } from "../transform.ts"; + +const date = new Date(); +const object = { _: { _: { _: date, a: [date, date] } } }; + +Deno.bench("transform", () => { + transform(object); +}); + +Deno.bench("JSON.stringify(transform)", () => { + JSON.stringify(transform(object)); +}); + +Deno.bench("JSON.parse(JSON.stringify(transform))", () => { + JSON.parse(JSON.stringify(transform(object))); +}); + +Deno.bench("transform(JSON.parse(JSON.stringify(transform)))", () => { + transform(JSON.parse(JSON.stringify(transform(object)))); +}); diff --git a/client/client.ts b/client/client.ts index ddfeecb..198f2f0 100644 --- a/client/client.ts +++ b/client/client.ts @@ -22,7 +22,6 @@ import { AllowedMethod, BotCommand, BusinessConnection, - InlineQueryAnswer, CallbackQueryAnswer, Chat, ChatMember, @@ -31,8 +30,10 @@ import { Composer, Context_, InactiveChat, + InlineQueryAnswer, InviteLink, iterateReadableStream, + LiveStreamChannel, Message, MessageAnimation, MessageAudio, @@ -52,10 +53,14 @@ import { resolve, Sticker, Story, + transform, unimplemented, unreachable, Update, User, + VideoChat, + VideoChatActive, + VideoChatScheduled, } from "./deps.ts"; import { Queue } from "./queue.ts"; @@ -701,7 +706,7 @@ export class Client extends Composer { : { "content-type": "application/json" }, body, }); - const result = await response.json(); + const result = transform(await response.json()); if (response.status == 200) { return result; } else { @@ -1110,11 +1115,10 @@ export class Client extends Composer { // ========================= CALLBACK QUERIES ========================= // // - - sendCallbackQuery( + sendCallbackQuery( ...args: Parameters ): Promise { - return this.#request("sendCallbackQuery", args); + return this.#request("sendCallbackQuery", args); } async answerCallbackQuery( @@ -1127,11 +1131,10 @@ export class Client extends Composer { // ========================= INLINE QUERIES ========================= // // - sendInlineQuery( ...args: Parameters ): Promise { - return this.#request("sendInlineQuery", args); + return this.#request("sendInlineQuery", args); } async answerInlineQuery( @@ -1289,4 +1292,54 @@ export class Client extends Composer { ): Promise { await this.#request("unblockUser", args); } + + // + // ========================= VIDEO CHATS ========================= // + // + + downloadLiveStreamChunk(): never { + unimplemented(); + } + + getLiveStreamChannels( + ...args: Parameters + ): Promise { + return this.#request("getLiveStreamChannels", args); + } + + getVideoChat( + ...args: Parameters + ): Promise { + return this.#request("getVideoChat", args); + } + + async joinLiveStream( + ...args: Parameters + ): Promise { + await this.#request("joinLiveStream", args); + } + + joinVideoChat( + ...args: Parameters + ): Promise { + return this.#request("joinVideoChat", args); + } + + async leaveVideoChat( + ...args: Parameters + ): Promise { + await this.#request("leaveVideoChat", args); + } + + scheduleVideoChat( + ...args: Parameters + ): Promise { + return this.#request("scheduleVideoChat", args); + } + + startVideoChat( + ...args: Parameters + ): Promise { + return this.#request("startVideoChat", args); + } } diff --git a/client/deps.ts b/client/deps.ts index 9144888..6ad8896 100644 --- a/client/deps.ts +++ b/client/deps.ts @@ -18,29 +18,31 @@ * along with this program. If not, see . */ -export { unreachable } from "https://deno.land/std@0.223.0/assert/unreachable.ts"; -export { unimplemented } from "https://deno.land/std@0.223.0/assert/unimplemented.ts"; +export { unreachable } from "https://deno.land/std@0.224.0/assert/unreachable.ts"; +export { unimplemented } from "https://deno.land/std@0.224.0/assert/unimplemented.ts"; export type { BotCommand, BusinessConnection, + CallbackQueryAnswer, Chat, ChatMember, Client as Client_, Context as Context_, InactiveChat, + InlineQueryAnswer, InviteLink, + LiveStreamChannel, Message, MessageAnimation, MessageAudio, MessageContact, MessageDice, MessageDocument, - MessageLocation,CallbackQueryAnswer, + MessageLocation, MessagePhoto, MessagePoll, MessageSticker, - InlineQueryAnswer, MessageText, MessageVenue, MessageVideo, @@ -51,9 +53,13 @@ export type { Story, Update, User, -} from "https://deno.land/x/mtkruto@0.1.500/mod.ts"; -export { cleanObject } from "https://deno.land/x/mtkruto@0.1.500/utilities/0_object.ts"; -export { iterateReadableStream } from "https://deno.land/x/mtkruto@0.1.500/utilities/1_misc.ts"; -export { Composer } from "https://deno.land/x/mtkruto@0.1.500/client/1_composer.ts"; -export { resolve } from "https://deno.land/x/mtkruto@0.1.500/client/0_utilities.ts"; + VideoChat, + VideoChatActive, + VideoChatScheduled, +} from "https://deno.land/x/mtkruto@0.1.708/mod.ts"; +export { cleanObject } from "https://deno.land/x/mtkruto@0.1.708/utilities/0_object.ts"; +export { iterateReadableStream } from "https://deno.land/x/mtkruto@0.1.708/utilities/1_misc.ts"; +export { Composer } from "https://deno.land/x/mtkruto@0.1.708/client/1_composer.ts"; +export { resolve } from "https://deno.land/x/mtkruto@0.1.708/client/0_utilities.ts"; export type { AllowedMethod } from "../allowed_methods.ts"; +export { transform } from "../transform.ts"; diff --git a/client_manager.ts b/client_manager.ts index 3347183..a8bbc89 100644 --- a/client_manager.ts +++ b/client_manager.ts @@ -109,15 +109,13 @@ export class ClientManager { throw new InputError("Invalid client ID"); } const kvPath = path.join(ClientManager.KV_PATH, id); - const client = new Client( - new StorageDenoKV(kvPath), - this.#apiId, - this.#apiHash, - { - dropPendingUpdates: false, - transportProvider: transportProviderTcp(), - }, - ); + const client = new Client({ + storage: new StorageDenoKV(kvPath), + apiId: this.#apiId, + apiHash: this.#apiHash, + dropPendingUpdates: false, + transportProvider: transportProviderTcp(), + }); let updates = this.#updates.get(client); if (!updates) { this.#updates.set(client, updates = []); @@ -148,8 +146,8 @@ export class ClientManager { } }); if (id.startsWith("bot")) { - const token = id.slice(3); - await client.start(token); + const botToken = id.slice(3); + await client.start({ botToken }); } else { await client.start({ phone: () => { @@ -215,21 +213,8 @@ export class ClientManager { async getUpdates(id: string, timeoutSeconds: number) { const updates = await this.#getUpdatesInner(id, timeoutSeconds); - try { - return updates; - } finally { - this.#addToUpdateCleanupQueue(id, updates); - } - } - - async canGetUpdates(id: string) { - const client = await this.getClient(id); - if (this.#webhooks.has(client)) { - throw new InputError("getUpdates is not allowed when a webhook is set."); - } - if (this.#polls.has(client)) { - throw new InputError("Another getUpdates is in progress."); - } + this.#addToUpdateCleanupQueue(id, updates); + return updates; } #polls = new Set(); @@ -237,12 +222,21 @@ export class ClientManager { #updateResolvers = new Map void>(); #getUpdatesControllers = new Map(); async #getUpdatesInner(id: string, timeoutSeconds: number) { - const client = this.mustGetClient(id); + const client = await this.getClient(id); if (this.#webhooks.has(client)) { - unreachable(); + throw new InputError("getUpdates is not allowed when a webhook is set."); } + if (this.#polls.has(client)) { - unreachable(); + const controller = this.#getUpdatesControllers.get(client); + if (controller) { + controller.abort(); + } + this.#getUpdatesControllers.delete(client); + // just in case + this.#polls.delete(client); + this.#updateResolvers.get(client)?.(); + this.#updateResolvers.delete(client); } this.#polls.add(client); let controller: AbortController | null = null; @@ -268,6 +262,9 @@ export class ClientManager { } this.#updateResolvers.set(client, resolve); }); + if (controller.signal.aborted) { + throw new InputError("Aborted by another getUpdates request."); + } updates = this.#updates.get(client); if (updates && updates.length) { @@ -285,20 +282,15 @@ export class ClientManager { if (timeout != null) { clearTimeout(timeout); } - if (controller != null) { + if ( + controller != null && + this.#getUpdatesControllers.get(client) == controller + ) { this.#getUpdatesControllers.delete(client); } } } - abortGetUpdates(id: string) { - const client = this.mustGetClient(id); - const controller = this.#getUpdatesControllers.get(client); - if (controller) { - controller.abort(); - } - } - #startPeriodicChecks() { Promise.resolve().then(this.#periodicChecks.bind(this)); } diff --git a/deno.jsonc b/deno.jsonc index 0e6d511..345438e 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -8,7 +8,7 @@ }, "imports": { "mtkruto/": "./MTKruto/", - "std/": "https://deno.land/std@0.223.0/", + "std/": "https://deno.land/std@0.224.0/", "preact": "https://esm.sh/preact@10.20.1", "preact/": "https://esm.sh/preact@10.20.1/", "ts_morph/": "https://deno.land/x/ts_morph@19.0.0/", diff --git a/main.ts b/main.ts index 89bffac..4f46996 100644 --- a/main.ts +++ b/main.ts @@ -128,7 +128,7 @@ async function handleRequest(id: string, method: string, params: any[]) { } switch (method) { case "getUpdates": - return await handleGetUpdates(worker, id); + return await handleGetUpdates(worker, id, params[0]); case "invoke": assertArgCount(params.length, 1); return await handleInvoke(worker, id, params[0]); @@ -160,30 +160,9 @@ async function handleMethod( } } -async function handleGetUpdates(worker: number, id: string) { - const enc = new TextEncoder(); - const response = await workers.call(worker, "canGetUpdates", id); - if (response != null) { - return Response.json(...response); - } - return new Response( - new ReadableStream( - { - async start(controller) { - try { - const updates = await workers.call(worker, "getUpdates", id, 0); - controller.enqueue(enc.encode(JSON.stringify(updates))); - controller.close(); - } catch { - controller.error(); - } - }, - async cancel() { - await workers.call(worker, "abortGetUpdates", id); - }, - }, - ), - ); +async function handleGetUpdates(worker: number, id: string, timeout: number) { + const result = await workers.call(worker, "getUpdates", id, timeout || 0); + return Response.json(...result); } async function handleInvoke(worker: number, id: string, function_: any) { diff --git a/tests/disallowed_functions_test.ts b/tests/disallowed_functions_test.ts new file mode 100644 index 0000000..3dbcac1 --- /dev/null +++ b/tests/disallowed_functions_test.ts @@ -0,0 +1,52 @@ +/** + * MTKruto Server + * Copyright (C) 2024 Roj + * + * This file is part of MTKruto Server. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { assertEquals } from "std/assert/mod.ts"; + +import { functions } from "mtkruto/mod.ts"; + +import { ALLOWED_METHODS } from "../allowed_methods.ts"; +import { isFunctionDisallowed } from "../disallowed_functions.ts"; + +Deno.bench("isFunctionDisallowed(ping)", () => { +}); + +Deno.test("isFunctionDisallowed", () => { + assertEquals( + isFunctionDisallowed(new functions.ping({ ping_id: 0n })), + false, + ); + for (const m of ALLOWED_METHODS) { + assertEquals(isFunctionDisallowed(m), false); + } + assertEquals( + isFunctionDisallowed( + new functions.req_DH_params({ + nonce: 0n, + encrypted_data: new Uint8Array(), + p: new Uint8Array(), + q: new Uint8Array(), + public_key_fingerprint: 0n, + server_nonce: 0n, + }), + ), + true, + ); +}); diff --git a/tests/responses_test.ts b/tests/responses_test.ts new file mode 100644 index 0000000..81249b5 --- /dev/null +++ b/tests/responses_test.ts @@ -0,0 +1,36 @@ +/** + * MTKruto Server + * Copyright (C) 2024 Roj + * + * This file is part of MTKruto Server. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { assertThrows } from "std/assert/mod.ts"; + +import { assertArgCount } from "../responses.ts"; + +Deno.test("assertArgCount", () => { + assertArgCount(2, 2); + assertThrows(() => { + assertArgCount(1, 2); + }); + assertThrows(() => { + assertArgCount(0, 1); + }); + assertThrows(() => { + assertArgCount(1, 0); + }); +}); diff --git a/tests/transform_test.ts b/tests/transform_test.ts new file mode 100644 index 0000000..aa7bb66 --- /dev/null +++ b/tests/transform_test.ts @@ -0,0 +1,29 @@ +/** + * MTKruto Server + * Copyright (C) 2024 Roj + * + * This file is part of MTKruto Server. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { assertEquals } from "std/assert/mod.ts"; + +import { transform } from "../transform.ts"; + +Deno.test("transform", () => { + const date = new Date(); + const a = { _: { _: { _: date, a: [date, date] } } }; + assertEquals(transform(transform(a)), a); +}); diff --git a/transform.ts b/transform.ts new file mode 100644 index 0000000..2ffd3fc --- /dev/null +++ b/transform.ts @@ -0,0 +1,42 @@ +/** + * MTKruto Server + * Copyright (C) 2024 Roj + * + * This file is part of MTKruto Server. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * Utility function to transform dates in objects into a JSON-(de)serializable format and vice-verca. + */ +export function transform(a: any) { + if (a != null && typeof a === "object") { + for (const key in a) { + if (a[key] != null && typeof a[key] === "object") { + if (a[key] instanceof Date) { + a[key] = { _: "date", value: a[key].toJSON() }; + } else if ( + "_" in a[key] && a[key] == "date" && "value" in a[key] && + typeof a[key].value === "string" + ) { + a[key] = new Date(a[key].value); + } else { + transform(a[key]); + } + } + } + } + return a; +} diff --git a/worker.ts b/worker.ts index d469a14..5c98aca 100644 --- a/worker.ts +++ b/worker.ts @@ -26,10 +26,11 @@ import { existsSync } from "std/fs/mod.ts"; import { InputError } from "mtkruto/0_errors.ts"; import { setLogVerbosity } from "mtkruto/1_utilities.ts"; -import { functions, setLoggingProvider, types, Update } from "mtkruto/mod.ts"; +import { functions, setLoggingProvider, types } from "mtkruto/mod.ts"; import { serialize } from "./tl_json.ts"; import { deserialize } from "./tl_json.ts"; +import { transform } from "./transform.ts"; import { fileLogger } from "./file_logger.ts"; import { isFunctionDisallowed } from "./disallowed_functions.ts"; import { ClientManager, ClientStats } from "./client_manager.ts"; @@ -83,14 +84,12 @@ const handlers = { serve, stats, getUpdates, - abortGetUpdates, invoke, setWebhook, deleteWebhook, startWebhookLoop, unload, dropPendingUpdates, - canGetUpdates, }; export type Handler = typeof handlers; @@ -163,7 +162,7 @@ async function serve( const client = await clientManager.getClient(id); // deno-lint-ignore ban-ts-comment // @ts-ignore - const result = await client[method](...args); + const result = transform(await client[method](...args)); if (result !== undefined) { return [result]; } else { @@ -202,15 +201,14 @@ async function stats(): Promise { }; } -function getUpdates(id: string, timeout: number): Promise { +async function getUpdates( + id: string, + timeout: number, +): Promise> { if (timeout < 0) { - throw new Error(`Invalid timeout: ${timeout}`); + throw new InputError(`Invalid timeout: ${timeout}`); } - return clientManager.getUpdates(id, timeout); -} - -async function abortGetUpdates(id: string) { - await clientManager.abortGetUpdates(id); + return [await clientManager.getUpdates(id, timeout)]; } async function setWebhook( @@ -242,10 +240,3 @@ async function dropPendingUpdates( await clientManager.dropPendingUpdates(id); return [null]; } - -async function canGetUpdates( - id: string, -): Promise | null> { - await clientManager.canGetUpdates(id); - return null; -} diff --git a/worker_manager.ts b/worker_manager.ts index a181ba6..2de7f1e 100644 --- a/worker_manager.ts +++ b/worker_manager.ts @@ -26,7 +26,9 @@ import { Mutex } from "mtkruto/1_utilities.ts"; import type { Handler, WorkerStats } from "./worker.ts"; import { ClientManager } from "./client_manager.ts"; -const workerUrl = path.toFileUrl(path.join(import.meta.dirname!, "worker.ts")); +const workerUrl = path.toFileUrl( + path.join(path.dirname(path.fromFileUrl(import.meta.url)), "worker.ts"), +); interface Worker_ { worker: Worker;