@koishi-ce/plugin-echo 1.0.0

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.
@@ -0,0 +1,12 @@
1
+ commands:
2
+ echo:
3
+ description: 发送消息
4
+ options:
5
+ escape: 发送转义消息
6
+ unescape: 发送反转义消息
7
+ user: 发送到用户
8
+ channel: 发送到频道
9
+ guild: 指定群组编号
10
+ messages:
11
+ expect-text: 请输入要发送的文本。
12
+ platform-not-found: 找不到指定的平台。
package/lib/index.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { Context, Schema } from "@koishi-ce/koishi";
2
+ //#region src/index.d.ts
3
+ /**
4
+ * 解析 `platform:id` 形式的目标标识
5
+ * @param target 形如 `platform:id` 的字符串
6
+ * @returns [平台名, 目标 ID] 二元组
7
+ */
8
+ declare function parsePlatform(target: string): [platform: string, id: string];
9
+ /** 配置项(当前无可用配置) */
10
+ type Config = Record<never, never>;
11
+ declare const name = "echo";
12
+ declare const Config: Schema<Config>;
13
+ declare function apply(ctx: Context, _config: Config): void;
14
+ //#endregion
15
+ export { Config, apply, name, parsePlatform };
package/lib/index.mjs ADDED
@@ -0,0 +1,43 @@
1
+ import { Schema, h } from "@koishi-ce/koishi";
2
+ import zhCN from "./assets/zh-CN-CrW2M7HM.yml";
3
+ //#region src/index.ts
4
+ /**
5
+ * 消息复述插件(echo)。
6
+ *
7
+ * 提供 `echo <message>` 指令:原样返回输入文本。
8
+ * 权限 3 以上的选项:`-e` / `-E` 转义 / 反转义消息中的 CQ 码;
9
+ * `-u` / `-c`(可配合 `-g` 指定群组)把消息发送到指定用户私聊或指定频道。
10
+ */
11
+ /**
12
+ * 解析 `platform:id` 形式的目标标识
13
+ * @param target 形如 `platform:id` 的字符串
14
+ * @returns [平台名, 目标 ID] 二元组
15
+ */
16
+ function parsePlatform(target) {
17
+ const index = target.indexOf(":");
18
+ return [target.slice(0, index), target.slice(index + 1)];
19
+ }
20
+ const name = "echo";
21
+ const Config = Schema.object({});
22
+ function apply(ctx, _config) {
23
+ ctx.i18n.define("zh-CN", zhCN);
24
+ ctx.command("echo <message:text>").option("escape", "-e", { authority: 3 }).option("unescape", "-E", { authority: 3 }).option("user", "-u [user:user]", { authority: 3 }).option("channel", "-c [channel:channel]", { authority: 3 }).option("guild", "-g [guild:string]", { authority: 3 }).action(async ({ options, session }, message) => {
25
+ if (!session || !options) return;
26
+ if (!message) return session.text(".expect-text");
27
+ let content = [message];
28
+ if (options.unescape) content = h.parse(message);
29
+ else if (options.escape) content = [h.escape(message)];
30
+ const target = options.user || options.channel;
31
+ if (target) {
32
+ const [platform, id] = parsePlatform(target);
33
+ const bot = ctx.bots.find((bot) => bot.platform === platform);
34
+ if (!bot) return session.text(".platform-not-found");
35
+ else if (options.user) await bot.sendPrivateMessage(id, content, session.guildId);
36
+ else await bot.sendMessage(id, content, options.guild);
37
+ return;
38
+ }
39
+ return content;
40
+ });
41
+ }
42
+ //#endregion
43
+ export { Config, apply, name, parsePlatform };
@@ -0,0 +1,12 @@
1
+ commands:
2
+ echo:
3
+ description: 发送消息
4
+ options:
5
+ escape: 发送转义消息
6
+ unescape: 发送反转义消息
7
+ user: 发送到用户
8
+ channel: 发送到频道
9
+ guild: 指定群组编号
10
+ messages:
11
+ expect-text: 请输入要发送的文本。
12
+ platform-not-found: 找不到指定的平台。
@@ -0,0 +1,12 @@
1
+ commands:
2
+ echo:
3
+ description: Send messages
4
+ options:
5
+ escape: Sending escape messages
6
+ unescape: Sending of reverse-escaped messages
7
+ user: Send to user
8
+ channel: Send to channel
9
+ guild: Assign guild number
10
+ messages:
11
+ expect-text: Please type to send
12
+ platform-not-found: Can't find the platform you have in mind.
@@ -0,0 +1,12 @@
1
+ commands:
2
+ echo:
3
+ description: 发送消息
4
+ options:
5
+ escape: 发送转义消息
6
+ unescape: 发送反转义消息
7
+ user: 发送到用户
8
+ channel: 发送到频道
9
+ guild: 指定群组编号
10
+ messages:
11
+ expect-text: 请输入要发送的文本。
12
+ platform-not-found: 找不到指定的平台。
@@ -0,0 +1,12 @@
1
+ commands:
2
+ echo:
3
+ description: 发送消息
4
+ options:
5
+ escape: 发送转义消息
6
+ unescape: 发送反转义消息
7
+ user: 发送到用户
8
+ channel: 发送到频道
9
+ guild: 指定群组编号
10
+ messages:
11
+ expect-text: 请输入要发送的文本。
12
+ platform-not-found: 找不到指定的平台。
@@ -0,0 +1,12 @@
1
+ commands:
2
+ echo:
3
+ description: 发送消息
4
+ options:
5
+ escape: 发送转义消息
6
+ unescape: 发送反转义消息
7
+ user: 发送到用户
8
+ channel: 发送到频道
9
+ guild: 指定群组编号
10
+ messages:
11
+ expect-text: 请输入要发送的文本。
12
+ platform-not-found: 找不到指定的平台。
@@ -0,0 +1,12 @@
1
+ commands:
2
+ echo:
3
+ description: 发送消息
4
+ options:
5
+ escape: 发送转义消息
6
+ unescape: 发送反转义消息
7
+ user: 发送到用户
8
+ channel: 发送到频道
9
+ guild: 指定群组编号
10
+ messages:
11
+ expect-text: 请输入要发送的文本。
12
+ platform-not-found: 找不到指定的平台。
@@ -0,0 +1,12 @@
1
+ commands:
2
+ echo:
3
+ description: 發送消息
4
+ options:
5
+ escape: 發送轉義消息
6
+ unescape: 發送反轉義消息
7
+ user: 發送到使用者
8
+ channel: 發送到頻道
9
+ guild: 指定群組編號
10
+ messages:
11
+ expect-text: 請輸入要發送的文本。
12
+ platform-not-found: 找不到指定的平台。
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@koishi-ce/plugin-echo",
3
+ "description": "Echo plugin for Koishi",
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "main": "lib/index.mjs",
7
+ "typings": "lib/index.d.ts",
8
+ "files": [
9
+ "lib",
10
+ "src",
11
+ "locales"
12
+ ],
13
+ "contributors": [
14
+ "Shigma <shigma10826@gmail.com>",
15
+ "Oppenheymu <oppenheymu@gmail.com>"
16
+ ],
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Koishi-CE/koishi.git",
21
+ "directory": "plugins/common/echo"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/Koishi-CE/koishi/issues"
25
+ },
26
+ "homepage": "https://koishi.chat/plugins/common/echo.html",
27
+ "keywords": [
28
+ "bot",
29
+ "chatbot",
30
+ "koishi",
31
+ "plugin",
32
+ "echo"
33
+ ],
34
+ "koishi": {
35
+ "browser": true,
36
+ "category": "tool",
37
+ "description": {
38
+ "en": "Echo messages",
39
+ "zh": "发送指定消息"
40
+ },
41
+ "locales": [
42
+ "zh"
43
+ ]
44
+ },
45
+ "peerDependencies": {
46
+ "koishi": "^4.18.11"
47
+ },
48
+ "devDependencies": {
49
+ "@koishi-ce/plugin-mock": "^1.0.0",
50
+ "@koishi-ce/koishi": "^1.0.0"
51
+ },
52
+ "exports": {
53
+ ".": {
54
+ "source": "./src/index.ts",
55
+ "types": "./lib/index.d.ts",
56
+ "import": "./lib/index.mjs",
57
+ "default": "./lib/index.mjs"
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * echo 插件测试:验证基础复述、CQ 码转义 / 反转义
3
+ * 与定向发送(-u 用户私聊 / -c 频道)的参数形状。
4
+ */
5
+ import { beforeAll, describe, expect, it, jest } from "bun:test";
6
+ import { App, type Bot, h } from "@koishi-ce/koishi";
7
+ import * as echo from "@koishi-ce/plugin-echo";
8
+ import mock from "@koishi-ce/plugin-mock";
9
+
10
+ const app = new App();
11
+
12
+ app.plugin(mock);
13
+ app.plugin(echo);
14
+
15
+ const client = app.mock.client("123");
16
+
17
+ beforeAll(() => app.start());
18
+
19
+ describe("@koishi-ce/plugin-echo", () => {
20
+ // 覆盖纯文本、转义输入、-E 反转义(拆分为多条消息),以及向用户 / 频道定向发送时的调用参数
21
+ it("basic support", async () => {
22
+ await client.shouldReply("echo", "请输入要发送的文本。");
23
+ await client.shouldReply("echo foo", "foo");
24
+ await client.shouldReply(h.escape("echo &lt;&gt;"), "&lt;&gt;");
25
+ await client.shouldReply(
26
+ h.escape("echo 1<message>2</message>3"),
27
+ "1<message>2</message>3",
28
+ );
29
+ await client.shouldReply(h.escape("echo -E &lt;&gt;"), "<>");
30
+ await client.shouldReply(h.escape("echo -E 1<message>2</message>3"), [
31
+ "1",
32
+ "2",
33
+ "3",
34
+ ]);
35
+
36
+ const send1 = (app.bots[0]!.sendPrivateMessage =
37
+ jest.fn<Bot["sendPrivateMessage"]>());
38
+ await client.shouldNotReply("echo -u @100 foo");
39
+ expect(send1.mock.calls).toHaveLength(1);
40
+ // 形状断言:仅校验期望侧的数组索引(args[0] 平台目标、args[1] 消息体)
41
+ expect(send1.mock.calls[0]?.[0]).toBe("100");
42
+ expect(send1.mock.calls[0]?.[1]).toStrictEqual(["foo"]);
43
+
44
+ const send2 = (app.bots[0]!.sendMessage = jest.fn<Bot["sendMessage"]>());
45
+ await client.shouldNotReply("echo -c #200 foo");
46
+ expect(send1.mock.calls).toHaveLength(1);
47
+ expect(send2.mock.calls[0]?.[0]).toBe("200");
48
+ expect(send2.mock.calls[0]?.[1]).toStrictEqual(["foo"]);
49
+ });
50
+ });
package/src/index.ts ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * 消息复述插件(echo)。
3
+ *
4
+ * 提供 `echo <message>` 指令:原样返回输入文本。
5
+ * 权限 3 以上的选项:`-e` / `-E` 转义 / 反转义消息中的 CQ 码;
6
+ * `-u` / `-c`(可配合 `-g` 指定群组)把消息发送到指定用户私聊或指定频道。
7
+ */
8
+ import { type Context, h, Schema } from "@koishi-ce/koishi";
9
+ import zhCN from "../locales/zh-CN.yml";
10
+
11
+ /**
12
+ * 解析 `platform:id` 形式的目标标识
13
+ * @param target 形如 `platform:id` 的字符串
14
+ * @returns [平台名, 目标 ID] 二元组
15
+ */
16
+ export function parsePlatform(target: string): [platform: string, id: string] {
17
+ const index = target.indexOf(":");
18
+ const platform = target.slice(0, index);
19
+ const id = target.slice(index + 1);
20
+ return [platform, id];
21
+ }
22
+
23
+ /** 配置项(当前无可用配置) */
24
+ export type Config = Record<never, never>;
25
+
26
+ export const name = "echo";
27
+ export const Config: Schema<Config> = Schema.object({});
28
+
29
+ export function apply(ctx: Context, _config: Config) {
30
+ ctx.i18n.define("zh-CN", zhCN);
31
+
32
+ ctx
33
+ .command("echo <message:text>")
34
+ .option("escape", "-e", { authority: 3 })
35
+ .option("unescape", "-E", { authority: 3 })
36
+ .option("user", "-u [user:user]", { authority: 3 })
37
+ .option("channel", "-c [channel:channel]", { authority: 3 })
38
+ .option("guild", "-g [guild:string]", { authority: 3 })
39
+ .action(async ({ options, session }, message) => {
40
+ if (!session || !options) return;
41
+ if (!message) return session.text(".expect-text");
42
+
43
+ // 用数组包裹,避免返回内容在发送环节再被反转义
44
+ let content: h.Fragment = [message];
45
+ if (options.unescape) {
46
+ content = h.parse(message);
47
+ } else if (options.escape) {
48
+ content = [h.escape(message)];
49
+ }
50
+
51
+ // 指定了 -u / -c 时改为向目标发送,而不是回复当前会话
52
+ const target = options.user || options.channel;
53
+ if (target) {
54
+ const [platform, id] = parsePlatform(target as string);
55
+ const bot = ctx.bots.find((bot) => bot.platform === platform);
56
+ if (!bot) {
57
+ return session.text(".platform-not-found");
58
+ } else if (options.user) {
59
+ await bot.sendPrivateMessage(id, content, session.guildId);
60
+ } else {
61
+ await bot.sendMessage(id, content, options.guild);
62
+ }
63
+ return;
64
+ }
65
+
66
+ return content;
67
+ });
68
+ }