itd-api 0.0.4 → 0.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 +45 -8
- package/dist/{chunk-JV75JWOX.js → chunk-RUPF4X5L.js} +39 -12
- package/dist/chunk-RUPF4X5L.js.map +1 -0
- package/dist/{chunk-XG43KEYF.cjs → chunk-YM2YUO4D.cjs} +39 -12
- package/dist/chunk-YM2YUO4D.cjs.map +1 -0
- package/dist/{index-olL_q_yu.d.cts → index-Dv0LXpMf.d.cts} +42 -11
- package/dist/{index-olL_q_yu.d.ts → index-Dv0LXpMf.d.ts} +42 -11
- package/dist/index.cjs +84 -84
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node.cjs +83 -83
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +2 -2
- package/package.json +7 -3
- package/dist/chunk-JV75JWOX.js.map +0 -1
- package/dist/chunk-XG43KEYF.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -111,9 +111,8 @@ itd.on('authError', ({ error }) => {
|
|
|
111
111
|
|
|
112
112
|
### Капча обязательна при входе
|
|
113
113
|
|
|
114
|
-
`signIn`, `signUp` и `forgotPassword` требуют токен Cloudflare Turnstile.
|
|
115
|
-
|
|
116
|
-
снаружи, а библиотека принимает готовый токен.
|
|
114
|
+
`signIn`, `signUp` и `forgotPassword` требуют токен Cloudflare Turnstile. Сам клиент капчу
|
|
115
|
+
не решает — он принимает готовый токен, а решает его кто-то снаружи.
|
|
117
116
|
|
|
118
117
|
```ts
|
|
119
118
|
import { TURNSTILE_SITE_KEY } from 'itd-api';
|
|
@@ -134,6 +133,24 @@ new ItdClient({
|
|
|
134
133
|
});
|
|
135
134
|
```
|
|
136
135
|
|
|
136
|
+
В Node таким источником может быть [`itd-api-turnstile`](./turnstile) — отдельный пакет,
|
|
137
|
+
который поднимает браузер и приносит токен. Отдельный он намеренно: тянет за собой Playwright
|
|
138
|
+
и требует графической оболочки, а нужен далеко не всем — с сохранённой сессией до входа
|
|
139
|
+
по паролю дело обычно вообще не доходит.
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
npm i itd-api-turnstile playwright
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { createTurnstileSolver } from 'itd-api-turnstile';
|
|
147
|
+
|
|
148
|
+
new ItdClient({
|
|
149
|
+
storage: new FileTokenStorage('./.itd-session.json'),
|
|
150
|
+
auth: { email, password, getTurnstileToken: createTurnstileSolver() },
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
137
154
|
### Вход с кодом из письма
|
|
138
155
|
|
|
139
156
|
```ts
|
|
@@ -205,8 +222,8 @@ await itd.setSession(await loadFromSomewhere());
|
|
|
205
222
|
// по элементам
|
|
206
223
|
for await (const post of itd.posts.iterate({ tab: 'popular' })) { … }
|
|
207
224
|
|
|
208
|
-
// по страницам — когда
|
|
209
|
-
for await (const page of itd.
|
|
225
|
+
// по страницам — когда нужны сведения о самой странице
|
|
226
|
+
for await (const page of itd.posts.iterateComments(postId).pages()) {
|
|
210
227
|
console.log(page.items.length, 'из', page.total);
|
|
211
228
|
}
|
|
212
229
|
|
|
@@ -224,6 +241,24 @@ const next = await itd.posts.list({ tab: 'popular', cursor: page.nextCursor ?? u
|
|
|
224
241
|
Курсор непрозрачен: у вкладки `popular` это номер страницы, у `following` — отметка времени.
|
|
225
242
|
Передавайте его обратно как есть.
|
|
226
243
|
|
|
244
|
+
Перебор одноразовый: позиция хранится внутри, поэтому второй `for await` по тому же объекту
|
|
245
|
+
ничего не выдаст. Нужен ещё проход — возьмите новый перебор у того же метода.
|
|
246
|
+
|
|
247
|
+
### Чего API не умеет
|
|
248
|
+
|
|
249
|
+
**Подписчики, подписки и заблокированные не листаются.** Сервер отдаёт первые 20 записей
|
|
250
|
+
и на этом всё: `page` он игнорирует, `limit` больше 20 молча уменьшает, а `hasMore` всегда
|
|
251
|
+
`false`. Числу `total` там тоже верить нельзя: оно расходится с `followersCount` из профиля.
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
// вернёт 20 записей и остановится — это предел API, а не библиотеки
|
|
255
|
+
const all = await itd.users.iterateFollowers('durov').collect();
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**`posts.byUser()` — это стена, а не авторские посты.** В неё входят и записи, которые
|
|
259
|
+
другие оставили на странице пользователя, поэтому записей обычно больше, чем `postsCount`
|
|
260
|
+
в профиле. Нужны только свои — отфильтруйте по `post.author.id`.
|
|
261
|
+
|
|
227
262
|
---
|
|
228
263
|
|
|
229
264
|
## Публикация
|
|
@@ -361,8 +396,8 @@ const itd = new ItdClient({
|
|
|
361
396
|
по модулям, и темп будет общим.
|
|
362
397
|
|
|
363
398
|
`concurrency` (по умолчанию 6) ограничивает только одновременность. От ограничения частоты
|
|
364
|
-
он почти не спасает: десять запросов подряд при `concurrency: 1` уходят за ~150 мс,
|
|
365
|
-
|
|
399
|
+
он почти не спасает: десять запросов подряд при `concurrency: 1` уходят за ~150 мс,
|
|
400
|
+
а окно сервера измеряется десятками секунд. Темп задаёт `rps`:
|
|
366
401
|
|
|
367
402
|
```ts
|
|
368
403
|
rateLimit: { concurrency: 2, rps: 0.5 } // не чаще одного запроса в 2 секунды
|
|
@@ -371,7 +406,9 @@ rateLimit: { concurrency: 2, rps: 0.5 } // не чаще одного запр
|
|
|
371
406
|
Ставить `concurrency: 1` без нужды не стоит: загрузка видео с таймаутом в 300 секунд
|
|
372
407
|
заблокирует на это время вообще всё остальное.
|
|
373
408
|
|
|
374
|
-
**Ограничение частоты — отдельный механизм.**
|
|
409
|
+
**Ограничение частоты — отдельный механизм.** Лимит у каждого эндпоинта свой: замеры по
|
|
410
|
+
`x-ratelimit-limit` дали 90 у `/api/posts`, 40 у `/api/users/me` и `/api/notifications/`,
|
|
411
|
+
25 у `/api/v1/auth/refresh` и всего 15 у `/api/files/upload`. Сервер
|
|
375
412
|
не присылает `Retry-After` и не сообщает, когда окно сбросится: есть только заголовки
|
|
376
413
|
`x-ratelimit-limit` и `x-ratelimit-remaining` (доступны на `ItdRateLimitError` как
|
|
377
414
|
`rateLimit` и `rateLimitRemaining`).
|
|
@@ -1554,7 +1554,7 @@ var AuthManager = class {
|
|
|
1554
1554
|
}
|
|
1555
1555
|
if (credentials.turnstileToken) return credentials.turnstileToken;
|
|
1556
1556
|
throw new ItdConfigError(
|
|
1557
|
-
"\u0412\u0445\u043E\u0434 \u043F\u043E email \u0438 \u043F\u0430\u0440\u043E\u043B\u044E \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0442\u043E\u043A\u0435\u043D \u043A\u0430\u043F\u0447\u0438 Cloudflare Turnstile: \u0431\u0435\u0437 \u043D\u0435\u0433\u043E \u0441\u0435\u0440\u0432\u0435\u0440 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 422. \u041F\u0435\u0440\u0435\u0434\u0430\u0439\u0442\u0435 auth.getTurnstileToken (\u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A \u0441\u0432\u0435\u0436\u0435\u0433\u043E \u0442\u043E\u043A\u0435\u043D\u0430) \u043B\u0438\u0431\u043E \u0440\u0430\u0437\u043E\u0432\u044B\u0439 auth.turnstileToken. \u041A\u043B\u044E\u0447 \u0432\u0438\u0434\u0436\u0435\u0442\u0430 \u2014 TURNSTILE_SITE_KEY."
|
|
1557
|
+
"\u0412\u0445\u043E\u0434 \u043F\u043E email \u0438 \u043F\u0430\u0440\u043E\u043B\u044E \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0442\u043E\u043A\u0435\u043D \u043A\u0430\u043F\u0447\u0438 Cloudflare Turnstile: \u0431\u0435\u0437 \u043D\u0435\u0433\u043E \u0441\u0435\u0440\u0432\u0435\u0440 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 422. \u041F\u0435\u0440\u0435\u0434\u0430\u0439\u0442\u0435 auth.getTurnstileToken (\u0438\u0441\u0442\u043E\u0447\u043D\u0438\u043A \u0441\u0432\u0435\u0436\u0435\u0433\u043E \u0442\u043E\u043A\u0435\u043D\u0430) \u043B\u0438\u0431\u043E \u0440\u0430\u0437\u043E\u0432\u044B\u0439 auth.turnstileToken. \u041A\u043B\u044E\u0447 \u0432\u0438\u0434\u0436\u0435\u0442\u0430 \u2014 TURNSTILE_SITE_KEY. \u0412 Node \u0442\u043E\u043A\u0435\u043D \u0443\u043C\u0435\u0435\u0442 \u0434\u043E\u0431\u044B\u0432\u0430\u0442\u044C \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439 \u043F\u0430\u043A\u0435\u0442: npm i itd-api-turnstile, \u0437\u0430\u0442\u0435\u043C getTurnstileToken: createTurnstileSolver()."
|
|
1558
1558
|
);
|
|
1559
1559
|
}
|
|
1560
1560
|
async #performSignIn(credentials) {
|
|
@@ -1696,7 +1696,7 @@ function normalizeBaseUrl(baseUrl) {
|
|
|
1696
1696
|
// src/core/config.ts
|
|
1697
1697
|
var DEFAULT_BASE_URL = "https://xn--d1ah4a.com";
|
|
1698
1698
|
var DEFAULT_TIMEOUT = 3e4;
|
|
1699
|
-
var LIBRARY_VERSION = "0.0.
|
|
1699
|
+
var LIBRARY_VERSION = "0.0.5";
|
|
1700
1700
|
var DEFAULT_USER_AGENT = `Mozilla/5.0 (compatible; itd-api/${LIBRARY_VERSION}; +https://github.com/KiowDev/itd-api)`;
|
|
1701
1701
|
var DEFAULT_RATE_LIMIT_DELAYS = Object.freeze([1e3, 5e3, 3e4, 6e4, 9e4]);
|
|
1702
1702
|
function requirePositive(value, name) {
|
|
@@ -4475,7 +4475,16 @@ var PostsResource = class extends BaseResource {
|
|
|
4475
4475
|
});
|
|
4476
4476
|
return pickArray(body, "posts");
|
|
4477
4477
|
}
|
|
4478
|
-
/**
|
|
4478
|
+
/**
|
|
4479
|
+
* Загружает страницу стены пользователя.
|
|
4480
|
+
*
|
|
4481
|
+
* Это **не только его собственные посты**: сюда попадают и записи, которые другие
|
|
4482
|
+
* оставили на его стене — у них `author` чужой, а `wallRecipient` указывает на владельца
|
|
4483
|
+
* стены. Поэтому число записей обычно больше, чем `postsCount` из профиля; чтобы
|
|
4484
|
+
* получить только авторские посты, отфильтруйте по `post.author.id`.
|
|
4485
|
+
*
|
|
4486
|
+
* Принимает и UUID, и имя пользователя.
|
|
4487
|
+
*/
|
|
4479
4488
|
async byUser(user, params = {}) {
|
|
4480
4489
|
const body = await this.http.request({
|
|
4481
4490
|
method: "GET",
|
|
@@ -4490,7 +4499,7 @@ var PostsResource = class extends BaseResource {
|
|
|
4490
4499
|
});
|
|
4491
4500
|
return readCursorPage(body, "posts");
|
|
4492
4501
|
}
|
|
4493
|
-
/** Перебирает
|
|
4502
|
+
/** Перебирает стену пользователя. Что именно в неё входит — см. {@link byUser}. */
|
|
4494
4503
|
iterateByUser(user, params = {}) {
|
|
4495
4504
|
const path = `/api/posts/user/${encodePathSegment(user, "user")}`;
|
|
4496
4505
|
return this.paginate(
|
|
@@ -4733,19 +4742,34 @@ var UsersResource = class extends BaseResource {
|
|
|
4733
4742
|
...this.requestOptions(options)
|
|
4734
4743
|
});
|
|
4735
4744
|
}
|
|
4736
|
-
/**
|
|
4745
|
+
/**
|
|
4746
|
+
* Загружает подписчиков пользователя.
|
|
4747
|
+
*
|
|
4748
|
+
* ⚠️ **Сервер этот список не листает.** Возвращаются первые 20 записей и только они:
|
|
4749
|
+
* параметр `page` игнорируется (любая страница отдаёт те же записи и `pagination.page: 1`),
|
|
4750
|
+
* `limit` больше 20 молча уменьшается, а `hasMore` всегда `false`. Последнее честно —
|
|
4751
|
+
* получить продолжение нечем.
|
|
4752
|
+
*
|
|
4753
|
+
* Числу `total` доверять тоже не стоит: оно расходится с `followersCount` из профиля —
|
|
4754
|
+
* на проверенных аккаунтах занижено примерно на 1–4%.
|
|
4755
|
+
*/
|
|
4737
4756
|
followers(user, params = {}) {
|
|
4738
4757
|
return this.#userPage(`/api/users/${encodePathSegment(user, "user")}/followers`, params);
|
|
4739
4758
|
}
|
|
4740
|
-
/**
|
|
4759
|
+
/**
|
|
4760
|
+
* Перебирает подписчиков.
|
|
4761
|
+
*
|
|
4762
|
+
* ⚠️ Перебор закончится после первых 20 записей: сервер список не листает —
|
|
4763
|
+
* см. {@link followers}. Метод оставлен на случай, если пагинацию починят.
|
|
4764
|
+
*/
|
|
4741
4765
|
iterateFollowers(user, params = {}) {
|
|
4742
4766
|
return this.#userPaginator(`/api/users/${encodePathSegment(user, "user")}/followers`, params);
|
|
4743
4767
|
}
|
|
4744
|
-
/** Загружает
|
|
4768
|
+
/** Загружает подписки пользователя. Ограничения те же, что у {@link followers}. */
|
|
4745
4769
|
following(user, params = {}) {
|
|
4746
4770
|
return this.#userPage(`/api/users/${encodePathSegment(user, "user")}/following`, params);
|
|
4747
4771
|
}
|
|
4748
|
-
/** Перебирает подписки. */
|
|
4772
|
+
/** Перебирает подписки. Закончится после первых 20 записей — см. {@link followers}. */
|
|
4749
4773
|
iterateFollowing(user, params = {}) {
|
|
4750
4774
|
return this.#userPaginator(`/api/users/${encodePathSegment(user, "user")}/following`, params);
|
|
4751
4775
|
}
|
|
@@ -4785,11 +4809,11 @@ var UsersResource = class extends BaseResource {
|
|
|
4785
4809
|
...this.requestOptions(options)
|
|
4786
4810
|
});
|
|
4787
4811
|
}
|
|
4788
|
-
/** Загружает
|
|
4812
|
+
/** Загружает заблокированных пользователей. Ограничения те же, что у {@link followers}. */
|
|
4789
4813
|
blocked(params = {}) {
|
|
4790
4814
|
return this.#userPage("/api/users/me/blocked", params);
|
|
4791
4815
|
}
|
|
4792
|
-
/** Перебирает
|
|
4816
|
+
/** Перебирает заблокированных. Закончится после первых 20 записей — см. {@link followers}. */
|
|
4793
4817
|
iterateBlocked(params = {}) {
|
|
4794
4818
|
return this.#userPaginator("/api/users/me/blocked", params);
|
|
4795
4819
|
}
|
|
@@ -4850,6 +4874,9 @@ var UsersResource = class extends BaseResource {
|
|
|
4850
4874
|
* Имена полей перечислены с запасом: списки подписчиков и заблокированных приходят
|
|
4851
4875
|
* под `users`, но альтернативное имя ничего не стоит и спасает, если эндпоинт назовёт
|
|
4852
4876
|
* список по-своему.
|
|
4877
|
+
*
|
|
4878
|
+
* `page` уходит в запрос, хотя сервер его сейчас не читает (см. {@link followers}):
|
|
4879
|
+
* когда пагинацию починят, работать начнёт само.
|
|
4853
4880
|
*/
|
|
4854
4881
|
async #loadUserPage(path, params, state) {
|
|
4855
4882
|
const body = await this.http.request({
|
|
@@ -5190,5 +5217,5 @@ function toDate(value) {
|
|
|
5190
5217
|
}
|
|
5191
5218
|
|
|
5192
5219
|
export { ALLOWED_MIME_TYPES, AUDIO_MIME_TYPES, AUTH_FLAG_COOKIE, AUTH_PATHS, AttachmentType, CommentSort, DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DEFAULT_USER_AGENT, DEVICE_ID_HEADER, DetectedRuntime, FeedTab, IMAGE_MIME_TYPES, ItdAbortError, ItdApiError, ItdApiErrorKind, ItdAuthError, ItdClient, ItdConfigError, ItdConflictError, ItdError, ItdErrorCode, ItdErrorKind, ItdForbiddenError, ItdNetworkError, ItdNotFoundError, ItdPhoneVerificationError, ItdRateLimitError, ItdRealtime, ItdServerError, ItdTimeoutError, ItdValidationError, LIBRARY_VERSION, LikesVisibility, LocalStorageTokenStorage, MAX_RECONNECT_ATTEMPTS, MemoryTokenStorage, NOTIFICATION_TYPE_ALIASES, NotificationType, OAuthProvider, PaginationMode, Paginator, RECONNECT_BACKOFF, RECONNECT_JITTER, REFRESH_COOKIE, REFRESH_COOKIE_PATH, RealtimeStatus, RealtimeTransportKind, ReportReason, ReportTargetType, RuntimeMode, STREAM_PATH, SignInStatus, TURNSTILE_SITE_KEY, UnauthorizedStreamError, VIDEO_MIME_TYPES, WallAccess, canonicalNotificationType, comment, createClient, createTokenStorage, formatNotificationText, isBuilder, isItdApiError, isItdAuthError, isItdConflictError, isItdError, isItdForbiddenError, isItdNotFoundError, isItdPhoneVerificationError, isItdRateLimitError, isItdServerError, isItdValidationError, isKnownNotificationType, isMyProfile, normalizeNotification, poll, post, readNotificationEvent, readUnreadCountEvent, report, resolveNotificationUrl, toDate };
|
|
5193
|
-
//# sourceMappingURL=chunk-
|
|
5194
|
-
//# sourceMappingURL=chunk-
|
|
5220
|
+
//# sourceMappingURL=chunk-RUPF4X5L.js.map
|
|
5221
|
+
//# sourceMappingURL=chunk-RUPF4X5L.js.map
|