itd-api 0.0.8 → 0.0.10
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 +155 -253
- package/dist/{chunk-ATCZ4T2K.cjs → chunk-BN4AC3DP.cjs} +3303 -1628
- package/dist/chunk-BN4AC3DP.cjs.map +1 -0
- package/dist/{chunk-JIYN33FG.js → chunk-SMV7TF5P.js} +3283 -1629
- package/dist/chunk-SMV7TF5P.js.map +1 -0
- package/dist/{index-Duh31Wnx.d.cts → index-CSjDNGCE.d.cts} +1298 -410
- package/dist/{index-Duh31Wnx.d.ts → index-CSjDNGCE.d.ts} +1298 -410
- package/dist/index.cjs +169 -89
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node.cjs +266 -124
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +42 -4
- package/dist/node.d.ts +42 -4
- package/dist/node.js +106 -39
- package/dist/node.js.map +1 -1
- package/guides/README.md +22 -0
- package/guides/authentication/README.md +176 -0
- package/guides/authentication/examples/bot-with-session.mjs +98 -0
- package/guides/authentication/examples/turnstile-login.mjs +56 -0
- package/guides/integrations/README.md +62 -0
- package/guides/integrations/examples/proxy.mjs +26 -0
- package/guides/multi-accounts/README.md +142 -0
- package/guides/multi-accounts/examples/multi-accounts.mjs +71 -0
- package/guides/plugins/README.md +162 -0
- package/guides/plugins/examples/cache.mjs +33 -0
- package/guides/plugins/examples/crypto.mjs +54 -0
- package/guides/quickstart/README.md +124 -0
- package/guides/quickstart/examples/quick-start.mjs +44 -0
- package/guides/quickstart/examples/typescript.ts +90 -0
- package/guides/realtime/README.md +109 -0
- package/guides/realtime/examples/notifications.mjs +62 -0
- package/guides/text-markup/README.md +214 -0
- package/guides/text-markup/examples/create-post.mjs +64 -0
- package/package.json +6 -4
- package/dist/chunk-ATCZ4T2K.cjs.map +0 -1
- package/dist/chunk-JIYN33FG.js.map +0 -1
package/README.md
CHANGED
|
@@ -43,65 +43,24 @@ const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json')
|
|
|
43
43
|
await itd.posts.create((p) => p.content('привет').attach('./photo.jpg'));
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Продолжение с запускаемыми примерами — в [руководстве по быстрому старту](./guides/quickstart/README.md).
|
|
47
|
+
Все тематические материалы собраны в [`guides/`](./guides/README.md).
|
|
47
48
|
|
|
48
49
|
---
|
|
49
50
|
|
|
50
51
|
## Авторизация
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
из явного вызова входа. Всё это взаимозаменяемо, и **обязательного варианта нет**.
|
|
53
|
+
Клиент может взять доступ из `auth`, сохранённой сессии или явного вызова `itd.auth`:
|
|
54
54
|
|
|
55
55
|
```ts
|
|
56
|
-
new ItdClient({ auth: '<accessToken>' });
|
|
57
|
-
new ItdClient({ auth: { accessToken, refreshToken } });
|
|
58
|
-
new ItdClient({ auth: { email, password, getTurnstileToken } });
|
|
59
|
-
new ItdClient({
|
|
60
|
-
|
|
61
|
-
new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') }); // сессия с прошлого раза
|
|
62
|
-
new ItdClient(); // войти позже, через itd.auth
|
|
56
|
+
new ItdClient({ auth: '<accessToken>' });
|
|
57
|
+
new ItdClient({ auth: { accessToken, refreshToken } });
|
|
58
|
+
new ItdClient({ auth: { email, password, getTurnstileToken } });
|
|
59
|
+
new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });
|
|
63
60
|
```
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
|
|
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(); // токен подставится сам
|
|
79
|
-
```
|
|
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
|
-
|
|
96
|
-
При ответе `401` библиотека продлевает сессию и повторяет запрос. Параллельные запросы,
|
|
97
|
-
одновременно получившие `401`, ждут **одного** обновления, а не запускают своё — иначе сервер
|
|
98
|
-
увидел бы десяток одновременных `refresh` и отверг бы все, кроме первого.
|
|
99
|
-
|
|
100
|
-
Отключить автоматику: `autoRefresh: false`, дальше `await itd.auth.refresh()` вручную.
|
|
101
|
-
|
|
102
|
-
Когда продлить не удалось, `refresh()` бросает ошибку **сервера** — по её коду видно, что
|
|
103
|
-
именно случилось: `SESSION_NOT_FOUND` (сессия отозвана или истекла), `SESSION_REVOKED`,
|
|
104
|
-
`REFRESH_TOKEN_MISSING` (продлевать нечем). Та же ошибка приходит в событии `authError`:
|
|
62
|
+
При `401` сессия продлевается, исходный запрос повторяется, а новое состояние сохраняется.
|
|
63
|
+
Параллельные запросы ждут одного refresh. Потерю сессии можно отследить:
|
|
105
64
|
|
|
106
65
|
```ts
|
|
107
66
|
itd.on('authError', ({ error }) => {
|
|
@@ -109,108 +68,39 @@ itd.on('authError', ({ error }) => {
|
|
|
109
68
|
});
|
|
110
69
|
```
|
|
111
70
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
не решает — он принимает готовый токен, а решает его кто-то снаружи.
|
|
116
|
-
|
|
117
|
-
```ts
|
|
118
|
-
import { TURNSTILE_SITE_KEY } from 'itd-api';
|
|
119
|
-
|
|
120
|
-
// в браузере — виджет Turnstile с этим ключом
|
|
121
|
-
turnstile.render('#captcha', {
|
|
122
|
-
sitekey: TURNSTILE_SITE_KEY,
|
|
123
|
-
callback: (turnstileToken) => itd.auth.signIn({ email, password, turnstileToken }),
|
|
124
|
-
});
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
Токен одноразовый и живёт несколько минут. Долгоживущему боту передавайте не строку,
|
|
128
|
-
а источник — он спрашивается заново перед каждой попыткой входа:
|
|
71
|
+
Вход, регистрация и восстановление пароля требуют одноразовый Turnstile token. Подробности
|
|
72
|
+
о cookie, `deviceId`, OTP, собственном storage и автоматическом решателе:
|
|
73
|
+
[руководство по авторизации](./guides/authentication/README.md).
|
|
129
74
|
|
|
130
|
-
|
|
131
|
-
new ItdClient({
|
|
132
|
-
auth: { email, password, getTurnstileToken: () => captchaSolver.solve() },
|
|
133
|
-
});
|
|
134
|
-
```
|
|
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';
|
|
75
|
+
---
|
|
147
76
|
|
|
148
|
-
|
|
149
|
-
storage: new FileTokenStorage('./.itd-session.json'),
|
|
150
|
-
auth: { email, password, getTurnstileToken: createTurnstileSolver() },
|
|
151
|
-
});
|
|
152
|
-
```
|
|
77
|
+
## Несколько аккаунтов
|
|
153
78
|
|
|
154
|
-
|
|
79
|
+
`ItdAccounts` хранит именованные клиенты с отдельными tokens, cookie и `deviceId`, но одним
|
|
80
|
+
`MultiTokenStorage`:
|
|
155
81
|
|
|
156
82
|
```ts
|
|
157
|
-
import {
|
|
83
|
+
import { ItdAccounts, FileMultiTokenStorage } from 'itd-api/node';
|
|
158
84
|
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
email, password, turnstileToken,
|
|
163
|
-
getOtp: () => rl.question('Код из письма: '),
|
|
85
|
+
const accounts = new ItdAccounts({
|
|
86
|
+
storage: new FileMultiTokenStorage('./.itd-sessions.json'),
|
|
87
|
+
rateLimit: { concurrency: 4 },
|
|
164
88
|
});
|
|
165
|
-
```
|
|
166
89
|
|
|
167
|
-
|
|
90
|
+
// Поднимаем тех, кто уже входил раньше: ни auth, ни капча не нужны.
|
|
91
|
+
await accounts.restore();
|
|
168
92
|
|
|
169
|
-
|
|
93
|
+
if (!accounts.has('kiow')) {
|
|
94
|
+
accounts.addAccount('kiow', { auth: { email, password, getTurnstileToken } });
|
|
95
|
+
}
|
|
170
96
|
|
|
171
|
-
|
|
172
|
-
await itd.
|
|
173
|
-
|
|
174
|
-
getOtp: () => rl.question('Код из письма: '),
|
|
175
|
-
});
|
|
97
|
+
const itd = accounts.account('kiow'); // обычный ItdClient со всеми разделами
|
|
98
|
+
await itd.posts.create({ content: 'привет' });
|
|
99
|
+
await accounts.close();
|
|
176
100
|
```
|
|
177
101
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
| Хранилище | Откуда | Среда |
|
|
181
|
-
|---|---|---|
|
|
182
|
-
| `MemoryTokenStorage` (по умолчанию) | `itd-api` | везде |
|
|
183
|
-
| `LocalStorageTokenStorage` | `itd-api` | браузер |
|
|
184
|
-
| `FileTokenStorage` | `itd-api/node` | Node, Bun, Deno |
|
|
185
|
-
| `createTokenStorage({ get, set, clear })` | `itd-api` | своё: Redis, БД, AsyncStorage |
|
|
186
|
-
|
|
187
|
-
По умолчанию сессия живёт в памяти процесса и теряется при перезапуске. Укажите `storage` —
|
|
188
|
-
и библиотека сама запишет туда всё нужное после входа и после каждого продления; отдельно
|
|
189
|
-
сохранять ничего не надо.
|
|
190
|
-
|
|
191
|
-
В сессию попадают `accessToken`, `refreshToken`, cookie и `deviceId`. Сохранять её целиком
|
|
192
|
-
важно: refresh-токен приходит в cookie `refresh_token`, а `fetch` вне браузера их не хранит,
|
|
193
|
-
поэтому библиотека ведёт собственный cookie-jar. В браузере используется
|
|
194
|
-
`credentials: 'include'`, в React Native cookie ведёт нативный слой.
|
|
195
|
-
|
|
196
|
-
`deviceId` — идентификатор устройства из заголовка `X-Device-Id`. Сервер различает по нему
|
|
197
|
-
записи в списке сессий (`itd.auth.sessions()`), поэтому при постоянном хранилище он переживает
|
|
198
|
-
перезапуск и бот не плодит по новой сессии на каждый старт. Своё значение — опцией `deviceId`.
|
|
199
|
-
|
|
200
|
-
Refresh-токен можно передать и строкой (`auth: { accessToken, refreshToken }`) — вне браузера
|
|
201
|
-
библиотека сама подставит его нужной cookie. В браузере так не выйдет: cookie помечена
|
|
202
|
-
`HttpOnly`, и из JS её не выставить.
|
|
203
|
-
|
|
204
|
-
**Сервер выдаёт при каждом продлении новый refresh-токен взамен прежнего.** Со штатным
|
|
205
|
-
`storage` это происходит само. Если же вы храните сессию сами, снимайте её после каждого
|
|
206
|
-
обновления, а не один раз при входе, — иначе сохранённое значение протухнет:
|
|
207
|
-
|
|
208
|
-
```ts
|
|
209
|
-
itd.on('tokens', async () => saveSomewhere(await itd.getSession()));
|
|
210
|
-
|
|
211
|
-
// при следующем запуске
|
|
212
|
-
await itd.setSession(await loadFromSomewhere());
|
|
213
|
-
```
|
|
102
|
+
Личные прокси, общая или раздельные очереди, собственное хранилище и события контейнера
|
|
103
|
+
описаны в [руководстве по нескольким аккаунтам](./guides/multi-accounts/README.md).
|
|
214
104
|
|
|
215
105
|
---
|
|
216
106
|
|
|
@@ -287,25 +177,48 @@ await itd.posts.create(draft.content('второй')); // заготовка
|
|
|
287
177
|
Файлы из `attach()` загружаются автоматически, порядок вложений сохраняется, MIME-тип
|
|
288
178
|
проверяется до отправки.
|
|
289
179
|
|
|
290
|
-
Разметка текста
|
|
291
|
-
|
|
292
|
-
|
|
180
|
+
### Разметка текста
|
|
181
|
+
|
|
182
|
+
Билдер собирает текст и сам считает смещения:
|
|
293
183
|
|
|
294
184
|
```ts
|
|
295
|
-
import {
|
|
296
|
-
|
|
297
|
-
await itd.posts.create(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
185
|
+
import { post, renderSpans } from 'itd-api';
|
|
186
|
+
|
|
187
|
+
const created = await itd.posts.create(
|
|
188
|
+
post().markup((m) =>
|
|
189
|
+
m
|
|
190
|
+
.text('смотрите ')
|
|
191
|
+
.hashtag('котики')
|
|
192
|
+
.text(' от ')
|
|
193
|
+
.mention('durov')
|
|
194
|
+
.text(': ')
|
|
195
|
+
.bold('важно'),
|
|
196
|
+
),
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
renderSpans(created.content, created.spans); // безопасный HTML по умолчанию
|
|
304
200
|
```
|
|
305
201
|
|
|
306
|
-
|
|
202
|
+
Доступны `bold`, `italic`, `underline`, `strike`, `spoiler`, `monospace`, `quote`, `link`,
|
|
203
|
+
`hashtag`, `mention`, `span()` и несколько стилей сразу через `styled()`. Вложенные и
|
|
204
|
+
пересекающиеся spans поддерживаются. Смещения измеряются в единицах UTF-16, как индексы
|
|
205
|
+
строк и DOM Selection в JavaScript.
|
|
206
|
+
|
|
207
|
+
Для обычного текста есть автоматическое обнаружение ссылок, хэштегов и упоминаний:
|
|
307
208
|
|
|
308
|
-
|
|
209
|
+
```ts
|
|
210
|
+
await itd.posts.create(post('#котики от @durov: https://example.com').autoSpans());
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`renderSpans()` также выводит Markdown и ANSI и позволяет настроить маршруты упоминаний,
|
|
214
|
+
хэштегов и префикс CSS-классов. Вызов `postBuilder.content(newText)` сбрасывает прежние
|
|
215
|
+
spans, поскольку они рассчитаны для другого текста. `posts.update()` принимает тот же билдер,
|
|
216
|
+
но требует явно заданный `content`.
|
|
217
|
+
|
|
218
|
+
Авторазметка, пересекающиеся стили, обновление и настройка рендера подробно разобраны
|
|
219
|
+
в [руководстве по разметке](./guides/text-markup/README.md).
|
|
220
|
+
|
|
221
|
+
Билдеры есть у разметки, поста, комментария, опроса и жалобы. Все они неизменяемые, а `build()`
|
|
309
222
|
проверяет данные и бросает `ItdConfigError` **до** обращения к сети:
|
|
310
223
|
|
|
311
224
|
```ts
|
|
@@ -328,33 +241,72 @@ stream.on('notification', ({ notification }) => {
|
|
|
328
241
|
console.log(formatNotificationText(notification)); // «Аня и ещё 2 оценили ваш пост»
|
|
329
242
|
console.log(resolveNotificationUrl(notification)); // '/@anya/post/9f1c…'
|
|
330
243
|
});
|
|
331
|
-
stream.on('unreadCount', (count) => setBadge(count));
|
|
332
244
|
|
|
333
245
|
await stream.connect();
|
|
334
246
|
```
|
|
335
247
|
|
|
336
|
-
|
|
337
|
-
|
|
248
|
+
REST и поток используют одну форму уведомления. Переподключение, refresh token, keep-alive
|
|
249
|
+
и fallback на polling обрабатываются внутри. Эксплуатационные настройки и счётчик
|
|
250
|
+
непрочитанных разобраны в [руководстве по realtime](./guides/realtime/README.md).
|
|
338
251
|
|
|
339
|
-
|
|
340
|
-
складываются в один список. Сервер называет типы коротко (`like`, `comment`, `repost`),
|
|
341
|
-
библиотека приводит их к однозначным (`post_reaction`, `post_comment`, `post_repost`),
|
|
342
|
-
а пришедшее значение оставляет в `rawType`; весь исходный объект — в `raw`.
|
|
252
|
+
---
|
|
343
253
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
на
|
|
254
|
+
## Статус сервисов
|
|
255
|
+
|
|
256
|
+
`itd.platform.status()` отдаёт состояние платформы и историю доступности за 90 суток.
|
|
257
|
+
Авторизация не нужна, ответ кэшируется сервером на минуту.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import { statusDays } from 'itd-api';
|
|
348
261
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
(
|
|
353
|
-
|
|
354
|
-
|
|
262
|
+
const status = await itd.platform.status();
|
|
263
|
+
|
|
264
|
+
status.overall_status; // 'operational' | 'degraded' | 'downtime'
|
|
265
|
+
status.services.map((s) => s.current_status);
|
|
266
|
+
|
|
267
|
+
const auth = status.services.find((s) => s.id === 'auth');
|
|
268
|
+
auth?.uptime_90d; // 97.92
|
|
269
|
+
auth?.last_checked; // '2026-07-23T23:14:25Z'
|
|
270
|
+
|
|
271
|
+
const days = auth ? statusDays(auth) : []; // 90 элементов, [0] — сегодня
|
|
272
|
+
days[0]?.uptime; // 100
|
|
273
|
+
days[0]?.lines; // [{ t: 'down', text: 'недоступен 6 мин (12:00–12:06)' }]
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Поле `days` приходит объектом с числовыми ключами, и сутки без данных сервер пропускает —
|
|
277
|
+
`statusDays()` разворачивает его в массив, где пропуски равны `null`. Строки в `lines`
|
|
278
|
+
готовы к показу как есть: длительность и границы интервала отдельными полями не приходят,
|
|
279
|
+
время в них московское, тогда как `date_key` суток нарезан по UTC.
|
|
280
|
+
|
|
281
|
+
### Сервисы платформы
|
|
282
|
+
|
|
283
|
+
Статус живёт на отдельном домене — `статус.итд.com`. Такие домены описываются как сервисы:
|
|
284
|
+
у каждого своё имя, хост, заголовки и признак публичности. Запрос выбирает сервис
|
|
285
|
+
полем `service`.
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
const itd = new ItdClient({
|
|
289
|
+
services: {
|
|
290
|
+
pb: {
|
|
291
|
+
baseUrl: 'https://pbapi.xn--d1ah4a.com',
|
|
292
|
+
headers: { Referer: 'https://pixel.xn--d1ah4a.com/' },
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
await itd.request({ method: 'GET', service: 'pb', path: '/api/pixel-info', query: { x: 1, y: 2 } });
|
|
298
|
+
```
|
|
355
299
|
|
|
356
|
-
|
|
357
|
-
|
|
300
|
+
То же самое после создания клиента — `itd.defineService({ name, baseUrl, headers, auth })`;
|
|
301
|
+
базовый URL сервиса отдаёт `itd.serviceBaseUrl(name)`.
|
|
302
|
+
|
|
303
|
+
Bearer-токен по умолчанию отправляется только основному хосту и его поддоменам.
|
|
304
|
+
Публичный или сторонний сервис его не получает. То же правило действует для разового
|
|
305
|
+
`itd.request({ baseUrl })`; если внешнему хосту действительно нужна авторизация,
|
|
306
|
+
разрешите её явно через `skipAuth: false`.
|
|
307
|
+
|
|
308
|
+
У каждого сервиса своя очередь `rateLimit`: лимит частоты сервер считает по хосту, поэтому
|
|
309
|
+
`429` от статуса не тормозит основной API и наоборот.
|
|
358
310
|
|
|
359
311
|
---
|
|
360
312
|
|
|
@@ -397,6 +349,7 @@ const itd = new ItdClient({
|
|
|
397
349
|
// Заголовки латиницей: кириллица в них запрещена самим HTTP.
|
|
398
350
|
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 — не слать
|
|
399
351
|
deviceId: '3f2a…-uuid', // по умолчанию заводится сам и живёт в сессии
|
|
352
|
+
services: { pb: 'https://pbapi.xn--d1ah4a.com' }, // домены сервисов платформы, см. ниже
|
|
400
353
|
logger: true, // токены и пароли в логах маскируются
|
|
401
354
|
hooks: {
|
|
402
355
|
onRequest: (ctx) => console.log(ctx.method, ctx.path),
|
|
@@ -456,111 +409,59 @@ rateLimit: { retryDelays: [1000, 5000, 30_000, 60_000, 90_000] } // по умо
|
|
|
456
409
|
Поэтому в браузерном приложении укажите в `baseUrl` адрес своего прокси. В Node, Bun,
|
|
457
410
|
Deno и React Native ограничение не действует.
|
|
458
411
|
|
|
412
|
+
Исключение — `itd.platform.status()`: страница статуса отдаёт
|
|
413
|
+
`Access-Control-Allow-Origin: *`, и этот метод работает из браузера напрямую.
|
|
414
|
+
|
|
459
415
|
### Прокси (HTTP/SOCKS5)
|
|
460
416
|
|
|
461
417
|
Чтобы направить запросы клиента через прокси, возьмите `fetch` из пакета
|
|
462
|
-
[
|
|
418
|
+
[`@itd-api/proxy`](./proxy/README.md):
|
|
463
419
|
|
|
464
420
|
```sh
|
|
465
|
-
npm i itd-api
|
|
421
|
+
npm i @itd-api/proxy
|
|
466
422
|
```
|
|
467
423
|
|
|
468
424
|
```ts
|
|
469
425
|
import { ItdClient } from 'itd-api';
|
|
470
|
-
import { proxyFetch } from 'itd-api
|
|
426
|
+
import { proxyFetch } from '@itd-api/proxy';
|
|
471
427
|
|
|
472
|
-
const
|
|
428
|
+
const fetch = proxyFetch('socks5://127.0.0.1:1080');
|
|
473
429
|
// http://…, https://…, socks5://… — можно с user:pass@
|
|
430
|
+
const itd = new ItdClient({ fetch });
|
|
431
|
+
|
|
432
|
+
// …работа…
|
|
433
|
+
|
|
434
|
+
await itd.close();
|
|
435
|
+
await fetch.close(); // закрывает пул соединений
|
|
474
436
|
```
|
|
475
437
|
|
|
476
438
|
Через тот же `fetch` пойдут авторизация, cookie, очередь, повторы и поток уведомлений.
|
|
477
|
-
Только для Node/Bun/Deno.
|
|
439
|
+
Только для Node/Bun/Deno. Подключение proxy и Turnstile разобрано в
|
|
440
|
+
[руководстве по интеграциям](./guides/integrations/README.md), параметры транспорта — в
|
|
441
|
+
[README пакета](./proxy/README.md).
|
|
478
442
|
|
|
479
443
|
---
|
|
480
444
|
|
|
481
445
|
## Плагины
|
|
482
446
|
|
|
483
|
-
Плагин
|
|
484
|
-
одна обёртка охватывает сразу все методы клиента. Подключается через `itd.use()`:
|
|
447
|
+
Плагин оборачивает запросы и ответы сразу всех ресурсов:
|
|
485
448
|
|
|
486
449
|
```ts
|
|
487
450
|
import { ItdClient } from 'itd-api';
|
|
488
|
-
import {
|
|
451
|
+
import { cache } from '@itd-api/cache';
|
|
489
452
|
|
|
490
453
|
const itd = new ItdClient({ auth: token });
|
|
491
|
-
itd.use(
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
[Отдельный пакет](./crypto): прячет текст в невидимых символах внутри обычного поста.
|
|
497
|
-
Читатель видит обложку, а тот, у кого подключён плагин, получает спрятанное отдельным полем.
|
|
498
|
-
|
|
499
|
-
```sh
|
|
500
|
-
npm i itd-api-crypto
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
```ts
|
|
504
|
-
// отправка: текст прогоняется через шифр, обложка остаётся видимой
|
|
505
|
-
const created = await itd.posts.create(
|
|
506
|
-
{ content: 'секретный текст' },
|
|
507
|
-
{ encrypt: { cipher: 'invisible', cover: 'обычный пост' } },
|
|
454
|
+
itd.use(
|
|
455
|
+
cache({
|
|
456
|
+
ttl: 60_000,
|
|
457
|
+
routes: ['users.get', 'posts.get', 'posts.list'],
|
|
458
|
+
}),
|
|
508
459
|
);
|
|
509
|
-
|
|
510
|
-
// чтение: content не меняется, расшифровка приезжает рядом
|
|
511
|
-
const post = await itd.posts.get(created.id);
|
|
512
|
-
post.secret?.text; // 'секретный текст'
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
Работает для постов, комментариев, ответов, имени и подписи профиля. Расшифровка идёт сама
|
|
516
|
-
и вглубь: находки появляются и у постов ленты, и у исходного поста репоста, и у авторов.
|
|
517
|
-
|
|
518
|
-
Шифра два: `invisible` — невидимые символы с обложкой, `beecrypt` — видимый текст из букв
|
|
519
|
-
`жъЖЪ`. Подробности, ограничения и то, как подключить свой шифр, — в
|
|
520
|
-
[README пакета](./crypto).
|
|
521
|
-
|
|
522
|
-
### Свой плагин
|
|
523
|
-
|
|
524
|
-
```ts
|
|
525
|
-
import type { ItdPlugin } from 'itd-api';
|
|
526
|
-
|
|
527
|
-
const timing: ItdPlugin = {
|
|
528
|
-
name: 'timing',
|
|
529
|
-
install({ use, logger }) {
|
|
530
|
-
use(async (request, next) => {
|
|
531
|
-
const started = Date.now();
|
|
532
|
-
try {
|
|
533
|
-
return await next(request);
|
|
534
|
-
} finally {
|
|
535
|
-
logger?.info(`${request.method} ${request.path}: ${Date.now() - started} мс`);
|
|
536
|
-
}
|
|
537
|
-
});
|
|
538
|
-
},
|
|
539
|
-
};
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
Обёртка может изменить запрос (передайте в `next` копию), подменить ответ или вернуть своё,
|
|
543
|
-
не обращаясь к сети. Подключённая раньше оказывается снаружи. Выполняется она один раз
|
|
544
|
-
на запрос, независимо от числа повторов.
|
|
545
|
-
|
|
546
|
-
Свои опции запроса плагин объявляет сам — библиотека их не понимает, но доносит до обёртки
|
|
547
|
-
нетронутыми:
|
|
548
|
-
|
|
549
|
-
```ts
|
|
550
|
-
const plugin: ItdPlugin = {
|
|
551
|
-
name: 'мой',
|
|
552
|
-
optionKeys: ['мояОпция'],
|
|
553
|
-
install({ use }) { /* … */ },
|
|
554
|
-
};
|
|
555
|
-
|
|
556
|
-
declare module 'itd-api' {
|
|
557
|
-
interface RequestOptions { мояОпция?: string | undefined }
|
|
558
|
-
}
|
|
559
460
|
```
|
|
560
461
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
462
|
+
Официальные плагины Cache и Crypto, полный контракт `ItdPlugin`, собственные опции и
|
|
463
|
+
структура пакета описаны в
|
|
464
|
+
[руководстве по плагинам](./guides/plugins/README.md).
|
|
564
465
|
|
|
565
466
|
---
|
|
566
467
|
|
|
@@ -576,10 +477,11 @@ declare module 'itd-api' {
|
|
|
576
477
|
| `itd.files` | загрузка медиа |
|
|
577
478
|
| `itd.hashtags` · `itd.search` | хэштеги, трендовые, глобальный поиск |
|
|
578
479
|
| `itd.reports` · `itd.verification` | жалобы, заявка на верификацию |
|
|
579
|
-
| `itd.subscription` · `itd.platform` | подписка, способы оплаты,
|
|
480
|
+
| `itd.subscription` · `itd.platform` | подписка, способы оплаты, анонсы, статус сервисов |
|
|
580
481
|
| `itd.realtime()` | поток уведомлений |
|
|
581
482
|
| `itd.use()` | плагины: обёртки вокруг запроса и ответа |
|
|
582
483
|
| `itd.request()` | произвольный запрос, если метода ещё нет |
|
|
484
|
+
| `ItdAccounts` | несколько аккаунтов с общим хранилищем сессий |
|
|
583
485
|
|
|
584
486
|
Метода не хватает или ответ разошёлся с документацией — есть запасной путь:
|
|
585
487
|
|
|
@@ -607,7 +509,7 @@ TypeScript 5.0+. Пакет собран в ESM и CommonJS, типы корре
|
|
|
607
509
|
|
|
608
510
|
```bash
|
|
609
511
|
npm install
|
|
610
|
-
npm test #
|
|
512
|
+
npm test # 611 тестов
|
|
611
513
|
npm run test:all # вместе с пакетами workspace
|
|
612
514
|
npm run typecheck
|
|
613
515
|
npm run lint
|