mirror of
https://github.com/LLOneBot/LLOneBot.git
synced 2024-11-22 01:56:33 +00:00
Merge pull request #206 from LLOneBot/feat/music-card
feat: music card sign
This commit is contained in:
commit
25c3d51d69
@ -52,6 +52,7 @@ export class ConfigUtil {
|
|||||||
autoDeleteFile: false,
|
autoDeleteFile: false,
|
||||||
autoDeleteFileSecond: 60,
|
autoDeleteFileSecond: 60,
|
||||||
enablePoke: false,
|
enablePoke: false,
|
||||||
|
musicSignUrl: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(this.configPath)) {
|
if (!fs.existsSync(this.configPath)) {
|
||||||
|
@ -28,6 +28,7 @@ export interface Config {
|
|||||||
autoDeleteFileSecond?: number
|
autoDeleteFileSecond?: number
|
||||||
ffmpeg?: string // ffmpeg路径
|
ffmpeg?: string // ffmpeg路径
|
||||||
enablePoke?: boolean
|
enablePoke?: boolean
|
||||||
|
musicSignUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LLOneBotError {
|
export interface LLOneBotError {
|
||||||
|
37
src/common/utils/sign.ts
Normal file
37
src/common/utils/sign.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { log } from './log'
|
||||||
|
|
||||||
|
export interface IdMusicSignPostData {
|
||||||
|
type: 'qq' | '163'
|
||||||
|
id: string | number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomMusicSignPostData {
|
||||||
|
type: 'custom'
|
||||||
|
url: string
|
||||||
|
audio: string
|
||||||
|
title: string
|
||||||
|
image?: string
|
||||||
|
singer?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MusicSignPostData = IdMusicSignPostData | CustomMusicSignPostData
|
||||||
|
|
||||||
|
export class MusicSign {
|
||||||
|
private readonly url: string
|
||||||
|
|
||||||
|
constructor(url: string) {
|
||||||
|
this.url = url
|
||||||
|
}
|
||||||
|
|
||||||
|
async sign(postData: MusicSignPostData): Promise<string> {
|
||||||
|
const resp = await fetch(this.url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(postData),
|
||||||
|
})
|
||||||
|
if (!resp.ok) throw new Error(resp.statusText)
|
||||||
|
const data = await resp.text()
|
||||||
|
log('音乐消息生成成功', data)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
@ -310,7 +310,7 @@ export class SendMsgElementConstructor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ark(data: any): SendArkElement {
|
static ark(data: string): SendArkElement {
|
||||||
return {
|
return {
|
||||||
elementType: ElementType.ARK,
|
elementType: ElementType.ARK,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
|
@ -15,7 +15,9 @@ import {
|
|||||||
OB11MessageCustomMusic,
|
OB11MessageCustomMusic,
|
||||||
OB11MessageData,
|
OB11MessageData,
|
||||||
OB11MessageDataType,
|
OB11MessageDataType,
|
||||||
|
OB11MessageJson,
|
||||||
OB11MessageMixType,
|
OB11MessageMixType,
|
||||||
|
OB11MessageMusic,
|
||||||
OB11MessageNode,
|
OB11MessageNode,
|
||||||
OB11PostSendMsg,
|
OB11PostSendMsg,
|
||||||
} from '../../types'
|
} from '../../types'
|
||||||
@ -26,12 +28,13 @@ import { ActionName, BaseCheckResult } from '../types'
|
|||||||
import * as fs from 'node:fs'
|
import * as fs from 'node:fs'
|
||||||
import { decodeCQCode } from '../../cqcode'
|
import { decodeCQCode } from '../../cqcode'
|
||||||
import { dbUtil } from '../../../common/db'
|
import { dbUtil } from '../../../common/db'
|
||||||
import { ALLOW_SEND_TEMP_MSG } from '../../../common/config'
|
import { ALLOW_SEND_TEMP_MSG, getConfigUtil } from '../../../common/config'
|
||||||
import { log } from '../../../common/utils/log'
|
import { log } from '../../../common/utils/log'
|
||||||
import { sleep } from '../../../common/utils/helper'
|
import { sleep } from '../../../common/utils/helper'
|
||||||
import { uri2local } from '../../../common/utils'
|
import { uri2local } from '../../../common/utils'
|
||||||
import { crychic } from '../../../ntqqapi/external/crychic'
|
import { crychic } from '../../../ntqqapi/external/crychic'
|
||||||
import { NTQQGroupApi } from '../../../ntqqapi/api'
|
import { NTQQGroupApi } from '../../../ntqqapi/api'
|
||||||
|
import { CustomMusicSignPostData, MusicSign, MusicSignPostData } from '../../../common/utils/sign'
|
||||||
|
|
||||||
function checkSendMessage(sendMsgList: OB11MessageData[]) {
|
function checkSendMessage(sendMsgList: OB11MessageData[]) {
|
||||||
function checkUri(uri: string): boolean {
|
function checkUri(uri: string): boolean {
|
||||||
@ -302,6 +305,13 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
message: '转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素',
|
message: '转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const musicNum = this.getSpecialMsgNum(payload, OB11MessageDataType.music)
|
||||||
|
if (musicNum && messages.length > 1) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: '音乐消息不可以和其他消息混在一起发送',
|
||||||
|
}
|
||||||
|
}
|
||||||
if (payload.message_type !== 'private' && payload.group_id && !(await getGroup(payload.group_id))) {
|
if (payload.message_type !== 'private' && payload.group_id && !(await getGroup(payload.group_id))) {
|
||||||
return {
|
return {
|
||||||
valid: false,
|
valid: false,
|
||||||
@ -374,19 +384,32 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw '发送转发消息失败 ' + e.toString()
|
throw '发送转发消息失败 ' + e.toString()
|
||||||
}
|
}
|
||||||
} else {
|
} else if (this.getSpecialMsgNum(payload, OB11MessageDataType.music)) {
|
||||||
if (this.getSpecialMsgNum(payload, OB11MessageDataType.music)) {
|
const music = messages[0] as OB11MessageMusic
|
||||||
const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic
|
if (music) {
|
||||||
if (music) {
|
const { musicSignUrl } = getConfigUtil().getConfig()
|
||||||
const { url, audio, title, content, image } = music.data
|
if (!musicSignUrl) {
|
||||||
const selfPeer: Peer = { peerUid: selfInfo.uid, chatType: ChatType.friend }
|
throw '音乐签名地址未配置'
|
||||||
// 搞不定!
|
|
||||||
// const musicMsg = await this.send(selfPeer, [this.genMusicElement(url, audio, title, content, image)], [], false)
|
|
||||||
// 转发
|
|
||||||
// const res = await NTQQApi.forwardMsg(selfPeer, peer, [musicMsg.msgId])
|
|
||||||
// log("转发音乐消息成功", res);
|
|
||||||
// return {message_id: musicMsg.msgShortId}
|
|
||||||
}
|
}
|
||||||
|
const { type } = music.data
|
||||||
|
if (!['qq', '163', 'custom'].includes(type)) {
|
||||||
|
throw `不支持的音乐类型 ${type}`
|
||||||
|
}
|
||||||
|
const postData: MusicSignPostData = { ...music.data }
|
||||||
|
if (type === 'custom' && music.data.content) {
|
||||||
|
;(postData as CustomMusicSignPostData).singer = music.data.content
|
||||||
|
delete (postData as OB11MessageCustomMusic['data']).content
|
||||||
|
}
|
||||||
|
let jsonContent: string
|
||||||
|
try {
|
||||||
|
jsonContent = await new MusicSign(musicSignUrl).sign(postData)
|
||||||
|
} catch (e) {
|
||||||
|
throw `签名音乐消息失败:${e}`
|
||||||
|
}
|
||||||
|
messages[0] = {
|
||||||
|
type: OB11MessageDataType.json,
|
||||||
|
data: { data: jsonContent },
|
||||||
|
} as OB11MessageJson
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// log("send msg:", peer, sendElements)
|
// log("send msg:", peer, sendElements)
|
||||||
@ -555,41 +578,41 @@ export class SendMsg extends BaseAction<OB11PostSendMsg, ReturnDataType> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private genMusicElement(url: string, audio: string, title: string, content: string, image: string): SendArkElement {
|
// private genMusicElement(url: string, audio: string, title: string, content: string, image: string): SendArkElement {
|
||||||
const musicJson = {
|
// const musicJson = {
|
||||||
app: 'com.tencent.structmsg',
|
// app: 'com.tencent.structmsg',
|
||||||
config: {
|
// config: {
|
||||||
ctime: 1709689928,
|
// ctime: 1709689928,
|
||||||
forward: 1,
|
// forward: 1,
|
||||||
token: '5c1e4905f926dd3a64a4bd3841460351',
|
// token: '5c1e4905f926dd3a64a4bd3841460351',
|
||||||
type: 'normal',
|
// type: 'normal',
|
||||||
},
|
// },
|
||||||
extra: { app_type: 1, appid: 100497308, uin: selfInfo.uin },
|
// extra: { app_type: 1, appid: 100497308, uin: selfInfo.uin },
|
||||||
meta: {
|
// meta: {
|
||||||
news: {
|
// news: {
|
||||||
action: '',
|
// action: '',
|
||||||
android_pkg_name: '',
|
// android_pkg_name: '',
|
||||||
app_type: 1,
|
// app_type: 1,
|
||||||
appid: 100497308,
|
// appid: 100497308,
|
||||||
ctime: 1709689928,
|
// ctime: 1709689928,
|
||||||
desc: content || title,
|
// desc: content || title,
|
||||||
jumpUrl: url,
|
// jumpUrl: url,
|
||||||
musicUrl: audio,
|
// musicUrl: audio,
|
||||||
preview: image,
|
// preview: image,
|
||||||
source_icon: 'https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0',
|
// source_icon: 'https://p.qpic.cn/qqconnect/0/app_100497308_1626060999/100?max-age=2592000&t=0',
|
||||||
source_url: '',
|
// source_url: '',
|
||||||
tag: 'QQ音乐',
|
// tag: 'QQ音乐',
|
||||||
title: title,
|
// title: title,
|
||||||
uin: selfInfo.uin,
|
// uin: selfInfo.uin,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
prompt: content || title,
|
// prompt: content || title,
|
||||||
ver: '0.0.0.1',
|
// ver: '0.0.0.1',
|
||||||
view: 'news',
|
// view: 'news',
|
||||||
}
|
// }
|
||||||
|
|
||||||
return SendMsgElementConstructor.ark(musicJson)
|
// return SendMsgElementConstructor.ark(musicJson)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SendMsg
|
export default SendMsg
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { PicSubType, RawMessage } from '../ntqqapi/types'
|
import { PicSubType, RawMessage } from '../ntqqapi/types'
|
||||||
import { EventType } from './event/OB11BaseEvent'
|
import { EventType } from './event/OB11BaseEvent'
|
||||||
|
import { IdMusicSignPostData, CustomMusicSignPostData } from '../common/utils/sign'
|
||||||
|
|
||||||
export interface OB11User {
|
export interface OB11User {
|
||||||
user_id: number
|
user_id: number
|
||||||
@ -219,21 +220,21 @@ export interface OB11MessageNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OB11MessageIdMusic {
|
||||||
|
type: OB11MessageDataType.music
|
||||||
|
data: IdMusicSignPostData
|
||||||
|
}
|
||||||
|
|
||||||
export interface OB11MessageCustomMusic {
|
export interface OB11MessageCustomMusic {
|
||||||
type: OB11MessageDataType.music
|
type: OB11MessageDataType.music
|
||||||
data: {
|
data: Omit<CustomMusicSignPostData, 'singer'> & { content?: string }
|
||||||
type: 'custom'
|
|
||||||
url: string
|
|
||||||
audio: string
|
|
||||||
title: string
|
|
||||||
content?: string
|
|
||||||
image?: string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OB11MessageMusic = OB11MessageIdMusic | OB11MessageCustomMusic
|
||||||
|
|
||||||
export interface OB11MessageJson {
|
export interface OB11MessageJson {
|
||||||
type: OB11MessageDataType.json
|
type: OB11MessageDataType.json
|
||||||
data: { config: { token: string } } & any
|
data: { data: string /* , config: { token: string } */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OB11MessageData =
|
export type OB11MessageData =
|
||||||
@ -247,6 +248,7 @@ export type OB11MessageData =
|
|||||||
| OB11MessageFile
|
| OB11MessageFile
|
||||||
| OB11MessageVideo
|
| OB11MessageVideo
|
||||||
| OB11MessageNode
|
| OB11MessageNode
|
||||||
|
| OB11MessageIdMusic
|
||||||
| OB11MessageCustomMusic
|
| OB11MessageCustomMusic
|
||||||
| OB11MessageJson
|
| OB11MessageJson
|
||||||
| OB11MessagePoke
|
| OB11MessagePoke
|
||||||
|
@ -95,7 +95,9 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
<setting-text>HTTP 事件上报密钥</setting-text>
|
<setting-text>HTTP 事件上报密钥</setting-text>
|
||||||
</div>
|
</div>
|
||||||
<div class="q-input">
|
<div class="q-input">
|
||||||
<input id="config-ob11-httpSecret" class="q-input__inner" data-config-key="ob11.httpSecret" type="text" value="${config.ob11.httpSecret}" placeholder="未设置" />
|
<input id="config-ob11-httpSecret" class="q-input__inner" data-config-key="ob11.httpSecret" type="text" value="${
|
||||||
|
config.ob11.httpSecret
|
||||||
|
}" placeholder="未设置" />
|
||||||
</div>
|
</div>
|
||||||
</setting-item>
|
</setting-item>
|
||||||
<setting-item data-direction="row">
|
<setting-item data-direction="row">
|
||||||
@ -158,9 +160,17 @@ async function onSettingWindowCreated(view: Element) {
|
|||||||
),
|
),
|
||||||
SettingItem(
|
SettingItem(
|
||||||
'ffmpeg 路径,发送语音、视频需要,同时保证ffprobe和ffmpeg在一起',
|
'ffmpeg 路径,发送语音、视频需要,同时保证ffprobe和ffmpeg在一起',
|
||||||
` <a href="javascript:LiteLoader.api.openExternal(\'https://llonebot.github.io/zh-CN/guide/ffmpeg\');">下载地址</a> <span id="config-ffmpeg-path-text">, 路径:${!isEmpty(config.ffmpeg) ? config.ffmpeg : '未指定'}</span>`,
|
` <a href="javascript:LiteLoader.api.openExternal(\'https://llonebot.github.io/zh-CN/guide/ffmpeg\');">下载地址</a> <span id="config-ffmpeg-path-text">, 路径:${
|
||||||
|
!isEmpty(config.ffmpeg) ? config.ffmpeg : '未指定'
|
||||||
|
}</span>`,
|
||||||
SettingButton('选择ffmpeg', 'config-ffmpeg-select'),
|
SettingButton('选择ffmpeg', 'config-ffmpeg-select'),
|
||||||
),
|
),
|
||||||
|
SettingItem(
|
||||||
|
'音乐卡片签名地址',
|
||||||
|
null,
|
||||||
|
`<div class="q-input" style="width:210px;"><input class="q-input__inner" data-config-key="musicSignUrl" type="text" value="${config.musicSignUrl}" placeholder="未设置" /></div>`,
|
||||||
|
'config-musicSignUrl',
|
||||||
|
),
|
||||||
SettingItem('', null, SettingButton('保存', 'config-ob11-save', 'primary')),
|
SettingItem('', null, SettingButton('保存', 'config-ob11-save', 'primary')),
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
Loading…
x
Reference in New Issue
Block a user