koishi-plugin-dew-bot 1.0.261 → 1.0.263
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 +316 -105
- package/lib/plugin/characterCardAssets.d.ts +3 -1
- package/lib/plugin/characterCardStyles.d.ts +1 -1
- package/lib/plugin/serverHandel/imageMessageFollowup.d.ts +9 -0
- package/lib/plugin/serverHandel/qqMessageButtons.d.ts +27 -0
- package/lib/plugin/serverHandel/qqMessagePreview.d.ts +3 -0
- package/lib/plugin/serverHandel/qqPageMessage.d.ts +2 -1
- package/lib/shared/bot/analysisMsg.d.ts +5 -0
- package/lib/shared/bot/inlineCommands.d.ts +11 -0
- package/lib/shared/face/index.d.ts +16 -1
- package/package.json +1 -1
- package/src/plugin/bloodlineCardTemplate.ts +2 -4
- package/src/plugin/cardLayout.ts +15 -1
- package/src/plugin/characterCardAssets.ts +9 -1
- package/src/plugin/characterCardStyles.ts +6 -4
- package/src/plugin/characterCardTemplate.ts +9 -8
- package/src/plugin/serverHandel/image.ts +5 -5
- package/src/plugin/serverHandel/imageMessageFollowup.ts +23 -0
- package/src/plugin/serverHandel/qqCommandMessage.ts +10 -3
- package/src/plugin/serverHandel/qqMessageButtons.ts +38 -0
- package/src/plugin/serverHandel/qqMessagePreview.ts +19 -8
- package/src/plugin/serverHandel/qqPageMessage.ts +9 -21
- package/src/plugin/serverHandel/qqRewardMessage.ts +2 -0
- package/src/plugin/serverHandel/qqStoryMessage.ts +4 -1
- package/src/plugin/serverHandel/qqTransactionMessage.ts +4 -1
- package/src/plugin/serverHandel/text.ts +1 -1
- package/src/shared/bot/analysisMsg.ts +38 -1
- package/src/shared/bot/inlineCommands.ts +39 -0
- package/src/shared/bot/serviceProto.ts +80 -1
- package/src/shared/face/index.ts +18 -1
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { Session } from 'koishi'
|
|
2
2
|
import { MessageType, type IMessage, type PageNavData } from '../../shared/face'
|
|
3
3
|
import type { MsgMessage } from '../../shared/bot/MsgMessage'
|
|
4
|
+
import { withoutMessageButtonFallbacks } from '../../shared/bot/analysisMsg'
|
|
5
|
+
import { renderQQKeyboard, type QQKeyboard } from './qqMessageButtons'
|
|
4
6
|
import { compactDisplayText, escapeMarkdown, renderQQTitle } from './qqMarkdownHelpers'
|
|
5
7
|
import { isRewardMessage, renderRewardLine, renderRewardTitle } from './qqRewardMessage'
|
|
6
8
|
import { artworkIdentity } from '../skillArtwork'
|
|
7
9
|
import { battleLogLinkSpans, renderBattleLogLink } from './qqBattleLogLinks'
|
|
10
|
+
import { inlineCommandSpans as bracketedCommandSpans, type InlineCommandSpan } from '../../shared/bot/inlineCommands'
|
|
8
11
|
|
|
9
12
|
export { escapeMarkdown, renderQQTitle } from './qqMarkdownHelpers'
|
|
10
13
|
|
|
@@ -12,6 +15,7 @@ type QQRequest = {
|
|
|
12
15
|
content: string
|
|
13
16
|
msg_type: 2
|
|
14
17
|
markdown: { content: string }
|
|
18
|
+
keyboard?: QQKeyboard
|
|
15
19
|
msg_id?: string
|
|
16
20
|
msg_seq?: number
|
|
17
21
|
}
|
|
@@ -54,27 +58,9 @@ export function inlineCommandUrl(command: string): string {
|
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
|
|
57
|
-
type InlineCommandSpan = { start: number; end: number; label: string; command: string }
|
|
58
|
-
|
|
59
61
|
/** Bracketed commands may appear anywhere in prose, including escaped brackets. */
|
|
60
62
|
function inlineCommandSpans(content: string): InlineCommandSpan[] {
|
|
61
|
-
const spans
|
|
62
|
-
let depth = 0, start = 0, labelStart = 0
|
|
63
|
-
for (let index = 0; index < content.length; index++) {
|
|
64
|
-
if (content[index] === '[') {
|
|
65
|
-
if (depth === 0) {
|
|
66
|
-
start = index > 0 && content[index - 1] === '\\' ? index - 1 : index
|
|
67
|
-
labelStart = index + 1
|
|
68
|
-
}
|
|
69
|
-
depth++
|
|
70
|
-
} else if (content[index] === ']' && depth > 0 && --depth === 0) {
|
|
71
|
-
const label = content.slice(labelStart, index).replace(/\\$/, '')
|
|
72
|
-
if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue
|
|
73
|
-
// The refresh icon is part of the displayed label, not the command to send.
|
|
74
|
-
const command = label.split(/[++]/, 1)[0].trim().replace(/^🔄\s*/u, '')
|
|
75
|
-
if (command) spans.push({ start, end: index + 1, label, command })
|
|
76
|
-
}
|
|
77
|
-
}
|
|
63
|
+
const spans = bracketedCommandSpans(content)
|
|
78
64
|
const logLinks = battleLogLinkSpans(content)
|
|
79
65
|
return spans.filter(span => !logLinks.some(link => span.start < link.end && span.end > link.start))
|
|
80
66
|
}
|
|
@@ -233,6 +219,7 @@ function renderQQInventoryMarkdown(data: IMessage, showGreeting: boolean): strin
|
|
|
233
219
|
}
|
|
234
220
|
|
|
235
221
|
export function renderQQPageMarkdown(data: IMessage, showGreeting: boolean): string {
|
|
222
|
+
data = withoutMessageButtonFallbacks(data)
|
|
236
223
|
const hasEntryLabels = data.pageNav?.entries?.some(entry => entry.label !== undefined) ?? false
|
|
237
224
|
if (data.task && !hasEntryLabels) return renderQQTaskMarkdown(data)
|
|
238
225
|
if (!hasEntryLabels && data.pageNav && data.line.some(item => item.type === MessageType.title
|
|
@@ -375,11 +362,11 @@ export function canSendQQPage(session: Session | undefined, data: MsgMessage): s
|
|
|
375
362
|
/** Send a native QQ Markdown page with inline command links. */
|
|
376
363
|
export async function sendQQPage(session: Session, data: MsgMessage): Promise<void> {
|
|
377
364
|
const markdown = renderQQPageMarkdown(data, session.event?.channel?.type === 0)
|
|
378
|
-
await sendQQMarkdown(session, markdown)
|
|
365
|
+
await sendQQMarkdown(session, markdown, renderQQKeyboard(data))
|
|
379
366
|
}
|
|
380
367
|
|
|
381
368
|
/** Reuse the adapter's native reply API for structured Markdown messages. */
|
|
382
|
-
export async function sendQQMarkdown(session: Session, markdown: string): Promise<void> {
|
|
369
|
+
export async function sendQQMarkdown(session: Session, markdown: string, keyboard?: QQKeyboard): Promise<void> {
|
|
383
370
|
const internal = (session.bot as typeof session.bot & { internal: QQInternal }).internal
|
|
384
371
|
const request: QQRequest = {
|
|
385
372
|
// With msg_type=2, QQ requires the ordinary content field to be empty.
|
|
@@ -387,6 +374,7 @@ export async function sendQQMarkdown(session: Session, markdown: string): Promis
|
|
|
387
374
|
msg_type: 2,
|
|
388
375
|
markdown: { content: markdown },
|
|
389
376
|
}
|
|
377
|
+
if (keyboard) request.keyboard = keyboard
|
|
390
378
|
// Match the QQ adapter's passive-reply sequence.
|
|
391
379
|
const age = Date.now() - session.timestamp
|
|
392
380
|
if (session.messageId && Number.isFinite(age) && age >= 0 && age < 5 * 60 * 1000 - 2000) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withoutMessageButtonFallbacks } from '../../shared/bot/analysisMsg'
|
|
1
2
|
import { MessageType, type IMessage } from '../../shared/face'
|
|
2
3
|
import { compactDisplayText, escapeMarkdown } from './qqMarkdownHelpers'
|
|
3
4
|
import { appendMissingBattleLogLinks, renderBattleLogLinkText } from './qqBattleLogLinks'
|
|
@@ -68,6 +69,7 @@ function renderQQMailMarkdown(data: IMessage): string {
|
|
|
68
69
|
|
|
69
70
|
/** Reward cards stay compact; mail keeps its content before the attachment quote. */
|
|
70
71
|
export function renderQQRewardMarkdown(data: IMessage, showGreeting: boolean): string {
|
|
72
|
+
data = withoutMessageButtonFallbacks(data)
|
|
71
73
|
if (data.mail) return appendMissingBattleLogLinks(renderQQMailMarkdown(data), data)
|
|
72
74
|
const rewards = data.line.filter(item => item.type === MessageType.line && item.reward)
|
|
73
75
|
.map(item => renderRewardLine(item.content)).filter(Boolean)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { withoutMessageButtonFallbacks } from '../../shared/bot/analysisMsg'
|
|
2
|
+
import { renderQQKeyboard } from './qqMessageButtons'
|
|
1
3
|
import type { Session } from 'koishi'
|
|
2
4
|
import { MessageType, type IMessage } from '../../shared/face'
|
|
3
5
|
import type { MsgMessage } from '../../shared/bot/MsgMessage'
|
|
@@ -20,6 +22,7 @@ function fencedStory(content: string): string {
|
|
|
20
22
|
|
|
21
23
|
/** Marked stories keep their complete text; settled rewards use the shared reward card. */
|
|
22
24
|
export function renderQQStoryMarkdown(data: IMessage, showGreeting: boolean): string {
|
|
25
|
+
data = withoutMessageButtonFallbacks(data)
|
|
23
26
|
if (data.reward === true && data.line.some(item => item.type === MessageType.line && item.reward === true && item.content.trim())) {
|
|
24
27
|
const stories = data.line.filter(item => item.type === MessageType.line && item.codeBlock === true)
|
|
25
28
|
.map(item => fencedStory(item.content))
|
|
@@ -75,5 +78,5 @@ export function renderQQStoryMarkdown(data: IMessage, showGreeting: boolean): st
|
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
export async function sendQQStory(session: Session, data: MsgMessage): Promise<void> {
|
|
78
|
-
await sendQQMarkdown(session, renderQQStoryMarkdown(data, session.event?.channel?.type === 0))
|
|
81
|
+
await sendQQMarkdown(session, renderQQStoryMarkdown(data, session.event?.channel?.type === 0), renderQQKeyboard(data))
|
|
79
82
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { withoutMessageButtonFallbacks } from '../../shared/bot/analysisMsg'
|
|
2
|
+
import { renderQQKeyboard } from './qqMessageButtons'
|
|
1
3
|
import type { Session } from 'koishi'
|
|
2
4
|
import { MessageType, type IMessage } from '../../shared/face'
|
|
3
5
|
import type { MsgMessage } from '../../shared/bot/MsgMessage'
|
|
@@ -6,6 +8,7 @@ import { isRewardMessage, renderRewardLine, renderRewardTitle } from './qqReward
|
|
|
6
8
|
|
|
7
9
|
/** Render only explicitly marked transactions; line content never enables this channel. */
|
|
8
10
|
export function renderQQTransactionMarkdown(data: IMessage, showGreeting: boolean): string {
|
|
11
|
+
data = withoutMessageButtonFallbacks(data)
|
|
9
12
|
const rewardMessage = isRewardMessage(data)
|
|
10
13
|
const shortageMessage = data.transaction === true && data.line.some(item =>
|
|
11
14
|
item.type === MessageType.title && item.content === '❌材料不足')
|
|
@@ -90,5 +93,5 @@ export function renderQQTransactionMarkdown(data: IMessage, showGreeting: boolea
|
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
export async function sendQQTransaction(session: Session, data: MsgMessage): Promise<void> {
|
|
93
|
-
await sendQQMarkdown(session, renderQQTransactionMarkdown(data, session.event?.channel?.type === 0))
|
|
96
|
+
await sendQQMarkdown(session, renderQQTransactionMarkdown(data, session.event?.channel?.type === 0), renderQQKeyboard(data))
|
|
94
97
|
}
|
|
@@ -12,7 +12,7 @@ export default class handelr_text extends handel_base {
|
|
|
12
12
|
if (preview.markdown !== undefined && canSendQQPage(session, data)) {
|
|
13
13
|
const sendStartedAt = this.timing ? performance.now() : 0
|
|
14
14
|
try {
|
|
15
|
-
await sendQQMarkdown(session, preview.markdown)
|
|
15
|
+
await sendQQMarkdown(session, preview.markdown, preview.keyboard)
|
|
16
16
|
if (this.timing) {
|
|
17
17
|
this.timing.delivery = 'qq-markdown'
|
|
18
18
|
this.timing.sendMs = performance.now() - sendStartedAt
|
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
import { IMessage, MessageType, type PageNavData } from '../face'
|
|
2
|
+
import { renderPlainInlineCommandAliases } from './inlineCommands'
|
|
2
3
|
|
|
4
|
+
/** Platform-neutral button hints also work when Markdown or keyboards are unavailable. */
|
|
5
|
+
export function messageButtonFallbackLines(data: Pick<IMessage, 'buttons'>): string[] {
|
|
6
|
+
return [...new Set((data.buttons ?? []).flatMap(row => row.buttons.map(button =>
|
|
7
|
+
'[' + button.command + ']' + button.label)))]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function matchesButtonFallback(content: string, fallback: string): boolean {
|
|
11
|
+
const bracket = fallback.indexOf(']')
|
|
12
|
+
const prefix = fallback.slice(0, bracket + 1)
|
|
13
|
+
const line = content.trim()
|
|
14
|
+
return line.startsWith(prefix) && line.slice(prefix.length).trim() === fallback.slice(prefix.length).trim()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function appendMessageButtonFallbacks(content: string, data: Pick<IMessage, 'buttons'>): string {
|
|
18
|
+
const lines = content.replace(/\r\n?/g, '\n').split('\n')
|
|
19
|
+
const missing = messageButtonFallbackLines(data).filter(fallback =>
|
|
20
|
+
!lines.some(line => matchesButtonFallback(line, fallback)))
|
|
21
|
+
if (!missing.length) return content
|
|
22
|
+
return content + (content && !content.endsWith('\n') ? '\n' : '') + missing.join('\n') + '\n'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Remove only complete action hints; keep source line indexes used by page metadata. */
|
|
26
|
+
export function withoutMessageButtonFallbacks<T extends IMessage>(data: T): T {
|
|
27
|
+
const fallbacks = messageButtonFallbackLines(data)
|
|
28
|
+
if (!fallbacks.length) return data
|
|
29
|
+
return { ...data, line: data.line.map(item => {
|
|
30
|
+
if (item.type !== MessageType.line || item.codeBlock || item.reward || item.mailPart !== undefined) return item
|
|
31
|
+
const lines = item.content.replace(/\r\n?/g, '\n').split('\n')
|
|
32
|
+
const kept = lines.filter(line => !fallbacks.some(fallback => matchesButtonFallback(line, fallback)))
|
|
33
|
+
return kept.length === lines.length ? item : { ...item, content: kept.join('\n') }
|
|
34
|
+
}) }
|
|
35
|
+
}
|
|
36
|
+
|
|
3
37
|
/** 把服务器下发的结构化行拼成纯文本;第一阶段只处理 title / line / error **/
|
|
4
38
|
export class AnalysisMsg {
|
|
5
39
|
parse(data: IMessage) {
|
|
@@ -31,7 +65,9 @@ export class AnalysisMsg {
|
|
|
31
65
|
? labeledRow.map(entry => entry.label! + (entry.description ? ' · ' + entry.description : '')
|
|
32
66
|
+ ` [${entry.viewCommand}]`).join('')
|
|
33
67
|
: withCommandHint(item.content, commands.get(lineIndex))
|
|
34
|
-
|
|
68
|
+
const plainContent = item.codeBlock || item.mailPart === 'body' || labeledRow
|
|
69
|
+
? content : renderPlainInlineCommandAliases(content)
|
|
70
|
+
msg += `${plainContent}\n`
|
|
35
71
|
break
|
|
36
72
|
case MessageType.error:
|
|
37
73
|
msg += `❌出错了!错误原因:${item.content},请反馈至联系开发者\n`
|
|
@@ -40,6 +76,7 @@ export class AnalysisMsg {
|
|
|
40
76
|
break
|
|
41
77
|
}
|
|
42
78
|
}
|
|
79
|
+
msg = appendMessageButtonFallbacks(msg, data)
|
|
43
80
|
if (haveTitle) {
|
|
44
81
|
msg += '┄━════════━┄'
|
|
45
82
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type InlineCommandSpan = { start: number; end: number; label: string; command: string; aliased?: true }
|
|
2
|
+
|
|
3
|
+
/** Boxed commands may use [label](command:input) to show a different action label. */
|
|
4
|
+
export function inlineCommandSpans(content: string): InlineCommandSpan[] {
|
|
5
|
+
const spans: InlineCommandSpan[] = []
|
|
6
|
+
let depth = 0, start = 0, labelStart = 0
|
|
7
|
+
for (let index = 0; index < content.length; index++) {
|
|
8
|
+
if (content[index] === '[') {
|
|
9
|
+
if (depth === 0) {
|
|
10
|
+
start = index > 0 && content[index - 1] === '\\' ? index - 1 : index
|
|
11
|
+
labelStart = index + 1
|
|
12
|
+
}
|
|
13
|
+
depth++
|
|
14
|
+
} else if (content[index] === ']' && depth > 0 && --depth === 0) {
|
|
15
|
+
const label = content.slice(labelStart, index).replace(/\\$/, '')
|
|
16
|
+
if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue
|
|
17
|
+
const alias = /^\(command:([^()[\]\r\n]+)\)/.exec(content.slice(index + 1))
|
|
18
|
+
// Ordinary templates keep their existing command extraction rules.
|
|
19
|
+
const command = alias ? alias[1].trim() : label.split(/[++]/, 1)[0].trim().replace(/^🔄\s*/u, '')
|
|
20
|
+
if (!command) continue
|
|
21
|
+
const end = index + 1 + (alias?.[0].length ?? 0)
|
|
22
|
+
spans.push({ start, end, label, command, ...(alias ? { aliased: true as const } : {}) })
|
|
23
|
+
if (alias) index = end - 1
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return spans
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Keep aliased actions usable when the platform falls back to ordinary text. */
|
|
30
|
+
export function renderPlainInlineCommandAliases(content: string): string {
|
|
31
|
+
let result = '', offset = 0
|
|
32
|
+
for (const span of inlineCommandSpans(content)) {
|
|
33
|
+
if (!span.aliased) continue
|
|
34
|
+
result += content.slice(offset, span.start)
|
|
35
|
+
+ (span.label.trim() === span.command ? `[${span.command}]` : `${span.label}[${span.command}]`)
|
|
36
|
+
offset = span.end
|
|
37
|
+
}
|
|
38
|
+
return result + content.slice(offset)
|
|
39
|
+
}
|
|
@@ -25,7 +25,7 @@ export interface ServiceType {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const serviceProto: ServiceProto<ServiceType> = {
|
|
28
|
-
"version":
|
|
28
|
+
"version": 39,
|
|
29
29
|
"services": [
|
|
30
30
|
{
|
|
31
31
|
"id": 0,
|
|
@@ -253,6 +253,18 @@ export const serviceProto: ServiceProto<ServiceType> = {
|
|
|
253
253
|
"target": "messageFace/TitleCardData"
|
|
254
254
|
},
|
|
255
255
|
"optional": true
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"id": 17,
|
|
259
|
+
"name": "buttons",
|
|
260
|
+
"type": {
|
|
261
|
+
"type": "Array",
|
|
262
|
+
"elementType": {
|
|
263
|
+
"type": "Reference",
|
|
264
|
+
"target": "messageFace/MessageButtonRow"
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
"optional": true
|
|
256
268
|
}
|
|
257
269
|
]
|
|
258
270
|
},
|
|
@@ -709,6 +721,14 @@ export const serviceProto: ServiceProto<ServiceType> = {
|
|
|
709
721
|
}
|
|
710
722
|
},
|
|
711
723
|
"optional": true
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
"id": 14,
|
|
727
|
+
"name": "explorationRank",
|
|
728
|
+
"type": {
|
|
729
|
+
"type": "String"
|
|
730
|
+
},
|
|
731
|
+
"optional": true
|
|
712
732
|
}
|
|
713
733
|
]
|
|
714
734
|
},
|
|
@@ -1849,6 +1869,65 @@ export const serviceProto: ServiceProto<ServiceType> = {
|
|
|
1849
1869
|
}
|
|
1850
1870
|
]
|
|
1851
1871
|
},
|
|
1872
|
+
"messageFace/MessageButtonRow": {
|
|
1873
|
+
"type": "Interface",
|
|
1874
|
+
"properties": [
|
|
1875
|
+
{
|
|
1876
|
+
"id": 0,
|
|
1877
|
+
"name": "buttons",
|
|
1878
|
+
"type": {
|
|
1879
|
+
"type": "Array",
|
|
1880
|
+
"elementType": {
|
|
1881
|
+
"type": "Reference",
|
|
1882
|
+
"target": "messageFace/MessageButton"
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
]
|
|
1887
|
+
},
|
|
1888
|
+
"messageFace/MessageButton": {
|
|
1889
|
+
"type": "Interface",
|
|
1890
|
+
"properties": [
|
|
1891
|
+
{
|
|
1892
|
+
"id": 0,
|
|
1893
|
+
"name": "label",
|
|
1894
|
+
"type": {
|
|
1895
|
+
"type": "String"
|
|
1896
|
+
}
|
|
1897
|
+
},
|
|
1898
|
+
{
|
|
1899
|
+
"id": 1,
|
|
1900
|
+
"name": "command",
|
|
1901
|
+
"type": {
|
|
1902
|
+
"type": "String"
|
|
1903
|
+
}
|
|
1904
|
+
},
|
|
1905
|
+
{
|
|
1906
|
+
"id": 2,
|
|
1907
|
+
"name": "style",
|
|
1908
|
+
"type": {
|
|
1909
|
+
"type": "Union",
|
|
1910
|
+
"members": [
|
|
1911
|
+
{
|
|
1912
|
+
"id": 0,
|
|
1913
|
+
"type": {
|
|
1914
|
+
"type": "Literal",
|
|
1915
|
+
"literal": "primary"
|
|
1916
|
+
}
|
|
1917
|
+
},
|
|
1918
|
+
{
|
|
1919
|
+
"id": 1,
|
|
1920
|
+
"type": {
|
|
1921
|
+
"type": "Literal",
|
|
1922
|
+
"literal": "secondary"
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
]
|
|
1926
|
+
},
|
|
1927
|
+
"optional": true
|
|
1928
|
+
}
|
|
1929
|
+
]
|
|
1930
|
+
},
|
|
1852
1931
|
"PtlMessage/ReqMessage": {
|
|
1853
1932
|
"type": "Interface",
|
|
1854
1933
|
"extends": [
|
package/src/shared/face/index.ts
CHANGED
|
@@ -38,7 +38,7 @@ export interface CharacterCardData {
|
|
|
38
38
|
bloodlines: Array<{ name: string; quality: string; artworkUrl?: string }>;
|
|
39
39
|
/** 当前基因锁大阶的配置名称,不包含层级;未开启时为基因锁未开启。 */
|
|
40
40
|
geneLockRankName?: string;
|
|
41
|
-
/**
|
|
41
|
+
/** 当前称号的图片链接;未获得称号或图片不可用时省略,客户端不显示称号横幅。 */
|
|
42
42
|
titleImageUrl?: string;
|
|
43
43
|
/** 已获得的真实基因锁大阶(0~5);0 表示尚未解锁。 */
|
|
44
44
|
geneLockTier?: number;
|
|
@@ -46,6 +46,8 @@ export interface CharacterCardData {
|
|
|
46
46
|
equipment?: Array<{ id: string; name: string; quality: string; slot: string; enhancement?: number }>;
|
|
47
47
|
/** 当前构筑的全部技能,包括装备、递归附带与限时增益来源。 */
|
|
48
48
|
skills?: Array<{ id: string; name: string; rank: number; quality?: string; type?: CompanionSkillType; artworkUrl?: string }>;
|
|
49
|
+
/** 玩家当前的轮回探索品阶(F~SSS),与角色等级、基因锁阶级分开显示。 */
|
|
50
|
+
explorationRank?: string;
|
|
49
51
|
}
|
|
50
52
|
export enum CompanionCardKind {
|
|
51
53
|
pet = 'pet',
|
|
@@ -196,6 +198,19 @@ export interface PageNavData {
|
|
|
196
198
|
filters?: Array<{ name: string; emoji?: string; command: string }>;
|
|
197
199
|
}
|
|
198
200
|
|
|
201
|
+
/** 平台无关的消息按钮;点击后发送普通指令,业务仍负责权限与状态校验。 */
|
|
202
|
+
export interface MessageButton {
|
|
203
|
+
label: string;
|
|
204
|
+
command: string;
|
|
205
|
+
/** 主操作蓝色线框,次操作灰色线框;缺省为次操作。 */
|
|
206
|
+
style?: 'primary' | 'secondary';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** 同一行的按钮;QQ 原生键盘最多五行,每行最多五个。 */
|
|
210
|
+
export interface MessageButtonRow {
|
|
211
|
+
buttons: MessageButton[];
|
|
212
|
+
}
|
|
213
|
+
|
|
199
214
|
/** 下发给客户端的消息主体 **/
|
|
200
215
|
export interface IMessage {
|
|
201
216
|
handleType: MessageHandleType;
|
|
@@ -222,4 +237,6 @@ export interface IMessage {
|
|
|
222
237
|
line: IMessageItem[];
|
|
223
238
|
companionCard?: CompanionCardData;
|
|
224
239
|
titleCard?: TitleCardData;
|
|
240
|
+
/** 通用指令按钮;不支持原生按钮的平台保留文字指令。 */
|
|
241
|
+
buttons?: MessageButtonRow[];
|
|
225
242
|
}
|