koishi-plugin-rinngo-antirecall 0.0.1
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 +14 -0
- package/lib/index.js +185 -0
- package/package.json +20 -0
- package/readme.md +5 -0
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Context, Schema } from 'koishi';
|
|
2
|
+
export declare const name = "rinngo-antirecall";
|
|
3
|
+
export interface Config {
|
|
4
|
+
groups: string[];
|
|
5
|
+
users: string[];
|
|
6
|
+
targetGroups: string[];
|
|
7
|
+
targetUsers: string[];
|
|
8
|
+
cacheTimeout: number;
|
|
9
|
+
botPlatform: string;
|
|
10
|
+
botId: string;
|
|
11
|
+
contextCount: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const Config: Schema<Config>;
|
|
14
|
+
export declare function apply(pluginCtx: Context, pluginConfig: Config): void;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name2 in all)
|
|
8
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
Config: () => Config,
|
|
24
|
+
apply: () => apply,
|
|
25
|
+
name: () => name
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(src_exports);
|
|
28
|
+
var import_koishi = require("koishi");
|
|
29
|
+
var name = "rinngo-antirecall";
|
|
30
|
+
var Config = import_koishi.Schema.object({
|
|
31
|
+
groups: import_koishi.Schema.array(import_koishi.Schema.string()).description("监听群组"),
|
|
32
|
+
users: import_koishi.Schema.array(import_koishi.Schema.string()).description("监听私聊"),
|
|
33
|
+
targetGroups: import_koishi.Schema.array(import_koishi.Schema.string()).description("发送目标群组"),
|
|
34
|
+
targetUsers: import_koishi.Schema.array(import_koishi.Schema.string()).description("发送目标私聊"),
|
|
35
|
+
cacheTimeout: import_koishi.Schema.number().default(5 * 60 * 1e3).description("消息缓存时间(毫秒)"),
|
|
36
|
+
botPlatform: import_koishi.Schema.string().default("onebot").description("机器人 adapter"),
|
|
37
|
+
botId: import_koishi.Schema.string().description("机器人 id"),
|
|
38
|
+
contextCount: import_koishi.Schema.number().default(10).description("撤回时获取的上文消息数量")
|
|
39
|
+
});
|
|
40
|
+
var Cache = class {
|
|
41
|
+
static {
|
|
42
|
+
__name(this, "Cache");
|
|
43
|
+
}
|
|
44
|
+
m = /* @__PURE__ */ new Map();
|
|
45
|
+
get(key, defaultValue = void 0) {
|
|
46
|
+
return this.m.get(key) ?? defaultValue;
|
|
47
|
+
}
|
|
48
|
+
set(key, value, timeout = 0) {
|
|
49
|
+
this.m.set(key, value);
|
|
50
|
+
if (timeout > 0) {
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
this.m.delete(key);
|
|
53
|
+
}, timeout);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var SessionHistory = class {
|
|
58
|
+
static {
|
|
59
|
+
__name(this, "SessionHistory");
|
|
60
|
+
}
|
|
61
|
+
history = /* @__PURE__ */ new Map();
|
|
62
|
+
// key -> messageId[]
|
|
63
|
+
maxLength = 200;
|
|
64
|
+
// 最大保留消息数,防止无限增长
|
|
65
|
+
// 添加消息 ID 到会话历史
|
|
66
|
+
add(sessionKey, messageId, timeout, onExpire) {
|
|
67
|
+
if (!this.history.has(sessionKey)) {
|
|
68
|
+
this.history.set(sessionKey, []);
|
|
69
|
+
}
|
|
70
|
+
const arr = this.history.get(sessionKey);
|
|
71
|
+
arr.push(messageId);
|
|
72
|
+
if (arr.length > this.maxLength) arr.shift();
|
|
73
|
+
setTimeout(() => {
|
|
74
|
+
const currentArr = this.history.get(sessionKey);
|
|
75
|
+
if (currentArr) {
|
|
76
|
+
const idx = currentArr.indexOf(messageId);
|
|
77
|
+
if (idx !== -1) currentArr.splice(idx, 1);
|
|
78
|
+
if (currentArr.length === 0) this.history.delete(sessionKey);
|
|
79
|
+
}
|
|
80
|
+
onExpire(messageId);
|
|
81
|
+
}, timeout);
|
|
82
|
+
}
|
|
83
|
+
// 获取某条消息之前的 N 条消息 ID(包含自身)
|
|
84
|
+
getPrevious(sessionKey, messageId, count) {
|
|
85
|
+
const arr = this.history.get(sessionKey);
|
|
86
|
+
if (!arr) return [];
|
|
87
|
+
const idx = arr.indexOf(messageId);
|
|
88
|
+
if (idx === -1) return [messageId];
|
|
89
|
+
const start = Math.max(0, idx - count);
|
|
90
|
+
return arr.slice(start, idx + 1);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
var cache;
|
|
94
|
+
var sessionHistory;
|
|
95
|
+
var ctx;
|
|
96
|
+
var config;
|
|
97
|
+
function apply(pluginCtx, pluginConfig) {
|
|
98
|
+
ctx = pluginCtx;
|
|
99
|
+
config = pluginConfig;
|
|
100
|
+
cache = new Cache();
|
|
101
|
+
sessionHistory = new SessionHistory();
|
|
102
|
+
ctx.on("message-created", async (session) => {
|
|
103
|
+
if (!isHit(session.guildId, session.userId)) return;
|
|
104
|
+
const sessionKey = getSessionKey(session.guildId, session.userId);
|
|
105
|
+
const messageId = session.messageId;
|
|
106
|
+
const msgData = {
|
|
107
|
+
msg: session.event.message.elements,
|
|
108
|
+
groupId: session.guildId || "",
|
|
109
|
+
userName: session.username
|
|
110
|
+
};
|
|
111
|
+
cacheMsg(messageId, msgData, config.cacheTimeout);
|
|
112
|
+
sessionHistory.add(sessionKey, messageId, config.cacheTimeout, () => {
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
ctx.on("message-deleted", async (session) => {
|
|
116
|
+
if (!isHit(session.guildId, session.userId)) return;
|
|
117
|
+
const sessionKey = getSessionKey(session.guildId, session.userId);
|
|
118
|
+
const messageId = session.messageId;
|
|
119
|
+
const recalledMsg = getCache(messageId);
|
|
120
|
+
if (!recalledMsg) return;
|
|
121
|
+
const prevIds = sessionHistory.getPrevious(sessionKey, messageId, config.contextCount);
|
|
122
|
+
const messages = [];
|
|
123
|
+
for (const id of prevIds) {
|
|
124
|
+
const data = getCache(id);
|
|
125
|
+
if (data) {
|
|
126
|
+
messages.push(data);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (messages.length === 0) messages.push(recalledMsg);
|
|
130
|
+
const forwardNodes = [];
|
|
131
|
+
const sessionIdText = session.guildId ? `群组: ${session.guildId}` : `私聊用户: ${session.userId}`;
|
|
132
|
+
forwardNodes.push((0, import_koishi.h)("message", sessionIdText));
|
|
133
|
+
for (const msg of messages) {
|
|
134
|
+
const sender = msg.userName || (msg.groupId ? "群成员" : "用户");
|
|
135
|
+
const prefix = sender + (msg.groupId ? ` (${msg.groupId})` : "");
|
|
136
|
+
forwardNodes.push((0, import_koishi.h)("message", [import_koishi.h.text(`${prefix}:
|
|
137
|
+
`), ...msg.msg]));
|
|
138
|
+
}
|
|
139
|
+
forward(forwardNodes);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
__name(apply, "apply");
|
|
143
|
+
function isHit(guildId, userId) {
|
|
144
|
+
if (guildId) return config.groups.includes(guildId);
|
|
145
|
+
return config.users.includes(userId);
|
|
146
|
+
}
|
|
147
|
+
__name(isHit, "isHit");
|
|
148
|
+
function getSessionKey(guildId, userId) {
|
|
149
|
+
if (guildId) return `group:${guildId}`;
|
|
150
|
+
return `user:${userId}`;
|
|
151
|
+
}
|
|
152
|
+
__name(getSessionKey, "getSessionKey");
|
|
153
|
+
function cacheMsg(key, value, timeout) {
|
|
154
|
+
cache.set(key, value, timeout);
|
|
155
|
+
}
|
|
156
|
+
__name(cacheMsg, "cacheMsg");
|
|
157
|
+
function getCache(key) {
|
|
158
|
+
return cache.get(key);
|
|
159
|
+
}
|
|
160
|
+
__name(getCache, "getCache");
|
|
161
|
+
async function forward(nodes) {
|
|
162
|
+
try {
|
|
163
|
+
const bot = ctx.bots[`${config.botPlatform}:${config.botId}`];
|
|
164
|
+
if (!bot) {
|
|
165
|
+
ctx.logger.warn("撤回消息转发失败,未找到可用 bot");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const forwardMsg = (0, import_koishi.h)("message", { forward: true }, nodes);
|
|
169
|
+
for (const guildId of config.targetGroups) {
|
|
170
|
+
await bot.sendMessage(guildId, forwardMsg, guildId);
|
|
171
|
+
}
|
|
172
|
+
for (const userId of config.targetUsers) {
|
|
173
|
+
await bot.sendPrivateMessage(userId, forwardMsg);
|
|
174
|
+
}
|
|
175
|
+
} catch (e) {
|
|
176
|
+
ctx.logger.error(e);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
__name(forward, "forward");
|
|
180
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
181
|
+
0 && (module.exports = {
|
|
182
|
+
Config,
|
|
183
|
+
apply,
|
|
184
|
+
name
|
|
185
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "koishi-plugin-rinngo-antirecall",
|
|
3
|
+
"description": "防止撤回,合并消息转发到指定群聊。",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"typings": "lib/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib",
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"chatbot",
|
|
14
|
+
"koishi",
|
|
15
|
+
"plugin"
|
|
16
|
+
],
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"koishi": "^4.18.11"
|
|
19
|
+
}
|
|
20
|
+
}
|