koishi-plugin-cat-raising 0.0.8 → 0.0.10
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.js +123 -30
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -32,37 +32,127 @@ var Config = import_koishi.Schema.object({
|
|
|
32
32
|
isGroup: import_koishi.Schema.boolean().description("是否为QQ群").default(false),
|
|
33
33
|
monitorGroup: import_koishi.Schema.string().description("监听的群号(只检测此群的消息)").required()
|
|
34
34
|
});
|
|
35
|
+
function extractAllRoomIds(text) {
|
|
36
|
+
const patterns = [
|
|
37
|
+
/(?:播间号|房间号|直播间)[::\s]*(\d{6,15})/g,
|
|
38
|
+
/\b(\d{8,15})\b/g
|
|
39
|
+
];
|
|
40
|
+
const foundIds = /* @__PURE__ */ new Set();
|
|
41
|
+
for (const pattern of patterns) {
|
|
42
|
+
const matches = text.matchAll(pattern);
|
|
43
|
+
for (const match of matches) {
|
|
44
|
+
if (match[1]) foundIds.add(match[1]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return Array.from(foundIds);
|
|
48
|
+
}
|
|
49
|
+
__name(extractAllRoomIds, "extractAllRoomIds");
|
|
50
|
+
function extractDateTime(line) {
|
|
51
|
+
let match = line.match(/(\d{1,2})\s*[月.]\s*(\d{1,2})\s*日?/);
|
|
52
|
+
if (match) return `${match[1]}月${match[2]}日`;
|
|
53
|
+
match = line.match(/每晚\s*(\d{1,2})\s*点/);
|
|
54
|
+
if (match) return `每晚 ${match[1].padStart(2, "0")}:00`;
|
|
55
|
+
match = line.match(/(\d{1,2}\s*月\s*(?:上|中|下)旬)/);
|
|
56
|
+
if (match) return match[1];
|
|
57
|
+
match = line.match(/(\d{1,2})[::.点时]\s*(\d{1,2})/);
|
|
58
|
+
if (match) return `${match[1].padStart(2, "0")}:${match[2].padStart(2, "0")}`;
|
|
59
|
+
match = line.match(/(\d{1,2})\s*点\s*半/);
|
|
60
|
+
if (match) return `${match[1].padStart(2, "0")}:30`;
|
|
61
|
+
match = line.match(/(\d{1,2})\s*分/);
|
|
62
|
+
if (match) {
|
|
63
|
+
const now = /* @__PURE__ */ new Date();
|
|
64
|
+
const minuteVal = parseInt(match[1]);
|
|
65
|
+
let hourVal = now.getMinutes() > minuteVal ? now.getHours() + 1 : now.getHours();
|
|
66
|
+
hourVal = hourVal % 24;
|
|
67
|
+
return `${hourVal.toString().padStart(2, "0")}:${match[1].padStart(2, "0")}`;
|
|
68
|
+
}
|
|
69
|
+
match = line.match(/.*?(?:生日|周年|新衣|活动).*/);
|
|
70
|
+
if (match) return match[0].trim();
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
__name(extractDateTime, "extractDateTime");
|
|
74
|
+
function extractRewards(line) {
|
|
75
|
+
const rewards = [];
|
|
76
|
+
const regex = /(?:(\d{1,2})\s*级(?:灯牌)?\s*)?(?:发\s*)?(\d+\.?\d*w?\+?)(?:神金|钻石|猫猫钻)?/gi;
|
|
77
|
+
let match;
|
|
78
|
+
while ((match = regex.exec(line)) !== null) {
|
|
79
|
+
if (line.includes("签到") && !line.match(/发|神金|猫猫钻/)) continue;
|
|
80
|
+
const condition = match[1] ? `${match[1]}级灯牌` : "无限制";
|
|
81
|
+
let amountStr = match[2].toLowerCase();
|
|
82
|
+
let amount = 0;
|
|
83
|
+
if (amountStr.includes("w")) {
|
|
84
|
+
amount = parseFloat(amountStr.replace("w", "")) * 1e4;
|
|
85
|
+
} else {
|
|
86
|
+
amount = parseFloat(amountStr);
|
|
87
|
+
}
|
|
88
|
+
rewards.push({ amount, condition });
|
|
89
|
+
}
|
|
90
|
+
return rewards;
|
|
91
|
+
}
|
|
92
|
+
__name(extractRewards, "extractRewards");
|
|
93
|
+
function parseEvents(text) {
|
|
94
|
+
const lines = text.split("\n").filter((line) => line.trim() !== "");
|
|
95
|
+
const events = [];
|
|
96
|
+
let currentDateTime = null;
|
|
97
|
+
for (const line of lines) {
|
|
98
|
+
const foundDateTime = extractDateTime(line);
|
|
99
|
+
const foundRewards = extractRewards(line);
|
|
100
|
+
if (foundDateTime) {
|
|
101
|
+
currentDateTime = foundDateTime;
|
|
102
|
+
}
|
|
103
|
+
if (foundRewards.length > 0) {
|
|
104
|
+
const eventTime = currentDateTime || "时间未知";
|
|
105
|
+
events.push({ dateTime: eventTime, rewards: foundRewards });
|
|
106
|
+
if (foundDateTime) currentDateTime = null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return events.length > 0 ? events : null;
|
|
110
|
+
}
|
|
111
|
+
__name(parseEvents, "parseEvents");
|
|
35
112
|
function apply(ctx, config) {
|
|
36
|
-
const
|
|
37
|
-
const HISTORY_SIZE =
|
|
38
|
-
const
|
|
113
|
+
const forwardedHistory = [];
|
|
114
|
+
const HISTORY_SIZE = 30;
|
|
115
|
+
const REJECTION_KEYWORDS = ["签到"];
|
|
116
|
+
const OVERRIDE_KEYWORDS = ["神金", "发"];
|
|
39
117
|
ctx.on("message", async (session) => {
|
|
118
|
+
if (session.channelId !== config.monitorGroup) return;
|
|
40
119
|
const originalMessageContent = session.content;
|
|
41
120
|
const messageForChecks = session.stripped.content;
|
|
42
121
|
const messageId = session.messageId;
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
122
|
+
const hasRejectionKeyword = REJECTION_KEYWORDS.some((keyword) => messageForChecks.includes(keyword));
|
|
123
|
+
if (hasRejectionKeyword) {
|
|
124
|
+
const hasOverrideKeyword = OVERRIDE_KEYWORDS.some((keyword) => messageForChecks.includes(keyword));
|
|
125
|
+
if (!hasOverrideKeyword) {
|
|
126
|
+
ctx.logger.info(`消息包含拒绝关键词且无覆盖词,已忽略: ${messageForChecks.substring(0, 50)}...`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const roomIds = extractAllRoomIds(messageForChecks);
|
|
131
|
+
if (roomIds.length > 1) {
|
|
132
|
+
session.send(`检测到多个直播间号 (${roomIds.join(", ")}),为避免信息混淆,已停止处理。`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const roomId = roomIds.length === 1 ? roomIds[0] : null;
|
|
136
|
+
const parsedEvents = parseEvents(messageForChecks);
|
|
137
|
+
if (!parsedEvents || !roomId) {
|
|
138
|
+
if (messageForChecks.match(/神金|w|发|掉落|\d{4,}/)) {
|
|
139
|
+
ctx.logger.info(`消息可能为神金信息但无法完整解析(缺少房间号或事件),已忽略: ${messageForChecks.substring(0, 50)}...`);
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (forwardedHistory.some((entry) => entry.originalContent === originalMessageContent)) {
|
|
144
|
+
session.send("这个消息刚刚已经发过啦~");
|
|
51
145
|
return;
|
|
52
146
|
}
|
|
53
147
|
let biliInfo = "";
|
|
54
148
|
try {
|
|
55
149
|
const roomInfoUrl = `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${roomId}`;
|
|
56
150
|
const roomInfo = await ctx.http.get(roomInfoUrl);
|
|
57
|
-
if (roomInfo.code !== 0 || !roomInfo.data
|
|
58
|
-
throw new Error("无法通过直播间号获取UID");
|
|
59
|
-
}
|
|
151
|
+
if (roomInfo.code !== 0 || !roomInfo.data?.uid) throw new Error("无法通过直播间号获取UID");
|
|
60
152
|
const uid = roomInfo.data.uid;
|
|
61
153
|
const statsUrl = `https://api.bilibili.com/x/space/navnum?mid=${uid}`;
|
|
62
154
|
const statsInfo = await ctx.http.get(statsUrl);
|
|
63
|
-
if (statsInfo.code !== 0 ||
|
|
64
|
-
throw new Error("无法获取用户投稿数");
|
|
65
|
-
}
|
|
155
|
+
if (statsInfo.code !== 0 || statsInfo.data?.video === void 0) throw new Error("无法获取用户投稿数");
|
|
66
156
|
const videoCount = statsInfo.data.video;
|
|
67
157
|
biliInfo = `
|
|
68
158
|
|
|
@@ -87,10 +177,14 @@ function apply(ctx, config) {
|
|
|
87
177
|
const result = await session.bot.sendPrivateMessage(config.targetQQ, forwardMessage);
|
|
88
178
|
forwardedMessageId = result[0];
|
|
89
179
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
180
|
+
const newEntry = {
|
|
181
|
+
originalMessageId: messageId,
|
|
182
|
+
forwardedMessageId,
|
|
183
|
+
originalContent: originalMessageContent
|
|
184
|
+
};
|
|
185
|
+
forwardedHistory.push(newEntry);
|
|
186
|
+
if (forwardedHistory.length > HISTORY_SIZE) {
|
|
187
|
+
forwardedHistory.shift();
|
|
94
188
|
}
|
|
95
189
|
} catch (error) {
|
|
96
190
|
session.send("🐱 - 转发失败,请检查配置");
|
|
@@ -99,17 +193,16 @@ function apply(ctx, config) {
|
|
|
99
193
|
});
|
|
100
194
|
ctx.on("message-deleted", async (session) => {
|
|
101
195
|
const originalMessageId = session.messageId;
|
|
102
|
-
|
|
103
|
-
|
|
196
|
+
const entryIndex = forwardedHistory.findIndex((entry) => entry.originalMessageId === originalMessageId);
|
|
197
|
+
if (entryIndex !== -1) {
|
|
198
|
+
const entry = forwardedHistory[entryIndex];
|
|
104
199
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
} else {
|
|
108
|
-
await session.bot.deleteMessage(config.targetQQ, forwardedMessageId);
|
|
109
|
-
}
|
|
110
|
-
messageMap.delete(originalMessageId);
|
|
200
|
+
await session.bot.deleteMessage(config.targetQQ, entry.forwardedMessageId);
|
|
201
|
+
ctx.logger.info(`成功撤回转发的消息: ${entry.forwardedMessageId}`);
|
|
111
202
|
} catch (error) {
|
|
112
|
-
ctx.logger.error(
|
|
203
|
+
ctx.logger.error(`撤回转发消息 (ID: ${entry.forwardedMessageId}) 失败:`, error);
|
|
204
|
+
} finally {
|
|
205
|
+
forwardedHistory.splice(entryIndex, 1);
|
|
113
206
|
}
|
|
114
207
|
}
|
|
115
208
|
});
|