koishi-plugin-fake-message 1.0.1 → 2.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/index.d.ts +0 -2
- package/lib/index.js +104 -90
- package/package.json +3 -11
- package/readme.md +19 -28
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -28,106 +28,120 @@ module.exports = __toCommonJS(src_exports);
|
|
|
28
28
|
var import_koishi = require("koishi");
|
|
29
29
|
var name = "fake-message";
|
|
30
30
|
var Config = import_koishi.Schema.object({
|
|
31
|
-
|
|
32
|
-
messageSplit: import_koishi.Schema.string().default(" ").description("同一用户多条消息分隔符"),
|
|
33
|
-
blockedUsers: import_koishi.Schema.array(import_koishi.Schema.string()).default([]).description("被屏蔽的QQ号列表,这些QQ号将无法被伪造消息")
|
|
31
|
+
blockedUsers: import_koishi.Schema.array(import_koishi.Schema.string()).default([]).description("被屏蔽的用户ID列表")
|
|
34
32
|
});
|
|
35
|
-
|
|
33
|
+
function parseMessages(elements, quotedContent) {
|
|
34
|
+
const messages = [];
|
|
35
|
+
let currentMessage = null;
|
|
36
|
+
let nextIsNewMessage = false;
|
|
37
|
+
for (let i = 0; i < elements.length; i++) {
|
|
38
|
+
const element = elements[i];
|
|
39
|
+
if (element.type === "message-boundary") {
|
|
40
|
+
nextIsNewMessage = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (element.type === "at") {
|
|
44
|
+
if (nextIsNewMessage || messages.length === 0) {
|
|
45
|
+
const userId = element.attrs.id;
|
|
46
|
+
const userName = element.attrs.name || userId;
|
|
47
|
+
currentMessage = {
|
|
48
|
+
userId,
|
|
49
|
+
userName,
|
|
50
|
+
content: []
|
|
51
|
+
};
|
|
52
|
+
messages.push(currentMessage);
|
|
53
|
+
nextIsNewMessage = false;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (currentMessage) {
|
|
58
|
+
currentMessage.content.push(element);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (quotedContent && quotedContent.length > 0 && messages.length > 0) {
|
|
62
|
+
const firstMsg = messages[0];
|
|
63
|
+
const hasNonEmptyContent = firstMsg.content.some((el) => {
|
|
64
|
+
if (el.type === "text") {
|
|
65
|
+
return el.attrs.content.trim().length > 0;
|
|
66
|
+
}
|
|
67
|
+
return el.type !== "br" && el.type !== "p";
|
|
68
|
+
});
|
|
69
|
+
if (!hasNonEmptyContent) {
|
|
70
|
+
firstMsg.content = quotedContent;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return messages;
|
|
74
|
+
}
|
|
75
|
+
__name(parseMessages, "parseMessages");
|
|
36
76
|
function apply(ctx, config) {
|
|
37
|
-
ctx.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
].join("\n"));
|
|
44
|
-
});
|
|
45
|
-
const checkIfFakeMsg = /* @__PURE__ */ __name((content) => {
|
|
46
|
-
return fakeMsgRegex.test(content);
|
|
47
|
-
}, "checkIfFakeMsg");
|
|
48
|
-
const isUserBlocked = /* @__PURE__ */ __name((userId) => {
|
|
49
|
-
return config.blockedUsers.includes(userId);
|
|
50
|
-
}, "isUserBlocked");
|
|
51
|
-
ctx.middleware(async (session, next) => {
|
|
52
|
-
const content = session.content;
|
|
53
|
-
if (!checkIfFakeMsg(content)) {
|
|
54
|
-
return next();
|
|
77
|
+
ctx.command("fake <content:el>", "伪造合并转发消息", { checkArgCount: true }).usage(
|
|
78
|
+
"用法示例:\nfake @用户A 消息内容1\n@用户B 消息内容2\n@用户A 第一行\n第二行(同一条消息)\n\n规则:\n- 每行开头的 @用户 表示一条新消息\n- 换行后如果不是 @用户 开头,则属于上一条消息\n- 行内的 @用户 (非行首) 作为消息内容\n- 支持发送图片\n- 引用本指令可将被引用消息作为第一个用户的消息内容(如果没有为其提供消息的话)"
|
|
79
|
+
).action(async ({ session }) => {
|
|
80
|
+
let contentStr = session.content;
|
|
81
|
+
if (!contentStr) {
|
|
82
|
+
return "请输入要伪造的消息内容";
|
|
55
83
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
const userInfo = await session.bot.getUser(userQQ);
|
|
71
|
-
const userName = userInfo?.name || userQQ;
|
|
72
|
-
const messages = message.split(config.messageSplit);
|
|
73
|
-
for (const msg of messages) {
|
|
74
|
-
if (msg.trim()) {
|
|
75
|
-
fakeMessages.push({
|
|
76
|
-
userId: userQQ,
|
|
77
|
-
nickname: userName,
|
|
78
|
-
content: msg.trim()
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
} catch (e) {
|
|
83
|
-
console.error(`获取用户 ${userQQ} 信息失败:`, e);
|
|
84
|
-
const messages = message.split(config.messageSplit);
|
|
85
|
-
for (const msg of messages) {
|
|
86
|
-
if (msg.trim()) {
|
|
87
|
-
fakeMessages.push({
|
|
88
|
-
userId: userQQ,
|
|
89
|
-
nickname: userQQ,
|
|
90
|
-
content: msg.trim()
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
84
|
+
contentStr = contentStr.replace(/(^|[\s])@(\d+)(?=$|[\s])/g, (match, prefix, id) => {
|
|
85
|
+
return `${prefix}<at id="${id}"/>`;
|
|
86
|
+
});
|
|
87
|
+
contentStr = contentStr.replace(/(\n\s*)(<at)/g, "$1<message-boundary/>$2");
|
|
88
|
+
const elements = import_koishi.h.parse(contentStr);
|
|
89
|
+
let quotedContent = null;
|
|
90
|
+
if (session.quote) {
|
|
91
|
+
if (session.quote.content) {
|
|
92
|
+
quotedContent = import_koishi.h.parse(session.quote.content);
|
|
93
|
+
} else if (session.quote.elements && session.quote.elements.length > 0) {
|
|
94
|
+
quotedContent = session.quote.elements;
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
}
|
|
97
|
+
const quoteElementIndex = elements.findIndex((el) => el.type === "quote");
|
|
98
|
+
if (quoteElementIndex !== -1) {
|
|
99
|
+
const quoteEl = elements[quoteElementIndex];
|
|
100
|
+
if (!quotedContent || quotedContent.length === 0) {
|
|
101
|
+
if (quoteEl.children && quoteEl.children.length > 0) {
|
|
102
|
+
quotedContent = quoteEl.children.filter((el) => el.type !== "author");
|
|
103
|
+
}
|
|
100
104
|
}
|
|
101
|
-
|
|
102
|
-
console.error("伪造消息失败:", e);
|
|
103
|
-
await session.send(`发送失败: ${e.message || "未知错误"}`);
|
|
105
|
+
elements.splice(quoteElementIndex, 1);
|
|
104
106
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
const fakeMessages = parseMessages(elements, quotedContent);
|
|
108
|
+
if (fakeMessages.length === 0) {
|
|
109
|
+
return "未能解析出任何消息,请确保格式正确(每条消息以 @用户 开头)";
|
|
110
|
+
}
|
|
111
|
+
if (session.guildId) {
|
|
112
|
+
await Promise.all(fakeMessages.map(async (msg) => {
|
|
113
|
+
if (msg.userName === msg.userId || !msg.userName) {
|
|
114
|
+
try {
|
|
115
|
+
const member = await session.bot.getGuildMember(session.guildId, msg.userId);
|
|
116
|
+
msg.userName = member.nick || member.user?.nick || member.user?.name || msg.userId;
|
|
117
|
+
} catch {
|
|
118
|
+
try {
|
|
119
|
+
const user = await session.bot.getUser(msg.userId);
|
|
120
|
+
msg.userName = user.nick || user.name || msg.userId;
|
|
121
|
+
} catch {
|
|
122
|
+
if (!msg.userName) msg.userName = msg.userId;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
115
125
|
}
|
|
116
126
|
}));
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
console.error("发送合并转发消息失败:", e);
|
|
124
|
-
await session.send("合并转发失败,将逐条发送消息");
|
|
125
|
-
for (const msg of messages) {
|
|
126
|
-
await session.send(`${msg.nickname}:${msg.content}`);
|
|
127
|
+
}
|
|
128
|
+
const forwardMessages = [];
|
|
129
|
+
for (let idx = 0; idx < fakeMessages.length; idx++) {
|
|
130
|
+
const msg = fakeMessages[idx];
|
|
131
|
+
if (config.blockedUsers.includes(msg.userId)) {
|
|
132
|
+
continue;
|
|
127
133
|
}
|
|
134
|
+
const messageChildren = [
|
|
135
|
+
(0, import_koishi.h)("author", { id: msg.userId, name: msg.userName }),
|
|
136
|
+
...msg.content
|
|
137
|
+
];
|
|
138
|
+
forwardMessages.push((0, import_koishi.h)("message", {}, messageChildren));
|
|
128
139
|
}
|
|
129
|
-
|
|
130
|
-
|
|
140
|
+
if (forwardMessages.length === 0) {
|
|
141
|
+
return "所有消息内容都为空或发送者被屏蔽";
|
|
142
|
+
}
|
|
143
|
+
await session.send((0, import_koishi.h)("message", { forward: true }, forwardMessages));
|
|
144
|
+
});
|
|
131
145
|
}
|
|
132
146
|
__name(apply, "apply");
|
|
133
147
|
// Annotate the CommonJS export names for ESM import in node:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-fake-message",
|
|
3
|
-
"description": "
|
|
4
|
-
"version": "
|
|
3
|
+
"description": "伪造合并转发消息",
|
|
4
|
+
"version": "2.0.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -9,19 +9,11 @@
|
|
|
9
9
|
"dist"
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"scripts": {
|
|
13
|
-
"build": "tsc"
|
|
14
|
-
},
|
|
15
12
|
"keywords": [
|
|
16
13
|
"chatbot",
|
|
17
14
|
"koishi",
|
|
18
|
-
"plugin"
|
|
19
|
-
"fake-message",
|
|
20
|
-
"forward-message"
|
|
15
|
+
"plugin"
|
|
21
16
|
],
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"typescript": "^5.0.0"
|
|
24
|
-
},
|
|
25
17
|
"peerDependencies": {
|
|
26
18
|
"koishi": "^4.18.7"
|
|
27
19
|
}
|
package/readme.md
CHANGED
|
@@ -1,44 +1,35 @@
|
|
|
1
1
|
# koishi-plugin-fake-message
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/koishi-plugin-fake-message)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
伪造合并转发消息
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## 功能特点
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- 换行后的第一个内容如果是 @用户,则表示新的一条消息
|
|
10
|
+
- 支持多行消息(换行后不是 @用户 开头则属于同一条消息)
|
|
11
|
+
- 支持发送图片
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## 使用方法
|
|
12
14
|
|
|
13
|
-
例如:
|
|
14
15
|
```
|
|
15
|
-
|
|
16
|
+
fake @用户A 这是第一条消息
|
|
17
|
+
@用户B 这是第二条消息
|
|
18
|
+
@用户A 这是第三条消息
|
|
19
|
+
第二行(同一条消息)
|
|
20
|
+
第三行(同一条消息)
|
|
21
|
+
@用户C 这是第四条消息 <img src="图片链接"/>
|
|
16
22
|
```
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
123456说你好|654321说早上好
|
|
21
|
-
```
|
|
24
|
+
### 引用被回复的消息
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
123456说你好 早上好 今天天气真不错
|
|
26
|
-
```
|
|
26
|
+
回复某条消息并使用本指令时,如果第一个用户没有提供消息内容,则会使用被引用的消息内容:
|
|
27
27
|
|
|
28
|
-
当然,你也可以综合使用:
|
|
29
28
|
```
|
|
30
|
-
|
|
29
|
+
[回复某条消息]
|
|
30
|
+
fake @用户A
|
|
31
|
+
@用户B 第二条消息
|
|
31
32
|
```
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- `userSplit`: 用户消息分隔符,默认为 `|`
|
|
36
|
-
- `messageSplit`: 同一用户多条消息分隔符,默认为空格
|
|
37
|
-
- `blockedUsers`: 被屏蔽的QQ号列表,这些QQ号将无法被伪造消息,默认为空数组
|
|
38
|
-
|
|
39
|
-
## 注意事项
|
|
34
|
+
此时用户A的消息内容将是被回复的那条消息的内容。
|
|
40
35
|
|
|
41
|
-
1. 伪造消息的 QQ 号必须是真实存在的用户
|
|
42
|
-
2. 如果合并转发消息发送失败,插件会自动退化为逐条发送文本消息
|
|
43
|
-
3. 本插件适用于所有适配器,但合并转发功能可能只在部分适配器中可用
|
|
44
|
-
4. 被屏蔽的QQ号将无法被伪造消息,尝试伪造被屏蔽QQ号的消息时会收到提示
|