koishi-plugin-ll-bns-fortune 0.2.0 → 0.2.2

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 (3) hide show
  1. package/lib/index.js +38 -45
  2. package/package.json +1 -1
  3. package/readme.md +1 -1
package/lib/index.js CHANGED
@@ -36,7 +36,6 @@ exports.usage = `
36
36
  {prefix}运势 查看今日运势(出图)
37
37
  {prefix}运势 图片 强制出图
38
38
  {prefix}运势 文字 强制发文字
39
- {prefix}运势 @某人 帮别人看(受 atOthers 开关控制)
40
39
 
41
40
  如果只发出文字、没有图片,说明没检测到可用的 canvas,任选一种解决:
42
41
  1) 在插件目录执行 npm i @napi-rs/canvas(推荐,预编译二进制,不需要编译工具链)
@@ -128,17 +127,6 @@ const colorsSchema = Schema.object({
128
127
  star: Schema.string().role('color').default('#C8434F').description('星星颜色'),
129
128
  }).description('配色(默认按内置底图的暖色纸面调的)')
130
129
 
131
- const perfSchema = Schema.object({
132
- outputWidth: Schema.number().min(0).max(4096).default(1280)
133
- .description('输出图片宽度(像素)。0 = 改用下面的倍率渲染。'
134
- + '默认 1280:出图快、群里也看得清;想要更锐利的文字可调到 1920(编码会慢一些)'),
135
- }).description('出图性能')
136
-
137
- const databaseSchema = Schema.object({
138
- cache: Schema.boolean().default(true).description('把当天结果存库:同一天不再重算,且支持管理员手动指定'),
139
- retentionDays: Schema.number().min(1).max(3650).default(60).description('历史记录保留天数(用于以后的历史查询功能)'),
140
- }).description('数据存储')
141
-
142
130
  exports.Config = Schema.intersect([
143
131
  /* ============ 1. 基础设置(日常只需要看这一组) ============ */
144
132
  Schema.object({
@@ -148,13 +136,13 @@ exports.Config = Schema.intersect([
148
136
  .description('指令名(第一个是主指令,其余是别名)'),
149
137
  keywordReply: Schema.boolean().default(false)
150
138
  .description('关键词触发:群里有人说到关键词就自动发运势,不用敲指令。'
151
- + '<b>注意</b>:请先把上面的「指令名」改成不常用的词(如 bns运势),否则同一条消息会被响应两次'),
139
+ + '和「指令名」重名的词会被忽略(不会重复响应),所以两边可以都开着'),
152
140
  keywords: Schema.array(Schema.string())
153
- .default(['今日运势', '运势', 'jrys', '人品', '玄学'])
154
- .description('关键词列表,也可以用正则(如「今日.*运势」)'),
141
+ .default(['人品', '玄学', '求签', '今日运气'])
142
+ .description('关键词列表,也可以用正则(如「今日.*运势」)。'
143
+ + '<b>不要填和上面「指令名」重复的词</b>,否则同一条消息会响应两次'),
155
144
  cooldown: Schema.number().role('slider').min(0).max(600).step(5).default(5)
156
145
  .description('每人冷却(秒),0 = 不限制'),
157
- atOthers: Schema.boolean().default(false).description('允许通过 @某人 查看别人的运势'),
158
146
  }).description('基础设置'),
159
147
 
160
148
  /* ============ 2. 运势内容(文案池,最常改的都是这里) ============ */
@@ -273,9 +261,8 @@ exports.Config = Schema.intersect([
273
261
 
274
262
  storage: Schema.object({
275
263
  databaseCache: Schema.boolean().default(true)
276
- .description('把当天结果存库:同一天不再重算,也支持管理员手动指定'),
277
- retentionDays: Schema.number().min(1).max(3650).default(60)
278
- .description('历史记录保留天数'),
264
+ .description('把当天结果存库:同一天不再重算(也方便以后加历史查询)。'
265
+ + '关闭后每天首次查询都要重新计算,但功能不受影响'),
279
266
  }).collapse().description('数据存储'),
280
267
  }).description('外观与输出'),
281
268
  ])
@@ -330,7 +317,7 @@ function renderCfg (config) {
330
317
  }
331
318
 
332
319
  function storageCfg (config) {
333
- return Object.assign({ databaseCache: true, retentionDays: 60 }, config && config.storage)
320
+ return Object.assign({ databaseCache: true }, config && config.storage)
334
321
  }
335
322
 
336
323
  function kwMore (config) {
@@ -542,6 +529,14 @@ exports.apply = function apply (ctx, config) {
542
529
 
543
530
  const logger = safeLogger(ctx, 'll-fortune')
544
531
 
532
+ // 折叠起来的子配置在旧配置里可能不存在,统一在最前面兜底。
533
+ // ⚠️ 必须定义在所有使用者之前:const 有暂时性死区,
534
+ // 放到后面会在启动时直接抛 "Cannot access 'xxx' before initialization"。
535
+ const geom = panelGeom(config)
536
+ const render = renderCfg(config)
537
+ const storage = storageCfg(config)
538
+ const keywordMore = kwMore(config)
539
+
545
540
  /* ---------------- 数据库表 ---------------- */
546
541
  if (storage.databaseCache && ctx.model) {
547
542
  try {
@@ -588,12 +583,12 @@ exports.apply = function apply (ctx, config) {
588
583
  }
589
584
  }
590
585
 
591
- /** 同时清掉过期记录(顺手做,失败无所谓) */
586
+ /** 顺手清掉过期记录(固定保留 60 天,不开放配置 —— 反正只是缓存,失败也无所谓) */
587
+ const RETENTION_DAYS = 60
588
+
592
589
  async function cleanup () {
593
590
  if (!useCache()) return
594
- const days = Number(storage.retentionDays) || 60
595
- if (days >= 3650) return
596
- const deadline = new Date(Date.now() - days * 86400000)
591
+ const deadline = new Date(Date.now() - RETENTION_DAYS * 86400000)
597
592
  try {
598
593
  await ctx.database.remove('ll_fortune', { createdAt: { $lt: deadline } })
599
594
  } catch (error) { /* 忽略 */ }
@@ -642,12 +637,6 @@ exports.apply = function apply (ctx, config) {
642
637
  }
643
638
 
644
639
  /* ---------------- 出图 ---------------- */
645
- // 折叠起来的子配置在旧配置里可能不存在,统一在这里兜底
646
- const geom = panelGeom(config)
647
- const render = renderCfg(config)
648
- const storage = storageCfg(config)
649
- const keywordMore = kwMore(config)
650
-
651
640
  async function makeImage (fortune, formatOverride) {
652
641
  const backgroundBuffer = await getBackground()
653
642
  const values = templateValues(fortune, config)
@@ -698,7 +687,7 @@ exports.apply = function apply (ctx, config) {
698
687
  },
699
688
  backgroundBuffer,
700
689
  })
701
- logger.info('出图完成:%dx%d %s,耗时 %d ms', result.width, result.height, result.mime, Date.now() - started)
690
+ logger.info('出图完成:%d x %d %s,耗时 %d ms', result.width, result.height, result.mime, Date.now() - started)
702
691
  return result
703
692
  }
704
693
 
@@ -801,23 +790,12 @@ exports.apply = function apply (ctx, config) {
801
790
  const raw = String(session.content || '').trim()
802
791
  const wantsText = /文字|文本|text/i.test(raw)
803
792
 
804
- let targetId = session.userId
805
- let targetName = ''
806
- if (config.atOthers && session.elements) {
807
- const at = session.elements.find(el => el && el.type === 'at' && el.attrs && el.attrs.id)
808
- if (at) {
809
- targetId = String(at.attrs.id)
810
- targetName = at.attrs.name ? String(at.attrs.name) : ''
811
- }
812
- }
813
-
814
- const self = targetId === String(session.userId)
815
- const { fortune, header } = await getFortune(targetId, targetName)
793
+ const { fortune, header } = await getFortune(session.userId, '')
816
794
 
817
795
  // 结果算完再判冷却,避免冷却拦下时白算一遍
818
- const wait = self ? checkCooldown(session.userId) : 0
796
+ const wait = checkCooldown(session.userId)
819
797
  if (wait) return `别急嘛,${wait} 秒后再来看运势吧~`
820
- if (self) lastUsed.set(session.userId, Date.now())
798
+ lastUsed.set(session.userId, Date.now())
821
799
 
822
800
  return renderReply(fortune, header, wantsText)
823
801
  })
@@ -836,7 +814,22 @@ exports.apply = function apply (ctx, config) {
836
814
  return text.toLowerCase().includes(raw.toLowerCase())
837
815
  }
838
816
 
817
+ /** 这条消息是不是在调用本插件的指令(指令名或别名)?
818
+ * 是的话就交给指令处理,关键词不再重复响应 —— 避免同一条消息发两张图 */
819
+ const commandNames = new Set(
820
+ (config.commands && config.commands.length ? config.commands : ['运势'])
821
+ .map(n => String(n).trim().toLowerCase())
822
+ .filter(Boolean),
823
+ )
824
+
825
+ function looksLikeCommand (text) {
826
+ // 取消息的第一个词,去掉可能的前缀(/ 或 !等)再比对
827
+ const first = String(text || '').trim().split(/\s+/)[0].replace(/^[!//!!.。]+/, '').toLowerCase()
828
+ return first ? commandNames.has(first) : false
829
+ }
830
+
839
831
  function keywordHit (text) {
832
+ if (looksLikeCommand(text)) return null
840
833
  const mode = keywordMore.keywordMatch || 'contains'
841
834
  for (const keyword of (config.keywords || [])) {
842
835
  if (testKeyword(text, keyword, mode)) return String(keyword).trim()
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-ll-bns-fortune",
3
3
  "description": "剑灵怀旧服每日运势:宜/忌、玄学地图、彩色五角星运势指数,自定义底图出图",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
5
  "main": "lib/index.js",
6
6
  "files": [
7
7
  "lib",
package/readme.md CHANGED
@@ -32,7 +32,7 @@ npm i @napi-rs/canvas # 出图用,预编译二进制,不需要编译工
32
32
  | `运势` | 出图(默认) |
33
33
  | `运势 文字` | 强制发文字 |
34
34
  | `运势 图片` | 强制出图 |
35
- | `运势 @某人` | 查看别人的运势,需要打开 `atOthers` |
35
+
36
36
 
37
37
  指令名、别名都能在设置页改(默认 `运势` / `今日运势` / `jrys`)。
38
38