@mtcute/bun 0.16.7 → 0.16.14
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/chunks/es/BmtxbrWZ.js +29 -0
- package/client.d.ts +3 -4
- package/common-internals-node/platform.d.ts +1 -1
- package/index.d.cts +9 -0
- package/index.js +541 -10
- package/methods/download-file.d.ts +1 -1
- package/methods/download-node-stream.d.ts +1 -1
- package/methods.d.cts +3 -0
- package/methods.js +6 -4
- package/package.json +51 -24
- package/sqlite/driver.d.ts +1 -2
- package/sqlite/index.d.ts +1 -1
- package/sqlite/sqlite.test.d.ts +1 -0
- package/utils/crypto.d.ts +1 -2
- package/utils/crypto.test.d.ts +1 -0
- package/utils/normalize-file.d.ts +1 -1
- package/utils/tcp.d.ts +4 -5
- package/utils.d.cts +1 -0
- package/utils.js +1 -2
- package/worker.d.ts +1 -2
- package/client.js +0 -97
- package/client.js.map +0 -1
- package/common-internals-node/exit-hook.js +0 -42
- package/common-internals-node/exit-hook.js.map +0 -1
- package/common-internals-node/logging.js +0 -31
- package/common-internals-node/logging.js.map +0 -1
- package/common-internals-node/platform.js +0 -58
- package/common-internals-node/platform.js.map +0 -1
- package/index.js.map +0 -1
- package/methods/download-file.js +0 -29
- package/methods/download-file.js.map +0 -1
- package/methods/download-node-stream.js +0 -13
- package/methods/download-node-stream.js.map +0 -1
- package/methods.js.map +0 -1
- package/platform.js +0 -10
- package/platform.js.map +0 -1
- package/sqlite/driver.js +0 -19
- package/sqlite/driver.js.map +0 -1
- package/sqlite/index.js +0 -13
- package/sqlite/index.js.map +0 -1
- package/utils/crypto.js +0 -74
- package/utils/crypto.js.map +0 -1
- package/utils/normalize-file.js +0 -38
- package/utils/normalize-file.js.map +0 -1
- package/utils/tcp.js +0 -127
- package/utils/tcp.js.map +0 -1
- package/utils.js.map +0 -1
- package/worker.js +0 -43
- package/worker.js.map +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { unlinkSync } from "node:fs";
|
|
2
|
+
import { FileLocation } from "@mtcute/core";
|
|
3
|
+
import { downloadAsIterable, downloadAsStream } from "@mtcute/core/methods.js";
|
|
4
|
+
import { Readable } from "node:stream";
|
|
5
|
+
async function downloadToFile(client, filename, location, params) {
|
|
6
|
+
if (location instanceof FileLocation && ArrayBuffer.isView(location.location)) {
|
|
7
|
+
await Bun.write(filename, location.location);
|
|
8
|
+
}
|
|
9
|
+
const output = Bun.file(filename).writer();
|
|
10
|
+
if (params?.abortSignal) {
|
|
11
|
+
params.abortSignal.addEventListener("abort", () => {
|
|
12
|
+
client.log.debug("aborting file download %s - cleaning up", filename);
|
|
13
|
+
Promise.resolve(output.end()).catch(() => {
|
|
14
|
+
});
|
|
15
|
+
unlinkSync(filename);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
for await (const chunk of downloadAsIterable(client, location, params)) {
|
|
19
|
+
output.write(chunk);
|
|
20
|
+
}
|
|
21
|
+
await output.end();
|
|
22
|
+
}
|
|
23
|
+
function downloadAsNodeStream(client, location, params) {
|
|
24
|
+
return Readable.fromWeb(downloadAsStream(client, location, params));
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
downloadAsNodeStream,
|
|
28
|
+
downloadToFile
|
|
29
|
+
};
|
package/client.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { BaseTelegramClient as BaseTelegramClientBase, TelegramClient as TelegramClientBase } from '@mtcute/core/client.js';
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { FileDownloadLocation, FileDownloadParameters, ITelegramStorageProvider, PartialOnly, User } from '@mtcute/core';
|
|
3
|
+
import { BaseTelegramClientOptions as BaseTelegramClientOptionsBase, TelegramClientOptions, BaseTelegramClient as BaseTelegramClientBase, TelegramClient as TelegramClientBase } from '@mtcute/core/client.js';
|
|
5
4
|
export type { TelegramClientOptions };
|
|
6
5
|
export interface BaseTelegramClientOptions extends PartialOnly<Omit<BaseTelegramClientOptionsBase, 'storage'>, 'transport' | 'crypto'> {
|
|
7
6
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ICorePlatform } from '@mtcute/core/platform.js';
|
|
2
2
|
import { normalizeFile } from '../utils/normalize-file.js';
|
|
3
3
|
import { beforeExit } from './exit-hook.js';
|
|
4
4
|
import { defaultLoggingHandler } from './logging.js';
|
package/index.d.cts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
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/index.js
CHANGED
|
@@ -1,10 +1,541 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { BaseTelegramClient as BaseTelegramClient$1, TelegramClient as TelegramClient$1 } from "@mtcute/core/client.js";
|
|
3
|
+
import { setPlatform } from "@mtcute/core/platform.js";
|
|
4
|
+
import { downloadToFile, downloadAsNodeStream } from "./chunks/es/BmtxbrWZ.js";
|
|
5
|
+
import * as os from "node:os";
|
|
6
|
+
import { createReadStream, ReadStream } from "node:fs";
|
|
7
|
+
import { stat, readFile } from "node:fs/promises";
|
|
8
|
+
import { basename } from "node:path";
|
|
9
|
+
import { Readable } from "node:stream";
|
|
10
|
+
import { BaseSqliteStorageDriver, BaseSqliteStorage, TransportState, MtcuteError, IntermediatePacketCodec } from "@mtcute/core";
|
|
11
|
+
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
|
+
export * from "@mtcute/html-parser";
|
|
21
|
+
export * from "@mtcute/markdown-parser";
|
|
22
|
+
function nodeStreamToWeb(stream) {
|
|
23
|
+
if (typeof Readable.toWeb === "function") {
|
|
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
|
+
}
|
|
530
|
+
export {
|
|
531
|
+
BaseTcpTransport,
|
|
532
|
+
BaseTelegramClient,
|
|
533
|
+
BunCryptoProvider,
|
|
534
|
+
BunPlatform,
|
|
535
|
+
SqliteStorage,
|
|
536
|
+
SqliteStorageDriver,
|
|
537
|
+
TcpTransport,
|
|
538
|
+
TelegramClient,
|
|
539
|
+
TelegramWorker,
|
|
540
|
+
TelegramWorkerPort
|
|
541
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FileDownloadLocation, FileDownloadParameters, ITelegramClient } from '@mtcute/core';
|
|
2
2
|
/**
|
|
3
3
|
* Download a remote file to a local file (only for NodeJS).
|
|
4
4
|
* Promise will resolve once the download is complete.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Readable } from 'node:stream';
|
|
2
|
-
import
|
|
2
|
+
import { FileDownloadLocation, FileDownloadParameters, ITelegramClient } from '@mtcute/core';
|
|
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).
|
package/methods.d.cts
ADDED
package/methods.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
|
|
1
|
+
import { downloadAsNodeStream, downloadToFile } from "./chunks/es/BmtxbrWZ.js";
|
|
2
|
+
export * from "@mtcute/core/methods.js";
|
|
3
|
+
export {
|
|
4
|
+
downloadAsNodeStream,
|
|
5
|
+
downloadToFile
|
|
6
|
+
};
|