itd-sdk-js 1.0.4 → 1.0.6
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/API_REFERENCE.md +34 -4
- package/README.md +9 -3
- package/package.json +2 -2
- package/src/auth.js +3 -3
- package/src/client.js +46 -12
- package/src/files.js +4 -2
- package/src/posts.js +17 -10
- package/src/token-storage.js +15 -17
- package/src/users.js +4 -3
package/API_REFERENCE.md
CHANGED
|
@@ -30,15 +30,17 @@ import { ITDClient } from 'itd-sdk-js';
|
|
|
30
30
|
|
|
31
31
|
Для работы автоматического обновления токена создайте файл `.cookies` в корне проекта. Скопируйте содержимое заголовка `Cookie` из любого сетевого запроса к сайту в браузере и вставьте в этот файл.
|
|
32
32
|
|
|
33
|
+
**Пути к .env и .cookies:** по умолчанию SDK ищет и сохраняет эти файлы в **корне проекта** (`process.cwd()`). При refresh токена обновлённые значения пишутся в ваш проект, а не в папку пакета. При необходимости можно задать свой корень или явные пути через опции конструктора (см. ниже).
|
|
34
|
+
|
|
33
35
|
---
|
|
34
36
|
|
|
35
37
|
## Авторизация и сессии
|
|
36
38
|
|
|
37
39
|
### Инициализация клиента
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
**Простой вариант (корень проекта = текущая рабочая директория):**
|
|
40
42
|
|
|
41
|
-
```
|
|
43
|
+
```javascript
|
|
42
44
|
import { ITDClient } from 'itd-sdk-js';
|
|
43
45
|
import dotenv from 'dotenv';
|
|
44
46
|
|
|
@@ -49,6 +51,28 @@ client.setAccessToken(process.env.ITD_ACCESS_TOKEN);
|
|
|
49
51
|
client.auth.isAuthenticated = true;
|
|
50
52
|
```
|
|
51
53
|
|
|
54
|
+
**С опциями (projectRoot / envPath / cookiesPath):**
|
|
55
|
+
|
|
56
|
+
Если скрипт запускается из подпапки или нужны явные пути:
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
const client = new ITDClient({
|
|
60
|
+
baseUrl: 'https://xn--d1ah4a.com',
|
|
61
|
+
userAgent: '...',
|
|
62
|
+
projectRoot: process.cwd(), // корень проекта (по умолчанию process.cwd())
|
|
63
|
+
// envPath: '/path/to/project/.env',
|
|
64
|
+
// cookiesPath: '/path/to/project/.cookies',
|
|
65
|
+
requestTimeout: 60000, // таймаут обычных запросов, мс (по умолчанию 60 с)
|
|
66
|
+
uploadTimeout: 120000, // таймаут загрузки файлов и создания поста, мс (по умолчанию 120 с)
|
|
67
|
+
});
|
|
68
|
+
client.setAccessToken(process.env.ITD_ACCESS_TOKEN);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- `projectRoot` — директория, в которой ищутся `.env` и `.cookies` (по умолчанию `process.cwd()`).
|
|
72
|
+
- `envPath` / `cookiesPath` — при указании переопределяют пути, собранные из `projectRoot`.
|
|
73
|
+
- `requestTimeout` — таймаут обычных запросов в мс (по умолчанию 60000). Предотвращает бесконечное ожидание при «тяжёлой» сети.
|
|
74
|
+
- `uploadTimeout` — таймаут для загрузки файлов и создания поста в мс (по умолчанию 120000). Используется в `uploadFile`, `createPost`, `createWallPost`.
|
|
75
|
+
|
|
52
76
|
### Автоматическое обновление (Refresh Token)
|
|
53
77
|
|
|
54
78
|
При получении ошибки `401 Unauthorized` клиент автоматически обращается к эндпоинту `/api/v1/auth/refresh`, используя данные из `.cookies`. В случае успеха новый токен сохраняется в `.env`, обновляются куки, и исходный запрос повторяется.
|
|
@@ -91,6 +115,10 @@ const post = await client.createPost('Текст поста', 'image.jpg');
|
|
|
91
115
|
|
|
92
116
|
Создает новый пост. При указании `imagePath` файл предварительно загружается через `/api/files/upload`, после чего ID файла прикрепляется к посту через поле `attachmentIds`.
|
|
93
117
|
|
|
118
|
+
- **Возвращает:** объект поста при успехе; **`null`** при любой ошибке (сеть, 5xx, 429, не удалось загрузить файл, неверный ответ). Всегда проверяйте результат на `null`.
|
|
119
|
+
- **Таймаут:** для загрузки файла и создания поста используется `uploadTimeout` (по умолчанию 120 с), чтобы запрос не зависал при 504 или медленной сети.
|
|
120
|
+
- **Ретраи:** при 5xx/429 или «API вернул null» рекомендуется повторять запрос в приложении (например, до 3–8 попыток с экспоненциальной задержкой). Встроенных ретраев в SDK нет.
|
|
121
|
+
|
|
94
122
|
**Важно:** API использует поле `attachmentIds` (массив ID файлов), а не `attachments`. SDK автоматически использует правильное поле.
|
|
95
123
|
|
|
96
124
|
- **Параметры**: `text` (string), `imagePath` (string, опционально).
|
|
@@ -99,6 +127,8 @@ const post = await client.createPost('Текст поста', 'image.jpg');
|
|
|
99
127
|
|
|
100
128
|
Создает пост **на стене другого пользователя** (wall post).
|
|
101
129
|
|
|
130
|
+
- **Возвращает:** объект поста при успехе; **`null`** при любой ошибке. Проверяйте результат на `null`; при 5xx/429 рекомендуется ретраи в приложении.
|
|
131
|
+
- **Таймаут:** используется `uploadTimeout` (по умолчанию 120 с).
|
|
102
132
|
- Делается через `POST /api/posts` с телом `{ content, wallRecipientId, attachmentIds? }`.
|
|
103
133
|
- `wallRecipientId` — это **ID пользователя-получателя**, поэтому метод сначала запрашивает профиль через `getUserProfile(username)` и берет `profile.id`.
|
|
104
134
|
- При указании `imagePath` файл загружается и прикрепляется через `attachmentIds`.
|
|
@@ -195,14 +225,14 @@ const post = await client.createPost('Текст поста', 'image.jpg');
|
|
|
195
225
|
- `search(query, userLimit?, hashtagLimit?)` — универсальный поиск пользователей и хэштегов. Возвращает `{ users: [], hashtags: [] }`.
|
|
196
226
|
- `searchUsers(query, limit?)` — поиск только пользователей.
|
|
197
227
|
- `searchHashtags(query, limit?)` — поиск только хэштегов.
|
|
198
|
-
- `getTopClans()` — рейтинг кланов по количеству участников.
|
|
228
|
+
- `getTopClans()` — рейтинг кланов по количеству участников. **Возвращает массив** `Array<{ avatar, memberCount }>` или **`null`** при ошибке (не объект с полем `clans`).
|
|
199
229
|
- `getWhoToFollow()` — рекомендованные пользователи.
|
|
200
230
|
- `getTrendingHashtags(limit)` — список популярных тегов.
|
|
201
231
|
- `getPostsByHashtag(tagName, limit, cursor)` — поиск постов по тегу. Возвращает `{ posts: [], hashtag: {}, pagination: {} }`.
|
|
202
232
|
|
|
203
233
|
### Файлы и репорты
|
|
204
234
|
|
|
205
|
-
- `uploadFile(filePath)` — загрузка файла через `/api/files/upload`. Возвращает `{ id, url, filename, mimeType, size }
|
|
235
|
+
- `uploadFile(filePath)` — загрузка файла через `/api/files/upload`. Возвращает `{ id, url, filename, mimeType, size }` или **`null`** при ошибке. Таймаут — `uploadTimeout` (по умолчанию 120 с). Используется автоматически при создании поста с изображением.
|
|
206
236
|
- `report(targetType, targetId, reason?, description?)` — отправка репорта. `targetType`: `"post"`, `"comment"`, `"user"`. Возвращает `{ id, createdAt }`.
|
|
207
237
|
- `reportPost(postId, reason?, description?)` — репорт поста.
|
|
208
238
|
- `reportComment(commentId, reason?, description?)` — репорт комментария.
|
package/README.md
CHANGED
|
@@ -26,11 +26,11 @@ npm install
|
|
|
26
26
|
|
|
27
27
|
## Настройка
|
|
28
28
|
|
|
29
|
-
1. Создайте `.env` на основе `.env.example` (или используйте переменные окружения).
|
|
29
|
+
1. Создайте `.env` в корне проекта на основе `.env.example` (или используйте переменные окружения).
|
|
30
30
|
2. Вставьте свой `ITD_ACCESS_TOKEN` (его можно вытащить из Network в DevTools).
|
|
31
|
-
3. Для работы авто-обновления сессии создайте файл `.cookies` и вставьте туда строку `Cookie` из любого запроса к сайту в браузере.
|
|
31
|
+
3. Для работы авто-обновления сессии создайте файл `.cookies` в корне проекта и вставьте туда строку `Cookie` из любого запроса к сайту в браузере.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
SDK по умолчанию читает и пишет `.env` и `.cookies` в корне проекта (`process.cwd()`). При обновлении токена изменения сохраняются в ваш проект. При необходимости можно задать `projectRoot` или явные пути в конструкторе — см. [API_REFERENCE.md](API_REFERENCE.md).
|
|
34
34
|
|
|
35
35
|
## Примеры
|
|
36
36
|
|
|
@@ -91,6 +91,12 @@ console.log(`${stats.likes} лайков, ${stats.views} просмотров`);
|
|
|
91
91
|
await client.createWallPost('ITD_API', 'Тестовый пост на чужой стене 🦫');
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
## Рекомендации при создании постов
|
|
95
|
+
|
|
96
|
+
- **createPost** и **createWallPost** при любой ошибке возвращают **`null`** — всегда проверяйте результат.
|
|
97
|
+
- Для загрузки файла и создания поста используется таймаут **120 с** по умолчанию (`uploadTimeout` в опциях клиента), чтобы запрос не зависал при 504 или медленной сети.
|
|
98
|
+
- При 5xx/429 или «API вернул null» рекомендуется повторять запрос в приложении (ретраи с задержкой). Подробнее — в [API_REFERENCE.md](API_REFERENCE.md).
|
|
99
|
+
|
|
94
100
|
## Важно
|
|
95
101
|
|
|
96
102
|
Это неофициальный проект. Если разработчики сайта изменят структуру API или введут новую защиту, методы могут временно перестать работать. Используйте аккуратно и не спамьте запросами.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "itd-sdk-js",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Unofficial SDK for итд.com - Node.js library for working with API. Automatic token refresh, session management, and convenient methods for posts, comments, users, and notifications.",
|
|
5
5
|
"main": "src/client.js",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"license": "MIT",
|
|
40
40
|
"repository": {
|
|
41
41
|
"type": "git",
|
|
42
|
-
"url": "https://github.com/FriceKa/ITD-SDK-js.git"
|
|
42
|
+
"url": "git+https://github.com/FriceKa/ITD-SDK-js.git"
|
|
43
43
|
},
|
|
44
44
|
"bugs": {
|
|
45
45
|
"url": "https://github.com/FriceKa/ITD-SDK-js/issues"
|
package/src/auth.js
CHANGED
|
@@ -68,8 +68,8 @@ export class AuthManager {
|
|
|
68
68
|
this.client.setAccessToken(newToken);
|
|
69
69
|
this.isAuthenticated = true;
|
|
70
70
|
|
|
71
|
-
// Сохраняем токен в .env файл
|
|
72
|
-
await saveAccessToken(newToken);
|
|
71
|
+
// Сохраняем токен в .env файл (в корне проекта)
|
|
72
|
+
await saveAccessToken(newToken, this.client.envPath);
|
|
73
73
|
|
|
74
74
|
// Обновляем cookies, если они пришли в ответе
|
|
75
75
|
if (response.headers['set-cookie']) {
|
|
@@ -94,7 +94,7 @@ export class AuthManager {
|
|
|
94
94
|
);
|
|
95
95
|
if (importantCookies.length > 0) {
|
|
96
96
|
const cookieHeader = importantCookies.map(c => `${c.key}=${c.value}`).join('; ');
|
|
97
|
-
await saveCookieHeader(cookieHeader);
|
|
97
|
+
await saveCookieHeader(cookieHeader, this.client.cookiesPath);
|
|
98
98
|
}
|
|
99
99
|
} catch (e) {
|
|
100
100
|
console.warn('⚠️ Не удалось сохранить обновленные cookies:', e.message);
|
package/src/client.js
CHANGED
|
@@ -5,7 +5,6 @@ import axios from 'axios';
|
|
|
5
5
|
import dotenv from 'dotenv';
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import { fileURLToPath } from 'url';
|
|
9
8
|
import { CookieJar } from 'tough-cookie';
|
|
10
9
|
import { wrapper } from 'axios-cookiejar-support';
|
|
11
10
|
import { AuthManager } from './auth.js';
|
|
@@ -20,22 +19,57 @@ import { SearchManager } from './search.js';
|
|
|
20
19
|
|
|
21
20
|
dotenv.config();
|
|
22
21
|
|
|
23
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
-
const __dirname = path.dirname(__filename);
|
|
25
|
-
|
|
26
22
|
export class ITDClient {
|
|
27
23
|
/**
|
|
28
24
|
* Инициализация клиента
|
|
29
25
|
*
|
|
30
|
-
* @param {string}
|
|
31
|
-
* @param {string} userAgent - User-Agent
|
|
26
|
+
* @param {string|Object} baseUrlOrOptions - Базовый URL сайта или объект опций
|
|
27
|
+
* @param {string} [userAgent] - User-Agent (если первый аргумент — baseUrl)
|
|
28
|
+
*
|
|
29
|
+
* Опции (если первый аргумент — объект):
|
|
30
|
+
* @param {string} [options.baseUrl] - Базовый URL сайта
|
|
31
|
+
* @param {string} [options.userAgent] - User-Agent
|
|
32
|
+
* @param {string} [options.projectRoot] - Корень проекта (по умолчанию process.cwd()); .env и .cookies ищутся здесь
|
|
33
|
+
* @param {string} [options.envPath] - Полный путь к .env (переопределяет projectRoot для .env)
|
|
34
|
+
* @param {string} [options.cookiesPath] - Полный путь к .cookies (переопределяет projectRoot для .cookies)
|
|
35
|
+
* @param {number} [options.requestTimeout] - Таймаут обычных запросов в мс (по умолчанию 60000)
|
|
36
|
+
* @param {number} [options.uploadTimeout] - Таймаут загрузки файлов и создания поста в мс (по умолчанию 120000)
|
|
32
37
|
*/
|
|
33
|
-
constructor(
|
|
38
|
+
constructor(baseUrlOrOptions = null, userAgent = null) {
|
|
39
|
+
let baseUrl, projectRoot, envPath, cookiesPath, requestTimeout, uploadTimeout;
|
|
40
|
+
|
|
41
|
+
if (baseUrlOrOptions && typeof baseUrlOrOptions === 'object' && !(baseUrlOrOptions instanceof URL)) {
|
|
42
|
+
const opts = baseUrlOrOptions;
|
|
43
|
+
baseUrl = opts.baseUrl ?? process.env.ITD_BASE_URL ?? 'https://xn--d1ah4a.com';
|
|
44
|
+
userAgent = opts.userAgent ?? process.env.ITD_USER_AGENT ?? null;
|
|
45
|
+
projectRoot = opts.projectRoot ?? process.cwd();
|
|
46
|
+
envPath = opts.envPath ?? path.join(projectRoot, '.env');
|
|
47
|
+
cookiesPath = opts.cookiesPath ?? path.join(projectRoot, '.cookies');
|
|
48
|
+
requestTimeout = opts.requestTimeout ?? 60000;
|
|
49
|
+
uploadTimeout = opts.uploadTimeout ?? 120000;
|
|
50
|
+
} else {
|
|
51
|
+
projectRoot = process.cwd();
|
|
52
|
+
baseUrl = baseUrlOrOptions || process.env.ITD_BASE_URL || 'https://xn--d1ah4a.com';
|
|
53
|
+
envPath = path.join(projectRoot, '.env');
|
|
54
|
+
cookiesPath = path.join(projectRoot, '.cookies');
|
|
55
|
+
requestTimeout = 60000;
|
|
56
|
+
uploadTimeout = 120000;
|
|
57
|
+
}
|
|
58
|
+
|
|
34
59
|
// Используем реальный домен (IDN: итд.com = xn--d1ah4a.com)
|
|
35
|
-
this.baseUrl = baseUrl
|
|
36
|
-
this.userAgent = userAgent || process.env.ITD_USER_AGENT ||
|
|
60
|
+
this.baseUrl = baseUrl;
|
|
61
|
+
this.userAgent = userAgent || process.env.ITD_USER_AGENT ||
|
|
37
62
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
|
38
63
|
|
|
64
|
+
/** Пути к .env и .cookies (корень проекта по умолчанию) */
|
|
65
|
+
this.envPath = envPath;
|
|
66
|
+
this.cookiesPath = cookiesPath;
|
|
67
|
+
|
|
68
|
+
/** Таймаут обычных запросов (мс). Для загрузки и создания поста используется uploadTimeout. */
|
|
69
|
+
this.requestTimeout = requestTimeout;
|
|
70
|
+
/** Таймаут загрузки файлов и создания поста (мс), чтобы не зависать при 504/медленной сети. */
|
|
71
|
+
this.uploadTimeout = uploadTimeout;
|
|
72
|
+
|
|
39
73
|
/** @type {string|null} */
|
|
40
74
|
this.accessToken = null;
|
|
41
75
|
|
|
@@ -55,6 +89,7 @@ export class ITDClient {
|
|
|
55
89
|
// Создание axios instance + cookie jar
|
|
56
90
|
const axiosConfig = {
|
|
57
91
|
baseURL: this.baseUrl,
|
|
92
|
+
timeout: requestTimeout,
|
|
58
93
|
withCredentials: true,
|
|
59
94
|
jar: this.cookieJar,
|
|
60
95
|
headers: {
|
|
@@ -178,13 +213,12 @@ export class ITDClient {
|
|
|
178
213
|
*/
|
|
179
214
|
_loadCookiesFromFile() {
|
|
180
215
|
try {
|
|
181
|
-
|
|
182
|
-
if (!fs.existsSync(cookiesPath)) {
|
|
216
|
+
if (!fs.existsSync(this.cookiesPath)) {
|
|
183
217
|
// Файл не существует - это нормально, просто пропускаем
|
|
184
218
|
return;
|
|
185
219
|
}
|
|
186
220
|
|
|
187
|
-
const cookieHeader = fs.readFileSync(cookiesPath, 'utf8').trim();
|
|
221
|
+
const cookieHeader = fs.readFileSync(this.cookiesPath, 'utf8').trim();
|
|
188
222
|
if (!cookieHeader) {
|
|
189
223
|
return;
|
|
190
224
|
}
|
package/src/files.js
CHANGED
|
@@ -11,8 +11,9 @@ export class FilesManager {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Загружает файл (изображение) на
|
|
15
|
-
*
|
|
14
|
+
* Загружает файл (изображение) на сервер.
|
|
15
|
+
* Таймаут — client.uploadTimeout (по умолчанию 120 с). При ошибке возвращает null.
|
|
16
|
+
*
|
|
16
17
|
* @param {string} filePath - Путь к файлу
|
|
17
18
|
* @returns {Promise<Object|null>} { id, url, filename, mimeType, size } или null при ошибке
|
|
18
19
|
*/
|
|
@@ -36,6 +37,7 @@ export class FilesManager {
|
|
|
36
37
|
formData.append('file', fs.createReadStream(filePath));
|
|
37
38
|
|
|
38
39
|
const response = await this.axios.post(uploadUrl, formData, {
|
|
40
|
+
timeout: this.client.uploadTimeout ?? 120000,
|
|
39
41
|
headers: {
|
|
40
42
|
...formData.getHeaders(),
|
|
41
43
|
}
|
package/src/posts.js
CHANGED
|
@@ -16,8 +16,10 @@ export class PostsManager {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Создает новый
|
|
20
|
-
*
|
|
19
|
+
* Создает новый пост.
|
|
20
|
+
* При любой ошибке (сеть, 5xx, 429, не удалось загрузить файл) возвращает null.
|
|
21
|
+
* Таймаут загрузки и создания — client.uploadTimeout (по умолчанию 120 с).
|
|
22
|
+
*
|
|
21
23
|
* @param {string} text - Текст поста
|
|
22
24
|
* @param {string|null} imagePath - Путь к изображению (опционально)
|
|
23
25
|
* @returns {Promise<Object|null>} Данные созданного поста или null при ошибке
|
|
@@ -51,9 +53,11 @@ export class PostsManager {
|
|
|
51
53
|
postData.attachmentIds = [uploadedFile.id];
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
// Создаем пост (с изображением или без)
|
|
55
|
-
const response = await this.axios.post(postUrl, postData
|
|
56
|
-
|
|
56
|
+
// Создаем пост (с изображением или без); увеличенный таймаут для тяжёлых запросов
|
|
57
|
+
const response = await this.axios.post(postUrl, postData, {
|
|
58
|
+
timeout: this.client.uploadTimeout ?? 120000,
|
|
59
|
+
});
|
|
60
|
+
|
|
57
61
|
if (response.status === 200 || response.status === 201) {
|
|
58
62
|
return response.data;
|
|
59
63
|
} else {
|
|
@@ -71,8 +75,9 @@ export class PostsManager {
|
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
/**
|
|
74
|
-
* Создает пост на стене другого пользователя (wall post)
|
|
75
|
-
*
|
|
78
|
+
* Создает пост на стене другого пользователя (wall post).
|
|
79
|
+
* При любой ошибке возвращает null. Таймаут — client.uploadTimeout (по умолчанию 120 с).
|
|
80
|
+
*
|
|
76
81
|
* @param {string} username - Имя пользователя, на чью стену нужно написать
|
|
77
82
|
* @param {string} text - Текст поста
|
|
78
83
|
* @param {string|null} imagePath - Путь к изображению (опционально)
|
|
@@ -117,9 +122,11 @@ export class PostsManager {
|
|
|
117
122
|
postData.attachmentIds = [uploadedFile.id];
|
|
118
123
|
}
|
|
119
124
|
|
|
120
|
-
// Создаем пост на
|
|
121
|
-
const response = await this.axios.post(postUrl, postData
|
|
122
|
-
|
|
125
|
+
// Создаем пост на стене; увеличенный таймаут для тяжёлых запросов
|
|
126
|
+
const response = await this.axios.post(postUrl, postData, {
|
|
127
|
+
timeout: this.client.uploadTimeout ?? 120000,
|
|
128
|
+
});
|
|
129
|
+
|
|
123
130
|
if (response.status === 200 || response.status === 201) {
|
|
124
131
|
return response.data;
|
|
125
132
|
} else {
|
package/src/token-storage.js
CHANGED
|
@@ -3,27 +3,24 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import { fileURLToPath } from 'url';
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
6
|
|
|
11
7
|
/**
|
|
12
8
|
* Обновляет ITD_ACCESS_TOKEN в .env файле
|
|
13
|
-
*
|
|
9
|
+
*
|
|
14
10
|
* @param {string} newToken - Новый access token
|
|
11
|
+
* @param {string} [envPath] - Путь к .env (по умолчанию process.cwd() + '/.env')
|
|
15
12
|
* @returns {Promise<boolean>} True если успешно
|
|
16
13
|
*/
|
|
17
|
-
export async function saveAccessToken(newToken) {
|
|
14
|
+
export async function saveAccessToken(newToken, envPath = null) {
|
|
18
15
|
try {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
if (!fs.existsSync(
|
|
16
|
+
const targetPath = envPath ?? path.join(process.cwd(), '.env');
|
|
17
|
+
|
|
18
|
+
if (!fs.existsSync(targetPath)) {
|
|
22
19
|
console.warn('⚠️ Файл .env не найден, токен не сохранен');
|
|
23
20
|
return false;
|
|
24
21
|
}
|
|
25
|
-
|
|
26
|
-
let content = fs.readFileSync(
|
|
22
|
+
|
|
23
|
+
let content = fs.readFileSync(targetPath, 'utf8');
|
|
27
24
|
|
|
28
25
|
// Ищем строку с ITD_ACCESS_TOKEN
|
|
29
26
|
const tokenRegex = /^ITD_ACCESS_TOKEN=.*$/m;
|
|
@@ -36,7 +33,7 @@ export async function saveAccessToken(newToken) {
|
|
|
36
33
|
content += `\nITD_ACCESS_TOKEN=${newToken}\n`;
|
|
37
34
|
}
|
|
38
35
|
|
|
39
|
-
fs.writeFileSync(
|
|
36
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
40
37
|
console.log('✅ Токен сохранен в .env');
|
|
41
38
|
return true;
|
|
42
39
|
} catch (error) {
|
|
@@ -47,16 +44,17 @@ export async function saveAccessToken(newToken) {
|
|
|
47
44
|
|
|
48
45
|
/**
|
|
49
46
|
* Сохраняет cookies в файл .cookies
|
|
50
|
-
*
|
|
47
|
+
*
|
|
51
48
|
* @param {string} newCookieHeader - Новый cookie header
|
|
49
|
+
* @param {string} [cookiesPath] - Путь к .cookies (по умолчанию process.cwd() + '/.cookies')
|
|
52
50
|
* @returns {Promise<boolean>} True если успешно
|
|
53
51
|
*/
|
|
54
|
-
export async function saveCookieHeader(newCookieHeader) {
|
|
52
|
+
export async function saveCookieHeader(newCookieHeader, cookiesPath = null) {
|
|
55
53
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
54
|
+
const targetPath = cookiesPath ?? path.join(process.cwd(), '.cookies');
|
|
55
|
+
|
|
58
56
|
// Просто записываем cookies в файл (одна строка)
|
|
59
|
-
fs.writeFileSync(
|
|
57
|
+
fs.writeFileSync(targetPath, newCookieHeader, 'utf8');
|
|
60
58
|
console.log('✅ Cookies сохранены в .cookies');
|
|
61
59
|
return true;
|
|
62
60
|
} catch (error) {
|
package/src/users.js
CHANGED
|
@@ -295,9 +295,10 @@ export class UsersManager {
|
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
/**
|
|
298
|
-
* Получает топ кланов по количеству
|
|
299
|
-
*
|
|
300
|
-
*
|
|
298
|
+
* Получает топ кланов по количеству участников.
|
|
299
|
+
* Возвращает массив (Array), не объект с полем clans.
|
|
300
|
+
*
|
|
301
|
+
* @returns {Promise<Array|null>} Массив кланов [{ avatar, memberCount }, ...] или null при ошибке
|
|
301
302
|
*/
|
|
302
303
|
async getTopClans() {
|
|
303
304
|
try {
|