nodebb-plugin-openai 1.4.2 → 1.4.4

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.
@@ -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",
@@ -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",
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "error.need-x-reputation-to-mention": "提及 chatgpt 至少需要 %1 声望值",
3
3
  "summarize-topic": "总结主题",
4
- "topic-summary": "主题摘要"
4
+ "topic-summary": "主题摘要",
5
+ "summary-cooldown-error": "在再次总结主题之前,您必须等待。"
5
6
  }
package/library.js CHANGED
@@ -18,6 +18,11 @@ const groups = require.main.require('./src/groups');
18
18
  const sockets = require.main.require('./src/socket.io');
19
19
  const socketPlugins = require.main.require('./src/socket.io/plugins');
20
20
  const summary = require('./lib/summary');
21
+ const cacheCreate = require('../cache/lru');
22
+ const summaryCache = cacheCreate({
23
+ name: 'openai-summary',
24
+ max: 200,
25
+ });
21
26
 
22
27
  const plugin = module.exports;
23
28
 
@@ -26,7 +31,7 @@ const defaults = {
26
31
  'chatgpt-username': '',
27
32
  enablePrivateMessages: 'off',
28
33
  model: 'gpt-3.5-turbo',
29
- minimumReputation: 0,
34
+ minimumReputation: 2,
30
35
  allowedGroups: '[]',
31
36
  systemPrompt: 'You are a helpful assistant',
32
37
  };
@@ -125,9 +130,13 @@ plugin.actionMessagingSave = async function (hookData) {
125
130
  return;
126
131
  }
127
132
 
133
+ // Check actual room membership directly against the database.
134
+ // messaging.isUserInRoom() runs filter:messaging.isUserInRoom, which
135
+ // moderation plugins use to grant virtual access to every room — that
136
+ // would make the bot react to every private chat on the forum.
128
137
  const [roomData, inRoom] = await Promise.all([
129
138
  messaging.getRoomData(roomId),
130
- messaging.isUserInRoom(chatgptUid, roomId),
139
+ db.isSortedSetMember(`chat:room:${roomId}:uids`, chatgptUid),
131
140
  ]);
132
141
 
133
142
  if (!roomData || !inRoom) {
@@ -145,24 +154,19 @@ plugin.actionMessagingSave = async function (hookData) {
145
154
  }
146
155
  let conversation = [{ role: 'user', content: message.content }];
147
156
  if (isPrivate) {
148
- const mids = await getMessageIds(roomId, chatgptUid, 0, 20);
149
-
150
- // dont allow chats to get too long
151
- if (mids.length > 20) {
152
- await api.chats.post({ uid: chatgptUid, session: {} }, {
153
- roomId,
154
- message: 'Conversation too long, please start a new chat',
155
- toMid: message.mid,
156
- });
157
- return;
158
- }
157
+ // keep context bounded to the 20 most recent messages
158
+ const mids = await getMessageIds(roomId, chatgptUid, 0, 19);
159
159
  let messages = await messaging.getMessagesFields(mids, ['fromuid', 'content', 'system']);
160
- messages = messages.filter(m => m && !m.system);
160
+ messages = messages.filter(m => m && !m.system && m.content);
161
161
  conversation = messages.map(
162
- msg => ({ role: msg.fromuid === chatgptUid ? 'assistant' : 'user', content: msg.content })
162
+ msg => ({ role: parseInt(msg.fromuid, 10) === parseInt(chatgptUid, 10) ? 'assistant' : 'user', content: msg.content })
163
163
  );
164
164
  }
165
165
 
166
+ if (!conversation.length) {
167
+ return;
168
+ }
169
+
166
170
  const response = await chatComplete(conversation);
167
171
 
168
172
  if (response) {
@@ -227,17 +231,23 @@ async function checkGroupMembership(uid, settings, silent) {
227
231
  return memberOfAny;
228
232
  }
229
233
 
234
+ // Returns the newest messages (like core's getMessageIds), in chronological order
230
235
  async function getMessageIds(roomId, uid, start, stop) {
231
236
  const isPublic = await db.getObjectField(`chat:room:${roomId}`, 'public');
232
237
  if (parseInt(isPublic, 10) === 1) {
233
- return await db.getSortedSetRange(
238
+ const mids = await db.getSortedSetRevRange(
234
239
  `chat:room:${roomId}:mids`, start, stop
235
240
  );
241
+ return mids.reverse();
236
242
  }
237
243
  const userjoinTimestamp = await db.sortedSetScore(`chat:room:${roomId}:uids`, uid);
238
- return await db.getSortedSetRangeByScore(
239
- `chat:room:${roomId}:mids`, start, stop === -1 ? -1 : stop - start + 1, userjoinTimestamp, '+inf'
244
+ if (userjoinTimestamp === null) {
245
+ return [];
246
+ }
247
+ const mids = await db.getSortedSetRevRangeByScore(
248
+ `chat:room:${roomId}:mids`, start, stop === -1 ? -1 : stop - start + 1, '+inf', userjoinTimestamp
240
249
  );
250
+ return mids.reverse();
241
251
  }
242
252
 
243
253
  async function chatComplete(messages) {
@@ -325,16 +335,22 @@ socketPlugins.openai.summarizeTopic = async function (socket, data) {
325
335
  if (!await privileges.topics.can('topics:read', tid, socket.uid)) {
326
336
  throw new Error('[[error:no-privileges]]');
327
337
  }
328
- const settings = await getSettings();
329
- if (!await canUseOpenAI(socket.uid, settings)) {
330
- return;
331
- }
332
338
 
333
339
  let openaiSummary = await topics.getTopicField(tid, 'openai:summary');
334
340
  if (openaiSummary) {
335
341
  return openaiSummary;
336
342
  }
343
+ const settings = await getSettings();
344
+ if (!await canUseOpenAI(socket.uid, settings)) {
345
+ return;
346
+ }
347
+ const cooldown = summaryCache.get(`uid:${socket.uid}`);
348
+ if (cooldown) {
349
+ throw new Error('[[openai:summary-cooldown-error]]');
350
+ }
351
+
337
352
  openaiSummary = await summary.summarizeTopic(tid, openai, settings);
353
+ summaryCache.set(`uid:${socket.uid}`, 1, 60000);
338
354
  await topics.setTopicField(tid, 'openai:summary', openaiSummary);
339
355
  return openaiSummary;
340
356
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-openai",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "A NodeBB OpenAI plugin",
5
5
  "main": "library.js",
6
6
  "repository": {