@mtcute/bun 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.d.ts +2 -9
- package/client.js +90 -0
- package/index.d.ts +1 -0
- package/index.js +14 -527
- package/{chunks/es/BmtxbrWZ.js → methods/download-file.js} +1 -6
- package/methods/download-node-stream.d.ts +1 -1
- package/methods/download-node-stream.js +8 -0
- package/methods.js +2 -1
- package/package.json +16 -12
- package/platform.d.ts +7 -2
- package/platform.js +22 -0
- package/sqlite/driver.js +19 -0
- package/sqlite/index.d.ts +1 -1
- package/sqlite/index.js +13 -0
- package/utils/crypto.js +77 -0
- package/utils/exit-hook.js +41 -0
- package/utils/logging.js +28 -0
- package/utils/normalize-file.js +37 -0
- package/utils/proxies.d.ts +27 -0
- package/utils/proxies.js +62 -0
- package/utils/tcp.d.ts +6 -30
- package/utils/tcp.js +13 -0
- package/worker.d.ts +5 -3
- package/worker.js +44 -0
- package/common-internals-node/platform.d.ts +0 -18
- package/index.d.cts +0 -9
- package/methods.d.cts +0 -3
- package/utils.d.cts +0 -1
- /package/{common-internals-node → utils}/exit-hook.d.ts +0 -0
- /package/{common-internals-node → utils}/logging.d.ts +0 -0
package/client.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Readable } from 'node:stream';
|
|
2
1
|
import { FileDownloadLocation, FileDownloadParameters, ITelegramStorageProvider, PartialOnly, User } from '@mtcute/core';
|
|
3
2
|
import { BaseTelegramClientOptions as BaseTelegramClientOptionsBase, TelegramClientOptions, BaseTelegramClient as BaseTelegramClientBase, TelegramClient as TelegramClientBase } from '@mtcute/core/client.js';
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
4
4
|
export type { TelegramClientOptions };
|
|
5
|
-
export interface BaseTelegramClientOptions extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto'> {
|
|
5
|
+
export interface BaseTelegramClientOptions extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto' | 'platform'> {
|
|
6
6
|
/**
|
|
7
7
|
* Storage to use for this client.
|
|
8
8
|
*
|
|
@@ -12,13 +12,6 @@ export interface BaseTelegramClientOptions extends PartialOnly<Omit<BaseTelegram
|
|
|
12
12
|
* @default `"client.session"`
|
|
13
13
|
*/
|
|
14
14
|
storage?: string | ITelegramStorageProvider;
|
|
15
|
-
/**
|
|
16
|
-
* **ADVANCED USE ONLY**
|
|
17
|
-
*
|
|
18
|
-
* Whether to not set up the platform.
|
|
19
|
-
* This is useful if you call `setPlatform` yourself.
|
|
20
|
-
*/
|
|
21
|
-
platformless?: boolean;
|
|
22
15
|
}
|
|
23
16
|
export declare class BaseTelegramClient extends BaseTelegramClientBase {
|
|
24
17
|
constructor(opts: BaseTelegramClientOptions);
|
package/client.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { unknownToError } from "@fuman/utils";
|
|
3
|
+
import { BaseTelegramClient as BaseTelegramClient$1, TelegramClient as TelegramClient$1 } from "@mtcute/core/client.js";
|
|
4
|
+
import { downloadToFile } from "./methods/download-file.js";
|
|
5
|
+
import { downloadAsNodeStream } from "./methods/download-node-stream.js";
|
|
6
|
+
import { BunPlatform } from "./platform.js";
|
|
7
|
+
import { SqliteStorage } from "./sqlite/index.js";
|
|
8
|
+
import { BunCryptoProvider } from "./utils/crypto.js";
|
|
9
|
+
import { TcpTransport } from "./utils/tcp.js";
|
|
10
|
+
class BaseTelegramClient extends BaseTelegramClient$1 {
|
|
11
|
+
constructor(opts) {
|
|
12
|
+
super({
|
|
13
|
+
crypto: new BunCryptoProvider(),
|
|
14
|
+
transport: new TcpTransport(),
|
|
15
|
+
platform: new BunPlatform(),
|
|
16
|
+
...opts,
|
|
17
|
+
storage: typeof opts.storage === "string" ? new SqliteStorage(opts.storage) : opts.storage ?? new SqliteStorage("client.session")
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
class TelegramClient extends TelegramClient$1 {
|
|
22
|
+
constructor(opts) {
|
|
23
|
+
if ("client" in opts) {
|
|
24
|
+
super(opts);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
super({
|
|
28
|
+
client: new BaseTelegramClient(opts),
|
|
29
|
+
disableUpdates: opts.disableUpdates,
|
|
30
|
+
skipConversationUpdates: opts.skipConversationUpdates,
|
|
31
|
+
updates: opts.updates
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
_rl;
|
|
35
|
+
/**
|
|
36
|
+
* Tiny wrapper over Node `readline` package
|
|
37
|
+
* for simpler user input for `.start()` method.
|
|
38
|
+
*
|
|
39
|
+
* Associated `readline` interface is closed
|
|
40
|
+
* after `start()` returns, or with the client.
|
|
41
|
+
*
|
|
42
|
+
* @param text Text of the question
|
|
43
|
+
*/
|
|
44
|
+
input(text) {
|
|
45
|
+
if (!this._rl) {
|
|
46
|
+
this._rl = createInterface({
|
|
47
|
+
input: process.stdin,
|
|
48
|
+
output: process.stdout
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return new Promise((res) => this._rl?.question(text, res));
|
|
52
|
+
}
|
|
53
|
+
close() {
|
|
54
|
+
this._rl?.close();
|
|
55
|
+
return super.close();
|
|
56
|
+
}
|
|
57
|
+
start(params = {}) {
|
|
58
|
+
if (!params.botToken) {
|
|
59
|
+
if (!params.phone) params.phone = () => this.input("phone > ");
|
|
60
|
+
if (!params.code) params.code = () => this.input("code > ");
|
|
61
|
+
if (!params.password) {
|
|
62
|
+
params.password = () => this.input("2fa password > ");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return super.start(params).then((user) => {
|
|
66
|
+
if (this._rl) {
|
|
67
|
+
this._rl.close();
|
|
68
|
+
delete this._rl;
|
|
69
|
+
}
|
|
70
|
+
return user;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
run(params, then) {
|
|
74
|
+
if (typeof params === "function") {
|
|
75
|
+
then = params;
|
|
76
|
+
params = {};
|
|
77
|
+
}
|
|
78
|
+
this.start(params).then(then).catch((err) => this.onError.emit(unknownToError(err)));
|
|
79
|
+
}
|
|
80
|
+
downloadToFile(filename, location, params) {
|
|
81
|
+
return downloadToFile(this, filename, location, params);
|
|
82
|
+
}
|
|
83
|
+
downloadAsNodeStream(location, params) {
|
|
84
|
+
return downloadAsNodeStream(this, location, params);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
BaseTelegramClient,
|
|
89
|
+
TelegramClient
|
|
90
|
+
};
|
package/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './client.js';
|
|
|
2
2
|
export * from './platform.js';
|
|
3
3
|
export * from './sqlite/index.js';
|
|
4
4
|
export * from './utils/crypto.js';
|
|
5
|
+
export * from './utils/proxies.js';
|
|
5
6
|
export * from './utils/tcp.js';
|
|
6
7
|
export * from './worker.js';
|
|
7
8
|
export * from '@mtcute/core';
|
package/index.js
CHANGED
|
@@ -1,537 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { basename } from "node:path";
|
|
9
|
-
import { Readable } from "node:stream";
|
|
10
|
-
import { BaseSqliteStorageDriver, BaseSqliteStorage, TransportState, MtcuteError, IntermediatePacketCodec } from "@mtcute/core";
|
|
1
|
+
import { BaseTelegramClient, TelegramClient } from "./client.js";
|
|
2
|
+
import { BunPlatform } from "./platform.js";
|
|
3
|
+
import { SqliteStorage } from "./sqlite/index.js";
|
|
4
|
+
import { BunCryptoProvider } from "./utils/crypto.js";
|
|
5
|
+
import { HttpProxyTcpTransport, MtProxyTcpTransport, SocksProxyTcpTransport } from "./utils/proxies.js";
|
|
6
|
+
import { TcpTransport } from "./utils/tcp.js";
|
|
7
|
+
import { TelegramWorker, TelegramWorkerPort } from "./worker.js";
|
|
11
8
|
export * from "@mtcute/core";
|
|
12
|
-
import { Database } from "bun:sqlite";
|
|
13
|
-
import { deflateSync, gunzipSync } from "node:zlib";
|
|
14
|
-
import { pbkdf2 } from "node:crypto";
|
|
15
|
-
import { BaseCryptoProvider } from "@mtcute/core/utils.js";
|
|
16
|
-
import { initSync, ige256Encrypt, ige256Decrypt, createCtr256, ctr256, freeCtr256 } from "@mtcute/wasm";
|
|
17
|
-
import EventEmitter from "events";
|
|
18
|
-
import { parentPort, Worker } from "node:worker_threads";
|
|
19
|
-
import { TelegramWorker as TelegramWorker$1, TelegramWorkerPort as TelegramWorkerPort$1 } from "@mtcute/core/worker.js";
|
|
20
9
|
export * from "@mtcute/html-parser";
|
|
21
10
|
export * from "@mtcute/markdown-parser";
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return Readable.toWeb(stream);
|
|
25
|
-
}
|
|
26
|
-
stream.pause();
|
|
27
|
-
return new ReadableStream({
|
|
28
|
-
start(c) {
|
|
29
|
-
stream.on("data", (chunk) => {
|
|
30
|
-
c.enqueue(chunk);
|
|
31
|
-
});
|
|
32
|
-
stream.on("end", () => {
|
|
33
|
-
c.close();
|
|
34
|
-
});
|
|
35
|
-
stream.on("error", (err) => {
|
|
36
|
-
c.error(err);
|
|
37
|
-
});
|
|
38
|
-
},
|
|
39
|
-
pull() {
|
|
40
|
-
stream.resume();
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
async function normalizeFile$1(file) {
|
|
45
|
-
if (typeof file === "string") {
|
|
46
|
-
file = createReadStream(file);
|
|
47
|
-
}
|
|
48
|
-
if (file instanceof ReadStream) {
|
|
49
|
-
const fileName = basename(file.path.toString());
|
|
50
|
-
const fileSize = await stat(file.path.toString()).then((stat2) => stat2.size);
|
|
51
|
-
return {
|
|
52
|
-
file: nodeStreamToWeb(file),
|
|
53
|
-
fileName,
|
|
54
|
-
fileSize
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
if (file instanceof Readable) {
|
|
58
|
-
return {
|
|
59
|
-
file: nodeStreamToWeb(file)
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
let installed = false;
|
|
65
|
-
let handled = false;
|
|
66
|
-
const callbacks = /* @__PURE__ */ new Set();
|
|
67
|
-
const myHandlers = /* @__PURE__ */ new Map();
|
|
68
|
-
function register(shouldManuallyExit, signal, event) {
|
|
69
|
-
function eventHandler() {
|
|
70
|
-
if (handled) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
handled = true;
|
|
74
|
-
for (const callback of callbacks) {
|
|
75
|
-
callback();
|
|
76
|
-
}
|
|
77
|
-
for (const [event2, handler] of myHandlers) {
|
|
78
|
-
process.off(event2, handler);
|
|
79
|
-
}
|
|
80
|
-
if (shouldManuallyExit) {
|
|
81
|
-
process.kill(process.pid, signal);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
process.on(event, eventHandler);
|
|
85
|
-
myHandlers.set(event, eventHandler);
|
|
86
|
-
}
|
|
87
|
-
function beforeExit(fn) {
|
|
88
|
-
if (typeof process === "undefined") return () => {
|
|
89
|
-
};
|
|
90
|
-
if (!installed) {
|
|
91
|
-
installed = true;
|
|
92
|
-
register(true, 0, "beforeExit");
|
|
93
|
-
register(true, 2, "SIGINT");
|
|
94
|
-
register(true, 15, "SIGTERM");
|
|
95
|
-
register(false, 15, "exit");
|
|
96
|
-
}
|
|
97
|
-
callbacks.add(fn);
|
|
98
|
-
return () => {
|
|
99
|
-
callbacks.delete(fn);
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
const isTty = typeof process === "object" && Boolean(process.stdout?.isTTY);
|
|
103
|
-
const BASE_FORMAT = isTty ? "%s [%s] [%s%s\x1B[0m] " : "%s [%s] [%s] ";
|
|
104
|
-
const LEVEL_NAMES = isTty ? [
|
|
105
|
-
"",
|
|
106
|
-
// OFF
|
|
107
|
-
"\x1B[31mERR\x1B[0m",
|
|
108
|
-
"\x1B[33mWRN\x1B[0m",
|
|
109
|
-
"\x1B[34mINF\x1B[0m",
|
|
110
|
-
"\x1B[36mDBG\x1B[0m",
|
|
111
|
-
"\x1B[35mVRB\x1B[0m"
|
|
112
|
-
] : [
|
|
113
|
-
"",
|
|
114
|
-
// OFF
|
|
115
|
-
"ERR",
|
|
116
|
-
"WRN",
|
|
117
|
-
"INF",
|
|
118
|
-
"DBG",
|
|
119
|
-
"VRB"
|
|
120
|
-
];
|
|
121
|
-
const TAG_COLORS = [6, 2, 3, 4, 5, 1].map((i) => `\x1B[3${i};1m`);
|
|
122
|
-
const defaultLoggingHandler = isTty ? (color, level, tag, fmt, args) => {
|
|
123
|
-
console.log(BASE_FORMAT + fmt, (/* @__PURE__ */ new Date()).toISOString(), LEVEL_NAMES[level], TAG_COLORS[color], tag, ...args);
|
|
124
|
-
} : (color, level, tag, fmt, args) => {
|
|
125
|
-
console.log(BASE_FORMAT + fmt, (/* @__PURE__ */ new Date()).toISOString(), LEVEL_NAMES[level], tag, ...args);
|
|
126
|
-
};
|
|
127
|
-
const BUFFER_BASE64_URL_AVAILABLE = typeof Buffer.isEncoding === "function" && Buffer.isEncoding("base64url");
|
|
128
|
-
const toBuffer = (buf) => Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
129
|
-
class NodePlatform {
|
|
130
|
-
getDeviceModel() {
|
|
131
|
-
return `Node.js/${process.version} (${os.type()} ${os.arch()})`;
|
|
132
|
-
}
|
|
133
|
-
getDefaultLogLevel() {
|
|
134
|
-
const envLogLevel = Number.parseInt(process.env.MTCUTE_LOG_LEVEL ?? "");
|
|
135
|
-
if (!Number.isNaN(envLogLevel)) {
|
|
136
|
-
return envLogLevel;
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
// ITlPlatform
|
|
141
|
-
utf8ByteLength(str) {
|
|
142
|
-
return Buffer.byteLength(str, "utf8");
|
|
143
|
-
}
|
|
144
|
-
utf8Encode(str) {
|
|
145
|
-
return Buffer.from(str, "utf8");
|
|
146
|
-
}
|
|
147
|
-
utf8Decode(buf) {
|
|
148
|
-
return toBuffer(buf).toString("utf8");
|
|
149
|
-
}
|
|
150
|
-
hexEncode(buf) {
|
|
151
|
-
return toBuffer(buf).toString("hex");
|
|
152
|
-
}
|
|
153
|
-
hexDecode(str) {
|
|
154
|
-
return Buffer.from(str, "hex");
|
|
155
|
-
}
|
|
156
|
-
base64Encode(buf, url = false) {
|
|
157
|
-
const nodeBuffer = toBuffer(buf);
|
|
158
|
-
if (url && BUFFER_BASE64_URL_AVAILABLE) return nodeBuffer.toString("base64url");
|
|
159
|
-
const str = nodeBuffer.toString("base64");
|
|
160
|
-
if (url) return str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
161
|
-
return str;
|
|
162
|
-
}
|
|
163
|
-
base64Decode(string, url = false) {
|
|
164
|
-
if (url && BUFFER_BASE64_URL_AVAILABLE) {
|
|
165
|
-
return Buffer.from(string, "base64url");
|
|
166
|
-
}
|
|
167
|
-
if (url) {
|
|
168
|
-
string = string.replace(/-/g, "+").replace(/_/g, "/");
|
|
169
|
-
while (string.length % 4) string += "=";
|
|
170
|
-
}
|
|
171
|
-
return Buffer.from(string, "base64");
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
NodePlatform.prototype.log = defaultLoggingHandler;
|
|
175
|
-
NodePlatform.prototype.beforeExit = beforeExit;
|
|
176
|
-
NodePlatform.prototype.normalizeFile = normalizeFile$1;
|
|
177
|
-
function isBunFile(file) {
|
|
178
|
-
return file instanceof Blob && "name" in file && file.name.length > 0;
|
|
179
|
-
}
|
|
180
|
-
async function normalizeFile(file) {
|
|
181
|
-
if (typeof file === "string") {
|
|
182
|
-
file = Bun.file(file);
|
|
183
|
-
}
|
|
184
|
-
if (isBunFile(file)) {
|
|
185
|
-
return {
|
|
186
|
-
file,
|
|
187
|
-
fileName: file.name,
|
|
188
|
-
fileSize: file.size
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
if (file instanceof ReadStream) {
|
|
192
|
-
const fileName = basename(file.path.toString());
|
|
193
|
-
const fileSize = await stat(file.path.toString()).then((stat2) => stat2.size);
|
|
194
|
-
return {
|
|
195
|
-
file: Readable.toWeb(file),
|
|
196
|
-
fileName,
|
|
197
|
-
fileSize
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
if (file instanceof Readable) {
|
|
201
|
-
return {
|
|
202
|
-
file: Readable.toWeb(file)
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
class BunPlatform extends NodePlatform {
|
|
208
|
-
getDeviceModel() {
|
|
209
|
-
return `Bun/${Bun.version} (${os.type()} ${os.arch()})`;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
BunPlatform.prototype.normalizeFile = normalizeFile;
|
|
213
|
-
class SqliteStorageDriver extends BaseSqliteStorageDriver {
|
|
214
|
-
constructor(filename = ":memory:", params) {
|
|
215
|
-
super();
|
|
216
|
-
this.filename = filename;
|
|
217
|
-
this.params = params;
|
|
218
|
-
}
|
|
219
|
-
_createDatabase() {
|
|
220
|
-
const db = new Database(this.filename);
|
|
221
|
-
if (!this.params?.disableWal) {
|
|
222
|
-
db.exec("PRAGMA journal_mode = WAL;");
|
|
223
|
-
}
|
|
224
|
-
return db;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
class SqliteStorage extends BaseSqliteStorage {
|
|
228
|
-
constructor(filename = ":memory:", params) {
|
|
229
|
-
super(new SqliteStorageDriver(filename, params));
|
|
230
|
-
this.filename = filename;
|
|
231
|
-
this.params = params;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
class BunCryptoProvider extends BaseCryptoProvider {
|
|
235
|
-
async initialize() {
|
|
236
|
-
const wasmFile = require.resolve("@mtcute/wasm/mtcute.wasm");
|
|
237
|
-
const wasm = await readFile(wasmFile);
|
|
238
|
-
initSync(wasm);
|
|
239
|
-
}
|
|
240
|
-
createAesIge(key, iv) {
|
|
241
|
-
return {
|
|
242
|
-
encrypt(data) {
|
|
243
|
-
return ige256Encrypt(data, key, iv);
|
|
244
|
-
},
|
|
245
|
-
decrypt(data) {
|
|
246
|
-
return ige256Decrypt(data, key, iv);
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
createAesCtr(key, iv) {
|
|
251
|
-
const ctx = createCtr256(key, iv);
|
|
252
|
-
return {
|
|
253
|
-
process: (data) => ctr256(ctx, data),
|
|
254
|
-
close: () => freeCtr256(ctx)
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
pbkdf2(password, salt, iterations, keylen = 64, algo = "sha512") {
|
|
258
|
-
return new Promise(
|
|
259
|
-
(resolve, reject) => pbkdf2(password, salt, iterations, keylen, algo, (err, buf) => err !== null ? reject(err) : resolve(buf))
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
sha1(data) {
|
|
263
|
-
const res = new Uint8Array(Bun.SHA1.byteLength);
|
|
264
|
-
Bun.SHA1.hash(data, res);
|
|
265
|
-
return res;
|
|
266
|
-
}
|
|
267
|
-
sha256(data) {
|
|
268
|
-
const res = new Uint8Array(Bun.SHA256.byteLength);
|
|
269
|
-
Bun.SHA256.hash(data, res);
|
|
270
|
-
return res;
|
|
271
|
-
}
|
|
272
|
-
async hmacSha256(data, key) {
|
|
273
|
-
const keyMaterial = await crypto.subtle.importKey(
|
|
274
|
-
"raw",
|
|
275
|
-
key,
|
|
276
|
-
{ name: "HMAC", hash: { name: "SHA-256" } },
|
|
277
|
-
false,
|
|
278
|
-
["sign"]
|
|
279
|
-
);
|
|
280
|
-
const res = await crypto.subtle.sign({ name: "HMAC" }, keyMaterial, data);
|
|
281
|
-
return new Uint8Array(res);
|
|
282
|
-
}
|
|
283
|
-
gzip(data, maxSize) {
|
|
284
|
-
try {
|
|
285
|
-
return deflateSync(data, {
|
|
286
|
-
maxOutputLength: maxSize
|
|
287
|
-
});
|
|
288
|
-
} catch (e) {
|
|
289
|
-
if (e.code === "ERR_BUFFER_TOO_LARGE") {
|
|
290
|
-
return null;
|
|
291
|
-
}
|
|
292
|
-
throw e;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
gunzip(data) {
|
|
296
|
-
return gunzipSync(data);
|
|
297
|
-
}
|
|
298
|
-
randomFill(buf) {
|
|
299
|
-
crypto.getRandomValues(buf);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
class BaseTcpTransport extends EventEmitter {
|
|
303
|
-
_currentDc = null;
|
|
304
|
-
_state = TransportState.Idle;
|
|
305
|
-
_socket = null;
|
|
306
|
-
_crypto;
|
|
307
|
-
log;
|
|
308
|
-
packetCodecInitialized = false;
|
|
309
|
-
_updateLogPrefix() {
|
|
310
|
-
if (this._currentDc) {
|
|
311
|
-
this.log.prefix = `[TCP:${this._currentDc.ipAddress}:${this._currentDc.port}] `;
|
|
312
|
-
} else {
|
|
313
|
-
this.log.prefix = "[TCP:disconnected] ";
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
setup(crypto2, log) {
|
|
317
|
-
this._crypto = crypto2;
|
|
318
|
-
this.log = log.create("tcp");
|
|
319
|
-
this._updateLogPrefix();
|
|
320
|
-
}
|
|
321
|
-
state() {
|
|
322
|
-
return this._state;
|
|
323
|
-
}
|
|
324
|
-
currentDc() {
|
|
325
|
-
return this._currentDc;
|
|
326
|
-
}
|
|
327
|
-
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
328
|
-
connect(dc, testMode) {
|
|
329
|
-
if (this._state !== TransportState.Idle) {
|
|
330
|
-
throw new MtcuteError("Transport is not IDLE");
|
|
331
|
-
}
|
|
332
|
-
if (!this.packetCodecInitialized) {
|
|
333
|
-
this._packetCodec.setup?.(this._crypto, this.log);
|
|
334
|
-
this._packetCodec.on("error", (err) => this.emit("error", err));
|
|
335
|
-
this._packetCodec.on("packet", (buf) => this.emit("message", buf));
|
|
336
|
-
this.packetCodecInitialized = true;
|
|
337
|
-
}
|
|
338
|
-
this._state = TransportState.Connecting;
|
|
339
|
-
this._currentDc = dc;
|
|
340
|
-
this._updateLogPrefix();
|
|
341
|
-
this.log.debug("connecting to %j", dc);
|
|
342
|
-
Bun.connect({
|
|
343
|
-
hostname: dc.ipAddress,
|
|
344
|
-
port: dc.port,
|
|
345
|
-
socket: {
|
|
346
|
-
open: this.handleConnect.bind(this),
|
|
347
|
-
error: this.handleError.bind(this),
|
|
348
|
-
data: (socket, data) => this._packetCodec.feed(data),
|
|
349
|
-
close: this.close.bind(this),
|
|
350
|
-
drain: this.handleDrained.bind(this)
|
|
351
|
-
}
|
|
352
|
-
}).catch((err) => {
|
|
353
|
-
this.handleError(null, err);
|
|
354
|
-
this.close();
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
close() {
|
|
358
|
-
if (this._state === TransportState.Idle) return;
|
|
359
|
-
this.log.info("connection closed");
|
|
360
|
-
this._state = TransportState.Idle;
|
|
361
|
-
this._socket?.end();
|
|
362
|
-
this._socket = null;
|
|
363
|
-
this._currentDc = null;
|
|
364
|
-
this._packetCodec.reset();
|
|
365
|
-
this._sendOnceDrained = [];
|
|
366
|
-
this.emit("close");
|
|
367
|
-
}
|
|
368
|
-
handleError(socket, error) {
|
|
369
|
-
this.log.error("error: %s", error.stack);
|
|
370
|
-
if (this.listenerCount("error") > 0) {
|
|
371
|
-
this.emit("error", error);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
handleConnect(socket) {
|
|
375
|
-
this._socket = socket;
|
|
376
|
-
this.log.info("connected");
|
|
377
|
-
Promise.resolve(this._packetCodec.tag()).then((initialMessage) => {
|
|
378
|
-
if (initialMessage.length) {
|
|
379
|
-
this._socket.write(initialMessage);
|
|
380
|
-
this._state = TransportState.Ready;
|
|
381
|
-
this.emit("ready");
|
|
382
|
-
} else {
|
|
383
|
-
this._state = TransportState.Ready;
|
|
384
|
-
this.emit("ready");
|
|
385
|
-
}
|
|
386
|
-
}).catch((err) => {
|
|
387
|
-
if (this.listenerCount("error") > 0) {
|
|
388
|
-
this.emit("error", err);
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
async send(bytes) {
|
|
393
|
-
const framed = await this._packetCodec.encode(bytes);
|
|
394
|
-
if (this._state !== TransportState.Ready) {
|
|
395
|
-
throw new MtcuteError("Transport is not READY");
|
|
396
|
-
}
|
|
397
|
-
const written = this._socket.write(framed);
|
|
398
|
-
if (written < framed.length) {
|
|
399
|
-
this._sendOnceDrained.push(framed.subarray(written));
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
_sendOnceDrained = [];
|
|
403
|
-
handleDrained() {
|
|
404
|
-
while (this._sendOnceDrained.length) {
|
|
405
|
-
const data = this._sendOnceDrained.shift();
|
|
406
|
-
const written = this._socket.write(data);
|
|
407
|
-
if (written < data.length) {
|
|
408
|
-
this._sendOnceDrained.unshift(data.subarray(written));
|
|
409
|
-
break;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
class TcpTransport extends BaseTcpTransport {
|
|
415
|
-
_packetCodec = new IntermediatePacketCodec();
|
|
416
|
-
}
|
|
417
|
-
class BaseTelegramClient extends BaseTelegramClient$1 {
|
|
418
|
-
constructor(opts) {
|
|
419
|
-
if (!opts.platformless) setPlatform(new BunPlatform());
|
|
420
|
-
super({
|
|
421
|
-
crypto: new BunCryptoProvider(),
|
|
422
|
-
transport: () => new TcpTransport(),
|
|
423
|
-
...opts,
|
|
424
|
-
storage: typeof opts.storage === "string" ? new SqliteStorage(opts.storage) : opts.storage ?? new SqliteStorage("client.session")
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
class TelegramClient extends TelegramClient$1 {
|
|
429
|
-
constructor(opts) {
|
|
430
|
-
if ("client" in opts) {
|
|
431
|
-
super(opts);
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
super({
|
|
435
|
-
client: new BaseTelegramClient(opts),
|
|
436
|
-
disableUpdates: opts.disableUpdates,
|
|
437
|
-
skipConversationUpdates: opts.skipConversationUpdates,
|
|
438
|
-
updates: opts.updates
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
_rl;
|
|
442
|
-
/**
|
|
443
|
-
* Tiny wrapper over Node `readline` package
|
|
444
|
-
* for simpler user input for `.start()` method.
|
|
445
|
-
*
|
|
446
|
-
* Associated `readline` interface is closed
|
|
447
|
-
* after `start()` returns, or with the client.
|
|
448
|
-
*
|
|
449
|
-
* @param text Text of the question
|
|
450
|
-
*/
|
|
451
|
-
input(text) {
|
|
452
|
-
if (!this._rl) {
|
|
453
|
-
this._rl = createInterface({
|
|
454
|
-
input: process.stdin,
|
|
455
|
-
output: process.stdout
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
return new Promise((res) => this._rl?.question(text, res));
|
|
459
|
-
}
|
|
460
|
-
close() {
|
|
461
|
-
this._rl?.close();
|
|
462
|
-
return super.close();
|
|
463
|
-
}
|
|
464
|
-
start(params = {}) {
|
|
465
|
-
if (!params.botToken) {
|
|
466
|
-
if (!params.phone) params.phone = () => this.input("phone > ");
|
|
467
|
-
if (!params.code) params.code = () => this.input("code > ");
|
|
468
|
-
if (!params.password) {
|
|
469
|
-
params.password = () => this.input("2fa password > ");
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
return super.start(params).then((user) => {
|
|
473
|
-
if (this._rl) {
|
|
474
|
-
this._rl.close();
|
|
475
|
-
delete this._rl;
|
|
476
|
-
}
|
|
477
|
-
return user;
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
run(params, then) {
|
|
481
|
-
if (typeof params === "function") {
|
|
482
|
-
then = params;
|
|
483
|
-
params = {};
|
|
484
|
-
}
|
|
485
|
-
this.start(params).then(then).catch((err) => this.emitError(err));
|
|
486
|
-
}
|
|
487
|
-
downloadToFile(filename, location, params) {
|
|
488
|
-
return downloadToFile(this, filename, location, params);
|
|
489
|
-
}
|
|
490
|
-
downloadAsNodeStream(location, params) {
|
|
491
|
-
return downloadAsNodeStream(this, location, params);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
let _registered = false;
|
|
495
|
-
class TelegramWorker extends TelegramWorker$1 {
|
|
496
|
-
registerWorker(handler) {
|
|
497
|
-
if (!parentPort) {
|
|
498
|
-
throw new Error("TelegramWorker must be created from a worker thread");
|
|
499
|
-
}
|
|
500
|
-
if (_registered) {
|
|
501
|
-
throw new Error("TelegramWorker must be created only once");
|
|
502
|
-
}
|
|
503
|
-
_registered = true;
|
|
504
|
-
const port = parentPort;
|
|
505
|
-
const respond = port.postMessage.bind(port);
|
|
506
|
-
parentPort.on("message", (message) => handler(message, respond));
|
|
507
|
-
return respond;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
class TelegramWorkerPort extends TelegramWorkerPort$1 {
|
|
511
|
-
constructor(options) {
|
|
512
|
-
setPlatform(new BunPlatform());
|
|
513
|
-
super(options);
|
|
514
|
-
this.options = options;
|
|
515
|
-
}
|
|
516
|
-
connectToWorker(worker, handler) {
|
|
517
|
-
if (!(worker instanceof Worker)) {
|
|
518
|
-
throw new TypeError("Only worker_threads are supported");
|
|
519
|
-
}
|
|
520
|
-
const send = worker.postMessage.bind(worker);
|
|
521
|
-
worker.on("message", handler);
|
|
522
|
-
return [
|
|
523
|
-
send,
|
|
524
|
-
() => {
|
|
525
|
-
worker.off("message", handler);
|
|
526
|
-
}
|
|
527
|
-
];
|
|
528
|
-
}
|
|
529
|
-
}
|
|
11
|
+
import { SqliteStorageDriver } from "./sqlite/driver.js";
|
|
12
|
+
import { HttpProxyConnectionError, SocksProxyConnectionError } from "@fuman/net";
|
|
530
13
|
export {
|
|
531
|
-
BaseTcpTransport,
|
|
532
14
|
BaseTelegramClient,
|
|
533
15
|
BunCryptoProvider,
|
|
534
16
|
BunPlatform,
|
|
17
|
+
HttpProxyConnectionError,
|
|
18
|
+
HttpProxyTcpTransport,
|
|
19
|
+
MtProxyTcpTransport,
|
|
20
|
+
SocksProxyConnectionError,
|
|
21
|
+
SocksProxyTcpTransport,
|
|
535
22
|
SqliteStorage,
|
|
536
23
|
SqliteStorageDriver,
|
|
537
24
|
TcpTransport,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { unlinkSync } from "node:fs";
|
|
2
2
|
import { FileLocation } from "@mtcute/core";
|
|
3
|
-
import { downloadAsIterable
|
|
4
|
-
import { Readable } from "node:stream";
|
|
3
|
+
import { downloadAsIterable } from "@mtcute/core/methods.js";
|
|
5
4
|
async function downloadToFile(client, filename, location, params) {
|
|
6
5
|
if (location instanceof FileLocation && ArrayBuffer.isView(location.location)) {
|
|
7
6
|
await Bun.write(filename, location.location);
|
|
@@ -20,10 +19,6 @@ async function downloadToFile(client, filename, location, params) {
|
|
|
20
19
|
}
|
|
21
20
|
await output.end();
|
|
22
21
|
}
|
|
23
|
-
function downloadAsNodeStream(client, location, params) {
|
|
24
|
-
return Readable.fromWeb(downloadAsStream(client, location, params));
|
|
25
|
-
}
|
|
26
22
|
export {
|
|
27
|
-
downloadAsNodeStream,
|
|
28
23
|
downloadToFile
|
|
29
24
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Readable } from 'node:stream';
|
|
2
1
|
import { FileDownloadLocation, FileDownloadParameters, ITelegramClient } from '@mtcute/core';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
3
|
/**
|
|
4
4
|
* Download a remote file as a Node.js Readable stream
|
|
5
5
|
* (discouraged under Bun, since Web Streams are first-class).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
import { downloadAsStream } from "@mtcute/core/methods.js";
|
|
3
|
+
function downloadAsNodeStream(client, location, params) {
|
|
4
|
+
return Readable.fromWeb(downloadAsStream(client, location, params));
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
downloadAsNodeStream
|
|
8
|
+
};
|
package/methods.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { downloadToFile } from "./methods/download-file.js";
|
|
2
|
+
import { downloadAsNodeStream } from "./methods/download-node-stream.js";
|
|
2
3
|
export * from "@mtcute/core/methods.js";
|
|
3
4
|
export {
|
|
4
5
|
downloadAsNodeStream,
|
package/package.json
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mtcute/bun",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.18.0",
|
|
5
5
|
"description": "Meta-package for Bun",
|
|
6
|
-
"author": "alina sireneva <alina@tei.su>",
|
|
7
6
|
"license": "MIT",
|
|
8
|
-
"
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@mtcute/core": "^0.18.0",
|
|
9
|
+
"@mtcute/html-parser": "^0.18.0",
|
|
10
|
+
"@mtcute/markdown-parser": "^0.18.0",
|
|
11
|
+
"@mtcute/wasm": "^0.18.0",
|
|
12
|
+
"@fuman/utils": "0.0.4",
|
|
13
|
+
"@fuman/bun": "0.0.4",
|
|
14
|
+
"@fuman/net": "0.0.4",
|
|
15
|
+
"@fuman/io": "0.0.4"
|
|
16
|
+
},
|
|
9
17
|
"exports": {
|
|
10
18
|
".": {
|
|
11
19
|
"import": {
|
|
@@ -38,16 +46,12 @@
|
|
|
38
46
|
}
|
|
39
47
|
}
|
|
40
48
|
},
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"@mtcute/core": "^0.17.0",
|
|
44
|
-
"@mtcute/html-parser": "^0.17.0",
|
|
45
|
-
"@mtcute/markdown-parser": "^0.17.0",
|
|
46
|
-
"@mtcute/wasm": "^0.17.0"
|
|
47
|
-
},
|
|
49
|
+
"author": "alina sireneva <alina@tei.su>",
|
|
50
|
+
"sideEffects": false,
|
|
48
51
|
"homepage": "https://mtcute.dev",
|
|
49
52
|
"repository": {
|
|
50
53
|
"type": "git",
|
|
51
|
-
"url": "https://github.com/mtcute/mtcute"
|
|
52
|
-
}
|
|
54
|
+
"url": "git+https://github.com/mtcute/mtcute.git"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {}
|
|
53
57
|
}
|
package/platform.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ICorePlatform } from '@mtcute/core';
|
|
2
|
+
import { beforeExit } from './utils/exit-hook.js';
|
|
3
|
+
import { defaultLoggingHandler } from './utils/logging.js';
|
|
2
4
|
import { normalizeFile } from './utils/normalize-file.js';
|
|
3
|
-
export declare class BunPlatform
|
|
5
|
+
export declare class BunPlatform implements ICorePlatform {
|
|
6
|
+
log: typeof defaultLoggingHandler;
|
|
7
|
+
beforeExit: typeof beforeExit;
|
|
4
8
|
normalizeFile: typeof normalizeFile;
|
|
5
9
|
getDeviceModel(): string;
|
|
10
|
+
getDefaultLogLevel(): number | null;
|
|
6
11
|
}
|
package/platform.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
import { beforeExit } from "./utils/exit-hook.js";
|
|
3
|
+
import { defaultLoggingHandler } from "./utils/logging.js";
|
|
4
|
+
import { normalizeFile } from "./utils/normalize-file.js";
|
|
5
|
+
class BunPlatform {
|
|
6
|
+
getDeviceModel() {
|
|
7
|
+
return `Bun/${Bun.version} (${os.type()} ${os.arch()})`;
|
|
8
|
+
}
|
|
9
|
+
getDefaultLogLevel() {
|
|
10
|
+
const envLogLevel = Number.parseInt(process.env.MTCUTE_LOG_LEVEL ?? "");
|
|
11
|
+
if (!Number.isNaN(envLogLevel)) {
|
|
12
|
+
return envLogLevel;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
BunPlatform.prototype.normalizeFile = normalizeFile;
|
|
18
|
+
BunPlatform.prototype.log = defaultLoggingHandler;
|
|
19
|
+
BunPlatform.prototype.beforeExit = beforeExit;
|
|
20
|
+
export {
|
|
21
|
+
BunPlatform
|
|
22
|
+
};
|
package/sqlite/driver.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BaseSqliteStorageDriver } from "@mtcute/core";
|
|
2
|
+
import { Database } from "bun:sqlite";
|
|
3
|
+
class SqliteStorageDriver extends BaseSqliteStorageDriver {
|
|
4
|
+
constructor(filename = ":memory:", params) {
|
|
5
|
+
super();
|
|
6
|
+
this.filename = filename;
|
|
7
|
+
this.params = params;
|
|
8
|
+
}
|
|
9
|
+
_createDatabase() {
|
|
10
|
+
const db = new Database(this.filename);
|
|
11
|
+
if (!this.params?.disableWal) {
|
|
12
|
+
db.exec("PRAGMA journal_mode = WAL;");
|
|
13
|
+
}
|
|
14
|
+
return db;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
SqliteStorageDriver
|
|
19
|
+
};
|
package/sqlite/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseSqliteStorage } from '@mtcute/core';
|
|
2
1
|
import { SqliteStorageDriverOptions } from './driver.js';
|
|
2
|
+
import { BaseSqliteStorage } from '@mtcute/core';
|
|
3
3
|
export { SqliteStorageDriver } from './driver.js';
|
|
4
4
|
export declare class SqliteStorage extends BaseSqliteStorage {
|
|
5
5
|
readonly filename: string;
|
package/sqlite/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseSqliteStorage } from "@mtcute/core";
|
|
2
|
+
import { SqliteStorageDriver } from "./driver.js";
|
|
3
|
+
class SqliteStorage extends BaseSqliteStorage {
|
|
4
|
+
constructor(filename = ":memory:", params) {
|
|
5
|
+
super(new SqliteStorageDriver(filename, params));
|
|
6
|
+
this.filename = filename;
|
|
7
|
+
this.params = params;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
SqliteStorage,
|
|
12
|
+
SqliteStorageDriver
|
|
13
|
+
};
|
package/utils/crypto.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { pbkdf2 } from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { deflateSync, gunzipSync } from "node:zlib";
|
|
4
|
+
import { u8 } from "@fuman/utils";
|
|
5
|
+
import { BaseCryptoProvider } from "@mtcute/core/utils.js";
|
|
6
|
+
import { initSync, ige256Decrypt, ige256Encrypt, createCtr256, freeCtr256, ctr256 } from "@mtcute/wasm";
|
|
7
|
+
class BunCryptoProvider extends BaseCryptoProvider {
|
|
8
|
+
async initialize() {
|
|
9
|
+
const wasmFile = require.resolve("@mtcute/wasm/mtcute.wasm");
|
|
10
|
+
const wasm = await readFile(wasmFile);
|
|
11
|
+
initSync(wasm);
|
|
12
|
+
}
|
|
13
|
+
createAesIge(key, iv) {
|
|
14
|
+
return {
|
|
15
|
+
encrypt(data) {
|
|
16
|
+
return ige256Encrypt(data, key, iv);
|
|
17
|
+
},
|
|
18
|
+
decrypt(data) {
|
|
19
|
+
return ige256Decrypt(data, key, iv);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
createAesCtr(key, iv) {
|
|
24
|
+
const ctx = createCtr256(key, iv);
|
|
25
|
+
return {
|
|
26
|
+
process: (data) => ctr256(ctx, data),
|
|
27
|
+
close: () => freeCtr256(ctx)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
pbkdf2(password, salt, iterations, keylen = 64, algo = "sha512") {
|
|
31
|
+
return new Promise(
|
|
32
|
+
(resolve, reject) => pbkdf2(password, salt, iterations, keylen, algo, (err, buf) => err !== null ? reject(err) : resolve(buf))
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
sha1(data) {
|
|
36
|
+
const res = u8.alloc(Bun.SHA1.byteLength);
|
|
37
|
+
Bun.SHA1.hash(data, res);
|
|
38
|
+
return res;
|
|
39
|
+
}
|
|
40
|
+
sha256(data) {
|
|
41
|
+
const res = u8.alloc(Bun.SHA256.byteLength);
|
|
42
|
+
Bun.SHA256.hash(data, res);
|
|
43
|
+
return res;
|
|
44
|
+
}
|
|
45
|
+
async hmacSha256(data, key) {
|
|
46
|
+
const keyMaterial = await crypto.subtle.importKey(
|
|
47
|
+
"raw",
|
|
48
|
+
key,
|
|
49
|
+
{ name: "HMAC", hash: { name: "SHA-256" } },
|
|
50
|
+
false,
|
|
51
|
+
["sign"]
|
|
52
|
+
);
|
|
53
|
+
const res = await crypto.subtle.sign({ name: "HMAC" }, keyMaterial, data);
|
|
54
|
+
return new Uint8Array(res);
|
|
55
|
+
}
|
|
56
|
+
gzip(data, maxSize) {
|
|
57
|
+
try {
|
|
58
|
+
return deflateSync(data, {
|
|
59
|
+
maxOutputLength: maxSize
|
|
60
|
+
});
|
|
61
|
+
} catch (e) {
|
|
62
|
+
if (e.code === "ERR_BUFFER_TOO_LARGE") {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
throw e;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
gunzip(data) {
|
|
69
|
+
return gunzipSync(data);
|
|
70
|
+
}
|
|
71
|
+
randomFill(buf) {
|
|
72
|
+
crypto.getRandomValues(buf);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
BunCryptoProvider
|
|
77
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
let installed = false;
|
|
2
|
+
let handled = false;
|
|
3
|
+
const callbacks = /* @__PURE__ */ new Set();
|
|
4
|
+
const myHandlers = /* @__PURE__ */ new Map();
|
|
5
|
+
function register(shouldManuallyExit, signal, event) {
|
|
6
|
+
function eventHandler() {
|
|
7
|
+
if (handled) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
handled = true;
|
|
11
|
+
for (const callback of callbacks) {
|
|
12
|
+
callback();
|
|
13
|
+
}
|
|
14
|
+
for (const [event2, handler] of myHandlers) {
|
|
15
|
+
process.off(event2, handler);
|
|
16
|
+
}
|
|
17
|
+
if (shouldManuallyExit) {
|
|
18
|
+
process.kill(process.pid, signal);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
process.on(event, eventHandler);
|
|
22
|
+
myHandlers.set(event, eventHandler);
|
|
23
|
+
}
|
|
24
|
+
function beforeExit(fn) {
|
|
25
|
+
if (typeof process === "undefined") return () => {
|
|
26
|
+
};
|
|
27
|
+
if (!installed) {
|
|
28
|
+
installed = true;
|
|
29
|
+
register(true, 0, "beforeExit");
|
|
30
|
+
register(true, 2, "SIGINT");
|
|
31
|
+
register(true, 15, "SIGTERM");
|
|
32
|
+
register(false, 15, "exit");
|
|
33
|
+
}
|
|
34
|
+
callbacks.add(fn);
|
|
35
|
+
return () => {
|
|
36
|
+
callbacks.delete(fn);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
beforeExit
|
|
41
|
+
};
|
package/utils/logging.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const isTty = typeof process === "object" && Boolean(process.stdout?.isTTY);
|
|
2
|
+
const BASE_FORMAT = isTty ? "%s [%s] [%s%s\x1B[0m] " : "%s [%s] [%s] ";
|
|
3
|
+
const LEVEL_NAMES = isTty ? [
|
|
4
|
+
"",
|
|
5
|
+
// OFF
|
|
6
|
+
"\x1B[31mERR\x1B[0m",
|
|
7
|
+
"\x1B[33mWRN\x1B[0m",
|
|
8
|
+
"\x1B[34mINF\x1B[0m",
|
|
9
|
+
"\x1B[36mDBG\x1B[0m",
|
|
10
|
+
"\x1B[35mVRB\x1B[0m"
|
|
11
|
+
] : [
|
|
12
|
+
"",
|
|
13
|
+
// OFF
|
|
14
|
+
"ERR",
|
|
15
|
+
"WRN",
|
|
16
|
+
"INF",
|
|
17
|
+
"DBG",
|
|
18
|
+
"VRB"
|
|
19
|
+
];
|
|
20
|
+
const TAG_COLORS = [6, 2, 3, 4, 5, 1].map((i) => `\x1B[3${i};1m`);
|
|
21
|
+
const defaultLoggingHandler = isTty ? (color, level, tag, fmt, args) => {
|
|
22
|
+
console.log(BASE_FORMAT + fmt, (/* @__PURE__ */ new Date()).toISOString(), LEVEL_NAMES[level], TAG_COLORS[color], tag, ...args);
|
|
23
|
+
} : (color, level, tag, fmt, args) => {
|
|
24
|
+
console.log(BASE_FORMAT + fmt, (/* @__PURE__ */ new Date()).toISOString(), LEVEL_NAMES[level], tag, ...args);
|
|
25
|
+
};
|
|
26
|
+
export {
|
|
27
|
+
defaultLoggingHandler
|
|
28
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ReadStream } from "node:fs";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { basename } from "node:path";
|
|
4
|
+
import { Readable } from "node:stream";
|
|
5
|
+
function isBunFile(file) {
|
|
6
|
+
return file instanceof Blob && "name" in file && file.name.length > 0;
|
|
7
|
+
}
|
|
8
|
+
async function normalizeFile(file) {
|
|
9
|
+
if (typeof file === "string") {
|
|
10
|
+
file = Bun.file(file);
|
|
11
|
+
}
|
|
12
|
+
if (isBunFile(file)) {
|
|
13
|
+
return {
|
|
14
|
+
file,
|
|
15
|
+
fileName: file.name,
|
|
16
|
+
fileSize: file.size
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (file instanceof ReadStream) {
|
|
20
|
+
const fileName = basename(file.path.toString());
|
|
21
|
+
const fileSize = await stat(file.path.toString()).then((stat2) => stat2.size);
|
|
22
|
+
return {
|
|
23
|
+
file: Readable.toWeb(file),
|
|
24
|
+
fileName,
|
|
25
|
+
fileSize
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (file instanceof Readable) {
|
|
29
|
+
return {
|
|
30
|
+
file: Readable.toWeb(file)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
normalizeFile
|
|
37
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { HttpProxySettings as FumanHttpProxySettings, ITcpConnection, SocksProxySettings, TcpEndpoint } from '@fuman/net';
|
|
2
|
+
import { BasicDcOption } from '@mtcute/core/utils.js';
|
|
3
|
+
import { BaseMtProxyTransport, IntermediatePacketCodec, ITelegramConnection, TelegramTransport } from '@mtcute/core';
|
|
4
|
+
export type { SocksProxySettings } from '@fuman/net';
|
|
5
|
+
export { HttpProxyConnectionError, SocksProxyConnectionError } from '@fuman/net';
|
|
6
|
+
export type { MtProxySettings } from '@mtcute/core';
|
|
7
|
+
export interface HttpProxySettings extends FumanHttpProxySettings {
|
|
8
|
+
/**
|
|
9
|
+
* Whether this is a HTTPS proxy (by default it is regular HTTP).
|
|
10
|
+
*/
|
|
11
|
+
tls?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare class HttpProxyTcpTransport implements TelegramTransport {
|
|
14
|
+
readonly proxy: HttpProxySettings;
|
|
15
|
+
constructor(proxy: HttpProxySettings);
|
|
16
|
+
connect(dc: BasicDcOption): Promise<ITelegramConnection>;
|
|
17
|
+
packetCodec(): IntermediatePacketCodec;
|
|
18
|
+
}
|
|
19
|
+
export declare class SocksProxyTcpTransport implements TelegramTransport {
|
|
20
|
+
readonly proxy: SocksProxySettings;
|
|
21
|
+
constructor(proxy: SocksProxySettings);
|
|
22
|
+
connect(dc: BasicDcOption): Promise<ITelegramConnection>;
|
|
23
|
+
packetCodec(): IntermediatePacketCodec;
|
|
24
|
+
}
|
|
25
|
+
export declare class MtProxyTcpTransport extends BaseMtProxyTransport {
|
|
26
|
+
_connectTcp(endpoint: TcpEndpoint): Promise<ITcpConnection>;
|
|
27
|
+
}
|
package/utils/proxies.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { connectTls, connectTcp } from "@fuman/bun";
|
|
2
|
+
import { performHttpProxyHandshake, performSocksHandshake } from "@fuman/net";
|
|
3
|
+
import { HttpProxyConnectionError, SocksProxyConnectionError } from "@fuman/net";
|
|
4
|
+
import { IntermediatePacketCodec, BaseMtProxyTransport } from "@mtcute/core";
|
|
5
|
+
class HttpProxyTcpTransport {
|
|
6
|
+
constructor(proxy) {
|
|
7
|
+
this.proxy = proxy;
|
|
8
|
+
}
|
|
9
|
+
async connect(dc) {
|
|
10
|
+
let conn;
|
|
11
|
+
if (this.proxy.tls) {
|
|
12
|
+
conn = await connectTls({
|
|
13
|
+
address: this.proxy.host,
|
|
14
|
+
port: this.proxy.port
|
|
15
|
+
});
|
|
16
|
+
} else {
|
|
17
|
+
conn = await connectTcp({
|
|
18
|
+
address: this.proxy.host,
|
|
19
|
+
port: this.proxy.port
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
await performHttpProxyHandshake(conn, conn, this.proxy, {
|
|
23
|
+
address: dc.ipAddress,
|
|
24
|
+
port: dc.port
|
|
25
|
+
});
|
|
26
|
+
return conn;
|
|
27
|
+
}
|
|
28
|
+
packetCodec() {
|
|
29
|
+
return new IntermediatePacketCodec();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
class SocksProxyTcpTransport {
|
|
33
|
+
constructor(proxy) {
|
|
34
|
+
this.proxy = proxy;
|
|
35
|
+
}
|
|
36
|
+
async connect(dc) {
|
|
37
|
+
const conn = await connectTcp({
|
|
38
|
+
address: this.proxy.host,
|
|
39
|
+
port: this.proxy.port
|
|
40
|
+
});
|
|
41
|
+
await performSocksHandshake(conn, conn, this.proxy, {
|
|
42
|
+
address: dc.ipAddress,
|
|
43
|
+
port: dc.port
|
|
44
|
+
});
|
|
45
|
+
return conn;
|
|
46
|
+
}
|
|
47
|
+
packetCodec() {
|
|
48
|
+
return new IntermediatePacketCodec();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
class MtProxyTcpTransport extends BaseMtProxyTransport {
|
|
52
|
+
async _connectTcp(endpoint) {
|
|
53
|
+
return connectTcp(endpoint);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
HttpProxyConnectionError,
|
|
58
|
+
HttpProxyTcpTransport,
|
|
59
|
+
MtProxyTcpTransport,
|
|
60
|
+
SocksProxyConnectionError,
|
|
61
|
+
SocksProxyTcpTransport
|
|
62
|
+
};
|
package/utils/tcp.d.ts
CHANGED
|
@@ -1,31 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* Subclasses must provide packet codec in `_packetCodec` property
|
|
8
|
-
*/
|
|
9
|
-
export declare abstract class BaseTcpTransport extends EventEmitter implements ITelegramTransport {
|
|
10
|
-
protected _currentDc: BasicDcOption | null;
|
|
11
|
-
protected _state: TransportState;
|
|
12
|
-
protected _socket: Socket | null;
|
|
13
|
-
abstract _packetCodec: IPacketCodec;
|
|
14
|
-
protected _crypto: ICryptoProvider;
|
|
15
|
-
protected log: Logger;
|
|
16
|
-
packetCodecInitialized: boolean;
|
|
17
|
-
private _updateLogPrefix;
|
|
18
|
-
setup(crypto: ICryptoProvider, log: Logger): void;
|
|
19
|
-
state(): TransportState;
|
|
20
|
-
currentDc(): BasicDcOption | null;
|
|
21
|
-
connect(dc: BasicDcOption, testMode: boolean): void;
|
|
22
|
-
close(): void;
|
|
23
|
-
handleError(socket: unknown, error: Error): void;
|
|
24
|
-
handleConnect(socket: Socket): void;
|
|
25
|
-
send(bytes: Uint8Array): Promise<void>;
|
|
26
|
-
private _sendOnceDrained;
|
|
27
|
-
private handleDrained;
|
|
28
|
-
}
|
|
29
|
-
export declare class TcpTransport extends BaseTcpTransport {
|
|
30
|
-
_packetCodec: IntermediatePacketCodec;
|
|
1
|
+
import { ITcpConnection } from '@fuman/net';
|
|
2
|
+
import { BasicDcOption } from '@mtcute/core/utils.js';
|
|
3
|
+
import { IntermediatePacketCodec, TelegramTransport } from '@mtcute/core';
|
|
4
|
+
export declare class TcpTransport implements TelegramTransport {
|
|
5
|
+
connect(dc: BasicDcOption): Promise<ITcpConnection>;
|
|
6
|
+
packetCodec(): IntermediatePacketCodec;
|
|
31
7
|
}
|
package/utils/tcp.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { connectTcp } from "@fuman/bun";
|
|
2
|
+
import { IntermediatePacketCodec } from "@mtcute/core";
|
|
3
|
+
class TcpTransport {
|
|
4
|
+
connect(dc) {
|
|
5
|
+
return connectTcp({ address: dc.ipAddress, port: dc.port });
|
|
6
|
+
}
|
|
7
|
+
packetCodec() {
|
|
8
|
+
return new IntermediatePacketCodec();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
TcpTransport
|
|
13
|
+
};
|
package/worker.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { ClientMessageHandler, RespondFn, SendFn, SomeWorker, TelegramWorkerOptions,
|
|
2
|
-
export type { TelegramWorkerOptions,
|
|
1
|
+
import { ClientMessageHandler, RespondFn, SendFn, SomeWorker, TelegramWorkerOptions, WorkerCustomMethods, WorkerMessageHandler, TelegramWorker as TelegramWorkerBase, TelegramWorkerPort as TelegramWorkerPortBase } from '@mtcute/core/worker.js';
|
|
2
|
+
export type { TelegramWorkerOptions, WorkerCustomMethods };
|
|
3
|
+
export interface TelegramWorkerPortOptions {
|
|
4
|
+
worker: SomeWorker;
|
|
5
|
+
}
|
|
3
6
|
export declare class TelegramWorker<T extends WorkerCustomMethods> extends TelegramWorkerBase<T> {
|
|
4
7
|
registerWorker(handler: WorkerMessageHandler): RespondFn;
|
|
5
8
|
}
|
|
6
9
|
export declare class TelegramWorkerPort<T extends WorkerCustomMethods> extends TelegramWorkerPortBase<T> {
|
|
7
|
-
readonly options: TelegramWorkerPortOptions;
|
|
8
10
|
constructor(options: TelegramWorkerPortOptions);
|
|
9
11
|
connectToWorker(worker: SomeWorker, handler: ClientMessageHandler): [SendFn, () => void];
|
|
10
12
|
}
|
package/worker.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { parentPort, Worker } from "node:worker_threads";
|
|
2
|
+
import { TelegramWorker as TelegramWorker$1, TelegramWorkerPort as TelegramWorkerPort$1 } from "@mtcute/core/worker.js";
|
|
3
|
+
import { BunPlatform } from "./platform.js";
|
|
4
|
+
let _registered = false;
|
|
5
|
+
class TelegramWorker extends TelegramWorker$1 {
|
|
6
|
+
registerWorker(handler) {
|
|
7
|
+
if (!parentPort) {
|
|
8
|
+
throw new Error("TelegramWorker must be created from a worker thread");
|
|
9
|
+
}
|
|
10
|
+
if (_registered) {
|
|
11
|
+
throw new Error("TelegramWorker must be created only once");
|
|
12
|
+
}
|
|
13
|
+
_registered = true;
|
|
14
|
+
const port = parentPort;
|
|
15
|
+
const respond = port.postMessage.bind(port);
|
|
16
|
+
parentPort.on("message", (message) => handler(message, respond));
|
|
17
|
+
return respond;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
class TelegramWorkerPort extends TelegramWorkerPort$1 {
|
|
21
|
+
constructor(options) {
|
|
22
|
+
super({
|
|
23
|
+
worker: options.worker,
|
|
24
|
+
platform: new BunPlatform()
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
connectToWorker(worker, handler) {
|
|
28
|
+
if (!(worker instanceof Worker)) {
|
|
29
|
+
throw new TypeError("Only worker_threads are supported");
|
|
30
|
+
}
|
|
31
|
+
const send = worker.postMessage.bind(worker);
|
|
32
|
+
worker.on("message", handler);
|
|
33
|
+
return [
|
|
34
|
+
send,
|
|
35
|
+
() => {
|
|
36
|
+
worker.off("message", handler);
|
|
37
|
+
}
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
TelegramWorker,
|
|
43
|
+
TelegramWorkerPort
|
|
44
|
+
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ICorePlatform } from '@mtcute/core/platform.js';
|
|
2
|
-
import { normalizeFile } from '../utils/normalize-file.js';
|
|
3
|
-
import { beforeExit } from './exit-hook.js';
|
|
4
|
-
import { defaultLoggingHandler } from './logging.js';
|
|
5
|
-
export declare class NodePlatform implements ICorePlatform {
|
|
6
|
-
log: typeof defaultLoggingHandler;
|
|
7
|
-
beforeExit: typeof beforeExit;
|
|
8
|
-
normalizeFile: typeof normalizeFile;
|
|
9
|
-
getDeviceModel(): string;
|
|
10
|
-
getDefaultLogLevel(): number | null;
|
|
11
|
-
utf8ByteLength(str: string): number;
|
|
12
|
-
utf8Encode(str: string): Uint8Array;
|
|
13
|
-
utf8Decode(buf: Uint8Array): string;
|
|
14
|
-
hexEncode(buf: Uint8Array): string;
|
|
15
|
-
hexDecode(str: string): Uint8Array;
|
|
16
|
-
base64Encode(buf: Uint8Array, url?: boolean): string;
|
|
17
|
-
base64Decode(string: string, url?: boolean): Uint8Array;
|
|
18
|
-
}
|
package/index.d.cts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export * from './client.js';
|
|
2
|
-
export * from './platform.js';
|
|
3
|
-
export * from './sqlite/index.js';
|
|
4
|
-
export * from './utils/crypto.js';
|
|
5
|
-
export * from './utils/tcp.js';
|
|
6
|
-
export * from './worker.js';
|
|
7
|
-
export * from '@mtcute/core';
|
|
8
|
-
export * from '@mtcute/html-parser';
|
|
9
|
-
export * from '@mtcute/markdown-parser';
|
package/methods.d.cts
DELETED
package/utils.d.cts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '@mtcute/core/utils.js';
|
|
File without changes
|
|
File without changes
|