nodebb-plugin-integration 0.4.2 → 0.5.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/README.md +199 -320
- package/README.ru.md +199 -347
- package/library.js +93 -115
- package/package.json +60 -57
- package/plugin.json +50 -33
- package/public/js/admin.js +58 -45
- package/public/templates/admin/plugins/integration.tpl +213 -0
- package/src/ai/embeddings.js +46 -253
- package/src/ai/indexer.js +156 -343
- package/src/ai/openai.js +74 -330
- package/src/ai/qdrant.js +114 -517
- package/src/ai/search.js +138 -251
- package/src/config/index.js +176 -133
- package/src/config/validation.js +250 -0
- package/src/events/index.js +174 -324
- package/src/mcp/manager.js +396 -0
- package/src/mcp/server.js +347 -177
- package/src/mcp/tools.js +158 -617
- package/src/nodebb/categories.js +39 -0
- package/src/nodebb/index.js +72 -305
- package/src/nodebb/meta.js +24 -273
- package/src/nodebb/posts.js +93 -348
- package/src/nodebb/privileges.js +68 -0
- package/src/nodebb/topics.js +91 -355
- package/src/nodebb/users.js +62 -345
- package/src/routes/api.js +255 -97
- package/src/routes/index.js +64 -49
- package/templates/admin/plugins/integration.tpl +0 -130
package/src/events/index.js
CHANGED
|
@@ -1,324 +1,174 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return post;
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
/*
|
|
205
|
-
|
|
206
|
-
New topic handler
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
NodeBB hook:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
filter.create
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
*/
|
|
217
|
-
|
|
218
|
-
async function topicCreate(topic) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if(!topic) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return topic;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
console.log(
|
|
242
|
-
|
|
243
|
-
"[Events] topic created:",
|
|
244
|
-
|
|
245
|
-
topic.tid
|
|
246
|
-
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if(Indexer && typeof Indexer.indexTopic === "function") {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
await Indexer.indexTopic(
|
|
260
|
-
|
|
261
|
-
topic
|
|
262
|
-
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
catch(error) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
console.error(
|
|
278
|
-
|
|
279
|
-
"[Events] topicCreate error",
|
|
280
|
-
|
|
281
|
-
error.message
|
|
282
|
-
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
return topic;
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
module.exports = {
|
|
312
|
-
|
|
313
|
-
init,
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
postSave,
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
topicCreate
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const Config = require("../config");
|
|
4
|
+
const Indexer = require("../ai/indexer");
|
|
5
|
+
const Topics = require("../nodebb/topics");
|
|
6
|
+
|
|
7
|
+
let initialized = false;
|
|
8
|
+
|
|
9
|
+
function getPostId(data) {
|
|
10
|
+
return Number(data?.post?.pid || data?.pid || 0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getPostIds(data) {
|
|
14
|
+
const posts = Array.isArray(data?.posts) ? data.posts : [];
|
|
15
|
+
|
|
16
|
+
return [
|
|
17
|
+
...new Set(
|
|
18
|
+
posts
|
|
19
|
+
.map(post => Number(post?.pid || post))
|
|
20
|
+
.filter(pid => Number.isSafeInteger(pid) && pid > 0)
|
|
21
|
+
)
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function isAiEnabled() {
|
|
26
|
+
const settings = await Config.get();
|
|
27
|
+
return settings.aiProvider === "openai";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function indexEvent(data) {
|
|
31
|
+
if (!(await isAiEnabled())) {
|
|
32
|
+
return data;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const pid = getPostId(data);
|
|
36
|
+
|
|
37
|
+
if (!pid) {
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
await Indexer.indexPost(pid);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error(
|
|
45
|
+
`[Integration] post indexing failed (${pid}):`,
|
|
46
|
+
error.message
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function removeEvent(data) {
|
|
54
|
+
if (!(await isAiEnabled())) {
|
|
55
|
+
return data;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const pid = getPostId(data);
|
|
59
|
+
|
|
60
|
+
if (!pid) {
|
|
61
|
+
return data;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
await Indexer.removePost(pid);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error(
|
|
68
|
+
`[Integration] post removal failed (${pid}):`,
|
|
69
|
+
error.message
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return data;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function purgePosts(data) {
|
|
77
|
+
if (!(await isAiEnabled())) {
|
|
78
|
+
return data;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const pids = getPostIds(data);
|
|
82
|
+
|
|
83
|
+
for (const pid of pids) {
|
|
84
|
+
try {
|
|
85
|
+
await Indexer.removePost(pid);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(
|
|
88
|
+
`[Integration] post purge failed (${pid}):`,
|
|
89
|
+
error.message
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return data;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function topicDelete(data) {
|
|
98
|
+
if (!(await isAiEnabled())) {
|
|
99
|
+
return data;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const tid = Number(data?.topic?.tid || data?.tid || 0);
|
|
103
|
+
|
|
104
|
+
if (!Number.isSafeInteger(tid) || tid <= 0) {
|
|
105
|
+
return data;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const pids = await Topics.getTopicPids(tid);
|
|
110
|
+
|
|
111
|
+
for (const pid of pids) {
|
|
112
|
+
try {
|
|
113
|
+
await Indexer.removePost(pid);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error(
|
|
116
|
+
`[Integration] topic post removal failed (${pid}):`,
|
|
117
|
+
error.message
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error(
|
|
123
|
+
`[Integration] topic removal failed (${tid}):`,
|
|
124
|
+
error.message
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return data;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function topicRestore(data) {
|
|
132
|
+
if (!(await isAiEnabled())) {
|
|
133
|
+
return data;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const tid = Number(data?.topic?.tid || data?.tid || 0);
|
|
137
|
+
|
|
138
|
+
if (!Number.isSafeInteger(tid) || tid <= 0) {
|
|
139
|
+
return data;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const pids = await Topics.getTopicPids(tid);
|
|
144
|
+
await Indexer.indexPosts(pids);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error(
|
|
147
|
+
`[Integration] topic restoration failed (${tid}):`,
|
|
148
|
+
error.message
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return data;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function init() {
|
|
156
|
+
if (initialized) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
initialized = true;
|
|
161
|
+
|
|
162
|
+
console.log("[Integration] NodeBB event handlers initialized");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = {
|
|
166
|
+
init,
|
|
167
|
+
postSave: indexEvent,
|
|
168
|
+
postEdit: indexEvent,
|
|
169
|
+
postDelete: removeEvent,
|
|
170
|
+
postRestore: indexEvent,
|
|
171
|
+
postsPurge: purgePosts,
|
|
172
|
+
topicDelete,
|
|
173
|
+
topicRestore
|
|
174
|
+
};
|