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.
Files changed (2) hide show
  1. package/index.js +40 -89
  2. 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-13:30
20
+ 每天 13:00-15:00
21
21
 
22
- 群命令:订阅 / 取消订阅 / 订阅列表 / schedule.list / schedule.test
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) continue
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
- return acts.filter(a => a.slots.length > 0)
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}" 发送,请用群内"订阅"命令或 schedule.groups 查看 openid`)
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
- '群内发"订阅"即可让本群收到全部提醒\n' +
926
- '命令:订阅 | 取消订阅 | 订阅列表 | schedule.list | schedule.groups | schedule.test'
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('订阅 [taskName:text]', '订阅全部剑灵提醒')
1174
- .action(async ({ session }, taskName) => {
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('取消订阅 [taskName:text]', '取消本群的全部剑灵提醒')
1181
- .action(async ({ session }, taskName) => {
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.5",
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