koishi-plugin-ll-schedule 1.7.5 → 1.7.7
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/index.js +40 -89
- package/package.json +3 -1
package/index.js
CHANGED
|
@@ -11,16 +11,15 @@ const { Schema, h } = require('koishi')
|
|
|
11
11
|
|
|
12
12
|
exports.name = 'll-schedule'
|
|
13
13
|
|
|
14
|
-
exports.usage = `剑灵活动提醒:全部活动写在 activities
|
|
14
|
+
exports.usage = `剑灵活动提醒:全部活动写在 activities 活动表里,群里发「剑灵提醒订阅」即可收到所有活动的提前提醒。
|
|
15
15
|
|
|
16
16
|
活动表格式(一份表管所有活动):
|
|
17
17
|
[世界BOSS] 提前:5分钟
|
|
18
18
|
周一 21:00-22:00 地点:白青山脉 / 仙幻岛
|
|
19
19
|
[寻宝] 提前:2分钟
|
|
20
|
-
每天 13:00-
|
|
20
|
+
每天 13:00-15:00
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
管理员:schedule.unsuball(取消所有群的订阅)
|
|
22
|
+
群命令:剑灵提醒订阅 / 取消剑灵订阅 / 订阅列表 / schedule.list / schedule.test / schedule.debug
|
|
24
23
|
`
|
|
25
24
|
|
|
26
25
|
// ===== 纯函数(可单测) =====
|
|
@@ -243,6 +242,7 @@ function hmToMin(hm) {
|
|
|
243
242
|
function parseActivities(text) {
|
|
244
243
|
const lines = String(text || '').split(/\r?\n/)
|
|
245
244
|
const acts = []
|
|
245
|
+
const errors = []
|
|
246
246
|
let cur = null
|
|
247
247
|
const pad = (s) => (String(s).length === 4 ? '0' + s : String(s))
|
|
248
248
|
for (const raw of lines) {
|
|
@@ -262,12 +262,22 @@ function parseActivities(text) {
|
|
|
262
262
|
if (/^[#/]/.test(line)) continue // 注释行
|
|
263
263
|
// 时段行:星期 开始-结束 [地点:xxx]
|
|
264
264
|
const m = line.match(/^(\S+)\s+(\d{1,2}:\d{2})\s*[-~—至]\s*(\d{1,2}:\d{2})\s*(.*)$/)
|
|
265
|
-
if (!m)
|
|
265
|
+
if (!m) {
|
|
266
|
+
errors.push(line)
|
|
267
|
+
continue
|
|
268
|
+
}
|
|
266
269
|
const rest = (m[4] || '').trim()
|
|
267
270
|
const pm = rest.match(/地点\s*[::]\s*(.+)$/)
|
|
268
271
|
cur.slots.push({ wd: m[1], start: pad(m[2]), end: pad(m[3]), place: pm ? pm[1].trim() : rest })
|
|
269
272
|
}
|
|
270
|
-
|
|
273
|
+
const out = acts.filter(a => {
|
|
274
|
+
if (a.slots.length) return true
|
|
275
|
+
errors.push(`【${a.name}】下面没有任何可识别的时段行`)
|
|
276
|
+
return false
|
|
277
|
+
})
|
|
278
|
+
// 把"看不懂的行"挂到数组上,供 list/debug 提示(不影响正常调用)
|
|
279
|
+
out.errors = errors
|
|
280
|
+
return out
|
|
271
281
|
}
|
|
272
282
|
|
|
273
283
|
// 展开成"周内实例"(星期 × 时段)
|
|
@@ -396,10 +406,7 @@ const Config = Schema.object({
|
|
|
396
406
|
.description('活动提醒是否用 QQ官方 Markdown 卡片发送(仅官机生效,失败自动回退纯文本)'),
|
|
397
407
|
globalGroups: Schema.array(Schema.string())
|
|
398
408
|
.default([])
|
|
399
|
-
.description('额外固定推送群(可选):这些群无需订阅也会收到活动提醒(留空即可,一般用群内"
|
|
400
|
-
unsubAllNotice: Schema.string()
|
|
401
|
-
.default('本群已取消剑灵提醒推送')
|
|
402
|
-
.description('管理员用 schedule.unsuball 取消全部订阅时,发到每个被取消群的提示语(留空则不发送提示)'),
|
|
409
|
+
.description('额外固定推送群(可选):这些群无需订阅也会收到活动提醒(留空即可,一般用群内"剑灵提醒订阅"命令)'),
|
|
403
410
|
permDeniedHint: Schema.string()
|
|
404
411
|
.default('❌ 权限不足,请联系李浅浅,或成为群主/管理员')
|
|
405
412
|
.description('权限不足时的提示语(管理命令/订阅命令都会用它;可改成你自己的联系方式)'),
|
|
@@ -712,7 +719,7 @@ function apply(ctx, config) {
|
|
|
712
719
|
const target = String(rawTarget).trim()
|
|
713
720
|
if (!target) continue
|
|
714
721
|
if (bot.platform === 'qq' && /^\d+$/.test(target)) {
|
|
715
|
-
logger.warn(`[schedule] 任务 "${name}":官方QQ机器人不能用数字群号 "${target}" 发送,请用群内"
|
|
722
|
+
logger.warn(`[schedule] 任务 "${name}":官方QQ机器人不能用数字群号 "${target}" 发送,请用群内"剑灵提醒订阅"命令或 schedule.groups 查看 openid`)
|
|
716
723
|
continue
|
|
717
724
|
}
|
|
718
725
|
try {
|
|
@@ -896,6 +903,10 @@ function apply(ctx, config) {
|
|
|
896
903
|
}
|
|
897
904
|
const subCount = (subs.get(ALL) || new Map()).size
|
|
898
905
|
lines.push('', `已订阅的群:${subCount} 个(发送"订阅列表"查看)`)
|
|
906
|
+
if (acts.errors && acts.errors.length) {
|
|
907
|
+
lines.push(`⚠️ 有 ${acts.errors.length} 行看不懂、已忽略(检查写法:星期 开始-结束,时间要 HH:MM):`)
|
|
908
|
+
for (const e of acts.errors.slice(0, 5)) lines.push(` ✗ ${e}`)
|
|
909
|
+
}
|
|
899
910
|
return lines.join('\n')
|
|
900
911
|
}
|
|
901
912
|
// 旧任务模式
|
|
@@ -922,8 +933,8 @@ function apply(ctx, config) {
|
|
|
922
933
|
.action(() => {
|
|
923
934
|
logger.info('[schedule] 命令被调用: schedule')
|
|
924
935
|
return '⚙️ 活动表在插件设置页的 activities 里配置\n' +
|
|
925
|
-
'群内发"
|
|
926
|
-
'
|
|
936
|
+
'群内发"剑灵提醒订阅"即可让本群收到全部提醒\\n' +
|
|
937
|
+
'命令:剑灵提醒订阅 | 取消剑灵订阅 | 订阅列表 | schedule.list | schedule.test | schedule.debug'
|
|
927
938
|
})
|
|
928
939
|
|
|
929
940
|
ctx.command('schedule.list', '查看活动表')
|
|
@@ -997,6 +1008,10 @@ function apply(ctx, config) {
|
|
|
997
1008
|
lines.push(`⏭ 下一次提醒:「${best.inst.activity}」${when} ${hh}:${mm}(活动 ${best.inst.start}-${best.inst.end}${best.inst.place ? ' ' + best.inst.place : ''})`)
|
|
998
1009
|
}
|
|
999
1010
|
lines.push(`💾 已订阅群:${(subs.get(ALL) || new Map()).size} 个`)
|
|
1011
|
+
if (acts.errors && acts.errors.length) {
|
|
1012
|
+
lines.push(`⚠️ 有 ${acts.errors.length} 行看不懂、已忽略(检查写法:星期 开始-结束,时间要 HH:MM):`)
|
|
1013
|
+
for (const e of acts.errors.slice(0, 5)) lines.push(` ✗ ${e}`)
|
|
1014
|
+
}
|
|
1000
1015
|
const fired = Object.keys(firedToday).filter(k => k !== 'date')
|
|
1001
1016
|
lines.push(`📤 今天已发过的提醒(${firedToday.date || todayStr(now)}):${fired.length ? fired.join('、') : '无'}`)
|
|
1002
1017
|
return lines.join('\n')
|
|
@@ -1064,24 +1079,6 @@ function apply(ctx, config) {
|
|
|
1064
1079
|
return `✅ 已向本群测试发送任务 "${name}"`
|
|
1065
1080
|
})
|
|
1066
1081
|
|
|
1067
|
-
ctx.command('schedule.testcur [name:text]', '只向当前群测试发送提醒(不发订阅群)')
|
|
1068
|
-
.action(async ({ session }, name) => {
|
|
1069
|
-
logger.info(`[schedule] 命令被调用: schedule.testcur name=${name || '(第一个)'}`)
|
|
1070
|
-
const actResult = await testActivityToGroup(session, name)
|
|
1071
|
-
if (actResult !== null) return actResult
|
|
1072
|
-
const tasks = config.tasks || {}
|
|
1073
|
-
const task = tasks[name]
|
|
1074
|
-
if (!task) return `未找到任务 "${name}",可用 schedule.list 查看`
|
|
1075
|
-
if (!session.guildId) return '❌ 只能在群里使用'
|
|
1076
|
-
const testTask = { ...task, groups: [String(session.guildId)] }
|
|
1077
|
-
const rows = parseScheduleRows(task)
|
|
1078
|
-
const day = todayDay()
|
|
1079
|
-
const hit = rows.find(r => dayMatches(parseDays(r.wd), day))
|
|
1080
|
-
const sendText = hit ? (hit.text || '') : (rows.length ? (rows[0].text || '') : '')
|
|
1081
|
-
await sendTask(name, testTask, { text: sendText, markdown: task.markdown, onlyBotSid: session.bot?.sid, skipSubs: true })
|
|
1082
|
-
return `✅ 已向当前群测试发送任务 "${name}"(没有发给订阅群)`
|
|
1083
|
-
})
|
|
1084
|
-
|
|
1085
1082
|
// ===== 群内订阅命令 =====
|
|
1086
1083
|
async function isGroupAdmin(session) {
|
|
1087
1084
|
if (!session.guildId || !session.userId) return false
|
|
@@ -1132,20 +1129,20 @@ function apply(ctx, config) {
|
|
|
1132
1129
|
function listSubscribable(session) {
|
|
1133
1130
|
const lines = ['📋 剑灵提醒(订阅一次,全部活动都会收到):', '', activityOverview()]
|
|
1134
1131
|
if (session.bot && session.guildId) {
|
|
1135
|
-
lines.push('', isSubscribed(session) ? '✅
|
|
1132
|
+
lines.push('', isSubscribed(session) ? '✅ 本群已订阅(发送「取消剑灵订阅」可退订)' : '发送「剑灵提醒订阅」即可让本群收到全部提醒')
|
|
1136
1133
|
}
|
|
1137
1134
|
return lines.join('\n')
|
|
1138
1135
|
}
|
|
1139
1136
|
|
|
1140
1137
|
// 列出本群订阅状态
|
|
1141
1138
|
function listMySubs(session) {
|
|
1142
|
-
if (!isSubscribed(session)) return '本群还没有订阅提醒\n
|
|
1143
|
-
return '📋 本群已订阅全部剑灵提醒:\n\n' + activityOverview() + '\n\n
|
|
1139
|
+
if (!isSubscribed(session)) return '本群还没有订阅提醒\n发送「剑灵提醒订阅」即可订阅全部活动提醒'
|
|
1140
|
+
return '📋 本群已订阅全部剑灵提醒:\n\n' + activityOverview() + '\n\n发送「取消剑灵订阅」可退订'
|
|
1144
1141
|
}
|
|
1145
1142
|
|
|
1146
1143
|
async function subscribeCurrentGroup(session) {
|
|
1147
|
-
logger.info('[schedule] 命令被调用:
|
|
1148
|
-
if (!session.guildId || !session.bot) return '❌
|
|
1144
|
+
logger.info('[schedule] 命令被调用: 剑灵提醒订阅')
|
|
1145
|
+
if (!session.guildId || !session.bot) return '❌ 剑灵提醒订阅只能在群聊里使用'
|
|
1149
1146
|
const key = `${session.bot.sid}:${session.guildId}`
|
|
1150
1147
|
const allSubs = getTaskSubs(ALL)
|
|
1151
1148
|
if (allSubs.has(key)) {
|
|
@@ -1159,8 +1156,8 @@ function apply(ctx, config) {
|
|
|
1159
1156
|
}
|
|
1160
1157
|
|
|
1161
1158
|
async function unsubscribeCurrentGroup(session) {
|
|
1162
|
-
logger.info('[schedule] 命令被调用:
|
|
1163
|
-
if (!session.guildId || !session.bot) return '❌
|
|
1159
|
+
logger.info('[schedule] 命令被调用: 取消剑灵订阅')
|
|
1160
|
+
if (!session.guildId || !session.bot) return '❌ 取消剑灵订阅只能在群聊里使用'
|
|
1164
1161
|
const key = `${session.bot.sid}:${session.guildId}`
|
|
1165
1162
|
const allSubs = subs.get(ALL)
|
|
1166
1163
|
if (!allSubs || !allSubs.has(key)) return 'ℹ️ 本群还没有订阅提醒'
|
|
@@ -1170,15 +1167,14 @@ function apply(ctx, config) {
|
|
|
1170
1167
|
return '✅ 本群已取消全部剑灵提醒'
|
|
1171
1168
|
}
|
|
1172
1169
|
|
|
1173
|
-
ctx.command('
|
|
1174
|
-
.action(async ({ session }
|
|
1170
|
+
ctx.command('剑灵提醒订阅', '订阅全部剑灵提醒')
|
|
1171
|
+
.action(async ({ session }) => {
|
|
1175
1172
|
if (!(await checkSubscribePerm(session))) return permDeniedHint()
|
|
1176
|
-
// 兼容旧用法「订阅 任务名」:现在一律按"订阅全部"处理
|
|
1177
1173
|
return await subscribeCurrentGroup(session)
|
|
1178
1174
|
})
|
|
1179
1175
|
|
|
1180
|
-
ctx.command('
|
|
1181
|
-
.action(async ({ session }
|
|
1176
|
+
ctx.command('取消剑灵订阅', '取消本群的全部剑灵提醒')
|
|
1177
|
+
.action(async ({ session }) => {
|
|
1182
1178
|
if (!(await checkSubscribePerm(session))) return permDeniedHint()
|
|
1183
1179
|
return await unsubscribeCurrentGroup(session, taskName)
|
|
1184
1180
|
})
|
|
@@ -1187,7 +1183,7 @@ function apply(ctx, config) {
|
|
|
1187
1183
|
.action(async () => {
|
|
1188
1184
|
logger.info('[schedule] 命令被调用: 订阅列表')
|
|
1189
1185
|
const allSubs = subs.get(ALL)
|
|
1190
|
-
if (!allSubs || !allSubs.size) return '暂无任何群订阅。群内发"
|
|
1186
|
+
if (!allSubs || !allSubs.size) return '暂无任何群订阅。群内发"剑灵提醒订阅"即可订阅全部提醒'
|
|
1191
1187
|
const lines = [`📋 已订阅提醒的群(共 ${allSubs.size} 个):`]
|
|
1192
1188
|
for (const s of allSubs.values()) {
|
|
1193
1189
|
const bot = ctx.bots.find(b => b.sid === s.botSid)
|
|
@@ -1197,51 +1193,6 @@ function apply(ctx, config) {
|
|
|
1197
1193
|
return lines.join('\n')
|
|
1198
1194
|
})
|
|
1199
1195
|
|
|
1200
|
-
// 管理员:取消所有群的提醒订阅(并通知每个被取消的群)
|
|
1201
|
-
ctx.command('schedule.unsuball', '取消所有群的提醒订阅(管理员)')
|
|
1202
|
-
.action(async ({ session }) => {
|
|
1203
|
-
logger.info('[schedule] 命令被调用: schedule.unsuball')
|
|
1204
|
-
// 权限:权限等级 ≥ 3,或(开启群主绕过时)本群群主/群管理
|
|
1205
|
-
const isAdmin = (session.user?.authority >= 3) ||
|
|
1206
|
-
(config.subscribeAdminBypass && await isGroupAdmin(session))
|
|
1207
|
-
if (!isAdmin) return permDeniedHint()
|
|
1208
|
-
|
|
1209
|
-
const allSubs = subs.get(ALL)
|
|
1210
|
-
if (!allSubs || !allSubs.size) return 'ℹ️ 当前没有任何群订阅'
|
|
1211
|
-
const targets = [...allSubs.values()].map(e => ({ botSid: e.botSid, guildId: e.guildId }))
|
|
1212
|
-
|
|
1213
|
-
// 逐群发送"已取消"提示
|
|
1214
|
-
let notified = 0
|
|
1215
|
-
if (config.unsubAllNotice) {
|
|
1216
|
-
for (const t of targets) {
|
|
1217
|
-
const bot = (ctx.bots || []).find(b => b.sid === t.botSid)
|
|
1218
|
-
if (!bot || !bot.online) continue
|
|
1219
|
-
const text = String(config.unsubAllNotice).split('{task}').join('剑灵提醒')
|
|
1220
|
-
try {
|
|
1221
|
-
await bot.sendMessage(t.guildId, [text])
|
|
1222
|
-
notified++
|
|
1223
|
-
}
|
|
1224
|
-
catch (e) {
|
|
1225
|
-
logger.warn(`[schedule] 取消提示发送失败 ${t.botSid}:${t.guildId}: ${e.message}`)
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
// 清空订阅(内存 + 数据库)
|
|
1231
|
-
allSubs.clear()
|
|
1232
|
-
for (const k of [...subs.keys()]) if (k !== ALL) subs.delete(k)
|
|
1233
|
-
if (ctx.database) {
|
|
1234
|
-
try {
|
|
1235
|
-
await ctx.database.remove('ll_schedule_subs', {})
|
|
1236
|
-
}
|
|
1237
|
-
catch (e) {
|
|
1238
|
-
logger.warn('[schedule] 清空订阅表失败:', e)
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
logger.info(`[schedule] 已取消 ${targets.length} 个群的订阅(通知 ${notified} 个群)`)
|
|
1242
|
-
return `✅ 已取消 ${targets.length} 个群的订阅\n已向 ${notified} 个群发送取消提示`
|
|
1243
|
-
})
|
|
1244
|
-
|
|
1245
1196
|
ctx.command('我的ID', '查看自己的账号信息(openid/群ID/角色)')
|
|
1246
1197
|
.action(({ session }) => {
|
|
1247
1198
|
logger.info('[schedule] 命令被调用: 我的ID')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-ll-schedule",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.7",
|
|
4
4
|
"description": "Scheduled message sender with weekly slots (per-day time & content), QQ official markdown card support",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -36,4 +36,6 @@
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
|
|
40
|
+
|
|
39
41
|
|