mytglib 1.1.1 → 1.1.3
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/README.md +341 -1
- package/dist/index.js +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 环境
|
|
6
6
|
|
|
7
|
-
- Node.js 20
|
|
7
|
+
- Node.js 20、22、23、24、25 或 26(与 `better-sqlite3@12.11.1` 支持范围一致)
|
|
8
8
|
- 执行登录或代理检查时,需要一个可连接 Telegram 主 DC 的 HTTP 或 SOCKS5 代理
|
|
9
9
|
|
|
10
10
|
安装当前仓库依赖:
|
|
@@ -173,6 +173,346 @@ const jsonFilePath = await core.syncSessionJson();
|
|
|
173
173
|
|
|
174
174
|
`syncSessionJson()` 无参数并返回 JSON 文件路径。JSON 中的 `session_str` 为可与 Telethon StringSession v1 兼容的字符串,`reg_time` 按中国标准时间转为 `YYYY-MM-DD`;当前转换仅支持项目使用的 IPv4 主 DC。底层 `.session` 仍是 mtcute SQLite,不会被转换成 Telethon SQLite。输出文件包含 auth key 和明文两步验证密码,会以 `0600` 权限原子替换,必须与 `.session` 一样按密码保护。
|
|
175
175
|
|
|
176
|
+
## 手机号状态账号池
|
|
177
|
+
|
|
178
|
+
`createPhoneStatusPool()` 用于在常驻 Node.js API 进程内维护一组已经授权的 Telegram
|
|
179
|
+
账号。账号导入后由 Pool 自主预连接、派单、冷却、隔离、补槽和回收;管理员只负责导入、查看、
|
|
180
|
+
测试或删除账号,不需要手工启用、停用或扩缩容。
|
|
181
|
+
|
|
182
|
+
Pool 使用消费者模式:外部请求只消费已经连接的 `READY` 槽位,不会在请求路径中临时连接
|
|
183
|
+
Telegram。创建 Pool 和后续补槽均为异步操作,因此进程刚启动、账号刚导入或资源已达上限而没有
|
|
184
|
+
`READY` 账号时,检测会立即返回 `BUSY`,不会排队等待建连。
|
|
185
|
+
|
|
186
|
+
### 初始化与导入
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
import { resolve } from "node:path";
|
|
190
|
+
import { createPhoneStatusPool } from "mytglib";
|
|
191
|
+
|
|
192
|
+
const pool = await createPhoneStatusPool({
|
|
193
|
+
databasePath: resolve(".data/phone-status-pool/pool.sqlite"),
|
|
194
|
+
minWarmSlots: 10,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const account = await pool.importAccount({
|
|
198
|
+
sessionFilePath: "/secure/imports/13099434947.session",
|
|
199
|
+
sessionJsonFilePath: "/secure/imports/13099434947.json",
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
console.log(account.accountId, account.runtimeStatus);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`databasePath` 必须是绝对文件路径。数据库目录和文件分别使用 `0700`、`0600` 权限;Pool 会把
|
|
206
|
+
账号连接所需的 auth key 和 JSON Metadata 明文持久化到 SQLite,因此应把数据库目录视为密钥目录
|
|
207
|
+
进行保护。`.session` 与 JSON 必须属于同一个有效的 Telethon 账号,导入时会严格核对 auth key、
|
|
208
|
+
DC、账号身份和手机号。Pool 只接受 Telegram 官方移动端凭据:Android 必须使用 `app_id: 4`
|
|
209
|
+
及其官方 `app_hash`,iOS 必须使用 `app_id: 8` 及其官方 `app_hash`;TDesktop、自定义 API ID
|
|
210
|
+
或平台与 API 凭据不匹配的 JSON 会在建立 Telegram 连接前直接拒绝。当前不支持代理。
|
|
211
|
+
|
|
212
|
+
为兼容常见 Telethon 导出格式,JSON 的 `session_file` 只能写真实文件名
|
|
213
|
+
`account.session` 或同名 stem `account`,不得携带目录组件;导入后会统一规整为真实文件名。
|
|
214
|
+
StringSession 优先读取 `session_str`,也兼容 `session_string`;两者都缺失时会根据只读 SQLite
|
|
215
|
+
中唯一的 `sessions` 行生成规范 `session_str`。任何显式提供的 StringSession 都必须与 SQLite
|
|
216
|
+
中的主 DC、IPv4、端口和 auth key 完全一致,否则拒绝导入。
|
|
217
|
+
|
|
218
|
+
`minWarmSlots` 可省略,默认值为 `10`,允许范围为 `1..50`。它是正常负载下希望维持的最小
|
|
219
|
+
预连接槽位数,不是导入账号数,也不是无条件建立的连接数。例如导入 500 个账号且设置
|
|
220
|
+
`minWarmSlots: 10` 时,Pool 只会先预热目标槽位,剩余账号作为持久化库存待命,不会把 500 个
|
|
221
|
+
账号同时上线。
|
|
222
|
+
|
|
223
|
+
Pool 的策略硬上限固定为 `minWarmSlots * 5`,实际连接上限还会受进程 CPU、内存、文件描述符和
|
|
224
|
+
事件循环压力评估限制。忙碌槽位超过当前连接数一半时,Pool 会在资源允许的情况下异步提高目标槽位;
|
|
225
|
+
高峰过后,弹性槽位连续空闲 10 分钟才进入回收,并且每 30 秒最多回收一个,避免连接数瞬间震荡。
|
|
226
|
+
当总连接上限高于 `minWarmSlots` 时,Pool 会在这个上限内部为导入和待机账号自检保留 1 个临时
|
|
227
|
+
管理连接位;该预留不会突破 `minWarmSlots * 5`。若资源上限已经低到不高于最小暖槽数,管理建连
|
|
228
|
+
会返回 `POOL_BUSY`,优先保护正在提供服务的暖槽。
|
|
229
|
+
|
|
230
|
+
### 检测与返回值
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
const response = await pool.checkPhoneStatus({
|
|
234
|
+
phone: "+12025550123",
|
|
235
|
+
abortSignal: new AbortController().signal,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (response.status === "RESULT") {
|
|
239
|
+
console.log(response.result, response.checkedAt, response.cached);
|
|
240
|
+
} else {
|
|
241
|
+
console.log(`线路忙,请在 ${response.retryAfterSeconds} 秒后重试`);
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
对外只有两种返回形态:
|
|
246
|
+
|
|
247
|
+
```js
|
|
248
|
+
{
|
|
249
|
+
status: "RESULT",
|
|
250
|
+
phone: "+12025550123",
|
|
251
|
+
result: "PHONE_NUMBER_OCCUPIED",
|
|
252
|
+
checkedAt: "2026-08-13T00:00:00.000Z",
|
|
253
|
+
cached: false,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
{ status: "BUSY", retryAfterSeconds: 1 }
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
单次检测总期限固定为 10 秒。账号触发 `FLOOD_WAIT` 后会按 Telegram 返回的等待秒数退出暖槽并
|
|
260
|
+
进入冷却,Pool 随即从库存异步补入其他账号;当前请求最多再换用一个已经存在的 `READY` 账号,
|
|
261
|
+
第二次仍未得到明确结果时返回 `BUSY`。`PHONE_NUMBER_FLOOD` 只保护目标手机号 5 分钟,不处罚
|
|
262
|
+
账号。明确检测结果缓存 5 分钟,最多保存 10,000 条;同一手机号的并发请求会合并为一次检测,
|
|
263
|
+
基础设施错误不缓存,并统一按 `BUSY` 返回;也不提供强制刷新。
|
|
264
|
+
|
|
265
|
+
### 管理与运行状态
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
const accounts = await pool.listAccounts();
|
|
269
|
+
const detail = await pool.getAccount(account.accountId);
|
|
270
|
+
const testResult = await pool.testAccount(account.accountId);
|
|
271
|
+
const status = await pool.getStatus();
|
|
272
|
+
|
|
273
|
+
console.log(accounts, detail, testResult, status.runtime);
|
|
274
|
+
|
|
275
|
+
// 删除会先停止派单,等待该账号当前任务结束,再断开连接并删除持久化记录。
|
|
276
|
+
const removed = await pool.removeAccount(account.accountId);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
账号运行态由 Pool 管理:可用账号处于 `STANDBY`、`RESERVED`、`CONNECTING`、`READY` 或
|
|
280
|
+
`BUSY`;触发 flood 的账号进入 `COOLDOWN`,认证失效等致命错误会进入 `QUARANTINED`。
|
|
281
|
+
`testAccount()` 是账号健康自检:它使用 `users.getUsers(inputUserSelf)` 验证该账号的授权和身份,
|
|
282
|
+
不会拿这个账号去检测某个外部手机号。验证成功会把隔离账号恢复为可调度状态。
|
|
283
|
+
管理接口返回的账号信息不会包含 auth key 或原始 Metadata,手机号也会被掩码。
|
|
284
|
+
|
|
285
|
+
`getStatus()` 可用于健康检查和后台展示,包含生命周期、账号库存、各运行态槽位数、当前目标、
|
|
286
|
+
策略上限、资源上限、资源压力和建连并发等信息。若业务需要等待初始暖槽,可以在 API 对外接流量前
|
|
287
|
+
轮询 `status.runtime.ready`;检测方法自身始终不会等待预热。
|
|
288
|
+
|
|
289
|
+
### 部署与关闭
|
|
290
|
+
|
|
291
|
+
一个 SQLite 号池数据库同时只能由一个 Pool/进程持有,重复打开会抛出
|
|
292
|
+
`POOL_ALREADY_OWNED`。为避免多个进程同时接管同一数据库,Pool 不会自动删除陈旧 owner lock;
|
|
293
|
+
进程异常退出后再次启动会抛出 `POOL_STALE_LOCK`,必须先确认原 Pool 进程已经停止,再人工删除错误
|
|
294
|
+
`details.lockPath` 指向的 `.lock` 文件。因此 Pool 应作为 API 进程级单例创建,而不是在每个路由或
|
|
295
|
+
每次请求中创建。
|
|
296
|
+
使用 PM2 时应采用单实例 `fork` 模式,不要使用 `cluster`:
|
|
297
|
+
|
|
298
|
+
```js
|
|
299
|
+
export default {
|
|
300
|
+
apps: [{
|
|
301
|
+
name: "api",
|
|
302
|
+
script: "server.js",
|
|
303
|
+
exec_mode: "fork",
|
|
304
|
+
instances: 1,
|
|
305
|
+
}],
|
|
306
|
+
};
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Next.js 必须让持有 Pool 的路由运行在 Node.js runtime,不能使用 Edge runtime;常驻连接和本地
|
|
310
|
+
SQLite owner lock 也不适合无状态 serverless 部署。由于 `better-sqlite3` 是原生依赖,建议在
|
|
311
|
+
`next.config.js` 中把库及相关运行时包保持为服务端外部依赖:
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
/** @type {import("next").NextConfig} */
|
|
315
|
+
const nextConfig = {
|
|
316
|
+
serverExternalPackages: ["mytglib", "@mtcute/node", "better-sqlite3"],
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
export default nextConfig;
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
对应 Route Handler 显式声明:
|
|
323
|
+
|
|
324
|
+
```js
|
|
325
|
+
export const runtime = "nodejs";
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
进程退出时应停止接收新请求并安全关闭 Pool:
|
|
329
|
+
|
|
330
|
+
```js
|
|
331
|
+
await pool.close({ drainTimeoutMs: 10_000 });
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
`close()` 会先等待正在执行的管理和检测操作;超过 drain deadline 后取消未完成操作,再销毁所有
|
|
335
|
+
Telegram 客户端、定时器、缓存、SQLite 连接和 owner lock。关闭后的 Pool 不能复用,应重新调用
|
|
336
|
+
`createPhoneStatusPool()`。
|
|
337
|
+
|
|
338
|
+
### Rust API
|
|
339
|
+
|
|
340
|
+
Rust 实现从 `grammers_core` 根模块导出 `create_phone_status_pool()`、`PhoneStatusPool` 及相关
|
|
341
|
+
输入、响应和状态类型。调度、持久化格式、暖槽策略及检测业务响应的 `RESULT`/`BUSY` 边界与上述
|
|
342
|
+
Node.js 实现一致。目标手机号格式无效属于明确检测结果,返回
|
|
343
|
+
`Ok(PhoneStatusCheckResponse::Result { result: PhoneNumberInvalid, .. })`;没有 `READY` 账号、检测
|
|
344
|
+
期限耗尽、目标号码触发 `PHONE_NUMBER_FLOOD` 或检测基础设施失败均返回
|
|
345
|
+
`Ok(PhoneStatusCheckResponse::Busy { .. })`。创建和管理操作的无效输入、调用取消、Pool
|
|
346
|
+
生命周期错误或内部协调错误才通过 `PhoneStatusPoolError` 返回,不应转换成业务 `BUSY`。
|
|
347
|
+
Rust 字段名使用 snake_case,通过 serde 序列化时使用 camelCase。资源上限使用 CPU、内存和文件
|
|
348
|
+
描述符评估;Tokio 没有稳定的 event-loop 利用率指标,因此 Rust 状态不会伪造该项数据。
|
|
349
|
+
|
|
350
|
+
```rust,no_run
|
|
351
|
+
use grammers_core::{
|
|
352
|
+
CheckPhoneStatusInput, CloseOptions, ImportAccountInput, PhoneStatusCheckResponse,
|
|
353
|
+
PhoneStatusPoolError, PhoneStatusPoolOptions, create_phone_status_pool,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
# async fn example() -> Result<(), PhoneStatusPoolError> {
|
|
357
|
+
let mut options = PhoneStatusPoolOptions::new("/secure/phone-status-pool/pool.sqlite");
|
|
358
|
+
options.min_warm_slots = 10;
|
|
359
|
+
let pool = create_phone_status_pool(options).await?;
|
|
360
|
+
|
|
361
|
+
// 即使任一管理或检测步骤失败,下面仍会执行安全关闭。
|
|
362
|
+
let operation: Result<(), PhoneStatusPoolError> = async {
|
|
363
|
+
let account = pool
|
|
364
|
+
.import_account(ImportAccountInput::new(
|
|
365
|
+
"/secure/imports/13099434947.session",
|
|
366
|
+
"/secure/imports/13099434947.json",
|
|
367
|
+
))
|
|
368
|
+
.await?;
|
|
369
|
+
|
|
370
|
+
match pool
|
|
371
|
+
.check_phone_status(CheckPhoneStatusInput::new("+12025550123"))
|
|
372
|
+
.await?
|
|
373
|
+
{
|
|
374
|
+
PhoneStatusCheckResponse::Result {
|
|
375
|
+
result,
|
|
376
|
+
checked_at,
|
|
377
|
+
cached,
|
|
378
|
+
..
|
|
379
|
+
} => println!("{result:?} {checked_at} cached={cached}"),
|
|
380
|
+
PhoneStatusCheckResponse::Busy {
|
|
381
|
+
retry_after_seconds,
|
|
382
|
+
} => println!("BUSY retryAfterSeconds={retry_after_seconds}"),
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
let accounts = pool.list_accounts().await?;
|
|
386
|
+
let detail = pool.get_account(&account.account_id).await?;
|
|
387
|
+
let tested = pool.test_account(&account.account_id).await?;
|
|
388
|
+
let status = pool.get_status().await?;
|
|
389
|
+
println!(
|
|
390
|
+
"accounts={} detail={} tested={} ready={}",
|
|
391
|
+
accounts.len(),
|
|
392
|
+
detail.is_some(),
|
|
393
|
+
tested.is_some(),
|
|
394
|
+
status.runtime.ready,
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
pool.remove_account(&account.account_id).await?;
|
|
398
|
+
Ok(())
|
|
399
|
+
}
|
|
400
|
+
.await;
|
|
401
|
+
|
|
402
|
+
let close_result = pool.close(CloseOptions::default()).await;
|
|
403
|
+
operation?;
|
|
404
|
+
close_result?;
|
|
405
|
+
# Ok(())
|
|
406
|
+
# }
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
`PhoneStatusPoolOptions::new()` 要求绝对数据库路径,`min_warm_slots` 省略时为 `10`。创建方法只
|
|
410
|
+
打开本地状态并启动异步预热,不等待 Telegram 暖槽连接完成;可在接入业务流量前轮询
|
|
411
|
+
`pool.get_status().await?.runtime.ready`。同一个数据库仍只能由一个 Pool 实例持有,进程退出前应
|
|
412
|
+
始终调用 `pool.close(CloseOptions::default()).await`。如果强制关闭宽限期结束时仍有后台清理,
|
|
413
|
+
`close()` 返回 `POOL_CLOSE_TIMEOUT` 并暂时保留 SQLite owner lock;后台任务退出后可再次调用
|
|
414
|
+
`close()` 完成资源释放。导入的 session JSON 最大为 1 MiB。SQLite 与 StringSession 中的
|
|
415
|
+
endpoint 只用于校验两个文件描述同一个会话;实际连接始终使用 grammers 内置的 Telegram DC
|
|
416
|
+
地址,上传文件不能指定 API 进程的 TCP 目标。`ImportAccountInput` 和
|
|
417
|
+
`CheckPhoneStatusInput` 的 `abort_signal` 是仅运行时字段,使用 serde 反序列化输入时需要由 Rust
|
|
418
|
+
调用方另行赋值。
|
|
419
|
+
|
|
420
|
+
## 一次性手机号状态检查
|
|
421
|
+
|
|
422
|
+
`checkPhoneStatus()` 是独立于注册流程的根导出方法。Telegram 没有为此提供纯查询接口;
|
|
423
|
+
本方法使用一个已授权账号的 mtcute `.session` 文件和对应 JSON,根据
|
|
424
|
+
[`account.sendChangePhoneCode`](https://core.telegram.org/method/account.sendChangePhoneCode)
|
|
425
|
+
的响应或 RPC 错误推断号码状态:
|
|
426
|
+
|
|
427
|
+
```js
|
|
428
|
+
import { checkPhoneStatus } from "mytglib";
|
|
429
|
+
|
|
430
|
+
const result = await checkPhoneStatus({
|
|
431
|
+
phone: "+12025550123",
|
|
432
|
+
sessionFilePath: "/secure/account/account.session",
|
|
433
|
+
sessionJsonFilePath: "/secure/account/account.json",
|
|
434
|
+
proxy: "socks5://user:password@127.0.0.1:1080",
|
|
435
|
+
timeout_ms: 30_000,
|
|
436
|
+
abort_signal: new AbortController().signal,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
console.log(result);
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
JSON 必须包含 `syncSessionJson()` 生成的 `session_str`、`session_file`、`app_id`、`app_hash`
|
|
443
|
+
和客户端配置;自定义 iOS 版本还会携带 `override_layer` 以便完整还原。
|
|
444
|
+
方法会以只读方式打开 `.session`,校验其主 DC、IPv4、端口和 auth key 与 JSON 的规范
|
|
445
|
+
`session_str` 一致,再把必要连接状态复制到 `MemoryStorage`;不会迁移或修改原 SQLite 的表和主数据库
|
|
446
|
+
内容。不过 SQLite 在 WAL 模式下即使只读打开也可能创建或更新 `-shm` 等 sidecar,因此这不等同于
|
|
447
|
+
“文件系统零写入”。用于请求的临时客户端不会注册 reCAPTCHA middleware,也不会求解或主动重放
|
|
448
|
+
challenge;正常主 DC 路径只调用一次 `client.call()`,不做应用层重试。mtcute 仍可能为 DC 迁移或
|
|
449
|
+
MTProto 连接恢复重发底层请求,这不是 `maxRetryCount` 控制的应用层重试。请求的
|
|
450
|
+
`settings` 固定为
|
|
451
|
+
`{ _: "codeSettings", allowFlashcall: true, allowFirebase: false, logoutTokens: [] }`。
|
|
452
|
+
`timeout_ms` 默认为 `30000`。会话导出、连接和目标 RPC 等受控异步阶段各自使用完整的
|
|
453
|
+
`timeout_ms` 上限,而不是共享整个方法的总 deadline;资源清理也使用独立的同值上限,并且不会因
|
|
454
|
+
调用方取消而跳过。`abort_signal` 会取消清理前的受控异步阶段;请求重试次数和 flood 自动等待均固定
|
|
455
|
+
为 `0`。客户端无论成功或失败都会在返回前尝试有界销毁。
|
|
456
|
+
|
|
457
|
+
Rust 对应入口是 `grammers_core` 根导出的 `check_phone_status()`。它读取项目生成的 `.grammers`
|
|
458
|
+
会话和同目录 JSON,校验 JSON 中的 `session_str` 与只读会话的主 DC、地址和 auth key 一致,随后
|
|
459
|
+
把授权 key 与连接配置复制到 `MemorySession`;原 `.grammers` 以只读方式
|
|
460
|
+
打开,不会迁移或写回。输入字段使用 snake_case,结果序列化时使用与 npm 一致的 camelCase:
|
|
461
|
+
|
|
462
|
+
```rust,no_run
|
|
463
|
+
use grammers_core::{AbortSignal, PhoneStatusCheckInput, Proxy, check_phone_status};
|
|
464
|
+
|
|
465
|
+
# async fn example() -> Result<(), Box<dyn std::error::Error>> {
|
|
466
|
+
let abort_signal = AbortSignal::new();
|
|
467
|
+
let mut input = PhoneStatusCheckInput::new(
|
|
468
|
+
"+12025550123",
|
|
469
|
+
"/secure/account/account.grammers",
|
|
470
|
+
"/secure/account/account.json",
|
|
471
|
+
);
|
|
472
|
+
input.proxy = Some(Proxy::from_url(
|
|
473
|
+
"socks5://user:password@127.0.0.1:1080",
|
|
474
|
+
).expect("valid proxy URL"));
|
|
475
|
+
input.timeout_ms = 30_000;
|
|
476
|
+
input.abort_signal = Some(abort_signal);
|
|
477
|
+
|
|
478
|
+
let result = check_phone_status(input).await;
|
|
479
|
+
println!("{}", serde_json::to_string_pretty(&result)?);
|
|
480
|
+
# Ok(())
|
|
481
|
+
# }
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Rust 的 `timeout_ms` 覆盖文件读取、只读会话加载、连接及目标 RPC;每个阶段各自使用完整上限,而非
|
|
485
|
+
共享整个方法的总 deadline。`abort_signal` 也覆盖清理前的这些异步阶段;sender cleanup 使用独立的
|
|
486
|
+
同值上限且不会因调用方取消而跳过,因此超时或取消后仍会先尝试有界资源清理再返回。该方法使用
|
|
487
|
+
`NoRetries`,且不自动等待 `FLOOD_WAIT`。`abort_signal` 标记为 serde skip,若
|
|
488
|
+
输入来自 JSON,必须在反序列化后由 Rust 代码赋值。`proxy` 可使用 `Proxy::from_url()`,也可直接
|
|
489
|
+
构造 `Proxy`。
|
|
490
|
+
|
|
491
|
+
这不是无副作用的号码查询:未注册号码可能因此真实收到验证码。调用方必须只在获授权的账号和号码
|
|
492
|
+
范围内使用,并自行控制调用频率,避免触发发送滥用或 `FLOOD_WAIT`。
|
|
493
|
+
|
|
494
|
+
返回对象固定包含 `waitTime`、原样传入的 `phone`、ISO 时间字符串 `startedAt`/`endedAt`、
|
|
495
|
+
`durationMs`、`status` 和 `error`。`status` 可能为 `PHONE_NUMBER_OCCUPIED`、
|
|
496
|
+
`PHONE_NUMBER_NO_OCCUPIED`、`PHONE_NUMBER_INVALID`、`PHONE_NUMBER_BANNED`、`FLOOD_WAIT`
|
|
497
|
+
或 `OTHER_ERROR`。`RECAPTCHA_CHECK_signup` 不会触发求解或再次请求,也不用于推断号码状态,而是按
|
|
498
|
+
`OTHER_ERROR` 返回;`FLOOD_WAIT` 的秒数写入 `waitTime`;其他异常同样不会向外抛出,而是在 `error`
|
|
499
|
+
中保留可用的 `message`、`name`、`code`、`text` 和 `seconds`。
|
|
500
|
+
|
|
501
|
+
授权成功后可调用 Telegram 官方的 [`account.getAuthorizations`](https://core.telegram.org/method/account.getAuthorizations)
|
|
502
|
+
获取当前账号的全部登录会话,并把响应保存到同目录的 `<session stem>.authorizations.json`:
|
|
503
|
+
|
|
504
|
+
```js
|
|
505
|
+
const authorizationsJsonPath = await core.syncAuthorizationsJson();
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
`syncAuthorizationsJson()` 无参数并返回 JSON 文件路径;Rust 对应入口为
|
|
509
|
+
`CoreClient::sync_authorizations_json()`。文件保留 mtcute 的 camelCase 响应结构(根节点为
|
|
510
|
+
`account.authorizations`),仅按官方 [`Authorization`](https://core.telegram.org/constructor/authorization)
|
|
511
|
+
构造器的 `date_created`(会话创建时间)语义,把 `dateCreated` 的 Unix 秒时间戳按 UTC 转为补零的
|
|
512
|
+
`YYYY-MM` 字符串;为避免 JavaScript `Long` 的 JSON 对象结构和数字精度差异,`hash` 统一写为
|
|
513
|
+
十进制字符串,其他授权字段保持原始响应值。该方法要求客户端已初始化并完成授权,应在
|
|
514
|
+
`destroy()` 前调用。
|
|
515
|
+
|
|
176
516
|
`sendCode()`、`changePassword()`、`setNewPassword()`、`confirmPasswordEmail()`、`sendEmailCode()`、`verifyEmailCode()`、`verifyPhoneCode()`、`register()` 和 `submitCredentials()` 会直接向 `onLog` 发出 `started`、`completed` 或 `failed` 事件。日志不做脱敏,记录真实输入、响应和序列化后的错误;调用方需要自行控制日志文件的访问权限和生命周期。
|
|
177
517
|
|
|
178
518
|
## 两步验证密码
|