Skip to content

Commit 47384e7

Browse files
committed
feat: 用合并转发发送完整歌曲信息
1 parent 2ec7dd6 commit 47384e7

File tree

9 files changed

+214
-47
lines changed

9 files changed

+214
-47
lines changed

apps/qqbot/src/adapter/Bot.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class BotAdapter extends Bot<BotTypes> {
5858

5959
private readonly ws: WebSocket;
6060
private readonly logger = createLogg('BotAdapter').useGlobalConfig();
61-
private selfId = 0;
61+
public selfId = 0;
6262

6363
public constructor(private readonly env: Env) {
6464
super();
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { BundledMessageBase, BundledMessageNodeBase } from '@clansty/maibot-firm';
2+
import { BotAdapter, BotTypes } from './Bot';
3+
4+
export class BundledMessage extends BundledMessageBase<BotTypes> {
5+
public constructor(protected bot: BotAdapter) {
6+
super();
7+
}
8+
9+
protected _nodes: BundledMessageNode[] = [];
10+
11+
public addNode() {
12+
const node = new BundledMessageNode(this.bot);
13+
this._nodes.push(node);
14+
return node;
15+
}
16+
17+
public compose() {
18+
return {
19+
message: this._nodes.map(node => node.compose()),
20+
source: this._title,
21+
news: this._description.split('\n').filter(it => it).map(line => ({ text: line })),
22+
summary: this._summary,
23+
prompt: this._prompt
24+
};
25+
}
26+
}
27+
28+
export class BundledMessageNode extends BundledMessageNodeBase<BotTypes> {
29+
public constructor(protected bot: BotAdapter) {
30+
super(bot);
31+
}
32+
33+
public dispatch() {
34+
return Promise.resolve({} as any);
35+
}
36+
37+
public compose() {
38+
const content = [];
39+
40+
if (this._file) {
41+
content.push({
42+
type: 'image',
43+
data: {
44+
file: this._file,
45+
name: '我也不知道什么图',
46+
summary: '我也不知道什么图'
47+
}
48+
});
49+
}
50+
if (this._text) {
51+
content.push({
52+
type: 'text',
53+
data: {
54+
text: this._text
55+
}
56+
});
57+
}
58+
59+
return {
60+
type: 'node',
61+
data: {
62+
nickname: 'AquaDX Bot',
63+
user_id: this.bot.selfId,
64+
content
65+
}
66+
};
67+
}
68+
}

apps/qqbot/src/adapter/MessageAction.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { SendMessageAction as SendMessageActionBase, SendMessageResult as SendMessageResultBase } from '@clansty/maibot-firm';
1+
import { BundledMessageBase, SendMessageAction as SendMessageActionBase, SendMessageResult as SendMessageResultBase } from '@clansty/maibot-firm';
22
import { WSSendParam, WSSendReturn } from 'node-napcat-ts/dist/Interfaces';
33
import { BotAdapter, BotTypes } from './Bot';
44
import { NoReportError } from '@clansty/maibot-core';
5+
import { BundledMessage } from './BundledMessage';
56

67
export class SendMessageResult extends SendMessageResultBase<BotTypes> {
78
public constructor(protected bot: BotAdapter, protected data: WSSendReturn['send_msg']) {
@@ -26,6 +27,11 @@ export class SendMessageAction extends SendMessageActionBase<BotTypes> {
2627
super(bot, chatId);
2728
}
2829

30+
public override addBundledMessage(): BundledMessage {
31+
this._bundledMessage = new BundledMessage(this.bot);
32+
return this._bundledMessage as BundledMessage;
33+
}
34+
2935
public async dispatch(): Promise<SendMessageResultBase<BotTypes>> {
3036
let params: WSSendParam['send_msg'];
3137

@@ -76,6 +82,14 @@ export class SendMessageAction extends SendMessageActionBase<BotTypes> {
7682
});
7783
}
7884

85+
if (this._bundledMessage) {
86+
// @ts-ignore
87+
params = {
88+
...params,
89+
...(this._bundledMessage as BundledMessage).compose()
90+
};
91+
}
92+
7993
const ret = await this.bot.callApi('send_msg', params);
8094
return new SendMessageResult(this.bot, ret);
8195
}

packages/botcore/src/modules/musicSearch.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,27 @@ export default <T extends BotTypes>({ bot, env, getContext, musicToFile }: Build
7272
if (!song) return;
7373

7474
const { buttons, lyrics } = await genSongInfoButtonsWithCachedLyrics(song);
75+
const msgTitle = song.display.substring(0, song.display.indexOf('\n'));
76+
const msgText = song.display.substring(song.display.indexOf('\n') + 1).trim();
77+
const bundle = req.addBundledMessage();
7578
if (musicToFile[song.id]) {
7679
req.addAudio(musicToFile[song.id]);
7780
} else if (song.coverUrl) {
78-
const msgTitle = song.display.substring(0, song.display.indexOf('\n'));
79-
const msgText = song.display.substring(song.display.indexOf('\n') + 1);
8081
req
8182
.addPhoto(song.coverUrl)
8283
.setTemplatedMessage(MESSAGE_TEMPLATE.MusicInfo, {
8384
title: msgTitle,
8485
content: msgText,
8586
image: song.coverUrl
8687
});
88+
bundle.addNode().addPhoto(song.coverUrl);
8789
}
90+
bundle.setTitle(msgTitle).setPrompt(msgTitle).setDescription(song.basicInfo.substring(song.basicInfo.indexOf('\n') + 1)).setSummary('点击查看歌曲和谱面详情');
91+
bundle.addNode().setText(msgText);
92+
for (const sheet of song.sheets) {
93+
bundle.addNode().setText(`${sheet.type === 'dx' ? 'DX ' : '标准'}谱面\n` + sheet.display.trim());
94+
}
95+
8896
const message = await req.setText(song.display).setButtons(buttons).dispatch();
8997
// 异步获取歌词,只在 undefined 的时候
9098
if (!lyrics && bot.isEditMessageSupported) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Bot, BotTypes } from './Bot';
2+
import { MessageButton } from './MessageAction';
3+
4+
export abstract class BaseTextMessageAction<T extends BotTypes> {
5+
protected constructor(
6+
protected readonly bot: Bot<T>
7+
) {
8+
}
9+
10+
protected _text: string;
11+
12+
public setText(text: string) {
13+
this._text = text;
14+
return this;
15+
}
16+
17+
protected _parseAsHtml = false;
18+
19+
// 如果 html 不支持,会自动清除 html 标签
20+
public setHtml(html: string) {
21+
if (this.bot.isHtmlMessageSupported) {
22+
this._text = html;
23+
this._parseAsHtml = true;
24+
} else {
25+
this._text = html.replace(/<[^>]+>/g, '');
26+
}
27+
return this;
28+
}
29+
30+
protected _buttons: MessageButton[][] = [];
31+
32+
public addButtons(buttons: MessageButton[] | MessageButton) {
33+
if (!Array.isArray(buttons)) {
34+
buttons = [buttons];
35+
}
36+
this._buttons.push(buttons);
37+
return this;
38+
}
39+
40+
public setButtons(buttons: MessageButton[][]) {
41+
this._buttons = buttons;
42+
return this;
43+
}
44+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { Bot, BotTypes } from './Bot';
2+
import { BaseTextMessageAction } from './BaseTextMessageAction';
3+
4+
export abstract class BundledMessageBase<T extends BotTypes> {
5+
protected _title?: string;
6+
protected _description?: string;
7+
protected _summary?: string;
8+
protected _prompt?: string;
9+
10+
public abstract addNode(): BundledMessageNodeBase<T>;
11+
12+
public setTitle(title: string) {
13+
this._title = title;
14+
return this;
15+
}
16+
17+
public setDescription(description: string) {
18+
this._description = description;
19+
return this;
20+
}
21+
22+
public setSummary(summary: string) {
23+
this._summary = summary;
24+
return this;
25+
}
26+
27+
public setPrompt(prompt: string) {
28+
this._prompt = prompt;
29+
return this;
30+
}
31+
}
32+
33+
export abstract class BundledMessageNodeBase<T extends BotTypes> extends BaseTextMessageAction<T> {
34+
protected _file: T['SendableFile'] = null;
35+
protected _fileType: 'audio' | 'document' | 'photo' = null;
36+
37+
public addPhoto(file: T['SendableFile']) {
38+
this._fileType = 'photo';
39+
this._file = file;
40+
return this;
41+
}
42+
}
43+
44+
export class DummyBundledMessage<T extends BotTypes> extends BundledMessageBase<T> {
45+
public constructor(protected bot: Bot<T>) {
46+
super();
47+
}
48+
49+
public addNode() {
50+
return new DummyBundledMessageNode<T>(this.bot);
51+
}
52+
}
53+
54+
export class DummyBundledMessageNode<T extends BotTypes> extends BundledMessageNodeBase<T> {
55+
public constructor(bot: Bot<T>) {
56+
super(bot);
57+
}
58+
59+
public dispatch() {
60+
return Promise.resolve({} as any);
61+
}
62+
}

packages/botfirm/src/InlineQuery.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Bot, BotTypes, EventBase } from './Bot';
2-
import { EditMessageAction, BaseTextMessageAction } from './MessageAction';
2+
import { EditMessageAction } from './MessageAction';
3+
import { BaseTextMessageAction } from './BaseTextMessageAction';
34

45
export abstract class InlineQueryEventBase<T extends BotTypes> extends EventBase<T> {
56
protected constructor(public bot: Bot<T>) {

packages/botfirm/src/MessageAction.ts

Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { Bot, BotTypes } from './Bot';
2+
import { BundledMessageBase, DummyBundledMessage } from './BundledMessage';
3+
import { BaseTextMessageAction } from './BaseTextMessageAction';
24

35
export abstract class SendMessageResult<T extends BotTypes> {
46
protected constructor(
@@ -14,48 +16,6 @@ export abstract class SendMessageResult<T extends BotTypes> {
1416
public abstract fileId?: T['SendableFile'];
1517
}
1618

17-
export abstract class BaseTextMessageAction<T extends BotTypes> {
18-
protected constructor(
19-
protected readonly bot: Bot<T>
20-
) {
21-
}
22-
23-
protected _text: string;
24-
25-
public setText(text: string) {
26-
this._text = text;
27-
return this;
28-
}
29-
30-
protected _parseAsHtml = false;
31-
32-
// 如果 html 不支持,会自动清除 html 标签
33-
public setHtml(html: string) {
34-
if (this.bot.isHtmlMessageSupported) {
35-
this._text = html;
36-
this._parseAsHtml = true;
37-
} else {
38-
this._text = html.replace(/<[^>]+>/g, '');
39-
}
40-
return this;
41-
}
42-
43-
protected _buttons: MessageButton[][] = [];
44-
45-
public addButtons(buttons: MessageButton[] | MessageButton) {
46-
if (!Array.isArray(buttons)) {
47-
buttons = [buttons];
48-
}
49-
this._buttons.push(buttons);
50-
return this;
51-
}
52-
53-
public setButtons(buttons: MessageButton[][]) {
54-
this._buttons = buttons;
55-
return this;
56-
}
57-
}
58-
5919
abstract class DispatchableMessageAction<T extends BotTypes> extends BaseTextMessageAction<T> {
6020
public abstract dispatch(): Promise<SendMessageResult<T>>;
6121

@@ -94,6 +54,7 @@ export abstract class SendMessageAction<T extends BotTypes> extends Dispatchable
9454
protected _file: T['SendableFile'] = null;
9555
protected _fileType: 'audio' | 'document' | 'photo' = null;
9656
protected _templatedMessage: TemplatedMessage<T> = null;
57+
protected _bundledMessage: BundledMessageBase<T> = null;
9758

9859
public addPhoto(file: T['SendableFile']) {
9960
this._fileType = 'photo';
@@ -113,6 +74,13 @@ export abstract class SendMessageAction<T extends BotTypes> extends Dispatchable
11374
return this;
11475
}
11576

77+
// 仅在 QQ 中支持
78+
public addBundledMessage() {
79+
this._bundledMessage = new DummyBundledMessage(this.bot);
80+
return this._bundledMessage;
81+
}
82+
83+
// 仅在 QQ 官 Bot 中支持,弃用
11684
public setTemplatedMessage(template: T['MessageTemplateID'], values: Record<string, string>) {
11785
this._templatedMessage = new TemplatedMessage(template, values);
11886
return this;

packages/botfirm/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ export * from './CallbackQuery';
33
export * from './InlineQuery';
44
export * from './MessageAction';
55
export * from './MessageEvent';
6+
export * from './BundledMessage';
7+
export * from './BaseTextMessageAction';

0 commit comments

Comments
 (0)