koishi-plugin-dew-bot 1.0.260 → 1.0.261

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.d.ts CHANGED
@@ -3,7 +3,7 @@ export declare const inject: {
3
3
  optional: string[];
4
4
  };
5
5
  export declare const name = "dew-bot";
6
- export declare const usage: string;
6
+ export declare const filter = false;
7
7
  export interface Config {
8
8
  服务器地址: string;
9
9
  }
package/lib/index.js CHANGED
@@ -2374,9 +2374,9 @@ var src_exports = {};
2374
2374
  __export(src_exports, {
2375
2375
  Config: () => Config,
2376
2376
  apply: () => apply2,
2377
+ filter: () => filter,
2377
2378
  inject: () => inject,
2378
- name: () => name,
2379
- usage: () => usage
2379
+ name: () => name
2380
2380
  });
2381
2381
  module.exports = __toCommonJS(src_exports);
2382
2382
  var import_koishi2 = require("koishi");
@@ -3945,6 +3945,52 @@ function renderQQTitle(content) {
3945
3945
  }
3946
3946
  __name(renderQQTitle, "renderQQTitle");
3947
3947
 
3948
+ // src/plugin/serverHandel/qqBattleLogLinks.ts
3949
+ function battleLogLinkSpans(content) {
3950
+ const links = [];
3951
+ const pattern = /\[([^\[\]\r\n]+)\]\((https?:\/\/[^\s<>()]+)\)/g;
3952
+ for (const match of content.matchAll(pattern)) {
3953
+ try {
3954
+ const url = new URL(match[2]);
3955
+ const id = url.searchParams.get("id") ?? "";
3956
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.hash || url.pathname !== "/battlelog" || [...url.searchParams].length !== 1 || !/^[1-9]\d{0,15}$/.test(id) || !Number.isSafeInteger(Number(id))) continue;
3957
+ links.push({ start: match.index, end: match.index + match[0].length, label: match[1], url: url.href, id });
3958
+ } catch {
3959
+ }
3960
+ }
3961
+ return links;
3962
+ }
3963
+ __name(battleLogLinkSpans, "battleLogLinkSpans");
3964
+ function renderBattleLogLink(link) {
3965
+ return "[" + escapeMarkdown(link.label) + "](" + link.url + ")";
3966
+ }
3967
+ __name(renderBattleLogLink, "renderBattleLogLink");
3968
+ function renderBattleLogLinkText(content) {
3969
+ const parts = [];
3970
+ let offset = 0;
3971
+ for (const link of battleLogLinkSpans(content)) {
3972
+ parts.push(escapeMarkdown(content.slice(offset, link.start)), renderBattleLogLink(link));
3973
+ offset = link.end;
3974
+ }
3975
+ parts.push(escapeMarkdown(content.slice(offset)));
3976
+ return parts.join("");
3977
+ }
3978
+ __name(renderBattleLogLinkText, "renderBattleLogLinkText");
3979
+ function battleLogMessageLinks(data) {
3980
+ const links = /* @__PURE__ */ new Map();
3981
+ for (const line of data.line) {
3982
+ if (line.type !== 1 /* line */ || line.codeBlock || line.mailPart === "body") continue;
3983
+ for (const link of battleLogLinkSpans(line.content)) if (!links.has(link.url)) links.set(link.url, link);
3984
+ }
3985
+ return [...links.values()];
3986
+ }
3987
+ __name(battleLogMessageLinks, "battleLogMessageLinks");
3988
+ function appendMissingBattleLogLinks(markdown, data) {
3989
+ const missing = battleLogMessageLinks(data).map(renderBattleLogLink).filter((link) => !markdown.includes(link));
3990
+ return [markdown, ...missing].filter(Boolean).join("\n\n");
3991
+ }
3992
+ __name(appendMissingBattleLogLinks, "appendMissingBattleLogLinks");
3993
+
3948
3994
  // src/plugin/serverHandel/qqRewardMessage.ts
3949
3995
  function hasRewardLines(data) {
3950
3996
  return data.line.some((item) => item.type === 1 /* line */ && item.reward === true);
@@ -4005,9 +4051,9 @@ function renderQQMailMarkdown(data) {
4005
4051
  }
4006
4052
  __name(renderQQMailMarkdown, "renderQQMailMarkdown");
4007
4053
  function renderQQRewardMarkdown(data, showGreeting) {
4008
- if (data.mail) return renderQQMailMarkdown(data);
4054
+ if (data.mail) return appendMissingBattleLogLinks(renderQQMailMarkdown(data), data);
4009
4055
  const rewards = data.line.filter((item) => item.type === 1 /* line */ && item.reward).map((item) => renderRewardLine(item.content)).filter(Boolean);
4010
- if (rewards.length && !data.mail) return "🎁获得奖励\n\n" + rewards.join(" \n");
4056
+ if (rewards.length && !data.mail) return appendMissingBattleLogLinks("🎁获得奖励\n\n" + rewards.join(" \n"), data);
4011
4057
  const sections = [];
4012
4058
  const bodyLines = [];
4013
4059
  const flushBody = /* @__PURE__ */ __name(() => {
@@ -4022,7 +4068,7 @@ function renderQQRewardMarkdown(data, showGreeting) {
4022
4068
  } else if (item.type === 1 /* line */) {
4023
4069
  for (const line of item.content.replace(/\r\n?/g, "\n").split("\n")) {
4024
4070
  if (!line.trim()) flushBody();
4025
- else bodyLines.push(escapeMarkdown(line));
4071
+ else bodyLines.push(renderBattleLogLinkText(line));
4026
4072
  }
4027
4073
  } else if (item.type === 2 /* error */) {
4028
4074
  bodyLines.push(`❌ ${escapeMarkdown(item.content)}`);
@@ -4030,7 +4076,7 @@ function renderQQRewardMarkdown(data, showGreeting) {
4030
4076
  }
4031
4077
  flushBody();
4032
4078
  if (rewards.length) sections.push("🎁获得奖励\n\n" + rewards.join(" \n"));
4033
- return sections.join("\n\n") || " ";
4079
+ return appendMissingBattleLogLinks(sections.join("\n\n") || " ", data);
4034
4080
  }
4035
4081
  __name(renderQQRewardMarkdown, "renderQQRewardMarkdown");
4036
4082
 
@@ -4070,7 +4116,8 @@ function inlineCommandSpans(content) {
4070
4116
  if (command) spans.push({ start, end: index + 1, label, command });
4071
4117
  }
4072
4118
  }
4073
- return spans;
4119
+ const logLinks = battleLogLinkSpans(content);
4120
+ return spans.filter((span) => !logLinks.some((link) => span.start < link.end && span.end > link.start));
4074
4121
  }
4075
4122
  __name(inlineCommandSpans, "inlineCommandSpans");
4076
4123
  function hasInlineCommands(content) {
@@ -4080,9 +4127,12 @@ __name(hasInlineCommands, "hasInlineCommands");
4080
4127
  function renderInlineCommands(content) {
4081
4128
  const parts = [];
4082
4129
  let offset = 0;
4083
- for (const span of inlineCommandSpans(content)) {
4084
- parts.push(escapeMarkdown(content.slice(offset, span.start)));
4085
- parts.push("[" + escapeMarkdown(span.label) + "](" + inlineCommandUrl(span.command) + ")");
4130
+ const spans = [
4131
+ ...inlineCommandSpans(content).map((span) => ({ ...span, rendered: "[" + escapeMarkdown(span.label) + "](" + inlineCommandUrl(span.command) + ")" })),
4132
+ ...battleLogLinkSpans(content).map((span) => ({ ...span, rendered: renderBattleLogLink(span) }))
4133
+ ].sort((a, b) => a.start - b.start);
4134
+ for (const span of spans) {
4135
+ parts.push(escapeMarkdown(content.slice(offset, span.start)), span.rendered);
4086
4136
  offset = span.end;
4087
4137
  }
4088
4138
  parts.push(escapeMarkdown(content.slice(offset)));
@@ -4592,14 +4642,14 @@ function renderQQStoryMarkdown(data, showGreeting) {
4592
4642
  } else {
4593
4643
  for (const line of item.content.replace(/\r\n?/g, "\n").split("\n")) {
4594
4644
  if (!line.trim()) flushBody();
4595
- else bodyLines.push(data.reward ? escapeMarkdown(line) : renderInlineCommands(line));
4645
+ else bodyLines.push(data.reward ? renderBattleLogLinkText(line) : renderInlineCommands(line));
4596
4646
  }
4597
4647
  }
4598
4648
  break;
4599
4649
  case 2 /* error */:
4600
4650
  flushBody();
4601
4651
  if (!data.reward) flushRewards();
4602
- sections.push("❌ 出错了!错误原因:" + (data.reward ? escapeMarkdown(item.content) : renderInlineCommands(item.content)) + ",请反馈至联系开发者");
4652
+ sections.push("❌ 出错了!错误原因:" + (data.reward ? renderBattleLogLinkText(item.content) : renderInlineCommands(item.content)) + ",请反馈至联系开发者");
4603
4653
  break;
4604
4654
  }
4605
4655
  }
@@ -4619,7 +4669,7 @@ function renderQQMessagePreview(data, showGreeting = false) {
4619
4669
  if (data.transaction) return { kind: "transaction", markdown: renderQQTransactionMarkdown(data, showGreeting), text };
4620
4670
  if (data.pageNav) return { kind: "page", markdown: renderQQPageMarkdown(data, showGreeting), text };
4621
4671
  if (isRewardMessage(data)) return { kind: "reward", markdown: renderQQRewardMarkdown(data, showGreeting), text };
4622
- if (data.line.some((item) => (item.type === 1 /* line */ || item.type === 2 /* error */) && hasInlineCommands(item.content))) {
4672
+ if (battleLogMessageLinks(data).length || data.line.some((item) => (item.type === 1 /* line */ || item.type === 2 /* error */) && hasInlineCommands(item.content))) {
4623
4673
  return { kind: "command", markdown: renderQQCommandMarkdown(data, showGreeting), text };
4624
4674
  }
4625
4675
  return { kind: "text", text };
@@ -4814,10 +4864,7 @@ __name(apply, "apply");
4814
4864
  var import_node_path2 = require("node:path");
4815
4865
  var inject = { optional: ["puppeteer", "console"] };
4816
4866
  var name = "dew-bot";
4817
- var usage = `
4818
- ## 🌀 达尔文进化岛 · 文字RPG机器人
4819
- **一款基于《无限恐怖》的文字RPG**
4820
- `;
4867
+ var filter = false;
4821
4868
  var Config = import_koishi2.Schema.object({
4822
4869
  服务器地址: import_koishi2.Schema.string().default("ws://dew-v7-test:3021").description("DEW 游戏服")
4823
4870
  });
@@ -4836,7 +4883,7 @@ __name(apply2, "apply");
4836
4883
  0 && (module.exports = {
4837
4884
  Config,
4838
4885
  apply,
4886
+ filter,
4839
4887
  inject,
4840
- name,
4841
- usage
4888
+ name
4842
4889
  });
@@ -0,0 +1,16 @@
1
+ import { type IMessage } from '../../shared/face';
2
+ export type BattleLogLinkSpan = {
3
+ start: number;
4
+ end: number;
5
+ label: string;
6
+ url: string;
7
+ id: string;
8
+ };
9
+ /** Only the dedicated battle-log route can carry server-supplied external links. */
10
+ export declare function battleLogLinkSpans(content: string): BattleLogLinkSpan[];
11
+ export declare function renderBattleLogLink(link: BattleLogLinkSpan): string;
12
+ /** Keep every other Markdown token literal while preserving verified log links. */
13
+ export declare function renderBattleLogLinkText(content: string): string;
14
+ export declare function battleLogMessageLinks(data: Pick<IMessage, 'line'>): BattleLogLinkSpan[];
15
+ /** Compact reward cards omit ordinary details, so add their log links explicitly. */
16
+ export declare function appendMissingBattleLogLinks(markdown: string, data: Pick<IMessage, 'line'>): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-dew-bot",
3
3
  "description": "基于《无限恐怖》小说改编的 RPG 机器人。踏入主神空间,与伙伴迎接轮回试炼,书写属于你的无限冒险。",
4
- "version": "1.0.260",
4
+ "version": "1.0.261",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/src/index.ts CHANGED
@@ -6,11 +6,7 @@ import type {} from '@koishijs/plugin-console'
6
6
  export const inject = { optional: ['puppeteer', 'console'] }
7
7
 
8
8
  export const name = 'dew-bot'
9
-
10
- export const usage: string = `
11
- ## 🌀 达尔文进化岛 · 文字RPG机器人
12
- **一款基于《无限恐怖》的文字RPG**
13
- `
9
+ export const filter = false
14
10
 
15
11
  export interface Config {
16
12
  服务器地址: string
@@ -0,0 +1,52 @@
1
+ import { MessageType, type IMessage } from '../../shared/face'
2
+ import { escapeMarkdown } from './qqMarkdownHelpers'
3
+
4
+ export type BattleLogLinkSpan = { start: number; end: number; label: string; url: string; id: string }
5
+
6
+ /** Only the dedicated battle-log route can carry server-supplied external links. */
7
+ export function battleLogLinkSpans(content: string): BattleLogLinkSpan[] {
8
+ const links: BattleLogLinkSpan[] = []
9
+ const pattern = /\[([^\[\]\r\n]+)\]\((https?:\/\/[^\s<>()]+)\)/g
10
+ for (const match of content.matchAll(pattern)) {
11
+ try {
12
+ const url = new URL(match[2])
13
+ const id = url.searchParams.get('id') ?? ''
14
+ if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.hash
15
+ || url.pathname !== '/battlelog' || [...url.searchParams].length !== 1
16
+ || !/^[1-9]\d{0,15}$/.test(id) || !Number.isSafeInteger(Number(id))) continue
17
+ links.push({ start: match.index!, end: match.index! + match[0].length, label: match[1], url: url.href, id })
18
+ } catch { /* Ordinary prose and malformed links remain escaped text. */ }
19
+ }
20
+ return links
21
+ }
22
+
23
+ export function renderBattleLogLink(link: BattleLogLinkSpan): string {
24
+ return '[' + escapeMarkdown(link.label) + '](' + link.url + ')'
25
+ }
26
+
27
+ /** Keep every other Markdown token literal while preserving verified log links. */
28
+ export function renderBattleLogLinkText(content: string): string {
29
+ const parts: string[] = []
30
+ let offset = 0
31
+ for (const link of battleLogLinkSpans(content)) {
32
+ parts.push(escapeMarkdown(content.slice(offset, link.start)), renderBattleLogLink(link))
33
+ offset = link.end
34
+ }
35
+ parts.push(escapeMarkdown(content.slice(offset)))
36
+ return parts.join('')
37
+ }
38
+
39
+ export function battleLogMessageLinks(data: Pick<IMessage, 'line'>): BattleLogLinkSpan[] {
40
+ const links = new Map<string, BattleLogLinkSpan>()
41
+ for (const line of data.line) {
42
+ if (line.type !== MessageType.line || line.codeBlock || line.mailPart === 'body') continue
43
+ for (const link of battleLogLinkSpans(line.content)) if (!links.has(link.url)) links.set(link.url, link)
44
+ }
45
+ return [...links.values()]
46
+ }
47
+
48
+ /** Compact reward cards omit ordinary details, so add their log links explicitly. */
49
+ export function appendMissingBattleLogLinks(markdown: string, data: Pick<IMessage, 'line'>): string {
50
+ const missing = battleLogMessageLinks(data).map(renderBattleLogLink).filter(link => !markdown.includes(link))
51
+ return [markdown, ...missing].filter(Boolean).join('\n\n')
52
+ }
@@ -4,6 +4,7 @@ import { hasInlineCommands, renderQQPageMarkdown } from './qqPageMessage'
4
4
  import { renderQQCommandMarkdown } from './qqCommandMessage'
5
5
  import { renderQQTransactionMarkdown } from './qqTransactionMessage'
6
6
  import { hasStoryCodeBlock, renderQQStoryMarkdown } from './qqStoryMessage'
7
+ import { battleLogMessageLinks } from './qqBattleLogLinks'
7
8
  import { isRewardMessage, renderQQRewardMarkdown } from './qqRewardMessage'
8
9
 
9
10
  export type QQMessageKind = 'text' | 'story' | 'command' | 'transaction' | 'page' | 'reward'
@@ -26,7 +27,7 @@ export function renderQQMessagePreview(data: IMessage, showGreeting = false): QQ
26
27
  if (data.transaction) return { kind: 'transaction', markdown: renderQQTransactionMarkdown(data, showGreeting), text }
27
28
  if (data.pageNav) return { kind: 'page', markdown: renderQQPageMarkdown(data, showGreeting), text }
28
29
  if (isRewardMessage(data)) return { kind: 'reward', markdown: renderQQRewardMarkdown(data, showGreeting), text }
29
- if (data.line.some(item => (item.type === MessageType.line || item.type === MessageType.error) && hasInlineCommands(item.content))) {
30
+ if (battleLogMessageLinks(data).length || data.line.some(item => (item.type === MessageType.line || item.type === MessageType.error) && hasInlineCommands(item.content))) {
30
31
  return { kind: 'command', markdown: renderQQCommandMarkdown(data, showGreeting), text }
31
32
  }
32
33
  return { kind: 'text', text }
@@ -4,6 +4,7 @@ import type { MsgMessage } from '../../shared/bot/MsgMessage'
4
4
  import { compactDisplayText, escapeMarkdown, renderQQTitle } from './qqMarkdownHelpers'
5
5
  import { isRewardMessage, renderRewardLine, renderRewardTitle } from './qqRewardMessage'
6
6
  import { artworkIdentity } from '../skillArtwork'
7
+ import { battleLogLinkSpans, renderBattleLogLink } from './qqBattleLogLinks'
7
8
 
8
9
  export { escapeMarkdown, renderQQTitle } from './qqMarkdownHelpers'
9
10
 
@@ -74,7 +75,8 @@ function inlineCommandSpans(content: string): InlineCommandSpan[] {
74
75
  if (command) spans.push({ start, end: index + 1, label, command })
75
76
  }
76
77
  }
77
- return spans
78
+ const logLinks = battleLogLinkSpans(content)
79
+ return spans.filter(span => !logLinks.some(link => span.start < link.end && span.end > link.start))
78
80
  }
79
81
 
80
82
  export function hasInlineCommands(content: string): boolean {
@@ -85,9 +87,12 @@ export function hasInlineCommands(content: string): boolean {
85
87
  export function renderInlineCommands(content: string): string {
86
88
  const parts: string[] = []
87
89
  let offset = 0
88
- for (const span of inlineCommandSpans(content)) {
89
- parts.push(escapeMarkdown(content.slice(offset, span.start)))
90
- parts.push('[' + escapeMarkdown(span.label) + '](' + inlineCommandUrl(span.command) + ')')
90
+ const spans = [
91
+ ...inlineCommandSpans(content).map(span => ({ ...span, rendered: '[' + escapeMarkdown(span.label) + '](' + inlineCommandUrl(span.command) + ')' })),
92
+ ...battleLogLinkSpans(content).map(span => ({ ...span, rendered: renderBattleLogLink(span) })),
93
+ ].sort((a, b) => a.start - b.start)
94
+ for (const span of spans) {
95
+ parts.push(escapeMarkdown(content.slice(offset, span.start)), span.rendered)
91
96
  offset = span.end
92
97
  }
93
98
  parts.push(escapeMarkdown(content.slice(offset)))
@@ -1,5 +1,6 @@
1
1
  import { MessageType, type IMessage } from '../../shared/face'
2
2
  import { compactDisplayText, escapeMarkdown } from './qqMarkdownHelpers'
3
+ import { appendMissingBattleLogLinks, renderBattleLogLinkText } from './qqBattleLogLinks'
3
4
 
4
5
  export function hasRewardLines(data: Pick<IMessage, 'line'>): boolean {
5
6
  return data.line.some(item => item.type === MessageType.line && item.reward === true)
@@ -67,10 +68,10 @@ function renderQQMailMarkdown(data: IMessage): string {
67
68
 
68
69
  /** Reward cards stay compact; mail keeps its content before the attachment quote. */
69
70
  export function renderQQRewardMarkdown(data: IMessage, showGreeting: boolean): string {
70
- if (data.mail) return renderQQMailMarkdown(data)
71
+ if (data.mail) return appendMissingBattleLogLinks(renderQQMailMarkdown(data), data)
71
72
  const rewards = data.line.filter(item => item.type === MessageType.line && item.reward)
72
73
  .map(item => renderRewardLine(item.content)).filter(Boolean)
73
- if (rewards.length && !data.mail) return '🎁获得奖励\n\n' + rewards.join(' \n')
74
+ if (rewards.length && !data.mail) return appendMissingBattleLogLinks('🎁获得奖励\n\n' + rewards.join(' \n'), data)
74
75
 
75
76
  const sections: string[] = []
76
77
  const bodyLines: string[] = []
@@ -86,7 +87,7 @@ export function renderQQRewardMarkdown(data: IMessage, showGreeting: boolean): s
86
87
  } else if (item.type === MessageType.line) {
87
88
  for (const line of item.content.replace(/\r\n?/g, '\n').split('\n')) {
88
89
  if (!line.trim()) flushBody()
89
- else bodyLines.push(escapeMarkdown(line))
90
+ else bodyLines.push(renderBattleLogLinkText(line))
90
91
  }
91
92
  } else if (item.type === MessageType.error) {
92
93
  bodyLines.push(`❌ ${escapeMarkdown(item.content)}`)
@@ -94,5 +95,5 @@ export function renderQQRewardMarkdown(data: IMessage, showGreeting: boolean): s
94
95
  }
95
96
  flushBody()
96
97
  if (rewards.length) sections.push('🎁获得奖励\n\n' + rewards.join(' \n'))
97
- return sections.join('\n\n') || ' '
98
+ return appendMissingBattleLogLinks(sections.join('\n\n') || ' ', data)
98
99
  }
@@ -2,6 +2,7 @@ import type { Session } from 'koishi'
2
2
  import { MessageType, type IMessage } from '../../shared/face'
3
3
  import type { MsgMessage } from '../../shared/bot/MsgMessage'
4
4
  import { escapeMarkdown, renderInlineCommands, renderQQTitle, sendQQMarkdown } from './qqPageMessage'
5
+ import { renderBattleLogLinkText } from './qqBattleLogLinks'
5
6
  import { isRewardMessage, renderQQRewardMarkdown, renderRewardLine, renderRewardTitle } from './qqRewardMessage'
6
7
 
7
8
  export function hasStoryCodeBlock(data: Pick<IMessage, 'line'>): boolean {
@@ -57,14 +58,14 @@ export function renderQQStoryMarkdown(data: IMessage, showGreeting: boolean): st
57
58
  } else {
58
59
  for (const line of item.content.replace(/\r\n?/g, '\n').split('\n')) {
59
60
  if (!line.trim()) flushBody()
60
- else bodyLines.push(data.reward ? escapeMarkdown(line) : renderInlineCommands(line))
61
+ else bodyLines.push(data.reward ? renderBattleLogLinkText(line) : renderInlineCommands(line))
61
62
  }
62
63
  }
63
64
  break
64
65
  case MessageType.error:
65
66
  flushBody()
66
67
  if (!data.reward) flushRewards()
67
- sections.push('❌ 出错了!错误原因:' + (data.reward ? escapeMarkdown(item.content) : renderInlineCommands(item.content)) + ',请反馈至联系开发者')
68
+ sections.push('❌ 出错了!错误原因:' + (data.reward ? renderBattleLogLinkText(item.content) : renderInlineCommands(item.content)) + ',请反馈至联系开发者')
68
69
  break
69
70
  }
70
71
  }