@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.
- package/lib/assets/zh-CN-CrW2M7HM.yml +12 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.mjs +43 -0
- package/locales/de-DE.yml +12 -0
- package/locales/en-US.yml +12 -0
- package/locales/fr-FR.yml +12 -0
- package/locales/ja-JP.yml +12 -0
- package/locales/ru-RU.yml +12 -0
- package/locales/zh-CN.yml +12 -0
- package/locales/zh-TW.yml +12 -0
- package/package.json +60 -0
- package/src/index.test.ts +50 -0
- package/src/index.ts +68 -0
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: 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.
|
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 <>"), "<>");
|
|
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 <>"), "<>");
|
|
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
|
+
}
|