@yaebal/toml 0.0.1
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/LICENSE +21 -0
- package/README.md +92 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -0
- package/lib/index.test.d.ts +2 -0
- package/lib/index.test.d.ts.map +1 -0
- package/lib/index.test.js +100 -0
- package/lib/index.test.js.map +1 -0
- package/lib/install.d.ts +9 -0
- package/lib/install.d.ts.map +1 -0
- package/lib/install.js +85 -0
- package/lib/install.js.map +1 -0
- package/lib/parse.d.ts +4 -0
- package/lib/parse.d.ts.map +1 -0
- package/lib/parse.js +41 -0
- package/lib/parse.js.map +1 -0
- package/lib/schema.d.ts +33 -0
- package/lib/schema.d.ts.map +1 -0
- package/lib/schema.js +101 -0
- package/lib/schema.js.map +1 -0
- package/lib/types.d.ts +37 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/package.json +52 -0
- package/src/index.test.ts +137 -0
- package/src/index.ts +4 -0
- package/src/install.ts +146 -0
- package/src/parse.ts +44 -0
- package/src/schema.ts +118 -0
- package/src/types.ts +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 neverlane
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# @yaebal/toml
|
|
2
|
+
|
|
3
|
+
declarative toml routes for yaebal bots.
|
|
4
|
+
|
|
5
|
+
status: experimental.
|
|
6
|
+
|
|
7
|
+
## installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add @yaebal/toml
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## what it is
|
|
14
|
+
|
|
15
|
+
`@yaebal/toml` lets small bots describe commands, text routes, message filters, callback queries, and simple replies in toml. typescript stays available for real logic through a handler registry.
|
|
16
|
+
|
|
17
|
+
## bot.toml
|
|
18
|
+
|
|
19
|
+
```toml
|
|
20
|
+
[bot]
|
|
21
|
+
name = "demo"
|
|
22
|
+
|
|
23
|
+
[[commands]]
|
|
24
|
+
name = "start"
|
|
25
|
+
description = "start command"
|
|
26
|
+
reply = "привет! я бот из toml."
|
|
27
|
+
|
|
28
|
+
[[commands]]
|
|
29
|
+
name = "ping"
|
|
30
|
+
handler = "ping"
|
|
31
|
+
|
|
32
|
+
[[hears]]
|
|
33
|
+
text = "ping"
|
|
34
|
+
reply = "pong"
|
|
35
|
+
|
|
36
|
+
[[messages]]
|
|
37
|
+
on = "message:text"
|
|
38
|
+
contains = "yaebal"
|
|
39
|
+
reply = "yaebal мощь"
|
|
40
|
+
|
|
41
|
+
[[callbacks]]
|
|
42
|
+
data = "profile"
|
|
43
|
+
handler = "profileCallback"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## index.ts
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { Bot } from "@yaebal/core";
|
|
50
|
+
import { installToml } from "@yaebal/toml";
|
|
51
|
+
|
|
52
|
+
const bot = new Bot(process.env.BOT_TOKEN!);
|
|
53
|
+
|
|
54
|
+
installToml(bot, "./bot.toml", {
|
|
55
|
+
handlers: {
|
|
56
|
+
ping: async (ctx) => {
|
|
57
|
+
await ctx.reply("pong from typescript");
|
|
58
|
+
},
|
|
59
|
+
profileCallback: async (ctx) => {
|
|
60
|
+
await ctx.reply("profile");
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await bot.start();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## handler registry
|
|
69
|
+
|
|
70
|
+
when a route uses `handler = "name"`, the name must exist in `options.handlers`. if a handler is missing, install fails before routes are registered:
|
|
71
|
+
|
|
72
|
+
```txt
|
|
73
|
+
Missing handler "ping" referenced in commands[1]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
if both `handler` and `reply` are present, the handler wins. the reply is only used when no handler is configured.
|
|
77
|
+
|
|
78
|
+
## plugin usage
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { createTomlPlugin } from "@yaebal/toml";
|
|
82
|
+
|
|
83
|
+
bot.install(createTomlPlugin("./bot.toml", { handlers }));
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## limitations
|
|
87
|
+
|
|
88
|
+
toml does not replace typescript logic. it only describes routes and simple replies. use the handler registry for validation, database calls, external services, branching flows, and anything that needs compile-time types.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
part of [**yaebal**](https://github.com/neverlane/yaebal) — a type-safe, runtime-agnostic telegram bot api framework. mit.
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createTomlPlugin, installToml } from "./install.js";
|
|
2
|
+
export { parseTomlConfig } from "./parse.js";
|
|
3
|
+
export { tomlBotConfigSchema, validateTomlConfig } from "./schema.js";
|
|
4
|
+
export type { InstallTomlOptions, TomlBotConfig, TomlHandler, TomlHandlers } from "./types.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtE,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
package/lib/index.js
ADDED
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import { Composer } from "@yaebal/core";
|
|
7
|
+
import { callbackUpdate, createContext, messageUpdate, mockApi, runMiddleware } from "@yaebal/test";
|
|
8
|
+
import { installToml, parseTomlConfig, validateTomlConfig } from "./index.js";
|
|
9
|
+
test("parse raw toml string", () => {
|
|
10
|
+
const config = parseTomlConfig(`
|
|
11
|
+
[[commands]]
|
|
12
|
+
name = "start"
|
|
13
|
+
reply = "hi"
|
|
14
|
+
`);
|
|
15
|
+
assert.deepEqual(config.commands, [{ name: "start", reply: "hi" }]);
|
|
16
|
+
});
|
|
17
|
+
test("parse toml file path", async () => {
|
|
18
|
+
const dir = await mkdtemp(join(tmpdir(), "yaebal-toml-"));
|
|
19
|
+
const file = join(dir, "bot.toml");
|
|
20
|
+
try {
|
|
21
|
+
await writeFile(file, `[[hears]]\ntext = "ping"\nreply = "pong"\n`);
|
|
22
|
+
const config = parseTomlConfig(file);
|
|
23
|
+
assert.deepEqual(config.hears, [{ text: "ping", reply: "pong" }]);
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
await rm(dir, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
test("validate minimal empty config", () => {
|
|
30
|
+
assert.deepEqual(validateTomlConfig({}), {});
|
|
31
|
+
});
|
|
32
|
+
test("validate command with reply", () => {
|
|
33
|
+
assert.deepEqual(validateTomlConfig({ commands: [{ name: "start", reply: "hi" }] }), {
|
|
34
|
+
commands: [{ name: "start", reply: "hi" }],
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
test("validate command with missing reply or handler should fail", () => {
|
|
38
|
+
assert.throws(() => validateTomlConfig({ commands: [{ name: "start" }] }), /commands\[0\] must define either reply or handler/);
|
|
39
|
+
});
|
|
40
|
+
test("validate message with missing on should fail", () => {
|
|
41
|
+
assert.throws(() => validateTomlConfig({ messages: [{ reply: "hi" }] }), /messages\[0\]\.on is required/);
|
|
42
|
+
});
|
|
43
|
+
test("install registers command reply", async () => {
|
|
44
|
+
const composer = new Composer();
|
|
45
|
+
const { api, calls } = mockApi();
|
|
46
|
+
installToml(composer, { commands: [{ name: "start", reply: "hi" }] });
|
|
47
|
+
await runMiddleware(composer, createContext(messageUpdate({ text: "/start" }), api));
|
|
48
|
+
assert.deepEqual(calls, [
|
|
49
|
+
{
|
|
50
|
+
method: "sendMessage",
|
|
51
|
+
params: { chat_id: 1, text: "hi", reply_parameters: { message_id: 1 } },
|
|
52
|
+
},
|
|
53
|
+
]);
|
|
54
|
+
});
|
|
55
|
+
test("install throws on missing named handler", () => {
|
|
56
|
+
assert.throws(() => installToml(new Composer(), { commands: [{ name: "ping", handler: "ping" }] }), /Missing handler "ping" referenced in commands\[0\]/);
|
|
57
|
+
});
|
|
58
|
+
test("messages contains filter works", async () => {
|
|
59
|
+
const composer = new Composer();
|
|
60
|
+
const { api, calls } = mockApi();
|
|
61
|
+
installToml(composer, {
|
|
62
|
+
messages: [{ on: "message:text", contains: "yaebal", reply: "match" }],
|
|
63
|
+
});
|
|
64
|
+
await runMiddleware(composer, createContext(messageUpdate({ text: "hello" }), api));
|
|
65
|
+
await runMiddleware(composer, createContext(messageUpdate({ text: "hello yaebal" }), api));
|
|
66
|
+
assert.equal(calls.length, 1);
|
|
67
|
+
assert.equal(calls[0]?.params?.text, "match");
|
|
68
|
+
});
|
|
69
|
+
test("messages equals filter works", async () => {
|
|
70
|
+
const composer = new Composer();
|
|
71
|
+
const { api, calls } = mockApi();
|
|
72
|
+
installToml(composer, {
|
|
73
|
+
messages: [{ on: "message:text", equals: "secret", reply: "match" }],
|
|
74
|
+
});
|
|
75
|
+
await runMiddleware(composer, createContext(messageUpdate({ text: "secret!" }), api));
|
|
76
|
+
await runMiddleware(composer, createContext(messageUpdate({ text: "secret" }), api));
|
|
77
|
+
assert.equal(calls.length, 1);
|
|
78
|
+
assert.equal(calls[0]?.params?.text, "match");
|
|
79
|
+
});
|
|
80
|
+
test("callbacks with handler works", async () => {
|
|
81
|
+
const composer = new Composer();
|
|
82
|
+
const { api, calls } = mockApi();
|
|
83
|
+
installToml(composer, {
|
|
84
|
+
callbacks: [{ data: "profile", handler: "profileCallback" }],
|
|
85
|
+
}, {
|
|
86
|
+
handlers: {
|
|
87
|
+
profileCallback: async (ctx) => {
|
|
88
|
+
await ctx.reply("profile");
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
await runMiddleware(composer, createContext(callbackUpdate({ data: "profile" }), api));
|
|
93
|
+
assert.deepEqual(calls, [
|
|
94
|
+
{
|
|
95
|
+
method: "sendMessage",
|
|
96
|
+
params: { chat_id: 1, text: "profile" },
|
|
97
|
+
},
|
|
98
|
+
]);
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACpG,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9E,IAAI,CAAC,uBAAuB,EAAE,GAAG,EAAE;IAClC,MAAM,MAAM,GAAG,eAAe,CAAC;;;;CAI/B,CAAC,CAAC;IAEF,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;IACvC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAEnC,IAAI,CAAC;QACJ,MAAM,SAAS,CAAC,IAAI,EAAE,4CAA4C,CAAC,CAAC;QAEpE,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QAErC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACnE,CAAC;YAAS,CAAC;QACV,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC1C,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE;IACxC,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE;QACpF,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;KAC1C,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4DAA4D,EAAE,GAAG,EAAE;IACvE,MAAM,CAAC,MAAM,CACZ,GAAG,EAAE,CAAC,kBAAkB,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAC3D,mDAAmD,CACnD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACzD,MAAM,CAAC,MAAM,CACZ,GAAG,EAAE,CAAC,kBAAkB,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EACzD,+BAA+B,CAC/B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;IAClD,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC;IAEjC,WAAW,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IACtE,MAAM,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAErF,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE;QACvB;YACC,MAAM,EAAE,aAAa;YACrB,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE;SACvE;KACD,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACpD,MAAM,CAAC,MAAM,CACZ,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EACpF,oDAAoD,CACpD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;IACjD,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC;IAEjC,WAAW,CAAC,QAAQ,EAAE;QACrB,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;KACtE,CAAC,CAAC;IAEH,MAAM,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACpF,MAAM,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAE3F,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;IAC/C,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC;IAEjC,WAAW,CAAC,QAAQ,EAAE;QACrB,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;KACpE,CAAC,CAAC;IAEH,MAAM,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACtF,MAAM,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAErF,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;IAC/C,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC;IAEjC,WAAW,CACV,QAAQ,EACR;QACC,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;KAC5D,EACD;QACC,QAAQ,EAAE;YACT,eAAe,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBAC9B,MAAM,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAC5B,CAAC;SACD;KACD,CACD,CAAC;IAEF,MAAM,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAEvF,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE;QACvB;YACC,MAAM,EAAE,aAAa;YACrB,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE;SACvC;KACD,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
package/lib/install.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Composer, Context, Plugin } from "@yaebal/core";
|
|
2
|
+
import type { InstallTomlOptions, TomlConfigInput } from "./types.js";
|
|
3
|
+
type EmptyPluginOutput = Record<never, never>;
|
|
4
|
+
/** install toml routes on an existing bot or composer and return the same instance. */
|
|
5
|
+
export declare function installToml<C extends Context, T extends Composer<C>>(target: T, configPathOrObject: TomlConfigInput, options?: InstallTomlOptions<C>): T;
|
|
6
|
+
/** create a yaebal plugin that installs toml routes when mounted. */
|
|
7
|
+
export declare function createTomlPlugin<C extends Context = Context>(configPathOrObject: TomlConfigInput, options?: InstallTomlOptions<C>): Plugin<C, EmptyPluginOutput>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,QAAQ,EACR,OAAO,EAGP,MAAM,EACN,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EACX,kBAAkB,EAElB,eAAe,EAEf,MAAM,YAAY,CAAC;AAEpB,KAAK,iBAAiB,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AA2E9C,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,EACnE,MAAM,EAAE,CAAC,EACT,kBAAkB,EAAE,eAAe,EACnC,OAAO,GAAE,kBAAkB,CAAC,CAAC,CAAM,GACjC,CAAC,CAwCH;AAED,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAC3D,kBAAkB,EAAE,eAAe,EACnC,OAAO,GAAE,kBAAkB,CAAC,CAAC,CAAM,GACjC,MAAM,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAG9B"}
|
package/lib/install.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { parseTomlConfig } from "./parse.js";
|
|
2
|
+
function replyHandler(text) {
|
|
3
|
+
return async (ctx) => {
|
|
4
|
+
await ctx.reply(text);
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
function missingResponseError(kind, index) {
|
|
8
|
+
return new Error(`${kind}[${index}] must define either reply or handler`);
|
|
9
|
+
}
|
|
10
|
+
function resolveHandler(route, kind, index, options) {
|
|
11
|
+
if (route.handler !== undefined) {
|
|
12
|
+
const handler = options.handlers?.[route.handler];
|
|
13
|
+
if (!handler)
|
|
14
|
+
throw new Error(`Missing handler "${route.handler}" referenced in ${kind}[${index}]`);
|
|
15
|
+
return handler;
|
|
16
|
+
}
|
|
17
|
+
if (route.reply !== undefined)
|
|
18
|
+
return replyHandler(route.reply);
|
|
19
|
+
throw missingResponseError(kind, index);
|
|
20
|
+
}
|
|
21
|
+
function assertHandlersExist(config, options) {
|
|
22
|
+
for (const [index, route] of (config.commands ?? []).entries()) {
|
|
23
|
+
if (route.handler !== undefined && !options.handlers?.[route.handler]) {
|
|
24
|
+
throw new Error(`Missing handler "${route.handler}" referenced in commands[${index}]`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
for (const [index, route] of (config.hears ?? []).entries()) {
|
|
28
|
+
if (route.handler !== undefined && !options.handlers?.[route.handler]) {
|
|
29
|
+
throw new Error(`Missing handler "${route.handler}" referenced in hears[${index}]`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
for (const [index, route] of (config.messages ?? []).entries()) {
|
|
33
|
+
if (route.handler !== undefined && !options.handlers?.[route.handler]) {
|
|
34
|
+
throw new Error(`Missing handler "${route.handler}" referenced in messages[${index}]`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
for (const [index, route] of (config.callbacks ?? []).entries()) {
|
|
38
|
+
if (route.handler !== undefined && !options.handlers?.[route.handler]) {
|
|
39
|
+
throw new Error(`Missing handler "${route.handler}" referenced in callbacks[${index}]`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function matchesMessageFilters(ctx, filters) {
|
|
44
|
+
if (filters.contains === undefined && filters.equals === undefined)
|
|
45
|
+
return true;
|
|
46
|
+
const text = ctx.text;
|
|
47
|
+
if (text === undefined)
|
|
48
|
+
return false;
|
|
49
|
+
if (filters.equals !== undefined && text !== filters.equals)
|
|
50
|
+
return false;
|
|
51
|
+
if (filters.contains !== undefined && !text.includes(filters.contains))
|
|
52
|
+
return false;
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
/** install toml routes on an existing bot or composer and return the same instance. */
|
|
56
|
+
export function installToml(target, configPathOrObject, options = {}) {
|
|
57
|
+
const config = parseTomlConfig(configPathOrObject);
|
|
58
|
+
assertHandlersExist(config, options);
|
|
59
|
+
for (const [index, route] of (config.commands ?? []).entries()) {
|
|
60
|
+
const handler = resolveHandler(route, "commands", index, options);
|
|
61
|
+
target.command(route.name, handler);
|
|
62
|
+
}
|
|
63
|
+
for (const [index, route] of (config.hears ?? []).entries()) {
|
|
64
|
+
const handler = resolveHandler(route, "hears", index, options);
|
|
65
|
+
target.hears(route.text, handler);
|
|
66
|
+
}
|
|
67
|
+
for (const [index, route] of (config.messages ?? []).entries()) {
|
|
68
|
+
const handler = resolveHandler(route, "messages", index, options);
|
|
69
|
+
target.on(route.on, async (ctx, next) => {
|
|
70
|
+
if (!matchesMessageFilters(ctx, route))
|
|
71
|
+
return next();
|
|
72
|
+
return handler(ctx, next);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
for (const [index, route] of (config.callbacks ?? []).entries()) {
|
|
76
|
+
const handler = resolveHandler(route, "callbacks", index, options);
|
|
77
|
+
target.callbackQuery(route.data, handler);
|
|
78
|
+
}
|
|
79
|
+
return target;
|
|
80
|
+
}
|
|
81
|
+
/** create a yaebal plugin that installs toml routes when mounted. */
|
|
82
|
+
export function createTomlPlugin(configPathOrObject, options = {}) {
|
|
83
|
+
return (composer) => installToml(composer, configPathOrObject, options);
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
|
package/lib/parse.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA0BjE,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,aAAa,CAarE"}
|
package/lib/parse.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { parse } from "smol-toml";
|
|
3
|
+
import { validateTomlConfig } from "./schema.js";
|
|
4
|
+
function errorMessage(error) {
|
|
5
|
+
return error instanceof Error ? error.message : String(error);
|
|
6
|
+
}
|
|
7
|
+
function isPathLike(input) {
|
|
8
|
+
const trimmed = input.trim();
|
|
9
|
+
if (trimmed === "")
|
|
10
|
+
return false;
|
|
11
|
+
if (trimmed.includes("\n") || trimmed.includes("=") || trimmed.startsWith("["))
|
|
12
|
+
return false;
|
|
13
|
+
if (existsSync(input))
|
|
14
|
+
return true;
|
|
15
|
+
return trimmed.endsWith(".toml") || trimmed.endsWith(".tml");
|
|
16
|
+
}
|
|
17
|
+
function loadTomlSource(input) {
|
|
18
|
+
if (!isPathLike(input))
|
|
19
|
+
return input;
|
|
20
|
+
try {
|
|
21
|
+
return readFileSync(input, "utf8");
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new Error(`failed to read toml config file "${input}": ${errorMessage(error)}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** parse a toml config from a file path, raw toml string, or parsed object. */
|
|
28
|
+
export function parseTomlConfig(input) {
|
|
29
|
+
if (typeof input !== "string")
|
|
30
|
+
return validateTomlConfig(input);
|
|
31
|
+
const source = loadTomlSource(input);
|
|
32
|
+
let parsed;
|
|
33
|
+
try {
|
|
34
|
+
parsed = parse(source);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new Error(`failed to parse toml config: ${errorMessage(error)}`);
|
|
38
|
+
}
|
|
39
|
+
return validateTomlConfig(parsed);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=parse.js.map
|
package/lib/parse.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,SAAS,YAAY,CAAC,KAAc;IACnC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAChC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAE7B,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7F,IAAI,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnC,OAAO,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACpC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAErC,IAAI,CAAC;QACJ,OAAO,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,oCAAoC,KAAK,MAAM,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC;AACF,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,eAAe,CAAC,KAAsB;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAEhE,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,MAAe,CAAC;IAEpB,IAAI,CAAC;QACJ,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,gCAAgC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC"}
|
package/lib/schema.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { TomlBotConfig } from "./types.js";
|
|
3
|
+
export declare const tomlBotConfigSchema: z.ZodObject<{
|
|
4
|
+
bot: z.ZodOptional<z.ZodObject<{
|
|
5
|
+
name: z.ZodOptional<z.ZodString>;
|
|
6
|
+
}, z.core.$strip>>;
|
|
7
|
+
commands: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
8
|
+
reply: z.ZodOptional<z.ZodString>;
|
|
9
|
+
handler: z.ZodOptional<z.ZodString>;
|
|
10
|
+
name: z.ZodString;
|
|
11
|
+
description: z.ZodOptional<z.ZodString>;
|
|
12
|
+
}, z.core.$strip>>>;
|
|
13
|
+
hears: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
14
|
+
reply: z.ZodOptional<z.ZodString>;
|
|
15
|
+
handler: z.ZodOptional<z.ZodString>;
|
|
16
|
+
text: z.ZodString;
|
|
17
|
+
}, z.core.$strip>>>;
|
|
18
|
+
messages: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
19
|
+
reply: z.ZodOptional<z.ZodString>;
|
|
20
|
+
handler: z.ZodOptional<z.ZodString>;
|
|
21
|
+
on: z.ZodString;
|
|
22
|
+
contains: z.ZodOptional<z.ZodString>;
|
|
23
|
+
equals: z.ZodOptional<z.ZodString>;
|
|
24
|
+
}, z.core.$strip>>>;
|
|
25
|
+
callbacks: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
26
|
+
reply: z.ZodOptional<z.ZodString>;
|
|
27
|
+
handler: z.ZodOptional<z.ZodString>;
|
|
28
|
+
data: z.ZodString;
|
|
29
|
+
}, z.core.$strip>>>;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
/** validate an already parsed toml bot config and return the normalized object. */
|
|
32
|
+
export declare function validateTomlConfig(input: unknown): TomlBotConfig;
|
|
33
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA6DhD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAU9B,CAAC;AAsCH,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAMhE"}
|
package/lib/schema.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const requiredString = z.string().min(1);
|
|
3
|
+
const optionalString = z.string().optional();
|
|
4
|
+
const responseShape = {
|
|
5
|
+
reply: optionalString,
|
|
6
|
+
handler: requiredString.optional(),
|
|
7
|
+
};
|
|
8
|
+
function hasResponse(route) {
|
|
9
|
+
return route.reply !== undefined || route.handler !== undefined;
|
|
10
|
+
}
|
|
11
|
+
const commandSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
name: requiredString,
|
|
14
|
+
description: optionalString,
|
|
15
|
+
...responseShape,
|
|
16
|
+
})
|
|
17
|
+
.superRefine((route, ctx) => {
|
|
18
|
+
if (!hasResponse(route)) {
|
|
19
|
+
ctx.addIssue({ code: "custom", message: "must define either reply or handler" });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const hearSchema = z
|
|
23
|
+
.object({
|
|
24
|
+
text: requiredString,
|
|
25
|
+
...responseShape,
|
|
26
|
+
})
|
|
27
|
+
.superRefine((route, ctx) => {
|
|
28
|
+
if (!hasResponse(route)) {
|
|
29
|
+
ctx.addIssue({ code: "custom", message: "must define either reply or handler" });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
const messageSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
on: requiredString,
|
|
35
|
+
contains: optionalString,
|
|
36
|
+
equals: optionalString,
|
|
37
|
+
...responseShape,
|
|
38
|
+
})
|
|
39
|
+
.superRefine((route, ctx) => {
|
|
40
|
+
if (!hasResponse(route)) {
|
|
41
|
+
ctx.addIssue({ code: "custom", message: "must define either reply or handler" });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const callbackSchema = z
|
|
45
|
+
.object({
|
|
46
|
+
data: requiredString,
|
|
47
|
+
...responseShape,
|
|
48
|
+
})
|
|
49
|
+
.superRefine((route, ctx) => {
|
|
50
|
+
if (!hasResponse(route)) {
|
|
51
|
+
ctx.addIssue({ code: "custom", message: "must define either reply or handler" });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
export const tomlBotConfigSchema = z.object({
|
|
55
|
+
bot: z
|
|
56
|
+
.object({
|
|
57
|
+
name: optionalString,
|
|
58
|
+
})
|
|
59
|
+
.optional(),
|
|
60
|
+
commands: z.array(commandSchema).optional(),
|
|
61
|
+
hears: z.array(hearSchema).optional(),
|
|
62
|
+
messages: z.array(messageSchema).optional(),
|
|
63
|
+
callbacks: z.array(callbackSchema).optional(),
|
|
64
|
+
});
|
|
65
|
+
function formatPath(path) {
|
|
66
|
+
let out = "";
|
|
67
|
+
for (const segment of path) {
|
|
68
|
+
if (typeof segment === "number") {
|
|
69
|
+
out += `[${segment}]`;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
out += out ? `.${String(segment)}` : String(segment);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return out || "config";
|
|
76
|
+
}
|
|
77
|
+
function isMissingRequired(issue) {
|
|
78
|
+
const details = issue;
|
|
79
|
+
const message = issue.message.toLowerCase();
|
|
80
|
+
return (issue.code === "invalid_type" &&
|
|
81
|
+
(details.input === undefined ||
|
|
82
|
+
details.received === "undefined" ||
|
|
83
|
+
message.includes("required") ||
|
|
84
|
+
message.includes("undefined")));
|
|
85
|
+
}
|
|
86
|
+
function formatIssue(issue) {
|
|
87
|
+
const path = formatPath(issue.path);
|
|
88
|
+
if (issue.code === "custom")
|
|
89
|
+
return `${path} ${issue.message}`;
|
|
90
|
+
if (isMissingRequired(issue))
|
|
91
|
+
return `${path} is required`;
|
|
92
|
+
return `${path}: ${issue.message}`;
|
|
93
|
+
}
|
|
94
|
+
/** validate an already parsed toml bot config and return the normalized object. */
|
|
95
|
+
export function validateTomlConfig(input) {
|
|
96
|
+
const result = tomlBotConfigSchema.safeParse(input);
|
|
97
|
+
if (result.success)
|
|
98
|
+
return result.data;
|
|
99
|
+
throw new Error(result.error.issues.map(formatIssue).join("; "));
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Context, Middleware } from "@yaebal/core";
|
|
2
|
+
export interface TomlBotConfig {
|
|
3
|
+
bot?: {
|
|
4
|
+
name?: string;
|
|
5
|
+
};
|
|
6
|
+
commands?: TomlCommandRoute[];
|
|
7
|
+
hears?: TomlHearRoute[];
|
|
8
|
+
messages?: TomlMessageRoute[];
|
|
9
|
+
callbacks?: TomlCallbackRoute[];
|
|
10
|
+
}
|
|
11
|
+
export interface TomlRouteResponse {
|
|
12
|
+
reply?: string;
|
|
13
|
+
handler?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface TomlCommandRoute extends TomlRouteResponse {
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface TomlHearRoute extends TomlRouteResponse {
|
|
20
|
+
text: string;
|
|
21
|
+
}
|
|
22
|
+
export interface TomlMessageRoute extends TomlRouteResponse {
|
|
23
|
+
on: string;
|
|
24
|
+
contains?: string;
|
|
25
|
+
equals?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface TomlCallbackRoute extends TomlRouteResponse {
|
|
28
|
+
data: string;
|
|
29
|
+
}
|
|
30
|
+
export type TomlHandler<C extends Context = Context> = Middleware<C>;
|
|
31
|
+
export type TomlHandlers<C extends Context = Context> = Record<string, TomlHandler<C>>;
|
|
32
|
+
export interface InstallTomlOptions<C extends Context = Context> {
|
|
33
|
+
/** named handlers referenced by `handler = "name"` in toml routes. */
|
|
34
|
+
handlers?: TomlHandlers<C>;
|
|
35
|
+
}
|
|
36
|
+
export type TomlConfigInput = string | TomlBotConfig | Record<string, unknown>;
|
|
37
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,WAAW,aAAa;IAC7B,GAAG,CAAC,EAAE;QACL,IAAI,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,iBAAiB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAc,SAAQ,iBAAiB;IACvD,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IAC1D,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC3D,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;AAErE,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AAEvF,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO;IAC9D,sEAAsE;IACtE,QAAQ,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;CAC3B;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC"}
|
package/lib/types.js
ADDED
package/lib/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yaebal/toml",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "yaebal toml — declarative toml routes for yaebal bots.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./lib/index.js",
|
|
7
|
+
"types": "./lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"import": "./lib/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"lib",
|
|
16
|
+
"src"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"smol-toml": "^1.7.0",
|
|
20
|
+
"zod": "^4.4.3",
|
|
21
|
+
"@yaebal/core": "0.0.5"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "latest",
|
|
25
|
+
"@yaebal/test": "0.0.1"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"telegram",
|
|
32
|
+
"telegram-bot",
|
|
33
|
+
"yaebal",
|
|
34
|
+
"toml",
|
|
35
|
+
"config",
|
|
36
|
+
"routes"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/neverlane/yaebal",
|
|
42
|
+
"directory": "packages/toml"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.json",
|
|
49
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
50
|
+
"test": "node --test lib/index.test.js"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import { Composer } from "@yaebal/core";
|
|
7
|
+
import { callbackUpdate, createContext, messageUpdate, mockApi, runMiddleware } from "@yaebal/test";
|
|
8
|
+
import { installToml, parseTomlConfig, validateTomlConfig } from "./index.js";
|
|
9
|
+
|
|
10
|
+
test("parse raw toml string", () => {
|
|
11
|
+
const config = parseTomlConfig(`
|
|
12
|
+
[[commands]]
|
|
13
|
+
name = "start"
|
|
14
|
+
reply = "hi"
|
|
15
|
+
`);
|
|
16
|
+
|
|
17
|
+
assert.deepEqual(config.commands, [{ name: "start", reply: "hi" }]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("parse toml file path", async () => {
|
|
21
|
+
const dir = await mkdtemp(join(tmpdir(), "yaebal-toml-"));
|
|
22
|
+
const file = join(dir, "bot.toml");
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
await writeFile(file, `[[hears]]\ntext = "ping"\nreply = "pong"\n`);
|
|
26
|
+
|
|
27
|
+
const config = parseTomlConfig(file);
|
|
28
|
+
|
|
29
|
+
assert.deepEqual(config.hears, [{ text: "ping", reply: "pong" }]);
|
|
30
|
+
} finally {
|
|
31
|
+
await rm(dir, { recursive: true, force: true });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("validate minimal empty config", () => {
|
|
36
|
+
assert.deepEqual(validateTomlConfig({}), {});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("validate command with reply", () => {
|
|
40
|
+
assert.deepEqual(validateTomlConfig({ commands: [{ name: "start", reply: "hi" }] }), {
|
|
41
|
+
commands: [{ name: "start", reply: "hi" }],
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("validate command with missing reply or handler should fail", () => {
|
|
46
|
+
assert.throws(
|
|
47
|
+
() => validateTomlConfig({ commands: [{ name: "start" }] }),
|
|
48
|
+
/commands\[0\] must define either reply or handler/,
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("validate message with missing on should fail", () => {
|
|
53
|
+
assert.throws(
|
|
54
|
+
() => validateTomlConfig({ messages: [{ reply: "hi" }] }),
|
|
55
|
+
/messages\[0\]\.on is required/,
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("install registers command reply", async () => {
|
|
60
|
+
const composer = new Composer();
|
|
61
|
+
const { api, calls } = mockApi();
|
|
62
|
+
|
|
63
|
+
installToml(composer, { commands: [{ name: "start", reply: "hi" }] });
|
|
64
|
+
await runMiddleware(composer, createContext(messageUpdate({ text: "/start" }), api));
|
|
65
|
+
|
|
66
|
+
assert.deepEqual(calls, [
|
|
67
|
+
{
|
|
68
|
+
method: "sendMessage",
|
|
69
|
+
params: { chat_id: 1, text: "hi", reply_parameters: { message_id: 1 } },
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("install throws on missing named handler", () => {
|
|
75
|
+
assert.throws(
|
|
76
|
+
() => installToml(new Composer(), { commands: [{ name: "ping", handler: "ping" }] }),
|
|
77
|
+
/Missing handler "ping" referenced in commands\[0\]/,
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("messages contains filter works", async () => {
|
|
82
|
+
const composer = new Composer();
|
|
83
|
+
const { api, calls } = mockApi();
|
|
84
|
+
|
|
85
|
+
installToml(composer, {
|
|
86
|
+
messages: [{ on: "message:text", contains: "yaebal", reply: "match" }],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
await runMiddleware(composer, createContext(messageUpdate({ text: "hello" }), api));
|
|
90
|
+
await runMiddleware(composer, createContext(messageUpdate({ text: "hello yaebal" }), api));
|
|
91
|
+
|
|
92
|
+
assert.equal(calls.length, 1);
|
|
93
|
+
assert.equal(calls[0]?.params?.text, "match");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("messages equals filter works", async () => {
|
|
97
|
+
const composer = new Composer();
|
|
98
|
+
const { api, calls } = mockApi();
|
|
99
|
+
|
|
100
|
+
installToml(composer, {
|
|
101
|
+
messages: [{ on: "message:text", equals: "secret", reply: "match" }],
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
await runMiddleware(composer, createContext(messageUpdate({ text: "secret!" }), api));
|
|
105
|
+
await runMiddleware(composer, createContext(messageUpdate({ text: "secret" }), api));
|
|
106
|
+
|
|
107
|
+
assert.equal(calls.length, 1);
|
|
108
|
+
assert.equal(calls[0]?.params?.text, "match");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("callbacks with handler works", async () => {
|
|
112
|
+
const composer = new Composer();
|
|
113
|
+
const { api, calls } = mockApi();
|
|
114
|
+
|
|
115
|
+
installToml(
|
|
116
|
+
composer,
|
|
117
|
+
{
|
|
118
|
+
callbacks: [{ data: "profile", handler: "profileCallback" }],
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
handlers: {
|
|
122
|
+
profileCallback: async (ctx) => {
|
|
123
|
+
await ctx.reply("profile");
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
await runMiddleware(composer, createContext(callbackUpdate({ data: "profile" }), api));
|
|
130
|
+
|
|
131
|
+
assert.deepEqual(calls, [
|
|
132
|
+
{
|
|
133
|
+
method: "sendMessage",
|
|
134
|
+
params: { chat_id: 1, text: "profile" },
|
|
135
|
+
},
|
|
136
|
+
]);
|
|
137
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createTomlPlugin, installToml } from "./install.js";
|
|
2
|
+
export { parseTomlConfig } from "./parse.js";
|
|
3
|
+
export { tomlBotConfigSchema, validateTomlConfig } from "./schema.js";
|
|
4
|
+
export type { InstallTomlOptions, TomlBotConfig, TomlHandler, TomlHandlers } from "./types.js";
|
package/src/install.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CallbackQuery,
|
|
3
|
+
Composer,
|
|
4
|
+
Context,
|
|
5
|
+
FilterQuery,
|
|
6
|
+
Middleware,
|
|
7
|
+
Plugin,
|
|
8
|
+
} from "@yaebal/core";
|
|
9
|
+
import { parseTomlConfig } from "./parse.js";
|
|
10
|
+
import type {
|
|
11
|
+
InstallTomlOptions,
|
|
12
|
+
TomlBotConfig,
|
|
13
|
+
TomlConfigInput,
|
|
14
|
+
TomlRouteResponse,
|
|
15
|
+
} from "./types.js";
|
|
16
|
+
|
|
17
|
+
type EmptyPluginOutput = Record<never, never>;
|
|
18
|
+
|
|
19
|
+
function replyHandler<C extends Context>(text: string): Middleware<C> {
|
|
20
|
+
return async (ctx) => {
|
|
21
|
+
await ctx.reply(text);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function missingResponseError(kind: string, index: number): Error {
|
|
26
|
+
return new Error(`${kind}[${index}] must define either reply or handler`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function resolveHandler<C extends Context>(
|
|
30
|
+
route: TomlRouteResponse,
|
|
31
|
+
kind: string,
|
|
32
|
+
index: number,
|
|
33
|
+
options: InstallTomlOptions<C>,
|
|
34
|
+
): Middleware<C> {
|
|
35
|
+
if (route.handler !== undefined) {
|
|
36
|
+
const handler = options.handlers?.[route.handler];
|
|
37
|
+
|
|
38
|
+
if (!handler)
|
|
39
|
+
throw new Error(`Missing handler "${route.handler}" referenced in ${kind}[${index}]`);
|
|
40
|
+
|
|
41
|
+
return handler;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (route.reply !== undefined) return replyHandler(route.reply);
|
|
45
|
+
|
|
46
|
+
throw missingResponseError(kind, index);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function assertHandlersExist<C extends Context>(
|
|
50
|
+
config: TomlBotConfig,
|
|
51
|
+
options: InstallTomlOptions<C>,
|
|
52
|
+
): void {
|
|
53
|
+
for (const [index, route] of (config.commands ?? []).entries()) {
|
|
54
|
+
if (route.handler !== undefined && !options.handlers?.[route.handler]) {
|
|
55
|
+
throw new Error(`Missing handler "${route.handler}" referenced in commands[${index}]`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const [index, route] of (config.hears ?? []).entries()) {
|
|
60
|
+
if (route.handler !== undefined && !options.handlers?.[route.handler]) {
|
|
61
|
+
throw new Error(`Missing handler "${route.handler}" referenced in hears[${index}]`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const [index, route] of (config.messages ?? []).entries()) {
|
|
66
|
+
if (route.handler !== undefined && !options.handlers?.[route.handler]) {
|
|
67
|
+
throw new Error(`Missing handler "${route.handler}" referenced in messages[${index}]`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const [index, route] of (config.callbacks ?? []).entries()) {
|
|
72
|
+
if (route.handler !== undefined && !options.handlers?.[route.handler]) {
|
|
73
|
+
throw new Error(`Missing handler "${route.handler}" referenced in callbacks[${index}]`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function matchesMessageFilters(
|
|
79
|
+
ctx: Context,
|
|
80
|
+
filters: { contains?: string; equals?: string },
|
|
81
|
+
): boolean {
|
|
82
|
+
if (filters.contains === undefined && filters.equals === undefined) return true;
|
|
83
|
+
|
|
84
|
+
const text = ctx.text;
|
|
85
|
+
if (text === undefined) return false;
|
|
86
|
+
if (filters.equals !== undefined && text !== filters.equals) return false;
|
|
87
|
+
if (filters.contains !== undefined && !text.includes(filters.contains)) return false;
|
|
88
|
+
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** install toml routes on an existing bot or composer and return the same instance. */
|
|
93
|
+
export function installToml<C extends Context, T extends Composer<C>>(
|
|
94
|
+
target: T,
|
|
95
|
+
configPathOrObject: TomlConfigInput,
|
|
96
|
+
options: InstallTomlOptions<C> = {},
|
|
97
|
+
): T {
|
|
98
|
+
const config = parseTomlConfig(configPathOrObject);
|
|
99
|
+
|
|
100
|
+
assertHandlersExist(config, options);
|
|
101
|
+
|
|
102
|
+
for (const [index, route] of (config.commands ?? []).entries()) {
|
|
103
|
+
const handler = resolveHandler(route, "commands", index, options) as Middleware<
|
|
104
|
+
C & { command: string; args: string[] }
|
|
105
|
+
>;
|
|
106
|
+
|
|
107
|
+
target.command(route.name, handler);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (const [index, route] of (config.hears ?? []).entries()) {
|
|
111
|
+
const handler = resolveHandler(route, "hears", index, options) as Middleware<
|
|
112
|
+
C & { match: string | RegExpMatchArray }
|
|
113
|
+
>;
|
|
114
|
+
|
|
115
|
+
target.hears(route.text, handler);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const [index, route] of (config.messages ?? []).entries()) {
|
|
119
|
+
const handler = resolveHandler(route, "messages", index, options);
|
|
120
|
+
|
|
121
|
+
target.on(route.on as FilterQuery, async (ctx, next) => {
|
|
122
|
+
if (!matchesMessageFilters(ctx, route)) return next();
|
|
123
|
+
|
|
124
|
+
return handler(ctx as C, next);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const [index, route] of (config.callbacks ?? []).entries()) {
|
|
129
|
+
const handler = resolveHandler(route, "callbacks", index, options) as Middleware<
|
|
130
|
+
C & { match: string | RegExpMatchArray; callbackQuery: CallbackQuery }
|
|
131
|
+
>;
|
|
132
|
+
|
|
133
|
+
target.callbackQuery(route.data, handler);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return target;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** create a yaebal plugin that installs toml routes when mounted. */
|
|
140
|
+
export function createTomlPlugin<C extends Context = Context>(
|
|
141
|
+
configPathOrObject: TomlConfigInput,
|
|
142
|
+
options: InstallTomlOptions<C> = {},
|
|
143
|
+
): Plugin<C, EmptyPluginOutput> {
|
|
144
|
+
return <D extends C>(composer: Composer<D>) =>
|
|
145
|
+
installToml(composer, configPathOrObject, options as InstallTomlOptions<D>);
|
|
146
|
+
}
|
package/src/parse.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { parse } from "smol-toml";
|
|
3
|
+
import { validateTomlConfig } from "./schema.js";
|
|
4
|
+
import type { TomlBotConfig, TomlConfigInput } from "./types.js";
|
|
5
|
+
|
|
6
|
+
function errorMessage(error: unknown): string {
|
|
7
|
+
return error instanceof Error ? error.message : String(error);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isPathLike(input: string): boolean {
|
|
11
|
+
const trimmed = input.trim();
|
|
12
|
+
|
|
13
|
+
if (trimmed === "") return false;
|
|
14
|
+
if (trimmed.includes("\n") || trimmed.includes("=") || trimmed.startsWith("[")) return false;
|
|
15
|
+
if (existsSync(input)) return true;
|
|
16
|
+
|
|
17
|
+
return trimmed.endsWith(".toml") || trimmed.endsWith(".tml");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function loadTomlSource(input: string): string {
|
|
21
|
+
if (!isPathLike(input)) return input;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
return readFileSync(input, "utf8");
|
|
25
|
+
} catch (error) {
|
|
26
|
+
throw new Error(`failed to read toml config file "${input}": ${errorMessage(error)}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** parse a toml config from a file path, raw toml string, or parsed object. */
|
|
31
|
+
export function parseTomlConfig(input: TomlConfigInput): TomlBotConfig {
|
|
32
|
+
if (typeof input !== "string") return validateTomlConfig(input);
|
|
33
|
+
|
|
34
|
+
const source = loadTomlSource(input);
|
|
35
|
+
let parsed: unknown;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
parsed = parse(source);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
throw new Error(`failed to parse toml config: ${errorMessage(error)}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return validateTomlConfig(parsed);
|
|
44
|
+
}
|
package/src/schema.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { TomlBotConfig } from "./types.js";
|
|
3
|
+
|
|
4
|
+
const requiredString = z.string().min(1);
|
|
5
|
+
const optionalString = z.string().optional();
|
|
6
|
+
|
|
7
|
+
const responseShape = {
|
|
8
|
+
reply: optionalString,
|
|
9
|
+
handler: requiredString.optional(),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function hasResponse(route: { reply?: string; handler?: string }): boolean {
|
|
13
|
+
return route.reply !== undefined || route.handler !== undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const commandSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
name: requiredString,
|
|
19
|
+
description: optionalString,
|
|
20
|
+
...responseShape,
|
|
21
|
+
})
|
|
22
|
+
.superRefine((route, ctx) => {
|
|
23
|
+
if (!hasResponse(route)) {
|
|
24
|
+
ctx.addIssue({ code: "custom", message: "must define either reply or handler" });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const hearSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
text: requiredString,
|
|
31
|
+
...responseShape,
|
|
32
|
+
})
|
|
33
|
+
.superRefine((route, ctx) => {
|
|
34
|
+
if (!hasResponse(route)) {
|
|
35
|
+
ctx.addIssue({ code: "custom", message: "must define either reply or handler" });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const messageSchema = z
|
|
40
|
+
.object({
|
|
41
|
+
on: requiredString,
|
|
42
|
+
contains: optionalString,
|
|
43
|
+
equals: optionalString,
|
|
44
|
+
...responseShape,
|
|
45
|
+
})
|
|
46
|
+
.superRefine((route, ctx) => {
|
|
47
|
+
if (!hasResponse(route)) {
|
|
48
|
+
ctx.addIssue({ code: "custom", message: "must define either reply or handler" });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const callbackSchema = z
|
|
53
|
+
.object({
|
|
54
|
+
data: requiredString,
|
|
55
|
+
...responseShape,
|
|
56
|
+
})
|
|
57
|
+
.superRefine((route, ctx) => {
|
|
58
|
+
if (!hasResponse(route)) {
|
|
59
|
+
ctx.addIssue({ code: "custom", message: "must define either reply or handler" });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const tomlBotConfigSchema = z.object({
|
|
64
|
+
bot: z
|
|
65
|
+
.object({
|
|
66
|
+
name: optionalString,
|
|
67
|
+
})
|
|
68
|
+
.optional(),
|
|
69
|
+
commands: z.array(commandSchema).optional(),
|
|
70
|
+
hears: z.array(hearSchema).optional(),
|
|
71
|
+
messages: z.array(messageSchema).optional(),
|
|
72
|
+
callbacks: z.array(callbackSchema).optional(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
function formatPath(path: PropertyKey[]): string {
|
|
76
|
+
let out = "";
|
|
77
|
+
|
|
78
|
+
for (const segment of path) {
|
|
79
|
+
if (typeof segment === "number") {
|
|
80
|
+
out += `[${segment}]`;
|
|
81
|
+
} else {
|
|
82
|
+
out += out ? `.${String(segment)}` : String(segment);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return out || "config";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function isMissingRequired(issue: z.ZodIssue): boolean {
|
|
90
|
+
const details = issue as z.ZodIssue & { input?: unknown; received?: string };
|
|
91
|
+
const message = issue.message.toLowerCase();
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
issue.code === "invalid_type" &&
|
|
95
|
+
(details.input === undefined ||
|
|
96
|
+
details.received === "undefined" ||
|
|
97
|
+
message.includes("required") ||
|
|
98
|
+
message.includes("undefined"))
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function formatIssue(issue: z.ZodIssue): string {
|
|
103
|
+
const path = formatPath(issue.path);
|
|
104
|
+
|
|
105
|
+
if (issue.code === "custom") return `${path} ${issue.message}`;
|
|
106
|
+
if (isMissingRequired(issue)) return `${path} is required`;
|
|
107
|
+
|
|
108
|
+
return `${path}: ${issue.message}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** validate an already parsed toml bot config and return the normalized object. */
|
|
112
|
+
export function validateTomlConfig(input: unknown): TomlBotConfig {
|
|
113
|
+
const result = tomlBotConfigSchema.safeParse(input);
|
|
114
|
+
|
|
115
|
+
if (result.success) return result.data;
|
|
116
|
+
|
|
117
|
+
throw new Error(result.error.issues.map(formatIssue).join("; "));
|
|
118
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Context, Middleware } from "@yaebal/core";
|
|
2
|
+
|
|
3
|
+
export interface TomlBotConfig {
|
|
4
|
+
bot?: {
|
|
5
|
+
name?: string;
|
|
6
|
+
};
|
|
7
|
+
commands?: TomlCommandRoute[];
|
|
8
|
+
hears?: TomlHearRoute[];
|
|
9
|
+
messages?: TomlMessageRoute[];
|
|
10
|
+
callbacks?: TomlCallbackRoute[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TomlRouteResponse {
|
|
14
|
+
reply?: string;
|
|
15
|
+
handler?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface TomlCommandRoute extends TomlRouteResponse {
|
|
19
|
+
name: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TomlHearRoute extends TomlRouteResponse {
|
|
24
|
+
text: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface TomlMessageRoute extends TomlRouteResponse {
|
|
28
|
+
on: string;
|
|
29
|
+
contains?: string;
|
|
30
|
+
equals?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface TomlCallbackRoute extends TomlRouteResponse {
|
|
34
|
+
data: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type TomlHandler<C extends Context = Context> = Middleware<C>;
|
|
38
|
+
|
|
39
|
+
export type TomlHandlers<C extends Context = Context> = Record<string, TomlHandler<C>>;
|
|
40
|
+
|
|
41
|
+
export interface InstallTomlOptions<C extends Context = Context> {
|
|
42
|
+
/** named handlers referenced by `handler = "name"` in toml routes. */
|
|
43
|
+
handlers?: TomlHandlers<C>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type TomlConfigInput = string | TomlBotConfig | Record<string, unknown>;
|