mytglib 2.0.3 → 2.0.5

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 CHANGED
@@ -177,7 +177,7 @@ const response = await core.sendCode({
177
177
  });
178
178
  ```
179
179
 
180
- 传入的五个能力字段必须是 boolean;Android 的 `allowAppHash` 以及 iOS 的 APNs `token`、`appSandbox` 仍由平台配置自动生成,不属于可覆盖字段。根据 [mtcute 0.32.0 `sendCode` API](https://ref.mtcute.dev/funcs/_mtcute_core.highlevel_methods.sendCode.html),future auth tokens 的参数类型是 `Uint8Array[]`;本库直接构造 raw `codeSettings`,因此对应的 `logoutTokens` 使用原生 JavaScript 数组,并在请求对象中固定保留空数组 `[]`,而不是 TL JSON 包装对象或字符串数组。mtcute 0.32.0 只会序列化非空的 `logoutTokens`,因此空数组不会设置可选 TL vector flag。
180
+ 传入的五个能力字段必须是 boolean;Android 的 `allowAppHash` 以及 iOS 的 APNs `token`、`appSandbox` 仍由平台配置自动生成,不属于可覆盖字段。根据 [mtcute 0.32.1 `sendCode` API](https://ref.mtcute.dev/funcs/_mtcute_core.highlevel_methods.sendCode.html),future auth tokens 的参数类型是 `Uint8Array[]`;本库直接构造 raw `codeSettings`,因此对应的 `logoutTokens` 使用原生 JavaScript 数组,并在请求对象中固定保留空数组 `[]`,而不是 TL JSON 包装对象或字符串数组。mtcute 0.32.1 只会序列化非空的 `logoutTokens`,因此空数组不会设置可选 TL vector flag。
181
181
 
182
182
  `deviceTokenResolver` 和 `recaptchaMobileTokenResolver` 必须返回包含非空字符串 `token` 字段的对象,对象可保留求解服务返回的 `errorMessage`、`message` 等附加字段。无有效 token 时会抛出 `TypeError`,其 `cause` 是 resolver 返回的完整结果;reCAPTCHA token 最长为 16,384 个字符。
183
183
 
@@ -203,7 +203,7 @@ const updates = await core.submitCredentials({
203
203
 
204
204
  `timeout`、`maxRetryCount` 和 `abortSignal` 是所有 mytglib 登录 RPC 的统一调用参数。`timeout` 默认为 `30000`,必须是正安全整数毫秒;`maxRetryCount` 默认为 `0`,必须是非负安全整数;`abortSignal` 默认为 `undefined`,传入时必须是 `AbortSignal`。客户端创建后会通过原始 `TelegramClient.withParams()` 生成 `core.client`,登录流程和通过 `core.client.call()` 发出的请求都会使用这些参数。密码更新的最终 `account.updatePasswordSettings` 会把 `maxRetryCount` 强制为 `0`,避免 internal/flood retry middleware 重放;前置的只读 `account.getPassword` 仍使用客户端配置。它们只作用于 RPC,不处理 `connect()` 超时,也不会取消正在进行的 `connect()`。
205
205
 
206
- `sessionDirPath` 是必传的会话主目录。路径已存在时必须是目录;路径不存在时会在初始化过程中递归创建。`init()` 会按照 `<主目录>/<+手机号>/<+手机号>.session` 创建 SQLite 会话文件,然后依次解析设备 token 并连接客户端。连接成功后可通过 `core.sessionFilePath` 直接取得当前客户端使用的 SQLite 文件路径;连接前该属性为 `null`。登录相关公共异步方法均返回原始 Telegram TL 响应。`core.rawClient` 保存原始 `TelegramClient`,只负责 `connect()`、`notifyLoggedIn()` 和 `destroy()`;`core.client` 保存 `withParams()` 返回的 RPC Proxy。`core.destroy()` 始终销毁 `rawClient`,不会在 `@mtcute/node` 0.32.0 的包装对象上调用 `destroy()`,从而避免私有字段错误。无论成功或失败,都应在 `finally` 中调用 `core.destroy()`,永久关闭连接、定时器和存储资源。销毁成功后两个客户端引用都会恢复为 `null`,SQLite 文件路径仍保留在 `core.sessionFilePath` 上。
206
+ `sessionDirPath` 是必传的会话主目录。路径已存在时必须是目录;路径不存在时会在初始化过程中递归创建。`init()` 会按照 `<主目录>/<+手机号>/<+手机号>.session` 创建 SQLite 会话文件,然后依次解析设备 token 并连接客户端。连接成功后可通过 `core.sessionFilePath` 直接取得当前客户端使用的 SQLite 文件路径;连接前该属性为 `null`。登录相关公共异步方法均返回原始 Telegram TL 响应。`core.rawClient` 保存原始 `TelegramClient`,只负责 `connect()`、`notifyLoggedIn()` 和 `destroy()`;`core.client` 保存 `withParams()` 返回的 RPC Proxy。`core.destroy()` 始终销毁 `rawClient`,不会在 `@mtcute/node` 0.32.1 的包装对象上调用 `destroy()`,从而避免私有字段错误。无论成功或失败,都应在 `finally` 中调用 `core.destroy()`,永久关闭连接、定时器和存储资源。销毁成功后两个客户端引用都会恢复为 `null`,SQLite 文件路径仍保留在 `core.sessionFilePath` 上。
207
207
 
208
208
  初始化 `.session` 时,库会在同一个 SQLite 数据库内创建单行 `session_metadata` 表,并在授权成功后写入账号 Metadata;不会自动创建 JSON 文件或第二个数据库连接。每次初始化都会让 `session_file` 与当前会话路径一致;初始化配置与首次授权时间作为注册快照保留,`reg_time` 取最终产生授权的注册或登录方法入口调用时间。重新授权只刷新用户名、姓名、Premium 状态及非空运行态字段。`first_name` 和 `last_name` 在注册或登录授权成功后自动从 Telegram 用户信息同步,缺失时写入 `NULL`。`completion_type` 只有 `sign_up`(实际执行 `auth.signUp` 创建账号)和 `login`(已有账号完成授权)两种;缺失字段写入 `NULL`。最新 `phone_code_hash`、已验证登录邮箱、成功使用的 reCAPTCHA token 和两步验证密码会随运行生命周期同步更新。数据库写入失败时会按 `50ms`、`150ms`、`300ms` 额外重试三次;Telegram RPC 已成功但 Metadata 最终仍失败时,可调用 `await core.retrySaveSessionMetadata()` 重试当前全部待写操作。
209
209
 
@@ -236,6 +236,7 @@ const pool = await createPhoneStatusPool({
236
236
  databasePath: resolve(".data/phone-status-pool/pool.sqlite"),
237
237
  minWarmSlots: 10,
238
238
  retryWarmSlots: 200,
239
+ maxConcurrentChecks: 256,
239
240
  });
240
241
 
241
242
  const account = await pool.importAccount({
@@ -288,8 +289,21 @@ Pool 的 PRIMARY 策略硬上限固定为 `minWarmSlots * 5`,加上 `retryWarm
288
289
  事件循环压力评估限制。忙碌槽位超过当前连接数一半时,Pool 会在资源允许的情况下异步提高目标槽位;
289
290
  高峰过后,弹性槽位连续空闲 10 分钟才进入回收,并且每 30 秒最多回收一个,避免连接数瞬间震荡。
290
291
  进入 `PRESSURED` 时,Pool 会先回收 PRIMARY 弹性槽,并保留当前资源上限能够承载的基础 PRIMARY
291
- 和独立 RETRY 槽;若上限不足,则优先保证 PRIMARY,按剩余容量降低 RETRY 数。只有进入
292
- `CRITICAL` 时才暂停建连并回收全部 RETRY 和 PRIMARY 弹性槽。
292
+ 和独立 RETRY 槽;若上限不足,则优先保证 PRIMARY,按剩余容量降低 RETRY 数。
293
+ 单次事件循环尖峰只触发 `PRESSURED`,连续 3 次成功采集的严重样本才进入软 `CRITICAL`,连续 2 次健康样本恢复;采样失败会打断连续计数。
294
+ `resource.hardCritical` 区分内存/堆硬危险与事件循环软压力:软 `CRITICAL` 暂停建连、保留就绪主备槽并
295
+ 以 10% 检测预算继续服务,不因 PRIMARY 未达到配置目标而回收已有 RETRY;暂停预热期间有效目标按实际存量显示。
296
+ 硬危险立即拒绝新检测并逐步释放空闲连接,可低于配置的最小暖槽数。
297
+ 压力回收每秒最多启动一个,不中断正在使用的槽。`resource.pressureReasons` 报告具体压力原因,
298
+ `resource.capacityLimitingFactor` 单独报告连接容量瓶颈,避免把“目标未预热满”当成“资源未承压”。
299
+ 监控采样失败时停止新建连接、缩小检测预算,连续 3 次失败后拒绝新检测;运行期单次采样最多等待 3 秒,
300
+ 未完成的采样不会重复启动。`lastSuccessfulSampleAt` 用于识别陈旧数据。
301
+
302
+ `maxConcurrentChecks` 默认 `256`,限制未完成的不同手机号共享检测任务(包含等待 READY 的任务)。
303
+ `PRESSURED` 下预算为 50%,软 `CRITICAL` 为 10%,硬危险为 0;达到上限立即返回 `POOL_QUEUE_FULL`。
304
+ 缓存命中和相同手机号的跟随者不新增共享检测名额。HTTP 层仍需独立限制总请求数与每用户并发,
305
+ 防止大量跟随者占满 Web 进程。预热只排入最多两倍握手并发的连接任务,分别计算 PRIMARY/RETRY 缺口,主槽富余不会抵消重试槽缺口。
306
+ READY 槽按风险/最近使用时间由索引优先队列选择,不再逐请求排序全部连接。
293
307
  当总连接上限高于 `minWarmSlots` 时,Pool 会在这个上限内部为导入和待机账号自检保留 1 个临时
294
308
  管理连接位;该预留不会突破 PRIMARY 策略上限与 `retryWarmSlots` 之和。若资源上限已经低到不高于
295
309
  最小暖槽数,管理建连会返回 `POOL_BUSY`,优先保护正在提供服务的暖槽。
@@ -300,6 +314,7 @@ Pool 的 PRIMARY 策略硬上限固定为 `minWarmSlots * 5`,加上 `retryWarm
300
314
  const response = await pool.checkPhoneStatus({
301
315
  phone: "+12025550123",
302
316
  abortSignal: new AbortController().signal,
317
+ deadlineAt: Date.now() + 13_000, // 可选:调用方等待的绝对期限
303
318
  });
304
319
 
305
320
  console.log(response.status, response.waitTime, response.error);
@@ -345,6 +360,11 @@ Telegram 原始 `code = 420` 和错误文本。
345
360
  `abortSignal` 只取消当前调用方等待,并以 `OTHER_ERROR` 返回取消原因;它不会中断已经启动的
346
361
  single-flight RPC。即使当前只有一个等待者,后台检测仍可完成并写入明确结果缓存。调用开始前已经
347
362
  取消的 signal 会在读取缓存前返回,不会命中旧结果或启动新 RPC。
363
+ `deadlineAt` 同样只限制当前调用方等待,不会缩短同号码其他调用者共享任务的 14 秒内部预算。
364
+ 调用方离开后,内部任务仍占用 `maxConcurrentChecks` 名额直至完成。
365
+ 普通检测的统计增量每秒合并写入 SQLite,并在候选选择和关闭时刷新;异常退出可能丢失最后
366
+ 约一秒统计,账号冷却/隔离等调度状态仍立即持久化。统计写失败保留待写增量并报告后台错误。
367
+ 管理查询直接叠加待写统计、不强制刷新;仓储新增不会因无关统计刷新失败出现“已落库却抛错”,删除和冷却/隔离状态落盘也不受统计刷新阻断。候选选择仍要求统计刷新成功以保持排序;完整导入流程中的必要写入失败时仍会回滚导入。
348
368
 
349
369
  按照当前业务规则,`403 RECAPTCHA_CHECK_signup`(含 site key 后缀)直接判定为
350
370
  `PHONE_NUMBER_NO_OCCUPIED`。Pool 不求解、不重放 challenge;它会回收触发 challenge 的槽位并从库存
@@ -352,6 +372,15 @@ single-flight RPC。即使当前只有一个等待者,后台检测仍可完成
352
372
 
353
373
  ### 管理与运行状态
354
374
 
375
+ 大库存管理列表可调用 `await pool.listAccountsPage({ page: 1, pageSize: 20, phone: "", runtimeStatus: "READY" })`,
376
+ 返回 `{ accounts, total, page, pageSize }`。`pageSize` 为 1–100,页码越界时回到最后一页,筛选后的账号投影不读取
377
+ auth key 或 session metadata。原 `listAccounts()` 继续兼容。`getStatus()` 从轻量状态索引汇总,包含
378
+ `resource`、`roles`、`queues` 和 `admission`,不读取全量会话。
379
+
380
+ 本机 mock Telegram + 真实 SQLite 的容量测量见 [基准说明](docs/phone-status-pool-benchmark.md)。
381
+ 该基准只能验证调度开销与预算上限,真实吞吐还需在目标硬件、真实网络延迟和账号限流条件下测量。
382
+
383
+
355
384
  ```js
356
385
  const accounts = await pool.listAccounts();
357
386
  const detail = await pool.getAccount(account.accountId);