koishi-plugin-music-link
Version:
/*音乐下载*/🎵搜索音乐资源🤩提供QQ、网易云平台的音乐下载,付费的也可以欸?[点我查看使用方法](https://github.com/shangxueink/koishi-shangxue-apps/tree/main/plugins/music-link)
1,292 lines (1,266 loc) • 94.1 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name2 in all)
__defProp(target, name2, { get: all[name2], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
Config: () => Config,
apply: () => apply,
inject: () => inject,
logger: () => logger,
name: () => name,
reusable: () => reusable,
usage: () => usage
});
module.exports = __toCommonJS(src_exports);
var import_koishi = require("koishi");
var import_promises = __toESM(require("node:fs/promises"));
var import_node_crypto = __toESM(require("node:crypto"));
var import_node_path = __toESM(require("node:path"));
var import_node_url = __toESM(require("node:url"));
var name = "music-link";
var reusable = true;
var logger = new import_koishi.Logger("music-link");
var inject = {
required: ["http", "i18n"],
optional: ["puppeteer"]
};
var usage = `
<hr>
<details>
<summary><h3>使用方法 (点击展开)</h3></summary>
<p>安装并配置插件后,使用下述命令搜索和下载音乐:</p>
<hr>
<h3>使用星之阁API搜索QQ、网易云音乐</h3>
<pre><code>下载音乐 [keywords]</code></pre>
<p><b>(不推荐)</b> 星之阁API,需要加群申请API Key,且API Key可能存在失效风险。支持QQ音乐和网易云音乐,速度较慢,稳定性一般。</p>
<hr>
<h3>使用星之阁-酷狗API搜索酷狗音乐</h3>
<pre><code>酷狗音乐 [keywords]</code></pre>
<p><b>(不推荐)</b> 星之阁-酷狗API,需要加群申请API Key,且API Key可能存在失效风险。仅支持酷狗音乐,速度较慢,稳定性一般。</p>
<hr>
<h3>使用music.gdstudio.xyz网站搜索各大音乐平台</h3>
<pre><code>歌曲搜索 [keywords]</code></pre>
<p><b>(比较推荐)</b> music.gdstudio.xyz 网站,无需API Key,但需要 <b>puppeteer</b> 服务支持进行网页爬取,速度还行。默认使用网易云音乐搜索,支持多平台选择。</p>
<hr>
<h3>使用api.injahow.cn网站搜索网易云音乐</h3>
<pre><code>网易点歌 [歌曲名称/歌曲ID]</code></pre>
<p><b>(很推荐)</b> api.injahow.cn 网站,API请求快速且稳定,无需 puppeteer 服务,推荐QQ官方机器人使用此后端,使用这个后端VIP歌曲只能听45秒,但这个指令还有一个后端可以都听。很好用哦<b>仅支持网易云音乐</b>,可以通过歌曲名称或歌曲ID进行搜索。</p>
<hr>
<h3>使用dev.iw233.cn网站搜索网易云音乐</h3>
<pre><code>音乐搜索器 [keywords]</code></pre>
<p><b>(推荐)</b> dev.iw233.cn 网站,无需API Key,但需要 <b>puppeteer</b> 服务支持进行网页爬取,速度较慢。支持网易云音乐搜索。</p>
<hr>
<h3>使用api.dragonlongzhu.cn网站API搜索音乐</h3>
<pre><code>龙珠搜索 [keywords]</code></pre>
<p><b>(一般推荐)</b> api.dragonlongzhu.cn 网站的点歌API。支持多平台音乐搜索。</p>
<hr>
</details>
---
<h3>如何返回语音/视频/群文件消息</h3>
<p>可以修改对应指令的<code>返回字段表</code>中的 <code>下载链接</code> 对应的 <code>字段发送类型</code> 字段,
把 <code>text</code> 更改为 <code>audio</code> 就是返回 语音,
改为 <code>video</code> 就是返回 视频消息,
改为 <code>file</code> 就是返回 群文件。</p>
<hr>
<p>⚠️需要注意的是,当配置返回格式为音频/视频的时候,请自行检查是否安装了 <code>silk</code>、<code>ffmpeg</code> 等服务。</p>
<p>⚠️如果你选择了 <code>file</code> 类型,请确保平台支持!目前仅实测了 <code>onebot</code> 平台的部分协议端支持!</p>
<hr>
<h3>使用 <code>-n 1</code> 直接返回内容</h3>
<p>在使用命令时,可以通过添加 <code>-n 1</code> 选项直接返回指定序号的歌曲内容。这对于快速获取特定歌曲非常有用。</p>
<p>例如,使用以下命令可以直接获取第一首歌曲的详细信息:</p>
<pre><code>歌曲搜索 -n 1 蔚蓝档案</code></pre>
---
## 重要提示⚠️
### 目前 星之阁API的key已经失效,如需使用请自行前往注册
### 目前 推荐使用<code>api.injahow.cn(网易云点歌)</code>的服务,请确保<code>puppeteer</code>服务可用
---
| 后端推荐度 | 名称 | 备注 |
| :--------: | :-------------------------------: | :---: |
| **ⅰ** | \`api.injahow.cn\` (歌曲搜索) | 较高 |
| **ⅱ** | \`dev.iw233.cn\` (音乐搜索器) | 中等 |
| *......* | 其他 | 中等 |
| **ⅳ** | \`星之阁API\` (下载音乐/酷狗音乐) | 较低 |
---
目前基本QQ音乐都死翘翘了 (腾讯太小气了
`;
var command1_return_qqdata_Field_default = [
{
"data": "songname",
"describe": "歌曲名称",
"type": "text",
"enable": true
},
{
"data": "subtitle",
"describe": "标题",
"type": "text",
"enable": false
},
{
"data": "name",
"describe": "歌手",
"type": "text",
"enable": true
},
{
"data": "album",
"describe": "专辑",
"type": "text",
"enable": false
},
{
"data": "pay",
"describe": "付费情况",
"type": "text",
"enable": false
},
{
"data": "song_type",
"describe": "歌曲类型",
"type": "text",
"enable": false
},
{
"data": "type",
"describe": "类型",
"type": "text",
"enable": false
},
{
"data": "songid",
"describe": "歌曲ID",
"type": "text",
"enable": false
},
{
"data": "mid",
"describe": "mid",
"type": "text",
"enable": false
},
{
"data": "time",
"describe": "发行时间",
"type": "text",
"enable": false
},
{
"data": "bpm",
"describe": "bpm",
"type": "text",
"enable": false
},
{
"data": "quality",
"describe": "音质",
"type": "text",
"enable": true
},
{
"data": "interval",
"describe": "时长",
"type": "text",
"enable": false
},
{
"data": "size",
"describe": "大小",
"type": "text",
"enable": true
},
{
"data": "kbps",
"describe": "分辨率",
"type": "text",
"enable": false
},
{
"data": "cover",
"describe": "封面",
"type": "image",
"enable": true
},
{
"data": "songurl",
"describe": "歌曲链接",
"type": "text",
"enable": false
},
{
"data": "src",
"describe": "下载链接",
"type": "text",
"enable": true
}
];
var command1_return_wyydata_Field_default = [
{
"data": "songname",
"describe": "歌曲名称",
"type": "text",
"enable": true
},
{
"data": "name",
"describe": "歌手",
"type": "text",
"enable": true
},
{
"data": "album",
"describe": "专辑",
"type": "text",
"enable": false
},
{
"data": "pay",
"describe": "付费情况",
"enable": false,
"type": "text"
},
{
"data": "id",
"describe": "歌曲ID",
"enable": false,
"type": "text"
},
{
"data": "quality",
"describe": "音质",
"type": "text",
"enable": true
},
{
"data": "interval",
"describe": "时长",
"enable": false,
"type": "text"
},
{
"data": "size",
"describe": "大小",
"type": "text",
"enable": true
},
{
"data": "kbps",
"describe": "分辨率",
"enable": false,
"type": "text"
},
{
"data": "cover",
"describe": "封面",
"type": "image",
"enable": true
},
{
"data": "songurl",
"describe": "歌曲链接",
"type": "text",
"enable": false
},
{
"data": "src",
"describe": "下载链接",
"type": "text",
"enable": true
}
];
var command4_return_data_Field_default = [
{
"data": "songname",
"describe": "歌曲名称",
"type": "text",
"enable": true
},
{
"data": "name",
"describe": "歌手",
"type": "text",
"enable": true
},
{
"data": "album",
"describe": "专辑",
"type": "text",
"enable": true
},
{
"data": "quality",
"describe": "音质",
"type": "text",
"enable": true
},
{
"data": "interval",
"describe": "时长",
"type": "text",
"enable": false
},
{
"data": "size",
"describe": "大小",
"type": "text",
"enable": true
},
{
"data": "kbps",
"describe": "分辨率",
"type": "text",
"enable": false
},
{
"data": "cover",
"describe": "封面",
"type": "image",
"enable": true
},
{
"data": "src",
"describe": "下载链接",
"type": "text",
"enable": true
},
{
"data": "songurl",
"describe": "跳转链接",
"type": "text",
"enable": false
}
];
var command5_return_data_Field_default = [
{
"data": "name",
"describe": "歌曲名称",
"type": "text",
"enable": true
},
{
"data": "artist",
"describe": "歌手",
"type": "text",
"enable": true
},
{
"data": "album",
"describe": "专辑",
"type": "text",
"enable": false
},
{
"data": "source",
"describe": "来源平台",
"enable": false,
"type": "text"
},
{
"data": "fileSize",
"describe": "文件大小",
"type": "text",
"enable": true
},
{
"data": "br",
"describe": "比特率",
"type": "text",
"enable": false
},
{
"data": "coverUrl",
"describe": "封面链接",
"type": "image",
"enable": true
},
{
"data": "musicUrl",
"describe": "下载链接",
"type": "text",
"enable": true
},
{
"data": "lyric",
"describe": "歌词",
"type": "text",
"enable": false
}
];
var command6_return_data_Field_default = [
{
"data": "name",
"describe": "歌曲名称",
"type": "text",
"enable": true
},
{
"data": "id",
"describe": "歌曲ID",
"type": "text",
"enable": true
},
{
"data": "artist",
"describe": "歌手",
"type": "text",
"enable": true
},
{
"data": "url",
"describe": "下载链接",
"type": "text",
"enable": true
},
{
"data": "pic",
"describe": "封面链接",
"type": "image",
"enable": true
},
{
"data": "lrc",
"describe": "歌词",
"type": "text",
"enable": false
}
];
var command7_return_data_Field_default = [
{
"type": "text",
"data": "type",
"describe": "平台名称",
"enable": false
},
{
"data": "link",
"describe": "音乐地址",
"type": "text",
"enable": false
},
{
"data": "songid",
"describe": "歌曲ID",
"type": "text",
"enable": false
},
{
"data": "title",
"describe": "歌曲名称",
"type": "text",
"enable": true
},
{
"data": "author",
"describe": "歌手",
"type": "text",
"enable": true
},
{
"data": "lrc",
"describe": "歌词",
"type": "text",
"enable": false
},
{
"data": "url",
"describe": "下载链接",
"type": "text",
"enable": true
},
{
"data": "pic",
"describe": "封面链接",
"type": "image",
"enable": true
}
];
var command8_return_QQdata_Field_default = [
{
"data": "title",
"describe": "歌曲名称",
"type": "text",
"enable": true
},
{
"data": "singer",
"describe": "歌手",
"type": "text",
"enable": true
},
{
"data": "cover",
"describe": "封面",
"type": "image",
"enable": true
},
{
"data": "link",
"describe": "歌曲链接",
"type": "text",
"enable": false
},
{
"data": "url",
"describe": "下载链接",
"type": "text",
"enable": true
},
{
"data": "lyric",
"describe": "歌词",
"type": "text",
"enable": false
}
];
var platformMap = {
"网易云": "netease",
"QQ": "tencent",
"酷我": "kuwo",
"Tidal": "tidal",
"Qobuz": "qobuz",
"喜马FM": "ximalaya",
"咪咕": "migu",
"酷狗": "kugou",
"油管": "ytmusic",
"Spotify": "spotify"
};
var Config = import_koishi.Schema.intersect([
import_koishi.Schema.object({
enableReplySonglist: import_koishi.Schema.boolean().default(false).description("开启后 发送歌单消息的时候 会回复触发指令的消息"),
waitTimeout: import_koishi.Schema.natural().role("s").description("允许用户返回选择序号的等待时间").default(45),
exitCommand: import_koishi.Schema.string().default("0, 不听了").description("退出选择指令,多个指令间请用逗号分隔开"),
// 兼容中文逗号、英文逗号
menuExitCommandTip: import_koishi.Schema.boolean().default(false).description("是否在歌单内容的后面,加上退出选择指令的文字提示")
}).description("基础设置"),
import_koishi.Schema.object({
imageMode: import_koishi.Schema.boolean().default(true).description("开启后返回图片歌单(需要puppeteer服务),关闭后返回文本歌单(部分指令必须使用puppeteer)"),
darkMode: import_koishi.Schema.boolean().default(true).description("是否开启暗黑模式(黑底菜单)")
}).description("图片歌单设置"),
import_koishi.Schema.object({
serverSelect: import_koishi.Schema.union([
import_koishi.Schema.const("command1").description("command1:星之阁API (需加群申请APIkey) (QQ + 网易云)"),
import_koishi.Schema.const("command4").description("command4:星之阁-酷狗API (需加群申请APIkey) (酷狗)"),
import_koishi.Schema.const("command5").description("command5:`music.gdstudio.xyz` 网站 (需puppeteer爬取 较慢,但访问性好) (多平台)"),
import_koishi.Schema.const("command6").description("command6:`api.injahow.cn`网站 (API 请求快 + 稳定 推荐QQ官方机器人使用) (网易云)"),
import_koishi.Schema.const("command7").description("command7:`dev.iw233.cn` 网站 (需puppeteer爬取 较慢) (网易云)"),
import_koishi.Schema.const("command8").description("command8:`api.dragonlongzhu.cn` 龙珠API (多平台音乐)")
]).role("radio").default("command6").description("选择使用的后端<br>➣ 推荐度:`api.injahow.cn` ≥ `music.gdstudio.xyz` ≥ `dev.iw233.cn` ≥ `api.dragonlongzhu.cn` > `星之阁API`")
}).description("后端选择"),
import_koishi.Schema.union([
import_koishi.Schema.object({
serverSelect: import_koishi.Schema.const("command1").required(),
xingzhigeAPIkey: import_koishi.Schema.string().role("secret").description("星之阁的音乐API的请求key<br>(默认值是作者自己的哦,如果失效了请你自己获取一个)<br>请前往 QQ群 905188643 <br>添加QQ好友 3556898686 <br>私聊发送 `/getapikey` 获得你的APIkey以填入此处 ").default("xhsP7Q4MulpzDU6BVwHSKB-j-NfvBxaqiT37hx8djyE="),
command1: import_koishi.Schema.string().default("下载音乐").description("星之阁API的指令名称"),
command1_wyy_Quality: import_koishi.Schema.number().default(2).description("网易云音乐默认下载音质。默认2,其余自己试 `不建议更改,可能会导致无音源`"),
command1_qq_Quality: import_koishi.Schema.number().default(2).description("QQ音乐默认下载音质。音质11为最高 `不建议更改,可能会导致无音源`"),
command1_qq_uin: import_koishi.Schema.string().description("QQ音乐搜索:提供skey的账号(当站长提供的cookie失效时必填,届时生效)"),
command1_qq_skey: import_koishi.Schema.string().description("QQ音乐搜索:提供开通有绿钻特权的skey可获取vip歌曲(当站长提供的cookie失效时必填,届时生效)为空默认获取站长提供的skey"),
command1_return_qqdata_Field: import_koishi.Schema.array(import_koishi.Schema.object({
data: import_koishi.Schema.string().description("返回的字段"),
describe: import_koishi.Schema.string().description("对该字段的中文描述"),
type: import_koishi.Schema.union([
import_koishi.Schema.const("text").description("文本(text)"),
import_koishi.Schema.const("image").description("图片(image)"),
import_koishi.Schema.const("audio").description("语音(audio)"),
import_koishi.Schema.const("video").description("视频(video)"),
import_koishi.Schema.const("file").description("文件(file)")
]).description("字段发送类型"),
enable: import_koishi.Schema.boolean().default(true).description("是否启用")
})).role("table").default(command1_return_qqdata_Field_default).description("歌曲返回信息的字段选择<br>[➣ 点我查看该API返回内容示例](https://api.xingzhige.com/API/QQmusicVIP/?songid=499449053&br=2&uin=2&skey=2&key=)"),
command1_return_wyydata_Field: import_koishi.Schema.array(import_koishi.Schema.object({
data: import_koishi.Schema.string().description("返回的字段"),
describe: import_koishi.Schema.string().description("对该字段的中文描述"),
type: import_koishi.Schema.union([
import_koishi.Schema.const("text").description("文本(text)"),
import_koishi.Schema.const("image").description("图片(image)"),
import_koishi.Schema.const("audio").description("语音(audio)"),
import_koishi.Schema.const("video").description("视频(video)"),
import_koishi.Schema.const("file").description("文件(file)")
]).description("字段发送类型"),
enable: import_koishi.Schema.boolean().default(true).description("是否启用")
})).role("table").default(command1_return_wyydata_Field_default).description("歌曲返回信息的字段选择<br>[➣ 点我查看该API返回内容示例](https://api.xingzhige.com/API/NetEase_CloudMusic_new/?name=%E8%94%9A%E8%93%9D%E6%A1%88&n=1&key=)")
}).description("星之阁API返回设置"),
import_koishi.Schema.object({
serverSelect: import_koishi.Schema.const("command4").required(),
xingzhigeAPIkey: import_koishi.Schema.string().role("secret").description("星之阁的音乐API的请求key<br>(默认值是作者自己的哦,如果失效了请你自己获取一个)<br>请前往 QQ群 905188643 <br>添加QQ好友 3556898686 <br>私聊发送 `/getapikey` 获得你的APIkey以填入此处 ").default("xhsP7Q4MulpzDU6BVwHSKB-j-NfvBxaqiT37hx8djyE="),
command4: import_koishi.Schema.string().default("酷狗音乐").description("酷狗-星之阁API的指令名称"),
command4_kugouQuality: import_koishi.Schema.number().default(1).description("音乐默认下载音质。音质,默认为1"),
command4_return_data_Field: import_koishi.Schema.array(import_koishi.Schema.object({
data: import_koishi.Schema.string().description("返回的字段"),
describe: import_koishi.Schema.string().description("对该字段的中文描述"),
type: import_koishi.Schema.union([
import_koishi.Schema.const("text").description("文本(text)"),
import_koishi.Schema.const("image").description("图片(image)"),
import_koishi.Schema.const("audio").description("语音(audio)"),
import_koishi.Schema.const("video").description("视频(video)"),
import_koishi.Schema.const("file").description("文件(file)")
]).description("字段发送类型"),
enable: import_koishi.Schema.boolean().default(true).description("是否启用")
})).role("table").default(command4_return_data_Field_default).description("歌曲返回信息的字段选择<br>[➣ 点我查看该API返回内容示例](https://api.xingzhige.com/API/Kugou_GN_new/?name=蔚蓝档案&pagesize=20&br=2&key=)")
}).description("酷狗-星之阁API返回设置"),
import_koishi.Schema.object({
serverSelect: import_koishi.Schema.const("command5").required(),
command5: import_koishi.Schema.string().default("歌曲搜索").description("`music.gdstudio.xyz`的指令名称"),
command5_defaultPlatform: import_koishi.Schema.union([
import_koishi.Schema.const("网易云").description("网易云"),
import_koishi.Schema.const("QQ").description("QQ"),
import_koishi.Schema.const("酷我").description("酷我"),
import_koishi.Schema.const("Tidal").description("Tidal"),
import_koishi.Schema.const("Qobuz").description("Qobuz"),
import_koishi.Schema.const("喜马FM").description("喜马FM"),
import_koishi.Schema.const("咪咕").description("咪咕"),
import_koishi.Schema.const("酷狗").description("酷狗"),
import_koishi.Schema.const("油管").description("油管"),
import_koishi.Schema.const("Spotify").description("Spotify")
]).description("音乐 **默认**使用的平台。").default("网易云"),
/*
command5_defaultQuality: Schema.union([
Schema.const('128K').description('128K标准 [ 全部音乐源 ]<br>192K较高 [ 网易云 / QQ / Spotify / 咪咕 / 油管 ]'),
Schema.const('320K').description('320K高品 [ 全部音乐源 ]'),
Schema.const('16bit').description('16bit无损 [ 网易云 / QQ / 酷我 / Tidal / Qobuz / 咪咕 ]'),
Schema.const('24bit').description('24bit无损 [ 网易云 / QQ / Tidal / Qobuz ]'),
]).role('radio').description('音乐 **默认**下载音质。').default('320K'),
*/
command5_searchList: import_koishi.Schema.number().default(20).min(1).max(50).description("歌曲搜索的列表长度。返回的候选项个数。"),
command5_page_setTimeout: import_koishi.Schema.number().default(15).min(1).description("等待页面完全加载的等待时间(秒)"),
command5_return_data_Field: import_koishi.Schema.array(import_koishi.Schema.object({
data: import_koishi.Schema.string().description("返回的字段"),
describe: import_koishi.Schema.string().description("对该字段的中文描述"),
type: import_koishi.Schema.union([
import_koishi.Schema.const("text").description("文本(text)"),
import_koishi.Schema.const("image").description("图片(image)"),
import_koishi.Schema.const("audio").description("语音(audio)"),
import_koishi.Schema.const("video").description("视频(video)"),
import_koishi.Schema.const("file").description("文件(file)")
]).description("字段发送类型"),
enable: import_koishi.Schema.boolean().default(true).description("是否启用")
})).role("table").description("歌曲返回信息的字段选择<br>").default(command5_return_data_Field_default)
}).description("`music.gdstudio.xyz`返回设置"),
import_koishi.Schema.object({
serverSelect: import_koishi.Schema.const("command6"),
command6: import_koishi.Schema.string().default("网易点歌").description("`网易点歌`的指令名称<br>输入歌曲ID,返回歌曲"),
command6_searchList: import_koishi.Schema.number().default(20).min(1).max(50).description("歌曲搜索的列表长度。返回的候选项个数。"),
maxDuration: import_koishi.Schema.natural().description("歌曲最长持续时间,单位为:秒").default(900),
command6_useProxy: import_koishi.Schema.boolean().experimental().description("是否使用 Apifox Web Proxy 代理请求(适用于海外用户)").default(false),
command6_usedAPI: import_koishi.Schema.union([
import_koishi.Schema.const("api.injahow.cn").description("(稳定)黑胶只能30秒的`api.injahow.cn`后端(适合官方bot)"),
import_koishi.Schema.const("meting.jmstrand.cn").description("(推荐)稳定性未知、全部可听的`meting.jmstrand.cn`后端").experimental(),
import_koishi.Schema.const("api.qijieya.cn").description("(推荐)稳定性未知、全部可听的`api.qijieya.cn`后端").experimental(),
import_koishi.Schema.const("metingapi.nanorocky.top").description("(不推荐 文件很大) 稳定性未知、无损音质、全部可听的`meting.jmstrand.cn`后端").experimental()
]).description("选择 获取音乐直链的后端API").default("api.qijieya.cn"),
command6_return_data_Field: import_koishi.Schema.array(import_koishi.Schema.object({
data: import_koishi.Schema.string().description("返回的字段"),
describe: import_koishi.Schema.string().description("对该字段的中文描述"),
type: import_koishi.Schema.union([
import_koishi.Schema.const("text").description("文本(text)"),
import_koishi.Schema.const("image").description("图片(image)"),
import_koishi.Schema.const("audio").description("语音(audio)"),
import_koishi.Schema.const("video").description("视频(video)"),
import_koishi.Schema.const("file").description("文件(file)")
]).description("字段发送类型"),
enable: import_koishi.Schema.boolean().default(true).description("是否启用")
})).role("table").description("歌曲返回信息的字段选择<br>[➣ 点我查看该API返回内容示例](http://music.163.com/api/search/get/web?csrf_token=hlpretag=&hlposttag=&s=蔚蓝档案&type=1&offset=0&total=true&limit=10)").default(command6_return_data_Field_default)
}).description("`网易点歌`返回设置"),
import_koishi.Schema.object({
serverSelect: import_koishi.Schema.const("command7").required(),
command7: import_koishi.Schema.string().default("音乐搜索器").description("`音乐搜索器`的指令名称<br>使用 dev.iw233.cn 提供的网站"),
command7_searchList: import_koishi.Schema.number().default(10).min(1).step(1).max(50).description("歌曲搜索的列表长度。返回的候选项个数。<br>为`网易云音乐`的组合"),
command7_return_data_Field: import_koishi.Schema.array(import_koishi.Schema.object({
data: import_koishi.Schema.string().description("返回的字段"),
describe: import_koishi.Schema.string().description("对该字段的中文描述"),
type: import_koishi.Schema.union([
import_koishi.Schema.const("text").description("文本(text)"),
import_koishi.Schema.const("image").description("图片(image)"),
import_koishi.Schema.const("audio").description("语音(audio)"),
import_koishi.Schema.const("video").description("视频(video)"),
import_koishi.Schema.const("file").description("文件(file)")
]).description("字段发送类型"),
enable: import_koishi.Schema.boolean().default(true).description("是否启用")
})).role("table").description("歌曲返回信息的字段选择<br>[➣ 点我查看该API返回内容示例](https://dev.iw233.cn/Music1/?name=%E8%94%9A%E8%93%9D%E6%A1%A3%E6%A1%88&type=netease) 需F12 网络标签页 预览响应 `Music1/`").default(command7_return_data_Field_default)
}).description("`dev.iw233.cn`返回设置"),
import_koishi.Schema.object({
serverSelect: import_koishi.Schema.const("command8").required(),
command8: import_koishi.Schema.string().default("龙珠搜索").description("龙珠API的指令名称"),
// command8_wyyQuality: Schema.number().default(1).description('QQ音乐默认下载音质。`找不到对应音质,会自动使用标准音质`<br>1(标准音质)/2(极高音质)/3(无损音质)/4(Hi-Res音质)/5(高清环绕声)/6(沉浸环绕声)/7(超清母带)'),
command8_searchList: import_koishi.Schema.number().default(20).min(1).max(50).description("歌曲搜索的列表长度。返回的候选项个数。"),
command8_return_QQdata_Field: import_koishi.Schema.array(import_koishi.Schema.object({
data: import_koishi.Schema.string().description("返回的字段"),
describe: import_koishi.Schema.string().description("对该字段的中文描述"),
type: import_koishi.Schema.union([
import_koishi.Schema.const("text").description("文本(text)"),
import_koishi.Schema.const("image").description("图片(image)"),
import_koishi.Schema.const("audio").description("语音(audio)"),
import_koishi.Schema.const("video").description("视频(video)"),
import_koishi.Schema.const("file").description("文件(file)")
]).description("字段发送类型"),
enable: import_koishi.Schema.boolean().default(true).description("是否启用")
})).role("table").default(command8_return_QQdata_Field_default).description("音乐歌曲返回信息的字段选择<br>[➣ 点我查看该API返回内容示例](https://api.dragonlongzhu.cn/api/joox/juhe_music.php?msg=%E8%94%9A%E8%93%9D%E6%A1%A3%E6%A1%88&type=json&br=1&num=20&n=1)")
}).description("龙珠API返回设置"),
import_koishi.Schema.object({}).description("↑ 请选择后端服务 ↑")
]),
import_koishi.Schema.object({
enablemiddleware: import_koishi.Schema.boolean().description("是否自动解析JSON音乐卡片").default(false),
middleware: import_koishi.Schema.boolean().description("`enablemiddleware`是否使用前置中间件监听<br>`中间件无法接受到消息可以考虑开启`").default(false),
used_id: import_koishi.Schema.number().default(1).min(0).max(10).description("在歌单里默认选择的序号<br>范围`0-10`,无需考虑11-20,会自动根据JSON卡片的平台选择。若音乐平台不匹配 则在搜索项前十个进行选择。")
}).description("JSON卡片解析设置"),
import_koishi.Schema.object({
isfigure: import_koishi.Schema.boolean().default(false).description("`图片、文本`元素 使用合并转发,其余单独发送<br>`仅支持 onebot 适配器` 其他平台开启 无效").experimental(),
isuppercase: import_koishi.Schema.boolean().default(false).description("将链接域名进行大写置换,仅适用于qq官方平台").experimental(),
data_Field_Mode: import_koishi.Schema.union([
import_koishi.Schema.const("text").description("富媒体置底:文字 > 图片 > 语音 ≥ 视频 ≥ 文件 (默认)"),
import_koishi.Schema.const("image").description("仅图片置顶的 富媒体置底:图片 > 文字 ≥ 语音 ≥ 视频 ≥ 文件 (仅官方机器人考虑使用)"),
import_koishi.Schema.const("raw").description("严格按照 `command_return_data_Field` 表格的顺序 (严格按照配置项表格的上下顺序)")
]).role("radio").default("text").description("对 `command*_return_data_Field`配置项 排序的控制<br>优先级越高,顺序越靠前<br>[➣点我查看此配置项 效果预览图](https://i0.hdslb.com/bfs/article/6e8b901f9b9daa57f082bf0cece36102312276085.png)"),
renameTempFile: import_koishi.Schema.boolean().default(false).description("是否对`临时音频文件`以`歌曲名称`重命名<br>否则会使用hash值为名称<br>(仅在部分协议端的`h.file`方法下见效)").experimental(),
deleteTempTime: import_koishi.Schema.number().default(20).description("对于`file`类型的`Temp`临时文件的删除时间<br>若干`秒`后 删除下载的本地临时文件").experimental()
}).description("高级进阶设置"),
import_koishi.Schema.object({
loggerinfo: import_koishi.Schema.boolean().default(false).description("日志调试开关")
}).description("调试模式")
]);
function apply(ctx, config) {
const tempDir = import_node_path.default.join(__dirname, "temp");
let isTempDirInitialized = false;
const tempFiles = /* @__PURE__ */ new Set();
ctx.on("ready", async () => {
ctx.i18n.define("zh-CN", {
commands: {
[config.command1]: {
description: `搜索歌曲`,
messages: {
"nokeyword": `请输入歌曲相关信息。
➣示例:/${config.command1} 蔚蓝档案`,
"songlisterror": "无法获取歌曲列表,请稍后再试。",
"invalidNumber": "序号输入错误,已退出歌曲选择。",
"waitTime": "请在{0}秒内,\n输入歌曲对应的序号:\n➣示例:@机器人 1",
"waitTimeout": "输入超时,已取消点歌。",
"exitprompt": "已退出歌曲选择。",
"noplatform": "获取歌曲失败。",
"somerror": "解析歌曲详情时发生错误"
}
},
[config.command4]: {
description: `搜索酷狗音乐`,
messages: {
"nokeyword": `请输入歌曲相关信息。
➣示例:/${config.command4} 蔚蓝档案`,
"songlisterror": "获取酷狗音乐数据时发生错误,请稍后再试。",
"invalidNumber": "序号输入错误,已退出歌曲选择。",
"waitTime": "请在{0}秒内,\n输入歌曲对应的序号:\n➣示例:@机器人 1",
"waitTimeout": "输入超时,已取消点歌。",
"exitprompt": "已退出歌曲选择。",
"noplatform": "获取歌曲失败。",
"somerror": "解析歌曲详情时发生错误"
}
},
[config.command5]: {
description: `歌曲搜索`,
messages: {
"nopuppeteer": "没有开启puppeteer服务",
"nokeyword": `请输入歌曲相关信息。
➣示例:/${config.command5} 蔚蓝档案`,
"invalidplatform": "`不支持的平台: {0}`;",
"songlisterror": "无法获取歌曲列表,请稍后再试。",
"invalidNumber": "序号输入错误,已退出歌曲选择。",
"waitTime": "请在{0}秒内,\n输入歌曲对应的序号:\n➣示例:@机器人 1",
"waitTimeout": "输入超时,已取消点歌。",
"exitprompt": "已退出歌曲选择。",
"noplatform": "获取歌曲失败。",
"somerror": "解析歌曲详情时发生错误",
"noSearchResults": "没有找到相关的歌曲,请尝试更换关键词或平台。"
}
},
[config.command6]: {
description: `网易云点歌`,
messages: {
"nopuppeteer": "没有开启puppeteer服务",
"nokeyword": `请输入网易云歌曲的 名称 或 ID。
➣示例:/${config.command6} 蔚蓝档案
➣示例:/${config.command6} 2608813264`,
"invalidNumber": "序号输入错误,已退出歌曲选择。",
"waitTime": "请在{0}秒内,\n输入歌曲对应的序号:\n➣示例:@机器人 1",
"waitTimeout": "输入超时,已取消点歌。",
"exitprompt": "已退出歌曲选择。",
"noplatform": "获取歌曲失败。",
"somerror": "解析歌曲详情时发生错误",
"songlisterror": "无法获取歌曲列表,请稍后再试。",
"maxsongDuration": "歌曲持续时间超出限制,允许的单曲最大时长为 {0} 秒。"
}
},
[config.command7]: {
description: `音乐搜索器`,
messages: {
"nopuppeteer": "没有开启puppeteer服务",
"nokeyword": `请输入歌曲相关信息。
➣示例:/${config.command7} 蔚蓝档案`,
"invalidNumber": "序号输入错误,已退出歌曲选择。",
"waitTime": "请在{0}秒内,\n输入歌曲对应的序号:\n➣示例:@机器人 1",
"waitTimeout": "输入超时,已取消点歌。",
"exitprompt": "已退出歌曲选择。",
"noplatform": "获取歌曲失败。",
"somerror": "解析歌曲详情时发生错误",
"songlisterror": "无法获取歌曲列表,请稍后再试。"
}
},
[config.command8]: {
description: `龙珠音乐`,
messages: {
"nopuppeteer": "没有开启puppeteer服务",
"nokeyword": `请输入歌曲相关信息。
➣示例:/${config.command8} 蔚蓝档案`,
"invalidNumber": "序号输入错误,已退出歌曲选择。",
"waitTime": "请在{0}秒内,\n输入歌曲对应的序号:\n➣示例:@机器人 1",
"waitTimeout": "输入超时,已取消点歌。",
"exitprompt": "已退出歌曲选择。",
"noplatform": "获取歌曲失败。",
"somerror": "解析歌曲详情时发生错误",
"songlisterror": "无法获取歌曲列表,请稍后再试。"
}
}
}
});
if (config.enablemiddleware) {
ctx.middleware(async (session, next) => {
try {
const messageElements = await import_koishi.h.parse(session.content);
for (const element of messageElements) {
if (element.type === "json" && element.attrs && element.attrs.data) {
const jsonData = JSON.parse(element.attrs.data);
logInfo(JSON.stringify(jsonData, null, 2));
const musicMeta = jsonData?.meta?.music || jsonData?.meta?.news;
const tag = musicMeta?.tag;
if (musicMeta && tag.includes("音乐")) {
const title = musicMeta.title;
const desc = musicMeta.desc;
logInfo("↡--------------中间件解析--------------↡");
logInfo(tag);
logInfo(title);
logInfo(desc);
logInfo("↟--------------中间件解析--------------↟");
let command = config.serverSelect;
let commandName = config[command];
logInfo(commandName);
if (!commandName) {
commandName = "歌曲搜索";
logger.error(`未找到配置项 ${command} 对应的指令名称,使用默认指令名称 '歌曲搜索'`);
}
if (command === "command6" && tag === "网易云音乐") {
const jumpUrl = musicMeta.jumpUrl;
const match = jumpUrl?.match(/id=(\d+)/);
if (match && match[1]) {
const songId = match[1];
logInfo(`提取到网易云音乐 ID: ${songId}`);
await session.execute(`${commandName} ${songId}`);
return;
} else {
logger.error("未能在 jumpUrl 中找到歌曲 ID");
}
} else {
let usedId = config.used_id;
if (tag === "网易云音乐") {
if (config.serverSelect === "command1") {
usedId += 10;
}
}
logInfo(`使用指令: ${command} ,选择序号:${usedId}`);
if (command) {
logInfo(`${commandName} -n ${usedId} “${title} ${desc}”`);
await session.execute(`${commandName} -n ${usedId} “${title} ${desc}”`);
}
}
}
}
}
} catch (error) {
ctx.logger.error(error);
await session.send("处理消息时出错。");
}
return next();
}, config.middleware);
}
if (config.serverSelect === "command1") {
ctx.command(`${config.command1} <keyword:text>`).option("quality", "-q <value:number> 品质因数").option("number", "-n <number:number> 歌曲序号").action(async ({ session, options }, keyword) => {
if (!keyword) return import_koishi.h.text(session.text(".nokeyword"));
let qq, netease;
try {
let res = await searchQQ(ctx.http, keyword);
if (typeof res === "string") res = JSON.parse(res);
const item = res.request?.data?.body?.item_song;
qq = {
code: res.code,
msg: "",
data: Array.isArray(item) ? item.map((v) => ({
songname: v.title.replaceAll("<em>", "").replaceAll("</em>", ""),
album: v.album.name,
songid: v.id,
songurl: `https://y.qq.com/n/ryqq/songDetail/${v.mid}`,
name: v.singer.map((v2) => v2.name).join("/")
})) : []
};
logInfo(qq);
} catch (e) {
logger.error("获取QQ音乐数据时发生错误", e);
}
try {
netease = await searchXZG(
ctx.http,
"NetEase Music",
{
name: keyword,
key: config.xingzhigeAPIkey
}
);
} catch (e) {
logger.error("获取网易云音乐数据时发生错误", e);
}
const qqData = qq?.data;
const neteaseData = netease?.data;
if (!qqData?.length && !neteaseData?.length) return import_koishi.h.text(session.text(`.songlisterror`));
const totalQQSongs = qqData?.length ?? 0;
const totalNetEaseSongs = neteaseData?.length ?? 0;
let serialNumber = options.number;
if (serialNumber) {
serialNumber = Number(serialNumber);
if (Number.isNaN(serialNumber) || serialNumber < 1 || serialNumber > totalQQSongs + totalNetEaseSongs) {
return import_koishi.h.text(session.text(`.invalidNumber`));
}
} else {
const qqListText = qqData?.length ? formatSongList(qqData, "QQ Music", 0, 10) : "<b>QQ Music</b>: 无法获取歌曲列表";
const neteaseListText = neteaseData?.length ? formatSongList(neteaseData, "NetEase Music", qqData?.length ? 0 : 10, 20) : "<b>NetEase Music</b>: 无法获取歌曲列表";
const listText = `${qqListText}<br /><br />${neteaseListText}`;
const exitCommands = config.exitCommand.split(/[,,]/).map((cmd) => cmd.trim());
const exitCommandTip = config.menuExitCommandTip ? `退出选择请发[${exitCommands}]中的任意内容<br /><br />` : "";
let quoteId = session.messageId;
if (config.imageMode) {
const imageBuffer = await generateSongListImage(ctx.puppeteer, listText);
const payload = [
import_koishi.h.image(imageBuffer, "image/png"),
import_koishi.h.text(`${exitCommandTip.replaceAll("<br />", "\n")}${import_koishi.h.text(session.text(`.waitTime`, [config.waitTimeout]))}`)
];
const msg = await session.send(payload);
quoteId = msg.at(-1);
} else {
const msg = await session.send(`${listText}<br /><br />${exitCommandTip}${import_koishi.h.text(session.text(`.waitTime`, [config.waitTimeout]))}`);
quoteId = msg.at(-1);
}
const input = await session.prompt(config.waitTimeout * 1e3);
if (!input) {
return quoteId ? import_koishi.h.quote(quoteId) : "" + import_koishi.h.text(session.text(`.waitTimeout`));
}
if (exitCommands.includes(input)) {
return import_koishi.h.text(session.text(`.exitprompt`));
}
serialNumber = +input;
if (Number.isNaN(serialNumber) || serialNumber < 1 || serialNumber > totalQQSongs + totalNetEaseSongs) {
return import_koishi.h.text(session.text(`.songlisterror`));
}
}
let platform, songid, br, uin, skey;
let selected;
if (serialNumber <= totalQQSongs) {
selected = qqData[serialNumber - 1];
platform = "QQ Music";
songid = selected.songid;
br = config.command1_qq_Quality;
uin = config.command1_qq_uin;
skey = config.command1_qq_skey;
} else {
selected = neteaseData[serialNumber - totalQQSongs - 1];
platform = "NetEase Music";
songid = selected.id;
br = config.command1_wyy_Quality;
uin = "onlyqq";
skey = "onlyqq";
}
if (options.quality) {
br = options.quality;
}
if (!platform) return import_koishi.h.text(session.text(`.noplatform`));
const song = await searchXZG(ctx.http, platform, {
songid,
br,
uin,
skey,
key: config.xingzhigeAPIkey
});
if (song.code === 0) {
const data = song.data;
try {
let songDetails;
if (serialNumber <= totalQQSongs) {
songDetails = generateResponse(session, data, config.command1_return_qqdata_Field);
} else {
songDetails = generateResponse(session, data, config.command1_return_wyydata_Field);
}
logInfo(songDetails);
return songDetails;
} catch (e) {
logger.error(e);
return import_koishi.h.text(session.text(`.somerror`));
}
} else {
logger.error(`获取歌曲失败:${JSON.stringify(song)}`);
return "获取歌曲失败:" + song.msg;
}
});
}
if (config.serverSelect === "command4") {
ctx.command(`${config.command4} <keyword:text>`).option("quality", "-q <value:number> 音质因数").option("number", "-n <number:number> 歌曲序号").action(async ({ session, options }, keyword) => {
if (!keyword) return import_koishi.h.text(session.text(`.nokeyword`));
let kugou;
try {
kugou = await searchKugou(ctx.http, keyword, options.quality || config.command4_kugouQuality);
if (kugou.code !== 200) {
logger.error(kugou);
return import_koishi.h.text(`获取酷狗音乐数据时发生错误`);
}
} catch (e) {
logger.error("获取酷狗音乐数据时发生错误", e);
return import_koishi.h.text(session.text(`.songlisterror`));
}
const kugouData = kugou?.data;
if (!kugouData?.length) return import_koishi.h.text(session.text(`.songlisterror`));
const totalKugouSongs = kugouData.length;
let serialNumber = options.number;
if (serialNumber) {
serialNumber = Number(serialNumber);
if (Number.isNaN(serialNumber) || serialNumber < 1 || serialNumber > totalKugouSongs) {
return import_koishi.h.text(session.text(`.invalidNumber`));
}
} else {
const kugouListText = formatSongList(kugouData, "酷狗音乐", 0, 20);
const exitCommands = config.exitCommand.split(/[,,]/).map((cmd) => cmd.trim());
const exitCommandTip = config.menuExitCommandTip ? `退出选择请发[${exitCommands}]中的任意内容<br /><br />` : "";
let quoteId = session.messageId;
if (config.imageMode) {
const imageBuffer = await generateSongListImage(ctx.puppeteer, kugouListText);
const payload = [
import_koishi.h.image(imageBuffer, "image/png"),
import_koishi.h.text(`${exitCommandTip.replaceAll("<br />", "\n")}${import_koishi.h.text(session.text(`.waitTime`, [config.waitTimeout]))}`)
];
const msg = await session.send(payload);
quoteId = msg.at(-1);
} else {
const msg = await session.send(`${kugouListText}<br /><br />${exitCommandTip}${import_koishi.h.text(session.text(`.waitTime`, [config.waitTimeout]))}`);
quoteId = msg.at(-1);
}
const input = await session.prompt(config.waitTimeout * 1e3);
if (!input) {
return `${quoteId ? import_koishi.h.quote(quoteId) : ""}输入超时,已取消点歌。`;
}
if (exitCommands.includes(input)) {
return import_koishi.h.text(session.text(`.exitprompt`));
}
serialNumber = +input;
if (Number.isNaN(serialNumber) || serialNumber < 1 || serialNumber > totalKugouSongs) {
return import_koishi.h.text(session.text(`.invalidNumber`));
}
}
const br = options.quality || config.command4_kugouQuality;
const song = await searchKugouSong(ctx.http, keyword, br, serialNumber);
if (song.code === 0) {
const data = song.data;
try {
logInfo(song);
logInfo(data);
const songDetails = generateResponse(session, data, config.command4_return_data_Field);
logInfo(songDetails);
return songDetails;
} catch (e) {
logger.error(e);
return import_koishi.h.text(session.text(`.somerror`));
}
} else {
logger.error(`获取歌曲失败:${JSON.stringify(song)}`);
return "获取歌曲失败:" + song.msg;
}
});
}
if (config.serverSelect === "command5") {
ctx.command(`${config.command5} <keyword:text>`).option("platform", "-p <platform:string> 平台名称").option("number", "-n <number:number> 歌曲序号").example("歌曲搜索 -p QQ -n 1 蔚蓝档案").action(async ({ session, options }, keyword) => {
if (!ctx.puppeteer) {
await session.send(import_koishi.h.text(session.text(`.nopuppeteer`)));
return;
}
if (!keyword) return import_koishi.h.text(session.text(`.nokeyword`));
const page = await ctx.puppeteer.page();
let searchResults = [];
let songDetails = {
musicUrl: void 0,
coverUrl: void 0,
lyric: void 0,
musicSize: void 0,
musicBr: void 0
};
const exitCommands = config.exitCommand.split(/[,,]/).map((cmd) => cmd.trim());
const handleApiResponse = /* @__PURE__ */ __name((text, type) => {
try {
const match = text.match(/^jQuery\w+\((.*)\)$/);
let jsonData;
if (match) {
jsonData = JSON.parse(match[1]);
} else {
jsonData = JSON.parse(text);
}
if (!jsonData) {
ctx.logger.warn(`无法解析 ${type} API 响应: 没有 JSON 数据`);
return null;
}
return jsonData;
} catch (error) {
ctx.logger.error(`解析 ${type} API 响应失败:`, error, text);
return null;
}
}, "handleApiResponse");
const exitCommandTip = config.menuExitCommandTip ? `退出选择请发[${exitCommands}]中的任意内容
` : "";
const promptText = `${exitCommandTip}${import_koishi.h.text(session.text(`.waitTime`, [config.waitTimeout]))}`;
const waitTimeout = session.text(`.waitTimeout`);
const exitprompt = session.text(`.exitprompt`);
const invalidNumber = session.text(`.invalidNumber`);
let popupError;
async function checkAndHandlePopup(page2) {
const alert = await page2.$(".layui-layer.layui-layer-msg.layui-layer-hui, .layui-layer-dialog.layui-layer-msg");
if (alert) {
const alertText = await page2.evaluate((alertElement) => {
const alertContent = alertElement.querySelector(".layui-layer-content");
return alertContent ? alertContent.innerText : null;
}, alert);
if (alertText) {
if (alertText.includes("使用国内节点")) {
logInfo("检测到国内节点提示弹窗");
await page2.evaluate((alertElement) => {
if (alertElement) alertElement.remove();
}, alert);
logInfo("已删除国内节点提示弹窗");
await new Promise((resolve) => ctx.setTimeout(resolve, 1e3));
logInfo("等待1秒后再次检查弹窗");
const secondAlert = await page2.$(".layui-layer.layui-layer-msg.layui-layer-hui, .layui-layer-dialog.layui-layer-msg");
if (secondAlert) {
logInfo("开始检测到第二个弹窗");
const secondAlertText = await page2.evaluate((alertElement) => {
const alertContent = alertElement.querySelector(".layui-layer-content");
return alertContent ? alertContent.innerText : null;
}, secondAlert);
if (secondAlertText) {
await page2.close();
logInfo(`${secondAlertText}`);
return `${secondAlertText}`;
}
}
return null;
}
if (alertText.includes("已达今日上限")) {
await page2.close();
logInfo("该平台请求已达今日上限。");
return `该平台请求已达今日上限。`;
}
if (alertText.includes("没有找到相关歌曲")) {
await page2.close();
logInfo("没有找到相关歌曲,请切换其它音乐源。");
return `没有找到相关歌曲,请切换其它音乐源。`;
}
if (alertText.includes("播放失败") || alertText.includes("已停止")) {
await page2.close();
logInfo("获取失败。没有此歌曲的下载链接。");
return `获取失败。没有此歌曲的下载链接。`;
}
}
}
return null;
}
__name(checkAndHandlePopup, "checkAndHandlePopup");
page.on("response", async (response) => {
const url2 = response.url();
if (url2.includes("api.php?callback=jQuery")) {
logInfo(url2);
try {
const text = await response.text();
let jsonData = handleApiResponse(text, "jQuery");
if (!jsonData) {
return;
}
if (Array.isArray(jsonData)) {
if (searchResults.length === 0 && jsonData.length > 0 && jsonData[0] && jsonData[0].hasOwnProperty("artist")) {
searchResults = jsonData;
const extractedSearchResults = [];
for (const item of searchResults) {
if (item && item.name && item.artist) {
extractedSearchResults.push({
songname: item.name,
name: item.artist.join("/")
});
}
}
if (!options.number) {
const listText = formatSongList(extractedSearchResults, options.platform || config.command5_defaultPlatform, 0, config.command5_searchList);
const screenshotPage = await ctx.puppeteer.browser.newPage();
try {
const screenshot = await generateSongListImage(ctx.puppeteer, listText);
await session.send([
import_koishi.h.image(screenshot, "image/png"),
import_koishi.h.text(promptText)
]);
} finally {
await screenshotPage.close();
}
const input = await session.prompt(config.waitTimeout * 1e3);
if (!input) {
await session.send(import_koishi.h.text(waitTimeout));
await page.close();
return;
}
if (exitCommands.includes(input)) {
await session.send(import_koishi.h.text(exitprompt));
await page.close();
return;
}
options.number = parseInt(input, 10);
}
if (isNaN(options.number) || options.number < 1 || options.number > config.command5_searchList || options.number > searchResults.length) {
await session.send(import_koishi.h.text(invalidNumber));
await page.close();
return;
}
const selectedIndex = options.number - 1;
const songElement = await page.$(`.list-item[data-no="${selectedIndex}"] .list-num`);
if (!songElement) {
await session.send("未找到歌曲元素,请检查页面结构。");
await page.close();
return;
}
await page.evaluate((element) => {
const dblclickEvent = new MouseEvent("dblclick", {
bubbles: true,
cancelable: true,
view: window
});
element.dispatchEvent(dblclickEvent);
}, songElement);
logInfo(`已双击歌曲序号: ${options.number}`);
po