🎊实现midi转换功能

This commit is contained in:
EillesWan 2024-07-21 02:27:04 +08:00
parent b4576d80ca
commit e2aa85141d
6 changed files with 35609 additions and 48 deletions

2
.gitignore vendored
View File

@ -4,7 +4,7 @@
.pycharm/ .pycharm/
.cache/ .cache/
node_modules/ node_modules/
data/ /data/
db/ db/
/resources/ /resources/
__pycache__/ __pycache__/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
## 伶伦转换器 - 机器人版使用文档
命令为标题,后面是功能和子命令。
支持传入 `.midi`、`.mid`、`.json` 文件载入服务器缓存。
<!-- 若子命令为参数项,后面需要参数。
若子命令为开关项,后面无需参数。 -->
**以下所有命令中,若字符串类型的参数需要且可以填入多个内容,则可用 `all` 代替参数中的全部数据;此情况下,也可以用和符`&`分割多个你想要填写的信息;同样的,若参数中需要包含空格,则须以英文双引号`"`扩起。**
### llmscvt | linglun_convert | 音乐转换 | midi转换 | 转换音乐 | linglun_music_convert
转换midi音乐到指定的格式支持批量格式批量文件。每次转换默认基础增加 0.5 点数,每多一种转换格式多增加 0.5 点数,`MSQ`格式不计入后续点数消耗。每日点数在凌晨四时整归零点数达到20则不可进行转换。
- `-f | --file <字符串>` : 缓存中的midi文件名称需提前上传mid文件默认为`all`
- `-emr | --enable-mismatch-error` : 对音符的不匹配报错;默认为关
- `-ps | --play-speed <小数>` : 播放速度;默认为`1.0`
- `-dftp | --default-tempo <整数>` : 默认的tempo默认为`500000`
- `-ptc | --pitched-note-table <字符串>` : **不可多填** : 乐音乐器对照表需要提前上传json文件此处输入缓存中的json文件名称或者默认存有的三组对照表名称`touch`、`classic`、`dislink`;默认为`touch`
- `-pcs | --percussion-note-table <字符串>` : **不可多填** : 打击乐器对照表需要提前上传json文件此处输入缓存中的json文件名称或者默认存有的三组对照表名称`touch`、`classic`、`dislink`;默认为`touch`
- `-e | --old-execute-format` : 是否使用旧版execute指令格式默认为关
- `-mv | --minimal-volume <小数>` : 最小播放音量;默认为`0.1`
- `-vpf | --volume-processing-function <字符串>` : 音量处理函数,支持两种音量函数:`natural`、`straight`;默认为`natural`
- `-t | --type <字符串>` : 转换结果类型,支持的类型有:`addon-delay`、`addon-score`、 `mcstructure-dalay`、`mcstructure-score`、`bdx-delay`、`bdx-score`、`msq`;默认为`all`
- `-htp | --high-time-precision` : **仅当结果类型包含 `msq` 时生效** : 是否使用高精度时间存储MSQ文件默认为关
- `-pgb | --progress-bar <字符串> <字符串> <字符串>` : **仅当结果包含 `addon-*`、`bdx-*` 之一时生效、不可多填** : 进度条样式,参照[进度条自定义](https://gitee.com/TriM-Organization/Musicreater/blob/master/docs/%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md#%E8%BF%9B%E5%BA%A6%E6%9D%A1%E8%87%AA%E5%AE%9A%E4%B9%89),以空格拆分三个字符串;默认请查阅上述文档
- `-s | --scoreboard-name <字符串>` : **仅当结果类型包含 `*-score` 之一时生效、不可多填** : 播放使用的计分板名称;默认为`mscplay`
- `-dsa | --disable-scoreboard-autoreset` : **仅当结果类型包含 `*-score` 之一时生效** : 是否禁用计分板自动重置;默认为关
- `-p | --player-selector <字符串>` : **仅当结果类型包含 `*-delay` 之一时生效、不可多填** : 播放使用的玩家选择器;默认为`@a`
- `-h | --height-limit <整数>` : **仅当结果类型包含 `*-delay`、`bdx-*` 之一时生效** : 生成结构的最大高度限制;默认为`32`
- `-a | --author <字符串>` : **仅当结果类型包含 `bdx-*` 之一时生效、不可多填** : 音乐文件的作者署名;默认为`Eilles`
- `-fa | --forward-axis <字符串>` : **仅当结果类型包含 `*-repeater` 之一时生效、不可多填** : 生成结构的朝向;默认为`z+`**未来功能**
### 查看缓存 | listCache | 查看文件缓存 | 查看缓存文件
查看自己上传到服务器的文件
### 清除缓存 | clearCache | 清除文件缓存 | 清除缓存文件 | 清空缓存
删除自己所有上传到服务器的文件
### 转换帮助 | 查看转换帮助 | 查看帮助 | cvt_help | convert_help | cvthlp
查看此帮助文档

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@ -1,8 +1,8 @@
import os import os
import sys import sys
import time import time
import json import json
import uuid
import shutil import shutil
import requests import requests
from io import StringIO from io import StringIO
@ -14,6 +14,7 @@ from typing import Annotated, Any
import zhDateTime import zhDateTime
import Musicreater import Musicreater
import Musicreater.plugin.archive
from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score
from Musicreater.plugin.addonpack import ( from Musicreater.plugin.addonpack import (
to_addon_pack_in_delay, to_addon_pack_in_delay,
@ -37,13 +38,18 @@ import nonebot.rule
from nonebot.params import CommandArg from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.adapters.onebot.v11.event import GroupUploadNoticeEvent, GroupMessageEvent from nonebot.adapters.onebot.v11.event import (
GroupUploadNoticeEvent,
GroupMessageEvent,
PrivateMessageEvent,
)
from src.utils.base.ly_typing import T_Bot, T_MessageEvent from src.utils.base.ly_typing import T_Bot, T_MessageEvent
from src.utils import event as event_utils from src.utils import event as event_utils
from src.utils.base.language import get_user_lang from src.utils.base.language import get_user_lang
from src.utils.base.config import get_config from src.utils.base.config import get_config
from src.utils.message.message import MarkdownMessage
from .execute_auto_translator import auto_translate # type: ignore from .execute_auto_translator import auto_translate # type: ignore
@ -143,7 +149,7 @@ people_convert_times = {}
# 每天1点更新 # 每天1点更新
@scheduler.scheduled_job("cron", hour=0) @scheduler.scheduled_job("cron", hour=4)
async def every_day_update(): async def every_day_update():
# ulang = Language(get_default_lang_code(), "zh-WY") # ulang = Language(get_default_lang_code(), "zh-WY")
global people_convert_times global people_convert_times
@ -318,6 +324,35 @@ async def _(
) )
on_convert_help = on_alconna(
command=Alconna("查看帮助"),
aliases={
"转换帮助",
"查看转换帮助",
"cvt_help",
"convert_help",
"cvthlp",
},
rule=nonebot.rule.to_me(),
)
@on_convert_help.handle()
async def _(
event: GroupMessageEvent,
bot: T_Bot,
):
await on_clear_cache.finish(
UniMessage.image(path=(Path(__file__).parent / "convert_helper.png")),
at_sender=True,
)
# await MarkdownMessage.send_md(
# (Path(__file__).parent / "convert_helper.md").read_text(encoding="utf-8"),
# bot,
# event=event,
# )
on_clear_cache = on_alconna( on_clear_cache = on_alconna(
command=Alconna("清除缓存"), command=Alconna("清除缓存"),
aliases={ aliases={
@ -420,10 +455,16 @@ async def _(
# vol_processing_func: Musicreater.FittingFunctionType = Musicreater.natural_curve, # vol_processing_func: Musicreater.FittingFunctionType = Musicreater.natural_curve,
# ) # )
musicreater_convert = on_alconna( linglun_convert = on_alconna(
aliases={"musicreater_convert", "音乐转换", "midi转换"}, aliases={
"linglun_convert",
"音乐转换",
"midi转换",
"转换音乐",
"linglun_music_convert",
},
command=Alconna( command=Alconna(
"msctcvt", "llmscvt",
Option("-f|--file", default="all", args=Args["file", str, "all"]), # ALL Option("-f|--file", default="all", args=Args["file", str, "all"]), # ALL
Option("-emr|--enable-mismatch-error", default=False, action=store_true), Option("-emr|--enable-mismatch-error", default=False, action=store_true),
Option("-ps|--play-speed", default=1.0, args=Args["play-speed", float, 1.0]), Option("-ps|--play-speed", default=1.0, args=Args["play-speed", float, 1.0]),
@ -454,6 +495,7 @@ musicreater_convert = on_alconna(
args=Args["volume-processing-function", str, "natural"], args=Args["volume-processing-function", str, "natural"],
), ),
Option("-t|-type", default="all", args=Args["type", str, "all"]), Option("-t|-type", default="all", args=Args["type", str, "all"]),
Option("-htp|--high-time-precision", default=False, action=store_true),
Option( Option(
"-pgb|--progress-bar", "-pgb|--progress-bar",
default={ default={
@ -478,18 +520,42 @@ musicreater_convert = on_alconna(
), ),
Option("-h|--height-limit", default=32, args=Args["height-limit", int, 32]), Option("-h|--height-limit", default=32, args=Args["height-limit", int, 32]),
Option("-a|--author", default="Eilles", args=Args["author", str, "Eilles"]), Option("-a|--author", default="Eilles", args=Args["author", str, "Eilles"]),
Option("-fa|--forward-axis", default="x+", args=Args["forward-axis", str, "x+"]), Option(
"-fa|--forward-axis", default="z+", args=Args["forward-axis", str, "z+"]
),
), ),
permission=SUPERUSER, permission=SUPERUSER,
) )
@musicreater_convert.handle() @linglun_convert.handle()
async def _( async def _(
result: Arparma, result: Arparma,
event: GroupMessageEvent, event: GroupMessageEvent | PrivateMessageEvent,
bot: T_Bot, bot: T_Bot,
): ):
usr_id = str(event.user_id)
if usr_id not in people_convert_times.keys():
people_convert_times[usr_id] = 0
else:
if people_convert_times[usr_id] > configdict["maxPersonConvert"]["music"]:
await linglun_convert.finish(
UniMessage.text(
"你今天音乐转换点数超限: {}/{}".format(
people_convert_times[usr_id],
configdict["maxPersonConvert"]["music"],
)
),
at_sender=True,
)
if usr_id not in filesaves.keys():
await linglun_convert.finish(
UniMessage.text("服务器内未存入你的任何文件请先使用上传midi文件吧")
)
_args: dict = { _args: dict = {
"file": "all", "file": "all",
"enable-mismatch-error": False, "enable-mismatch-error": False,
@ -501,6 +567,7 @@ async def _(
"minimal-volume": 0.1, "minimal-volume": 0.1,
"volume-processing-function": "natural", "volume-processing-function": "natural",
"type": "all", "type": "all",
"high-time-precision": False,
"progress-bar": { "progress-bar": {
"base_s": r"%%N [ %%s/%^s %%% §e__________§r %%t|%^t ]", "base_s": r"%%N [ %%s/%^s %%% §e__________§r %%t|%^t ]",
"to_play_s": r"§7=", "to_play_s": r"§7=",
@ -511,6 +578,7 @@ async def _(
"player-selector": "@a", "player-selector": "@a",
"height-limit": 32, "height-limit": 32,
"author": "Eilles", "author": "Eilles",
"forward-axis": "z+",
} }
for arg in _args.keys(): for arg in _args.keys():
_args[arg] = ( _args[arg] = (
@ -526,42 +594,87 @@ async def _(
# UniMessage.text(json.dumps(_args, indent=4, sort_keys=True, ensure_ascii=False)) # UniMessage.text(json.dumps(_args, indent=4, sort_keys=True, ensure_ascii=False))
# ) # )
usr_id = str(event.user_id)
usr_data_path = database_dir / usr_id usr_data_path = database_dir / usr_id
(usr_temp_path := temporary_dir / usr_id).mkdir(exist_ok=True) (usr_temp_path := temporary_dir / usr_id).mkdir(exist_ok=True)
if (_ppnt:=_args["pitched-note-table"].lower() )in ["touch","classic","dislink"]: if (_ppnt := _args["pitched-note-table"].lower()) in [
pitched_notechart = Musicreater.MM_DISLINK_PITCHED_INSTRUMENT_TABLE if _ppnt == "dislink" else (Musicreater.MM_CLASSIC_PITCHED_INSTRUMENT_TABLE if _ppnt == "classic" else Musicreater.MM_TOUCH_PITCHED_INSTRUMENT_TABLE) "touch",
elif (_ppnt:=(usr_data_path / _args["pitched-note-table"])).exists(): "classic",
"dislink",
]:
pitched_notechart = (
Musicreater.MM_DISLINK_PITCHED_INSTRUMENT_TABLE
if _ppnt == "dislink"
else (
Musicreater.MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
if _ppnt == "classic"
else Musicreater.MM_TOUCH_PITCHED_INSTRUMENT_TABLE
)
)
elif (_ppnt := (usr_data_path / _args["pitched-note-table"])).exists():
pitched_notechart = Musicreater.MM_TOUCH_PITCHED_INSTRUMENT_TABLE.copy() pitched_notechart = Musicreater.MM_TOUCH_PITCHED_INSTRUMENT_TABLE.copy()
pitched_notechart.update(json.load(_ppnt.open("r"))) pitched_notechart.update(json.load(_ppnt.open("r")))
else: else:
await musicreater_convert.finish(UniMessage.text("乐器对照表 {} 不存在".format(_args["pitched-note-table"]))) await linglun_convert.finish(
UniMessage.text("乐器对照表 {} 不存在".format(_args["pitched-note-table"]))
)
return return
if (_ppnt := _args["percussion-note-table"].lower()) in [
if (_ppnt:=_args["percussion-note-table"].lower() )in ["touch","classic","dislink"]: "touch",
percussion_notechart = Musicreater.MM_DISLINK_PERCUSSION_INSTRUMENT_TABLE if _ppnt == "dislink" else (Musicreater.MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE if _ppnt == "classic" else Musicreater.MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE) "classic",
elif (_ppnt:=(usr_data_path / _args["percussion-note-table"])).exists(): "dislink",
]:
percussion_notechart = (
Musicreater.MM_DISLINK_PERCUSSION_INSTRUMENT_TABLE
if _ppnt == "dislink"
else (
Musicreater.MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
if _ppnt == "classic"
else Musicreater.MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE
)
)
elif (_ppnt := (usr_data_path / _args["percussion-note-table"])).exists():
percussion_notechart = Musicreater.MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE.copy() percussion_notechart = Musicreater.MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE.copy()
percussion_notechart.update(json.load(_ppnt.open("r"))) percussion_notechart.update(json.load(_ppnt.open("r")))
else: else:
await musicreater_convert.finish(UniMessage.text("乐器对照表 {} 不存在".format(_args["percussion-note-table"]))) await linglun_convert.finish(
UniMessage.text(
"乐器对照表 {} 不存在".format(_args["percussion-note-table"])
)
)
return return
if (_ppnt:=_args["volume-processing-function"].lower()) in ["natural","straight"]: if (_ppnt := _args["volume-processing-function"].lower()) in [
volume_curve = Musicreater.straight_line if _ppnt == "straight" else Musicreater.natural_curve "natural",
"straight",
]:
volume_curve = (
Musicreater.straight_line
if _ppnt == "straight"
else Musicreater.natural_curve
)
else: else:
await musicreater_convert.finish(UniMessage.text("音量处理曲线 {} 不存在".format(_args["volume-processing-function"]))) await linglun_convert.finish(
UniMessage.text(
"音量处理曲线 {} 不存在".format(_args["volume-processing-function"])
)
)
return return
if (_ppnt:=_args["type"].lower()) == "all": if (_ppnt := _args["type"].lower()) == "all":
all_cvt_types = ["addon-delay","addon-score","mcstructure-dalay","mcstructure-score","bdx-delay","bdx-score",] all_cvt_types = [
"addon-delay",
"addon-score",
"mcstructure-dalay",
"mcstructure-score",
"bdx-delay",
"bdx-score",
"msq",
]
else: else:
all_cvt_types = _ppnt.split("&") all_cvt_types = _ppnt.split("&")
# 重定向标准输出 # 重定向标准输出
buffer = StringIO() buffer = StringIO()
sys.stdout = buffer sys.stdout = buffer
@ -571,7 +684,7 @@ async def _(
progress_bar_style = Musicreater.ProgressBarStyle(**_args["progress-bar"]) progress_bar_style = Musicreater.ProgressBarStyle(**_args["progress-bar"])
all_files = [] all_files: dict[str, dict[str, dict[str, int | tuple | str | list]]] = {}
for file_to_convert in ( for file_to_convert in (
filesaves[usr_id].keys() filesaves[usr_id].keys()
@ -579,8 +692,8 @@ async def _(
else _args["file"].split("&") else _args["file"].split("&")
): ):
if file_to_convert.endswith(".mid") or file_to_convert.endswith(".midi"): if file_to_convert.endswith(".mid") or file_to_convert.endswith(".midi"):
nonebot.logger.info("载入转换文件:", file_to_convert) nonebot.logger.info("载入转换文件:{}".format(file_to_convert))
all_files.append(file_to_convert) all_files[file_to_convert] = {}
msct_obj = Musicreater.MidiConvert.from_midi_file( msct_obj = Musicreater.MidiConvert.from_midi_file(
midi_file_path=usr_data_path / file_to_convert, midi_file_path=usr_data_path / file_to_convert,
mismatch_error_ignorance=not _args["enable-mismatch-error"], mismatch_error_ignorance=not _args["enable-mismatch-error"],
@ -593,30 +706,223 @@ async def _(
vol_processing_func=volume_curve, vol_processing_func=volume_curve,
) )
a = ["mcstructure-dalay","mcstructure-score","bdx-delay","bdx-score",] people_convert_times[usr_id] += 0.5
if "msq" in all_cvt_types:
all_files[file_to_convert]["msq"] = {"MSQ版本": "2-MSQ@"}
(usr_temp_path / "{}.msq".format(msct_obj.music_name)).open(
"wb"
).write(
msct_obj.encode_dump(
high_time_precision=_args["high-time-precision"]
)
)
if "addon-delay" in all_cvt_types: if "addon-delay" in all_cvt_types:
all_files[file_to_convert]["addon-delay"] = dict(
zip(
["指令数量", "音乐刻长"],
to_addon_pack_in_delay( to_addon_pack_in_delay(
midi_cvt=msct_obj, midi_cvt=msct_obj,
dist_path=str(usr_temp_path), dist_path=str(usr_temp_path),
progressbar_style=progress_bar_style, progressbar_style=progress_bar_style,
player=_args["player-selector"], player=_args["player-selector"],
max_height=_args["height-limit"], max_height=_args["height-limit"],
),
) )
all_cvt_types.remove("addon-delay") )
people_convert_times[usr_id] += 0.5
# all_cvt_types.remove("addon-delay")
if "addon-score" in all_cvt_types: if "addon-score" in all_cvt_types:
to_addon_pack_in_score() all_files[file_to_convert]["addon-score"] = dict(
zip(
["指令数量", "音乐刻长"],
to_addon_pack_in_score(
midi_cvt=msct_obj,
dist_path=str(usr_temp_path),
progressbar_style=progress_bar_style,
scoreboard_name=_args["scoreboard-name"],
auto_reset=not _args["disable-scoreboard-autoreset"],
),
)
)
people_convert_times[usr_id] += 0.5
# all_cvt_types.remove("addon-score")
if "mcstructure-dalay" in all_cvt_types:
all_files[file_to_convert]["mcstructure-dalay"] = dict(
zip(
["结构尺寸", "音乐刻长"],
to_mcstructure_file_in_delay(
midi_cvt=msct_obj,
dist_path=str(usr_temp_path),
player=_args["player-selector"],
max_height=_args["height-limit"],
),
)
)
people_convert_times[usr_id] += 0.5
# all_cvt_types.remove("mcstructure-dalay")
if "mcstructure-score" in all_cvt_types:
all_files[file_to_convert]["mcstructure-score"] = dict(
zip(
["结构尺寸", "音乐刻长", "指令数量"],
to_mcstructure_file_in_score(
midi_cvt=msct_obj,
dist_path=str(usr_temp_path),
scoreboard_name=_args["scoreboard-name"],
auto_reset=not _args["disable-scoreboard-autoreset"],
max_height=_args["height-limit"],
),
)
)
people_convert_times[usr_id] += 0.5
# all_cvt_types.remove("mcstructure-score")
if "bdx-delay" in all_cvt_types:
all_files[file_to_convert]["bdx-delay"] = dict(
zip(
[
"指令数量",
"音乐刻长",
"结构尺寸",
"终点坐标",
],
to_BDX_file_in_delay(
midi_cvt=msct_obj,
dist_path=str(usr_temp_path),
progressbar_style=progress_bar_style,
player=_args["player-selector"],
author=_args["author"],
max_height=_args["height-limit"],
),
)
)
people_convert_times[usr_id] += 0.5
# all_cvt_types.remove("bdx-delay")
if "bdx-score" in all_cvt_types:
all_files[file_to_convert]["bdx-score"] = dict(
zip(
[
"指令数量",
"音乐刻长",
"结构尺寸",
"终点坐标",
],
to_BDX_file_in_score(
midi_cvt=msct_obj,
dist_path=str(usr_temp_path),
progressbar_style=progress_bar_style,
scoreboard_name=_args["scoreboard-name"],
auto_reset=not _args["disable-scoreboard-autoreset"],
author=_args["author"],
max_height=_args["height-limit"],
),
)
)
people_convert_times[usr_id] += 0.5
# all_cvt_types.remove("bdx-score")
elif file_to_convert != "totalSize":
nonebot.logger.warning(
"文件类型错误:{}".format(file_to_convert),
)
buffer.write("文件 {} 已跳过".format(file_to_convert))
if people_convert_times[usr_id] > configdict["maxPersonConvert"]["music"]:
buffer.write("中途退出:转换点不足")
await linglun_convert.send(
UniMessage.text(
"今日音乐转换点数超限: {}/{}".format(
people_convert_times[usr_id],
configdict["maxPersonConvert"]["music"],
)
),
at_sender=True,
)
break
if not all_files: if not all_files:
nonebot.logger.warning("无可供转换的文件",) nonebot.logger.warning(
await musicreater_convert.send(UniMessage("不是哥们,空气咱这转不成面包,那是中科院的事。")) "无可供转换的文件",
)
await linglun_convert.finish(
UniMessage("不是哥们,空气咱这转不成面包,那是中科院的事。")
)
except Exception as e: except Exception as e:
nonebot.logger.error("转换存在错误:",e) nonebot.logger.error("转换存在错误:{}".format(e))
buffer.write("[ERROR] {}".format(e))
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__ sys.stderr = sys.__stderr__
Musicreater.plugin.archive.compress_zipfile(
usr_temp_path,
fp := str(temporary_dir / (fn := "result-{}.zip".format(usr_id))),
)
shutil.rmtree(usr_temp_path)
if isinstance(event, GroupMessageEvent) or isinstance(
event, GroupUploadNoticeEvent
):
await bot.call_api(
"upload_group_file", group_id=event.group_id, name=fn, file=fp
)
else:
await bot.call_api(
"upload_private_file", user_id=event.user_id, name=fn, file=fp
)
await MarkdownMessage.send_md(
"##{}\n\n```\n{}\n```".format(
MarkdownMessage.escape("日志信息:"),
buffer.getvalue().replace("\\", "/"),
),
bot,
event=event,
)
# nonebot.logger.info(buffer.getvalue())
await MarkdownMessage.send_md(
"## 转换结果\n\n"
+ ("\n\n\n").join(
[
"###{}\n\n{}".format(
fn,
"\n\n".join(
[
"- {}\n\t{}".format(
tn,
"\n\t".join(
["- {} : {}".format(i, j) for i, j in rps.items()]
),
)
for tn, rps in res.items()
]
),
)
for fn, res in all_files.items()
]
),
bot,
event=event,
)
os.remove(fp)
await linglun_convert.finish(
UniMessage.text(
"转换结束,当前所用转换点数: {}/{}".format(
people_convert_times[usr_id], configdict["maxPersonConvert"]["music"]
)
),
at_sender=True,
)
execute_cmd_convert_ablity = on_alconna( execute_cmd_convert_ablity = on_alconna(