feat: 新增群文件上传

This commit is contained in:
秋刀鱼 2024-11-13 15:58:26 +08:00
parent 05931a6586
commit 1671ab4cd7
3 changed files with 164 additions and 60 deletions

View File

@ -6,12 +6,14 @@ import PickSongList from "../model/pick-song.js";
import NeteaseMusicInfo from '../model/neteaseMusicInfo.js' import NeteaseMusicInfo from '../model/neteaseMusicInfo.js'
import { NETEASE_API_CN, NETEASE_SONG_DOWNLOAD, NETEASE_TEMP_API } from "../constants/tools.js"; import { NETEASE_API_CN, NETEASE_SONG_DOWNLOAD, NETEASE_TEMP_API } from "../constants/tools.js";
import { COMMON_USER_AGENT, REDIS_YUNZAI_ISOVERSEA, REDIS_YUNZAI_SONGINFO, REDIS_YUNZAI_CLOUDSONGLIST } from "../constants/constant.js"; import { COMMON_USER_AGENT, REDIS_YUNZAI_ISOVERSEA, REDIS_YUNZAI_SONGINFO, REDIS_YUNZAI_CLOUDSONGLIST } from "../constants/constant.js";
import { downloadAudio } from "../utils/common.js"; import { downloadAudio, retryAxiosReq } from "../utils/common.js";
import { redisExistKey, redisGetKey, redisSetKey } from "../utils/redis-util.js"; import { redisExistKey, redisGetKey, redisSetKey } from "../utils/redis-util.js";
import { checkAndRemoveFile } from "../utils/file.js"; import { checkAndRemoveFile } from "../utils/file.js";
import { sendMusicCard } from "../utils/yunzai-util.js"; import { sendMusicCard, getGroupFileUrl } from "../utils/yunzai-util.js";
import config from "../model/config.js"; import config from "../model/config.js";
import FormData from 'form-data'; import FormData from 'form-data';
import NodeID3 from 'node-id3';
import { isError } from "node:util";
let FileSuffix = 'flac' let FileSuffix = 'flac'
@ -53,6 +55,11 @@ export class songRequest extends plugin {
reg: '^#?清除云盘缓存$', reg: '^#?清除云盘缓存$',
fnc: 'cleanCloudData', fnc: 'cleanCloudData',
permission: 'master' permission: 'master'
},
{
reg: '^#?获取群文件$',
fnc: 'getLatestDocument',
permission: 'master'
} }
] ]
}); });
@ -95,16 +102,16 @@ export class songRequest extends plugin {
const saveId = songInfo.findIndex(item => item.group_id === e.group.group_id) const saveId = songInfo.findIndex(item => item.group_id === e.group.group_id)
let musicDate = { 'group_id': group_id, data: [] } let musicDate = { 'group_id': group_id, data: [] }
// 获取搜索歌曲列表信息 // 获取搜索歌曲列表信息
let detailUrl = autoSelectNeteaseApi + "/song/detail?ids={}" //歌曲详情API let detailUrl = autoSelectNeteaseApi + "/song/detail?ids={}&time=" + Date.now() //歌曲详情API
if (e.msg.replace(/\s+/g, "").match(/点歌(.+)/)) { if (e.msg.replace(/\s+/g, "").match(/点歌(.+)/)) {
const songKeyWord = e.msg.replace(/\s+/g, "").match(/点歌(.+)/)[1] const songKeyWord = e.msg.replace(/\s+/g, "").match(/点歌(.+)/)[1].replace(/[^\w\u4e00-\u9fa5]/g, '')
// 获取云盘歌单列表 // 获取云盘歌单列表
const cloudSongList = await this.getCloudSong() const cloudSongList = await this.getCloudSong()
// 搜索云盘歌单并进行搜索 // 搜索云盘歌单并进行搜索
const matchedSongs = await cloudSongList.filter(({ songName, singerName }) => const matchedSongs = await cloudSongList.filter(({ songName, singerName }) =>
songName.includes(songKeyWord) || singerName.includes(songKeyWord) songName.includes(songKeyWord) || singerName.includes(songKeyWord) || songName == songKeyWord || singerName == songKeyWord
); );
// 计算列表数 计算偏移量 // 计算列表数
let songListCount = matchedSongs.length >= this.songRequestMaxList ? this.songRequestMaxList : matchedSongs.length let songListCount = matchedSongs.length >= this.songRequestMaxList ? this.songRequestMaxList : matchedSongs.length
let searchCount = this.songRequestMaxList - songListCount let searchCount = this.songRequestMaxList - songListCount
for (let i = 0; i < songListCount; i++) { for (let i = 0; i < songListCount; i++) {
@ -317,51 +324,47 @@ export class songRequest extends plugin {
const title = msg.message[0].data.match(/"title":"([^"]+)"/)[1] const title = msg.message[0].data.match(/"title":"([^"]+)"/)[1]
const desc = msg.message[0].data.match(/"desc":"([^"]+)"/)[1] const desc = msg.message[0].data.match(/"desc":"([^"]+)"/)[1]
if (id === "") return if (id === "") return
let path = this.getCurDownloadPath(e) + '/' + title + '-' + desc + '.' + FileSuffix let path = this.getCurDownloadPath(e) + '/' + desc + '-' + title + '.' + FileSuffix
let tryCount = 0
const tryUpload = async () => { const tryUpload = async () => {
let formData = new FormData() let formData = new FormData();
await formData.append('songFile', fs.createReadStream(path)) formData.append('songFile', fs.createReadStream(path));
const headers = { const headers = {
...formData.getHeaders(), ...formData.getHeaders(),
'Cookie': this.neteaseCookie, 'Cookie': this.neteaseCookie,
}; };
const updateUrl = autoSelectNeteaseApi + `/cloud?time=${Date.now()}` const updateUrl = `${autoSelectNeteaseApi}/cloud?time=${Date.now()}`;
axios({ try {
const res = await axios({
method: 'post', method: 'post',
url: updateUrl, url: updateUrl,
headers: headers, headers: headers,
data: formData, data: formData,
}) });
.then(async res => {
if (res.data.code == 200) { if (res.data.code == 200) {
let matchUrl = autoSelectNeteaseApi + '/cloud/match?uid=' + this.uid + "&sid=" + res.data.privateCloud.songId + '&asid=' + id let matchUrl = `${autoSelectNeteaseApi}/cloud/match?uid=${this.uid}&sid=${res.data.privateCloud.songId}&asid=${id}`;
await axios.get(matchUrl, { try {
const matchRes = await axios.get(matchUrl, {
headers: { headers: {
"User-Agent": COMMON_USER_AGENT, "User-Agent": COMMON_USER_AGENT,
"Cookie": this.neteaseCookie "Cookie": this.neteaseCookie
}, },
}).then(res => { });
logger.info('歌曲信息匹配成功') logger.info('歌曲信息匹配成功');
}) } catch (error) {
.catch(error => { logger.error('歌曲信息匹配错误', error);
logger.error('歌曲信息匹配错误', error)
})
this.songCloudUpdate(e)
} }
}) this.songCloudUpdate(e);
.catch(error => { return res;
tryCount += 1;
logger.info('失败喽~再试一次')
if (tryCount < 3) {
tryUpload(); // 直接调用
} else { } else {
logger.error('怎么想都传不上去吧', error) throw new Error('上传失败,响应不正确');
} }
} catch (error) {
throw error;
} }
)
}; };
tryUpload(); await retryAxiosReq(() => tryUpload())
await checkAndRemoveFile(path)
} }
// 获取云盘歌单 // 获取云盘歌单
@ -380,10 +383,10 @@ export class songRequest extends plugin {
"Cookie": this.neteaseCookie "Cookie": this.neteaseCookie
} }
}); });
const songs = res.data.data.map(({ simpleSong }) => ({ const songs = res.data.data.map(({ songId, songName, artist }) => ({
'songName': simpleSong.name, 'songName': songName,
'id': simpleSong.id, 'id': songId,
'singerName': simpleSong.ar[0].name || '喵喵~', 'singerName': artist || '喵喵~',
'duration': '云盘' 'duration': '云盘'
})); }));
songList.push(...songs); songList.push(...songs);
@ -404,6 +407,70 @@ export class songRequest extends plugin {
} }
} }
// 群文件上传云盘
async getLatestDocument(e) {
const autoSelectNeteaseApi = await this.pickApi()
const cleanPath = await getGroupFileUrl(e)
logger.info(cleanPath)
// 拓展名
const extension = cleanPath.match(/\.\w+$/);
// 获取文件路径
const dirPath = cleanPath.substring(0, cleanPath.lastIndexOf('/'));
// 获取文件名
const fileName = cleanPath.split('/').pop().replace(/\.\w+$/, '');
logger.info(fileName)
const parts = fileName.match(/([\u4e00-\u9fa5a-zA-Z0-9]+)\s*-\s*([\u4e00-\u9fa5a-zA-Z0-9]+)/);
logger.info(parts)
logger.info(parts[1].trim())
const newFileName = dirPath + '/' + parts[2].trim() + extension
// 进行元数据编辑
if (parts) {
const tags = {
title: parts[2].trim(),
artist: parts[1].trim()
};
// 写入元数据
let success = NodeID3.write(tags, cleanPath)
if (fs.existsSync(newFileName)) {
logger.info(`音频已存在`);
fs.unlinkSync(newFileName);
}
// 文件重命名
fs.renameSync(cleanPath, newFileName)
if (success) logger.info('写入元数据成功')
} else {
logger.info('未按照标准命名')
}
// 上传请求
const tryUpload = async () => {
let formData = new FormData()
await formData.append('songFile', fs.createReadStream(newFileName))
const headers = {
...formData.getHeaders(),
'Cookie': this.neteaseCookie,
};
const updateUrl = autoSelectNeteaseApi + `/cloud?time=${Date.now()}`
try {
const res = await axios({
method: 'post',
url: updateUrl,
headers: headers,
data: formData,
});
this.songCloudUpdate(e);
return res;
} catch (error) {
throw error;
}
};
// 重试
await retryAxiosReq(() => tryUpload())
checkAndRemoveFile(newFileName)
}
// 清除缓存
async cleanCloudData(e) { async cleanCloudData(e) {
await redisSetKey(REDIS_YUNZAI_CLOUDSONGLIST, []) await redisSetKey(REDIS_YUNZAI_CLOUDSONGLIST, [])
} }
@ -494,7 +561,7 @@ export class songRequest extends plugin {
const AudioSize = bytesToMB(resp.data.data?.[0]?.size) const AudioSize = bytesToMB(resp.data.data?.[0]?.size)
// 获取歌曲标题 // 获取歌曲标题
let title = songInfo[pickNumber].songName + '-' + songInfo[pickNumber].singerName let title = songInfo[pickNumber].singerName + '-' + songInfo[pickNumber].songName
let typelist = [] let typelist = []
// 歌曲百科API // 歌曲百科API
await axios.get(songWikiUrl, { await axios.get(songWikiUrl, {
@ -503,8 +570,8 @@ export class songRequest extends plugin {
// "Cookie": this.neteaseCookie // "Cookie": this.neteaseCookie
}, },
}).then(res => { }).then(res => {
const wikiData = res.data.data.blocks[1].creatives const wikiData = res.data.data.blocks[1]?.creatives || []
if (wikiData[0]) {
typelist.push(wikiData[0].resources[0].uiElement.mainTitle.title) typelist.push(wikiData[0].resources[0].uiElement.mainTitle.title)
// 防止数据过深出错 // 防止数据过深出错
const recTags = wikiData[1] const recTags = wikiData[1]
@ -522,6 +589,7 @@ export class songRequest extends plugin {
} else { } else {
typelist.push(wikiData[2].uiElement.textLinks[0].text) typelist.push(wikiData[2].uiElement.textLinks[0].text)
} }
}
typelist.push(AudioLevel) typelist.push(AudioLevel)
}) })
let musicInfo = { let musicInfo = {

View File

@ -5,6 +5,7 @@
"dependencies": { "dependencies": {
"axios": "^1.3.4", "axios": "^1.3.4",
"form-data": "^4.0.1", "form-data": "^4.0.1",
"node-id3": "^0.2.6",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"p-queue": "^8.0.1" "p-queue": "^8.0.1"
} }

View File

@ -34,3 +34,38 @@ export async function sendMusicCard(e, platformType, musicId) {
] ]
}); });
} }
/**
* 获取群文件Url地址
* @param e
* @param count 获取群聊条数
*/
export async function getGroupFileUrl(e, count = 10) {
const latestChat = await e.bot.sendApi("get_group_msg_history", {
"group_id": e.group_id,
"count": count
});
const messages = latestChat.data.messages;
let file_id = "";
for (let i = messages.length - 1; i >= 0; i--) {
const message = messages?.[i]?.message;
if (message?.[0]?.type === "file") {
file_id = message?.[0].data?.file_id;
break;
}
}
if (file_id === "") {
logger.info('未找到群文件')
return "";
}
// 获取文件信息
let latestFileUrl = await e.bot.sendApi("get_group_file_url", {
"group_id": e.group_id,
"file_id": file_id
});
let path = decodeURIComponent(latestFileUrl.data.url)
const cleanPath = path.startsWith('file:///') ? path.replace('file:///', '') : path;
return cleanPath
}