@zhin.js/plugin-repeater 0.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/LICENSE +21 -0
- package/README.md +40 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +123 -0
- package/lib/index.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 凉菜
|
|
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,40 @@
|
|
|
1
|
+
# @zhin.js/plugin-repeater
|
|
2
|
+
|
|
3
|
+
复读机插件 —— 当群内连续多人发送相同消息时,Bot 自动跟读。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @zhin.js/plugin-repeater
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 配置
|
|
12
|
+
|
|
13
|
+
在 `zhin.config.yml` 中添加:
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
plugins:
|
|
17
|
+
- "@zhin.js/plugin-repeater"
|
|
18
|
+
repeater:
|
|
19
|
+
threshold: 3 # 触发复读的最少人数(默认 3)
|
|
20
|
+
cooldown: 30000 # 同一群冷却时间,毫秒(默认 30s)
|
|
21
|
+
maxLength: 200 # 消息长度上限,超过不触发(默认 200)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 工作原理
|
|
25
|
+
|
|
26
|
+
1. 监听群聊消息,记录每条消息的内容和发送者
|
|
27
|
+
2. 当 **不同** 用户连续发送完全相同的消息达到阈值时,Bot 自动回复该消息
|
|
28
|
+
3. 同一用户连续发送不计入(防止刷屏触发)
|
|
29
|
+
4. 每条消息在同一群只复读一次
|
|
30
|
+
5. 冷却期内不重复触发
|
|
31
|
+
|
|
32
|
+
## 命令
|
|
33
|
+
|
|
34
|
+
| 命令 | 说明 |
|
|
35
|
+
|------|------|
|
|
36
|
+
| `repeater-status` | 查看复读机运行状态 |
|
|
37
|
+
|
|
38
|
+
## 许可
|
|
39
|
+
|
|
40
|
+
MIT
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zhin.js/plugin-repeater
|
|
3
|
+
*
|
|
4
|
+
* 复读机插件 —— 当群内连续多人发送相同消息时自动跟读
|
|
5
|
+
*
|
|
6
|
+
* 规则:
|
|
7
|
+
* - 同一群聊内,连续 N 人(默认 3)发送完全相同的消息后触发复读
|
|
8
|
+
* - 同一用户的连续消息不计入(防止单人刷屏触发)
|
|
9
|
+
* - 每条消息在同一群只复读一次
|
|
10
|
+
* - 可配置中断策略(不同消息会重置计数)
|
|
11
|
+
* - 支持冷却时间防止频繁复读
|
|
12
|
+
*
|
|
13
|
+
* 命令:
|
|
14
|
+
* repeater-status 查看当前复读机状态
|
|
15
|
+
*
|
|
16
|
+
* 配置(zhin.config.yml):
|
|
17
|
+
* ```yaml
|
|
18
|
+
* plugins:
|
|
19
|
+
* - "@zhin.js/plugin-repeater"
|
|
20
|
+
* repeater:
|
|
21
|
+
* threshold: 3
|
|
22
|
+
* cooldown: 30000
|
|
23
|
+
* maxLength: 200
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import { usePlugin, MessageCommand } from "zhin.js";
|
|
27
|
+
const plugin = usePlugin();
|
|
28
|
+
const { logger, root, addCommand, addMiddleware, onDispose, declareSkill } = plugin;
|
|
29
|
+
const configService = root.inject("config");
|
|
30
|
+
const appConfig = configService?.get("zhin.config.yml") || {};
|
|
31
|
+
const config = {
|
|
32
|
+
threshold: 3,
|
|
33
|
+
cooldown: 30_000,
|
|
34
|
+
maxLength: 200,
|
|
35
|
+
...appConfig.repeater,
|
|
36
|
+
};
|
|
37
|
+
const groupStates = new Map();
|
|
38
|
+
const cooldownSet = new Map();
|
|
39
|
+
function getGroupId(message) {
|
|
40
|
+
if (message.type !== "group")
|
|
41
|
+
return null;
|
|
42
|
+
return String(message.$group?.id || message.$target?.id || "");
|
|
43
|
+
}
|
|
44
|
+
function getSenderId(message) {
|
|
45
|
+
return String(message.$sender?.id || "");
|
|
46
|
+
}
|
|
47
|
+
let totalRepeats = 0;
|
|
48
|
+
addMiddleware(async function repeaterMiddleware(message, next) {
|
|
49
|
+
const groupId = getGroupId(message);
|
|
50
|
+
if (!groupId)
|
|
51
|
+
return next();
|
|
52
|
+
const content = (message.$raw || "").trim();
|
|
53
|
+
if (!content || content.length > config.maxLength)
|
|
54
|
+
return next();
|
|
55
|
+
const senderId = getSenderId(message);
|
|
56
|
+
if (!senderId)
|
|
57
|
+
return next();
|
|
58
|
+
const state = groupStates.get(groupId);
|
|
59
|
+
if (state && state.content === content) {
|
|
60
|
+
if (state.users.has(senderId))
|
|
61
|
+
return next();
|
|
62
|
+
state.users.add(senderId);
|
|
63
|
+
state.lastTime = Date.now();
|
|
64
|
+
if (state.users.size >= config.threshold && !state.repeated) {
|
|
65
|
+
const lastCd = cooldownSet.get(groupId);
|
|
66
|
+
if (lastCd && Date.now() - lastCd < config.cooldown) {
|
|
67
|
+
return next();
|
|
68
|
+
}
|
|
69
|
+
state.repeated = true;
|
|
70
|
+
cooldownSet.set(groupId, Date.now());
|
|
71
|
+
totalRepeats++;
|
|
72
|
+
await message.$reply(content);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
groupStates.set(groupId, {
|
|
78
|
+
content,
|
|
79
|
+
users: new Set([senderId]),
|
|
80
|
+
repeated: false,
|
|
81
|
+
lastTime: Date.now(),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return next();
|
|
85
|
+
});
|
|
86
|
+
const staleCleanup = setInterval(() => {
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
const expiry = 5 * 60_000;
|
|
89
|
+
for (const [key, state] of groupStates) {
|
|
90
|
+
if (now - state.lastTime > expiry)
|
|
91
|
+
groupStates.delete(key);
|
|
92
|
+
}
|
|
93
|
+
for (const [key, time] of cooldownSet) {
|
|
94
|
+
if (now - time > config.cooldown * 2)
|
|
95
|
+
cooldownSet.delete(key);
|
|
96
|
+
}
|
|
97
|
+
}, 120_000);
|
|
98
|
+
onDispose(() => {
|
|
99
|
+
clearInterval(staleCleanup);
|
|
100
|
+
groupStates.clear();
|
|
101
|
+
cooldownSet.clear();
|
|
102
|
+
});
|
|
103
|
+
addCommand(new MessageCommand("repeater-status")
|
|
104
|
+
.desc("复读机状态", "查看复读机的运行状态")
|
|
105
|
+
.action(async () => {
|
|
106
|
+
const activeGroups = groupStates.size;
|
|
107
|
+
const lines = [
|
|
108
|
+
"复读机状态",
|
|
109
|
+
`监控群数: ${activeGroups}`,
|
|
110
|
+
`触发阈值: ${config.threshold} 人`,
|
|
111
|
+
`冷却时间: ${config.cooldown / 1000}s`,
|
|
112
|
+
`消息长度上限: ${config.maxLength}`,
|
|
113
|
+
`累计复读: ${totalRepeats} 次`,
|
|
114
|
+
];
|
|
115
|
+
return lines.join("\n");
|
|
116
|
+
}));
|
|
117
|
+
declareSkill({
|
|
118
|
+
description: "复读机:当群内连续多人发送相同消息时,Bot 自动跟读。支持阈值和冷却配置。",
|
|
119
|
+
keywords: ["repeater", "复读", "复读机", "跟读", "echo", "parrot"],
|
|
120
|
+
tags: ["repeater", "fun", "group"],
|
|
121
|
+
});
|
|
122
|
+
logger.info(`插件已加载 (阈值=${config.threshold}人, 冷却=${config.cooldown / 1000}s)`);
|
|
123
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zhin.js/plugin-repeater",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Repeater plugin for Zhin.js — auto-repeat messages when group members echo the same content",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./lib/index.js",
|
|
7
|
+
"types": "./lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"development": "./src/index.ts",
|
|
12
|
+
"import": "./lib/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"lib",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"zhin",
|
|
22
|
+
"bot",
|
|
23
|
+
"plugin",
|
|
24
|
+
"repeater",
|
|
25
|
+
"echo",
|
|
26
|
+
"parrot",
|
|
27
|
+
"chatbot",
|
|
28
|
+
"im"
|
|
29
|
+
],
|
|
30
|
+
"author": {
|
|
31
|
+
"name": "lc-cn",
|
|
32
|
+
"email": "admin@liucl.cn",
|
|
33
|
+
"url": "https://github.com/lc-cn"
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"dependencies": {},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"typescript": "^5.3.3",
|
|
39
|
+
"zhin.js": "1.0.42"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"zhin.js": ">=1.0.42"
|
|
43
|
+
},
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/zhinjs/zhin.git",
|
|
47
|
+
"directory": "plugins/utils/repeater"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public",
|
|
51
|
+
"registry": "https://registry.npmjs.org"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsc --build",
|
|
55
|
+
"clean": "rimraf lib"
|
|
56
|
+
}
|
|
57
|
+
}
|