koishi-plugin-dew-bot 1.0.244 → 1.0.245

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 CHANGED
@@ -721,6 +721,11 @@ function pageCommand(command) {
721
721
  return command.startsWith("/") ? command : `/${command}`;
722
722
  }
723
723
  __name(pageCommand, "pageCommand");
724
+ function inlineCommandUrl(command) {
725
+ const encoded = encodeURIComponent(pageCommand(command)).replace(/[!'()*]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`);
726
+ return `mqqapi://aio/inlinecmd?command=${encoded}&enter=true&reply=false`;
727
+ }
728
+ __name(inlineCommandUrl, "inlineCommandUrl");
724
729
  function pageCommandLines(nav) {
725
730
  return [
726
731
  ...nav.previousCommand ? [`上一页:${pageCommand(nav.previousCommand)}`] : [],
@@ -729,23 +734,67 @@ function pageCommandLines(nav) {
729
734
  }
730
735
  __name(pageCommandLines, "pageCommandLines");
731
736
  function renderQQPageMarkdown(data, showGreeting, showCommands) {
737
+ const nav = data.pageNav;
738
+ const entryCommands = new Map(nav?.entries?.map((entry) => [entry.lineIndex, entry.viewCommand]) ?? []);
732
739
  const sections = [];
740
+ const bodyLines = [];
741
+ const numberedLines = [];
742
+ const flushBody = /* @__PURE__ */ __name(() => {
743
+ if (bodyLines.length) sections.push(bodyLines.splice(0).join(" \n"));
744
+ }, "flushBody");
745
+ const flushNumbered = /* @__PURE__ */ __name(() => {
746
+ if (numberedLines.length) sections.push(numberedLines.splice(0).join("\n"));
747
+ }, "flushNumbered");
748
+ const flush = /* @__PURE__ */ __name(() => {
749
+ flushNumbered();
750
+ flushBody();
751
+ }, "flush");
752
+ let hasPageSummary = false;
733
753
  if (showGreeting) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
734
- for (const item of data.line) {
754
+ for (const [lineIndex, item] of data.line.entries()) {
735
755
  switch (item.type) {
736
756
  case 0 /* title */:
757
+ flush();
737
758
  sections.push(`### ${escapeMarkdown(item.content)}`);
738
759
  break;
739
- case 1 /* line */:
740
- sections.push(escapeMarkdown(item.content));
760
+ case 1 /* line */: {
761
+ const numbered = /^\[(\d+)\]\s*([^\r\n]+)$/.exec(item.content);
762
+ if (numbered) {
763
+ flushBody();
764
+ const command = entryCommands.get(lineIndex);
765
+ const label = escapeMarkdown(numbered[2]);
766
+ const name2 = command ? `[${label}](${inlineCommandUrl(command)})` : label;
767
+ numberedLines.push(`${numbered[1]}. ${name2}`);
768
+ break;
769
+ }
770
+ flushNumbered();
771
+ if (/^\[指令\]相关指令$/.test(item.content)) {
772
+ flushBody();
773
+ sections.push(`更多操作:[指令](${inlineCommandUrl("指令")})`);
774
+ break;
775
+ }
776
+ const page = /^第\s*\d+\s*\/\s*\d+\s*页(?:\s*[||]\s*共\s*(\d+)\s*([项条]))?$/.exec(item.content);
777
+ if (page && nav) {
778
+ flushBody();
779
+ sections.push(`**第 ${nav.current} / ${nav.total} 页${page[1] ? ` · 共 ${page[1]} ${page[2]}` : ""}**`);
780
+ hasPageSummary = true;
781
+ break;
782
+ }
783
+ bodyLines.push(escapeMarkdown(item.content));
741
784
  break;
785
+ }
742
786
  case 2 /* error */:
787
+ flush();
743
788
  sections.push(`❌ 出错了!错误原因:${escapeMarkdown(item.content)},请反馈至联系开发者`);
744
789
  break;
745
790
  }
746
791
  }
747
- if (showCommands && data.pageNav) {
748
- sections.push(...pageCommandLines(data.pageNav).map(escapeMarkdown));
792
+ flush();
793
+ if (nav && !hasPageSummary) sections.push(`**第 ${nav.current} / ${nav.total} 页**`);
794
+ if (showCommands && nav) {
795
+ const commands = pageCommandLines(nav);
796
+ if (commands.length) sections.push(`**翻页指令**
797
+ ${commands.map(escapeMarkdown).join(" \n")}`);
749
798
  }
750
799
  return sections.join("\n\n") || " ";
751
800
  }
@@ -783,14 +832,16 @@ async function sendQQPage(session, data, withKeyboard) {
783
832
  const nav = data.pageNav;
784
833
  const buttons = withKeyboard ? buttonsFor(nav, data.userId, session.isDirect) : [];
785
834
  const markdown = renderQQPageMarkdown(data, session.event?.channel?.type === 0, buttons.length === 0);
835
+ const hasEntries = !!nav.entries?.length;
836
+ const hasInlineHelp = data.line.some((item) => item.type === 1 /* line */ && item.content === "[指令]相关指令");
786
837
  const request = {
787
838
  // With msg_type=2, QQ requires the ordinary content field to be empty.
788
839
  content: "",
789
840
  msg_type: 2,
790
841
  markdown: {
791
- content: buttons.length && !session.isDirect ? `${markdown}
842
+ content: !session.isDirect && (buttons.length || hasEntries || hasInlineHelp) ? `${markdown}
792
843
 
793
- 群聊中点击按钮后,请发送已填入的指令。` : markdown
844
+ 点击后如未自动发送,请点发送。` : markdown
794
845
  },
795
846
  ...buttons.length ? { keyboard: { content: { rows: [{ buttons }] } } } : {}
796
847
  };
@@ -912,7 +963,7 @@ var import_tsrpc = require("tsrpc");
912
963
 
913
964
  // src/shared/bot/serviceProto.ts
914
965
  var serviceProto = {
915
- "version": 9,
966
+ "version": 10,
916
967
  "services": [
917
968
  {
918
969
  "id": 0,
@@ -1665,6 +1716,33 @@ var serviceProto = {
1665
1716
  "type": "String"
1666
1717
  },
1667
1718
  "optional": true
1719
+ },
1720
+ {
1721
+ "id": 4,
1722
+ "name": "entries",
1723
+ "type": {
1724
+ "type": "Array",
1725
+ "elementType": {
1726
+ "type": "Interface",
1727
+ "properties": [
1728
+ {
1729
+ "id": 0,
1730
+ "name": "lineIndex",
1731
+ "type": {
1732
+ "type": "Number"
1733
+ }
1734
+ },
1735
+ {
1736
+ "id": 1,
1737
+ "name": "viewCommand",
1738
+ "type": {
1739
+ "type": "String"
1740
+ }
1741
+ }
1742
+ ]
1743
+ }
1744
+ },
1745
+ "optional": true
1668
1746
  }
1669
1747
  ]
1670
1748
  },
@@ -114,6 +114,11 @@ export interface PageNavData {
114
114
  total: number;
115
115
  previousCommand?: string;
116
116
  nextCommand?: string;
117
+ /** 可点击的列表行;lineIndex 是 IMessage.line 的零起始位置。 */
118
+ entries?: Array<{
119
+ lineIndex: number;
120
+ viewCommand: string;
121
+ }>;
117
122
  }
118
123
  /** 下发给客户端的消息主体 **/
119
124
  export interface IMessage {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-dew-bot",
3
3
  "description": "连游戏服,把聊天消息交给服务器处理",
4
- "version": "1.0.244",
4
+ "version": "1.0.245",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -44,6 +44,14 @@ export function pageCommand(command: string): string {
44
44
  return command.startsWith('/') ? command : `/${command}`
45
45
  }
46
46
 
47
+ function inlineCommandUrl(command: string): string {
48
+ // encodeURIComponent leaves parentheses unescaped, which would close the
49
+ // surrounding Markdown link if an item ID ever contained one.
50
+ const encoded = encodeURIComponent(pageCommand(command)).replace(/[!'()*]/g, character =>
51
+ `%${character.charCodeAt(0).toString(16).toUpperCase()}`)
52
+ return `mqqapi://aio/inlinecmd?command=${encoded}&enter=true&reply=false`
53
+ }
54
+
47
55
  export function pageCommandLines(nav: PageNavData): string[] {
48
56
  return [
49
57
  ...(nav.previousCommand ? [`上一页:${pageCommand(nav.previousCommand)}`] : []),
@@ -52,23 +60,65 @@ export function pageCommandLines(nav: PageNavData): string[] {
52
60
  }
53
61
 
54
62
  export function renderQQPageMarkdown(data: MsgMessage, showGreeting: boolean, showCommands: boolean): string {
63
+ const nav = data.pageNav
64
+ const entryCommands = new Map(nav?.entries?.map(entry => [entry.lineIndex, entry.viewCommand]) ?? [])
55
65
  const sections: string[] = []
66
+ const bodyLines: string[] = []
67
+ const numberedLines: string[] = []
68
+ const flushBody = () => {
69
+ if (bodyLines.length) sections.push(bodyLines.splice(0).join(' \n'))
70
+ }
71
+ const flushNumbered = () => {
72
+ if (numberedLines.length) sections.push(numberedLines.splice(0).join('\n'))
73
+ }
74
+ const flush = () => { flushNumbered(); flushBody() }
75
+ let hasPageSummary = false
56
76
  if (showGreeting) sections.push(escapeMarkdown('Ciallo~(∠・ω< )'))
57
- for (const item of data.line) {
77
+ for (const [lineIndex, item] of data.line.entries()) {
58
78
  switch (item.type) {
59
79
  case MessageType.title:
80
+ flush()
60
81
  sections.push(`### ${escapeMarkdown(item.content)}`)
61
82
  break
62
- case MessageType.line:
63
- sections.push(escapeMarkdown(item.content))
83
+ case MessageType.line: {
84
+ const numbered = /^\[(\d+)\]\s*([^\r\n]+)$/.exec(item.content)
85
+ if (numbered) {
86
+ flushBody()
87
+ const command = entryCommands.get(lineIndex)
88
+ const label = escapeMarkdown(numbered[2])
89
+ const name = command
90
+ ? `[${label}](${inlineCommandUrl(command)})`
91
+ : label
92
+ numberedLines.push(`${numbered[1]}. ${name}`)
93
+ break
94
+ }
95
+ flushNumbered()
96
+ if (/^\[指令\]相关指令$/.test(item.content)) {
97
+ flushBody()
98
+ sections.push(`更多操作:[指令](${inlineCommandUrl('指令')})`)
99
+ break
100
+ }
101
+ const page = /^第\s*\d+\s*\/\s*\d+\s*页(?:\s*[||]\s*共\s*(\d+)\s*([项条]))?$/.exec(item.content)
102
+ if (page && nav) {
103
+ flushBody()
104
+ sections.push(`**第 ${nav.current} / ${nav.total} 页${page[1] ? ` · 共 ${page[1]} ${page[2]}` : ''}**`)
105
+ hasPageSummary = true
106
+ break
107
+ }
108
+ bodyLines.push(escapeMarkdown(item.content))
64
109
  break
110
+ }
65
111
  case MessageType.error:
112
+ flush()
66
113
  sections.push(`❌ 出错了!错误原因:${escapeMarkdown(item.content)},请反馈至联系开发者`)
67
114
  break
68
115
  }
69
116
  }
70
- if (showCommands && data.pageNav) {
71
- sections.push(...pageCommandLines(data.pageNav).map(escapeMarkdown))
117
+ flush()
118
+ if (nav && !hasPageSummary) sections.push(`**第 ${nav.current} / ${nav.total} 页**`)
119
+ if (showCommands && nav) {
120
+ const commands = pageCommandLines(nav)
121
+ if (commands.length) sections.push(`**翻页指令** \n${commands.map(escapeMarkdown).join(' \n')}`)
72
122
  }
73
123
  return sections.join('\n\n') || ' '
74
124
  }
@@ -108,13 +158,15 @@ export async function sendQQPage(session: Session, data: MsgMessage, withKeyboar
108
158
  const nav = data.pageNav!
109
159
  const buttons = withKeyboard ? buttonsFor(nav, data.userId, session.isDirect) : []
110
160
  const markdown = renderQQPageMarkdown(data, session.event?.channel?.type === 0, buttons.length === 0)
161
+ const hasEntries = !!nav.entries?.length
162
+ const hasInlineHelp = data.line.some(item => item.type === MessageType.line && item.content === '[指令]相关指令')
111
163
  const request: QQRequest = {
112
164
  // With msg_type=2, QQ requires the ordinary content field to be empty.
113
165
  content: '',
114
166
  msg_type: 2,
115
167
  markdown: {
116
- content: buttons.length && !session.isDirect
117
- ? `${markdown}\n\n群聊中点击按钮后,请发送已填入的指令。`
168
+ content: !session.isDirect && (buttons.length || hasEntries || hasInlineHelp)
169
+ ? `${markdown}\n\n点击后如未自动发送,请点发送。`
118
170
  : markdown,
119
171
  },
120
172
  ...(buttons.length ? { keyboard: { content: { rows: [{ buttons }] } } } : {}),
@@ -20,7 +20,7 @@ export interface ServiceType {
20
20
  }
21
21
 
22
22
  export const serviceProto: ServiceProto<ServiceType> = {
23
- "version": 9,
23
+ "version": 10,
24
24
  "services": [
25
25
  {
26
26
  "id": 0,
@@ -773,6 +773,33 @@ export const serviceProto: ServiceProto<ServiceType> = {
773
773
  "type": "String"
774
774
  },
775
775
  "optional": true
776
+ },
777
+ {
778
+ "id": 4,
779
+ "name": "entries",
780
+ "type": {
781
+ "type": "Array",
782
+ "elementType": {
783
+ "type": "Interface",
784
+ "properties": [
785
+ {
786
+ "id": 0,
787
+ "name": "lineIndex",
788
+ "type": {
789
+ "type": "Number"
790
+ }
791
+ },
792
+ {
793
+ "id": 1,
794
+ "name": "viewCommand",
795
+ "type": {
796
+ "type": "String"
797
+ }
798
+ }
799
+ ]
800
+ }
801
+ },
802
+ "optional": true
776
803
  }
777
804
  ]
778
805
  },
@@ -89,6 +89,8 @@ export interface PageNavData {
89
89
  total: number;
90
90
  previousCommand?: string;
91
91
  nextCommand?: string;
92
+ /** 可点击的列表行;lineIndex 是 IMessage.line 的零起始位置。 */
93
+ entries?: Array<{ lineIndex: number; viewCommand: string }>;
92
94
  }
93
95
 
94
96
  /** 下发给客户端的消息主体 **/