halbot 1989.6.37 → 1989.6.38

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/README.md CHANGED
@@ -88,7 +88,27 @@ All supported configuration fields:
88
88
  // Defaule: ['private', 'mention'].
89
89
  // By default, it will only reply to `private` chats and group `mention`s.
90
90
  // Adding 'group' or 'channel' may cause too much disturbance.
91
- "chatType": ["mention", "private"]
91
+ "chatType": ["mention", "private"],
92
+
93
+ // OPTIONAL, object.
94
+ // Session storage, support MariaDB/MySQL and Redis for now.
95
+ // If omitted, the bot will use memory storage and sync to this file.
96
+ // Example: (Compatibility: https://github.com/sidorares/node-mysql2)
97
+ "session": {
98
+ type: [['MARIADB' || 'MYSQL']],
99
+ host: [[DATABASE HOST]],
100
+ database: [[DATABASE NAME]],
101
+ user: [[DATABASE USER]],
102
+ password: [[DATABASE PASSWORD]],
103
+ ...[[OTHER DATABASE OPTIONS]],
104
+ },
105
+ // OR: (Compatibility: https://github.com/luin/ioredis)
106
+ "session": {
107
+ type: 'REDIS',
108
+ host: [[REDIS HOST]],
109
+ password: [[REDIS PASSWORD]],
110
+ ...[[OTHER REDIS OPTIONS]],
111
+ },
92
112
 
93
113
  }
94
114
  ```
@@ -161,6 +181,13 @@ const config = {
161
181
  // | };
162
182
  skillPath: './skills',
163
183
 
184
+ // OPTIONAL, object.
185
+ // Using customized callback as storage engine.
186
+ session: {
187
+ get: async (chatId) => { /* return session object by chatId. */ },
188
+ set: async (chatId, session) => { /* save session object by chatId. */ },
189
+ },
190
+
164
191
  };
165
192
 
166
193
  await halbot(config);
package/bin/halbot.mjs CHANGED
@@ -1,32 +1,34 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { storage, utilitas } from 'utilitas';
3
+ import { cache, dbio, memory, storage, utilitas } from 'utilitas';
4
4
  import halbot from '../index.mjs';
5
5
 
6
6
  const debug = utilitas.humanReadableBoolean(process.env['DEBUG']);
7
7
  const log = content => utilitas.log(content, import.meta.url);
8
+ const getConfig = async () => (await storage.getConfig())?.config;
8
9
 
9
- // Disabled for current version.
10
- // const args = await (async (options) => {
11
- // const { parseArgs } = await import('node:util');
12
- // if (parseArgs) { // https://kgrz.io/node-has-native-arg-parsing.html
13
- // const { values } = parseArgs({
14
- // options: {
15
- // // xxx: { type: 'string', short: 'x', default: '' }
16
- // },
17
- // ...options || {},
18
- // });
19
- // return values;
20
- // }
21
- // let args = {}; // simple fallback for node version < 19
22
- // process.argv.map(arg => {
23
- // const item = arg.replace(/^\-*([^=]*)=(.*)$/ig, '$1<,>$2').split('<,>');
24
- // item.length > 1 && (args[item[0]] = item[1]);
25
- // });
26
- // return args;
27
- // })();
10
+ let session = {
11
+ get: async key => (await getConfig())?.sessions?.[key],
12
+ set: async (k, v) => await storage.setConfig({ sessions: { [k]: v } }),
13
+ };
28
14
 
29
15
  try {
30
- const { config } = await storage.getConfig();
31
- await halbot(config);
16
+ const { filename, config } = await storage.getConfig();
17
+ assert(utilitas.countKeys(config), `Error loading config from ${filename}.`);
18
+ const sessionType = utilitas.trim(config.session?.type, { case: 'UP' });
19
+ if (config.session?.type) { delete config.session.type; }
20
+ switch (sessionType) {
21
+ case 'MARIADB': case 'MYSQL':
22
+ await dbio.init(config.session);
23
+ await memory.init();
24
+ session = memory;
25
+ break;
26
+ case 'REDIS':
27
+ await cache.init(config.session);
28
+ session = cache;
29
+ break;
30
+ default:
31
+ config.session && utilitas.throwError('Invalid session config.');
32
+ }
33
+ await halbot({ ...config, session });
32
34
  } catch (err) { debug ? utilitas.throwError(err) : log(err); }
package/index.mjs CHANGED
@@ -2,16 +2,37 @@ import { bot, hal, shot, speech, utilitas } from 'utilitas';
2
2
  import { parse } from 'csv-parse/sync';
3
3
 
4
4
  await utilitas.locate(utilitas.__(import.meta.url, 'package.json'));
5
+ const log = content => utilitas.log(content, 'halbot');
5
6
  const skillPath = utilitas.__(import.meta.url, 'skills');
6
- const MAX_MENU_LENGTH = 32;
7
7
 
8
8
  const promptSource = new Set([
9
- 'https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv',
9
+ // 'https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv',
10
+ 'https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/4fa40ad4067dce08a007f1c07562fac9dcbfcd1d/prompts.csv',
10
11
  ]);
11
12
 
13
+ const fetchPrompts = async () => {
14
+ const prompts = {};
15
+ for (let source of promptSource) {
16
+ try {
17
+ const resp = (await shot.get(source)).content;
18
+ const pmts = parse(resp, { columns: true, skip_empty_lines: true });
19
+ assert(pmts?.length, `Failed to load external prompts: ${source}.`);
20
+ pmts.filter(x => x.act && x.prompt).map(x => {
21
+ const command = utilitas.ensureString(
22
+ x.act, { case: 'SNAKE' }
23
+ ).slice(0, bot.MAX_MENU_LENGTH);
24
+ prompts[command] = { command, ...x };
25
+ });
26
+ } catch (err) { log(err?.message || err); }
27
+ }
28
+ log(`Awesome ChatGPT Prompts: fetch ${utilitas.countKeys(prompts)} items.`);
29
+ return prompts;
30
+ };
31
+
12
32
  const init = async (options) => {
13
33
  assert(options?.telegramToken, 'Telegram Bot API Token is required.');
14
- const [ai, _speech, prompts] = [{}, {}, {}];
34
+ const [pkg, ai, _speech] = [await utilitas.which(), {}, {}];
35
+ const info = bot.lines([`[🤖️ ${pkg.title}](${pkg.homepage})`, pkg.description]);
15
36
  if (options?.googleApiKey) {
16
37
  await speech.init({ apiKey: options?.googleApiKey, tts: true, stt: true });
17
38
  Object.assign(_speech, { stt: speech.stt, tts: speech.tts });
@@ -26,35 +47,21 @@ const init = async (options) => {
26
47
  provider: 'BING', clientOptions: { userToken: options.bingToken },
27
48
  });
28
49
  }
29
- utilitas.ensureArray(options?.prompts).filter(x => x).map(promptSource.add);
30
- for (let source of promptSource) {
31
- try {
32
- const resp = (await shot.get(source)).content;
33
- const pmts = parse(resp, { columns: true, skip_empty_lines: true });
34
- assert(pmts?.length, `Failed to load external prompts: ${source}.`);
35
- pmts.filter(x => x.act && x.prompt).map(x => {
36
- const cmd = utilitas.ensureString(x.act, { case: 'SNAKE' });
37
- prompts[cmd.slice(0, MAX_MENU_LENGTH)]
38
- = { description: x.act, prompt: x.prompt }
39
- });
40
- } catch (err) { utilitas.log(err?.message || err); }
41
- }
42
50
  assert(utilitas.countKeys(ai), 'No AI provider is configured.');
43
51
  const _bot = await bot.init({
52
+ info: options?.info || info,
44
53
  ai, auth: options?.auth,
45
54
  botToken: options?.telegramToken,
46
55
  chatType: options?.chatType,
47
56
  homeGroup: options?.homeGroup,
48
57
  magicWord: options?.magicWord,
49
58
  private: options?.private,
50
- prompts, provider: 'telegram',
59
+ provider: 'telegram',
60
+ session: options?.session,
51
61
  skillPath: options?.skillPath || skillPath,
52
62
  speech: _speech,
53
63
  });
54
- // https://limits.tginfo.me/en
55
- await _bot.telegram.setMyCommands(Object.keys(prompts).slice(0, 100).map(
56
- command => ({ command, description: prompts[command].description })
57
- ));
64
+ _bot._.prompts = await fetchPrompts();
58
65
  return _bot;
59
66
  };
60
67
 
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "halbot",
3
- "description": "Just another ChatGPT/Bing Telegram bob.",
4
- "version": "1989.6.37",
3
+ "description": "Just another ChatGPT/Bing Chat Telegram bob, which is simple design, easy to use, extendable and fun.",
4
+ "version": "1989.6.38",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/halbot",
7
7
  "type": "module",
8
8
  "engines": {
9
- "node": ">=18.x"
9
+ "node": ">=19.x"
10
10
  },
11
11
  "main": "index.mjs",
12
12
  "bin": {
@@ -31,9 +31,11 @@
31
31
  "dependencies": {
32
32
  "@google-cloud/speech": "^5.4.0",
33
33
  "@google-cloud/text-to-speech": "^4.2.1",
34
- "@waylaidwanderer/chatgpt-api": "^1.33.1",
34
+ "@waylaidwanderer/chatgpt-api": "^1.34.0",
35
35
  "csv-parse": "^5.3.6",
36
+ "ioredis": "^5.3.1",
37
+ "mysql2": "^3.2.0",
36
38
  "telegraf": "^4.12.2",
37
- "utilitas": "^1992.4.46"
39
+ "utilitas": "^1992.4.70"
38
40
  }
39
41
  }
@@ -0,0 +1,64 @@
1
+ import { utilitas } from 'utilitas';
2
+
3
+ const onProgress = { onProgress: true };
4
+ const [YOU, BOT, LN2] = ['😸 You:', '🤖️ ', '\n\n'];
5
+ const [joinL1, joinL2] = [a => a.join(`${LN2}---${LN2}`), a => a.join(LN2)];
6
+ const enrich = name => name === 'Bing' ? `${name} (Sydney)` : name;
7
+ const log = content => utilitas.log(content, import.meta.url);
8
+
9
+ const action = async (ctx, next) => {
10
+ if (!ctx.text) { return await next(); }
11
+ const [msgs, tts, pms, extra] = [{}, {}, [], {}];
12
+ let [lastMsg, lastSent] = ['', 0];
13
+ const packMsg = options => {
14
+ const packed = [...ctx.overwrite ? [joinL2([YOU, ctx.overwrite])] : []];
15
+ const source = options?.tts ? tts : msgs;
16
+ ctx.selectedAi.map(n => packed.push(joinL2([
17
+ ...ctx.multiAi || !ctx.isDefaultAi(n) || ctx.overwrite ? [`${BOT}${enrich(n)}:`] : [],
18
+ options?.onProgress ? (
19
+ source[n] ? `${source[n].trim()} █` : '💬'
20
+ ) : (source[n] || ''),
21
+ ])));
22
+ return joinL1(packed);
23
+ };
24
+ const ok = async options => {
25
+ const [curTime, curMsg] = [Date.now(), packMsg(options)];
26
+ if (options?.onProgress && (
27
+ curTime - lastSent < ctx.limit || lastMsg === curMsg
28
+ )) { return; }
29
+ [lastSent, lastMsg] = [curTime, curMsg];
30
+ return await ctx.ok(curMsg, { md: true, ...options || {}, ...extra });
31
+ };
32
+ await ok(onProgress);
33
+ for (let n of ctx.selectedAi) {
34
+ pms.push((async () => {
35
+ try {
36
+ const resp = await ctx._.ai[n].send(
37
+ ctx.text, { session: ctx.chatId }, tkn => {
38
+ msgs[n] = `${(msgs[n] || '')}${tkn}`;
39
+ ok(onProgress);
40
+ }
41
+ );
42
+ msgs[n] = ctx.session.config?.render === false ? resp.response : resp.responseRendered;
43
+ tts[n] = resp.spokenText;
44
+ extra.buttons = resp?.suggestedResponses?.map?.(label => ({
45
+ label, text: `/bing ${label}`,
46
+ }));
47
+ } catch (err) {
48
+ msgs[n] = err?.message || err;
49
+ tts[n] = msgs[n];
50
+ log(err);
51
+ }
52
+ })());
53
+ }
54
+ await Promise.all(pms);
55
+ await ok();
56
+ ctx.tts = packMsg({ tts: true });
57
+ await next();
58
+ };
59
+
60
+ export const { run, priority, func } = {
61
+ run: true,
62
+ priority: 60,
63
+ func: action,
64
+ };
@@ -0,0 +1,40 @@
1
+ import { bot, utilitas } from 'utilitas';
2
+
3
+ const action = async (ctx, next) => {
4
+ const allAi = Object.keys(ctx._.ai);
5
+ switch (ctx.cmd.cmd) {
6
+ case 'all':
7
+ ctx.selectedAi = allAi;
8
+ ctx.multiAi = ctx.selectedAi.length > 1;
9
+ ctx.text = ctx.cmd.args || ctx._.hello;
10
+ break;
11
+ case 'bing':
12
+ assert(utilitas.insensitiveHas(allAi, 'bing'), 'Bing is not available.');
13
+ ctx.selectedAi = ['Bing'];
14
+ ctx.text = ctx.cmd.args || ctx._.hello;
15
+ break;
16
+ case 'chatgpt':
17
+ assert(utilitas.insensitiveHas(allAi, 'chatgpt'), 'ChatGPT is not available.');
18
+ ctx.selectedAi = ['ChatGPT'];
19
+ ctx.text = ctx.cmd.args || ctx._.hello;
20
+ break;
21
+ }
22
+ await next();
23
+ };
24
+
25
+ export const { run, priority, func, help, cmds } = {
26
+ run: true,
27
+ priority: 20,
28
+ func: action,
29
+ help: bot.lines([
30
+ 'Useful commands for AI conversation.',
31
+ 'Example 1: `/chatgpt Say hello to ChatGPT!`',
32
+ 'Example 2: `/bing Say hello to Bing!`',
33
+ 'Example 3: `/all Say hello to all AI engines!`',
34
+ ]),
35
+ cmds: {
36
+ all: 'Use all AI engines simultaneously temporary.',
37
+ bing: 'Use Bing as temporary AI engine.',
38
+ chatgpt: 'Use ChatGPT as temporary AI engine.',
39
+ },
40
+ };
@@ -0,0 +1,57 @@
1
+ import { bot, utilitas } from 'utilitas';
2
+
3
+ let configuredAi;
4
+
5
+ const action = async (ctx, next) => {
6
+ ctx.firstAi = (configuredAi = Object.keys(ctx._.ai))[0];
7
+ switch (ctx.session.config?.ai) {
8
+ case '.': ctx.selectedAi = [ctx.firstAi]; break;
9
+ case '@': ctx.selectedAi = configuredAi; break;
10
+ default: ctx.selectedAi = [configuredAi.includes(ctx.session.config?.ai)
11
+ ? ctx.session.config?.ai : ctx.firstAi];
12
+ }
13
+ ctx.multiAi = ctx.selectedAi.length > 1;
14
+ ctx.isDefaultAi = name => name === ctx.firstAi;
15
+ ctx.clear = () => (ctx.selectedAi || []).map(n => {
16
+ ctx._.ai[n].clear(ctx.chatId);
17
+ ctx.text = ctx._.hello;
18
+ });
19
+ await next();
20
+ };
21
+
22
+ const validate = val => {
23
+ assert(configuredAi, 'Preparing data for this option. Please try later.');
24
+ for (let name of [...configuredAi, '.', '@']) {
25
+ if (utilitas.insensitiveCompare(val, name)) { return name; }
26
+ }
27
+ utilitas.throwError('No AI engine matched.');
28
+ };
29
+
30
+ export const { run, priority, func, help, args } = {
31
+ run: true,
32
+ priority: 10,
33
+ func: action,
34
+ help: bot.lines([
35
+ 'Config AI engine.',
36
+ 'Set `ai`=`.` to use default AI engine.',
37
+ 'Set `ai`=`@` to use all AI engines simultaneously.',
38
+ 'Example 1: `/set --ai ChatGPT`',
39
+ 'Example 2: `/set --ai Bing`',
40
+ 'Example 3: `/set --ai .`',
41
+ 'Example 4: `/set --ai @`',
42
+ 'Example 5: `/set --render on`',
43
+ 'Example 6: `/set --render off`',
44
+ ]),
45
+ args: {
46
+ ai: {
47
+ type: 'string', short: 'a', default: '',
48
+ desc: "`(ChatGPT, Bing, ., @)` Select AI engine.",
49
+ validate,
50
+ },
51
+ render: {
52
+ type: 'string', short: 'r', default: 'on',
53
+ desc: '`(on, off)` Enable/Disable enhanced output rendering.',
54
+ validate: utilitas.humanReadableBoolean,
55
+ },
56
+ },
57
+ };
@@ -0,0 +1,18 @@
1
+ import { bot, shot, utilitas } from 'utilitas';
2
+
3
+ const action = async (ctx, next) => {
4
+ let prompt = ctx.session.prompts?.[ctx.cmdExt?.cmd] || (
5
+ ctx._.prompts[ctx.cmdExt?.cmd] && ctx._.prompts[ctx.cmdExt.cmd].prompt
6
+ );
7
+ if (prompt) {
8
+ ctx.clear();
9
+ ctx.overwrite = ctx.text = prompt;
10
+ }
11
+ await next();
12
+ };
13
+
14
+ export const { run, priority, func, help, cmds } = {
15
+ run: true,
16
+ priority: 40,
17
+ func: action,
18
+ };
@@ -0,0 +1,81 @@
1
+ import { bot, utilitas } from 'utilitas';
2
+
3
+ const ACP = '[🧠 Awesome ChatGPT Prompts](https://github.com/f/awesome-chatgpt-prompts)';
4
+
5
+ const action = async (ctx, next) => {
6
+ ctx.session.prompts || (ctx.session.prompts = {});
7
+ switch (ctx.cmd.cmd) {
8
+ case 'prompts':
9
+ const prompts = bot.lines2(Object.keys(ctx.session.prompts || {}).map(
10
+ x => bot.lines([`- /${x}`, ctx.session.prompts[x]])
11
+ ));
12
+ await ctx.paging(prompts || 'No custom prompts.');
13
+ break;
14
+ case 'add':
15
+ const arrText = (ctx.cmd.args || '').split('\n');
16
+ const subArrText = arrText[0].split('>');
17
+ const cmd = utilitas.ensureString(
18
+ subArrText[0], { case: 'SNAKE' }
19
+ ).slice(0, bot.MAX_MENU_LENGTH);
20
+ const prompt = bot.lines([subArrText.slice(1).join(' '), ...arrText.slice(1)]).trim();
21
+ if (cmd && prompt) {
22
+ ctx.session.prompts[cmd] = prompt;
23
+ await ctx.ok(`Prompt added: /${cmd}`);
24
+ } else {
25
+ await ctx.ok('Invalid command or prompt.');
26
+ }
27
+ break;
28
+ case 'del':
29
+ if (ctx.session.prompts[ctx.cmd.args]) {
30
+ delete ctx.session.prompts[ctx.cmd.args];
31
+ await ctx.complete();
32
+ } else {
33
+ await ctx.ok('Prompt not found.');
34
+ }
35
+ break;
36
+ case 'acplist':
37
+ const list = bot.uList(Object.keys(ctx._.prompts || {}).map(
38
+ x => `/${ctx._.prompts[x].command}: ${ctx._.prompts[x].act}`
39
+ ));
40
+ await ctx.paging(list || 'Data not found.');
41
+ break;
42
+ case 'acpdetail':
43
+ const details = bot.lines2(Object.keys(ctx._.prompts || {}).map(
44
+ x => bot.lines([
45
+ `- /${ctx._.prompts[x].command}: ${ctx._.prompts[x].act}`,
46
+ ctx._.prompts[x].prompt
47
+ ])
48
+ ));
49
+ await ctx.paging(details || 'Data not found.');
50
+ break;
51
+ case 'clear':
52
+ ctx.clear();
53
+ await next();
54
+ break;
55
+ }
56
+ };
57
+
58
+ export const { run, priority, func, help, cmds } = {
59
+ run: true,
60
+ priority: 30,
61
+ func: action,
62
+ help: bot.lines([
63
+ 'Maintain custom prompts.',
64
+ `Get prompts from ${ACP}.`,
65
+ `Tips: Use /clear to end current prompted AI conversation session.`,
66
+ 'Example 1: /prompts',
67
+ 'Example 2: /add code > Code with me.',
68
+ 'Example 3: /del code',
69
+ 'Example 4: `/acplist`',
70
+ 'Example 5: `/acpdetail`',
71
+ 'Example 6: `/clear`',
72
+ ]),
73
+ cmds: {
74
+ prompts: 'List custom prompts.',
75
+ add: 'Add or update a custom prompt: `/add [COMMAND] > [PROMPT]`.',
76
+ del: 'Delete a custom prompt: `/del [COMMAND]`.',
77
+ acplist: `List prompts from ${ACP}.`,
78
+ acpdetail: `Show details of ${ACP}.`,
79
+ clear: 'Clear current AI conversation session and start a new one.',
80
+ },
81
+ };
@@ -1,3 +1,5 @@
1
+ // @todo: This is a temporary solution, and will be replaced by a more elegant solution in the future.
2
+
1
3
  const getTranslatePrompt = (lang) => // https://github.com/yetone/bob-plugin-openai-translator/blob/main/src/main.js
2
4
  'You are a translation engine that can only translate text and cannot interpret it.'
3
5
  + ` Translate the following text into ${lang}: `;
@@ -6,25 +8,24 @@ const getPolishPrompt = () => // https://github.com/yetone/bob-plugin-openai-pol
6
8
  + ' Please note that you need to list the changes and briefly explain why: ';
7
9
 
8
10
  const action = async (ctx, next) => {
9
- if (!ctx.text) { return await next(); }
10
- switch (ctx.cmd) {
11
+ switch (ctx.cmdExt?.cmd) {
11
12
  case '2en':
12
- ctx.text = ctx.text.replace(`/${ctx.cmd}`, getTranslatePrompt('English'));
13
+ ctx.text = ctx.text.replace(`/${ctx.cmdExt?.cmd}`, getTranslatePrompt('English'));
13
14
  break;
14
15
  case '2zh':
15
- ctx.text = ctx.text.replace(`/${ctx.cmd}`, getTranslatePrompt('Traditional Chinese'));
16
+ ctx.text = ctx.text.replace(`/${ctx.cmdExt?.cmd}`, getTranslatePrompt('Traditional Chinese'));
16
17
  break;
17
18
  case '2zht':
18
- ctx.text = ctx.text.replace(`/${ctx.cmd}`, getTranslatePrompt('Traditional Chinese'));
19
+ ctx.text = ctx.text.replace(`/${ctx.cmdExt?.cmd}`, getTranslatePrompt('Traditional Chinese'));
19
20
  break;
20
21
  case '2zhs':
21
- ctx.text = ctx.text.replace(`/${ctx.cmd}`, getTranslatePrompt('Simplified Chinese'));
22
+ ctx.text = ctx.text.replace(`/${ctx.cmdExt?.cmd}`, getTranslatePrompt('Simplified Chinese'));
22
23
  break;
23
24
  case '2fr':
24
- ctx.text = ctx.text.replace(`/${ctx.cmd}`, getTranslatePrompt('French'));
25
+ ctx.text = ctx.text.replace(`/${ctx.cmdExt?.cmd}`, getTranslatePrompt('French'));
25
26
  break;
26
27
  case 'p':
27
- ctx.text = ctx.text.replace(`/${ctx.cmd}`, getPolishPrompt());
28
+ ctx.text = ctx.text.replace(`/${ctx.cmdExt?.cmd}`, getPolishPrompt());
28
29
  break;
29
30
  }
30
31
  await next();
@@ -32,7 +33,6 @@ const action = async (ctx, next) => {
32
33
 
33
34
  export const { run, priority, func } = {
34
35
  run: true,
35
- priority: 20,
36
- name: 'ai',
36
+ priority: 50,
37
37
  func: action,
38
38
  };
package/skills/ai.mjs DELETED
@@ -1,73 +0,0 @@
1
- import { utilitas } from 'utilitas';
2
-
3
- const onProgress = { onProgress: true };
4
- const [YOU, BOT, LN2] = ['😸 You:', '🤖️ ', '\n\n'];
5
- const [joinL1, joinL2] = [a => a.join(`${LN2}---${LN2}`), a => a.join(LN2)];
6
- const enrich = name => name === 'Bing' ? `${name} (Sydney)` : name;
7
-
8
- const action = async (ctx, next) => {
9
- if (!ctx.text) { return await next(); }
10
- const [multiAi, msgs, tts, pms, extra]
11
- = [ctx.session.ai.size > 1, {}, {}, [], {}];
12
- let [lastMsg, lastSent] = ['', 0];
13
- const packMsg = options => {
14
- const packed = [...ctx.overwrite ? [joinL2([YOU, ctx.overwrite])] : []];
15
- const source = options?.tts ? tts : msgs;
16
- for (let name of ctx.session.ai.size ? ctx.session.ai : [ctx.firstAi]) {
17
- const defaultAi = name === ctx.firstAi;
18
- packed.push(joinL2([
19
- ...multiAi || !defaultAi || ctx.overwrite ? [`${BOT}${enrich(name)}:`] : [],
20
- options?.onProgress ? (
21
- source[name] ? `${source[name].trim()} █` : '💬'
22
- ) : (source[name] || ''),
23
- ]));
24
- }
25
- console.log(packed);
26
- return joinL1(packed);
27
- };
28
- const ok = async options => {
29
- const [curTime, curMsg] = [Date.now(), packMsg(options)];
30
- if (options?.onProgress && (
31
- curTime - lastSent < ctx.limit || lastMsg === curMsg
32
- )) { return; }
33
- [lastSent, lastMsg] = [curTime, curMsg];
34
- return await ctx.ok(curMsg, { ...options || {}, ...extra });
35
- };
36
- await ok(onProgress);
37
- for (let name of ctx.session.ai.size ? ctx.session.ai : [ctx.firstAi]) {
38
- if (utilitas.insensitiveCompare('/clear', ctx.text)) {
39
- ctx._.ai[name].clear(ctx.chatId);
40
- ctx.text = ctx._.hello;
41
- }
42
- pms.push((async () => {
43
- try {
44
- const resp = await ctx._.ai[name].send(
45
- ctx.text, { session: ctx.chatId }, tkn => {
46
- msgs[name] = `${(msgs[name] || '')}${tkn}`;
47
- ok(onProgress);
48
- }
49
- );
50
- msgs[name] = ctx.session.raw ? resp.response : resp.responseRendered;
51
- tts[name] = resp.spokenText;
52
- extra.textButtons = resp?.suggestedResponses?.map?.(text => ({
53
- text, data: `/bing ${text}`
54
- }));
55
- } catch (err) {
56
- msgs[name] = `[ERROR] ${err?.message || err}`;
57
- tts[name] = msgs[name];
58
- utilitas.log(err);
59
- }
60
- })());
61
- }
62
- await Promise.all(pms);
63
- await ok();
64
- ctx.tts = packMsg({ tts: true });
65
- await next();
66
- };
67
-
68
- export const { run, priority, func } = {
69
- run: true,
70
- priority: 30,
71
- name: 'ai',
72
- func: action,
73
- };
package/skills/cmd.mjs DELETED
@@ -1,32 +0,0 @@
1
- import { utilitas } from 'utilitas';
2
-
3
- const matchReg = /^\/([^\ ]*)(.*)$/ig;
4
-
5
- const action = async (ctx, next) => {
6
- if (!ctx.text) { return await next(); }
7
- ctx.session.ai || (ctx.session.ai = new Set());
8
- const curAi = new Set();
9
- ctx.cmd = ctx.text.split('\n')?.[0]?.replace(matchReg, '$1');
10
- let catched;
11
- switch (ctx.cmd) {
12
- case 'raw': ctx.session.raw = true; catched = true; break;
13
- case 'render': ctx.session.raw = false; catched = true; break;
14
- }
15
- for (let name in ctx._.ai) {
16
- ctx.firstAi || (ctx.firstAi = name);
17
- if (utilitas.insensitiveCompare(ctx.cmd, name) || ctx.cmd === '*') {
18
- curAi.add(name);
19
- catched = true;
20
- }
21
- }
22
- curAi.size && (ctx.session.ai = curAi);
23
- catched && (ctx.text = ctx.text.replace(matchReg, '$2').trim() || ctx._.hello);
24
- await next();
25
- };
26
-
27
- export const { run, priority, func } = {
28
- run: true,
29
- priority: 10,
30
- name: 'ai',
31
- func: action,
32
- };