itd-api 0.10.0 → 0.10.1
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/dist/events/index.cjs +2 -2
- package/dist/events/index.d.cts +2 -2
- package/dist/events/index.d.ts +2 -2
- package/dist/events/index.js +2 -2
- package/dist/index.cjs +105 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +160 -132
- package/dist/index.d.ts +160 -132
- package/dist/index.js +105 -58
- package/dist/index.js.map +1 -1
- package/dist/rest/index.cjs +2 -2
- package/dist/rest/index.d.cts +2 -2
- package/dist/rest/index.d.ts +2 -2
- package/dist/rest/index.js +2 -2
- package/dist/shared/{events-BNGjJVI3.d.cts → events-CZXU16qU.d.cts} +2 -2
- package/dist/shared/{events-B0Wtn4cF.js → events-Cipe-Fm9.js} +2 -2
- package/dist/shared/{events-B0Wtn4cF.js.map → events-Cipe-Fm9.js.map} +1 -1
- package/dist/shared/{events-CJO6q93Z.d.ts → events-D3CSlpXR.d.ts} +2 -2
- package/dist/shared/{events-BB791aPk.cjs → events-MtRNe_TF.cjs} +2 -2
- package/dist/shared/{events-BB791aPk.cjs.map → events-MtRNe_TF.cjs.map} +1 -1
- package/dist/shared/{render-DC5nvqcP.d.ts → render-Cs6AOXbD.d.ts} +2 -2
- package/dist/shared/{render-CkhvH_yh.js → render-CubTTof_.js} +2 -2
- package/dist/shared/{render-CkhvH_yh.js.map → render-CubTTof_.js.map} +1 -1
- package/dist/shared/{render-C1A57W-5.d.cts → render-bZwOk3CC.d.cts} +2 -2
- package/dist/shared/{render-BimHjOhw.cjs → render-dscKp9CC.cjs} +2 -2
- package/dist/shared/{render-BimHjOhw.cjs.map → render-dscKp9CC.cjs.map} +1 -1
- package/dist/shared/{url-BA5hEwj2.js → url-CntTEAxu.js} +4 -4
- package/dist/shared/{url-BA5hEwj2.js.map → url-CntTEAxu.js.map} +1 -1
- package/dist/shared/{url-CDVgMIq5.cjs → url-DgTYZN_v.cjs} +4 -4
- package/dist/shared/{url-CDVgMIq5.cjs.map → url-DgTYZN_v.cjs.map} +1 -1
- package/dist/shared/{url-DkMueFAT.d.cts → url-Dum7r_FT.d.cts} +4 -4
- package/dist/shared/{url-DkMueFAT.d.ts → url-Dum7r_FT.d.ts} +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { A as ViewReason, B as BUCKET_LIMITS, C as ItdErrorCode, D as ReportTargetType, E as ReportReason, F as defineBuiltInOperation, G as pickArray, H as createClientRuntime, I as isBuiltInOperationId, L as operationBucket, M as WallAccess, N as ITD_CATALOG, O as ServiceState, P as OPERATIONS, R as operationMethod, S as InteractionType, T as NotificationType, U as createApiError, V as DEFAULT_RATE_LIMIT_BUCKET, X as RetrySafety, Y as pickString, _ as CaptchaType, at as orderPluginDefinitions, b as FeedTab, c as normalizeNotification, ct as STATUS_SERVICE, d as canonicalNotificationType, dt as isRecord, et as createRequestAbortScope, f as isKnownNotificationType, ft as requireOptionalBoolean, g as CaptchaField, gt as installAsyncDisposeFallback, h as CaptchaChoice, ht as systemClock, it as assertPluginRemovable, j as ViewSource, k as SpanType, lt as resolveRateLimit, m as AttachmentType, n as formatNotificationText, o as passthroughOperation, ot as DEFAULT_BASE_URL, p as AccessType, pt as RateLimitPacing, rt as RequestQueuePool, s as voidOperation, t as resolveNotificationUrl, tt as requestAbortError, ut as LIBRARY_VERSION, v as CommentSort, w as LikesVisibility, x as IncidentKind, y as EventChannelStatus, z as operationRetrySafety } from "./shared/url-
|
|
1
|
+
import { A as ViewReason, B as BUCKET_LIMITS, C as ItdErrorCode, D as ReportTargetType, E as ReportReason, F as defineBuiltInOperation, G as pickArray, H as createClientRuntime, I as isBuiltInOperationId, L as operationBucket, M as WallAccess, N as ITD_CATALOG, O as ServiceState, P as OPERATIONS, R as operationMethod, S as InteractionType, T as NotificationType, U as createApiError, V as DEFAULT_RATE_LIMIT_BUCKET, X as RetrySafety, Y as pickString, _ as CaptchaType, at as orderPluginDefinitions, b as FeedTab, c as normalizeNotification, ct as STATUS_SERVICE, d as canonicalNotificationType, dt as isRecord, et as createRequestAbortScope, f as isKnownNotificationType, ft as requireOptionalBoolean, g as CaptchaField, gt as installAsyncDisposeFallback, h as CaptchaChoice, ht as systemClock, it as assertPluginRemovable, j as ViewSource, k as SpanType, lt as resolveRateLimit, m as AttachmentType, n as formatNotificationText, o as passthroughOperation, ot as DEFAULT_BASE_URL, p as AccessType, pt as RateLimitPacing, rt as RequestQueuePool, s as voidOperation, t as resolveNotificationUrl, tt as requestAbortError, ut as LIBRARY_VERSION, v as CommentSort, w as LikesVisibility, x as IncidentKind, y as EventChannelStatus, z as operationRetrySafety } from "./shared/url-CntTEAxu.js";
|
|
2
2
|
import { A as isItdStateError, C as isItdError, D as isItdPhoneVerificationError, E as isItdNotFoundError, O as isItdRateLimitError, S as isItdConflictError, T as isItdForbiddenError, _ as ItdStateError, a as ItdConfigError, b as isItdApiError, c as ItdErrorKind, d as ItdForbiddenError, f as ItdNetworkError, g as ItdServerError, h as ItdRateLimitError, i as ItdAuthError, j as isItdValidationError, k as isItdServerError, l as ItdFileError, m as ItdPhoneVerificationError, n as ItdApiError, o as ItdConflictError, p as ItdNotFoundError, r as ItdApiErrorKind, s as ItdError, t as ItdAbortError, u as ItdFileErrorReason, v as ItdTimeoutError, w as isItdFileError, x as isItdAuthError, y as ItdValidationError } from "./shared/errors-GI10kZxk.js";
|
|
3
3
|
import { c as REFRESH_COOKIE, h as joinUrl, l as REFRESH_COOKIE_PATH, s as CookieJar, u as isSerializedCookieEntry, v as RuntimeMode, y as createDeviceId } from "./shared/redact-DJV0XM3n.js";
|
|
4
|
-
import { A as utcStampToIso, C as AUDIO_MIME_TYPES, D as isBuilder, E as comment, F as mapPage, I as createFeatureHost, L as ExtensibleOperationCatalog, M as BaseResource, N as PaginationMode, O as createStatusFeature, P as Paginator, S as ALLOWED_MIME_TYPES, T as VIDEO_MIME_TYPES, _ as poll, a as createShopOrderAccessStorage, b as parseHtml, c as ShopOrderStatus, d as isMyProfile, f as fromStream, g as post, h as report, i as MemoryShopOrderAccessStorage, j as NotificationsResource, k as toDate, l as ShopProductCategory, m as createResources, n as renderSpans, o as statusDays, p as fromUrl, r as createShopFeature, s as ShopConsentKind, t as SpanRenderFormat, u as ShopProductStatus, v as autoSpans, w as IMAGE_MIME_TYPES, x as parseMarkdown, y as markup } from "./shared/render-
|
|
4
|
+
import { A as utcStampToIso, C as AUDIO_MIME_TYPES, D as isBuilder, E as comment, F as mapPage, I as createFeatureHost, L as ExtensibleOperationCatalog, M as BaseResource, N as PaginationMode, O as createStatusFeature, P as Paginator, S as ALLOWED_MIME_TYPES, T as VIDEO_MIME_TYPES, _ as poll, a as createShopOrderAccessStorage, b as parseHtml, c as ShopOrderStatus, d as isMyProfile, f as fromStream, g as post, h as report, i as MemoryShopOrderAccessStorage, j as NotificationsResource, k as toDate, l as ShopProductCategory, m as createResources, n as renderSpans, o as statusDays, p as fromUrl, r as createShopFeature, s as ShopConsentKind, t as SpanRenderFormat, u as ShopProductStatus, v as autoSpans, w as IMAGE_MIME_TYPES, x as parseMarkdown, y as markup } from "./shared/render-CubTTof_.js";
|
|
5
5
|
import { c as FileTransferMode } from "./shared/options-i4LrKfqx.js";
|
|
6
|
-
import { _ as Emitter, a as WebSocketTransport, c as NotificationEventsTransport, d as NotificationUpdateOrigin, f as NotificationUpdateType, g as runEventMiddleware, h as UnauthorizedStreamError, i as createNotificationEventsClient, l as resolveNotificationEventsOptions, m as EventChannel, n as EventRouter, o as EVENT_OPERATIONS, p as createParser, r as NotificationEventsClient, s as NotificationEvents, t as EventComposer, u as setNotificationEventsConnectGuard, v as reportListenerError } from "./shared/events-
|
|
6
|
+
import { _ as Emitter, a as WebSocketTransport, c as NotificationEventsTransport, d as NotificationUpdateOrigin, f as NotificationUpdateType, g as runEventMiddleware, h as UnauthorizedStreamError, i as createNotificationEventsClient, l as resolveNotificationEventsOptions, m as EventChannel, n as EventRouter, o as EVENT_OPERATIONS, p as createParser, r as NotificationEventsClient, s as NotificationEvents, t as EventComposer, u as setNotificationEventsConnectGuard, v as reportListenerError } from "./shared/events-Cipe-Fm9.js";
|
|
7
7
|
import { a as isEnumerableKeyValueStore, i as createRecordKeyValueStore, o as withCodec, r as createKeyValueStore, s as withNamespace, t as MemoryKeyValueStore } from "./shared/key-value-store-Bt2PSMxY.js";
|
|
8
8
|
import { n as copySession, r as createTokenStorage, t as MemoryTokenStorage } from "./shared/storage-DiDe0PZM.js";
|
|
9
9
|
import { a as scopedTokenStorage, i as isRestorableSession, n as controlledTokenStorage, r as createMultiTokenStorage, t as MemoryMultiTokenStorage } from "./shared/multi-storage-C3Ngtop1.js";
|
|
@@ -91,21 +91,36 @@ function requireCaptchaText(value, field) {
|
|
|
91
91
|
if (value !== void 0 && (typeof value !== "string" || value.trim() === "")) throw new ItdConfigError(`${field} должен быть непустой строкой`);
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
|
-
* Проверяет форму
|
|
94
|
+
* Проверяет форму источника токена и приводит его к объекту.
|
|
95
95
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
96
|
+
* Сам объект сохраняется как есть: `getToken` вызывается на нём, чтобы источнику остался
|
|
97
|
+
* доступен собственный `this`.
|
|
98
98
|
*
|
|
99
99
|
* @throws {ItdConfigError} при некорректных значениях
|
|
100
100
|
*/
|
|
101
|
-
function
|
|
101
|
+
function resolveCaptchaSolver(captcha) {
|
|
102
|
+
if (captcha === void 0) return void 0;
|
|
103
|
+
if (typeof captcha === "function") return { getToken: captcha };
|
|
104
|
+
if (!isRecord(captcha)) throw new ItdConfigError("captcha должен быть функцией либо объектом с getToken");
|
|
105
|
+
const { getToken, type, field } = captcha;
|
|
106
|
+
if (typeof getToken !== "function") throw new ItdConfigError("captcha.getToken должен быть функцией");
|
|
107
|
+
requireCaptchaText(type, "captcha.type");
|
|
108
|
+
requireCaptchaText(field, "captcha.field");
|
|
109
|
+
return captcha;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Проверяет форму готового токена капчи.
|
|
113
|
+
*
|
|
114
|
+
* @param path имя опции или аргумента для текста ошибки
|
|
115
|
+
* @throws {ItdConfigError} если токен или его тип не указаны
|
|
116
|
+
*/
|
|
117
|
+
function validateCaptchaToken(captcha, path) {
|
|
102
118
|
if (captcha === void 0) return;
|
|
103
|
-
if (!isRecord(captcha)) throw new ItdConfigError(
|
|
104
|
-
const { type, token,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
requireCaptchaText(field,
|
|
108
|
-
if (getToken !== void 0 && typeof getToken !== "function") throw new ItdConfigError("auth.captcha.getToken должен быть функцией");
|
|
119
|
+
if (!isRecord(captcha)) throw new ItdConfigError(`${path} должен быть объектом { type, token }`);
|
|
120
|
+
const { type, token, field } = captcha;
|
|
121
|
+
if (typeof token !== "string" || token.trim() === "") throw new ItdConfigError(`${path}.token должен быть непустой строкой`);
|
|
122
|
+
if (typeof type !== "string" || type.trim() === "") throw new ItdConfigError(`${path}.type должен быть непустой строкой`);
|
|
123
|
+
requireCaptchaText(field, `${path}.field`);
|
|
109
124
|
}
|
|
110
125
|
/** Разбирает ответ `GET /api/v1/auth/captcha/provider` для автоматического входа. */
|
|
111
126
|
function readCaptchaProvider(body) {
|
|
@@ -122,43 +137,40 @@ function readCaptchaProvider(body) {
|
|
|
122
137
|
function resolveField(type, configured) {
|
|
123
138
|
const field = configured ?? CAPTCHA_FIELDS[type];
|
|
124
139
|
if (field) return field;
|
|
125
|
-
throw new ItdConfigError(`Неизвестно, в каком поле сервер ждёт токен капчи «${type}». Передайте
|
|
140
|
+
throw new ItdConfigError(`Неизвестно, в каком поле сервер ждёт токен капчи «${type}». Передайте captcha.field либо оставьте captcha.type = ${CaptchaChoice.Auto}, чтобы поле назвал сервер.`);
|
|
126
141
|
}
|
|
127
|
-
/** Спрашивает
|
|
128
|
-
async function
|
|
129
|
-
const token = await
|
|
130
|
-
if (token) return token;
|
|
131
|
-
throw new ItdConfigError(`Источник капчи не вернул токен для провайдера «${type}». Проверьте
|
|
142
|
+
/** Спрашивает токен у источника и убеждается, что он непустой. */
|
|
143
|
+
async function requireSolvedToken(solver, type) {
|
|
144
|
+
const token = await solver.getToken(type);
|
|
145
|
+
if (typeof token === "string" && token.trim() !== "") return token;
|
|
146
|
+
throw new ItdConfigError(`Источник капчи не вернул токен для провайдера «${type}». Проверьте captcha.getToken.`);
|
|
132
147
|
}
|
|
133
148
|
/**
|
|
134
|
-
* Готовит фрагмент тела
|
|
149
|
+
* Готовит фрагмент тела запроса, взяв токен у источника.
|
|
135
150
|
*
|
|
136
151
|
* При `CaptchaChoice.Auto` провайдер спрашивается у сервера — тогда и тип виджета,
|
|
137
|
-
* и имя поля приходят оттуда. Явно названный тип
|
|
152
|
+
* и имя поля приходят оттуда. Явно названный тип обходится без этого запроса.
|
|
138
153
|
*
|
|
139
154
|
* @param askProvider как спросить активного провайдера; вызывается, только если это нужно
|
|
140
|
-
* @throws {ItdConfigError} если
|
|
155
|
+
* @throws {ItdConfigError} если токен получить не удалось
|
|
141
156
|
*/
|
|
142
|
-
async function
|
|
143
|
-
|
|
144
|
-
const choice = captcha.type ?? CaptchaChoice.Auto;
|
|
157
|
+
async function solveCaptchaBody(solver, askProvider) {
|
|
158
|
+
const choice = solver.type ?? CaptchaChoice.Auto;
|
|
145
159
|
if (choice === CaptchaChoice.Auto) {
|
|
146
160
|
const { provider, field } = await askProvider();
|
|
147
|
-
return { [field]: await
|
|
161
|
+
return { [field]: await requireSolvedToken(solver, provider) };
|
|
148
162
|
}
|
|
149
|
-
return { [resolveField(choice,
|
|
163
|
+
return { [resolveField(choice, solver.field)]: await requireSolvedToken(solver, choice) };
|
|
150
164
|
}
|
|
151
165
|
/**
|
|
152
|
-
* Готовит фрагмент тела
|
|
166
|
+
* Готовит фрагмент тела из готового токена.
|
|
153
167
|
*
|
|
154
|
-
* Поле берётся из самого
|
|
168
|
+
* Поле берётся из самого токена либо из умолчания провайдера; запроса к серверу нет.
|
|
155
169
|
*
|
|
156
|
-
* @throws {ItdConfigError} если
|
|
170
|
+
* @throws {ItdConfigError} если токен или его тип не указаны
|
|
157
171
|
*/
|
|
158
172
|
function captchaBody(captcha) {
|
|
159
|
-
|
|
160
|
-
if (typeof captcha.token !== "string" || captcha.token.trim() === "") throw new ItdConfigError("captcha.token должен быть непустой строкой");
|
|
161
|
-
if (typeof captcha.type !== "string" || captcha.type.trim() === "") throw new ItdConfigError("captcha.type должен быть непустой строкой");
|
|
173
|
+
validateCaptchaToken(captcha, "captcha");
|
|
162
174
|
return { [resolveField(captcha.type, captcha.field)]: captcha.token };
|
|
163
175
|
}
|
|
164
176
|
//#endregion
|
|
@@ -236,7 +248,7 @@ function validateAuth(auth) {
|
|
|
236
248
|
const { email, password, captcha } = auth;
|
|
237
249
|
if (typeof email !== "string" || email.trim() === "") throw new ItdConfigError("auth.email должен быть непустой строкой");
|
|
238
250
|
if (typeof password !== "string" || password === "") throw new ItdConfigError("auth.password должен быть непустой строкой");
|
|
239
|
-
|
|
251
|
+
validateCaptchaToken(captcha, "auth.captcha");
|
|
240
252
|
return { ...auth };
|
|
241
253
|
}
|
|
242
254
|
throw new ItdConfigError("auth не распознан. Ожидается строка с accessToken либо объект { accessToken }, { email, password } или { getToken }");
|
|
@@ -265,6 +277,7 @@ function resolveSessionConfig(options, runtime) {
|
|
|
265
277
|
return {
|
|
266
278
|
...runtime,
|
|
267
279
|
auth: validateAuth(options.auth),
|
|
280
|
+
captcha: resolveCaptchaSolver(options.captcha),
|
|
268
281
|
storage: resolveStorage(options.storage),
|
|
269
282
|
deviceId: options.deviceId,
|
|
270
283
|
autoRefresh: options.autoRefresh ?? true,
|
|
@@ -436,6 +449,8 @@ var AuthManager = class {
|
|
|
436
449
|
*/
|
|
437
450
|
#authEpoch = 0;
|
|
438
451
|
#externalReadGeneration = 0;
|
|
452
|
+
/** Разовый токен из `auth.captcha` тратится ровно на один запрос. */
|
|
453
|
+
#configCaptchaSpent = false;
|
|
439
454
|
#disposed = false;
|
|
440
455
|
#persistence = Promise.resolve();
|
|
441
456
|
constructor(config, http, jar, hooks = {}) {
|
|
@@ -963,6 +978,34 @@ var AuthManager = class {
|
|
|
963
978
|
this.#signingIn = promise;
|
|
964
979
|
return promise;
|
|
965
980
|
}
|
|
981
|
+
/**
|
|
982
|
+
* Готовит фрагмент тела с токеном капчи для запроса, который её требует.
|
|
983
|
+
*
|
|
984
|
+
* Источники в порядке убывания приоритета: токен, переданный в сам вызов; разовый токен
|
|
985
|
+
* из `auth.captcha`; опция `captcha`. Когда ни одного нет, фрагмент пустой: нужна ли
|
|
986
|
+
* капча этому запросу, решает сервер.
|
|
987
|
+
*
|
|
988
|
+
* @param explicit готовый токен, переданный в вызов
|
|
989
|
+
* @throws {ItdConfigError} если токен неполный или источник его не вернул
|
|
990
|
+
*/
|
|
991
|
+
async captchaBody(explicit) {
|
|
992
|
+
if (explicit) return captchaBody(explicit);
|
|
993
|
+
const configured = this.#takeConfiguredCaptcha();
|
|
994
|
+
if (configured) return captchaBody(configured);
|
|
995
|
+
const solver = this.#config.captcha;
|
|
996
|
+
if (solver) return solveCaptchaBody(solver, () => this.#resolveCaptchaProvider());
|
|
997
|
+
this.#config.logger?.debug("Запрос уходит без токена капчи: не передан аргумент captcha и не задана опция captcha");
|
|
998
|
+
return {};
|
|
999
|
+
}
|
|
1000
|
+
/** Отдаёт разовый токен из `auth.captcha`, помечая его потраченным. */
|
|
1001
|
+
#takeConfiguredCaptcha() {
|
|
1002
|
+
if (this.#configCaptchaSpent) return void 0;
|
|
1003
|
+
const auth = this.#config.auth;
|
|
1004
|
+
const captcha = isRecord(auth) ? auth.captcha : void 0;
|
|
1005
|
+
if (!captcha) return void 0;
|
|
1006
|
+
this.#configCaptchaSpent = true;
|
|
1007
|
+
return captcha;
|
|
1008
|
+
}
|
|
966
1009
|
/** Спрашивает у сервера активного провайдера и поле, в котором он ждёт токен. */
|
|
967
1010
|
async #resolveCaptchaProvider() {
|
|
968
1011
|
return readCaptchaProvider(await this.#http.execute(AUTH_CAPTCHA_PROVIDER, {
|
|
@@ -972,7 +1015,7 @@ var AuthManager = class {
|
|
|
972
1015
|
}));
|
|
973
1016
|
}
|
|
974
1017
|
async #performSignIn(credentials, revision) {
|
|
975
|
-
const captcha = await
|
|
1018
|
+
const captcha = await this.captchaBody();
|
|
976
1019
|
const flowCookies = new CookieJar();
|
|
977
1020
|
const result = await this.#http.execute(AUTH_SIGN_IN, {
|
|
978
1021
|
path: AUTH_PATHS.signIn,
|
|
@@ -1153,7 +1196,8 @@ var AuthResource = class extends BaseResource {
|
|
|
1153
1196
|
if (existing) return existing;
|
|
1154
1197
|
const flow = {
|
|
1155
1198
|
cookies: this.#auth.createCookieFlow(false),
|
|
1156
|
-
expiresAt: void 0
|
|
1199
|
+
expiresAt: void 0,
|
|
1200
|
+
captchaRequired: false
|
|
1157
1201
|
};
|
|
1158
1202
|
this.#qrFlows.set(qrId, flow);
|
|
1159
1203
|
return flow;
|
|
@@ -1204,7 +1248,8 @@ var AuthResource = class extends BaseResource {
|
|
|
1204
1248
|
if (typeof result.qrId === "string" && result.qrId !== "") {
|
|
1205
1249
|
const flow = {
|
|
1206
1250
|
cookies,
|
|
1207
|
-
expiresAt: void 0
|
|
1251
|
+
expiresAt: void 0,
|
|
1252
|
+
captchaRequired: false
|
|
1208
1253
|
};
|
|
1209
1254
|
this.#touchQrFlow(flow, result.expiresIn);
|
|
1210
1255
|
this.#qrFlows.set(result.qrId, flow);
|
|
@@ -1214,20 +1259,21 @@ var AuthResource = class extends BaseResource {
|
|
|
1214
1259
|
/**
|
|
1215
1260
|
* Проверяет QR-сессию и завершает вход после подтверждения на другом устройстве.
|
|
1216
1261
|
*
|
|
1217
|
-
* До подтверждения возвращает промежуточный статус.
|
|
1218
|
-
*
|
|
1219
|
-
* Полученный `accessToken` автоматически сохраняется в клиенте.
|
|
1262
|
+
* До подтверждения возвращает промежуточный статус. Токен капчи добывается, только когда
|
|
1263
|
+
* сервер о ней попросил — статусом `captcha_required` на прошлой проверке, — либо когда
|
|
1264
|
+
* передан в сам вызов. Полученный `accessToken` автоматически сохраняется в клиенте.
|
|
1220
1265
|
*/
|
|
1221
1266
|
async claimQrLogin(input, options = {}) {
|
|
1222
1267
|
const revision = this.#auth.revision();
|
|
1223
1268
|
const flow = this.#qrFlow(input.qrId);
|
|
1224
1269
|
try {
|
|
1225
1270
|
const { captcha, ...rest } = input;
|
|
1271
|
+
const needsCaptcha = captcha !== void 0 || flow.captchaRequired;
|
|
1226
1272
|
const result = await this.http.execute(AUTH_QR_CLAIM, {
|
|
1227
1273
|
path: AUTH_PATHS.qrClaim,
|
|
1228
1274
|
body: {
|
|
1229
1275
|
...rest,
|
|
1230
|
-
...
|
|
1276
|
+
...needsCaptcha ? await this.#auth.captchaBody(captcha) : {}
|
|
1231
1277
|
},
|
|
1232
1278
|
skipAuth: true,
|
|
1233
1279
|
skipAuthRefresh: true,
|
|
@@ -1235,6 +1281,7 @@ var AuthResource = class extends BaseResource {
|
|
|
1235
1281
|
...options
|
|
1236
1282
|
});
|
|
1237
1283
|
this.#touchQrFlow(flow, result.expiresIn);
|
|
1284
|
+
if (result.status === QrLoginStatus.CaptchaRequired) flow.captchaRequired = true;
|
|
1238
1285
|
if (result.status === QrLoginStatus.Authorized) {
|
|
1239
1286
|
await this.#auth.commitAuthFlow(result.accessToken, flow.cookies, revision);
|
|
1240
1287
|
this.#qrFlows.delete(input.qrId);
|
|
@@ -1270,13 +1317,13 @@ var AuthResource = class extends BaseResource {
|
|
|
1270
1317
|
*
|
|
1271
1318
|
* @returns `flowToken`, который нужно передать в {@link verifyOtp}
|
|
1272
1319
|
*/
|
|
1273
|
-
signUp(credentials, options = {}) {
|
|
1320
|
+
async signUp(credentials, options = {}) {
|
|
1274
1321
|
const { captcha, ...rest } = credentials;
|
|
1275
1322
|
return this.http.execute(AUTH_SIGN_UP, {
|
|
1276
1323
|
path: AUTH_PATHS.signUp,
|
|
1277
1324
|
body: {
|
|
1278
1325
|
...rest,
|
|
1279
|
-
...captchaBody(captcha)
|
|
1326
|
+
...await this.#auth.captchaBody(captcha)
|
|
1280
1327
|
},
|
|
1281
1328
|
skipAuth: true,
|
|
1282
1329
|
skipAuthRefresh: true,
|
|
@@ -1291,18 +1338,19 @@ var AuthResource = class extends BaseResource {
|
|
|
1291
1338
|
*
|
|
1292
1339
|
* При успешном входе токен сохраняется в клиенте автоматически.
|
|
1293
1340
|
*
|
|
1294
|
-
* @param credentials email, пароль и
|
|
1341
|
+
* @param credentials email, пароль и токен капчи — см. {@link CredentialsWithCaptcha}
|
|
1295
1342
|
*/
|
|
1296
1343
|
async signIn(credentials, options = {}) {
|
|
1297
1344
|
const { captcha, ...rest } = credentials;
|
|
1298
1345
|
const revision = this.#auth.revision();
|
|
1299
1346
|
const cookies = this.#auth.createCookieFlow(false);
|
|
1347
|
+
const body = {
|
|
1348
|
+
...rest,
|
|
1349
|
+
...await this.#auth.captchaBody(captcha)
|
|
1350
|
+
};
|
|
1300
1351
|
const result = await this.http.execute(AUTH_SIGN_IN, {
|
|
1301
1352
|
path: AUTH_PATHS.signIn,
|
|
1302
|
-
body
|
|
1303
|
-
...rest,
|
|
1304
|
-
...captchaBody(captcha)
|
|
1305
|
-
},
|
|
1353
|
+
body,
|
|
1306
1354
|
skipAuth: true,
|
|
1307
1355
|
skipAuthRefresh: true,
|
|
1308
1356
|
cookieJar: cookies,
|
|
@@ -1430,12 +1478,12 @@ var AuthResource = class extends BaseResource {
|
|
|
1430
1478
|
*
|
|
1431
1479
|
* @returns `flowToken`, который нужно передать в {@link resetPassword}
|
|
1432
1480
|
*/
|
|
1433
|
-
forgotPassword(input, options = {}) {
|
|
1481
|
+
async forgotPassword(input, options = {}) {
|
|
1434
1482
|
return this.http.execute(AUTH_FORGOT_PASSWORD, {
|
|
1435
1483
|
path: AUTH_PATHS.forgotPassword,
|
|
1436
1484
|
body: {
|
|
1437
1485
|
email: input.email,
|
|
1438
|
-
...captchaBody(input.captcha)
|
|
1486
|
+
...await this.#auth.captchaBody(input.captcha)
|
|
1439
1487
|
},
|
|
1440
1488
|
skipAuth: true,
|
|
1441
1489
|
skipAuthRefresh: true,
|
|
@@ -1570,8 +1618,9 @@ function createManagedClient(options, internals) {
|
|
|
1570
1618
|
* const itd = new ItdClient({
|
|
1571
1619
|
* // `auth` не обязателен: когда хранилище уже содержит сессию, токен берётся оттуда,
|
|
1572
1620
|
* // а истёкший продлевается сам. Здесь он нужен на первый запуск.
|
|
1573
|
-
*
|
|
1574
|
-
*
|
|
1621
|
+
* auth: { email, password },
|
|
1622
|
+
* // Капчу входа решает пакет @itd-api/captcha — см. CaptchaSolver.
|
|
1623
|
+
* captcha: createCaptchaSolver(),
|
|
1575
1624
|
* storage: new FileTokenStorage('./.itd-session.json'),
|
|
1576
1625
|
* rateLimit: { concurrency: 4, rps: 8 },
|
|
1577
1626
|
* });
|
|
@@ -2085,6 +2134,8 @@ function assertAccountsActive(accounts, action) {
|
|
|
2085
2134
|
*
|
|
2086
2135
|
* await using accounts = new ItdAccounts({
|
|
2087
2136
|
* storage: new FileMultiTokenStorage('./.itd-sessions.json'),
|
|
2137
|
+
* // Общие опции достаются каждому аккаунту: один источник токена капчи на всех.
|
|
2138
|
+
* captcha: createCaptchaSolver(),
|
|
2088
2139
|
* rateLimit: { concurrency: 4 },
|
|
2089
2140
|
* });
|
|
2090
2141
|
*
|
|
@@ -2092,9 +2143,7 @@ function assertAccountsActive(accounts, action) {
|
|
|
2092
2143
|
* await accounts.restore();
|
|
2093
2144
|
*
|
|
2094
2145
|
* if (!accounts.has('kiow')) {
|
|
2095
|
-
* accounts.addAccount('kiow', {
|
|
2096
|
-
* auth: { email, password, captcha: createCaptchaSolver() },
|
|
2097
|
-
* });
|
|
2146
|
+
* accounts.addAccount('kiow', { auth: { email, password } });
|
|
2098
2147
|
* }
|
|
2099
2148
|
*
|
|
2100
2149
|
* await accounts.account('kiow').posts.create({ content: 'привет' });
|
|
@@ -2173,9 +2222,7 @@ var ItdAccounts = class ItdAccounts {
|
|
|
2173
2222
|
*
|
|
2174
2223
|
* @example
|
|
2175
2224
|
* ```ts
|
|
2176
|
-
* accounts.addAccount('bot', {
|
|
2177
|
-
* auth: { email, password, captcha: createCaptchaSolver() },
|
|
2178
|
-
* });
|
|
2225
|
+
* accounts.addAccount('bot', { auth: { email, password } });
|
|
2179
2226
|
* accounts.addAccount('reader', { auth: '<accessToken>' });
|
|
2180
2227
|
* accounts.addAccount('через-прокси', { fetch: proxyFetch('socks5://…') });
|
|
2181
2228
|
* ```
|