mytglib 2.0.1 → 2.0.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.
Files changed (3) hide show
  1. package/README.md +106 -43
  2. package/dist/index.js +1 -1
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -55,6 +55,20 @@ npm run example:login
55
55
 
56
56
  示例按服务端响应处理邮箱设置、邮箱验证码、手机验证码和新账号注册。reCAPTCHA 出现时会显示 `action` 与 `siteKey`,并等待输入外部移动端求解器返回的 token。
57
57
 
58
+ ### RegHelp 发码验收
59
+
60
+ `examples/reghelp-send-code.js` 默认读取 `examples/.env`,使用 `ios.API_CREDENTIALS`
61
+ 第一项(当前 API ID `8`)和手机号 `+19156326494` 发起一次真实的 `auth.sendCode`。
62
+ 可通过 `TELEGRAM_PHONE` 覆盖手机号,或同时通过 `TG_API_ID` 与 `TG_API_HASH` 选择数组中的其他凭据:
63
+
64
+ ```bash
65
+ npm run example:reghelp-send-code
66
+ ```
67
+
68
+ 该示例会完整输出 device token API、RegHelp 建任务与每次轮询、`invokeWithReCaptcha` 和最终
69
+ `auth.sentCode` 响应,并输出中间件从 Telegram challenge 暴露的全部字段。其中包含 device token、
70
+ rcmToken 和 `phoneCodeHash`。只应在可信的本地终端运行,且每次执行都会真实请求发送登录验证码。
71
+
58
72
  ## 基本 API
59
73
 
60
74
  下面是常见的短信验证码登录路径;邮箱设置、新账号注册等完整分支见 `examples/login.js`。
@@ -102,6 +116,29 @@ try {
102
116
  }
103
117
  ```
104
118
 
119
+ Android 和 iOS 分别通过公开且深冻结的 `android.API_CREDENTIALS` 与
120
+ `ios.API_CREDENTIALS` 列出可用的官方 API 凭据。Android 支持 ID 4、6,iOS 支持 ID 8、1;
121
+ 数组第一项是省略自定义参数时的默认值,仍分别为 Android API ID 4 和 iOS API ID 8。需要使用
122
+ 同平台的其他官方凭据时,保持 `clientType` 为 `"android"` 或 `"ios"`,并同时传入数组中同一项的
123
+ `apiId` 与 `apiHash`:
124
+
125
+ ```js
126
+ const iosCore = new CoreClient({
127
+ clientType: "ios",
128
+ apiId: 1,
129
+ apiHash: "b6b154c3707471f5339bd661645ed3d6",
130
+ phone: "+12025550123",
131
+ proxy: { type: "http", host: "127.0.0.1", port: 8080 },
132
+ sessionDirPath: resolve("telegram-sessions"),
133
+ deviceTokenResolver: async () => ({ token: "AQIDBA==" }),
134
+ });
135
+ ```
136
+
137
+ `apiId` 与 `apiHash` 必须同时省略或同时提供;显式提供时必须精确匹配所选平台公开数组中的同一对,
138
+ 不能交叉组合,也不会新增 `clientType`。选中的凭据会用于连接初始化和登录请求;首次授权新建 session
139
+ Metadata 时也会保存该凭据,之后 `syncSessionJson()` 从这份注册快照导出。重新打开已有 session 时,
140
+ 按既有契约保留首次写入的 API 凭据及其他初始化配置,不会用本次构造参数改写注册快照。
141
+
105
142
  iOS 默认使用 `ios.CURRENT_VERSION` 的最后一项。覆盖版本时仍需同时传入 `appVersion` 与
106
143
  `overrideLayer`,以兼容现有调用方;`appVersion` 会原样用于 Telegram `initConnection.appVersion`,
107
144
  `overrideLayer` 不会传给底层客户端。Node.js 的协议 layer 始终由 `@mtcute/node` 当前 codec 的
@@ -185,8 +222,9 @@ const jsonFilePath = await core.syncSessionJson();
185
222
  测试或删除账号,不需要手工启用、停用或扩缩容。
186
223
 
187
224
  Pool 使用消费者模式:外部请求只消费已经连接的 `READY` 槽位,不会在请求路径中临时连接
188
- Telegram。创建 Pool 和后续补槽均为异步操作,因此进程刚启动、账号刚导入或资源已达上限而没有
189
- `READY` 账号时,检测会立即返回 `OTHER_ERROR`,不会排队等待建连。
225
+ Telegram。创建 Pool 和后续补槽均为异步操作;检测可以在内部 deadline 内等待正在连接、正在被其他
226
+ 请求使用或正在异步补入的槽位,但不会同步建立临时账号连接。生产环境应在接入流量前完成基础暖槽,
227
+ 并为重试配置独立的 `RETRY` 暖槽。
190
228
 
191
229
  ### 初始化与导入
192
230
 
@@ -197,6 +235,7 @@ import { createPhoneStatusPool } from "mytglib";
197
235
  const pool = await createPhoneStatusPool({
198
236
  databasePath: resolve(".data/phone-status-pool/pool.sqlite"),
199
237
  minWarmSlots: 10,
238
+ retryWarmSlots: 200,
200
239
  });
201
240
 
202
241
  const account = await pool.importAccount({
@@ -213,9 +252,9 @@ console.log(account.accountId, account.runtimeStatus);
213
252
  自动识别。两套 schema 同时存在时:两侧都有效且授权状态完全一致才接受;两侧都有效但内容冲突
214
253
  则拒绝;只有一侧有效时使用有效侧;两侧都无有效授权时拒绝。会话文件与 JSON 必须属于同一个
215
254
  有效账号,导入时会严格核对 auth key、DC、账号身份和手机号。Pool 只接受 Telegram 官方移动端
216
- 凭据:Android 必须使用 `app_id: 4` 及其官方 `app_hash`,iOS 必须使用 `app_id: 8` 及其官方
217
- `app_hash`;TDesktop、自定义 API ID 或平台与 API 凭据不匹配的 JSON 会在建立 Telegram 连接前
218
- 直接拒绝。当前不支持代理。
255
+ 凭据:Android 必须精确匹配 `android.API_CREDENTIALS` 中的 ID 4 或 6 凭据对,iOS 必须精确匹配
256
+ `ios.API_CREDENTIALS` 中的 ID 8 或 1 凭据对;TDesktop、自定义 API ID、交叉组合或平台与
257
+ API 凭据不匹配的 JSON 会在建立 Telegram 连接前直接拒绝。当前不支持代理。
219
258
 
220
259
  为兼容常见会话导出格式,JSON 的 `session_file` 只能写真实文件名
221
260
  `account.session` 或同名 stem `account`,不得携带目录组件;导入后会统一规整为真实文件名。
@@ -239,12 +278,21 @@ SQLite 与 StringSession 中的 endpoint 只用于交叉校验;实际创建 Po
239
278
  例如导入 500 个账号且设置 `minWarmSlots: 100` 时,Pool 只会先预热目标槽位,
240
279
  剩余账号作为持久化库存待命,不会把 500 个账号同时上线。
241
280
 
242
- Pool 的策略硬上限固定为 `minWarmSlots * 5`,实际连接上限还会受进程 CPU、内存、文件描述符和
281
+ `retryWarmSlots` 是专门服务账号级失败重试的独立暖槽数,默认值为 `0`,生产环境可按流量单独配置
282
+ (首轮建议 `200`,不把比例写死在代码中)。首次检测只使用 `PRIMARY` 槽;账号错误、
283
+ `FLOOD_WAIT` 或 RPC 超时等需要换账号时,只使用 `RETRY` 槽,普通 `PRIMARY` 槽不会被重试借用。
284
+ `maxAccountsPerPhone` 限制一次手机号检测最多使用的不同账号数,默认值为 `8`,可配置上限为 `16`,
285
+ 用于避免单个手机号占满重试区。
286
+
287
+ Pool 的 PRIMARY 策略硬上限固定为 `minWarmSlots * 5`,加上 `retryWarmSlots` 后形成总策略上限,实际连接上限还会受进程 CPU、内存、文件描述符和
243
288
  事件循环压力评估限制。忙碌槽位超过当前连接数一半时,Pool 会在资源允许的情况下异步提高目标槽位;
244
289
  高峰过后,弹性槽位连续空闲 10 分钟才进入回收,并且每 30 秒最多回收一个,避免连接数瞬间震荡。
290
+ 进入 `PRESSURED` 时,Pool 会先回收 PRIMARY 弹性槽,并保留当前资源上限能够承载的基础 PRIMARY
291
+ 和独立 RETRY 槽;若上限不足,则优先保证 PRIMARY,按剩余容量降低 RETRY 数。只有进入
292
+ `CRITICAL` 时才暂停建连并回收全部 RETRY 和 PRIMARY 弹性槽。
245
293
  当总连接上限高于 `minWarmSlots` 时,Pool 会在这个上限内部为导入和待机账号自检保留 1 个临时
246
- 管理连接位;该预留不会突破 `minWarmSlots * 5`。若资源上限已经低到不高于最小暖槽数,管理建连
247
- 会返回 `POOL_BUSY`,优先保护正在提供服务的暖槽。
294
+ 管理连接位;该预留不会突破 PRIMARY 策略上限与 `retryWarmSlots` 之和。若资源上限已经低到不高于
295
+ 最小暖槽数,管理建连会返回 `POOL_BUSY`,优先保护正在提供服务的暖槽。
248
296
 
249
297
  ### 检测与返回值
250
298
 
@@ -277,12 +325,17 @@ console.log(response.status, response.waitTime, response.error);
277
325
  未映射到上述业务状态的检测异常会被捕获并以 `OTHER_ERROR` 正常返回,`error` 仅保留可用的 `message`、`name`、
278
326
  `code`、`text` 和 `seconds`。
279
327
 
280
- 单次检测的所有尝试共享固定的 10 秒总期限。暖槽 RPC 触发 `FLOOD_WAIT`,或者槽位错误最终
281
- 会映射为 `OTHER_ERROR` 时,当前请求最多在重试时已处于 `READY` 的其他暖槽上重试三次,
282
- 即最多尝试四个暖槽;请求不会等待异步补槽。重试耗尽、没有更多 `READY` 暖槽或总期限耗尽时,
283
- 返回最后一次已实际发生的暖槽错误;如果尚未发生任何暖槽错误就无可用槽位,则直接返回 Pool busy。
284
- 触发 `FLOOD_WAIT` 的账号会按 Telegram 返回的等待秒数退出暖槽并进入冷却,
285
- Pool 随即从库存异步补入其他账号。
328
+ 外部 HTTP 接口的端到端目标是 15 秒;Pool 内部为输入、排队、重试和清理预留固定的 14 秒共享
329
+ 检测 deadline。每次 Telegram 检测 RPC 单独使用最多约 3 秒的局部 timeout,且关闭 mtcute 自带的该请求
330
+ 重试。第一次使用 `PRIMARY` 槽;账号级错误、`FLOOD_WAIT` 或 RPC 超时后,后续尝试只从已经
331
+ `READY` 或正在异步补入的 `RETRY` 槽获取。后续尝试次数由剩余 `RETRY` 资源、单手机号账号上限和
332
+ 14 秒 deadline 共同限制;资源允许时,仍可继续使用剩余重试暖槽兜底。
333
+
334
+ 请求会在 deadline 内等待正在使用、正在连接或正在补槽的对应角色槽位,但不会在请求路径同步重启账号。
335
+ 没有任何未使用的合格账号、重试暖槽耗尽或 deadline 到期时,返回最后一次已经实际发生的暖槽错误;
336
+ 如果还没有发生暖槽错误,则返回 `OTHER_ERROR`,并通过 `error.code` 区分 `POOL_RETRY_TIMEOUT`、
337
+ `POOL_QUEUE_FULL` 或 `POOL_RESOURCE_CRITICAL` 等 Pool 原因。触发 `FLOOD_WAIT` 的账号会按 Telegram
338
+ 返回的等待秒数退出暖槽并进入冷却,Pool 随即从库存异步补入其他账号。
286
339
  若暖槽返回冻结账号错误,Pool 会将该账号永久隔离并换用其他 `READY` 槽位;当前检测返回的错误仍保留
287
340
  Telegram 原始 `code = 420` 和错误文本。
288
341
  `PHONE_NUMBER_FLOOD` 是目标手机号级错误,不换槽重试,只保护目标手机号 5 分钟且不处罚账号。
@@ -319,6 +372,9 @@ const removed = await pool.removeAccount(account.accountId);
319
372
  `BUSY`;触发 flood 的账号进入 `COOLDOWN`,认证失效等致命错误会进入 `QUARANTINED`。
320
373
  `testAccount()` 是由管理员主动触发的账号健康自检,不会拿这个账号去检测某个外部手机号,也不会由
321
374
  协调器定时自动执行。`QUARANTINED` 是终态;自检不会把已隔离账号恢复为 `ACTIVE` 或重新加入调度。
375
+ 单账号自检失败时会直接拒绝 Promise:Telegram RPC 错误保留具体的 `message`、数值 `code`、`text` 和
376
+ `seconds` 字段;Pool 自身错误仍使用 `PhoneStatusPoolError` 的字符串 `code`。`testAccounts()` 继续将
377
+ 单账号错误收敛到批量报告的 `{ code, message }`,不改变批量接口契约。
322
378
  管理接口返回完整 `phone` 和 ISO 格式或 `null` 的 `cooldownUntil`,但不会包含 auth key、原始
323
379
  Metadata、session、Telegram 冻结时间或申诉地址等内部探测字段。
324
380
 
@@ -346,9 +402,10 @@ Metadata、session、Telegram 冻结时间或申诉地址等内部探测字段
346
402
  `PoolAccount` 或 `null`,`error` 为 `null` 或 `{ code: string | null, message: string }`。
347
403
  取消信号会停止批次并直接拒绝当前调用,不会把取消记成账号失败或改变账号健康状态。
348
404
 
349
- `getStatus()` 可用于健康检查和后台展示,包含生命周期、账号库存、各运行态槽位数、当前目标、
350
- 策略上限、资源上限、资源压力和建连并发等信息。若业务需要等待初始暖槽,可以在 API 对外接流量前
351
- 轮询 `status.runtime.ready`;检测方法自身始终不会等待预热。
405
+ `getStatus()` 可用于健康检查和后台展示,包含生命周期、账号库存、各运行态槽位数、PRIMARY/RETRY
406
+ 角色槽位、对应等待队列、当前目标、策略上限、资源上限、资源压力和建连并发等信息。若业务需要等待
407
+ 初始暖槽,可以在 API 对外接流量前轮询 `status.runtime.ready`;检测方法只在共享 deadline 内等待已安排的
408
+ 槽位,不会同步创建连接。
352
409
 
353
410
  ### 部署与关闭
354
411
 
@@ -404,14 +461,21 @@ Telegram 客户端、定时器、缓存、SQLite 连接和 owner lock。关闭
404
461
  ### Rust API
405
462
 
406
463
  Rust 实现从 `grammers_core` 根模块导出 `create_phone_status_pool()`、`PhoneStatusPool` 及相关
407
- 输入、响应和状态类型。调度、持久化格式和暖槽策略与 Node.js 实现一致。Rust Pool 仍使用
408
- `RESULT`/`BUSY` 调度响应。目标手机号格式无效属于明确检测结果,返回
409
- `Ok(PhoneStatusCheckResponse::Result { result: PhoneNumberInvalid, .. })`;没有 `READY` 账号、检测
410
- 期限耗尽、目标号码触发 `PHONE_NUMBER_FLOOD` 或检测基础设施失败均返回
411
- `Ok(PhoneStatusCheckResponse::Busy { .. })`。创建和管理操作的无效输入、调用取消、Pool
412
- 生命周期错误或内部协调错误才通过 `PhoneStatusPoolError` 返回,不应转换成业务 `BUSY`。
413
- Rust Pool 同样在共享的 10 秒总期限内对 `FLOOD_WAIT` 和暖槽 RPC 其他错误最多换槽重试三次,
414
- 无 `READY` 槽位、期限耗尽或 `PHONE_NUMBER_FLOOD` 不会继续换槽。
464
+ 输入、响应和状态类型。调度、持久化格式和暖槽策略与 Node.js 实现一致。手机号检测统一返回
465
+ 扁平的 `PhoneStatusCheckResponse`,字段为 `wait_time`、`phone`、`started_at`、`ended_at`、
466
+ `duration_ms`、`status` 和 `error`;serde 序列化后与 Node.js 的 camelCase 对齐。没有可用资源、
467
+ 排队超时或资源压力不会返回旧的 `BUSY` 变体,而是返回
468
+ `status = PhoneStatusPoolResult::OtherError`,序列化值为 `OTHER_ERROR`,并在 `error.code` 中区分
469
+ `POOL_RETRY_TIMEOUT`、`POOL_QUEUE_FULL` 或 `POOL_RESOURCE_CRITICAL`。创建和管理操作的
470
+ 无效输入、调用取消、Pool 生命周期错误或内部协调错误仍通过 `PhoneStatusPoolError` 返回。
471
+ `test_account()` 的单账号失败会把 Telegram RPC 的具体文本写入 `PhoneStatusPoolError` 的 `message()`;
472
+ 需要与 Node.js 对齐的结构化字段时调用 `phone_status_error()`,可取得 `message`、RPC 数值 `code`、
473
+ `text` 和 `seconds`。`PhoneStatusPoolError::code()` 仍保留 Pool 层分类,批量 `test_accounts()` 的错误
474
+ 报告契约不变。
475
+ Rust Pool 使用 14 秒共享检测 deadline,每次 Telegram 检测 RPC 最多约 3 秒;首次只用 `PRIMARY`
476
+ 暖槽,账号级失败后的重试只用隔离的 `RETRY` 暖槽。后续尝试由剩余 `RETRY` 资源、
477
+ `max_accounts_per_phone` 和 deadline 共同限制;`PHONE_NUMBER_FLOOD` 按手机号保护,
478
+ 不换账号。
415
479
  Rust 与 Node.js 采用相同的冻结账号规则:导入时探测到 `FROZEN` 或 `UNKNOWN` 不落库,运行中发现冻结
416
480
  则进入终态 `QUARANTINED`。AppConfig 主动识别冻结返回 `ACCOUNT_INVALID`;Telegram RPC 真实返回的
417
481
  `FROZEN_*` 错误仍在错误 source 链中保留 `code = 420`,不会改写成 401。公共账号结构不暴露冻结
@@ -421,13 +485,15 @@ Rust 字段名使用 snake_case,通过 serde 序列化时使用 camelCase。
421
485
 
422
486
  ```rust,no_run
423
487
  use grammers_core::{
424
- CheckPhoneStatusInput, CloseOptions, ImportAccountInput, PhoneStatusCheckResponse,
425
- PhoneStatusPoolError, PhoneStatusPoolOptions, TestAccountsInput, create_phone_status_pool,
488
+ CheckPhoneStatusInput, CloseOptions, ImportAccountInput, PhoneStatusPoolError,
489
+ PhoneStatusPoolOptions, TestAccountsInput, create_phone_status_pool,
426
490
  };
427
491
 
428
492
  # async fn example() -> Result<(), PhoneStatusPoolError> {
429
493
  let mut options = PhoneStatusPoolOptions::new("/secure/phone-status-pool/pool.sqlite");
430
494
  options.min_warm_slots = 10;
495
+ options.retry_warm_slots = 200;
496
+ options.max_accounts_per_phone = 8;
431
497
  let pool = create_phone_status_pool(options).await?;
432
498
 
433
499
  // 即使任一管理或检测步骤失败,下面仍会执行安全关闭。
@@ -439,20 +505,13 @@ use grammers_core::{
439
505
  ))
440
506
  .await?;
441
507
 
442
- match pool
508
+ let response = pool
443
509
  .check_phone_status(CheckPhoneStatusInput::new("+12025550123"))
444
- .await?
445
- {
446
- PhoneStatusCheckResponse::Result {
447
- result,
448
- checked_at,
449
- cached,
450
- ..
451
- } => println!("{result:?} {checked_at} cached={cached}"),
452
- PhoneStatusCheckResponse::Busy {
453
- retry_after_seconds,
454
- } => println!("BUSY retryAfterSeconds={retry_after_seconds}"),
455
- }
510
+ .await?;
511
+ println!(
512
+ "status={:?} waitTime={} durationMs={} error={:?}",
513
+ response.status, response.wait_time, response.duration_ms, response.error,
514
+ );
456
515
 
457
516
  let accounts = pool.list_accounts().await?;
458
517
  let detail = pool.get_account(&account.account_id).await?;
@@ -481,7 +540,8 @@ use grammers_core::{
481
540
  # }
482
541
  ```
483
542
 
484
- `PhoneStatusPoolOptions::new()` 要求绝对数据库路径,`min_warm_slots` 省略时为 `10`。创建方法只
543
+ `PhoneStatusPoolOptions::new()` 要求绝对数据库路径,`min_warm_slots` 省略时为 `10`,
544
+ `retry_warm_slots` 默认为 `0`,`max_accounts_per_phone` 默认为 `8` 且最大为 `16`。创建方法只
485
545
  打开本地状态并启动异步预热,不等待 Telegram 暖槽连接完成;可在接入业务流量前轮询
486
546
  `pool.get_status().await?.runtime.ready`。同一个数据库仍只能由一个 Pool 实例持有,进程退出前应
487
547
  始终调用 `pool.close(CloseOptions::default()).await`。如果强制关闭宽限期结束时仍有后台清理,
@@ -667,8 +727,11 @@ console.log(result);
667
727
 
668
728
  `vendor/grammers` 是为后续 Tauri/Rust 桌面端准备的内置 fork,其中项目自有的
669
729
  `grammers-core` 承载 `src` 对应的 Rust 实现。它目前尚未接入本 npm 包的 JavaScript 运行路径,
670
- 也不会进入 npm 发布包。Rust `CoreClient` 在 backend 连接初始化尚未完成、没有业务 transport frame
671
- 写出时,若初始化明确超时会自动重新创建一次 backend;该限定重试独立于 RPC 的 `max_retry_count`。
730
+ 也不会进入 npm 发布包。crate 根模块公开 `ApiCredentials`、`android::API_CREDENTIALS` 和
731
+ `ios::API_CREDENTIALS`;`CoreClientConfig.api_id` 与 `api_hash` 同时省略时使用对应数组第一项,同时
732
+ 提供时必须精确匹配同平台凭据对。Rust `CoreClient` 在 backend 连接初始化尚未完成、没有业务
733
+ transport frame 写出时,若初始化明确超时会自动重新创建一次 backend;该限定重试独立于 RPC 的
734
+ `max_retry_count`。
672
735
  上游基线、本地覆盖层、
673
736
  可重复的同步流程和验证范围见
674
737
  [grammers fork 维护文档](docs/grammers-fork.md)。