koishi-plugin-cat-raising 1.0.1 → 1.2.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 +8 -1
- package/lib/index.js +117 -125
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { Context, Schema } from 'koishi';
|
|
2
2
|
export declare const name = "cat-raising";
|
|
3
|
+
/** B站 access_key 配置项 */
|
|
4
|
+
export interface BiliAccessKeyConfig {
|
|
5
|
+
/** Bilibili access_key */
|
|
6
|
+
key: string;
|
|
7
|
+
/** 对此 access_key 的备注,例如所属账号 */
|
|
8
|
+
remark?: string;
|
|
9
|
+
}
|
|
3
10
|
/** 监听群组的配置 */
|
|
4
11
|
export interface MonitorGroupConfig {
|
|
5
12
|
/** 要监听的 QQ 群号 */
|
|
@@ -18,7 +25,7 @@ export interface Config {
|
|
|
18
25
|
/** 用于防复读的历史记录大小 */
|
|
19
26
|
historySize: number;
|
|
20
27
|
/** 用于发送B站弹幕的 access_key 列表 */
|
|
21
|
-
biliAccessKeys:
|
|
28
|
+
biliAccessKeys: BiliAccessKeyConfig[];
|
|
22
29
|
}
|
|
23
30
|
export declare const Config: Schema<Config>;
|
|
24
31
|
/**
|
package/lib/index.js
CHANGED
|
@@ -46,57 +46,62 @@ var Config = import_koishi.Schema.object({
|
|
|
46
46
|
groupId: import_koishi.Schema.string().description("要监听的 QQ 群号").required(),
|
|
47
47
|
sendHelperMessages: import_koishi.Schema.boolean().description("是否在此群内发送“看到啦”之类的辅助/警告消息").default(true)
|
|
48
48
|
})).description("监听的群组列表及其配置").required(),
|
|
49
|
-
historySize: import_koishi.Schema.number().description("
|
|
50
|
-
biliAccessKeys: import_koishi.Schema.array(import_koishi.Schema.
|
|
49
|
+
historySize: import_koishi.Schema.number().description("用于防复读的历史记录大小").default(30).min(5).max(100),
|
|
50
|
+
biliAccessKeys: import_koishi.Schema.array(import_koishi.Schema.object({
|
|
51
|
+
key: import_koishi.Schema.string().description("Bilibili access_key").required(),
|
|
52
|
+
remark: import_koishi.Schema.string().description("对此 access_key 的备注")
|
|
53
|
+
})).description("用于发送B站弹幕的 access_key 列表").default([])
|
|
51
54
|
});
|
|
52
55
|
function preprocessChineseNumerals(text) {
|
|
53
56
|
const numMap = { "零": 0, "一": 1, "二": 2, "两": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9 };
|
|
54
|
-
const unitMap = {
|
|
55
|
-
"十": { value: 10, isSection: false },
|
|
56
|
-
"百": { value: 100, isSection: false },
|
|
57
|
-
"千": { value: 1e3, isSection: false },
|
|
58
|
-
"万": { value: 1e4, isSection: true },
|
|
59
|
-
"亿": { value: 1e8, isSection: true }
|
|
60
|
-
};
|
|
57
|
+
const unitMap = { "十": 10, "百": 100, "千": 1e3, "万": 1e4, "亿": 1e8 };
|
|
61
58
|
const chineseNumRegex = /([一二三四五六七八九十百千万亿两零]+)/g;
|
|
62
59
|
return text.replace(chineseNumRegex, (match) => {
|
|
63
|
-
|
|
64
|
-
let
|
|
65
|
-
|
|
66
|
-
let currentNum = 0;
|
|
67
|
-
for (const char of match) {
|
|
60
|
+
let total = 0, tempVal = 0, sectionVal = 0;
|
|
61
|
+
for (let i = 0; i < match.length; i++) {
|
|
62
|
+
const char = match[i];
|
|
68
63
|
if (numMap[char] !== void 0) {
|
|
69
|
-
|
|
64
|
+
tempVal = numMap[char];
|
|
70
65
|
} else if (unitMap[char]) {
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
const unit = unitMap[char];
|
|
67
|
+
if (unit >= 1e4) {
|
|
68
|
+
sectionVal += tempVal;
|
|
69
|
+
total += sectionVal * unit;
|
|
70
|
+
sectionVal = 0;
|
|
71
|
+
} else {
|
|
72
|
+
sectionVal += (tempVal || 1) * unit;
|
|
78
73
|
}
|
|
74
|
+
tempVal = 0;
|
|
79
75
|
}
|
|
80
76
|
}
|
|
81
|
-
total +=
|
|
77
|
+
total += sectionVal + tempVal;
|
|
82
78
|
return String(total);
|
|
83
79
|
});
|
|
84
80
|
}
|
|
85
81
|
__name(preprocessChineseNumerals, "preprocessChineseNumerals");
|
|
86
82
|
function extractAllRoomIds(text) {
|
|
87
83
|
const sanitizedText = text.replace(/<[^>]+>/g, "");
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
const explicitPattern = /(?:播间号|房间号|直播间)[::\s]*(\d{3,15})/gi;
|
|
85
|
+
const explicitIds = /* @__PURE__ */ new Set();
|
|
86
|
+
for (const match of sanitizedText.matchAll(explicitPattern)) {
|
|
87
|
+
if (match[1]) explicitIds.add(match[1]);
|
|
88
|
+
}
|
|
89
|
+
if (explicitIds.size > 0) return Array.from(explicitIds);
|
|
90
|
+
const genericPattern = /\b(\d{3,15})\b/g;
|
|
91
|
+
const allNumericCandidates = /* @__PURE__ */ new Set();
|
|
92
|
+
for (const match of sanitizedText.matchAll(genericPattern)) {
|
|
93
|
+
if (match[1]) allNumericCandidates.add(match[1]);
|
|
94
|
+
}
|
|
95
|
+
if (allNumericCandidates.size <= 1) return Array.from(allNumericCandidates);
|
|
96
|
+
const preprocessedText = preprocessChineseNumerals(sanitizedText);
|
|
97
|
+
const rewards = extractRewards(preprocessedText);
|
|
98
|
+
const rewardAmounts = new Set(rewards.map((r) => String(r.amount)));
|
|
99
|
+
let filteredIds = Array.from(allNumericCandidates).filter((id) => !rewardAmounts.has(id));
|
|
100
|
+
if (filteredIds.length > 1) {
|
|
101
|
+
filteredIds.sort((a, b) => b.length - a.length);
|
|
102
|
+
return [filteredIds[0]];
|
|
98
103
|
}
|
|
99
|
-
return
|
|
104
|
+
return filteredIds;
|
|
100
105
|
}
|
|
101
106
|
__name(extractAllRoomIds, "extractAllRoomIds");
|
|
102
107
|
function extractDateTime(line) {
|
|
@@ -119,14 +124,31 @@ function extractDateTime(line) {
|
|
|
119
124
|
__name(extractDateTime, "extractDateTime");
|
|
120
125
|
function extractRewards(line) {
|
|
121
126
|
const rewards = [];
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
const foundAmounts = /* @__PURE__ */ new Set();
|
|
128
|
+
const addReward = /* @__PURE__ */ __name((amount, condition) => {
|
|
129
|
+
if (!isNaN(amount) && amount > 0 && !foundAmounts.has(amount)) {
|
|
130
|
+
rewards.push({ amount, condition });
|
|
131
|
+
foundAmounts.add(amount);
|
|
132
|
+
}
|
|
133
|
+
}, "addReward");
|
|
134
|
+
const strongRegex = /(?:(\d{1,2})\s*级(?:灯牌)?\s*)?(?:(发|掉落)\s*)?(?:(神金|钻石|猫猫钻)\s*(\d+\.?\d*w?|\b\d{3,5}\b)|(\d+\.?\d*w|\b\d{3,5}\b)\s*(?:神金|钻石|猫猫钻|w))/gi;
|
|
135
|
+
for (const match of line.matchAll(strongRegex)) {
|
|
125
136
|
const condition = match[1] ? `${match[1]}级灯牌` : "无限制";
|
|
126
|
-
const amountStr = (match[
|
|
137
|
+
const amountStr = (match[4] || match[5] || "").toLowerCase();
|
|
127
138
|
const amount = amountStr.includes("w") ? parseFloat(amountStr.replace("w", "")) * 1e4 : parseFloat(amountStr);
|
|
128
|
-
|
|
129
|
-
|
|
139
|
+
addReward(amount, condition);
|
|
140
|
+
}
|
|
141
|
+
if (rewards.length === 0) {
|
|
142
|
+
const conditionMatch = line.match(/(\d{1,2})\s*级(?:灯牌)?/);
|
|
143
|
+
if (conditionMatch) {
|
|
144
|
+
const condition = conditionMatch[0];
|
|
145
|
+
const conditionLevel = conditionMatch[1];
|
|
146
|
+
for (const numMatch of line.matchAll(/\b(\d{3,5})\b/g)) {
|
|
147
|
+
if (numMatch[1] !== conditionLevel) {
|
|
148
|
+
const amount = parseFloat(numMatch[1]);
|
|
149
|
+
addReward(amount, condition);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
130
152
|
}
|
|
131
153
|
}
|
|
132
154
|
return rewards;
|
|
@@ -149,58 +171,49 @@ __name(parseEventFromText, "parseEventFromText");
|
|
|
149
171
|
var HARD_REJECTION_KEYWORDS = ["发言榜单", "投稿数:"];
|
|
150
172
|
var REJECTION_KEYWORDS = ["签到", "打卡"];
|
|
151
173
|
var OVERRIDE_KEYWORDS = ["神金", "发"];
|
|
174
|
+
var CHECK_IN_REJECTION_REGEX = /\b\d{2,3}\s*\+/;
|
|
152
175
|
var TRIGGER_REGEX = /神金|发|掉落|猫猫钻|w|\b\d{3,5}\b|一千|一百|十|九|八|七|六|五|四|三|两|二|一/i;
|
|
153
176
|
var BILI_APPKEY = "4409e2ce8ffd12b8";
|
|
154
177
|
var BILI_APPSECRET = "59b43e04ad6965f34319062b478f83dd";
|
|
155
178
|
function signBilibiliParams(params, appSecret) {
|
|
156
179
|
const sortedKeys = Object.keys(params).sort();
|
|
157
180
|
const queryString = sortedKeys.map((key) => `${key}=${params[key]}`).join("&");
|
|
158
|
-
|
|
159
|
-
return sign;
|
|
181
|
+
return crypto.createHash("md5").update(queryString + appSecret).digest("hex");
|
|
160
182
|
}
|
|
161
183
|
__name(signBilibiliParams, "signBilibiliParams");
|
|
162
|
-
async function sendBilibiliDanmaku(ctx,
|
|
163
|
-
|
|
164
|
-
ctx.logger.info("[弹幕] 未配置 access_key,跳过发送弹幕。");
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
const accessKey = config.biliAccessKeys[Math.floor(Math.random() * config.biliAccessKeys.length)];
|
|
184
|
+
async function sendBilibiliDanmaku(ctx, keyConfig, roomId, message) {
|
|
185
|
+
const MAX_RETRIES = 4, RETRY_DELAY_MS = 3e3, FREQUENCY_LIMIT_KEYWORD = "频率过快";
|
|
168
186
|
const url = "https://api.live.bilibili.com/xlive/app-room/v1/dM/sendmsg";
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
appkey: BILI_APPKEY,
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
headers: {
|
|
193
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
194
|
-
"User-Agent": "Mozilla/5.0 BiliDroid/6.73.1 (bbcallen@gmail.com) os/android model/Mi 10 Pro mobi_app/android build/6731100 channel/xiaomi innerVer/6731110 osVer/12 network/2"
|
|
187
|
+
const logIdentifier = keyConfig.remark || keyConfig.key.slice(0, 8);
|
|
188
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
189
|
+
if (attempt > 0) await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
|
190
|
+
const ts = Math.floor(Date.now() / 1e3);
|
|
191
|
+
const baseParams = { access_key: keyConfig.key, actionKey: "appkey", appkey: BILI_APPKEY, cid: roomId, msg: message, rnd: ts, color: "16777215", fontsize: "25", mode: "1", ts };
|
|
192
|
+
const sign = signBilibiliParams(baseParams, BILI_APPSECRET);
|
|
193
|
+
const params = { ...baseParams, sign };
|
|
194
|
+
const formData = new import_url.URLSearchParams();
|
|
195
|
+
for (const key in params) formData.append(key, params[key]);
|
|
196
|
+
try {
|
|
197
|
+
const response = await ctx.http.post(url, formData, { headers: { "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 BiliDroid/6.73.1" } });
|
|
198
|
+
if (response.code === 0) {
|
|
199
|
+
ctx.logger.info(`[弹幕] [${logIdentifier}] 成功向直播间 ${roomId} 发送弹幕${attempt > 0 ? ` (重试 ${attempt} 次后)` : `: "${message}"`}`);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (response.message?.includes(FREQUENCY_LIMIT_KEYWORD)) {
|
|
203
|
+
if (attempt < MAX_RETRIES) {
|
|
204
|
+
ctx.logger.warn(`[弹幕] [${logIdentifier}] 发送频率过快 (尝试 ${attempt + 1}/${MAX_RETRIES + 1})。准备重试...`);
|
|
205
|
+
continue;
|
|
206
|
+
} else {
|
|
207
|
+
ctx.logger.warn(`[弹幕] [${logIdentifier}] 发送频率过快,已达最大重试次数 (${MAX_RETRIES}),放弃发送。`);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
195
210
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
211
|
+
ctx.logger.warn(`[弹幕] [${logIdentifier}] 发送失败,直播间 ${roomId}。原因: ${response.message || "未知错误"}`);
|
|
212
|
+
return;
|
|
213
|
+
} catch (error) {
|
|
214
|
+
ctx.logger.error(`[弹幕] [${logIdentifier}] 发送请求时发生网络错误 (尝试 ${attempt + 1}),直播间 ${roomId}:`, error);
|
|
215
|
+
return;
|
|
201
216
|
}
|
|
202
|
-
} catch (error) {
|
|
203
|
-
ctx.logger.error(`[弹幕] 发送请求时发生网络错误,直播间 ${roomId}:`, error);
|
|
204
217
|
}
|
|
205
218
|
}
|
|
206
219
|
__name(sendBilibiliDanmaku, "sendBilibiliDanmaku");
|
|
@@ -221,47 +234,40 @@ async function fetchBilibiliInfo(ctx, roomId) {
|
|
|
221
234
|
__name(fetchBilibiliInfo, "fetchBilibiliInfo");
|
|
222
235
|
function apply(ctx, config) {
|
|
223
236
|
const forwardedHistory = [];
|
|
224
|
-
const warningMessageMap = /* @__PURE__ */ new Map();
|
|
225
237
|
ctx.on("message", async (session) => {
|
|
226
238
|
const groupConfig = config.monitorGroups.find((g) => g.groupId === session.channelId);
|
|
227
239
|
if (!groupConfig) return;
|
|
228
240
|
const strippedContent = session.stripped.content;
|
|
229
241
|
if (!strippedContent.trim()) return;
|
|
230
242
|
if (HARD_REJECTION_KEYWORDS.some((keyword) => strippedContent.includes(keyword))) return;
|
|
243
|
+
if (CHECK_IN_REJECTION_REGEX.test(strippedContent)) return;
|
|
231
244
|
if (!TRIGGER_REGEX.test(strippedContent)) return;
|
|
232
245
|
const roomIds = extractAllRoomIds(session.content);
|
|
233
246
|
if (roomIds.length !== 1) {
|
|
234
|
-
if (roomIds.length > 1) ctx.logger.info(`[忽略]
|
|
247
|
+
if (roomIds.length > 1) ctx.logger.info(`[忽略] 消息包含多个可能的房间号: ${roomIds.join(", ")}`);
|
|
235
248
|
return;
|
|
236
249
|
}
|
|
237
250
|
const roomId = roomIds[0];
|
|
238
251
|
const preprocessedMessage = preprocessChineseNumerals(strippedContent);
|
|
239
|
-
|
|
240
|
-
if (hasRejectionKeyword && !OVERRIDE_KEYWORDS.some((keyword) => preprocessedMessage.includes(keyword))) return;
|
|
252
|
+
if (REJECTION_KEYWORDS.some((k) => preprocessedMessage.includes(k)) && !OVERRIDE_KEYWORDS.some((k) => preprocessedMessage.includes(k))) return;
|
|
241
253
|
const parsedEvent = parseEventFromText(preprocessedMessage);
|
|
242
254
|
if (!parsedEvent) return;
|
|
243
|
-
|
|
244
|
-
const hasTime = parsedEvent.dateTime !== "时间未知";
|
|
245
|
-
if (!hasStrongContext && !hasTime) return;
|
|
246
|
-
const { dateTime } = parsedEvent;
|
|
247
|
-
if (forwardedHistory.some((entry) => entry.roomId === roomId && entry.dateTime === dateTime)) {
|
|
248
|
-
ctx.logger.info(`[防复读] 检测到重复活动: 房间=${roomId}, 时间=${dateTime}`);
|
|
249
|
-
if (groupConfig.sendHelperMessages) {
|
|
250
|
-
try {
|
|
251
|
-
const [warningId] = await session.send(`看到啦看到啦,不要发那么多次嘛~`);
|
|
252
|
-
if (warningId) warningMessageMap.set(session.messageId, warningId);
|
|
253
|
-
} catch (e) {
|
|
254
|
-
ctx.logger.warn("[消息] 发送重复警告失败:", e);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
255
|
+
if (!/神金|发|w/i.test(preprocessedMessage) && parsedEvent.dateTime === "时间未知") return;
|
|
259
256
|
const biliInfo = await fetchBilibiliInfo(ctx, roomId);
|
|
260
257
|
if (!biliInfo) return;
|
|
261
258
|
let helperMessageId;
|
|
262
259
|
if (groupConfig.sendHelperMessages) {
|
|
263
|
-
|
|
260
|
+
try {
|
|
261
|
+
[helperMessageId] = await session.send(`直播间: ${roomId}
|
|
264
262
|
投稿数: ${biliInfo.videoCount}`);
|
|
263
|
+
} catch (e) {
|
|
264
|
+
ctx.logger.warn("[消息] 发送辅助信息失败:", e);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const { dateTime } = parsedEvent;
|
|
268
|
+
if (forwardedHistory.some((entry) => entry.roomId === roomId && entry.dateTime === dateTime)) {
|
|
269
|
+
ctx.logger.info(`[防复读] 检测到重复活动,已发送辅助信息,跳过转发: 房间=${roomId}, 时间=${dateTime}`);
|
|
270
|
+
return;
|
|
265
271
|
}
|
|
266
272
|
try {
|
|
267
273
|
const forwardMessage = `${session.content}
|
|
@@ -269,24 +275,20 @@ function apply(ctx, config) {
|
|
|
269
275
|
---
|
|
270
276
|
投稿数: ${biliInfo.videoCount}`;
|
|
271
277
|
const [forwardedMessageId] = config.isGroup ? await session.bot.sendMessage(config.targetQQ, forwardMessage) : await session.bot.sendPrivateMessage(config.targetQQ, forwardMessage);
|
|
272
|
-
forwardedHistory.push({
|
|
273
|
-
originalMessageId: session.messageId,
|
|
274
|
-
forwardedMessageId,
|
|
275
|
-
helperMessageId,
|
|
276
|
-
// helperMessageId 可能是 undefined
|
|
277
|
-
roomId,
|
|
278
|
-
dateTime
|
|
279
|
-
});
|
|
278
|
+
forwardedHistory.push({ originalMessageId: session.messageId, forwardedMessageId, helperMessageId, roomId, dateTime });
|
|
280
279
|
if (forwardedHistory.length > config.historySize) forwardedHistory.shift();
|
|
281
|
-
|
|
280
|
+
if (config.biliAccessKeys?.length > 0) {
|
|
281
|
+
ctx.logger.info(`[弹幕] 准备为 ${config.biliAccessKeys.length} 个账号发送弹幕到直播间 ${roomId}...`);
|
|
282
|
+
const danmakuPromises = config.biliAccessKeys.map((keyConfig) => sendBilibiliDanmaku(ctx, keyConfig, roomId, "喵喵喵"));
|
|
283
|
+
Promise.allSettled(danmakuPromises);
|
|
284
|
+
}
|
|
282
285
|
} catch (error) {
|
|
283
286
|
session.send("🐱 - 转发失败,请检查目标QQ/群号配置是否正确");
|
|
284
287
|
ctx.logger.error("[转发] 失败:", error);
|
|
285
288
|
}
|
|
286
289
|
});
|
|
287
290
|
ctx.on("message-deleted", async (session) => {
|
|
288
|
-
|
|
289
|
-
if (!isMonitored) return;
|
|
291
|
+
if (!config.monitorGroups.some((g) => g.groupId === session.channelId)) return;
|
|
290
292
|
const originalMessageId = session.messageId;
|
|
291
293
|
const entryIndex = forwardedHistory.findIndex((entry) => entry.originalMessageId === originalMessageId);
|
|
292
294
|
if (entryIndex !== -1) {
|
|
@@ -299,7 +301,8 @@ function apply(ctx, config) {
|
|
|
299
301
|
}
|
|
300
302
|
}
|
|
301
303
|
try {
|
|
302
|
-
|
|
304
|
+
const targetChannel = config.isGroup ? config.targetQQ : `private:${config.targetQQ}`;
|
|
305
|
+
await session.bot.deleteMessage(targetChannel, entry.forwardedMessageId);
|
|
303
306
|
} catch (e) {
|
|
304
307
|
ctx.logger.warn(`[撤回] 转发消息 (ID: ${entry.forwardedMessageId}) 失败:`, e);
|
|
305
308
|
} finally {
|
|
@@ -307,17 +310,6 @@ function apply(ctx, config) {
|
|
|
307
310
|
ctx.logger.info(`[撤回] 已联动撤回与源消息 ${originalMessageId} 相关的转发。`);
|
|
308
311
|
}
|
|
309
312
|
}
|
|
310
|
-
if (warningMessageMap.has(originalMessageId)) {
|
|
311
|
-
const warningMessageId = warningMessageMap.get(originalMessageId);
|
|
312
|
-
try {
|
|
313
|
-
await session.bot.deleteMessage(session.channelId, warningMessageId);
|
|
314
|
-
} catch (e) {
|
|
315
|
-
ctx.logger.warn(`[撤回] 警告消息 (ID: ${warningMessageId}) 失败:`, e);
|
|
316
|
-
} finally {
|
|
317
|
-
warningMessageMap.delete(originalMessageId);
|
|
318
|
-
ctx.logger.info(`[撤回] 已联动撤回与源消息 ${originalMessageId} 相关的警告。`);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
313
|
});
|
|
322
314
|
}
|
|
323
315
|
__name(apply, "apply");
|