halbot 1995.1.2 → 1995.1.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.
package/lib/hal.mjs CHANGED
@@ -2,6 +2,7 @@ import { alan, bot, callosum, dbio, storage, utilitas, } from 'utilitas';
2
2
  import { join } from 'path';
3
3
  import { parseArgs as _parseArgs } from 'node:util';
4
4
  import { readdirSync } from 'fs';
5
+ import { ignoreErrFunc } from 'utilitas/lib/utilitas.mjs';
5
6
 
6
7
  // 👇 https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this
7
8
  const [ // https://limits.tginfo.me/en
@@ -111,30 +112,21 @@ const json = obj => bot.lines([
111
112
  ]);
112
113
 
113
114
  const establish = module => {
114
- if (!module.run) { return; }
115
115
  assert(module?.func, 'Pipeline function is required.', 500);
116
116
  _.pipeline[module.name || (module.name = uuidv4())] = {
117
- args: module.args || {},
118
- cmds: module.cmds || {},
119
- cmdx: module.cmdx,
120
- help: module.help || '',
117
+ help: module.help || '', args: module.args || {},
121
118
  hidden: !!module.hidden,
122
119
  };
123
120
  _.args = { ..._.args, ...module.args || {} };
124
- for (let sub of ['cmds', 'cmdx']) {
125
- Object.keys(module[sub] || {}).map(command => _.cmds.push(
126
- newCommand(command, module[sub][command])
127
- ));
128
- }
121
+ Object.keys(module?.cmds || {}).map(command => _.cmds.push(
122
+ newCommand(command, module?.cmds[command])
123
+ ));
129
124
  log(`Establishing: ${module.name} (${module.priority})`, { force: true });
130
- return _.bot.use(utilitas.countKeys(module.cmds) && !module.cmdx ? async (ctx, next) => {
131
- for (let c in module.cmds) {
132
- if (utilitas.insensitiveCompare(ctx._?.cmd?.cmd, c)) {
133
- return await module.func(ctx, next);
134
- }
135
- }
136
- return await next();
137
- } : module.func);
125
+ return _.bot.use(utilitas.countKeys(module.cmds) ? (async (ctx, next) =>
126
+ await (utilitas.insensitiveHas(
127
+ Object.keys(module.cmds), ctx._?.cmd?.cmd
128
+ ) || module.run ? module.func(ctx, next) : next())
129
+ ) : module.func);
138
130
  };
139
131
 
140
132
  const parseArgs = async (args, ctx) => {
@@ -269,6 +261,11 @@ const extendSessionStorage = storage => storage && storage.client && {
269
261
  set: async (id, session, options) => await storage.set(id, session, options),
270
262
  };
271
263
 
264
+ const subconscious = {
265
+ name: 'Subconscious', run: true, priority: 0,
266
+ func: async (_, next) => ignoreErrFunc(next, logOptions), // non-blocking
267
+ };
268
+
272
269
  const init = async (options) => {
273
270
  if (options) {
274
271
  const { ais } = await alan.initChat({
@@ -302,7 +299,6 @@ const init = async (options) => {
302
299
  storage: options?.storage,
303
300
  supportedMimeTypes,
304
301
  };
305
- const mods = [];
306
302
  if (_.storage) {
307
303
  assert([
308
304
  dbio.MYSQL, dbio.POSTGRESQL, storage.FILE
@@ -322,6 +318,7 @@ const init = async (options) => {
322
318
  } catch (e) { console.error(e); }
323
319
  }
324
320
  }
321
+ const mods = [subconscious];
325
322
  for (let pipeline of utilitas.ensureArray(options?.pipelinePath || [])) {
326
323
  log(`PIPELINE: ${pipeline}`);
327
324
  const files = (readdirSync(pipeline) || []).filter(
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "halbot",
3
3
  "description": "Just another AI powered Telegram bot, which is simple design, easy to use, extendable and fun.",
4
- "version": "1995.1.2",
4
+ "version": "1995.1.4",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/halbot",
7
7
  "type": "module",
@@ -135,11 +135,11 @@ const ctxExt = ctx => {
135
135
  for (let i in pages) {
136
136
  const lastPage = ~~i === pages.length - 1;
137
137
  const shouldExtra = options?.lastMessageId || lastPage;
138
- if (options?.onProgress && !options?.lastMessageId
138
+ if (options?.processing && !options?.lastMessageId
139
139
  && pageMap[pageIds[~~i]]?.text === pages[i]) { continue; }
140
- if (options?.onProgress && !pageIds[~~i]) { // progress: new page, reply text
140
+ if (options?.processing && !pageIds[~~i]) { // progress: new page, reply text
141
141
  await ctx.resp(pages[i], false, extra);
142
- } else if (options?.onProgress) { // progress: ongoing, edit text
142
+ } else if (options?.processing) { // progress: ongoing, edit text
143
143
  await ctx.edit(
144
144
  pageIds[~~i], pages[i], false, shouldExtra ? extra : {}
145
145
  );
@@ -72,6 +72,12 @@ const action = async (ctx, next) => {
72
72
  await next();
73
73
  };
74
74
 
75
- export const { name, run, priority, func, cmdx } = {
76
- name: _name, run: true, priority: 20, func: action, cmdx: {}
75
+ export const { name, run, hidden, priority, func, help, cmds } = {
76
+ name: _name, run: true, hidden: true, priority: 20, func: action,
77
+ help: bot.lines([
78
+ '¶ Commands handler.',
79
+ ]),
80
+ cmds: {
81
+ clearkb: 'Clear keyboard: /clearkb',
82
+ },
77
83
  };
@@ -38,7 +38,7 @@ const action = async (ctx, next) => {
38
38
  await ctx.ok(bot.EMOJI_THINKING);
39
39
  for (let i = 0; i < 2; i++) {
40
40
  await ctx.timeout();
41
- await ctx.ok(ipsum(), { ...extra, onProgress: true });
41
+ await ctx.ok(ipsum(), { ...extra, processing: true });
42
42
  }
43
43
  await ctx.timeout();
44
44
  await ctx.ok(ipsum(), { ...extra, md });
@@ -54,9 +54,9 @@ const action = async (ctx, next) => {
54
54
  await ctx.ok(resp, { md });
55
55
  };
56
56
 
57
- export const { _NEED, name, run, hidden, priority, func, help, cmds } = {
57
+ export const { _NEED, name, hidden, priority, func, help, cmds } = {
58
58
  _NEED: ['lorem-ipsum'],
59
- name: 'Echo', run: true, hidden: true, priority: 30, func: action,
59
+ name: 'Echo', hidden: true, priority: 30, func: action,
60
60
  help: bot.lines([
61
61
  '¶ Basic behaviors for debug only.',
62
62
  ]),
@@ -10,14 +10,11 @@ const action = async (ctx, next) => {
10
10
  if (hal._.pipeline[i].help) {
11
11
  _help.push(hal._.pipeline[i].help);
12
12
  }
13
- const cmdsx = {
14
- ...hal._.pipeline[i].cmds || {},
15
- ...hal._.pipeline[i].cmdx || {},
16
- };
17
- if (utilitas.countKeys(cmdsx)) {
13
+ const cmds = hal._.pipeline[i].cmds || {};
14
+ if (utilitas.countKeys(cmds)) {
18
15
  _help.push(bot.lines([
19
16
  '_🪄 Commands:_',
20
- ...Object.keys(cmdsx).map(x => `- /${x}: ${cmdsx[x]}`),
17
+ ...Object.keys(cmds).map(x => `- /${x}: ${cmds[x]}`),
21
18
  ]));
22
19
  }
23
20
  if (utilitas.countKeys(hal._.pipeline[i].args)) {
@@ -36,8 +33,8 @@ const action = async (ctx, next) => {
36
33
  await ctx.ok(lines2(help), { md: true });
37
34
  };
38
35
 
39
- export const { name, run, priority, func, help, cmds } = {
40
- name: 'Help', run: true, priority: 40, func: action,
36
+ export const { name, priority, func, help, cmds } = {
37
+ name: 'Help', priority: 40, func: action,
41
38
  help: bot.lines([
42
39
  '¶ Basic syntax of this document:',
43
40
  'Scheme for commands: /`COMMAND`: `DESCRIPTION`',
@@ -11,7 +11,7 @@ const ctxExt = ctx => {
11
11
  const action = async (ctx, next) => {
12
12
  ctxExt(ctx);
13
13
  let parsed = null;
14
- switch (ctx._.cmd?.cmd) {
14
+ switch (ctx._.cmd.cmd) {
15
15
  case 'lang':
16
16
  if (!ctx._.cmd.args) {
17
17
  return await ctx.ok('Please specify a language.');
@@ -54,8 +54,8 @@ const action = async (ctx, next) => {
54
54
  await next();
55
55
  };
56
56
 
57
- export const { name, run, priority, func, help, cmdx, args } = {
58
- name: 'Config', run: true, priority: 60, func: action,
57
+ export const { name, priority, func, help, cmds, args } = {
58
+ name: 'Config', priority: 60, func: action,
59
59
  help: bot.lines([
60
60
  '¶ Configure the bot by UNIX/Linux CLI style.',
61
61
  'Using [node:util.parseArgs](https://nodejs.org/api/util.html#utilparseargsconfig) to parse arguments.',
@@ -64,12 +64,14 @@ export const { name, run, priority, func, help, cmdx, args } = {
64
64
  '¶ When enabled, the bot will speak out the answer if available.',
65
65
  'Example 1: /set --tts on',
66
66
  'Example 2: /set --tts off',
67
- ]), cmdx: {
67
+ ]),
68
+ cmds: {
68
69
  lang: 'Set your default language: /lang `LANG`',
69
70
  toggle: 'Toggle configurations. Only works for boolean values.',
70
71
  set: 'Usage: /set --`OPTION` `VALUE` -`SHORT`',
71
72
  reset: 'Reset configurations.',
72
- }, args: {
73
+ },
74
+ args: {
73
75
  chatty: {
74
76
  type: 'string', short: 'c', default: hal.ON,
75
77
  desc: `\`(${hal.BINARY_STRINGS.join(', ')})\` Enable/Disable chatty mode.`,
@@ -99,13 +99,14 @@ const action = async (ctx, next) => {
99
99
  }
100
100
  };
101
101
 
102
- export const { name, run, priority, func, help, cmdx } = {
102
+ export const { name, run, priority, func, help, cmds } = {
103
103
  name: 'History', run: true, priority: 80, func: action,
104
104
  help: bot.lines([
105
105
  '¶ Search history.',
106
106
  'Example 1: /search Answer to the Ultimate Question',
107
107
  'Example 2: /search Answer to the Ultimate Question --skip=10',
108
- ]), cmdx: {
108
+ ]),
109
+ cmds: {
109
110
  search: 'Usage: /search `ANYTHING` --skip=`OFFSET`',
110
- }
111
+ },
111
112
  };
@@ -5,17 +5,19 @@ const TOP = 'top';
5
5
 
6
6
  const listAIs = async ctx => {
7
7
  const lastMessageId = ctx?.update?.callback_query?.message?.message_id;
8
- const message = `Features:\n`
9
- + hal.uList(Object.entries(alan.FEATURE_ICONS).map(
10
- x => `${x[1]} ${x[0]}`
11
- )) + `\n\nAI${ais.length > 0 ? 's' : ''}:\n`;
8
+ const message = `*Time:* \n${new Date().toLocaleString()}\n\n`
9
+ + `*Features:*\n` + hal.uList(Object.entries(alan.FEATURE_ICONS).filter(
10
+ x => x[0] !== 'hidden'
11
+ ).map(
12
+ x => `${x[1]} \`${x[0]}\``
13
+ )) + `\n\n*AI${ais.length > 0 ? 's' : ''}:*\n`;
12
14
  const buttons = ais.map((x, i) => ({
13
15
  label: `${ctx._.session.config?.ai === x.id
14
16
  || (!ctx._.session.config?.ai && i === 0) ? `${hal.CHECK} `
15
- : ''}${x.name}: ${x.features}`,
17
+ : ''}${x.label}`,
16
18
  text: `/set --ai=${x.id}`,
17
19
  }));
18
- return await ctx.ok(message, { lastMessageId, buttons });
20
+ return await ctx.ok(message, { lastMessageId, buttons, md: true });
19
21
  };
20
22
 
21
23
  const action = async (ctx, next) => {
@@ -43,11 +45,8 @@ const validateAi = async (val, ctx) => {
43
45
  utilitas.throwError('No AI engine matched.');
44
46
  };
45
47
 
46
- export const { name, run, priority, func, help, args, cmdx } = {
47
- name: 'AI',
48
- run: true,
49
- priority: 90,
50
- func: action,
48
+ export const { name, run, priority, func, help, args, cmds } = {
49
+ name: 'AI', run: true, priority: 90, func: action,
51
50
  help: bot.lines([
52
51
  '¶ Set initial prompt to the AI model.',
53
52
  "Tip 1: Set `hello=''` to reset to default initial prompt.",
@@ -59,6 +58,10 @@ export const { name, run, priority, func, help, args, cmdx } = {
59
58
  'Tip 5: `/[AI_ID]` Tell me a joke.',
60
59
  'Tip 6: `/all` Use the top 3 AI models simultaneously, for current prompt.',
61
60
  ]),
61
+ cmds: {
62
+ ai: 'List all available AIs.',
63
+ all: 'Use the top 3 AI models simultaneously: /all Say hello to all AIs!',
64
+ },
62
65
  args: {
63
66
  hello: {
64
67
  type: 'string', short: 's', default: 'Hello!',
@@ -70,8 +73,4 @@ export const { name, run, priority, func, help, args, cmdx } = {
70
73
  validate: validateAi,
71
74
  },
72
75
  },
73
- cmdx: {
74
- ai: 'List all available AIs.',
75
- all: 'Use the top 3 AI models simultaneously: /all Say hello to all AIs!',
76
- }
77
76
  };
@@ -3,63 +3,53 @@ import { alan } from '../index.mjs';
3
3
  const _name = 'Chat';
4
4
  const log = (c, o) => utilitas.log(c, _name, { time: 1, ...o || {} });
5
5
 
6
- const generate = async (ctx) => {
7
- try {
8
- let [resp, extra, lock, sResp, lastMsg, lastSent] =
9
- [null, { buttons: [] }, 1000 * 5, null, null, 0];
10
- const ok = async options => {
11
- const curTime = Date.now();
12
- if (options?.processing && (
13
- curTime - lastSent < ctx._.limit || lastMsg === resp.text
14
- )) { return; }
15
- [lastSent, lastMsg] = [curTime + lock, resp.text];
16
- if (!options?.processing) {
17
- (resp.annotations || []).map((x, i) => extra.buttons.push({
18
- label: `${i + 1}. ${x.title}`, url: x.url,
19
- }));
20
- }
21
- sResp = await ctx.ok(resp.text, {
22
- ...ctx._.keyboards ? { keyboards: ctx._.keyboards } : {},
23
- md: true, ...extra, ...options || {},
24
- });
25
- lastSent = curTime;
26
- return sResp;
27
- };
28
- resp = await alan.talk(ctx._.text, {
29
- ...ctx._, sessionId: ctx._.chatId, // THIS LINE IS IMPORTANT
30
- stream: async rsp => { resp = rsp; ok({ processing: true }); }, // Never await, it will block the stream.
31
- });
32
- for (let image of resp?.images || []) {
33
- await ctx.timeout();
34
- await ctx.image(image.data, { caption: image.caption });
35
- }
36
- for (let video of resp?.videos || []) {
37
- await ctx.timeout();
38
- await ctx.video(video.data, { caption: video.caption });
39
- }
40
- for (let audio of resp?.audios || []) {
41
- await ctx.timeout();
42
- await ctx.audio(audio.data, { caption: audio.caption });
43
- }
44
- // print(resp);
45
- await resp.text.trim() ? ok({ processing: false })
46
- : ctx.deleteMessage(ctx._.done[0].message_id);
47
- ctx._.request = resp.request;
48
- ctx._.response = resp.response;
49
- ctx.memorize && await ctx.memorize();
50
- } catch (err) { log(err); }
51
- };
52
-
53
6
  const action = async (ctx, next) => {
54
- ctx.finish();
7
+ if (!ctx._.text && !ctx._.collected.length) { return await next(); }
8
+ let [resp, extra, lock, sResp, lastMsg, lastSent] =
9
+ [null, { buttons: [] }, 1000 * 5, null, null, 0];
10
+ const ok = async options => {
11
+ const curTime = Date.now();
12
+ if (options?.processing && (
13
+ curTime - lastSent < ctx._.limit || lastMsg === resp.text
14
+ )) { return; }
15
+ [lastSent, lastMsg] = [curTime + lock, resp.text];
16
+ if (!options?.processing) {
17
+ (resp.annotations || []).map((x, i) => extra.buttons.push({
18
+ label: `${i + 1}. ${x.title}`, url: x.url,
19
+ }));
20
+ }
21
+ sResp = await ctx.ok(resp.text, {
22
+ ...ctx._.keyboards ? { keyboards: ctx._.keyboards } : {},
23
+ md: true, ...extra, ...options || {},
24
+ });
25
+ lastSent = curTime;
26
+ return sResp;
27
+ };
28
+ resp = await alan.talk(ctx._.text, {
29
+ ...ctx._, sessionId: ctx._.chatId, // THIS LINE IS IMPORTANT
30
+ stream: async rsp => { resp = rsp; ok({ processing: true }); }, // Never await, it will block the stream.
31
+ });
32
+ for (let image of resp?.images || []) {
33
+ await ctx.timeout();
34
+ await ctx.image(image.data, { caption: image.caption });
35
+ }
36
+ for (let video of resp?.videos || []) {
37
+ await ctx.timeout();
38
+ await ctx.video(video.data, { caption: video.caption });
39
+ }
40
+ for (let audio of resp?.audios || []) {
41
+ await ctx.timeout();
42
+ await ctx.audio(audio.data, { caption: audio.caption });
43
+ }
44
+ // print(resp);
45
+ await resp.text.trim() ? ok({ processing: false })
46
+ : ctx.deleteMessage(ctx._.done[0].message_id);
47
+ ctx._.request = resp.request;
48
+ ctx._.response = resp.response;
49
+ ctx.memorize && await ctx.memorize();
55
50
  await next();
56
- if (!ctx._.text && !ctx._.collected.length) { return; }
57
- generate(ctx);
58
51
  };
59
52
 
60
53
  export const { name, run, priority, func } = {
61
- name: _name,
62
- run: true,
63
- priority: 100,
64
- func: action,
54
+ name: _name, run: true, priority: 100, func: action,
65
55
  };