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/comments.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Модуль работы с комментариями
|
|
3
|
+
*/
|
|
4
|
+
export class CommentsManager {
|
|
5
|
+
/**
|
|
6
|
+
* Управление комментариями
|
|
7
|
+
*
|
|
8
|
+
* @param {ITDClient} client - Главный клиент
|
|
9
|
+
*/
|
|
10
|
+
constructor(client) {
|
|
11
|
+
this.client = client;
|
|
12
|
+
this.axios = client.axios;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Добавляет комментарий к посту
|
|
17
|
+
*
|
|
18
|
+
* @param {string} postId - ID поста
|
|
19
|
+
* @param {string} text - Текст комментария
|
|
20
|
+
* @param {string|null} replyToCommentId - ID комментария для ответа (опционально)
|
|
21
|
+
* @returns {Promise<Object|null>} Данные созданного комментария или null
|
|
22
|
+
*/
|
|
23
|
+
async addComment(postId, text, replyToCommentId = null) {
|
|
24
|
+
if (!await this.client.auth.checkAuth()) {
|
|
25
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const commentUrl = `${this.client.baseUrl}/api/posts/${postId}/comments`;
|
|
31
|
+
const commentData = {
|
|
32
|
+
content: text, // Реальное поле - content
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Если это ответ на комментарий
|
|
36
|
+
if (replyToCommentId) {
|
|
37
|
+
commentData.replyTo = replyToCommentId;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const response = await this.axios.post(commentUrl, commentData);
|
|
41
|
+
|
|
42
|
+
if (response.status === 200 || response.status === 201) {
|
|
43
|
+
return response.data;
|
|
44
|
+
} else {
|
|
45
|
+
console.error(`Ошибка добавления комментария: ${response.status} - ${JSON.stringify(response.data)}`);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Исключение при добавлении комментария:', error.message);
|
|
50
|
+
if (error.response) {
|
|
51
|
+
console.error('Response status:', error.response.status);
|
|
52
|
+
console.error('Response data:', error.response.data);
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Получает комментарии к посту
|
|
60
|
+
*
|
|
61
|
+
* @param {string} postId - ID поста
|
|
62
|
+
* @param {number} limit - Количество комментариев
|
|
63
|
+
* @param {string} sort - Сортировка: "popular", "new", "old" (по умолчанию "popular")
|
|
64
|
+
* @returns {Promise<Object>} { comments: [], total, hasMore, nextCursor } или { comments: [] } при ошибке
|
|
65
|
+
*
|
|
66
|
+
* Примечание: Авторизация не требуется для просмотра комментариев
|
|
67
|
+
*/
|
|
68
|
+
async getComments(postId, limit = 20, sort = 'popular') {
|
|
69
|
+
try {
|
|
70
|
+
const commentsUrl = `${this.client.baseUrl}/api/posts/${postId}/comments`;
|
|
71
|
+
const params = {
|
|
72
|
+
limit: limit,
|
|
73
|
+
sort: sort,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const response = await this.axios.get(commentsUrl, { params });
|
|
77
|
+
|
|
78
|
+
if (response.status === 200) {
|
|
79
|
+
const data = response.data;
|
|
80
|
+
// Структура: { data: { comments: [...], total, hasMore, nextCursor } }
|
|
81
|
+
if (data.data && data.data.comments) {
|
|
82
|
+
return {
|
|
83
|
+
comments: data.data.comments,
|
|
84
|
+
total: data.data.total || 0,
|
|
85
|
+
hasMore: data.data.hasMore || false,
|
|
86
|
+
nextCursor: data.data.nextCursor || null
|
|
87
|
+
};
|
|
88
|
+
} else if (data.comments) {
|
|
89
|
+
return {
|
|
90
|
+
comments: data.comments,
|
|
91
|
+
total: data.total || 0,
|
|
92
|
+
hasMore: data.hasMore || false,
|
|
93
|
+
nextCursor: data.nextCursor || null
|
|
94
|
+
};
|
|
95
|
+
} else if (Array.isArray(data)) {
|
|
96
|
+
return {
|
|
97
|
+
comments: data,
|
|
98
|
+
total: data.length,
|
|
99
|
+
hasMore: false,
|
|
100
|
+
nextCursor: null
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return { comments: [], total: 0, hasMore: false, nextCursor: null };
|
|
104
|
+
} else {
|
|
105
|
+
console.error(`Ошибка получения комментариев: ${response.status}`);
|
|
106
|
+
return { comments: [], total: 0, hasMore: false, nextCursor: null };
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error('Исключение при получении комментариев:', error.message);
|
|
110
|
+
if (error.response) {
|
|
111
|
+
console.error('Response status:', error.response.status);
|
|
112
|
+
console.error('Response data:', error.response.data);
|
|
113
|
+
}
|
|
114
|
+
return { comments: [], total: 0, hasMore: false, nextCursor: null };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Ставит лайк на комментарий
|
|
120
|
+
*
|
|
121
|
+
* @param {string} commentId - ID комментария
|
|
122
|
+
* @returns {Promise<Object|null>} { liked: true, likesCount: number } или null при ошибке
|
|
123
|
+
*/
|
|
124
|
+
async likeComment(commentId) {
|
|
125
|
+
if (!await this.client.auth.checkAuth()) {
|
|
126
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const likeUrl = `${this.client.baseUrl}/api/comments/${commentId}/like`;
|
|
132
|
+
const response = await this.axios.post(likeUrl);
|
|
133
|
+
|
|
134
|
+
if (response.status === 200 || response.status === 201) {
|
|
135
|
+
return response.data; // { liked: true, likesCount: number }
|
|
136
|
+
} else {
|
|
137
|
+
console.error(`Ошибка лайка комментария: ${response.status} - ${JSON.stringify(response.data)}`);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error('Исключение при лайке комментария:', error.message);
|
|
142
|
+
if (error.response) {
|
|
143
|
+
console.error('Response:', error.response.status, error.response.data);
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Убирает лайк с комментария
|
|
151
|
+
*
|
|
152
|
+
* @param {string} commentId - ID комментария
|
|
153
|
+
* @returns {Promise<Object|null>} { liked: false, likesCount: number } или null при ошибке
|
|
154
|
+
*/
|
|
155
|
+
async unlikeComment(commentId) {
|
|
156
|
+
if (!await this.client.auth.checkAuth()) {
|
|
157
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const unlikeUrl = `${this.client.baseUrl}/api/comments/${commentId}/like`;
|
|
163
|
+
const response = await this.axios.delete(unlikeUrl);
|
|
164
|
+
|
|
165
|
+
if (response.status === 200 || response.status === 204) {
|
|
166
|
+
return response.data || { liked: false, likesCount: 0 };
|
|
167
|
+
} else {
|
|
168
|
+
console.error(`Ошибка убирания лайка с комментария: ${response.status}`);
|
|
169
|
+
if (response.data) {
|
|
170
|
+
console.error('Response data:', response.data);
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error('Исключение при убирании лайка с комментария:', error.message);
|
|
176
|
+
if (error.response) {
|
|
177
|
+
console.error('Response status:', error.response.status);
|
|
178
|
+
console.error('Response data:', error.response.data);
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Удаляет комментарий
|
|
186
|
+
*
|
|
187
|
+
* @param {number} commentId - ID комментария
|
|
188
|
+
* @returns {Promise<boolean>} True если успешно
|
|
189
|
+
*/
|
|
190
|
+
async deleteComment(commentId) {
|
|
191
|
+
if (!await this.client.auth.checkAuth()) {
|
|
192
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const deleteUrl = `${this.client.baseUrl}/api/comments/${commentId}`;
|
|
198
|
+
const response = await this.axios.delete(deleteUrl);
|
|
199
|
+
|
|
200
|
+
if (response.status === 200 || response.status === 204) {
|
|
201
|
+
return true;
|
|
202
|
+
} else {
|
|
203
|
+
console.error(`Ошибка удаления комментария: ${response.status}`);
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error('Исключение при удалении комментария:', error.message);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ========== USER-FRIENDLY МЕТОДЫ ==========
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Получает количество комментариев поста (удобный метод)
|
|
216
|
+
*
|
|
217
|
+
* @param {string} postId - ID поста
|
|
218
|
+
* @returns {Promise<number>} Количество комментариев
|
|
219
|
+
*/
|
|
220
|
+
async getPostCommentsCount(postId) {
|
|
221
|
+
const result = await this.getComments(postId, 1);
|
|
222
|
+
if (result && result.total !== undefined) {
|
|
223
|
+
return result.total;
|
|
224
|
+
}
|
|
225
|
+
// Fallback: получаем через пост
|
|
226
|
+
const post = await this.client.posts.getPost(postId);
|
|
227
|
+
return post ? (post.commentsCount || 0) : 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Получает топ-комментарий поста (с наибольшим количеством лайков) (удобный метод)
|
|
232
|
+
*
|
|
233
|
+
* @param {string} postId - ID поста
|
|
234
|
+
* @returns {Promise<Object|null>} Топ-комментарий или null
|
|
235
|
+
*/
|
|
236
|
+
async getTopComment(postId) {
|
|
237
|
+
const result = await this.getComments(postId, 20, 'popular');
|
|
238
|
+
if (result && result.comments && result.comments.length > 0) {
|
|
239
|
+
// Сортируем по лайкам и возвращаем первый
|
|
240
|
+
const sorted = [...result.comments].sort((a, b) => (b.likesCount || 0) - (a.likesCount || 0));
|
|
241
|
+
return sorted[0];
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Проверяет, есть ли комментарии у поста (удобный метод)
|
|
248
|
+
*
|
|
249
|
+
* @param {string} postId - ID поста
|
|
250
|
+
* @returns {Promise<boolean>} True если есть комментарии
|
|
251
|
+
*/
|
|
252
|
+
async hasComments(postId) {
|
|
253
|
+
const count = await this.getPostCommentsCount(postId);
|
|
254
|
+
return count > 0;
|
|
255
|
+
}
|
|
256
|
+
}
|
package/src/files.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Модуль для работы с файлами
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import FormData from 'form-data';
|
|
6
|
+
|
|
7
|
+
export class FilesManager {
|
|
8
|
+
constructor(client) {
|
|
9
|
+
this.client = client;
|
|
10
|
+
this.axios = client.axios;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Загружает файл (изображение) на сервер
|
|
15
|
+
*
|
|
16
|
+
* @param {string} filePath - Путь к файлу
|
|
17
|
+
* @returns {Promise<Object|null>} { id, url, filename, mimeType, size } или null при ошибке
|
|
18
|
+
*/
|
|
19
|
+
async uploadFile(filePath) {
|
|
20
|
+
if (!await this.client.auth.checkAuth()) {
|
|
21
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Проверка существования файла
|
|
27
|
+
if (!fs.existsSync(filePath)) {
|
|
28
|
+
console.error(`Ошибка: файл ${filePath} не найден`);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const uploadUrl = `${this.client.baseUrl}/api/files/upload`;
|
|
33
|
+
|
|
34
|
+
// Создаем FormData для multipart/form-data
|
|
35
|
+
const formData = new FormData();
|
|
36
|
+
formData.append('file', fs.createReadStream(filePath));
|
|
37
|
+
|
|
38
|
+
const response = await this.axios.post(uploadUrl, formData, {
|
|
39
|
+
headers: {
|
|
40
|
+
...formData.getHeaders(),
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (response.status === 200 || response.status === 201) {
|
|
45
|
+
return response.data; // { id, url, filename, mimeType, size }
|
|
46
|
+
} else {
|
|
47
|
+
console.error(`Ошибка загрузки файла: ${response.status} - ${JSON.stringify(response.data)}`);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('Исключение при загрузке файла:', error.message);
|
|
52
|
+
if (error.response) {
|
|
53
|
+
console.error('Response status:', error.response.status);
|
|
54
|
+
console.error('Response data:', error.response.data);
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/hashtags.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Модуль для работы с хэштегами
|
|
3
|
+
*/
|
|
4
|
+
export class HashtagsManager {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
this.axios = client.axios;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Получает трендовые хэштеги
|
|
12
|
+
*
|
|
13
|
+
* @param {number} limit - Количество хэштегов (по умолчанию 10)
|
|
14
|
+
* @returns {Promise<Object|null>} { hashtags: [] } или null при ошибке
|
|
15
|
+
*/
|
|
16
|
+
async getTrending(limit = 10) {
|
|
17
|
+
try {
|
|
18
|
+
const trendingUrl = `${this.client.baseUrl}/api/hashtags/trending`;
|
|
19
|
+
const params = { limit };
|
|
20
|
+
const response = await this.axios.get(trendingUrl, { params });
|
|
21
|
+
|
|
22
|
+
if (response.status === 200) {
|
|
23
|
+
const data = response.data;
|
|
24
|
+
// Структура: { data: { hashtags: [...] } }
|
|
25
|
+
if (data.data && data.data.hashtags) {
|
|
26
|
+
return {
|
|
27
|
+
hashtags: data.data.hashtags
|
|
28
|
+
};
|
|
29
|
+
} else if (data.hashtags) {
|
|
30
|
+
return {
|
|
31
|
+
hashtags: data.hashtags
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return { hashtags: [] };
|
|
35
|
+
} else {
|
|
36
|
+
console.error(`Ошибка получения трендовых хэштегов: ${response.status}`);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('Исключение при получении трендовых хэштегов:', error.message);
|
|
41
|
+
if (error.response) {
|
|
42
|
+
console.error('Response status:', error.response.status);
|
|
43
|
+
console.error('Response data:', error.response.data);
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Получает посты по хэштегу
|
|
51
|
+
*
|
|
52
|
+
* @param {string} hashtagName - Имя хэштега (без #)
|
|
53
|
+
* @param {number} limit - Количество постов (по умолчанию 20)
|
|
54
|
+
* @param {string|null} cursor - Курсор для пагинации
|
|
55
|
+
* @returns {Promise<Object|null>} { posts: [], hashtag: {}, pagination: {} } или null при ошибке
|
|
56
|
+
*/
|
|
57
|
+
async getPostsByHashtag(hashtagName, limit = 20, cursor = null) {
|
|
58
|
+
try {
|
|
59
|
+
// Убираем # если есть
|
|
60
|
+
const cleanHashtag = hashtagName.replace(/^#/, '');
|
|
61
|
+
const hashtagUrl = `${this.client.baseUrl}/api/hashtags/${encodeURIComponent(cleanHashtag)}/posts`;
|
|
62
|
+
|
|
63
|
+
const params = { limit };
|
|
64
|
+
if (cursor) {
|
|
65
|
+
params.cursor = cursor;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const response = await this.axios.get(hashtagUrl, { params });
|
|
69
|
+
|
|
70
|
+
if (response.status === 200) {
|
|
71
|
+
const data = response.data;
|
|
72
|
+
|
|
73
|
+
// Структура: { data: { hashtag: {...} или null, posts: [...], pagination: {...} } }
|
|
74
|
+
if (data.data) {
|
|
75
|
+
return {
|
|
76
|
+
hashtag: data.data.hashtag || null,
|
|
77
|
+
posts: data.data.posts || [],
|
|
78
|
+
pagination: data.data.pagination || {}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Fallback
|
|
83
|
+
return {
|
|
84
|
+
hashtag: null,
|
|
85
|
+
posts: data.posts || [],
|
|
86
|
+
pagination: data.pagination || {}
|
|
87
|
+
};
|
|
88
|
+
} else {
|
|
89
|
+
console.error(`Ошибка получения постов по хэштегу: ${response.status}`);
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('Исключение при получении постов по хэштегу:', error.message);
|
|
94
|
+
if (error.response) {
|
|
95
|
+
console.error('Response status:', error.response.status);
|
|
96
|
+
console.error('Response data:', error.response.data);
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Модуль для работы с уведомлениями
|
|
3
|
+
*/
|
|
4
|
+
export class NotificationsManager {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
this.axios = client.axios;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Получает список уведомлений
|
|
12
|
+
*
|
|
13
|
+
* @param {number} limit - Количество уведомлений
|
|
14
|
+
* @param {string|null} cursor - Курсор для пагинации
|
|
15
|
+
* @param {string|null} type - Фильтр по типу: 'reply', 'like', 'wall_post', 'follow', 'comment' (опционально)
|
|
16
|
+
* @returns {Promise<Object|null>} { notifications: [], pagination: {} } или null при ошибке
|
|
17
|
+
*/
|
|
18
|
+
async getNotifications(limit = 20, cursor = null, type = null) {
|
|
19
|
+
if (!await this.client.auth.checkAuth()) {
|
|
20
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const notificationsUrl = `${this.client.baseUrl}/api/notifications`;
|
|
26
|
+
const params = { limit };
|
|
27
|
+
if (cursor) {
|
|
28
|
+
params.cursor = cursor;
|
|
29
|
+
}
|
|
30
|
+
// Пробуем передать type в параметрах (если API поддерживает)
|
|
31
|
+
if (type) {
|
|
32
|
+
params.type = type;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const response = await this.axios.get(notificationsUrl, { params });
|
|
36
|
+
|
|
37
|
+
if (response.status === 200) {
|
|
38
|
+
const data = response.data;
|
|
39
|
+
let notifications = [];
|
|
40
|
+
let pagination = {};
|
|
41
|
+
|
|
42
|
+
// Предполагаемая структура: { data: { notifications: [...], pagination: {...} } }
|
|
43
|
+
if (data.data && data.data.notifications) {
|
|
44
|
+
notifications = data.data.notifications;
|
|
45
|
+
pagination = data.data.pagination || {};
|
|
46
|
+
} else if (Array.isArray(data)) {
|
|
47
|
+
notifications = data;
|
|
48
|
+
} else if (data.notifications) {
|
|
49
|
+
notifications = data.notifications;
|
|
50
|
+
pagination = data.pagination || {};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Фильтруем по типу на клиенте (если API не поддерживает фильтрацию)
|
|
54
|
+
if (type && notifications.length > 0) {
|
|
55
|
+
notifications = notifications.filter(notif => notif.type === type);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
notifications: notifications,
|
|
60
|
+
pagination: pagination
|
|
61
|
+
};
|
|
62
|
+
} else {
|
|
63
|
+
console.error(`Ошибка получения уведомлений: ${response.status}`);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('Исключение при получении уведомлений:', error.message);
|
|
68
|
+
if (error.response) {
|
|
69
|
+
console.error('Response status:', error.response.status);
|
|
70
|
+
console.error('Response data:', error.response.data);
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Отмечает уведомление как прочитанное
|
|
78
|
+
*
|
|
79
|
+
* @param {string} notificationId - ID уведомления
|
|
80
|
+
* @returns {Promise<Object|null>} { success: true } или null при ошибке
|
|
81
|
+
*/
|
|
82
|
+
async markAsRead(notificationId) {
|
|
83
|
+
if (!await this.client.auth.checkAuth()) {
|
|
84
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const readUrl = `${this.client.baseUrl}/api/notifications/${notificationId}/read`;
|
|
90
|
+
const response = await this.axios.post(readUrl);
|
|
91
|
+
|
|
92
|
+
if (response.status === 200 || response.status === 204) {
|
|
93
|
+
// Структура ответа: { success: true }
|
|
94
|
+
return response.data || { success: true };
|
|
95
|
+
} else {
|
|
96
|
+
console.error(`Ошибка отметки уведомления: ${response.status}`);
|
|
97
|
+
if (response.data) {
|
|
98
|
+
console.error('Response data:', response.data);
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Исключение при отметке уведомления:', error.message);
|
|
104
|
+
if (error.response) {
|
|
105
|
+
console.error('Response status:', error.response.status);
|
|
106
|
+
console.error('Response data:', error.response.data);
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Получает количество непрочитанных уведомлений
|
|
114
|
+
*
|
|
115
|
+
* @returns {Promise<number|null>} Количество уведомлений или null при ошибке
|
|
116
|
+
*/
|
|
117
|
+
async getUnreadCount() {
|
|
118
|
+
if (!await this.client.auth.checkAuth()) {
|
|
119
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const countUrl = `${this.client.baseUrl}/api/notifications/count`;
|
|
125
|
+
const response = await this.axios.get(countUrl);
|
|
126
|
+
|
|
127
|
+
if (response.status === 200) {
|
|
128
|
+
const data = response.data;
|
|
129
|
+
// Структура: { count: number }
|
|
130
|
+
return data.count || 0;
|
|
131
|
+
} else {
|
|
132
|
+
console.error(`Ошибка получения количества уведомлений: ${response.status}`);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error('Исключение при получении количества уведомлений:', error.message);
|
|
137
|
+
if (error.response) {
|
|
138
|
+
console.error('Response status:', error.response.status);
|
|
139
|
+
console.error('Response data:', error.response.data);
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Отмечает все уведомления как прочитанные
|
|
147
|
+
*
|
|
148
|
+
* @returns {Promise<boolean>} True если успешно
|
|
149
|
+
*/
|
|
150
|
+
async markAllAsRead() {
|
|
151
|
+
if (!await this.client.auth.checkAuth()) {
|
|
152
|
+
console.error('Ошибка: необходимо войти в аккаунт');
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
// Нужно найти реальный endpoint, пока используем предположительный
|
|
158
|
+
const readAllUrl = `${this.client.baseUrl}/api/notifications/read-all`;
|
|
159
|
+
const response = await this.axios.post(readAllUrl);
|
|
160
|
+
|
|
161
|
+
if (response.status === 200 || response.status === 204) {
|
|
162
|
+
return true;
|
|
163
|
+
} else {
|
|
164
|
+
console.error(`Ошибка отметки всех уведомлений: ${response.status}`);
|
|
165
|
+
if (response.data) {
|
|
166
|
+
console.error('Response data:', response.data);
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('Исключение при отметке всех уведомлений:', error.message);
|
|
172
|
+
if (error.response) {
|
|
173
|
+
console.error('Response status:', error.response.status);
|
|
174
|
+
console.error('Response data:', error.response.data);
|
|
175
|
+
// Если 404 - значит endpoint неправильный, нужно найти реальный
|
|
176
|
+
if (error.response.status === 404) {
|
|
177
|
+
console.error('💡 Endpoint не найден. Найди реальный URL в DevTools');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ========== USER-FRIENDLY МЕТОДЫ ==========
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Проверяет, есть ли непрочитанные уведомления (удобный метод)
|
|
188
|
+
*
|
|
189
|
+
* @returns {Promise<boolean>} True если есть непрочитанные
|
|
190
|
+
*/
|
|
191
|
+
async hasUnreadNotifications() {
|
|
192
|
+
const count = await this.getUnreadCount();
|
|
193
|
+
return (count || 0) > 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Получает только непрочитанные уведомления (удобный метод)
|
|
198
|
+
*
|
|
199
|
+
* @param {number} limit - Количество уведомлений
|
|
200
|
+
* @param {string|null} cursor - Курсор для пагинации
|
|
201
|
+
* @returns {Promise<Object|null>} { notifications: [], pagination: {} } или null
|
|
202
|
+
*/
|
|
203
|
+
async getUnreadNotifications(limit = 20, cursor = null) {
|
|
204
|
+
const all = await this.getNotifications(limit, cursor);
|
|
205
|
+
if (!all) return null;
|
|
206
|
+
|
|
207
|
+
// Фильтруем только непрочитанные
|
|
208
|
+
const unread = all.notifications.filter(n => !n.read);
|
|
209
|
+
return {
|
|
210
|
+
notifications: unread,
|
|
211
|
+
pagination: all.pagination
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
}
|