From 63eabf45d9cdbd54e7afc2fed495214da6f7bb58 Mon Sep 17 00:00:00 2001 From: Roj Date: Sat, 18 May 2024 02:18:43 +0300 Subject: [PATCH] Use latest MTKruto --- MTKruto | 2 +- client_manager.ts | 2 +- disallowed_functions.ts | 5 +- tests/tl_json/deserialize_test.ts | 138 ---------------------------- tests/tl_json/serialize_test.ts | 114 ------------------------ tests/transform_test.ts | 8 +- tl_json.ts | 143 ------------------------------ transform.ts | 18 +++- worker.ts | 20 ++--- 9 files changed, 38 insertions(+), 412 deletions(-) delete mode 100644 tests/tl_json/deserialize_test.ts delete mode 100644 tests/tl_json/serialize_test.ts delete mode 100644 tl_json.ts diff --git a/MTKruto b/MTKruto index 51f5f05..14f6ef4 160000 --- a/MTKruto +++ b/MTKruto @@ -1 +1 @@ -Subproject commit 51f5f0582c417ace4c5f0988d083db325581e5b4 +Subproject commit 14f6ef49659e1995082ab565c6897d43275b8737 diff --git a/client_manager.ts b/client_manager.ts index 1edce1d..1f5ef8e 100644 --- a/client_manager.ts +++ b/client_manager.ts @@ -23,11 +23,11 @@ import * as path from "std/path/mod.ts"; import { existsSync } from "std/fs/exists.ts"; import { unreachable } from "std/assert/unreachable.ts"; +import { InputError } from "mtkruto/0_errors.ts"; import { Mutex, Queue } from "mtkruto/1_utilities.ts"; import { Client, errors, - InputError, InvokeErrorHandler, NetworkStatistics, Update, diff --git a/disallowed_functions.ts b/disallowed_functions.ts index bb82308..3ad9da0 100644 --- a/disallowed_functions.ts +++ b/disallowed_functions.ts @@ -18,7 +18,6 @@ * along with this program. If not, see . */ -import { functions, name } from "mtkruto/mod.ts"; import { isMtprotoFunction } from "mtkruto/client/0_utilities.ts"; const DISALLOWED_FUNCTIONS = [ @@ -66,11 +65,11 @@ const DISALLOWED_FUNCTIONS = [ ]; export function isFunctionDisallowed(function_: any) { - if (function_ instanceof functions.ping) { + if (function_._ == "ping") { return false; } - if (DISALLOWED_FUNCTIONS.includes(function_[name])) { + if (DISALLOWED_FUNCTIONS.includes(function_._)) { return true; } diff --git a/tests/tl_json/deserialize_test.ts b/tests/tl_json/deserialize_test.ts deleted file mode 100644 index 788d082..0000000 --- a/tests/tl_json/deserialize_test.ts +++ /dev/null @@ -1,138 +0,0 @@ -/** - * 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, assertThrows } from "std/assert/mod.ts"; - -import { functions, types } from "mtkruto/mod.ts"; - -import { deserialize } from "../../tl_json.ts"; - -Deno.test("errors on null, symbol, undefined", () => { - assertThrows(() => deserialize(null)); - assertThrows(() => deserialize(Symbol.for("err"))); - assertThrows(() => deserialize(undefined)); -}); - -Deno.test("returns strings as-is", () => { - for (let i = 0; i < 100; ++i) { - const id = crypto.randomUUID(); - assertEquals(deserialize(id), id); - } -}); - -Deno.test("returns true as-is", () => { - assertEquals(deserialize(true), true); -}); - -Deno.test("returns false as undefined", () => { - assertEquals(deserialize(false), undefined); -}); - -Deno.test("errors on objects with no _ field", () => { - assertThrows(() => deserialize({})); - assertThrows(() => deserialize({ a: 1 })); -}); - -Deno.test("errors on BigInt objects with no value field", () => { - assertThrows(() => deserialize({ _: "bigint" })); -}); - -Deno.test("errors on BigInt objects with non-string value field", () => { - assertThrows(() => deserialize({ _: "bigint", value: 1 })); -}); -Deno.test("errors on BigInt objects with empty value field", () => { - assertThrows(() => deserialize({ _: "bigint", value: "" })); -}); - -Deno.test("properly deserializes BigInt objects", () => { - for (let i = 0; i < 1000; ++i) { - const bi = BigInt(Math.ceil(Math.random() * 1000)); - assertEquals(deserialize({ _: "bigint", value: String(bi) }), bi); - } -}); -Deno.test("errors on bytes objects with no value field", () => { - assertThrows(() => deserialize({ _: "bytes" })); -}); - -Deno.test("errors on bytes objects with non-string value field", () => { - assertThrows(() => deserialize({ _: "bytes", value: 1 })); -}); -Deno.test("errors on bytes objects with empty value field", () => { - assertThrows(() => deserialize({ _: "bytes", value: "" })); -}); - -Deno.test("properly deserializes bytes objects", () => { - const enc = new TextEncoder(); - for (let i = 0; i < 1000; ++i) { - const bytes = Math.ceil(Math.random() * 1000) + ""; - assertEquals( - deserialize({ _: "bytes", value: btoa(bytes) }), - enc.encode(bytes), - ); - } -}); - -Deno.test("properly deserializes functions", () => { - for (let i = 0; i < 1000; ++i) { - const pingId = BigInt(1 + Math.ceil(Math.random() * 1000)); - assertEquals( - deserialize({ _: "ping", ping_id: { _: "bigint", value: pingId + "" } }), - new functions.ping({ ping_id: pingId }), - ); - } - - const actual = { - _: "messages.sendMedia", - random_id: { - _: "bigint", - value: "123", - }, - peer: { - _: "inputPeerSelf", - }, - media: { - _: "inputMediaPhoto", - id: { - _: "inputPhoto", - id: { _: "bigint", value: "123" }, - access_hash: { _: "bigint", value: "123" }, - file_reference: { _: "bytes", value: btoa("R") }, - }, - spoiler: true, - }, - message: "Test", - silent: false, - }; - const expected = new functions.messages.sendMedia({ - random_id: 123n, - peer: new types.InputPeerSelf(), - media: new types.InputMediaPhoto({ - id: new types.InputPhoto({ - id: 123n, - access_hash: 123n, - file_reference: new Uint8Array([0x52]), - }), - spoiler: true, - }), - message: "Test", - silent: undefined, - }); - assertEquals(deserialize(actual), expected); -}); diff --git a/tests/tl_json/serialize_test.ts b/tests/tl_json/serialize_test.ts deleted file mode 100644 index 20bc064..0000000 --- a/tests/tl_json/serialize_test.ts +++ /dev/null @@ -1,114 +0,0 @@ -/** - * 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 { encodeBase64 } from "std/encoding/base64.ts"; - -import { functions, types } from "mtkruto/mod.ts"; - -import { serialize } from "../../tl_json.ts"; - -Deno.test("string", () => { - assertEquals(serialize("string"), "string"); -}); - -Deno.test("number", () => { - assertEquals(serialize("number"), "number"); -}); - -Deno.test("bigint", () => { - assertEquals(serialize(123n), { _: "bigint", value: "123" }); -}); - -Deno.test("boolean", () => { - assertEquals(serialize(true), true); - assertEquals(serialize(false), false); -}); - -Deno.test("undefined", () => { - assertEquals(serialize(undefined), false); -}); - -Deno.test("bytes", () => { - const enc = new TextEncoder(); - const bytes = enc.encode("Hello, world!"); - assertEquals(serialize(bytes), { _: "bytes", value: encodeBase64(bytes) }); -}); - -Deno.test("object", () => { - const actual = new functions.messages.sendMedia({ - random_id: 123n, - peer: new types.InputPeerSelf(), - media: new types.InputMediaPhoto({ - id: new types.InputPhoto({ - id: 123n, - access_hash: 123n, - file_reference: new Uint8Array([0x52]), - }), - spoiler: true, - }), - message: "Test", - silent: undefined, - }); - const expected = { - _: "messages.sendMedia", - background: false, - clear_draft: false, - entities: false, - invert_media: false, - media: { - _: "inputMediaPhoto", - id: { - _: "inputPhoto", - access_hash: { - _: "bigint", - value: "123", - }, - file_reference: { - _: "bytes", - value: "Ug==", - }, - id: { - _: "bigint", - value: "123", - }, - }, - spoiler: true, - ttl_seconds: false, - }, - message: "Test", - noforwards: false, - peer: { - _: "inputPeerSelf", - }, - quick_reply_shortcut: false, - random_id: { - _: "bigint", - value: "123", - }, - reply_markup: false, - reply_to: false, - schedule_date: false, - send_as: false, - silent: false, - update_stickersets_order: false, - }; - assertEquals(serialize(actual), expected); -}); diff --git a/tests/transform_test.ts b/tests/transform_test.ts index aa7bb66..b07812e 100644 --- a/tests/transform_test.ts +++ b/tests/transform_test.ts @@ -24,6 +24,12 @@ import { transform } from "../transform.ts"; Deno.test("transform", () => { const date = new Date(); - const a = { _: { _: { _: date, a: [date, date] } } }; + const bigint = 123123n; + const buffer = crypto.getRandomValues(new Uint8Array(1024)); + const a = { + _: { + _: { _: date, a: [buffer, date, date], bigint, c: bigint, x: buffer }, + }, + }; assertEquals(transform(transform(a)), a); }); diff --git a/tl_json.ts b/tl_json.ts deleted file mode 100644 index d86a7ac..0000000 --- a/tl_json.ts +++ /dev/null @@ -1,143 +0,0 @@ -/** - * 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 { decodeBase64, encodeBase64 } from "std/encoding/base64.ts"; - -import { functions, InputError, name, types } from "mtkruto/mod.ts"; - -function collectObjects(types: any, map: Record) { - for (const type of Object.values(types)) { - const name_: string | undefined = (type as any)[name]; - if (name_) { - map[name_] = type; - } - if ((type as any).constructor == Object) { - collectObjects(type, map); - } - } -} - -export const typeNameMap = {} as Record; -export const functionNameMap = {} as Record; -collectObjects(types, typeNameMap); -collectObjects(functions, functionNameMap); - -export function deserialize(value: any): any { - if (typeof value === "string") { - return value; - } else if (typeof value === "number") { - return value; - } else if (typeof value === "boolean") { - return value ? true : undefined; - } else if (typeof value === "object") { - if (!("_" in value)) { - throw new InputError("Expected object to contain the field _"); - } - if (typeof value._ !== "string") { - throw new InputError("Expected the _ field to be string"); - } - if (value._ == "bigint") { - if (!("value" in value)) { - throw new InputError( - "Expected BigInt object to include the field value", - ); - } - if (typeof value.value !== "string") { - throw new InputError("Expected BigInt value to be string"); - } - const v = value.value.trim(); - if (!v.trim()) { - throw new InputError("Expected BigInt value to be non-empty"); - } - try { - return BigInt(v); - } catch { - throw new InputError(`Invalid BigInt: ${v}`); - } - } - if (value._ == "bytes") { - if (!("value" in value)) { - throw new InputError( - "Expected bytes object to include the field value", - ); - } - if (typeof value.value !== "string") { - throw new InputError("Expected bytes value to be string"); - } - const v = value.value.trim(); - if (!v.trim()) { - throw new InputError("Expected bytes value to be non-empty"); - } - try { - return decodeBase64(v); - } catch { - throw new InputError("Expected bytes value to be vaild Base64"); - } - } - - if (!(value._ in typeNameMap) && !(value._ in functionNameMap)) { - throw new InputError(`Invalid type: ${value._}`); - } - - // @ts-ignore: no idea why it errors, but trust me it works - const params = Object.entries(value) - .filter(([k]) => k != "_") - .map(([k, v]) => [k, deserialize(v)]); - // @ts-ignore: too complex to represent, I feel you - const constructor = value._ in typeNameMap - ? typeNameMap[value._] - : functionNameMap[value._]; - return new constructor(Object.fromEntries(params)); - } else { - throw new InputError(`Unexpected value: ${typeof value}`); - } -} - -export function serialize(value: any): any { - if (typeof value === "string") { - return value; - } else if (typeof value === "number") { - return value; - } else if (typeof value === "boolean") { - return value; - } else if (value === undefined) { - return false; - } else if (typeof value === "bigint") { - return { - _: "bigint", - value: value + "", - }; - } else if (value instanceof Uint8Array) { - return { - _: "bytes", - value: encodeBase64(value), - }; - } - - // @ts-ignore: please - const params = Object.entries(value) - .filter(([k]) => k !== "__R") - .map(([k, v]) => [k, serialize(v)]); - - return { - _: value.constructor[name], - ...Object.fromEntries(params), - }; -} diff --git a/transform.ts b/transform.ts index 2ffd3fc..e2b95de 100644 --- a/transform.ts +++ b/transform.ts @@ -18,6 +18,8 @@ * along with this program. If not, see . */ +import { decodeBase64, encodeBase64 } from "std/encoding/base64.ts"; + /** * Utility function to transform dates in objects into a JSON-(de)serializable format and vice-verca. */ @@ -27,14 +29,28 @@ export function transform(a: any) { if (a[key] != null && typeof a[key] === "object") { if (a[key] instanceof Date) { a[key] = { _: "date", value: a[key].toJSON() }; + } else if (a[key] instanceof Uint8Array) { + a[key] = { _: "bytes", value: encodeBase64(a[key]) }; } else if ( - "_" in a[key] && a[key] == "date" && "value" in a[key] && + "_" in a[key] && a[key]._ == "date" && "value" in a[key] && typeof a[key].value === "string" ) { a[key] = new Date(a[key].value); + } else if ( + "_" in a[key] && a[key]._ == "bigint" && "value" in a[key] && + typeof a[key].value === "string" + ) { + a[key] = BigInt(a[key].value); + } else if ( + "_" in a[key] && a[key]._ == "bytes" && "value" in a[key] && + typeof a[key].value === "string" + ) { + a[key] = decodeBase64(a[key].value); } else { transform(a[key]); } + } else if (typeof a[key] === "bigint") { + a[key] = { _: "bigint", value: String(a[key]) }; } } } diff --git a/worker.ts b/worker.ts index 928899b..327bbfc 100644 --- a/worker.ts +++ b/worker.ts @@ -26,11 +26,10 @@ 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 } from "mtkruto/mod.ts"; +import { errors, isValidType, setLoggingProvider } from "mtkruto/mod.ts"; import { transform } from "./transform.ts"; import { fileLogger } from "./file_logger.ts"; -import { deserialize, serialize } from "./tl_json.ts"; import { isFunctionDisallowed } from "./disallowed_functions.ts"; import { ClientManager, ClientStats } from "./client_manager.ts"; import { ALLOWED_METHODS, AllowedMethod } from "./allowed_methods.ts"; @@ -60,9 +59,9 @@ addEventListener("message", async (e) => { status: 400, headers: { "x-error-type": "input" }, }]; - } else if (err instanceof types.Rpc_error) { - result = [err.error_message, { - status: err.error_code, + } else if (err instanceof errors.TelegramError) { + result = [err.errorMessage, { + status: err.errorCode, headers: { "x-error-type": "rpc" }, }]; } else { @@ -207,15 +206,16 @@ async function invoke( id: string, function_: any, ): Promise> { - const function__ = deserialize(function_); - if (!(function__ instanceof functions.Function)) { - throw new InputError("Expected a function"); + function_ = transform(function_); + console.log(function_); + if (!isValidType(function_)) { + throw new InputError("Invalid function"); } - if (isFunctionDisallowed(function__)) { + if (isFunctionDisallowed(function_)) { throw new InputError("Unallowed function"); } const client = await clientManager.getClient(id); - const result = serialize(await client.invoke(function__)); + const result = transform(await client.invoke(function_)); if (result !== undefined) { return [result]; } else {