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 CHANGED
@@ -1,8 +1,6 @@
1
1
  import { Context, Schema } from 'koishi';
2
2
  export declare const name = "fake-message";
3
3
  export interface Config {
4
- userSplit: string;
5
- messageSplit: string;
6
4
  blockedUsers: string[];
7
5
  }
8
6
  export declare const Config: Schema<Config>;
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
- userSplit: import_koishi.Schema.string().default("|").description("用户消息分隔符"),
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
- var fakeMsgRegex = /^\d{6,10}说/;
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.using(["help"], (ctx2) => {
38
- ctx2.command("fakemsg").action(() => [
39
- "消息伪造插件",
40
- "用法:【QQ号】说【内容】|【QQ号】说【内容】",
41
- "示例:123456说你好|654321说早上好",
42
- "注意:被屏蔽的QQ号将无法被伪造消息"
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
- await session.send("正在伪造消息……");
57
- try {
58
- const fakeMessages = [];
59
- const userMessages = content.split(config.userSplit);
60
- for (const userMsg of userMessages) {
61
- const trimmedMsg = userMsg.trim();
62
- if (!trimmedMsg || !trimmedMsg.includes("说")) continue;
63
- const [userQQ, message] = trimmedMsg.split("说", 2);
64
- if (!userQQ || !message) continue;
65
- if (isUserBlocked(userQQ)) {
66
- await session.send(`QQ号 ${userQQ} 已被屏蔽,无法伪造该用户的消息`);
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
- if (fakeMessages.length > 0) {
97
- await sendForwardMessage(session, fakeMessages);
98
- } else {
99
- await session.send("消息格式错误,请检查后重试");
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
- } catch (e) {
102
- console.error("伪造消息失败:", e);
103
- await session.send(`发送失败: ${e.message || "未知错误"}`);
105
+ elements.splice(quoteElementIndex, 1);
104
106
  }
105
- return;
106
- });
107
- async function sendForwardMessage(session, messages) {
108
- try {
109
- const nodes = messages.map((msg) => ({
110
- type: "node",
111
- data: {
112
- name: msg.nickname,
113
- uin: msg.userId,
114
- content: msg.content
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
- if (session.subtype === "group") {
118
- await session.bot.internal.sendGroupForwardMsg(session.guildId, nodes);
119
- } else {
120
- await session.bot.internal.sendPrivateForwardMsg(session.userId, nodes);
121
- }
122
- } catch (e) {
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
- __name(sendForwardMessage, "sendForwardMessage");
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": "1.0.1",
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
- 一个用于伪造消息的 Koishi 插件,移植自 [nonebot-plugin-fakemsg](https://github.com/Cvandia/nonebot-plugin-fakemsg)
3
+ [![npm](https://img.shields.io/npm/v/koishi-plugin-fake-message?style=flat-square)](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
- 触发格式:`QQ号说内容`
13
+ ## 使用方法
12
14
 
13
- 例如:
14
15
  ```
15
- 123456说你好
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
- 123456说你好 早上好|654321说今天天气真不错 周末要不要出去玩
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号的消息时会收到提示