├── .gitignore ├── LICENSE ├── README.md ├── impt_songlist.gif ├── interaction.gif ├── nonebot_plugin_zyk_music ├── User-Agent.json ├── __init__.py ├── config.py ├── music │ └── music.txt └── work.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.bat 3 | *.xml 4 | *.iml 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ZSSLM 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :memo: nonebot_plugin_zyk_music 2 | 3 | ## 由于接口跑路,这个项目暂时废了,我会尽快解决,现在先暂停维护 4 | 5 | **本插件是我另一个项目改的,有兴趣的话可以去看看[BlackStone_Music_GUI](https://github.com/ZYKsslm/BlackStone_Music_GUI)** 6 | 7 | *:page_facing_up: 使用本插件前请仔细阅读README* 8 | 9 | ## :sparkles: 新版本一览 10 | ### :pushpin: version 0.1.7 11 | >都更新了哪些内容? 12 | 1. 删去酷我音乐接口 13 | 2. 全面更新代码,更换接口 14 | 15 | 16 | ## :cd: 安装方式 17 | - #### 使用pip 18 | ``` 19 | pip install nonebot_plugin_zyk_music 20 | ``` 21 | - #### 使用nb-cli 22 | ``` 23 | nb plugin install nonebot_plugin_zyk_music 24 | ``` 25 | 26 | ## :wrench: env配置 27 | 28 | | Name | Example | Type | Usage | Required | 29 | |:-------------------:|:------------------:|:----:|:--------------------:|:--------:| 30 | | music_path | path/to/your/music | str | 音乐保存路径,默认保存在music目录下 | No | 31 | | music_del_file | False | bool | 是否删除下载的文件,默认为True | No | 32 | | music_retry_songnum | 50 | int | 歌单发送失败时重新发送的条数 | No | 33 | 34 | 35 | ## :bulb: 如何交互 36 | 37 | ### 点歌 38 | ![interaction](interaction.gif) 39 | 40 | ### 导入歌单 41 | ![impt_songlist](impt_songlist.gif) 42 | 43 | ## :label: 指令 44 | ### 支持的平台 45 | - [x] QQ音乐 46 | - [x] 网易云音乐 47 | - [x] 酷狗音乐 48 | - [x] 咪咕音乐 49 | 50 | 51 | ### QQ点歌 52 | ``` 53 | qq | QQ点歌 name 54 | 55 | eg: 56 | qq点歌 stay 57 | ``` 58 | 59 | ### QQVIP点歌 60 | ``` 61 | qqvip | QQVIP点歌 [母带|无损|HQ|标准音质] name 62 | 63 | eg: 64 | qq点歌 无损音质 one last kiss 65 | ``` 66 | 67 | ### 酷狗点歌 68 | ``` 69 | 酷狗 | kg点歌 name 70 | 71 | eg: 72 | kg点歌 stay 73 | ``` 74 | 75 | ### 网易云点歌 76 | ``` 77 | 网易云 | 网易 | wy点歌 name 78 | 79 | eg: 80 | wy点歌 stay 81 | ``` 82 | 83 | ### 咪咕点歌 84 | ``` 85 | 咪咕 | mg点歌 name 86 | 87 | eg: 88 | mg点歌 stay 89 | ``` 90 | 91 | ### 导入QQ音乐歌单 92 | ``` 93 | (COMMAND_START)导入歌单 歌单id | 歌单链接 94 | 95 | eg: 96 | /导入歌单 3865449936 97 | ``` 98 | 99 | 100 | --- 101 | :bug: 如果发现插件有BUG或有建议,欢迎**合理**提*Issue* 102 | 103 | :heart: 最后,如果你喜欢本插件,就请给本插件点个:star:吧 -------------------------------------------------------------------------------- /impt_songlist.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZYKsslm/nonebot_plugin_zyk_music/f3d36931cf72fa4684fc9a811d3be99a4390cb5f/impt_songlist.gif -------------------------------------------------------------------------------- /interaction.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZYKsslm/nonebot_plugin_zyk_music/f3d36931cf72fa4684fc9a811d3be99a4390cb5f/interaction.gif -------------------------------------------------------------------------------- /nonebot_plugin_zyk_music/__init__.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | from nonebot.adapters.onebot.v11 import GROUP, PRIVATE_FRIEND, Bot, Message 4 | from nonebot.log import logger 5 | from nonebot import on_regex, on_command 6 | from nonebot.exception import ActionFailed 7 | from nonebot.params import RegexDict, Arg, T_State, CommandArg 8 | 9 | from colorama import init, Fore 10 | from os import remove 11 | from .config import path, if_del, song_num 12 | from .work import * 13 | 14 | __version__ = "0.1.7" 15 | 16 | pattern = "^(?Pqq|QQ|qqvip|QQVIP|酷狗|kg|咪咕|mg|网易云|网易|wy)点歌( (?P
.*?)音质)? (?P.*)" 17 | music_matcher = on_regex(pattern, priority=5, permission=GROUP | PRIVATE_FRIEND, block=True) 18 | impt_songlist = on_command(cmd="导入歌单", priority=5, permission=GROUP | PRIVATE_FRIEND, block=True) 19 | 20 | # 字体样式初始化(自动重设字体样式) 21 | init(autoreset=True) 22 | 23 | logger.success(Fore.LIGHTGREEN_EX + f"成功导入本插件,插件版本为{__version__}") 24 | 25 | 26 | @music_matcher.handle() 27 | async def _(state: T_State, regex: dict = RegexDict()): 28 | # 保存音乐信息 29 | source = regex["source"] 30 | name = regex["name"] 31 | br = regex["br"] 32 | if br == "标准": 33 | br = 4 34 | elif br == "HQ": 35 | br = 3 36 | elif br == "无损": 37 | br = 2 38 | elif br == "母带": 39 | br = 1 40 | else: 41 | br = 3 42 | 43 | music_info, songids = await get_music(source=source, name=name) 44 | 45 | state.update( 46 | { 47 | "music_source": source, 48 | "music_name": name, 49 | "songids": songids, 50 | "br": br 51 | } 52 | ) 53 | 54 | await music_matcher.send(music_info, at_sender=True) 55 | 56 | 57 | @music_matcher.got(key="n") 58 | async def _(bot: Bot, event: Event, state: T_State, n: Message = Arg("n")): 59 | n = str(n) 60 | try: 61 | n = int(n) 62 | except ValueError: 63 | await music_matcher.finish("已取消点歌", at_sender=True) 64 | 65 | # 获取音乐信息 66 | songid = state["songids"] 67 | if songid is not None: 68 | songid = songid[n-1] 69 | name = state["music_name"] 70 | source = state["music_source"] 71 | br = state["br"] 72 | 73 | # 获取会话信息 74 | id_ = get_id(event) 75 | 76 | await music_matcher.send("正在努力下载,请稍后......", at_sender=True) 77 | logger.info(Fore.LIGHTCYAN_EX + f"开始下载'{name}',音源:{source},序号:{n},音质:{br}") 78 | info = await get_music(source=source, name=name, songid=songid, br=br, path=path, n=n) 79 | 80 | if info is False: 81 | logger.error(Fore.LIGHTRED_EX + f"'{name}'获取失败") 82 | await music_matcher.finish(f"'{name}'获取失败", at_sender=True) 83 | else: 84 | logger.success(Fore.LIGHTGREEN_EX + f"'{name}'下载成功") 85 | file = info[1] 86 | name = info[2] 87 | 88 | # 上传音乐 89 | try: 90 | if id_[0] == "group": 91 | await bot.upload_group_file(group_id=id_[1], file=file, name=name) 92 | else: 93 | await bot.upload_private_file(user_id=id_[1], file=file, name=name) 94 | except ActionFailed: 95 | await music_matcher.finish("Bot可能被风控或群聊不支持上传文件,请稍后再试") 96 | finally: 97 | if if_del is True: 98 | try: 99 | remove(file) 100 | except Exception as error: 101 | logger.warning(Fore.LIGHTYELLOW_EX + f"文件'{file}'删除失败:{error},可手动删除") 102 | 103 | 104 | @impt_songlist.handle() 105 | async def _(state: T_State, songlist_info: Message = CommandArg()): 106 | songlist_info = str(songlist_info) 107 | data = await import_songlist(songlist_info) 108 | 109 | if data is False: 110 | await impt_songlist.finish("解析失败,请尝试使用歌单id", at_sender=True) 111 | info, song_list, songids = data 112 | 113 | song_info = "\n".join([f"{i + 1}{song['name']}-{song['singer'][0]['name']}" for i, song in enumerate(song_list)]) 114 | 115 | try: 116 | await impt_songlist.send(f"导入成功\n{info}\n{song_info}", at_sender=True) 117 | except ActionFailed: 118 | await impt_songlist.send(f"发送失败,Bot可能被风控或歌单太长,尝试发送前{song_num}条") 119 | song_info = "\n".join([f"{i + 1}.{song_list[i]['name']}-{song_list[i]['singer'][0]['name']}" for i in range(song_num)]) 120 | 121 | try: 122 | await impt_songlist.send(f"导入成功\n{info}\n{song_info}", at_sender=True) 123 | except ActionFailed: 124 | await impt_songlist.finish("发送失败,Bot可能被风控,请稍后再试") 125 | 126 | state["songids"] = songids 127 | 128 | 129 | @impt_songlist.got(key="s") 130 | async def _(bot: Bot, state: T_State, event: Event, s: Message = Arg("s")): 131 | s = str(s) 132 | try: 133 | s = int(s) 134 | except ValueError: 135 | await music_matcher.finish() 136 | 137 | songids = state["songids"] 138 | # 获取会话信息 139 | id_ = get_id(event) 140 | 141 | await music_matcher.send("正在努力下载,请稍后......", at_sender=True) 142 | info = await vip_qq_download(br=3, path=path, songid=songids[s-1]) 143 | 144 | if info is False: 145 | await music_matcher.finish(f"获取失败", at_sender=True) 146 | else: 147 | file = info[1] 148 | name = info[2] 149 | # 上传音乐 150 | try: 151 | if id_[0] == "group": 152 | await bot.upload_group_file(group_id=id_[1], file=file, name=name) 153 | else: 154 | await bot.upload_private_file(user_id=id_[1], file=file, name=name) 155 | except ActionFailed: 156 | await music_matcher.finish("Bot可能被风控或群聊不支持上传文件,请稍后再试") 157 | finally: 158 | if if_del is True: 159 | try: 160 | remove(file) 161 | except Exception as error: 162 | logger.warning(Fore.LIGHTYELLOW_EX + f"文件'{file}'删除失败:{error},可手动删除") 163 | -------------------------------------------------------------------------------- /nonebot_plugin_zyk_music/config.py: -------------------------------------------------------------------------------- 1 | from nonebot import get_driver 2 | from os.path import join, abspath, dirname 3 | 4 | 5 | # 获取全局配置 6 | 7 | # 获取文件路径 8 | try: 9 | path = get_driver().config.music_path 10 | except AttributeError: 11 | path = join(abspath(dirname(__file__)), "music") 12 | 13 | 14 | # 是否删除文件 15 | try: 16 | if_del = get_driver().config.music_del_file 17 | except AttributeError: 18 | if_del = True 19 | else: 20 | if if_del == "True": 21 | if_del = True 22 | else: 23 | if_del = False 24 | 25 | # 歌单发送失败时发送的条数 26 | try: 27 | song_num = get_driver().config.music_retry_songnum 28 | except AttributeError: 29 | song_num = 50 30 | else: 31 | try: 32 | song_num = int(song_num) 33 | except ValueError: 34 | song_num = 50 35 | -------------------------------------------------------------------------------- /nonebot_plugin_zyk_music/music/music.txt: -------------------------------------------------------------------------------- 1 | 下载的音乐默认保存到这个文件夹 -------------------------------------------------------------------------------- /nonebot_plugin_zyk_music/work.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from httpx import AsyncClient 3 | from os import rename 4 | from os.path import join, abspath, dirname 5 | from random import choice 6 | from re import sub, compile, findall, S 7 | from filetype import guess 8 | from nonebot.adapters.onebot.v11 import Event 9 | 10 | try: 11 | from ujson import load, loads 12 | except ModuleNotFoundError: 13 | from json import load, loads 14 | 15 | kg_api = "http://ovooa.caonm.net/API/kgdg/api.php" 16 | mg_api = "http://ovooa.caonm.net/API/migu/api.php" 17 | qq_vip_api = "http://ovooa.caonm.net/API/QQ_Music" 18 | qq_songlist_api = "https://c.y.qq.com/v8/fcg-bin/fcg_v8_playlist_cp.fcg?cv=10000&ct=19&newsong=1&tpl=wk&id={}&g_tk=5381&platform=mac&g_tk_new_20200303=5381&loginUin=0&hostUin=0&format=json&inCharset=GB2312&outCharset=utf-8¬ice=0&platform=jqspaframe.json&needNewCode=0" 19 | qq_api = "http://ovooa.caonm.net/API/qqdg/api.php" 20 | wy_api = "http://ovooa.caonm.net/API/wydg/api.php" 21 | 22 | ua_path = join(abspath(dirname(__file__)), "User-Agent.json") 23 | with open(ua_path, "r") as u: 24 | user_agent_json = load(u) 25 | 26 | 27 | def get_id(event: Event): 28 | info = str(event.get_session_id()) 29 | try: 30 | res = findall(r"group_(?P\d+)_(?P\d+)", info)[0] 31 | except IndexError: 32 | id_ = "private", info 33 | else: 34 | id_ = "group", res[0] 35 | 36 | return id_ 37 | 38 | 39 | def set_name(string): 40 | """剔除字符 /:*?"<>| Windows操作系统下文件或文件夹名字中不允许出现以上字符""" 41 | pattern = compile(r'[/:*?"<>|]') 42 | new_string = sub(pattern=pattern, repl="", string=string) 43 | 44 | return new_string 45 | 46 | 47 | def get_user_agent(): 48 | """获取随机UA""" 49 | user_agent = choice(choice(list(choice(user_agent_json).values()))) 50 | return user_agent 51 | 52 | 53 | # 解析歌单 54 | async def import_songlist(info): 55 | try: 56 | songlist_id = int(info) 57 | except ValueError: 58 | try: 59 | songlist_id = findall(r'/(\d+)', info)[0] 60 | except IndexError: 61 | return False 62 | 63 | headers = { 64 | "User-Agent": get_user_agent() 65 | } 66 | url = qq_songlist_api.format(songlist_id) 67 | 68 | async with AsyncClient(headers=headers, follow_redirects=True, timeout=None) as client: 69 | resp = await client.get(url=url) 70 | data = resp.json()["data"]["cdlist"][0] 71 | 72 | song_list = data["songlist"] 73 | tags = "" 74 | for i in data["tags"]: 75 | tags += f":{i['name']}" 76 | 77 | songlist_info = f"歌单名:{data['dissname']}\n" \ 78 | f"创建者:{data['nickname']}\n" \ 79 | f"简介:{data['desc']}\n" \ 80 | f"总歌数:{data['total_song_num']}\n" \ 81 | f"浏览人数:{data['visitnum']}\n" \ 82 | f"标签:{tags}" 83 | 84 | return songlist_info, song_list, data["songids"].split(",") 85 | 86 | 87 | # 酷狗音乐 88 | async def kg_get_music(name): 89 | headers = { 90 | "User-Agent": get_user_agent() 91 | } 92 | 93 | data = { 94 | "msg": name, 95 | "sc": 50 96 | } 97 | 98 | async with AsyncClient(headers=headers, params=data, follow_redirects=True, timeout=None) as client: 99 | resp = await client.get(url=kg_api) 100 | music_info = loads(findall(r'"data": (\[.*\])', resp.text, S)[0]) 101 | 102 | choice_list = "" 103 | for i, music in enumerate(music_info): 104 | music_name = music["name"] 105 | singers = music["singer"] 106 | 107 | choose = f"{i+1}.{music_name}-{singers}\n" 108 | choice_list += choose 109 | 110 | return choice_list, None 111 | 112 | 113 | async def kg_download(name, n, path): 114 | headers = { 115 | "User-Agent": get_user_agent() 116 | } 117 | 118 | data = { 119 | "msg": name, 120 | "n": n, 121 | } 122 | 123 | async with AsyncClient(headers=headers, params=data, follow_redirects=True, timeout=None) as client: 124 | resp = await client.get(url=kg_api) 125 | try: 126 | music_info = resp.json() 127 | except: 128 | return False 129 | else: 130 | music_info = music_info["data"] 131 | 132 | song = set_name(music_info["song"]) 133 | singer = set_name(music_info["singer"]) 134 | try: 135 | music = music_info["url"] 136 | except KeyError: 137 | return False 138 | 139 | async with AsyncClient(headers=headers, follow_redirects=True, timeout=None) as client: 140 | resp = await client.get(url=music) 141 | content = resp.content 142 | 143 | kind = guess(content) 144 | if kind is None: 145 | return False 146 | 147 | file_name = join(path, f"{song}-{singer}.{kind.extension}") 148 | with open(file_name, "wb") as f: 149 | f.write(content) 150 | 151 | return True, file_name, f"{song}-{singer}.{kind.extension}" 152 | 153 | 154 | # 咪咕音乐 155 | async def mg_get_music(name): 156 | headers = { 157 | "User-Agent": get_user_agent() 158 | } 159 | 160 | data = { 161 | "msg": name, 162 | "sc": 50 163 | } 164 | 165 | async with AsyncClient(headers=headers, params=data, follow_redirects=True, timeout=None) as client: 166 | resp = await client.get(url=mg_api) 167 | music_info = loads(findall(r'"data": (\[.*\]),', resp.text, S)[0]) 168 | 169 | choice_list = "" 170 | for i, music in enumerate(music_info): 171 | music_name = music["song"] 172 | singers = music["singer"] 173 | 174 | choose = f"{i+1}.{music_name}-{singers}\n" 175 | choice_list += choose 176 | 177 | return choice_list, None 178 | 179 | 180 | async def mg_download(name, n, path): 181 | headers = { 182 | "User-Agent": get_user_agent() 183 | } 184 | 185 | data = { 186 | "msg": name, 187 | "n": n, 188 | } 189 | 190 | async with AsyncClient(headers=headers, params=data, follow_redirects=True, timeout=None) as client: 191 | resp = await client.get(url=mg_api) 192 | music_info = resp.json()["data"] 193 | 194 | song = set_name(music_info["musicname"]) 195 | singer = set_name(music_info["singer"]) 196 | try: 197 | music = music_info["musicurl"] 198 | except KeyError: 199 | return False 200 | 201 | async with AsyncClient(headers=headers, follow_redirects=True, timeout=None) as client: 202 | resp = await client.get(url=music) 203 | content = resp.content 204 | 205 | kind = guess(content) 206 | if kind is None: 207 | return False 208 | 209 | file_name = join(path, f"{song}-{singer}.{kind.extension}") 210 | with open(file_name, "wb") as f: 211 | f.write(content) 212 | 213 | return True, file_name, f"{song}-{singer}.{kind.extension}" 214 | 215 | 216 | # QQ音乐VIP 217 | async def vip_qq_get_music(name): 218 | headers = { 219 | "User-Agent": get_user_agent() 220 | } 221 | 222 | data = { 223 | "msg": name, 224 | "limit": 50 225 | } 226 | 227 | async with AsyncClient(headers=headers, params=data, follow_redirects=True, timeout=None) as client: 228 | resp = await client.get(url=qq_vip_api) 229 | music_info = resp.json()["data"] 230 | 231 | choice_list = "" 232 | songid_list = [] 233 | for i, music in enumerate(music_info): 234 | music_name = music["song"] 235 | singers = music["singer"] 236 | songid = music["songid"] 237 | songid_list.append(songid) 238 | choice_list += f"{i+1}.{music_name}-{singers}\n" 239 | 240 | return choice_list, songid_list 241 | 242 | 243 | async def vip_qq_download(br, path, songid): 244 | headers = { 245 | "User-Agent": get_user_agent() 246 | } 247 | 248 | data = { 249 | "songid": songid, 250 | "br": br 251 | } 252 | 253 | async with AsyncClient(headers=headers, params=data, follow_redirects=True, timeout=None) as client: 254 | resp = await client.get(url=qq_vip_api) 255 | try: 256 | info = resp.json() 257 | except: 258 | return False 259 | try: 260 | music_info = info["data"] 261 | except KeyError: 262 | return False 263 | 264 | song = set_name(music_info["song"]) 265 | singer = set_name(music_info["singer"]) 266 | music = music_info["music"] 267 | 268 | if br > 2: 269 | async with AsyncClient(headers=headers, follow_redirects=True, timeout=None) as client: 270 | resp = await client.get(url=music) 271 | content = resp.content 272 | 273 | kind = guess(content) 274 | if kind is None: 275 | return False 276 | 277 | file_name = join(path, f"{song}-{singer}.{kind.extension}") 278 | with open(file_name, "wb") as f: 279 | f.write(content) 280 | 281 | else: 282 | client = AsyncClient() 283 | async with client.stream(method="GET", url=music, headers=headers, follow_redirects=True, timeout=None) as r: 284 | with open(fr"{path}/{song}-{singer}", "wb+") as f: 285 | async for data in r.aiter_bytes(chunk_size=64*1024): 286 | f.write(data) 287 | kind = guess(f) 288 | 289 | file_name = f"{path}/{song}-{singer}.{kind.extension}" 290 | rename(f"{path}/{song}-{singer}", file_name) 291 | 292 | return True, file_name, f"{song}-{singer}.{kind.extension}" 293 | 294 | 295 | # QQ音乐 296 | async def qq_get_music(name): 297 | headers = { 298 | "User-Agent": get_user_agent() 299 | } 300 | 301 | data = { 302 | "msg": name, 303 | "sc": 50 304 | } 305 | 306 | async with AsyncClient(headers=headers, params=data, follow_redirects=True, timeout=None) as client: 307 | resp = await client.get(url=qq_api) 308 | music_info = resp.json()["data"] 309 | 310 | choice_list = "" 311 | for i, music in enumerate(music_info): 312 | music_name = music["song"] 313 | singers = music["singers"] 314 | 315 | choose = f"{i+1}.{music_name}-{singers}\n" 316 | choice_list += choose 317 | 318 | return choice_list, None 319 | 320 | 321 | async def qq_download(name, n, path): 322 | headers = { 323 | "User-Agent": get_user_agent() 324 | } 325 | 326 | data = { 327 | "msg": name, 328 | "n": n 329 | } 330 | async with AsyncClient(headers=headers, params=data, follow_redirects=True, timeout=None) as client: 331 | resp = await client.get(url=qq_api) 332 | info = resp.json() 333 | try: 334 | music_info = info["data"] 335 | except KeyError: 336 | return False 337 | 338 | song = set_name(music_info["Music"]) 339 | singer = set_name(music_info["Singer"]) 340 | music = music_info["Url"] 341 | 342 | async with AsyncClient(headers=headers, follow_redirects=True, timeout=None) as client: 343 | resp = await client.get(url=music) 344 | content = resp.content 345 | 346 | file_name = join(path, f"{song}-{singer}.mp3") 347 | with open(file_name, "wb") as f: 348 | f.write(content) 349 | 350 | return True, file_name, f"{song}-{singer}.mp3" 351 | 352 | 353 | # 网易云音乐 354 | async def wy_get_music(name): 355 | headers = { 356 | "User-Agent": get_user_agent() 357 | } 358 | 359 | data = { 360 | "msg": name, 361 | "sc": 50 362 | } 363 | 364 | async with AsyncClient(headers=headers, params=data, follow_redirects=True, timeout=None) as client: 365 | resp = await client.get(url=wy_api) 366 | music_info = resp.json()["data"] 367 | 368 | choice_list = "" 369 | for i, music in enumerate(music_info): 370 | music_name = music["song"] 371 | singer_list = music["singers"] 372 | singer_num = len(singer_list) 373 | if singer_num > 1: 374 | singers = "、".join([f"{singer[s - 1]}" for s, singer in enumerate(singer_list)]) 375 | else: 376 | singers = singer_list[0] 377 | choose = f"{i+1}.{music_name}-{singers}\n" 378 | choice_list += choose 379 | 380 | return choice_list, None 381 | 382 | 383 | async def wy_download(name, n, path): 384 | headers = { 385 | "User-Agent": get_user_agent() 386 | } 387 | 388 | data = { 389 | "msg": name, 390 | "n": n, 391 | } 392 | 393 | async with AsyncClient(headers=headers, params=data, follow_redirects=True, timeout=None) as client: 394 | resp = await client.get(url=wy_api) 395 | music_info = resp.json()["data"] 396 | 397 | song = set_name(music_info["Music"]) 398 | singer = set_name(music_info["Singer"]) 399 | try: 400 | music = music_info["Url"] 401 | except KeyError: 402 | return False 403 | 404 | async with AsyncClient(headers=headers, follow_redirects=True, timeout=None) as client: 405 | resp = await client.get(url=music) 406 | content = resp.content 407 | 408 | kind = guess(content) 409 | if kind is None: 410 | return False 411 | 412 | file_name = join(path, f"{song}-{singer}.{kind.extension}") 413 | with open(file_name, "wb") as f: 414 | f.write(content) 415 | 416 | return True, file_name, f"{song}-{singer}.{kind.extension}" 417 | 418 | 419 | # 定义一个字典,存储不同的音乐源和对应的函数 420 | music_sources = { 421 | "qq": (qq_get_music, qq_download), 422 | "QQ": (qq_get_music, qq_download), 423 | "qqvip": (vip_qq_get_music, vip_qq_download), 424 | "QQVIP": (vip_qq_get_music, vip_qq_download), 425 | "酷狗": (kg_get_music, kg_download), 426 | "kg": (kg_get_music, kg_download), 427 | "网易云": (wy_get_music, wy_download), 428 | "网易": (wy_get_music, wy_download), 429 | "wy": (wy_get_music, wy_download), 430 | "咪咕": (mg_get_music, mg_download), 431 | "mg": (mg_get_music, mg_download) 432 | } 433 | 434 | # 定义一个列表或者集合,存储需要音质参数的音乐源 435 | vip_sources = {"qqvip", "QQVIP"} 436 | 437 | 438 | async def get_music(source, name=None, songid=None, br=None, path=None, n=None): 439 | # 从字典中获取对应的函数 440 | get_data, download_data = music_sources[source] 441 | 442 | if (songid is None) and (n is None): 443 | # 调用下载音乐的函数 444 | return await get_data(name=name) 445 | else: 446 | # 判断是否需要传入音质参数 447 | if source in vip_sources: 448 | return await download_data(br, path, songid) 449 | else: 450 | return await download_data(name, n, path) -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | 4 | setup( 5 | name="nonebot_plugin_zyk_music", 6 | version="0.1.7.2", 7 | packages=find_packages(), 8 | author="ZSSLM", 9 | author_email="3119964735@qq.com", 10 | long_description="This is a simple plugin for nonebot2 to pick up music", 11 | url="https://github.com/ZYKsslm/nonebot_plugin_zyk_music", 12 | license="MIT License", 13 | install_requires=["httpx", "colorama", "filetype", "nonebot2", "nonebot_adapter_onebot"], 14 | package_data={"nonebot_plugin_zyk_music": ["User-Agent.json", "music/*"]} 15 | ) 16 | --------------------------------------------------------------------------------