koishi-plugin-ets2-tools-tmp 1.0.0 → 1.1.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.js +521 -499
- package/package.json +1 -1
- package/lib/command/ets-app/activity-reminder.js +0 -419
package/lib/index.js
CHANGED
|
@@ -2,39 +2,48 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Config = exports.inject = exports.name = void 0;
|
|
4
4
|
exports.apply = apply;
|
|
5
|
+
|
|
5
6
|
const koishi_1 = require("koishi");
|
|
6
7
|
const model = require('./database/model');
|
|
7
8
|
const { MileageRankingType } = require('./util/constant');
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
9
|
+
|
|
10
|
+
const commands = {
|
|
11
|
+
tmpQuery: require('./command/tmpQuery/tmpQuery'),
|
|
12
|
+
tmpServer: require('./command/tmpServer'),
|
|
13
|
+
tmpBind: require('./command/tmpBind'),
|
|
14
|
+
tmpTraffic: require('./command/tmpTraffic/tmpTraffic'),
|
|
15
|
+
tmpPosition: require('./command/tmpPosition'),
|
|
16
|
+
tmpVersion: require('./command/tmpVersion'),
|
|
17
|
+
tmpDlcMap: require('./command/tmpDlcMap'),
|
|
18
|
+
tmpMileageRanking: require('./command/tmpMileageRanking'),
|
|
19
|
+
resetPassword: require('./command/ets-app/resetPassword'),
|
|
20
|
+
queryPoint: require('./command/ets-app/queryPoint'),
|
|
21
|
+
tmpVtc: require('./command/tmpVtc')
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const __defProp = Object.defineProperty;
|
|
25
|
+
const __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
26
|
+
const __getOwnPropNames = Object.getOwnPropertyNames;
|
|
27
|
+
const __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
28
|
+
const __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
29
|
+
const __export = (target, all) => {
|
|
30
|
+
for (const name2 in all)
|
|
26
31
|
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
27
32
|
};
|
|
28
|
-
|
|
29
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
30
|
-
for (
|
|
33
|
+
const __copyProps = (to, from, except, desc) => {
|
|
34
|
+
if (from && (typeof from === "object" || typeof from === "function")) {
|
|
35
|
+
for (const key of __getOwnPropNames(from))
|
|
31
36
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
32
|
-
__defProp(to, key, {
|
|
37
|
+
__defProp(to, key, {
|
|
38
|
+
get: () => from[key],
|
|
39
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
40
|
+
});
|
|
33
41
|
}
|
|
34
42
|
return to;
|
|
35
43
|
};
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
const __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
45
|
+
|
|
46
|
+
const src_exports = {};
|
|
38
47
|
__export(src_exports, {
|
|
39
48
|
Config: () => Config,
|
|
40
49
|
apply: () => apply,
|
|
@@ -46,6 +55,7 @@ exports.inject = {
|
|
|
46
55
|
required: ['database'],
|
|
47
56
|
optional: ['puppeteer']
|
|
48
57
|
};
|
|
58
|
+
|
|
49
59
|
exports.Config = koishi_1.Schema.intersect([
|
|
50
60
|
koishi_1.Schema.object({
|
|
51
61
|
baiduTranslateEnable: koishi_1.Schema.boolean().default(false).description('启用百度翻译'),
|
|
@@ -112,6 +122,7 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
112
122
|
koishi_1.Schema.object({
|
|
113
123
|
mainGroups: koishi_1.Schema.array(koishi_1.Schema.string()).role("table").description("主群群号列表").default([]),
|
|
114
124
|
mainActivityReminderMessage: koishi_1.Schema.string().description("活动提醒消息模板,支持变量:{name}, {server}, {startingPoint}, {terminalPoint}, {distance}, {banner}, {timeLeft}").default("活动 {name} 还有 {timeLeft} 分钟就要开始啦!\n服务器: {server}\n起点: {startingPoint}\n终点: {terminalPoint}\n距离: {distance}KM"),
|
|
125
|
+
mainActivityStartReminderMessage: koishi_1.Schema.string().description("活动开始提醒消息模板,支持变量:{name}, {server}, {startingPoint}, {terminalPoint}, {distance}, {banner}").default("活动 {name} 现在开始集合啦!\n服务器: {server}\n起点: {startingPoint}\n终点: {terminalPoint}\n距离: {distance}KM\n活动将于20:30分开始!"),
|
|
115
126
|
mainActivityReminderTimes: koishi_1.Schema.array(koishi_1.Schema.number()).role("table").description("活动开始前提醒时间(分钟)").default([60, 30, 15])
|
|
116
127
|
}).description("活动查询 - 主群配置"),
|
|
117
128
|
koishi_1.Schema.object({
|
|
@@ -123,543 +134,554 @@ exports.Config = koishi_1.Schema.intersect([
|
|
|
123
134
|
}).description("活动查询 - 开发者选项")
|
|
124
135
|
]);
|
|
125
136
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
class ActivityService {
|
|
138
|
+
constructor(ctx, config) {
|
|
139
|
+
this.ctx = ctx;
|
|
140
|
+
this.cfg = config;
|
|
141
|
+
this.todayActivities = [];
|
|
142
|
+
this.todayTMPEvents = [];
|
|
143
|
+
this.sentReminders = new Set();
|
|
144
|
+
this.timers = [];
|
|
145
|
+
this.logger = this.initLogger();
|
|
135
146
|
}
|
|
136
147
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
ctx.command('路况 <serverName>').action(async ({ session }, serverName) => await tmpTraffic(ctx, cfg, serverName));
|
|
143
|
-
ctx.command('定位 <tmpId>').action(async ({ session }, tmpId) => await tmpPosition(ctx, cfg, session, tmpId));
|
|
144
|
-
ctx.command('tmp版本').action(async () => await tmpVersion(ctx));
|
|
145
|
-
ctx.command('地图dlc价格').action(async ({ session }) => await tmpDlcMap(ctx, session));
|
|
146
|
-
ctx.command('里程排行榜').action(async ({ session }) => await tmpMileageRanking(ctx, session, MileageRankingType.total));
|
|
147
|
-
ctx.command('今日里程排行榜').action(async ({ session }) => await tmpMileageRanking(ctx, session, MileageRankingType.today));
|
|
148
|
-
ctx.command('vtc查询 <vtcid>').action(async ({ session }, vtcid) => await tmpVtc(ctx, cfg, session, vtcid));
|
|
149
|
-
ctx.command(`重置密码 [targetTeamId:string]`, "重置欧卡车队平台密码")
|
|
150
|
-
.usage("重置自己的密码,或管理员重置指定teamId的密码")
|
|
151
|
-
.example(`重置密码new - 重置自己的密码`)
|
|
152
|
-
.example(`重置密码new 123 - 管理员重置指定teamId的密码`)
|
|
153
|
-
.action(async ({ session }, targetTeamId) => await resetPassword(ctx, cfg, session, targetTeamId));
|
|
154
|
-
ctx.command(`查询积分 [targetQQ:string]`, "查询欧卡车队平台积分")
|
|
155
|
-
.usage("查询自己或指定QQ号的积分,在群聊中可@他人查询")
|
|
156
|
-
.example(`查询积分 - 查询自己的积分`)
|
|
157
|
-
.example(`查询积分 123456 - 查询指定QQ号的积分`)
|
|
158
|
-
.action(async ({ session }, targetQQ) => await queryPoint(ctx, cfg, session, targetQQ));
|
|
159
|
-
ctx.command('规则查询').action(async ({ session }) => {
|
|
160
|
-
return 'TruckersMP官方规则链接:https://truckersmp.com/knowledge-base/article/746';
|
|
161
|
-
});
|
|
162
|
-
if (cfg.activityQuertEnable) {
|
|
163
|
-
let todayActivities = [];
|
|
164
|
-
let todayTMPEvents = [];
|
|
165
|
-
const sentReminders = /* @__PURE__ */ new Set();
|
|
166
|
-
const timers = [];
|
|
167
|
-
const logger = {
|
|
168
|
-
debug: /* @__PURE__ */ __name((message, ...args) => {
|
|
169
|
-
if (cfg.debugMode) {
|
|
170
|
-
ctx.logger.debug(`[TMP-BOT DEBUG] ${message}`, ...args);
|
|
148
|
+
initLogger() {
|
|
149
|
+
return {
|
|
150
|
+
debug: (message, ...args) => {
|
|
151
|
+
if (this.cfg.debugMode) {
|
|
152
|
+
this.ctx.logger.debug(`[TMP-BOT DEBUG] ${message}`, ...args);
|
|
171
153
|
}
|
|
172
|
-
},
|
|
173
|
-
info:
|
|
174
|
-
ctx.logger.info(`[TMP-BOT] ${message}`, ...args);
|
|
175
|
-
},
|
|
176
|
-
warn:
|
|
177
|
-
ctx.logger.warn(`[TMP-BOT WARN] ${message}`, ...args);
|
|
178
|
-
},
|
|
179
|
-
error:
|
|
180
|
-
ctx.logger.error(`[TMP-BOT ERROR] ${message}`, ...args);
|
|
181
|
-
},
|
|
182
|
-
api:
|
|
183
|
-
if (cfg.logApiResponses) {
|
|
184
|
-
ctx.logger.info(`[TMP-BOT API] ${message}`, data ? JSON.stringify(data, null, 2) : "");
|
|
154
|
+
},
|
|
155
|
+
info: (message, ...args) => {
|
|
156
|
+
this.ctx.logger.info(`[TMP-BOT] ${message}`, ...args);
|
|
157
|
+
},
|
|
158
|
+
warn: (message, ...args) => {
|
|
159
|
+
this.ctx.logger.warn(`[TMP-BOT WARN] ${message}`, ...args);
|
|
160
|
+
},
|
|
161
|
+
error: (message, ...args) => {
|
|
162
|
+
this.ctx.logger.error(`[TMP-BOT ERROR] ${message}`, ...args);
|
|
163
|
+
},
|
|
164
|
+
api: (message, data) => {
|
|
165
|
+
if (this.cfg.logApiResponses) {
|
|
166
|
+
this.ctx.logger.info(`[TMP-BOT API] ${message}`, data ? JSON.stringify(data, null, 2) : "");
|
|
185
167
|
}
|
|
186
|
-
},
|
|
187
|
-
timing:
|
|
188
|
-
if (cfg.logTimingDetails) {
|
|
189
|
-
ctx.logger.info(`[TMP-BOT TIMING] ${message}`, data || "");
|
|
168
|
+
},
|
|
169
|
+
timing: (message, data) => {
|
|
170
|
+
if (this.cfg.logTimingDetails) {
|
|
171
|
+
this.ctx.logger.info(`[TMP-BOT TIMING] ${message}`, data || "");
|
|
190
172
|
}
|
|
191
|
-
},
|
|
192
|
-
matching:
|
|
193
|
-
if (cfg.logActivityMatching) {
|
|
194
|
-
ctx.logger.info(`[TMP-BOT MATCHING] ${message}`, data || "");
|
|
173
|
+
},
|
|
174
|
+
matching: (message, data) => {
|
|
175
|
+
if (this.cfg.logActivityMatching) {
|
|
176
|
+
this.ctx.logger.info(`[TMP-BOT MATCHING] ${message}`, data || "");
|
|
195
177
|
}
|
|
196
|
-
},
|
|
197
|
-
message:
|
|
198
|
-
if (cfg.logMessageSending) {
|
|
199
|
-
ctx.logger.info(`[TMP-BOT MESSAGE] ${message}`, data || "");
|
|
178
|
+
},
|
|
179
|
+
message: (message, data) => {
|
|
180
|
+
if (this.cfg.logMessageSending) {
|
|
181
|
+
this.ctx.logger.info(`[TMP-BOT MESSAGE] ${message}`, data || "");
|
|
200
182
|
}
|
|
201
|
-
}, "message")
|
|
202
|
-
};
|
|
203
|
-
setupDailyTasks();
|
|
204
|
-
|
|
205
|
-
// 获取下次执行时间(修复函数命名)
|
|
206
|
-
function getNextTime(hours, minutes) {
|
|
207
|
-
const now = /* @__PURE__ */ new Date();
|
|
208
|
-
const target = new Date(
|
|
209
|
-
now.getFullYear(),
|
|
210
|
-
now.getMonth(),
|
|
211
|
-
now.getDate(),
|
|
212
|
-
hours,
|
|
213
|
-
minutes,
|
|
214
|
-
0,
|
|
215
|
-
0
|
|
216
|
-
);
|
|
217
|
-
if (target.getTime() <= now.getTime()) {
|
|
218
|
-
target.setDate(target.getDate() + 1);
|
|
219
183
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
__name(getNextTime, "getNextTime");
|
|
223
|
-
|
|
224
|
-
// 重置每日数据
|
|
225
|
-
function resetDailyData() {
|
|
226
|
-
const previousActivityCount = todayActivities.length;
|
|
227
|
-
const previousTMPCount = todayTMPEvents.length;
|
|
228
|
-
const previousReminderCount = sentReminders.size;
|
|
229
|
-
todayActivities = [];
|
|
230
|
-
todayTMPEvents = [];
|
|
231
|
-
sentReminders.clear();
|
|
232
|
-
logger.debug(`每日数据已重置: 活动${previousActivityCount}→0, TMP${previousTMPCount}→0, 提醒${previousReminderCount}→0`);
|
|
233
|
-
updateActivityData();
|
|
234
|
-
}
|
|
235
|
-
__name(resetDailyData, "resetDailyData");
|
|
236
|
-
|
|
237
|
-
// 设置每日定时任务
|
|
238
|
-
function setupDailyTasks() {
|
|
239
|
-
logger.timing("开始设置每日定时任务");
|
|
240
|
-
|
|
241
|
-
// 每日2:00重置数据
|
|
242
|
-
const resetHour = 2;
|
|
243
|
-
const resetMinute = 0;
|
|
244
|
-
const resetDelay = getNextTime(resetHour, resetMinute);
|
|
245
|
-
const resetTimer = setTimeout(() => {
|
|
246
|
-
logger.timing("执行每日数据重置");
|
|
247
|
-
resetDailyData();
|
|
248
|
-
const dailyResetTimer = setInterval(() => {
|
|
249
|
-
logger.timing("执行每日数据重置");
|
|
250
|
-
resetDailyData();
|
|
251
|
-
}, koishi_1.Time.day);
|
|
252
|
-
timers.push(dailyResetTimer);
|
|
253
|
-
}, resetDelay);
|
|
254
|
-
timers.push(resetTimer);
|
|
255
|
-
logger.timing(`设置数据重置定时器: ${resetHour}:${resetMinute.toString().padStart(2, "0")}, 延迟: ${resetDelay}ms`);
|
|
256
|
-
|
|
257
|
-
// 活动检查定时任务
|
|
258
|
-
cfg.adminCheckTimes.forEach((timeStr, index) => {
|
|
259
|
-
const [hours, minutes] = timeStr.split(":").map(Number);
|
|
260
|
-
const setupTimer = /* @__PURE__ */ __name(() => {
|
|
261
|
-
const delay = getNextTime(hours, minutes);
|
|
262
|
-
const timer = setTimeout(() => {
|
|
263
|
-
logger.timing(`执行定时检查任务 #${index + 1} (${timeStr})`);
|
|
264
|
-
updateActivityData();
|
|
265
|
-
const dailyTimer = setInterval(() => {
|
|
266
|
-
logger.timing(`执行每日检查任务 #${index + 1} (${timeStr})`);
|
|
267
|
-
updateActivityData();
|
|
268
|
-
}, koishi_1.Time.day);
|
|
269
|
-
timers.push(dailyTimer);
|
|
270
|
-
}, delay);
|
|
271
|
-
timers.push(timer);
|
|
272
|
-
logger.timing(`设置检查定时器 #${index + 1}: ${timeStr}, 延迟: ${delay}ms`);
|
|
273
|
-
}, "setupTimer");
|
|
274
|
-
setupTimer();
|
|
275
|
-
});
|
|
184
|
+
};
|
|
185
|
+
}
|
|
276
186
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
timers.push(dailyTimer);
|
|
290
|
-
}, delay);
|
|
291
|
-
timers.push(timer);
|
|
292
|
-
logger.timing(`设置发送定时器 #${index + 1}: ${timeStr}, 延迟: ${delay}ms`);
|
|
293
|
-
}, "setupTimer");
|
|
294
|
-
setupTimer();
|
|
187
|
+
start() {
|
|
188
|
+
this.setupDailyTasks();
|
|
189
|
+
this.registerAdminCommands();
|
|
190
|
+
this.ctx.on("dispose", () => this.cleanup());
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
registerAdminCommands() {
|
|
194
|
+
this.ctx.command("活动查询", "手动检查今日活动")
|
|
195
|
+
.action(async () => {
|
|
196
|
+
this.logger.debug("手动执行活动检查命令");
|
|
197
|
+
await this.updateActivityData();
|
|
198
|
+
return `检查完成!\n车队平台今日活动: ${this.todayActivities.length} 个\nTMP今日参与活动: ${this.todayTMPEvents.length} 个`;
|
|
295
199
|
});
|
|
296
200
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
await checkAndSendActivityReminders();
|
|
300
|
-
}, koishi_1.Time.minute);
|
|
301
|
-
timers.push(minuteTimer);
|
|
302
|
-
logger.timing("设置每分钟检查定时器");
|
|
201
|
+
this.ctx.command("活动DEBUG", "查看插件调试信息")
|
|
202
|
+
.action(() => this.getDebugInfo());
|
|
303
203
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
204
|
+
this.ctx.command("重置数据", "手动重置今日活动数据")
|
|
205
|
+
.action(() => {
|
|
206
|
+
this.logger.debug("手动执行数据重置命令");
|
|
207
|
+
this.resetDailyData();
|
|
208
|
+
return "✅ 今日活动数据已重置完成!";
|
|
209
|
+
});
|
|
210
|
+
}
|
|
309
211
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
await updateTodayActivities();
|
|
316
|
-
await updateTodayTMPEvents();
|
|
317
|
-
const duration = Date.now() - startTime;
|
|
318
|
-
logger.info(`活动数据更新完成,耗时: ${duration}ms`);
|
|
319
|
-
logger.debug(`今日活动数量: ${todayActivities.length}, TMP活动数量: ${todayTMPEvents.length}`);
|
|
320
|
-
} catch (error) {
|
|
321
|
-
logger.error("更新活动数据失败:", error.message);
|
|
212
|
+
getDebugInfo() {
|
|
213
|
+
let startRemindersSent = 0;
|
|
214
|
+
for (const reminderKey of this.sentReminders) {
|
|
215
|
+
if (reminderKey.endsWith('_started')) {
|
|
216
|
+
startRemindersSent++;
|
|
322
217
|
}
|
|
323
218
|
}
|
|
324
|
-
__name(updateActivityData, "updateActivityData");
|
|
325
219
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const response = await ctx.http.get(fullUrl, { timeout: 10000 });
|
|
336
|
-
const duration = Date.now() - startTime;
|
|
337
|
-
logger.api(`车队平台API响应耗时: ${duration}ms, 状态码: ${response.code}`);
|
|
338
|
-
|
|
339
|
-
if (cfg.logApiResponses) {
|
|
340
|
-
logger.api("车队平台API响应详情:", {
|
|
341
|
-
code: response.code,
|
|
342
|
-
totalCount: response.data?.totalCount,
|
|
343
|
-
listCount: response.data?.list?.length
|
|
344
|
-
});
|
|
345
|
-
}
|
|
220
|
+
return `📊 TMP-BOT 插件调试信息:\n` +
|
|
221
|
+
`• 今日活动: ${this.todayActivities.length} 个\n` +
|
|
222
|
+
`• TMP活动: ${this.todayTMPEvents.length} 个\n` +
|
|
223
|
+
`• 已发送提醒: ${this.sentReminders.size} 个\n` +
|
|
224
|
+
` - 活动开始提醒: ${startRemindersSent} 个\n` +
|
|
225
|
+
`• 活跃定时器: ${this.timers.length} 个\n` +
|
|
226
|
+
`• 调试模式: ${this.cfg.debugMode ? "✅ 开启" : "❌ 关闭"}\n` +
|
|
227
|
+
`• 日志选项: API=${this.cfg.logApiResponses ? "✅" : "❌"}, 定时=${this.cfg.logTimingDetails ? "✅" : "❌"}, 匹配=${this.cfg.logActivityMatching ? "✅" : "❌"}, 消息=${this.cfg.logMessageSending ? "✅" : "❌"}`;
|
|
228
|
+
}
|
|
346
229
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
logger.
|
|
367
|
-
|
|
368
|
-
}
|
|
230
|
+
setupDailyTasks() {
|
|
231
|
+
this.logger.timing("开始设置每日定时任务");
|
|
232
|
+
const resetHour = 2;
|
|
233
|
+
const resetMinute = 0;
|
|
234
|
+
const resetDelay = this.getNextTime(resetHour, resetMinute);
|
|
235
|
+
const resetTimer = setTimeout(() => {
|
|
236
|
+
this.logger.timing("执行每日数据重置");
|
|
237
|
+
this.resetDailyData();
|
|
238
|
+
const dailyResetTimer = setInterval(() => {
|
|
239
|
+
this.logger.timing("执行每日数据重置");
|
|
240
|
+
this.resetDailyData();
|
|
241
|
+
}, koishi_1.Time.day);
|
|
242
|
+
this.timers.push(dailyResetTimer);
|
|
243
|
+
}, resetDelay);
|
|
244
|
+
this.timers.push(resetTimer);
|
|
245
|
+
this.logger.timing(`设置数据重置定时器: ${resetHour}:${resetMinute.toString().padStart(2, "0")}, 延迟: ${resetDelay}ms`);
|
|
246
|
+
this.cfg.adminCheckTimes.forEach((timeStr, index) => {
|
|
247
|
+
const [hours, minutes] = timeStr.split(":").map(Number);
|
|
248
|
+
this.setupTimer(hours, minutes, () => {
|
|
249
|
+
this.logger.timing(`执行定时检查任务 #${index + 1} (${timeStr})`);
|
|
250
|
+
this.updateActivityData();
|
|
251
|
+
}, `检查定时器 #${index + 1}: ${timeStr}`);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
this.cfg.adminSendTimes.forEach((timeStr, index) => {
|
|
255
|
+
const [hours, minutes] = timeStr.split(":").map(Number);
|
|
256
|
+
this.setupTimer(hours, minutes, () => {
|
|
257
|
+
this.logger.timing(`执行定时发送任务 #${index + 1} (${timeStr})`);
|
|
258
|
+
this.checkAndSendProfileReminders();
|
|
259
|
+
}, `发送定时器 #${index + 1}: ${timeStr}`);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const minuteTimer = setInterval(async () => {
|
|
263
|
+
await this.checkAndSendActivityReminders();
|
|
264
|
+
}, koishi_1.Time.minute);
|
|
265
|
+
this.timers.push(minuteTimer);
|
|
266
|
+
this.logger.timing("设置每分钟检查定时器");
|
|
267
|
+
this.logger.debug("启动时立即更新活动数据");
|
|
268
|
+
this.updateActivityData();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
setupTimer(hours, minutes, callback, name) {
|
|
272
|
+
const delay = this.getNextTime(hours, minutes);
|
|
273
|
+
const timer = setTimeout(() => {
|
|
274
|
+
callback();
|
|
275
|
+
const dailyTimer = setInterval(callback, koishi_1.Time.day);
|
|
276
|
+
this.timers.push(dailyTimer);
|
|
277
|
+
}, delay);
|
|
278
|
+
this.timers.push(timer);
|
|
279
|
+
this.logger.timing(`${name}, 延迟: ${delay}ms`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
getNextTime(hours, minutes) {
|
|
283
|
+
const now = new Date();
|
|
284
|
+
const target = new Date(
|
|
285
|
+
now.getFullYear(),
|
|
286
|
+
now.getMonth(),
|
|
287
|
+
now.getDate(),
|
|
288
|
+
hours,
|
|
289
|
+
minutes,
|
|
290
|
+
0,
|
|
291
|
+
0
|
|
292
|
+
);
|
|
293
|
+
if (target.getTime() <= now.getTime()) {
|
|
294
|
+
target.setDate(target.getDate() + 1);
|
|
369
295
|
}
|
|
370
|
-
|
|
296
|
+
return target.getTime() - now.getTime();
|
|
297
|
+
}
|
|
371
298
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
299
|
+
resetDailyData() {
|
|
300
|
+
const previousActivityCount = this.todayActivities.length;
|
|
301
|
+
const previousTMPCount = this.todayTMPEvents.length;
|
|
302
|
+
const previousReminderCount = this.sentReminders.size;
|
|
303
|
+
|
|
304
|
+
this.todayActivities = [];
|
|
305
|
+
this.todayTMPEvents = [];
|
|
306
|
+
this.sentReminders.clear();
|
|
307
|
+
|
|
308
|
+
this.logger.debug(`每日数据已重置: 活动${previousActivityCount}→0, TMP${previousTMPCount}→0, 提醒${previousReminderCount}→0`);
|
|
309
|
+
this.updateActivityData();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async updateActivityData() {
|
|
313
|
+
try {
|
|
314
|
+
this.logger.debug("开始更新活动数据");
|
|
315
|
+
const startTime = Date.now();
|
|
316
|
+
await Promise.all([
|
|
317
|
+
this.updateTodayActivities(),
|
|
318
|
+
this.updateTodayTMPEvents()
|
|
319
|
+
]);
|
|
320
|
+
|
|
321
|
+
const duration = Date.now() - startTime;
|
|
322
|
+
this.logger.info(`活动数据更新完成,耗时: ${duration}ms`);
|
|
323
|
+
this.logger.debug(`今日活动数量: ${this.todayActivities.length}, TMP活动数量: ${this.todayTMPEvents.length}`);
|
|
324
|
+
} catch (error) {
|
|
325
|
+
this.logger.error("更新活动数据失败:", error.message);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async updateTodayActivities() {
|
|
330
|
+
try {
|
|
331
|
+
const protocol = this.cfg.adminUseHttps ? "https://" : "http://";
|
|
332
|
+
const fullUrl = `${protocol}${this.cfg.adminApiUrl}/api/activity/info/list?token=${this.cfg.adminApiToken}&page=1&limit=50&themeName=`;
|
|
333
|
+
this.logger.api(`请求车队平台API: ${fullUrl.replace(this.cfg.adminApiToken, "***")}`);
|
|
334
|
+
|
|
335
|
+
const startTime = Date.now();
|
|
336
|
+
const response = await this.ctx.http.get(fullUrl, { timeout: 10000 });
|
|
337
|
+
const duration = Date.now() - startTime;
|
|
338
|
+
this.logger.api(`车队平台API响应耗时: ${duration}ms, 状态码: ${response.code}`);
|
|
339
|
+
|
|
340
|
+
if (this.cfg.logApiResponses) {
|
|
341
|
+
this.logger.api("车队平台API响应详情:", {
|
|
342
|
+
code: response.code,
|
|
343
|
+
totalCount: response.data?.totalCount,
|
|
344
|
+
listCount: response.data?.list?.length
|
|
345
|
+
});
|
|
412
346
|
}
|
|
347
|
+
|
|
348
|
+
if (response.code === 0 && response.data?.list) {
|
|
349
|
+
const today = new Date().toISOString().split("T")[0];
|
|
350
|
+
const originalCount = response.data.list.length;
|
|
351
|
+
this.todayActivities = response.data.list.filter((activity) => {
|
|
352
|
+
const activityDate = activity.startTime?.split(" ")[0];
|
|
353
|
+
return activityDate === today;
|
|
354
|
+
});
|
|
355
|
+
this.logger.info(`从车队平台找到 ${this.todayActivities.length}/${originalCount} 个今日活动`);
|
|
356
|
+
} else {
|
|
357
|
+
this.logger.error(`车队平台API返回错误: ${response.msg || '未知错误'} (代码: ${response.code || '无'})`);
|
|
358
|
+
this.todayActivities = [];
|
|
359
|
+
}
|
|
360
|
+
} catch (error) {
|
|
361
|
+
this.logger.error("获取车队平台活动列表失败:", error.message);
|
|
362
|
+
this.todayActivities = [];
|
|
413
363
|
}
|
|
414
|
-
|
|
364
|
+
}
|
|
415
365
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (
|
|
419
|
-
logger.
|
|
366
|
+
async updateTodayTMPEvents() {
|
|
367
|
+
try {
|
|
368
|
+
if (!this.cfg.adminVtcId) {
|
|
369
|
+
this.logger.warn("TMP API请求失败:未配置adminVtcId");
|
|
370
|
+
this.todayTMPEvents = [];
|
|
420
371
|
return;
|
|
421
372
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
}
|
|
373
|
+
|
|
374
|
+
const tmpApiUrl = `https://api.truckersmp.com/v2/vtc/${this.cfg.adminVtcId}/events/attending/`;
|
|
375
|
+
this.logger.api(`请求TMP API: ${tmpApiUrl}`);
|
|
376
|
+
|
|
377
|
+
const startTime = Date.now();
|
|
378
|
+
const response = await this.ctx.http.get(tmpApiUrl, { timeout: 10000 });
|
|
379
|
+
const duration = Date.now() - startTime;
|
|
380
|
+
this.logger.api(`TMP API响应耗时: ${duration}ms, 错误状态: ${response.error}`);
|
|
381
|
+
|
|
382
|
+
if (!response.error && Array.isArray(response.response)) {
|
|
383
|
+
const today = new Date().toISOString().split("T")[0];
|
|
384
|
+
const originalCount = response.response.length;
|
|
385
|
+
this.todayTMPEvents = response.response.filter((event) => {
|
|
386
|
+
const eventDate = event.start_at?.split(" ")[0];
|
|
387
|
+
return eventDate === today;
|
|
388
|
+
});
|
|
389
|
+
this.logger.info(`从TMP找到 ${this.todayTMPEvents.length}/${originalCount} 个今日活动`);
|
|
390
|
+
} else {
|
|
391
|
+
this.logger.error(`TMP API返回错误: ${response.message || '未知错误'}`);
|
|
392
|
+
this.todayTMPEvents = [];
|
|
438
393
|
}
|
|
394
|
+
} catch (error) {
|
|
395
|
+
this.logger.error("获取TMP活动失败:", error.message);
|
|
396
|
+
this.todayTMPEvents = [];
|
|
439
397
|
}
|
|
440
|
-
|
|
398
|
+
}
|
|
441
399
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
400
|
+
async checkAndSendProfileReminders() {
|
|
401
|
+
if (this.todayActivities.length === 0) {
|
|
402
|
+
this.logger.debug("今日没有活动,跳过档位检查");
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
this.logger.debug(`开始检查 ${this.todayActivities.length} 个活动的档位状态`);
|
|
447
407
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
continue;
|
|
454
|
-
}
|
|
408
|
+
for (const activity of this.todayActivities) {
|
|
409
|
+
const hasProfile = !!activity.profileFile;
|
|
410
|
+
const message = hasProfile ? this.cfg.adminProfileUploadedMessage : this.cfg.adminProfileNotUploadedMessage;
|
|
411
|
+
const fullMessage = `活动 "${activity.themeName || '未知活动'}" - ${message}`;
|
|
412
|
+
this.logger.message(`活动档位检查: "${activity.themeName || '未知活动'}" - ${hasProfile ? "已上传" : "未上传"}`);
|
|
455
413
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
// 只处理未来的活动
|
|
461
|
-
if (minutesLeft < 0) continue;
|
|
462
|
-
|
|
463
|
-
for (const reminderTime of cfg.mainActivityReminderTimes) {
|
|
464
|
-
// 当剩余时间落在 [reminderTime-1, reminderTime] 区间时触发提醒
|
|
465
|
-
if (minutesLeft <= reminderTime && minutesLeft > reminderTime - 1) {
|
|
466
|
-
const reminderKey = `${activity.id}_${reminderTime}`;
|
|
467
|
-
if (!sentReminders.has(reminderKey)) {
|
|
468
|
-
logger.debug(`触发提醒: ${activity.themeName} - ${reminderTime} 分钟前`);
|
|
469
|
-
await sendActivityReminder(activity, minutesLeft);
|
|
470
|
-
sentReminders.add(reminderKey);
|
|
471
|
-
remindersSent++;
|
|
472
|
-
} else {
|
|
473
|
-
logger.debug(`提醒已发送过: ${activity.themeName} - ${reminderTime} 分钟前`);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}
|
|
414
|
+
for (const groupId of this.cfg.adminGroups) {
|
|
415
|
+
try {
|
|
416
|
+
await this.sendToGroup(groupId, fullMessage, "管理群组");
|
|
417
|
+
this.logger.message(`已发送档位提醒到管理群组 ${groupId}: ${activity.themeName || '未知活动'}`);
|
|
477
418
|
} catch (error) {
|
|
478
|
-
logger.error(
|
|
419
|
+
this.logger.error(`发送消息到管理群组 ${groupId} 失败:`, error.message);
|
|
479
420
|
}
|
|
480
421
|
}
|
|
481
|
-
|
|
482
|
-
if (remindersSent > 0) {
|
|
483
|
-
logger.debug(`本轮发送了 ${remindersSent} 个活动提醒`);
|
|
484
|
-
}
|
|
485
422
|
}
|
|
486
|
-
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async checkAndSendActivityReminders() {
|
|
426
|
+
const now = new Date();
|
|
427
|
+
let remindersSent = 0;
|
|
428
|
+
this.logger.debug(`检查 ${this.todayActivities.length} 个活动的提醒时间`);
|
|
487
429
|
|
|
488
|
-
|
|
489
|
-
async function sendActivityReminder(activity, minutesLeft) {
|
|
430
|
+
for (const activity of this.todayActivities) {
|
|
490
431
|
try {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
logger.matching(`活动匹配: "${activity.themeName}" - 找到TMP匹配: ${!!tmpEvent}`);
|
|
496
|
-
|
|
497
|
-
if (tmpEvent && cfg.logActivityMatching) {
|
|
498
|
-
logger.matching("TMP活动详情:", {
|
|
499
|
-
tmpName: tmpEvent.name,
|
|
500
|
-
activityName: activity.themeName,
|
|
501
|
-
server: tmpEvent.server?.name,
|
|
502
|
-
departure: `${tmpEvent.departure?.location} - ${tmpEvent.departure?.city}`,
|
|
503
|
-
arrive: `${tmpEvent.arrive?.location} - ${tmpEvent.arrive?.city}`
|
|
504
|
-
});
|
|
432
|
+
const activityStartTime = new Date(activity.startTime);
|
|
433
|
+
if (isNaN(activityStartTime.getTime())) {
|
|
434
|
+
this.logger.warn(`活动 "${activity.themeName}" 开始时间格式错误,跳过提醒`);
|
|
435
|
+
continue;
|
|
505
436
|
}
|
|
506
437
|
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
438
|
+
const timeDiff = activityStartTime.getTime() - now.getTime();
|
|
439
|
+
const minutesLeft = Math.floor(timeDiff / (1e3 * 60));
|
|
440
|
+
this.logger.debug(`活动 "${activity.themeName}" 剩余时间: ${minutesLeft} 分钟`);
|
|
441
|
+
|
|
442
|
+
if (minutesLeft <= 0 && minutesLeft > -1) {
|
|
443
|
+
const startReminderKey = `${activity.id}_started`;
|
|
444
|
+
if (!this.sentReminders.has(startReminderKey)) {
|
|
445
|
+
this.logger.debug(`触发活动开始提醒: ${activity.themeName}`);
|
|
446
|
+
await this.sendActivityStartReminder(activity);
|
|
447
|
+
this.sentReminders.add(startReminderKey);
|
|
448
|
+
remindersSent++;
|
|
449
|
+
}
|
|
519
450
|
}
|
|
520
451
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
452
|
+
if (minutesLeft < 0) continue;
|
|
453
|
+
for (const reminderTime of this.cfg.mainActivityReminderTimes) {
|
|
454
|
+
if (minutesLeft <= reminderTime && minutesLeft > reminderTime - 1) {
|
|
455
|
+
const reminderKey = `${activity.id}_${reminderTime}`;
|
|
456
|
+
if (!this.sentReminders.has(reminderKey)) {
|
|
457
|
+
this.logger.debug(`触发提醒: ${activity.themeName} - ${reminderTime} 分钟前`);
|
|
458
|
+
await this.sendActivityReminder(activity, minutesLeft);
|
|
459
|
+
this.sentReminders.add(reminderKey);
|
|
460
|
+
remindersSent++;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
526
463
|
}
|
|
464
|
+
} catch (error) {
|
|
465
|
+
this.logger.error(`处理活动 "${activity.themeName}" 提醒失败:`, error.message);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
527
468
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
replacements.terminalPoint = activity.terminalPoint || '未知终点';
|
|
533
|
-
}
|
|
469
|
+
if (remindersSent > 0) {
|
|
470
|
+
this.logger.debug(`本轮发送了 ${remindersSent} 个活动提醒`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
534
473
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
474
|
+
createActivityReplacements(activity, tmpEvent, minutesLeft) {
|
|
475
|
+
const replacements = {
|
|
476
|
+
name: activity.themeName || '未知活动',
|
|
477
|
+
distance: activity.distance?.toString() || '未知'
|
|
478
|
+
};
|
|
541
479
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
message = message.replace(new RegExp(`{${key}}`, "g"), value);
|
|
546
|
-
}
|
|
480
|
+
if (minutesLeft !== undefined) {
|
|
481
|
+
replacements.timeLeft = minutesLeft.toString();
|
|
482
|
+
}
|
|
547
483
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
484
|
+
if (this.cfg.adminServerSource === "tmp" && tmpEvent) {
|
|
485
|
+
replacements.server = tmpEvent.server?.name || '未知服务器';
|
|
486
|
+
} else {
|
|
487
|
+
replacements.server = activity.serverName || '未知服务器';
|
|
488
|
+
}
|
|
552
489
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
490
|
+
if (this.cfg.adminStartPointSource === "tmp" && tmpEvent) {
|
|
491
|
+
replacements.startingPoint = `${tmpEvent.departure?.location || ''} - ${tmpEvent.departure?.city || ''}`.trim() || '未知起点';
|
|
492
|
+
} else {
|
|
493
|
+
replacements.startingPoint = activity.startingPoint || '未知起点';
|
|
494
|
+
}
|
|
556
495
|
|
|
557
|
-
|
|
558
|
-
|
|
496
|
+
if (this.cfg.adminEndPointSource === "tmp" && tmpEvent) {
|
|
497
|
+
replacements.terminalPoint = `${tmpEvent.arrive?.location || ''} - ${tmpEvent.arrive?.city || ''}`.trim() || '未知终点';
|
|
498
|
+
} else {
|
|
499
|
+
replacements.terminalPoint = activity.terminalPoint || '未知终点';
|
|
500
|
+
}
|
|
559
501
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
logger.message(`已发送活动提醒到主群组 ${groupId}: ${activity.themeName}`);
|
|
565
|
-
} catch (error) {
|
|
566
|
-
logger.error(`发送活动提醒到主群组 ${groupId} 失败:`, error.message);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
} catch (error) {
|
|
570
|
-
logger.error(`发送活动提醒失败:`, error.message);
|
|
571
|
-
}
|
|
502
|
+
if (this.cfg.adminShowBanner && tmpEvent && tmpEvent.banner) {
|
|
503
|
+
replacements.banner = tmpEvent.banner;
|
|
504
|
+
} else {
|
|
505
|
+
replacements.banner = "无";
|
|
572
506
|
}
|
|
573
|
-
__name(sendActivityReminder, "sendActivityReminder");
|
|
574
|
-
|
|
575
|
-
// 发送消息到指定群组
|
|
576
|
-
async function sendToGroup(groupId, message, groupType) {
|
|
577
|
-
// 过滤不支持的平台
|
|
578
|
-
const availableBots = ctx.bots.filter((bot) => {
|
|
579
|
-
const unsupportedPlatforms = ["mail", "telegram", "discord"];
|
|
580
|
-
return !unsupportedPlatforms.includes(bot.platform);
|
|
581
|
-
});
|
|
582
507
|
|
|
583
|
-
|
|
584
|
-
|
|
508
|
+
return replacements;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async sendActivityStartReminder(activity) {
|
|
512
|
+
try {
|
|
513
|
+
const tmpEvent = this.todayTMPEvents.find(
|
|
514
|
+
(event) => event.name.includes(activity.themeName) || activity.themeName.includes(event.name)
|
|
515
|
+
);
|
|
516
|
+
this.logger.matching(`活动匹配: "${activity.themeName}" - 找到TMP匹配: ${!!tmpEvent}`);
|
|
517
|
+
|
|
518
|
+
const replacements = this.createActivityReplacements(activity, tmpEvent);
|
|
519
|
+
|
|
520
|
+
let message = this.cfg.mainActivityStartReminderMessage;
|
|
521
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
522
|
+
message = message.replace(new RegExp(`{${key}}`, "g"), value);
|
|
585
523
|
}
|
|
586
524
|
|
|
587
|
-
|
|
588
|
-
|
|
525
|
+
if (!this.cfg.adminShowBanner) {
|
|
526
|
+
message = message.replace(/活动横幅:.*?\n?/, "");
|
|
527
|
+
}
|
|
589
528
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
529
|
+
message = message.replace(/\\n/g, "\n").trim();
|
|
530
|
+
const fullMessage = `@全体成员\n${message}`;
|
|
531
|
+
|
|
532
|
+
await this.sendToMainGroups(fullMessage, activity.themeName, "开始提醒");
|
|
533
|
+
} catch (error) {
|
|
534
|
+
this.logger.error(`发送活动开始提醒失败:`, error.message);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async sendActivityReminder(activity, minutesLeft) {
|
|
539
|
+
try {
|
|
540
|
+
const tmpEvent = this.todayTMPEvents.find(
|
|
541
|
+
(event) => event.name.includes(activity.themeName) || activity.themeName.includes(event.name)
|
|
542
|
+
);
|
|
543
|
+
this.logger.matching(`活动匹配: "${activity.themeName}" - 找到TMP匹配: ${!!tmpEvent}`);
|
|
544
|
+
|
|
545
|
+
const replacements = this.createActivityReplacements(activity, tmpEvent, minutesLeft);
|
|
546
|
+
|
|
547
|
+
let message = this.cfg.mainActivityReminderMessage;
|
|
548
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
549
|
+
message = message.replace(new RegExp(`{${key}}`, "g"), value);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (!this.cfg.adminShowBanner) {
|
|
553
|
+
message = message.replace(/活动横幅:.*?\n?/, "");
|
|
600
554
|
}
|
|
601
555
|
|
|
602
|
-
|
|
556
|
+
message = message.replace(/\\n/g, "\n").trim();
|
|
557
|
+
const fullMessage = `@全体成员\n${message}`;
|
|
558
|
+
|
|
559
|
+
await this.sendToMainGroups(fullMessage, activity.themeName, `${minutesLeft}分钟前提醒`);
|
|
560
|
+
} catch (error) {
|
|
561
|
+
this.logger.error(`发送活动提醒失败:`, error.message);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
async sendToMainGroups(message, activityName, reminderType) {
|
|
566
|
+
for (const groupId of this.cfg.mainGroups) {
|
|
567
|
+
try {
|
|
568
|
+
await this.sendToGroup(groupId, message, "主群组");
|
|
569
|
+
this.logger.message(`已发送${reminderType}到主群组 ${groupId}: ${activityName}`);
|
|
570
|
+
} catch (error) {
|
|
571
|
+
this.logger.error(`发送${reminderType}到主群组 ${groupId} 失败:`, error.message);
|
|
572
|
+
}
|
|
603
573
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
ctx.
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
const result = `检查完成!\n车队平台今日活动: ${todayActivities.length} 个\nTMP今日参与活动: ${todayTMPEvents.length} 个`;
|
|
611
|
-
logger.debug(result);
|
|
612
|
-
return result;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
async sendToGroup(groupId, message, groupType) {
|
|
577
|
+
const availableBots = this.ctx.bots.filter((bot) => {
|
|
578
|
+
const unsupportedPlatforms = ["mail", "telegram", "discord"];
|
|
579
|
+
return !unsupportedPlatforms.includes(bot.platform);
|
|
613
580
|
});
|
|
614
581
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
todayActivities: todayActivities.length,
|
|
619
|
-
todayTMPEvents: todayTMPEvents.length,
|
|
620
|
-
sentReminders: sentReminders.size,
|
|
621
|
-
timers: timers.length,
|
|
622
|
-
config: {
|
|
623
|
-
debugMode: cfg.debugMode,
|
|
624
|
-
logApiResponses: cfg.logApiResponses,
|
|
625
|
-
logTimingDetails: cfg.logTimingDetails,
|
|
626
|
-
logActivityMatching: cfg.logActivityMatching,
|
|
627
|
-
logMessageSending: cfg.logMessageSending
|
|
628
|
-
}
|
|
629
|
-
};
|
|
582
|
+
if (availableBots.length === 0) {
|
|
583
|
+
throw new Error(`没有可用的聊天平台适配器(当前不支持邮件/电报/Discord)`);
|
|
584
|
+
}
|
|
630
585
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
message += `• TMP活动: ${state.todayTMPEvents} 个\n`;
|
|
634
|
-
message += `• 已发送提醒: ${state.sentReminders} 个\n`;
|
|
635
|
-
message += `• 活跃定时器: ${state.timers} 个\n`;
|
|
636
|
-
message += `• 调试模式: ${state.config.debugMode ? "✅ 开启" : "❌ 关闭"}\n`;
|
|
637
|
-
message += `• 日志选项: API=${state.config.logApiResponses ? "✅" : "❌"}, 定时=${state.config.logTimingDetails ? "✅" : "❌"}, 匹配=${state.config.logActivityMatching ? "✅" : "❌"}, 消息=${state.config.logMessageSending ? "✅" : "❌"}`;
|
|
586
|
+
let lastError = null;
|
|
587
|
+
this.logger.debug(`尝试通过 ${availableBots.length} 个适配器发送消息到${groupType} ${groupId}`);
|
|
638
588
|
|
|
639
|
-
|
|
640
|
-
|
|
589
|
+
for (const bot of availableBots) {
|
|
590
|
+
try {
|
|
591
|
+
await bot.sendMessage(groupId, message);
|
|
592
|
+
this.logger.debug(`已通过 ${bot.platform} 适配器发送消息到${groupType} ${groupId}`);
|
|
593
|
+
return;
|
|
594
|
+
} catch (error) {
|
|
595
|
+
lastError = error;
|
|
596
|
+
this.logger.warn(`通过 ${bot.platform} 适配器发送消息失败: ${error.message}`);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
641
599
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
logger.debug("手动执行数据重置命令");
|
|
645
|
-
resetDailyData();
|
|
646
|
-
return "✅ 今日活动数据已重置完成!";
|
|
647
|
-
});
|
|
600
|
+
throw lastError || new Error(`所有适配器都无法发送消息到${groupType} ${groupId}`);
|
|
601
|
+
}
|
|
648
602
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
clearTimeout(timer);
|
|
658
|
-
clearInterval(timer);
|
|
659
|
-
});
|
|
660
|
-
timers.length = 0;
|
|
661
|
-
logger.debug("资源清理完成");
|
|
603
|
+
cleanup() {
|
|
604
|
+
this.logger.debug("插件卸载,开始清理资源");
|
|
605
|
+
this.todayActivities = [];
|
|
606
|
+
this.todayTMPEvents = [];
|
|
607
|
+
this.sentReminders.clear();
|
|
608
|
+
this.timers.forEach((timer) => {
|
|
609
|
+
clearTimeout(timer);
|
|
610
|
+
clearInterval(timer);
|
|
662
611
|
});
|
|
612
|
+
this.timers.length = 0;
|
|
613
|
+
this.logger.debug("资源清理完成");
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function registerBaseCommands(ctx, cfg) {
|
|
618
|
+
ctx.command('查询 <tmpId>')
|
|
619
|
+
.action(async ({ session }, tmpId) => await commands.tmpQuery(ctx, cfg, session, tmpId));
|
|
620
|
+
|
|
621
|
+
ctx.command('美卡服务器')
|
|
622
|
+
.action(async () => await commands.tmpServer(ctx, cfg, 'ATS'));
|
|
623
|
+
|
|
624
|
+
ctx.command('欧卡服务器')
|
|
625
|
+
.action(async () => await commands.tmpServer(ctx, cfg, 'ETS2'));
|
|
626
|
+
|
|
627
|
+
ctx.command('绑定 <tmpId>')
|
|
628
|
+
.action(async ({ session }, tmpId) => await commands.tmpBind(ctx, cfg, session, tmpId));
|
|
629
|
+
|
|
630
|
+
ctx.command('路况 <serverName>')
|
|
631
|
+
.action(async ({ session }, serverName) => await commands.tmpTraffic(ctx, cfg, serverName));
|
|
632
|
+
|
|
633
|
+
ctx.command('定位 <tmpId>')
|
|
634
|
+
.action(async ({ session }, tmpId) => await commands.tmpPosition(ctx, cfg, session, tmpId));
|
|
635
|
+
|
|
636
|
+
ctx.command('tmp版本')
|
|
637
|
+
.action(async () => await commands.tmpVersion(ctx));
|
|
638
|
+
|
|
639
|
+
ctx.command('地图dlc价格')
|
|
640
|
+
.action(async ({ session }) => await commands.tmpDlcMap(ctx, session));
|
|
641
|
+
|
|
642
|
+
ctx.command('里程排行榜')
|
|
643
|
+
.action(async ({ session }) => await commands.tmpMileageRanking(ctx, session, MileageRankingType.total));
|
|
644
|
+
|
|
645
|
+
ctx.command('今日里程排行榜')
|
|
646
|
+
.action(async ({ session }) => await commands.tmpMileageRanking(ctx, session, MileageRankingType.today));
|
|
647
|
+
|
|
648
|
+
ctx.command('vtc查询 <vtcid>')
|
|
649
|
+
.action(async ({ session }, vtcid) => await commands.tmpVtc(ctx, cfg, session, vtcid));
|
|
650
|
+
|
|
651
|
+
ctx.command(`重置密码 [targetTeamId:string]`, "重置欧卡车队平台密码")
|
|
652
|
+
.usage("重置自己的密码,或管理员重置指定teamId的密码")
|
|
653
|
+
.example(`重置密码new - 重置自己的密码`)
|
|
654
|
+
.example(`重置密码new 123 - 管理员重置指定teamId的密码`)
|
|
655
|
+
.action(async ({ session }, targetTeamId) => await commands.resetPassword(ctx, cfg, session, targetTeamId));
|
|
656
|
+
|
|
657
|
+
ctx.command(`查询积分 [targetQQ:string]`, "查询欧卡车队平台积分")
|
|
658
|
+
.usage("查询自己或指定QQ号的积分,在群聊中可@他人查询")
|
|
659
|
+
.example(`查询积分 - 查询自己的积分`)
|
|
660
|
+
.example(`查询积分 123456 - 查询指定QQ号的积分`)
|
|
661
|
+
.action(async ({ session }, targetQQ) => await commands.queryPoint(ctx, cfg, session, targetQQ));
|
|
662
|
+
|
|
663
|
+
ctx.command('规则查询')
|
|
664
|
+
.action(async () => 'TruckersMP官方规则链接:https://truckersmp.com/knowledge-base/article/746');
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function apply(ctx, cfg) {
|
|
668
|
+
try {
|
|
669
|
+
model(ctx);
|
|
670
|
+
if (cfg.debugMode) {
|
|
671
|
+
ctx.logger.debug("[TMP-BOT] 数据库模型初始化成功");
|
|
672
|
+
}
|
|
673
|
+
} catch (error) {
|
|
674
|
+
ctx.logger.error("[TMP-BOT] 数据库模型初始化失败:", error.message);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
registerBaseCommands(ctx, cfg);
|
|
679
|
+
|
|
680
|
+
if (cfg.activityQuertEnable) {
|
|
681
|
+
const activityService = new ActivityService(ctx, cfg);
|
|
682
|
+
activityService.start();
|
|
683
|
+
} else if (cfg.debugMode) {
|
|
684
|
+
ctx.logger.debug("[TMP-BOT] 活动查询功能已禁用");
|
|
663
685
|
}
|
|
664
686
|
}
|
|
665
687
|
__name(apply, "apply");
|