koishi-plugin-dew-bot 1.0.249 → 1.0.250

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
@@ -839,8 +839,12 @@ function canSendQQPage(session, data) {
839
839
  }
840
840
  __name(canSendQQPage, "canSendQQPage");
841
841
  async function sendQQPage(session, data) {
842
- const internal = session.bot.internal;
843
842
  const markdown = renderQQPageMarkdown(data, session.event?.channel?.type === 0);
843
+ await sendQQMarkdown(session, markdown);
844
+ }
845
+ __name(sendQQPage, "sendQQPage");
846
+ async function sendQQMarkdown(session, markdown) {
847
+ const internal = session.bot.internal;
844
848
  const request = {
845
849
  // With msg_type=2, QQ requires the ordinary content field to be empty.
846
850
  content: "",
@@ -858,7 +862,63 @@ async function sendQQPage(session, data) {
858
862
  const pendingAudit = response?.code === 304023 && !!response.data?.message_audit?.audit_id;
859
863
  if (!response?.id && !response?.audit_id && !pendingAudit) throw new Error("QQ 未确认消息已接收");
860
864
  }
861
- __name(sendQQPage, "sendQQPage");
865
+ __name(sendQQMarkdown, "sendQQMarkdown");
866
+
867
+ // src/plugin/serverHandel/qqTransactionMessage.ts
868
+ function renderQQTransactionMarkdown(data, showGreeting) {
869
+ const sections = [];
870
+ const bodyLines = [];
871
+ const actions = [];
872
+ const flushBody = /* @__PURE__ */ __name(() => {
873
+ if (bodyLines.length) sections.push(bodyLines.splice(0).join(" \n"));
874
+ }, "flushBody");
875
+ const flushActions = /* @__PURE__ */ __name(() => {
876
+ if (actions.length) sections.push(actions.splice(0).join(" · "));
877
+ }, "flushActions");
878
+ const flush = /* @__PURE__ */ __name(() => {
879
+ flushBody();
880
+ flushActions();
881
+ }, "flush");
882
+ if (showGreeting) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
883
+ for (const item of data.line) {
884
+ if (item.type === 0 /* title */) {
885
+ flush();
886
+ sections.push(`**${escapeMarkdown(item.content)}**`);
887
+ continue;
888
+ }
889
+ if (item.type === 2 /* error */) {
890
+ flush();
891
+ sections.push(`❌ 出错了!错误原因:${escapeMarkdown(item.content)},请反馈至联系开发者`);
892
+ continue;
893
+ }
894
+ if (item.type !== 1 /* line */) continue;
895
+ const action = data.transaction && (item.content === "[是]确认交易" ? { command: "是", label: "确认交易" } : item.content === "[否]取消交易" ? { command: "否", label: "取消交易" } : void 0);
896
+ if (action) {
897
+ flushBody();
898
+ actions.push(`[${action.command}](${inlineCommandUrl(action.command)}) ${action.label}`);
899
+ continue;
900
+ }
901
+ flushActions();
902
+ if (!item.content.trim()) {
903
+ flushBody();
904
+ continue;
905
+ }
906
+ const section = /^┄━═(.+)═━┄$/.exec(item.content);
907
+ if (section || item.content === "【还缺】") {
908
+ flushBody();
909
+ sections.push(`**${escapeMarkdown(section?.[1] ?? "还缺")}**`);
910
+ } else {
911
+ bodyLines.push(escapeMarkdown(item.content));
912
+ }
913
+ }
914
+ flush();
915
+ return sections.join("\n\n") || " ";
916
+ }
917
+ __name(renderQQTransactionMarkdown, "renderQQTransactionMarkdown");
918
+ async function sendQQTransaction(session, data) {
919
+ await sendQQMarkdown(session, renderQQTransactionMarkdown(data, session.event?.channel?.type === 0));
920
+ }
921
+ __name(sendQQTransaction, "sendQQTransaction");
862
922
 
863
923
  // src/plugin/serverHandel/text.ts
864
924
  var handelr_text = class extends handel_base {
@@ -868,10 +928,11 @@ var handelr_text = class extends handel_base {
868
928
  async start(data) {
869
929
  const content = new AnalysisMsg().parse(data);
870
930
  const session = sessions_default.get(sessionKey(data.botId, data.userId, data.channelId));
871
- if (data.pageNav && canSendQQPage(session, data)) {
931
+ if ((data.transaction || data.pageNav) && canSendQQPage(session, data)) {
872
932
  const sendStartedAt = this.timing ? performance.now() : 0;
873
933
  try {
874
- await sendQQPage(session, data);
934
+ if (data.transaction) await sendQQTransaction(session, data);
935
+ else await sendQQPage(session, data);
875
936
  if (this.timing) {
876
937
  this.timing.delivery = "qq-markdown";
877
938
  this.timing.sendMs = performance.now() - sendStartedAt;
@@ -880,7 +941,7 @@ var handelr_text = class extends handel_base {
880
941
  } catch (error) {
881
942
  this.ctx.logger.warn("QQ Markdown 发送失败,改用普通文字", error instanceof Error ? error.message : error);
882
943
  }
883
- const commands = pageCommandLines(data.pageNav);
944
+ const commands = !data.transaction && data.pageNav ? pageCommandLines(data.pageNav) : [];
884
945
  await this.send(data, commands.length ? `${content.trimEnd()}
885
946
  ${commands.join("\n")}` : content);
886
947
  return;
@@ -953,7 +1014,7 @@ var import_tsrpc = require("tsrpc");
953
1014
 
954
1015
  // src/shared/bot/serviceProto.ts
955
1016
  var serviceProto = {
956
- "version": 11,
1017
+ "version": 12,
957
1018
  "services": [
958
1019
  {
959
1020
  "id": 0,
@@ -1088,6 +1149,14 @@ var serviceProto = {
1088
1149
  },
1089
1150
  "optional": true
1090
1151
  },
1152
+ {
1153
+ "id": 8,
1154
+ "name": "transaction",
1155
+ "type": {
1156
+ "type": "Boolean"
1157
+ },
1158
+ "optional": true
1159
+ },
1091
1160
  {
1092
1161
  "id": 1,
1093
1162
  "name": "line",
@@ -1,9 +1,14 @@
1
1
  import type { Session } from 'koishi';
2
2
  import { type PageNavData } from '../../shared/face';
3
3
  import type { MsgMessage } from '../../shared/bot/MsgMessage';
4
+ /** Escape server-provided lines before embedding them in native QQ Markdown. */
5
+ export declare function escapeMarkdown(value: string): string;
6
+ export declare function inlineCommandUrl(command: string): string;
4
7
  export declare function pageCommandLines(nav: PageNavData): string[];
5
8
  export declare function renderQQPageMarkdown(data: MsgMessage, showGreeting: boolean): string;
6
9
  /** Returns false if this is not the matching QQ session or the adapter has no native API. */
7
10
  export declare function canSendQQPage(session: Session | undefined, data: MsgMessage): session is Session;
8
11
  /** Send a native QQ Markdown page with inline command links. */
9
12
  export declare function sendQQPage(session: Session, data: MsgMessage): Promise<void>;
13
+ /** Reuse the adapter's native reply API for structured Markdown messages. */
14
+ export declare function sendQQMarkdown(session: Session, markdown: string): Promise<void>;
@@ -0,0 +1,5 @@
1
+ import type { Session } from 'koishi';
2
+ import type { MsgMessage } from '../../shared/bot/MsgMessage';
3
+ /** Render only explicitly marked transactions; line content never enables this channel. */
4
+ export declare function renderQQTransactionMarkdown(data: MsgMessage, showGreeting: boolean): string;
5
+ export declare function sendQQTransaction(session: Session, data: MsgMessage): Promise<void>;
@@ -130,6 +130,8 @@ export interface IMessage {
130
130
  equipmentCard?: EquipmentCardData;
131
131
  leaderboard?: LeaderboardImageData;
132
132
  pageNav?: PageNavData;
133
+ /** 通用交易确认、结果与失败消息;客户端可使用交易 Markdown 展示。 */
134
+ transaction?: boolean;
133
135
  /** 消息项列表 **/
134
136
  line: IMessageItem[];
135
137
  }
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.249",
4
+ "version": "1.0.250",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -23,7 +23,7 @@ type QQResponse = {
23
23
  }
24
24
 
25
25
  /** Escape server-provided lines before embedding them in native QQ Markdown. */
26
- function escapeMarkdown(value: string): string {
26
+ export function escapeMarkdown(value: string): string {
27
27
  return value.replace(/[\\`*_{}\[\]()#+\-.!|>~<&]/g, '\\$&')
28
28
  }
29
29
 
@@ -44,7 +44,7 @@ function compactListLabel(value: string): string {
44
44
  : label
45
45
  }
46
46
 
47
- function inlineCommandUrl(command: string): string {
47
+ export function inlineCommandUrl(command: string): string {
48
48
  // encodeURIComponent leaves parentheses unescaped, which would close the
49
49
  // surrounding Markdown link if an item ID ever contained one.
50
50
  const encoded = encodeURIComponent(command).replace(/[!'()*]/g, character =>
@@ -167,8 +167,13 @@ export function canSendQQPage(session: Session | undefined, data: MsgMessage): s
167
167
 
168
168
  /** Send a native QQ Markdown page with inline command links. */
169
169
  export async function sendQQPage(session: Session, data: MsgMessage): Promise<void> {
170
- const internal = (session.bot as typeof session.bot & { internal: QQInternal }).internal
171
170
  const markdown = renderQQPageMarkdown(data, session.event?.channel?.type === 0)
171
+ await sendQQMarkdown(session, markdown)
172
+ }
173
+
174
+ /** Reuse the adapter's native reply API for structured Markdown messages. */
175
+ export async function sendQQMarkdown(session: Session, markdown: string): Promise<void> {
176
+ const internal = (session.bot as typeof session.bot & { internal: QQInternal }).internal
172
177
  const request: QQRequest = {
173
178
  // With msg_type=2, QQ requires the ordinary content field to be empty.
174
179
  content: '',
@@ -0,0 +1,62 @@
1
+ import type { Session } from 'koishi'
2
+ import { MessageType } from '../../shared/face'
3
+ import type { MsgMessage } from '../../shared/bot/MsgMessage'
4
+ import { escapeMarkdown, inlineCommandUrl, sendQQMarkdown } from './qqPageMessage'
5
+
6
+ /** Render only explicitly marked transactions; line content never enables this channel. */
7
+ export function renderQQTransactionMarkdown(data: MsgMessage, showGreeting: boolean): string {
8
+ const sections: string[] = []
9
+ const bodyLines: string[] = []
10
+ const actions: string[] = []
11
+ const flushBody = () => {
12
+ if (bodyLines.length) sections.push(bodyLines.splice(0).join(' \n'))
13
+ }
14
+ const flushActions = () => {
15
+ if (actions.length) sections.push(actions.splice(0).join(' · '))
16
+ }
17
+ const flush = () => { flushBody(); flushActions() }
18
+ if (showGreeting) sections.push(escapeMarkdown('Ciallo~(∠・ω< )'))
19
+ for (const item of data.line) {
20
+ if (item.type === MessageType.title) {
21
+ flush()
22
+ sections.push(`**${escapeMarkdown(item.content)}**`)
23
+ continue
24
+ }
25
+ if (item.type === MessageType.error) {
26
+ flush()
27
+ sections.push(`❌ 出错了!错误原因:${escapeMarkdown(item.content)},请反馈至联系开发者`)
28
+ continue
29
+ }
30
+ if (item.type !== MessageType.line) continue
31
+ // These are the common trade actions. Names, reasons and arbitrary bracketed
32
+ // material text remain escaped display text rather than executable commands.
33
+ const action = data.transaction && (item.content === '[是]确认交易'
34
+ ? { command: '是', label: '确认交易' }
35
+ : item.content === '[否]取消交易' ? { command: '否', label: '取消交易' } : undefined)
36
+ if (action) {
37
+ flushBody()
38
+ actions.push(`[${action.command}](${inlineCommandUrl(action.command)}) ${action.label}`)
39
+ continue
40
+ }
41
+ flushActions()
42
+ if (!item.content.trim()) {
43
+ flushBody()
44
+ continue
45
+ }
46
+ const section = /^┄━═(.+)═━┄$/.exec(item.content)
47
+ if (section || item.content === '【还缺】') {
48
+ flushBody()
49
+ sections.push(`**${escapeMarkdown(section?.[1] ?? '还缺')}**`)
50
+ } else {
51
+ // Preserve the server's quoted amounts, names and descriptions verbatim
52
+ // apart from Markdown escaping; rendering never recalculates payments.
53
+ bodyLines.push(escapeMarkdown(item.content))
54
+ }
55
+ }
56
+ flush()
57
+ return sections.join('\n\n') || ' '
58
+ }
59
+
60
+ export async function sendQQTransaction(session: Session, data: MsgMessage): Promise<void> {
61
+ await sendQQMarkdown(session, renderQQTransactionMarkdown(data, session.event?.channel?.type === 0))
62
+ }
@@ -3,15 +3,17 @@ import { MsgMessage } from '../../shared/bot/MsgMessage'
3
3
  import sessions, { sessionKey } from '../sessions'
4
4
  import handel_base from './handel_base'
5
5
  import { canSendQQPage, pageCommandLines, sendQQPage } from './qqPageMessage'
6
+ import { sendQQTransaction } from './qqTransactionMessage'
6
7
 
7
8
  export default class handelr_text extends handel_base {
8
9
  async start(data: MsgMessage) {
9
10
  const content = new AnalysisMsg().parse(data)
10
11
  const session = sessions.get(sessionKey(data.botId, data.userId, data.channelId))
11
- if (data.pageNav && canSendQQPage(session, data)) {
12
+ if ((data.transaction || data.pageNav) && canSendQQPage(session, data)) {
12
13
  const sendStartedAt = this.timing ? performance.now() : 0
13
14
  try {
14
- await sendQQPage(session, data)
15
+ if (data.transaction) await sendQQTransaction(session, data)
16
+ else await sendQQPage(session, data)
15
17
  if (this.timing) {
16
18
  this.timing.delivery = 'qq-markdown'
17
19
  this.timing.sendMs = performance.now() - sendStartedAt
@@ -20,7 +22,7 @@ export default class handelr_text extends handel_base {
20
22
  } catch (error) {
21
23
  this.ctx.logger.warn('QQ Markdown 发送失败,改用普通文字', error instanceof Error ? error.message : error)
22
24
  }
23
- const commands = pageCommandLines(data.pageNav)
25
+ const commands = !data.transaction && data.pageNav ? pageCommandLines(data.pageNav) : []
24
26
  await this.send(data, commands.length ? `${content.trimEnd()}\n${commands.join('\n')}` : content)
25
27
  return
26
28
  }
@@ -20,7 +20,7 @@ export interface ServiceType {
20
20
  }
21
21
 
22
22
  export const serviceProto: ServiceProto<ServiceType> = {
23
- "version": 11,
23
+ "version": 12,
24
24
  "services": [
25
25
  {
26
26
  "id": 0,
@@ -155,6 +155,14 @@ export const serviceProto: ServiceProto<ServiceType> = {
155
155
  },
156
156
  "optional": true
157
157
  },
158
+ {
159
+ "id": 8,
160
+ "name": "transaction",
161
+ "type": {
162
+ "type": "Boolean"
163
+ },
164
+ "optional": true
165
+ },
158
166
  {
159
167
  "id": 1,
160
168
  "name": "line",
@@ -103,6 +103,8 @@ export interface IMessage {
103
103
  equipmentCard?: EquipmentCardData;
104
104
  leaderboard?: LeaderboardImageData;
105
105
  pageNav?: PageNavData;
106
+ /** 通用交易确认、结果与失败消息;客户端可使用交易 Markdown 展示。 */
107
+ transaction?: boolean;
106
108
  /** 消息项列表 **/
107
109
  line: IMessageItem[];
108
110
  }