itd-sdk-js 1.0.0
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/.cookies.example +1 -0
- package/.env.example +45 -0
- package/API_REFERENCE.md +237 -0
- package/README.md +85 -0
- package/examples/README.md +65 -0
- package/examples/auto-refresh.js +63 -0
- package/examples/basic-usage.js +54 -0
- package/examples/quick-start.js +326 -0
- package/examples/user-friendly.js +79 -0
- package/package.json +57 -0
- package/src/auth.js +126 -0
- package/src/client.js +920 -0
- package/src/comments.js +256 -0
- package/src/files.js +59 -0
- package/src/hashtags.js +101 -0
- package/src/notifications.js +215 -0
- package/src/posts.js +480 -0
- package/src/reports.js +97 -0
- package/src/search.js +86 -0
- package/src/token-storage.js +66 -0
- package/src/users.js +416 -0
package/src/posts.js
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Модуль работы с постами
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import FormData from 'form-data';
|
|
6
|
+
|
|
7
|
+
export class PostsManager {
|
|
8
|
+
/**
|
|
9
|
+
* Управление постами
|
|
10
|
+
*
|
|
11
|
+
* @param {ITDClient} client - Главный клиент
|
|
12
|
+
*/
|
|
13
|
+
constructor(client) {
|
|
14
|
+
this.client = client;
|
|
15
|
+
this.axios = client.axios;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Создает новый пост
|
|
20
|
+
*
|
|
21
|
+
* @param {string} text - Текст поста
|
|
22
|
+
* @param {string|null} imagePath - Путь к изображению (опционально)
|
|
23
|
+
* @returns {Promise<Object|null>} Данные созданного поста или null при ошибке
|
|
24
|
+
*/
|
|
25
|
+
async createPost(text, imagePath = null) {
|
|
26
|
+
if (!await this.client.auth.checkAuth()) {
|
|
27
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// Реальный URL из анализа
|
|
33
|
+
const postUrl = `${this.client.baseUrl}/api/posts`;
|
|
34
|
+
|
|
35
|
+
// Подготовка данных (реальная структура из анализа)
|
|
36
|
+
let postData = {
|
|
37
|
+
content: text, // Реальное поле называется "content"
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Если есть изображение
|
|
41
|
+
if (imagePath) {
|
|
42
|
+
// Сначала загружаем файл через /api/files/upload
|
|
43
|
+
const uploadedFile = await this.client.files.uploadFile(imagePath);
|
|
44
|
+
if (!uploadedFile) {
|
|
45
|
+
console.error('Ошибка: не удалось загрузить изображение');
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Затем создаем пост с ID загруженного файла
|
|
50
|
+
postData.attachments = [uploadedFile.id];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Создаем пост (с изображением или без)
|
|
54
|
+
const response = await this.axios.post(postUrl, postData);
|
|
55
|
+
|
|
56
|
+
if (response.status === 200 || response.status === 201) {
|
|
57
|
+
return response.data;
|
|
58
|
+
} else {
|
|
59
|
+
console.error(`Ошибка создания поста: ${response.status} - ${JSON.stringify(response.data)}`);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error('Исключение при создании поста:', error.message);
|
|
64
|
+
if (error.response) {
|
|
65
|
+
console.error('Response status:', error.response.status);
|
|
66
|
+
console.error('Response data:', error.response.data);
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Получает список постов пользователя или ленту
|
|
74
|
+
*
|
|
75
|
+
* @param {string|null} username - Имя пользователя (null = лента/свои посты)
|
|
76
|
+
* @param {number} limit - Количество постов
|
|
77
|
+
* @param {string} sort - Сортировка: "new" (новые), "old" (старые), "popular" (популярные), "trending" (трендовые, аналог popular), "recent" (недавние, аналог new)
|
|
78
|
+
* @param {string|null} cursor - Курсор для пагинации (из pagination.nextCursor)
|
|
79
|
+
* @param {string|null} tab - Тип ленты: "popular" (популярные), "following" (из подписок), null (обычная лента)
|
|
80
|
+
* @param {string|null} type - Альтернативный способ указать тип: "trending" (аналог tab=popular)
|
|
81
|
+
* @param {string|null} filter - Альтернативный способ указать фильтр: "trending" (аналог tab=popular)
|
|
82
|
+
* @returns {Promise<Object>} Объект с постами и пагинацией: { posts: [], pagination: {} }
|
|
83
|
+
*
|
|
84
|
+
* @note Параметры type и filter являются альтернативными способами получить те же данные,
|
|
85
|
+
* что и tab/sort. Они добавлены для совместимости с различными вариантами API.
|
|
86
|
+
*/
|
|
87
|
+
async getPosts(username = null, limit = 20, sort = 'new', cursor = null, tab = null, type = null, filter = null) {
|
|
88
|
+
// Если username не указан и tab указан, запрашиваем ленту - требуется авторизация
|
|
89
|
+
if (!username && (tab || type || filter) && !await this.client.auth.checkAuth()) {
|
|
90
|
+
console.error('Ошибка: необходимо войти в аккаунт для получения ленты');
|
|
91
|
+
return { posts: [], pagination: {} };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
let postsUrl;
|
|
96
|
+
const params = {
|
|
97
|
+
limit: limit,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (username) {
|
|
101
|
+
// Посты конкретного пользователя (публичные, auth не требуется)
|
|
102
|
+
postsUrl = `${this.client.baseUrl}/api/posts/user/${username}`;
|
|
103
|
+
params.sort = sort;
|
|
104
|
+
} else {
|
|
105
|
+
// Лента (популярные, из подписок, или обычная)
|
|
106
|
+
postsUrl = `${this.client.baseUrl}/api/posts`;
|
|
107
|
+
|
|
108
|
+
// Приоритет: tab > type > filter > sort
|
|
109
|
+
if (tab) {
|
|
110
|
+
params.tab = tab; // "popular" или "following"
|
|
111
|
+
} else if (type) {
|
|
112
|
+
params.type = type; // "trending" (аналог tab=popular)
|
|
113
|
+
} else if (filter) {
|
|
114
|
+
params.filter = filter; // "trending" (аналог tab=popular)
|
|
115
|
+
} else {
|
|
116
|
+
params.sort = sort; // "new", "old", "popular", "trending", "recent"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (cursor) {
|
|
121
|
+
params.cursor = cursor;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const response = await this.axios.get(postsUrl, { params });
|
|
125
|
+
|
|
126
|
+
if (response.status === 200) {
|
|
127
|
+
const data = response.data;
|
|
128
|
+
|
|
129
|
+
// Реальная структура: { data: { posts: [...], pagination: {...} } }
|
|
130
|
+
if (data.data && data.data.posts) {
|
|
131
|
+
return {
|
|
132
|
+
posts: data.data.posts,
|
|
133
|
+
pagination: data.data.pagination || {}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Fallback для других возможных структур
|
|
138
|
+
if (Array.isArray(data)) {
|
|
139
|
+
return { posts: data, pagination: {} };
|
|
140
|
+
} else if (data.posts && Array.isArray(data.posts)) {
|
|
141
|
+
return { posts: data.posts, pagination: data.pagination || {} };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { posts: [], pagination: {} };
|
|
145
|
+
} else {
|
|
146
|
+
console.error(`Ошибка получения постов: ${response.status}`);
|
|
147
|
+
return { posts: [], pagination: {} };
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('Исключение при получении постов:', error.message);
|
|
151
|
+
if (error.response) {
|
|
152
|
+
console.error('Response status:', error.response.status);
|
|
153
|
+
console.error('Response data:', error.response.data);
|
|
154
|
+
}
|
|
155
|
+
return { posts: [], pagination: {} };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Получает популярные посты (лента популярного)
|
|
161
|
+
*
|
|
162
|
+
* @param {number} limit - Количество постов
|
|
163
|
+
* @param {string|null} cursor - Курсор для пагинации
|
|
164
|
+
* @returns {Promise<Object>} Объект с постами и пагинацией: { posts: [], pagination: {} }
|
|
165
|
+
*/
|
|
166
|
+
async getFeedPopular(limit = 20, cursor = null) {
|
|
167
|
+
return await this.getPosts(null, limit, 'new', cursor, 'popular');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Получает посты из подписок (лента подписок)
|
|
172
|
+
*
|
|
173
|
+
* @param {number} limit - Количество постов
|
|
174
|
+
* @param {string|null} cursor - Курсор для пагинации
|
|
175
|
+
* @returns {Promise<Object>} Объект с постами и пагинацией: { posts: [], pagination: {} }
|
|
176
|
+
*/
|
|
177
|
+
async getFeedFollowing(limit = 20, cursor = null) {
|
|
178
|
+
if (!await this.client.auth.checkAuth()) {
|
|
179
|
+
console.error('Ошибка: необходимо войти в аккаунт для получения ленты подписок');
|
|
180
|
+
return { posts: [], pagination: {} };
|
|
181
|
+
}
|
|
182
|
+
return await this.getPosts(null, limit, 'new', cursor, 'following');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Получает конкретный пост по ID
|
|
188
|
+
*
|
|
189
|
+
* @param {string} postId - ID поста
|
|
190
|
+
* @returns {Promise<Object|null>} Данные поста или null
|
|
191
|
+
*
|
|
192
|
+
* Примечание: Авторизация не требуется для просмотра публичных постов
|
|
193
|
+
*/
|
|
194
|
+
async getPost(postId) {
|
|
195
|
+
try {
|
|
196
|
+
// Реальный URL из анализа
|
|
197
|
+
const postUrl = `${this.client.baseUrl}/api/posts/${postId}`;
|
|
198
|
+
const response = await this.axios.get(postUrl);
|
|
199
|
+
|
|
200
|
+
if (response.status === 200) {
|
|
201
|
+
// Структура ответа: { data: { id, content, comments: [...], ... } }
|
|
202
|
+
const post = response.data.data || response.data;
|
|
203
|
+
// Комментарии могут быть вложены в пост
|
|
204
|
+
return post;
|
|
205
|
+
} else {
|
|
206
|
+
console.error(`Ошибка получения поста: ${response.status}`);
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error('Исключение при получении поста:', error.message);
|
|
211
|
+
if (error.response) {
|
|
212
|
+
console.error('Response status:', error.response.status);
|
|
213
|
+
console.error('Response data:', error.response.data);
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Редактирует существующий пост
|
|
221
|
+
*
|
|
222
|
+
* @param {string} postId - ID поста
|
|
223
|
+
* @param {string} newContent - Новый текст поста
|
|
224
|
+
* @returns {Promise<Object|null>} Обновленные данные поста или null
|
|
225
|
+
*/
|
|
226
|
+
async editPost(postId, newContent) {
|
|
227
|
+
if (!await this.client.auth.checkAuth()) {
|
|
228
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
// Реальный URL из анализа: PUT /api/posts/{id}
|
|
234
|
+
const editUrl = `${this.client.baseUrl}/api/posts/${postId}`;
|
|
235
|
+
|
|
236
|
+
const postData = {
|
|
237
|
+
content: newContent, // Реальное поле называется "content"
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const response = await this.axios.put(editUrl, postData);
|
|
241
|
+
|
|
242
|
+
if (response.status === 200) {
|
|
243
|
+
return response.data;
|
|
244
|
+
} else {
|
|
245
|
+
console.error(`Ошибка редактирования поста: ${response.status} - ${JSON.stringify(response.data)}`);
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.error('Исключение при редактировании поста:', error.message);
|
|
250
|
+
if (error.response) {
|
|
251
|
+
console.error('Response status:', error.response.status);
|
|
252
|
+
console.error('Response data:', error.response.data);
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Удаляет пост
|
|
260
|
+
*
|
|
261
|
+
* @param {string} postId - ID поста
|
|
262
|
+
* @returns {Promise<boolean>} True если успешно
|
|
263
|
+
*/
|
|
264
|
+
async deletePost(postId) {
|
|
265
|
+
if (!await this.client.auth.checkAuth()) {
|
|
266
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
// Реальный URL из анализа
|
|
272
|
+
const deleteUrl = `${this.client.baseUrl}/api/posts/${postId}`;
|
|
273
|
+
const response = await this.axios.delete(deleteUrl);
|
|
274
|
+
|
|
275
|
+
if (response.status === 200 || response.status === 204) {
|
|
276
|
+
return true;
|
|
277
|
+
} else {
|
|
278
|
+
console.error(`Ошибка удаления поста: ${response.status}`);
|
|
279
|
+
if (response.data) {
|
|
280
|
+
console.error('Response data:', response.data);
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error('Исключение при удалении поста:', error.message);
|
|
286
|
+
if (error.response) {
|
|
287
|
+
console.error('Response status:', error.response.status);
|
|
288
|
+
console.error('Response data:', error.response.data);
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Закрепляет пост
|
|
296
|
+
*
|
|
297
|
+
* @param {string} postId - ID поста
|
|
298
|
+
* @returns {Promise<boolean>} True если успешно
|
|
299
|
+
*/
|
|
300
|
+
async pinPost(postId) {
|
|
301
|
+
if (!await this.client.auth.checkAuth()) {
|
|
302
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
const pinUrl = `${this.client.baseUrl}/api/posts/${postId}/pin`;
|
|
308
|
+
const response = await this.axios.post(pinUrl);
|
|
309
|
+
|
|
310
|
+
if (response.status === 200 || response.status === 201) {
|
|
311
|
+
return true;
|
|
312
|
+
} else {
|
|
313
|
+
console.error(`Ошибка закрепления поста: ${response.status}`);
|
|
314
|
+
if (response.data) {
|
|
315
|
+
console.error('Response data:', response.data);
|
|
316
|
+
}
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.error('Исключение при закреплении поста:', error.message);
|
|
321
|
+
if (error.response) {
|
|
322
|
+
console.error('Response status:', error.response.status);
|
|
323
|
+
console.error('Response data:', error.response.data);
|
|
324
|
+
}
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Делает репост
|
|
331
|
+
*
|
|
332
|
+
* @param {string} postId - ID поста для репоста
|
|
333
|
+
* @param {string|null} comment - Комментарий к репосту (опционально)
|
|
334
|
+
* @returns {Promise<Object|null>} Данные созданного репоста или null при ошибке
|
|
335
|
+
*/
|
|
336
|
+
async repost(postId, comment = null) {
|
|
337
|
+
if (!await this.client.auth.checkAuth()) {
|
|
338
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
const repostUrl = `${this.client.baseUrl}/api/posts/${postId}/repost`;
|
|
344
|
+
|
|
345
|
+
const repostData = {};
|
|
346
|
+
if (comment !== null && comment !== undefined && comment !== '') {
|
|
347
|
+
repostData.content = comment;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const response = await this.axios.post(repostUrl, repostData);
|
|
351
|
+
|
|
352
|
+
if (response.status === 200 || response.status === 201) {
|
|
353
|
+
return response.data;
|
|
354
|
+
} else {
|
|
355
|
+
console.error(`Ошибка репоста: ${response.status}`);
|
|
356
|
+
if (response.data) {
|
|
357
|
+
console.error('Response data:', response.data);
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
} catch (error) {
|
|
362
|
+
console.error('Исключение при репосте:', error.message);
|
|
363
|
+
if (error.response) {
|
|
364
|
+
console.error('Response status:', error.response.status);
|
|
365
|
+
console.error('Response data:', error.response.data);
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ========== USER-FRIENDLY МЕТОДЫ ==========
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Получает трендовые посты (удобный метод)
|
|
375
|
+
*
|
|
376
|
+
* @param {number} limit - Количество постов (по умолчанию 20)
|
|
377
|
+
* @param {string|null} cursor - Курсор для пагинации
|
|
378
|
+
* @returns {Promise<Object>} { posts: [], pagination: {} }
|
|
379
|
+
*/
|
|
380
|
+
async getTrendingPosts(limit = 20, cursor = null) {
|
|
381
|
+
return await this.getPosts(null, limit, 'trending', cursor);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Получает недавние посты (удобный метод)
|
|
386
|
+
*
|
|
387
|
+
* @param {number} limit - Количество постов (по умолчанию 20)
|
|
388
|
+
* @param {string|null} cursor - Курсор для пагинации
|
|
389
|
+
* @returns {Promise<Object>} { posts: [], pagination: {} }
|
|
390
|
+
*/
|
|
391
|
+
async getRecentPosts(limit = 20, cursor = null) {
|
|
392
|
+
return await this.getPosts(null, limit, 'recent', cursor);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Получает свои посты (удобный метод)
|
|
397
|
+
*
|
|
398
|
+
* @param {number} limit - Количество постов (по умолчанию 20)
|
|
399
|
+
* @param {string} sort - Сортировка: 'new', 'old', 'popular' (по умолчанию 'new')
|
|
400
|
+
* @param {string|null} cursor - Курсор для пагинации
|
|
401
|
+
* @returns {Promise<Object>} { posts: [], pagination: {} }
|
|
402
|
+
*/
|
|
403
|
+
async getMyPosts(limit = 20, sort = 'new', cursor = null) {
|
|
404
|
+
if (!await this.client.auth.checkAuth()) {
|
|
405
|
+
console.error('Ошибка: необходимо войти в аккаунт для получения своих постов');
|
|
406
|
+
return { posts: [], pagination: {} };
|
|
407
|
+
}
|
|
408
|
+
// Получаем свой username из профиля
|
|
409
|
+
const profile = await this.client.users.getMyProfile();
|
|
410
|
+
if (!profile || !profile.username) {
|
|
411
|
+
console.error('Ошибка: не удалось получить свой username');
|
|
412
|
+
return { posts: [], pagination: {} };
|
|
413
|
+
}
|
|
414
|
+
return await this.getPosts(profile.username, limit, sort, cursor);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Получает последний пост пользователя (удобный метод)
|
|
419
|
+
*
|
|
420
|
+
* @param {string} username - Имя пользователя
|
|
421
|
+
* @returns {Promise<Object|null>} Последний пост или null
|
|
422
|
+
*/
|
|
423
|
+
async getUserLatestPost(username) {
|
|
424
|
+
const result = await this.getPosts(username, 1, 'new');
|
|
425
|
+
if (result && result.posts && result.posts.length > 0) {
|
|
426
|
+
return result.posts[0];
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Получает количество лайков поста (удобный метод)
|
|
433
|
+
*
|
|
434
|
+
* @param {string} postId - ID поста
|
|
435
|
+
* @returns {Promise<number>} Количество лайков
|
|
436
|
+
*/
|
|
437
|
+
async getPostLikesCount(postId) {
|
|
438
|
+
const post = await this.getPost(postId);
|
|
439
|
+
return post ? (post.likesCount || 0) : 0;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Получает количество просмотров поста (удобный метод)
|
|
444
|
+
*
|
|
445
|
+
* @param {string} postId - ID поста
|
|
446
|
+
* @returns {Promise<number>} Количество просмотров
|
|
447
|
+
*/
|
|
448
|
+
async getPostViewsCount(postId) {
|
|
449
|
+
const post = await this.getPost(postId);
|
|
450
|
+
return post ? (post.viewsCount || 0) : 0;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Получает количество комментариев поста (удобный метод)
|
|
455
|
+
*
|
|
456
|
+
* @param {string} postId - ID поста
|
|
457
|
+
* @returns {Promise<number>} Количество комментариев
|
|
458
|
+
*/
|
|
459
|
+
async getPostCommentsCount(postId) {
|
|
460
|
+
const post = await this.getPost(postId);
|
|
461
|
+
return post ? (post.commentsCount || 0) : 0;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Получает статистику поста (удобный метод)
|
|
466
|
+
*
|
|
467
|
+
* @param {string} postId - ID поста
|
|
468
|
+
* @returns {Promise<Object|null>} { likes: number, views: number, comments: number, reposts: number } или null
|
|
469
|
+
*/
|
|
470
|
+
async getPostStats(postId) {
|
|
471
|
+
const post = await this.getPost(postId);
|
|
472
|
+
if (!post) return null;
|
|
473
|
+
return {
|
|
474
|
+
likes: post.likesCount || 0,
|
|
475
|
+
views: post.viewsCount || 0,
|
|
476
|
+
comments: post.commentsCount || 0,
|
|
477
|
+
reposts: post.repostsCount || 0
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
}
|
package/src/reports.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Менеджер для работы с репортами (жалобами)
|
|
3
|
+
*/
|
|
4
|
+
export class ReportsManager {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
this.axios = client.axios;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Отправляет репорт на пост, комментарий или пользователя
|
|
12
|
+
*
|
|
13
|
+
* @param {string} targetType - Тип цели: "post", "comment", "user"
|
|
14
|
+
* @param {string} targetId - ID цели (поста, комментария или пользователя)
|
|
15
|
+
* @param {string} reason - Причина репорта (например, "other", "spam", "harassment", "inappropriate")
|
|
16
|
+
* @param {string} description - Описание проблемы (опционально)
|
|
17
|
+
* @returns {Promise<Object|null>} { id, createdAt } или null при ошибке
|
|
18
|
+
*/
|
|
19
|
+
async report(targetType, targetId, reason = 'other', description = '') {
|
|
20
|
+
if (!await this.client.auth.checkAuth()) {
|
|
21
|
+
console.error('Ошибка: необходимо войти в аккаунт для отправки репорта');
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const reportUrl = `${this.client.baseUrl}/api/reports`;
|
|
27
|
+
const payload = {
|
|
28
|
+
targetType: targetType,
|
|
29
|
+
targetId: targetId,
|
|
30
|
+
reason: reason,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (description) {
|
|
34
|
+
payload.description = description;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const response = await this.axios.post(reportUrl, payload);
|
|
38
|
+
|
|
39
|
+
if (response.status === 200 || response.status === 201) {
|
|
40
|
+
// Структура ответа: { data: { id, createdAt } }
|
|
41
|
+
if (response.data && response.data.data) {
|
|
42
|
+
return response.data.data;
|
|
43
|
+
}
|
|
44
|
+
return response.data || { success: true };
|
|
45
|
+
} else {
|
|
46
|
+
console.error(`Ошибка отправки репорта: ${response.status}`);
|
|
47
|
+
if (response.data) {
|
|
48
|
+
console.error('Response data:', response.data);
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Исключение при отправке репорта:', error.message);
|
|
54
|
+
if (error.response) {
|
|
55
|
+
console.error('Response status:', error.response.status);
|
|
56
|
+
console.error('Response data:', error.response.data);
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Отправляет репорт на пост
|
|
64
|
+
*
|
|
65
|
+
* @param {string} postId - ID поста
|
|
66
|
+
* @param {string} reason - Причина репорта (по умолчанию "other")
|
|
67
|
+
* @param {string} description - Описание проблемы
|
|
68
|
+
* @returns {Promise<Object|null>} { id, createdAt } или null при ошибке
|
|
69
|
+
*/
|
|
70
|
+
async reportPost(postId, reason = 'other', description = '') {
|
|
71
|
+
return await this.report('post', postId, reason, description);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Отправляет репорт на комментарий
|
|
76
|
+
*
|
|
77
|
+
* @param {string} commentId - ID комментария
|
|
78
|
+
* @param {string} reason - Причина репорта (по умолчанию "other")
|
|
79
|
+
* @param {string} description - Описание проблемы
|
|
80
|
+
* @returns {Promise<Object|null>} { id, createdAt } или null при ошибке
|
|
81
|
+
*/
|
|
82
|
+
async reportComment(commentId, reason = 'other', description = '') {
|
|
83
|
+
return await this.report('comment', commentId, reason, description);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Отправляет репорт на пользователя
|
|
88
|
+
*
|
|
89
|
+
* @param {string} userId - ID пользователя
|
|
90
|
+
* @param {string} reason - Причина репорта (по умолчанию "other")
|
|
91
|
+
* @param {string} description - Описание проблемы
|
|
92
|
+
* @returns {Promise<Object|null>} { id, createdAt } или null при ошибке
|
|
93
|
+
*/
|
|
94
|
+
async reportUser(userId, reason = 'other', description = '') {
|
|
95
|
+
return await this.report('user', userId, reason, description);
|
|
96
|
+
}
|
|
97
|
+
}
|
package/src/search.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Менеджер для работы с поиском
|
|
3
|
+
*/
|
|
4
|
+
export class SearchManager {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
this.axios = client.axios;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Выполняет поиск пользователей и хэштегов
|
|
12
|
+
*
|
|
13
|
+
* @param {string} query - Поисковый запрос
|
|
14
|
+
* @param {number} userLimit - Максимальное количество пользователей (по умолчанию 5)
|
|
15
|
+
* @param {number} hashtagLimit - Максимальное количество хэштегов (по умолчанию 5)
|
|
16
|
+
* @returns {Promise<Object|null>} { users: [], hashtags: [] } или null при ошибке
|
|
17
|
+
*/
|
|
18
|
+
async search(query, userLimit = 5, hashtagLimit = 5) {
|
|
19
|
+
try {
|
|
20
|
+
const searchUrl = `${this.client.baseUrl}/api/search/`;
|
|
21
|
+
const params = {
|
|
22
|
+
q: query,
|
|
23
|
+
userLimit: userLimit,
|
|
24
|
+
hashtagLimit: hashtagLimit,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const response = await this.axios.get(searchUrl, { params });
|
|
28
|
+
|
|
29
|
+
if (response.status === 200) {
|
|
30
|
+
const data = response.data;
|
|
31
|
+
|
|
32
|
+
// Структура ответа: { data: { users: [], hashtags: [] } }
|
|
33
|
+
if (data.data) {
|
|
34
|
+
return {
|
|
35
|
+
users: data.data.users || [],
|
|
36
|
+
hashtags: data.data.hashtags || []
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Fallback
|
|
41
|
+
if (data.users || data.hashtags) {
|
|
42
|
+
return {
|
|
43
|
+
users: data.users || [],
|
|
44
|
+
hashtags: data.hashtags || []
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { users: [], hashtags: [] };
|
|
49
|
+
} else {
|
|
50
|
+
console.error(`Ошибка поиска: ${response.status}`);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error('Исключение при поиске:', error.message);
|
|
55
|
+
if (error.response) {
|
|
56
|
+
console.error('Response status:', error.response.status);
|
|
57
|
+
console.error('Response data:', error.response.data);
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Ищет пользователей
|
|
65
|
+
*
|
|
66
|
+
* @param {string} query - Поисковый запрос
|
|
67
|
+
* @param {number} limit - Максимальное количество пользователей (по умолчанию 5)
|
|
68
|
+
* @returns {Promise<Array|null>} Массив пользователей или null при ошибке
|
|
69
|
+
*/
|
|
70
|
+
async searchUsers(query, limit = 5) {
|
|
71
|
+
const result = await this.search(query, limit, 0);
|
|
72
|
+
return result ? result.users : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Ищет хэштеги
|
|
77
|
+
*
|
|
78
|
+
* @param {string} query - Поисковый запрос
|
|
79
|
+
* @param {number} limit - Максимальное количество хэштегов (по умолчанию 5)
|
|
80
|
+
* @returns {Promise<Array|null>} Массив хэштегов или null при ошибке
|
|
81
|
+
*/
|
|
82
|
+
async searchHashtags(query, limit = 5) {
|
|
83
|
+
const result = await this.search(query, 0, limit);
|
|
84
|
+
return result ? result.hashtags : null;
|
|
85
|
+
}
|
|
86
|
+
}
|