halbot 1991.2.19 → 1991.2.21

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "halbot",
3
3
  "description": "Just another `ChatGPT` / `Gemini` / `Mistral (by ollama)` Telegram bob, which is simple design, easy to use, extendable and fun.",
4
- "version": "1991.2.19",
4
+ "version": "1991.2.21",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/halbot",
7
7
  "type": "module",
@@ -46,12 +46,12 @@
46
46
  "mysql2": "^3.10.3",
47
47
  "office-text-extractor": "^3.0.3",
48
48
  "ollama": "^0.5.6",
49
- "openai": "^4.53.0",
49
+ "openai": "^4.53.2",
50
50
  "pg": "^8.12.0",
51
51
  "pgvector": "^0.2.0",
52
52
  "telegraf": "^4.16.3",
53
53
  "tesseract.js": "^5.1.0",
54
- "utilitas": "^1996.1.30",
54
+ "utilitas": "^1997.1.2",
55
55
  "youtube-transcript": "^1.2.1"
56
56
  }
57
57
  }
@@ -1,25 +1,140 @@
1
- import { alan } from 'utilitas';
1
+ import { alan, bot, uoid, utilitas } from 'utilitas';
2
+
3
+ const EMIJI_FINISH = '☑️';
4
+
5
+ // moved to help and configs
6
+ const keyboards = [[
7
+ { text: '/clear this thread 🆑' },
8
+ { text: '/clearall threads 🔄' }
9
+ ], [
10
+ { text: '/end this thread ❎' },
11
+ { text: '/list threads 🧵' },
12
+ { text: '/new thread ✨' },
13
+ ], [
14
+ { text: '/help 🛟' },
15
+ { text: '/ttsoff 🔇' },
16
+ { text: '/ttson 🔊' },
17
+ ]];
2
18
 
3
19
  const action = async (ctx, next) => {
4
- const resetContext = context => ctx.session.context = context || {}
20
+ // reset session storage
21
+ const resetSession = () => ctx.session.sessionId = uoid.create({
22
+ type: `ALAN_SESSION_${ctx.chatId}`, security: true,
23
+ });
24
+ const resetSessions = () => ctx.session.sessions = [];
25
+ const resetContext = context => ctx.session.context = context || {};
26
+ const now = Date.now();
27
+ ctx.session.sessionId || resetSession();
28
+ ctx.session.sessions || resetSessions();
5
29
  ctx.session.context || resetContext();
6
- ctx.carry = { sessionId: `ALAN_SESSION_${ctx.chatId}` };
30
+ // load functions
31
+ const switchSession = async () => {
32
+ let resp;
33
+ for (const session of ctx.session.sessions) {
34
+ if (session.id === ctx.session.sessionId) {
35
+ ctx.session.sessionId = session.id;
36
+ ctx.session.context = session.context;
37
+ session.touchedAt = now;
38
+ resp = session;
39
+ break;
40
+ }
41
+ }
42
+ if (!resp) {
43
+ ctx.session.sessions.push(resp = {
44
+ id: resetSession(),
45
+ createdAt: now, touchedAt: now, context: {},
46
+ });
47
+ await ctx.clear();
48
+ }
49
+ ctx.carry = { sessionId: ctx.session.sessionId };
50
+ return resp;
51
+ };
52
+ const defauleTitle = i => (ctx.session.sessions[i]?.context?.cmd
53
+ ? `\`${ctx.session.sessions[i].context.cmd}\` ` : 'Untitled thread '
54
+ ) + `(${new Date(ctx.session.sessions[i].createdAt).toLocaleString()})`;
55
+ const getLabel = i => ctx.session.sessions[i].label || defauleTitle(i);
56
+ const findSession = id => {
57
+ for (let i = 0; i < ctx.session.sessions.length; i++) {
58
+ if (ctx.session.sessions[i].id === utilitas.trim(id)) {
59
+ return i;
60
+ }
61
+ }
62
+ };
7
63
  ctx.clear = async context => {
8
64
  await alan.resetSession(
9
- ctx.carry.sessionId, // { systemPrompt: context?.prompt } @todo! // switch to real system prompt
65
+ ctx.session.sessionId,
66
+ // { systemPrompt: context?.prompt } // @todo: switch to real system prompt
10
67
  );
11
68
  resetContext(context);
69
+ const id = findSession(ctx.session.sessionId);
70
+ ctx.cmd.ignored = true;
71
+ ctx.session.sessions?.[id] && (
72
+ ctx.session.sessions[id].context = ctx.session.context
73
+ );
12
74
  ctx.hello(context?.prompt);
13
75
  };
76
+ const ok = async (message, options) => await ctx.ok(message, {
77
+ ...options || {}, ...options?.buttons ? {} : { keyboards }
78
+ });
79
+ // handle commands
80
+ const sendList = async (names, lastMsgId) => {
81
+ lastMsgId = lastMsgId || ctx.update?.callback_query?.message?.message_id;
82
+ const message = `🧵 Thread${ctx.session.sessions.length > 0 ? 's' : ''}:`;
83
+ const buttons = ctx.session.sessions.map((x, i) => {
84
+ names?.[x.id]
85
+ && (ctx.session.sessions[i].label = names[x.id])
86
+ && (ctx.session.sessions[i].labelUpdatedAt = now);
87
+ return {
88
+ label: `${ctx.session.sessions[i].id === ctx.session.sessionId
89
+ ? `${EMIJI_FINISH} ` : ''}${getLabel(i)}`,
90
+ text: `/switch ${x.id}`,
91
+ };
92
+ });
93
+ return await ok(message, { lastMessageId: lastMsgId, buttons });
94
+ };
95
+ const switched = async preTitle => await ok(
96
+ `${preTitle ? `❎ Thread ended: \`${preTitle}\`.\n\n` : ''}` +
97
+ `${EMIJI_FINISH} Thread switched: \`${getLabel(findSession(ctx.session.sessionId))}\`.`
98
+ );
14
99
  switch (ctx.cmd?.cmd) {
15
- case 'clear':
16
- await ctx.clear();
17
- break;
100
+ case 'clear': await ctx.clear(); break;
101
+ case 'clearall': resetSessions(); break;
102
+ case 'new': resetSession(); break;
103
+ case 'list':
104
+ const resp = await sendList();
105
+ utilitas.ignoreErrFunc(async () => {
106
+ const sNames = await alan.analyzeSessions(
107
+ ctx.session.sessions.filter(
108
+ x => (x.labelUpdatedAt || 0) < x.touchedAt
109
+ ).map(x => x.id), { ignoreRequest: bot.HELLO }
110
+ );
111
+ return await sendList(sNames, resp[0]?.message_id);
112
+ });
113
+ return resp;
114
+ case 'end':
115
+ const id = findSession(
116
+ ctx.cmd.args.startsWith('ALAN_SESSION_')
117
+ && utilitas.trim(ctx.cmd.args) || ctx.session.sessionId
118
+ );
119
+ let preTitle = '';
120
+ ctx.session.sessions?.[id] && (preTitle = getLabel(id))
121
+ && (ctx.session.sessions.splice(id, 1));
122
+ const sorted = ctx.session.sessions.slice().sort(
123
+ (x, y) => y.touchedAt - x.touchedAt
124
+ );
125
+ ctx.session.sessionId = sorted?.[0]?.id;
126
+ await switchSession();
127
+ return await switched(preTitle);
128
+ case 'switch':
129
+ ctx.session.sessionId = utilitas.trim(ctx.cmd.args);
130
+ await switchSession();
131
+ return await sendList();
18
132
  case 'factory':
19
133
  case 'reset':
20
- await alan.resetSession(ctx.carry.sessionId);
134
+ await alan.resetSession(ctx.session.sessionId);
21
135
  break;
22
136
  }
137
+ await switchSession();
23
138
  await next();
24
139
  };
25
140
 
@@ -29,5 +144,12 @@ export const { name, run, priority, func, help, cmds, cmdx } = {
29
144
  priority: -8845,
30
145
  func: action,
31
146
  help: '',
32
- cmdx: {},
147
+ cmdx: {
148
+ new: 'Add a session.',
149
+ end: 'Delete a session.',
150
+ switch: 'Switch to a session.',
151
+ clear: 'Clear current AI conversation session and start a new one.',
152
+ clearall: 'Clear all AI conversation sessions.',
153
+ list: 'List all AI conversation sessions.',
154
+ },
33
155
  };
@@ -73,7 +73,6 @@ export const { name, run, priority, func, help, cmds, cmdx } = {
73
73
  del: 'Delete a custom prompt: /del `COMMAND`.',
74
74
  acplist: `List prompts from ${ACP}.`,
75
75
  acpdetail: `Show details of ${ACP}.`,
76
- clear: 'Clear current AI conversation session and start a new one.',
77
76
  },
78
77
  cmdx: {},
79
78
  };
@@ -10,7 +10,7 @@ const action = async (ctx, next) => {
10
10
  ctx.avatar = '🔘'; ctx.result = utilitas.trim(ctx.txt);
11
11
  } else if (ctx.m?.poll) {
12
12
  ctx.avatar = '📊';
13
- } else if (ctx.cmd?.cmd && ctx.cmd?.cmd !== 'clear') {
13
+ } else if (ctx.cmd?.cmd && !ctx.cmd?.ignored) {
14
14
  ctx.avatar = '🚀'; ctx.result = utilitas.trim(ctx.txt);
15
15
  } else {
16
16
  ctx.avatar = '😸';
@@ -36,7 +36,7 @@ const action = async (ctx, next) => {
36
36
  options?.final && cmd && (extra.buttons = [{
37
37
  label: `❎ End context: \`${cmd}\``, text: '/clear',
38
38
  }]);
39
- return await ctx.ok(curMsg, { md: true, ...options || {}, ...extra });
39
+ return await ctx.ok(curMsg, { md: true, ...extra, ...options || {} });
40
40
  };
41
41
  await ok(onProgress);
42
42
  for (let n of ctx.selectedAi) {