nodebb-plugin-openai 1.4.3 → 2.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/languages/en-GB/openai.json +1 -0
- package/languages/en-US/openai.json +1 -0
- package/languages/he/openai.json +1 -0
- package/languages/zh-CN/openai.json +2 -1
- package/lib/controllers.js +44 -1
- package/lib/summary.js +5 -5
- package/library.js +30 -23
- package/package.json +3 -3
- package/public/lib/main.js +22 -22
- package/templates/admin/plugins/openai.tpl +20 -25
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"error.need-x-reputation-to-mention": "You need at least %1 reputation for mentioning chatgpt",
|
|
3
3
|
"summarize-topic": "Summarize Topic",
|
|
4
4
|
"topic-summary": "Topic Summary",
|
|
5
|
+
"summary-cooldown-error": "You must wait before summarizing a topic again.",
|
|
5
6
|
|
|
6
7
|
"admin.general": "General",
|
|
7
8
|
"admin.apikey": "API Key",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"error.need-x-reputation-to-mention": "You need at least %1 reputation for mentioning chatgpt",
|
|
3
3
|
"summarize-topic": "Summarize Topic",
|
|
4
4
|
"topic-summary": "Topic Summary",
|
|
5
|
+
"summary-cooldown-error": "You must wait before summarizing a topic again.",
|
|
5
6
|
|
|
6
7
|
"admin.general": "General",
|
|
7
8
|
"admin.apikey": "API Key",
|
package/languages/he/openai.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"error.need-x-reputation-to-mention": "דרושה לך מוניטין של %1 לפחות כדי לאזכר את chatgpt",
|
|
3
3
|
"summarize-topic": "סכם נושא",
|
|
4
4
|
"topic-summary": "סיכום הנושא",
|
|
5
|
+
"summary-cooldown-error": "עליך להמתין לפני שתסכם את הנושא שוב.",
|
|
5
6
|
|
|
6
7
|
"admin.general": "כללי",
|
|
7
8
|
"admin.apikey": "מפתח API",
|
package/lib/controllers.js
CHANGED
|
@@ -1,14 +1,57 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const groups =
|
|
3
|
+
const groups = nodebb.require('./src/groups');
|
|
4
4
|
|
|
5
5
|
const Controllers = module.exports;
|
|
6
6
|
|
|
7
7
|
Controllers.renderAdminPage = async function (req, res/* , next */) {
|
|
8
8
|
const groupsData = await groups.getNonPrivilegeGroups('groups:createtime', 0, -1);
|
|
9
9
|
groupsData.sort((a, b) => b.system - a.system);
|
|
10
|
+
// Uncomment the following lines to list available models
|
|
11
|
+
// const list = await openai.models.list();
|
|
12
|
+
// for await (const model of list) {
|
|
13
|
+
// console.log(model.id);
|
|
14
|
+
// }
|
|
15
|
+
|
|
10
16
|
res.render('admin/plugins/openai', {
|
|
11
17
|
title: 'OpenAI',
|
|
12
18
|
groups: groupsData,
|
|
19
|
+
models: [
|
|
20
|
+
{ id: 'gpt-3.5-turbo' },
|
|
21
|
+
{ id: 'gpt-4' },
|
|
22
|
+
{ id: 'gpt-4-turbo' },
|
|
23
|
+
{ id: 'gpt-4.1' },
|
|
24
|
+
{ id: 'gpt-4.1-mini' },
|
|
25
|
+
{ id: 'gpt-4.1-nano' },
|
|
26
|
+
{ id: 'gpt-4o' },
|
|
27
|
+
{ id: 'gpt-4o-mini' },
|
|
28
|
+
{ id: 'gpt-5-codex' },
|
|
29
|
+
{ id: 'gpt-5-mini' },
|
|
30
|
+
{ id: 'gpt-5-nano' },
|
|
31
|
+
{ id: 'gpt-5-pro' },
|
|
32
|
+
{ id: 'gpt-5.1' },
|
|
33
|
+
{ id: 'gpt-5.1-codex' },
|
|
34
|
+
{ id: 'gpt-5.1-codex-max' },
|
|
35
|
+
{ id: 'gpt-5.1-codex-mini' },
|
|
36
|
+
{ id: 'gpt-5.2' },
|
|
37
|
+
{ id: 'gpt-5.2-codex' },
|
|
38
|
+
{ id: 'gpt-5.2-pro' },
|
|
39
|
+
{ id: 'gpt-5.3-codex' },
|
|
40
|
+
{ id: 'gpt-5.4' },
|
|
41
|
+
{ id: 'gpt-5.4-mini' },
|
|
42
|
+
{ id: 'gpt-5.4-nano' },
|
|
43
|
+
{ id: 'gpt-5.4-pro' },
|
|
44
|
+
{ id: 'gpt-5.5' },
|
|
45
|
+
{ id: 'gpt-5.5-pro' },
|
|
46
|
+
{ id: 'gpt-5.6-luna' },
|
|
47
|
+
{ id: 'gpt-5.6-sol' },
|
|
48
|
+
{ id: 'gpt-5.6-terra' },
|
|
49
|
+
{ id: 'gpt-5.6' },
|
|
50
|
+
{ id: 'gpt-6-astra' },
|
|
51
|
+
{ id: 'gpt-6-luna' },
|
|
52
|
+
{ id: 'gpt-6-sol' },
|
|
53
|
+
{ id: 'gpt-6.1-sol' },
|
|
54
|
+
{ id: 'gemini-2.0-flash' },
|
|
55
|
+
],
|
|
13
56
|
});
|
|
14
57
|
};
|
package/lib/summary.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const db =
|
|
4
|
-
const batch =
|
|
5
|
-
const topics =
|
|
6
|
-
const posts =
|
|
7
|
-
const user =
|
|
3
|
+
const db = nodebb.require('./src/database');
|
|
4
|
+
const batch = nodebb.require('./src/batch');
|
|
5
|
+
const topics = nodebb.require('./src/topics');
|
|
6
|
+
const posts = nodebb.require('./src/posts');
|
|
7
|
+
const user = nodebb.require('./src/user');
|
|
8
8
|
|
|
9
9
|
function formatPosts(posts) {
|
|
10
10
|
return posts
|
package/library.js
CHANGED
|
@@ -4,21 +4,28 @@ const { OpenAI } = require('openai');
|
|
|
4
4
|
|
|
5
5
|
let openai;
|
|
6
6
|
|
|
7
|
-
const db =
|
|
8
|
-
const meta =
|
|
7
|
+
const db = nodebb.require('./src/database');
|
|
8
|
+
const meta = nodebb.require('./src/meta');
|
|
9
9
|
const controllers = require('./lib/controllers');
|
|
10
|
-
const routeHelpers = require.main.require('./src/routes/helpers');
|
|
11
|
-
const socketHelpers = require.main.require('./src/socket.io/helpers');
|
|
12
|
-
const topics = require.main.require('./src/topics');
|
|
13
|
-
const user = require.main.require('./src/user');
|
|
14
|
-
const messaging = require.main.require('./src/messaging');
|
|
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');
|
|
19
|
-
const socketPlugins = require.main.require('./src/socket.io/plugins');
|
|
20
10
|
const summary = require('./lib/summary');
|
|
21
11
|
|
|
12
|
+
const routeHelpers = nodebb.require('./src/routes/helpers');
|
|
13
|
+
const socketHelpers = nodebb.require('./src/socket.io/helpers');
|
|
14
|
+
const topics = nodebb.require('./src/topics');
|
|
15
|
+
const user = nodebb.require('./src/user');
|
|
16
|
+
const messaging = nodebb.require('./src/messaging');
|
|
17
|
+
const api = nodebb.require('./src/api');
|
|
18
|
+
const privileges = nodebb.require('./src/privileges');
|
|
19
|
+
const groups = nodebb.require('./src/groups');
|
|
20
|
+
const sockets = nodebb.require('./src/socket.io');
|
|
21
|
+
const socketPlugins = nodebb.require('./src/socket.io/plugins');
|
|
22
|
+
const cacheCreate = nodebb.require('./src/cache/lru');
|
|
23
|
+
|
|
24
|
+
const summaryCache = cacheCreate({
|
|
25
|
+
name: 'openai-summary',
|
|
26
|
+
max: 200,
|
|
27
|
+
});
|
|
28
|
+
|
|
22
29
|
const plugin = module.exports;
|
|
23
30
|
|
|
24
31
|
const defaults = {
|
|
@@ -26,7 +33,7 @@ const defaults = {
|
|
|
26
33
|
'chatgpt-username': '',
|
|
27
34
|
enablePrivateMessages: 'off',
|
|
28
35
|
model: 'gpt-3.5-turbo',
|
|
29
|
-
minimumReputation:
|
|
36
|
+
minimumReputation: 2,
|
|
30
37
|
allowedGroups: '[]',
|
|
31
38
|
systemPrompt: 'You are a helpful assistant',
|
|
32
39
|
};
|
|
@@ -41,12 +48,6 @@ plugin.init = async (params) => {
|
|
|
41
48
|
baseURL: settings.apiBaseUrl || 'https://api.openai.com/v1',
|
|
42
49
|
});
|
|
43
50
|
|
|
44
|
-
// Uncomment the following lines to list available models
|
|
45
|
-
// const list = await openai.models.list();
|
|
46
|
-
// for await (const model of list) {
|
|
47
|
-
// console.log(model.id);
|
|
48
|
-
// }
|
|
49
|
-
|
|
50
51
|
plugin.openai = openai;
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -330,16 +331,22 @@ socketPlugins.openai.summarizeTopic = async function (socket, data) {
|
|
|
330
331
|
if (!await privileges.topics.can('topics:read', tid, socket.uid)) {
|
|
331
332
|
throw new Error('[[error:no-privileges]]');
|
|
332
333
|
}
|
|
333
|
-
const settings = await getSettings();
|
|
334
|
-
if (!await canUseOpenAI(socket.uid, settings)) {
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
334
|
|
|
338
335
|
let openaiSummary = await topics.getTopicField(tid, 'openai:summary');
|
|
339
336
|
if (openaiSummary) {
|
|
340
337
|
return openaiSummary;
|
|
341
338
|
}
|
|
339
|
+
const settings = await getSettings();
|
|
340
|
+
if (!await canUseOpenAI(socket.uid, settings)) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const cooldown = summaryCache.get(`uid:${socket.uid}`);
|
|
344
|
+
if (cooldown) {
|
|
345
|
+
throw new Error('[[openai:summary-cooldown-error]]');
|
|
346
|
+
}
|
|
347
|
+
|
|
342
348
|
openaiSummary = await summary.summarizeTopic(tid, openai, settings);
|
|
349
|
+
summaryCache.set(`uid:${socket.uid}`, 1, 60000);
|
|
343
350
|
await topics.setTopicField(tid, 'openai:summary', openaiSummary);
|
|
344
351
|
return openaiSummary;
|
|
345
352
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-openai",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A NodeBB OpenAI plugin",
|
|
5
5
|
"main": "library.js",
|
|
6
6
|
"repository": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"readmeFilename": "README.md",
|
|
35
35
|
"nbbpm": {
|
|
36
|
-
"compatibility": "
|
|
36
|
+
"compatibility": "4.15.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"openai": "4.103.0"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@commitlint/cli": "19.3.0",
|
|
43
43
|
"@commitlint/config-angular": "19.3.0",
|
|
44
|
-
"eslint": "10.
|
|
44
|
+
"eslint": "10.11.0",
|
|
45
45
|
"eslint-config-nodebb": "^2.0.3",
|
|
46
46
|
"husky": "9.0.11",
|
|
47
47
|
"lint-staged": "15.2.7"
|
package/public/lib/main.js
CHANGED
|
@@ -12,30 +12,30 @@ $('document').ready(function () {
|
|
|
12
12
|
$('.openai-summarize-topic').on('click', summarizeTopic);
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
function summarizeTopic() {
|
|
15
|
+
async function summarizeTopic() {
|
|
16
16
|
const tid = ajaxify.data.tid;
|
|
17
|
-
require(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
const modals = await app.require('modals');
|
|
18
|
+
|
|
19
|
+
const modal = await modals.dialog({
|
|
20
|
+
title: '[[openai:topic-summary]]',
|
|
21
|
+
message: `<div class="openai-summarize-topic"><div class="loading text-center"><i class="fa-solid fa-spinner fa-spin fa-2x"></i></div></div>`,
|
|
22
|
+
size: 'large',
|
|
23
|
+
buttons: {
|
|
24
|
+
ok: {
|
|
25
|
+
label: '[[modules:bootbox.ok]]',
|
|
26
|
+
className: 'btn-primary',
|
|
27
27
|
},
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
socket.emit('plugins.openai.summarizeTopic', { tid }, function (err, data) {
|
|
31
|
+
if (err) {
|
|
32
|
+
return alertType('error', err.message);
|
|
33
|
+
}
|
|
34
|
+
if (data) {
|
|
35
|
+
modal.find('.openai-summarize-topic').text(data);
|
|
36
|
+
} else {
|
|
37
|
+
modal.modal('hide');
|
|
38
|
+
}
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
});
|
|
@@ -5,78 +5,73 @@
|
|
|
5
5
|
<div id="spy-container" class="col-12 col-md-8 px-0 mb-4" tabindex="0">
|
|
6
6
|
<form role="form" class="openai-settings">
|
|
7
7
|
<div class="mb-4">
|
|
8
|
-
<h5 class="fw-bold tracking-tight settings-header">
|
|
8
|
+
<h5 class="fw-bold tracking-tight settings-header">{{tx("openai:admin.general")}}</h5>
|
|
9
9
|
|
|
10
10
|
<div class="mb-3">
|
|
11
|
-
<label class="form-label" for="apikey">
|
|
11
|
+
<label class="form-label" for="apikey">{{tx("openai:admin.apikey")}}</label>
|
|
12
12
|
<input type="text" id="apikey" name="apikey" title="[[openai:admin.apikey]]" class="form-control">
|
|
13
13
|
<p class="form-text">
|
|
14
|
-
|
|
14
|
+
{{tx("openai:admin.apikey-help")}}
|
|
15
15
|
</p>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
18
|
<div class="mb-3">
|
|
19
|
-
<label class="form-label" for="apiBaseUrl">
|
|
19
|
+
<label class="form-label" for="apiBaseUrl">{{tx("openai:admin.api-base-url")}}</label>
|
|
20
20
|
<input type="text" id="apiBaseUrl" name="apiBaseUrl" title="[[openai:admin.api-base-url]]" class="form-control">
|
|
21
21
|
<p class="form-text">
|
|
22
|
-
|
|
22
|
+
{{tx("openai:admin.api-base-url-help")}}
|
|
23
23
|
</p>
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
26
|
<div class="mb-3">
|
|
27
|
-
<label class="form-label" for="chatgpt-username">
|
|
27
|
+
<label class="form-label" for="chatgpt-username">{{tx("openai:admin.chatgpt-username")}}</label>
|
|
28
28
|
<input type="text" id="chatgpt-username" name="chatgpt-username" title="[[openai:admin.chatgpt-username]]" class="form-control">
|
|
29
29
|
<p class="form-text">
|
|
30
|
-
|
|
30
|
+
{{tx("openai:admin.chatgpt-username-help", config.relative_path)}}
|
|
31
31
|
</p>
|
|
32
32
|
</div>
|
|
33
33
|
|
|
34
34
|
<div class="form-check form-switch">
|
|
35
35
|
<input type="checkbox" class="form-check-input" id="enablePrivateMessages" name="enablePrivateMessages">
|
|
36
|
-
<label for="enablePrivateMessages" class="form-check-label">
|
|
36
|
+
<label for="enablePrivateMessages" class="form-check-label">{{tx("openai:admin.enable-private-messages")}}</label>
|
|
37
37
|
<p class="form-text">
|
|
38
|
-
|
|
38
|
+
{{tx("openai:admin.enable-private-messages-help")}}
|
|
39
39
|
</p>
|
|
40
40
|
</div>
|
|
41
41
|
|
|
42
42
|
<div class="mb-3">
|
|
43
|
-
<label class="form-label" for="model">
|
|
43
|
+
<label class="form-label" for="model">{{tx("openai:admin.model")}}</label>
|
|
44
44
|
<select class="form-select" id="model" name="model" title="[[openai:admin.model]]">
|
|
45
|
-
|
|
46
|
-
<option value="
|
|
47
|
-
|
|
48
|
-
<option value="gpt-4-turbo">gpt-4-turbo</option>
|
|
49
|
-
<option value="gpt-4">gpt-4</option>
|
|
50
|
-
<option value="gpt-4.1-mini">gpt-4.1-mini</option>
|
|
51
|
-
<option value="gpt-4.1">gpt-4.1</option>
|
|
52
|
-
<option value="gemini-2.0-flash">gemini-2.0-flash</option>
|
|
45
|
+
{{{ each models }}}
|
|
46
|
+
<option value="{./id}">{./id}</option>
|
|
47
|
+
{{{ end }}}
|
|
53
48
|
</select>
|
|
54
49
|
</div>
|
|
55
50
|
<div class="">
|
|
56
|
-
<label class="form-label" for="systemPrompt">
|
|
57
|
-
<textarea class="form-control" id="systemPrompt" name="systemPrompt" title="
|
|
51
|
+
<label class="form-label" for="systemPrompt">{{tx("openai:admin.system-prompt")}}</label>
|
|
52
|
+
<textarea class="form-control" id="systemPrompt" name="systemPrompt" title="{{tx("openai:admin.system-prompt")}}" placeholder="{{tx("openai:admin.system-prompt-placeholder")}}" rows="8"></textarea>
|
|
58
53
|
</div>
|
|
59
54
|
</div>
|
|
60
55
|
|
|
61
56
|
<div class="">
|
|
62
|
-
<h5 class="fw-bold tracking-tight settings-header">
|
|
57
|
+
<h5 class="fw-bold tracking-tight settings-header">{{tx("openai:admin.restrictions")}}</h5>
|
|
63
58
|
|
|
64
59
|
<div class="mb-3">
|
|
65
|
-
<label class="form-label" for="minimumReputation">
|
|
60
|
+
<label class="form-label" for="minimumReputation">{{tx("openai:admin.minimum-reputation")}}</label>
|
|
66
61
|
<input type="text" id="minimumReputation" name="minimumReputation" title="[[openai:admin.minimum-reputation]]" class="form-control">
|
|
67
62
|
<p class="form-text">
|
|
68
|
-
|
|
63
|
+
{{tx("openai:admin.minimum-reputation-help")}}
|
|
69
64
|
</p>
|
|
70
65
|
</div>
|
|
71
66
|
<div class="mb-3">
|
|
72
|
-
<label class="form-label" form="allowedGroups">
|
|
67
|
+
<label class="form-label" form="allowedGroups">{{tx("openai:admin.allowed-groups")}}</label>
|
|
73
68
|
<select class="form-select" multiple id="allowedGroups" name="allowedGroups" size="10">
|
|
74
69
|
{{{ each groups }}}
|
|
75
70
|
<option value="{./displayName}">{./displayName}</option>
|
|
76
71
|
{{{ end }}}
|
|
77
72
|
</select>
|
|
78
73
|
<p class="form-text">
|
|
79
|
-
|
|
74
|
+
{{tx("openai:admin.allowed-groups-help")}}
|
|
80
75
|
</p>
|
|
81
76
|
</div>
|
|
82
77
|
</div>
|