halbot 1989.6.37 → 1990.1.2
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 +28 -1
- package/bin/halbot.mjs +24 -22
- package/index.mjs +28 -21
- package/package.json +7 -5
- package/skills/ai-chat.mjs +64 -0
- package/skills/ai-command.mjs +40 -0
- package/skills/ai-select.mjs +57 -0
- package/skills/execute.mjs +18 -0
- package/skills/prompt.mjs +81 -0
- package/skills/translate.mjs +10 -10
- package/skills/ai.mjs +0 -73
- package/skills/cmd.mjs +0 -32
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
59
|
+
provider: 'telegram',
|
|
60
|
+
session: options?.session,
|
|
51
61
|
skillPath: options?.skillPath || skillPath,
|
|
52
62
|
speech: _speech,
|
|
53
63
|
});
|
|
54
|
-
|
|
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": "
|
|
3
|
+
"description": "Just another ChatGPT/Bing Chat Telegram bob, which is simple design, easy to use, extendable and fun.",
|
|
4
|
+
"version": "1990.1.2",
|
|
5
5
|
"private": false,
|
|
6
6
|
"homepage": "https://github.com/Leask/halbot",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"engines": {
|
|
9
|
-
"node": ">=
|
|
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.
|
|
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.
|
|
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
|
+
};
|
package/skills/translate.mjs
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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
|
-
};
|