itd-api 0.0.3 → 0.0.4
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 +90 -19
- package/dist/{chunk-JIT55WDH.js → chunk-JV75JWOX.js} +315 -175
- package/dist/chunk-JV75JWOX.js.map +1 -0
- package/dist/{chunk-3ODOEKQO.cjs → chunk-XG43KEYF.cjs} +328 -174
- package/dist/chunk-XG43KEYF.cjs.map +1 -0
- package/dist/{index-BCuk8jNA.d.cts → index-olL_q_yu.d.cts} +176 -33
- package/dist/{index-BCuk8jNA.d.ts → index-olL_q_yu.d.ts} +176 -33
- package/dist/index.cjs +126 -70
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node.cjs +125 -69
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +8 -12
- package/dist/node.d.ts +8 -12
- package/dist/node.js +2 -2
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3ODOEKQO.cjs.map +0 -1
- package/dist/chunk-JIT55WDH.js.map +0 -1
package/README.md
CHANGED
|
@@ -36,10 +36,9 @@ for await (const post of itd.posts.iterate({ tab: FeedTab.Following })) {
|
|
|
36
36
|
```ts
|
|
37
37
|
import { ItdClient, FileTokenStorage } from 'itd-api/node';
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
39
|
+
// Сессия из файла: продлевается сама, `auth` не нужен. Как её туда положить —
|
|
40
|
+
// в разделе «Авторизация»: вход требует капчи, поэтому делается один раз.
|
|
41
|
+
const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });
|
|
43
42
|
|
|
44
43
|
await itd.posts.create((p) => p.content('привет').attach('./photo.jpg'));
|
|
45
44
|
```
|
|
@@ -50,21 +49,66 @@ await itd.posts.create((p) => p.content('привет').attach('./photo.jpg'));
|
|
|
50
49
|
|
|
51
50
|
## Авторизация
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
Откуда клиент берёт доступ к API — либо из опции `auth`, либо из `storage`, либо
|
|
53
|
+
из явного вызова входа. Всё это взаимозаменяемо, и **обязательного варианта нет**.
|
|
54
54
|
|
|
55
55
|
```ts
|
|
56
56
|
new ItdClient({ auth: '<accessToken>' }); // разовый вызов
|
|
57
|
-
new ItdClient({ auth: { accessToken, refreshToken } }); // восстановить сессию
|
|
57
|
+
new ItdClient({ auth: { accessToken, refreshToken } }); // восстановить сессию строками
|
|
58
58
|
new ItdClient({ auth: { email, password, getTurnstileToken } }); // войти самому
|
|
59
59
|
new ItdClient({ auth: { getToken: () => vault.read() } }); // токен извне
|
|
60
|
+
|
|
61
|
+
new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') }); // сессия с прошлого раза
|
|
62
|
+
new ItdClient(); // войти позже, через itd.auth
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`auth` и `storage` не конкурируют, а дополняют друг друга: **хранилище главнее** — оно
|
|
66
|
+
отражает текущее состояние сессии, — а недостающие поля берутся из `auth`. Типичный случай:
|
|
67
|
+
в хранилище лежит только `accessToken`, а `refreshToken` приходит из настроек приложения.
|
|
68
|
+
|
|
69
|
+
### Сессия из хранилища — `auth` не нужен
|
|
70
|
+
|
|
71
|
+
Если предыдущий запуск сохранил сессию, для работы достаточно одного `storage`:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { ItdClient, FileTokenStorage } from 'itd-api/node';
|
|
75
|
+
|
|
76
|
+
const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });
|
|
77
|
+
|
|
78
|
+
const me = await itd.users.me(); // токен подставится сам
|
|
60
79
|
```
|
|
61
80
|
|
|
81
|
+
Более того, сохранённого `accessToken` не требуется вовсе. Хватает cookie: первый запрос
|
|
82
|
+
получит `401`, библиотека продлит сессию и повторит его — вызывающий код ничего не заметит.
|
|
83
|
+
|
|
84
|
+
Refresh-токен живёт 30 суток и обновляется при каждом продлении, поэтому регулярно
|
|
85
|
+
работающему боту капчу достаточно решить один раз, при первом запуске.
|
|
86
|
+
|
|
87
|
+
Проверить, есть ли что продлевать, можно заранее:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
if (await itd.auth.hasRefreshSession()) await itd.auth.refresh();
|
|
91
|
+
else redirectToLogin();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Продление токена
|
|
95
|
+
|
|
62
96
|
При ответе `401` библиотека продлевает сессию и повторяет запрос. Параллельные запросы,
|
|
63
97
|
одновременно получившие `401`, ждут **одного** обновления, а не запускают своё — иначе сервер
|
|
64
98
|
увидел бы десяток одновременных `refresh` и отверг бы все, кроме первого.
|
|
65
99
|
|
|
66
100
|
Отключить автоматику: `autoRefresh: false`, дальше `await itd.auth.refresh()` вручную.
|
|
67
101
|
|
|
102
|
+
Когда продлить не удалось, `refresh()` бросает ошибку **сервера** — по её коду видно, что
|
|
103
|
+
именно случилось: `SESSION_NOT_FOUND` (сессия отозвана или истекла), `SESSION_REVOKED`,
|
|
104
|
+
`REFRESH_TOKEN_MISSING` (продлевать нечем). Та же ошибка приходит в событии `authError`:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
itd.on('authError', ({ error }) => {
|
|
108
|
+
if (isItdApiError(error)) console.error('Сессия потеряна:', error.code);
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
68
112
|
### Капча обязательна при входе
|
|
69
113
|
|
|
70
114
|
`signIn`, `signUp` и `forgotPassword` требуют токен Cloudflare Turnstile. Полностью
|
|
@@ -123,24 +167,33 @@ await itd.auth.resetPasswordWithOtp({
|
|
|
123
167
|
| `FileTokenStorage` | `itd-api/node` | Node, Bun, Deno |
|
|
124
168
|
| `createTokenStorage({ get, set, clear })` | `itd-api` | своё: Redis, БД, AsyncStorage |
|
|
125
169
|
|
|
126
|
-
|
|
127
|
-
библиотека
|
|
128
|
-
|
|
170
|
+
По умолчанию сессия живёт в памяти процесса и теряется при перезапуске. Укажите `storage` —
|
|
171
|
+
и библиотека сама запишет туда всё нужное после входа и после каждого продления; отдельно
|
|
172
|
+
сохранять ничего не надо.
|
|
173
|
+
|
|
174
|
+
В сессию попадают `accessToken`, `refreshToken`, cookie и `deviceId`. Сохранять её целиком
|
|
175
|
+
важно: refresh-токен приходит в cookie `refresh_token`, а `fetch` вне браузера их не хранит,
|
|
176
|
+
поэтому библиотека ведёт собственный cookie-jar. В браузере используется
|
|
177
|
+
`credentials: 'include'`, в React Native cookie ведёт нативный слой.
|
|
178
|
+
|
|
179
|
+
`deviceId` — идентификатор устройства из заголовка `X-Device-Id`. Сервер различает по нему
|
|
180
|
+
записи в списке сессий (`itd.auth.sessions()`), поэтому при постоянном хранилище он переживает
|
|
181
|
+
перезапуск и бот не плодит по новой сессии на каждый старт. Своё значение — опцией `deviceId`.
|
|
129
182
|
|
|
130
|
-
|
|
131
|
-
библиотека сама подставит его нужной cookie. В браузере так не выйдет:
|
|
132
|
-
|
|
183
|
+
Refresh-токен можно передать и строкой (`auth: { accessToken, refreshToken }`) — вне браузера
|
|
184
|
+
библиотека сама подставит его нужной cookie. В браузере так не выйдет: cookie помечена
|
|
185
|
+
`HttpOnly`, и из JS её не выставить.
|
|
133
186
|
|
|
134
|
-
|
|
135
|
-
|
|
187
|
+
**Сервер выдаёт при каждом продлении новый refresh-токен взамен прежнего.** Со штатным
|
|
188
|
+
`storage` это происходит само. Если же вы храните сессию сами, снимайте её после каждого
|
|
189
|
+
обновления, а не один раз при входе, — иначе сохранённое значение протухнет:
|
|
136
190
|
|
|
137
191
|
```ts
|
|
138
192
|
itd.on('tokens', async () => saveSomewhere(await itd.getSession()));
|
|
139
|
-
```
|
|
140
193
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
194
|
+
// при следующем запуске
|
|
195
|
+
await itd.setSession(await loadFromSomewhere());
|
|
196
|
+
```
|
|
144
197
|
|
|
145
198
|
---
|
|
146
199
|
|
|
@@ -283,11 +336,14 @@ try {
|
|
|
283
336
|
```ts
|
|
284
337
|
const itd = new ItdClient({
|
|
285
338
|
baseUrl: 'https://xn--d1ah4a.com', // свой прокси, если работаете из браузера
|
|
286
|
-
auth: { email, password },
|
|
339
|
+
auth: { email, password, getTurnstileToken },
|
|
287
340
|
storage: new FileTokenStorage('./.itd-session.json'),
|
|
288
341
|
timeout: 30_000,
|
|
289
342
|
retry: { attempts: 3, retryWrites: false },
|
|
290
343
|
rateLimit: { concurrency: 4, rps: 8 },
|
|
344
|
+
// Заголовки латиницей: кириллица в них запрещена самим HTTP.
|
|
345
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36', // по умолчанию itd-api/<версия>; false — не слать
|
|
346
|
+
deviceId: '3f2a…-uuid', // по умолчанию заводится сам и живёт в сессии
|
|
291
347
|
logger: true, // токены и пароли в логах маскируются
|
|
292
348
|
hooks: {
|
|
293
349
|
onRequest: (ctx) => console.log(ctx.method, ctx.path),
|
|
@@ -300,6 +356,21 @@ const itd = new ItdClient({
|
|
|
300
356
|
по умолчанию не повторяется (`retryWrites: false`): повтор мог бы создать дубль поста.
|
|
301
357
|
Чтения повторяются с экспоненциальным откатом.
|
|
302
358
|
|
|
359
|
+
**Очередь: `concurrency` и `rps` решают разные задачи.** Все запросы идут через одну очередь
|
|
360
|
+
клиента, поэтому достаточно **одного экземпляра `ItdClient` на приложение** — разложите его
|
|
361
|
+
по модулям, и темп будет общим.
|
|
362
|
+
|
|
363
|
+
`concurrency` (по умолчанию 6) ограничивает только одновременность. От ограничения частоты
|
|
364
|
+
он почти не спасает: десять запросов подряд при `concurrency: 1` уходят за ~150 мс, а окно
|
|
365
|
+
сервера — около 5 запросов на минуту с лишним. Темп задаёт `rps`:
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
rateLimit: { concurrency: 2, rps: 0.5 } // не чаще одного запроса в 2 секунды
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Ставить `concurrency: 1` без нужды не стоит: загрузка видео с таймаутом в 300 секунд
|
|
372
|
+
заблокирует на это время вообще всё остальное.
|
|
373
|
+
|
|
303
374
|
**Ограничение частоты — отдельный механизм.** Сервер разрешает около 5 запросов в окно,
|
|
304
375
|
не присылает `Retry-After` и не сообщает, когда окно сбросится: есть только заголовки
|
|
305
376
|
`x-ratelimit-limit` и `x-ratelimit-remaining` (доступны на `ItdRateLimitError` как
|