nodebb-plugin-openai 1.0.0 → 1.2.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/languages/en-GB/openai.json +3 -0
- package/languages/en-US/openai.json +3 -0
- package/lib/controllers.js +6 -1
- package/library.js +96 -6
- package/package.json +2 -2
- package/plugin.json +2 -1
- package/templates/admin/plugins/openai.tpl +27 -0
package/lib/controllers.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const groups = require.main.require('./src/groups');
|
|
4
|
+
|
|
3
5
|
const Controllers = module.exports;
|
|
4
6
|
|
|
5
|
-
Controllers.renderAdminPage = function (req, res/* , next */) {
|
|
7
|
+
Controllers.renderAdminPage = async function (req, res/* , next */) {
|
|
8
|
+
const groupsData = await groups.getNonPrivilegeGroups('groups:createtime', 0, -1);
|
|
9
|
+
groupsData.sort((a, b) => b.system - a.system);
|
|
6
10
|
res.render('admin/plugins/openai', {
|
|
7
11
|
title: 'OpenAI',
|
|
12
|
+
groups: groupsData,
|
|
8
13
|
});
|
|
9
14
|
};
|
package/library.js
CHANGED
|
@@ -13,9 +13,23 @@ const topics = require.main.require('./src/topics');
|
|
|
13
13
|
const user = require.main.require('./src/user');
|
|
14
14
|
const messaging = require.main.require('./src/messaging');
|
|
15
15
|
const api = require.main.require('./src/api');
|
|
16
|
+
const privileges = require.main.require('./src/privileges');
|
|
17
|
+
const groups = require.main.require('./src/groups');
|
|
18
|
+
const sockets = require.main.require('./src/socket.io');
|
|
16
19
|
|
|
17
20
|
const plugin = module.exports;
|
|
18
21
|
|
|
22
|
+
const defaults = {
|
|
23
|
+
apikey: '',
|
|
24
|
+
'chatgpt-username': '',
|
|
25
|
+
enablePrivateMessages: 'off',
|
|
26
|
+
model: 'gpt-3.5-turbo',
|
|
27
|
+
minimumReputation: 0,
|
|
28
|
+
allowedGroups: '[]',
|
|
29
|
+
systemPrompt: 'You are a helpful assistant',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
|
|
19
33
|
plugin.init = async (params) => {
|
|
20
34
|
const { router /* , middleware , controllers */ } = params;
|
|
21
35
|
const settings = await meta.settings.get('openai')
|
|
@@ -28,6 +42,11 @@ plugin.init = async (params) => {
|
|
|
28
42
|
routeHelpers.setupAdminPageRoute(router, '/admin/plugins/openai', controllers.renderAdminPage);
|
|
29
43
|
};
|
|
30
44
|
|
|
45
|
+
async function getSettings() {
|
|
46
|
+
const settings = await meta.settings.get('openai')
|
|
47
|
+
return {...defaults, ...settings };
|
|
48
|
+
}
|
|
49
|
+
|
|
31
50
|
plugin.actionMentionsNotify = async function (hookData) {
|
|
32
51
|
try {
|
|
33
52
|
const { notification } = hookData;
|
|
@@ -35,13 +54,21 @@ plugin.actionMentionsNotify = async function (hookData) {
|
|
|
35
54
|
return;
|
|
36
55
|
}
|
|
37
56
|
|
|
38
|
-
const settings =
|
|
57
|
+
const settings = await getSettings();
|
|
58
|
+
if (!await canUseOpenAI(notification.from, settings)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
39
62
|
const chatgptusername = settings['chatgpt-username'];
|
|
40
63
|
const chatgptUid = await user.getUidByUsername(chatgptusername);
|
|
41
64
|
if (!chatgptUid) {
|
|
42
65
|
return;
|
|
43
66
|
}
|
|
44
67
|
if (notification.tid && notification.bodyLong.startsWith(`@${chatgptusername}`)) {
|
|
68
|
+
const canReply = await privileges.topics.can('topics:reply', notification.tid, chatgptUid);
|
|
69
|
+
if (!canReply) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
45
72
|
const message = notification.bodyLong.replace(new RegExp(`^@${chatgptusername}`), '');
|
|
46
73
|
if (message.length) {
|
|
47
74
|
const response = await chatComplete(message);
|
|
@@ -74,7 +101,11 @@ plugin.actionMessagingSave = async function (hookData) {
|
|
|
74
101
|
if (message.system) {
|
|
75
102
|
return;
|
|
76
103
|
}
|
|
77
|
-
const settings =
|
|
104
|
+
const settings = await getSettings();
|
|
105
|
+
if (!await canUseOpenAI(message.fromuid, settings)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
78
109
|
const chatgptusername = settings['chatgpt-username'];
|
|
79
110
|
const chatgptUid = await user.getUidByUsername(chatgptusername);
|
|
80
111
|
const { roomId, content, fromuid } = message;
|
|
@@ -135,6 +166,55 @@ plugin.actionMessagingSave = async function (hookData) {
|
|
|
135
166
|
}
|
|
136
167
|
};
|
|
137
168
|
|
|
169
|
+
async function canUseOpenAI(uid, settings) {
|
|
170
|
+
if (!await checkReputation(uid, settings)) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (!await checkGroupMembership(uid, settings)) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function checkReputation(uid, settings) {
|
|
180
|
+
const reputation = await user.getUserField(uid, 'reputation');
|
|
181
|
+
const hasEnoughRep = parseInt(settings.minimumReputation, 10) === 0 || parseInt(reputation, 10) >= parseInt(settings.minimumReputation, 10);
|
|
182
|
+
|
|
183
|
+
if (!hasEnoughRep) {
|
|
184
|
+
sockets.server.in(`uid_${uid}`).emit('event:alert', {
|
|
185
|
+
type: 'danger',
|
|
186
|
+
title: '[[global:alert.error]]',
|
|
187
|
+
message: `[[openai:error.need-x-reputation-to-mention, ${settings.minimumReputation}]]`,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return hasEnoughRep;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function checkGroupMembership(uid, settings) {
|
|
194
|
+
let allowedGroups = [];
|
|
195
|
+
try {
|
|
196
|
+
allowedGroups = JSON.parse(settings.allowedGroups) || [];
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.error(err);
|
|
199
|
+
allowedGroups = [];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!allowedGroups.length) {
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const isMembers = await groups.isMemberOfGroups(uid, allowedGroups);
|
|
207
|
+
const memberOfAny = isMembers.includes(true);
|
|
208
|
+
if (!memberOfAny) {
|
|
209
|
+
sockets.server.in(`uid_${uid}`).emit('event:alert', {
|
|
210
|
+
type: 'danger',
|
|
211
|
+
title: '[[global:alert.error]]',
|
|
212
|
+
message: `[[error:no-privileges]]`,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return memberOfAny;
|
|
216
|
+
}
|
|
217
|
+
|
|
138
218
|
async function getMessageIds(roomId, uid, start, stop) {
|
|
139
219
|
const isPublic = await db.getObjectField(`chat:room:${roomId}`, 'public');
|
|
140
220
|
if (parseInt(isPublic, 10) === 1) {
|
|
@@ -148,15 +228,25 @@ async function getMessageIds(roomId, uid, start, stop) {
|
|
|
148
228
|
);
|
|
149
229
|
}
|
|
150
230
|
|
|
151
|
-
async function chatComplete(
|
|
231
|
+
async function chatComplete(messages) {
|
|
152
232
|
if (!openai) {
|
|
153
233
|
throw new Error('API not created!');
|
|
154
234
|
}
|
|
155
|
-
const isConversation = Array.isArray(
|
|
156
|
-
const { model } = await
|
|
235
|
+
const isConversation = Array.isArray(messages);
|
|
236
|
+
const { model, systemPrompt } = await getSettings();
|
|
237
|
+
const conversation = [];
|
|
238
|
+
if (systemPrompt) {
|
|
239
|
+
conversation.push({ role: 'system', content: systemPrompt });
|
|
240
|
+
}
|
|
241
|
+
if (isConversation) {
|
|
242
|
+
conversation.push(...messages);
|
|
243
|
+
} else {
|
|
244
|
+
conversation.push({ role: 'user', content: messages });
|
|
245
|
+
}
|
|
246
|
+
|
|
157
247
|
const chatCompletion = await openai.chat.completions.create({
|
|
158
248
|
model: model || 'gpt-3.5-turbo',
|
|
159
|
-
messages:
|
|
249
|
+
messages: conversation,
|
|
160
250
|
});
|
|
161
251
|
|
|
162
252
|
return chatCompletion.choices[0]?.message?.content;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-openai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A NodeBB OpenAI plugin",
|
|
5
5
|
"main": "library.js",
|
|
6
6
|
"repository": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"compatibility": "^3.2.0"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"openai": "4.53.
|
|
36
|
+
"openai": "4.53.2"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@commitlint/cli": "19.3.0",
|
package/plugin.json
CHANGED
|
@@ -40,6 +40,33 @@
|
|
|
40
40
|
<option value="gpt-4">gpt-4</option>
|
|
41
41
|
</select>
|
|
42
42
|
</div>
|
|
43
|
+
<div class="">
|
|
44
|
+
<label class="form-label" for="systemPrompt">System Prompt</label>
|
|
45
|
+
<textarea class="form-control" id="systemPrompt" name="systemPrompt" title="System prompt" placeholder="You are a helpful assistant"></textarea>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div class="">
|
|
50
|
+
<h5 class="fw-bold tracking-tight settings-header">Restrictions</h5>
|
|
51
|
+
|
|
52
|
+
<div class="mb-3">
|
|
53
|
+
<label class="form-label" for="minimumReputation">Minimum Reputation</label>
|
|
54
|
+
<input type="text" id="minimumReputation" name="minimumReputation" title="Minimum Reputation" class="form-control">
|
|
55
|
+
<p class="form-text">
|
|
56
|
+
Minimum reputation required to mention chatgpt user. (0 to disable)
|
|
57
|
+
</p>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="mb-3">
|
|
60
|
+
<label class="form-label" form="allowedGroups">Allowed Groups</label>
|
|
61
|
+
<select class="form-select" multiple id="allowedGroups" name="allowedGroups" size="10">
|
|
62
|
+
{{{ each groups }}}
|
|
63
|
+
<option value="{./displayName}">{./displayName}</option>
|
|
64
|
+
{{{ end }}}
|
|
65
|
+
</select>
|
|
66
|
+
<p class="form-text">
|
|
67
|
+
Only users in these groups will be able to mention the chatgpt user. Leave blank to allow all groups.
|
|
68
|
+
</p>
|
|
69
|
+
</div>
|
|
43
70
|
</div>
|
|
44
71
|
</form>
|
|
45
72
|
</div>
|