Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import base64
import aiofiles
from astrbot.core.utils.io import file_to_base64, download_image_by_url
from astrbot.core.utils.tencent_record_helper import wav_to_tencent_silk
from astrbot.core.utils.tencent_record_helper import audio_to_tencent_silk
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
from astrbot.api.event import AstrMessageEvent, MessageChain
from astrbot.api.platform import AstrBotMessage, PlatformMetadata
Expand Down Expand Up @@ -271,40 +271,49 @@ async def _parse_to_qqofficial(message: MessageChain):
image_base64 = None # only one img supported
image_file_path = None
record_file_path = None
for i in message.chain:
if isinstance(i, Plain):
plain_text += i.text
elif isinstance(i, Image) and not image_base64:
if i.file and i.file.startswith("file:///"):
image_base64 = file_to_base64(i.file[8:])
image_file_path = i.file[8:]
elif i.file and i.file.startswith("http"):
image_file_path = await download_image_by_url(i.file)

for element in message.chain:
if isinstance(element, Plain):
plain_text += element.text

elif isinstance(element, Image) and not image_base64:
if element.file and element.file.startswith("file:///"):
image_base64 = file_to_base64(element.file[8:])
image_file_path = element.file[8:]
elif element.file and element.file.startswith("http"):
image_file_path = await download_image_by_url(element.file)
image_base64 = file_to_base64(image_file_path)
elif i.file and i.file.startswith("base64://"):
image_base64 = i.file
elif element.file and element.file.startswith("base64://"):
image_base64 = element.file[9:] # 直接去掉前缀
else:
image_base64 = file_to_base64(i.file)
image_base64 = image_base64.removeprefix("base64://")
elif isinstance(i, Record):
if i.file:
record_wav_path = await i.convert_to_file_path() # wav 路径
image_base64 = file_to_base64(element.file)
# 确保去掉 base64 前缀
if image_base64 and image_base64.startswith("base64://"):
Comment on lines +290 to +291
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议: 直接切片后,多余的 base64 前缀删除逻辑可能不再需要。

重构以删除此冗余检查并提高代码清晰度。

建议的实现:

                elif element.file and element.file.startswith("base64://"):
                    image_base64 = element.file[9:]  # 直接去掉前缀
                else:
                    image_base64 = file_to_base64(element.file)

无需进一步更改,因为提供的代码中已不存在冗余的 base64 前缀删除逻辑。代码现在清晰简洁。

Original comment in English

suggestion: Redundant base64 prefix removal logic may be unnecessary after direct slicing above.

Refactor to remove this redundant check and improve code clarity.

Suggested implementation:

                elif element.file and element.file.startswith("base64://"):
                    image_base64 = element.file[9:]  # 直接去掉前缀
                else:
                    image_base64 = file_to_base64(element.file)

No further changes are needed, as the redundant base64 prefix removal logic is already absent in the provided code. The code is now clear and concise.

image_base64 = image_base64[9:]

elif isinstance(element, Record):
if element.file:
record_wav_path = await element.convert_to_file_path() # wav 路径
temp_dir = os.path.join(get_astrbot_data_path(), "temp")
record_tecent_silk_path = os.path.join(
os.makedirs(temp_dir, exist_ok=True) # 确保目录存在

record_tencent_silk_path = os.path.join(
temp_dir, f"{uuid.uuid4()}.silk"
)
try:
duration = await wav_to_tencent_silk(
record_wav_path, record_tecent_silk_path
duration = await audio_to_tencent_silk(
record_wav_path, record_tencent_silk_path
)
if duration > 0:
record_file_path = record_tecent_silk_path
record_file_path = record_tencent_silk_path
else:
record_file_path = None
logger.error("转换音频格式时出错:音频时长不大于0")
except Exception as e:
logger.error(f"处理语音时出错: {e}")
record_file_path = None

else:
logger.debug(f"qq_official 忽略 {i.type}")
logger.debug(f"qq_official 忽略 {element.type}")

return plain_text, image_base64, image_file_path, record_file_path
72 changes: 70 additions & 2 deletions astrbot/core/utils/tencent_record_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ async def convert_to_pcm_wav(input_path: str, output_path: str) -> str:
else:
raise RuntimeError("生成的WAV文件不存在或为空")


async def audio_to_tencent_silk_base64(audio_path: str) -> tuple[str, float]:
"""
将 MP3/WAV 文件转为 Tencent Silk 并返回 base64 编码与时长(秒)。
Expand All @@ -117,7 +116,7 @@ async def audio_to_tencent_silk_base64(audio_path: str) -> tuple[str, float]:
try:
import pilk
except ImportError as e:
raise Exception("未安装 pilk: pip install pilk") from e
raise Exception("pilk 模块未安装,请前往管理面板->控制台->安装pip库 安装 pilk 这个库") from e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

问题 (code-quality): 抛出特定错误而不是通用的 ExceptionBaseException (raise-specific-error)

解释如果一段代码抛出特定异常类型 而不是通用的 [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) 或 [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), 则调用代码可以:
  • 获取有关错误类型的更多信息
  • 为其定义特定的异常处理

这样,代码的调用者就可以适当地处理错误。

如何解决此问题?

因此,与其让代码抛出 ExceptionBaseException,例如

if incorrect_input(value):
    raise Exception("The input is incorrect")

你可以让代码抛出特定错误,例如

if incorrect_input(value):
    raise ValueError("The input is incorrect")

或者

class IncorrectInputError(Exception):
    pass


if incorrect_input(value):
    raise IncorrectInputError("The input is incorrect")
Original comment in English

issue (code-quality): Raise a specific error instead of the general Exception or BaseException (raise-specific-error)

ExplanationIf a piece of code raises a specific exception type rather than the generic [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) or [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), the calling code can:
  • get more information about what type of error it is
  • define specific exception handling for it

This way, callers of the code can handle the error appropriately.

How can you solve this?

So instead of having code raising Exception or BaseException like

if incorrect_input(value):
    raise Exception("The input is incorrect")

you can have code raising a specific error like

if incorrect_input(value):
    raise ValueError("The input is incorrect")

or

class IncorrectInputError(Exception):
    pass


if incorrect_input(value):
    raise IncorrectInputError("The input is incorrect")


temp_dir = os.path.join(get_astrbot_data_path(), "temp")
os.makedirs(temp_dir, exist_ok=True)
Expand Down Expand Up @@ -158,3 +157,72 @@ async def audio_to_tencent_silk_base64(audio_path: str) -> tuple[str, float]:
os.remove(wav_path)
if os.path.exists(silk_path):
os.remove(silk_path)

async def audio_to_tencent_silk(audio_path: str, output_path: str) -> float:
"""
将 MP3/WAV 文件转为 Tencent Silk 并返回时长(秒)。

参数:
- audio_path: 输入音频文件路径(.mp3 或 .wav)
- output_path: 输出的音频路径-> silk

返回:
- duration: 音频时长(秒)
Comment on lines +165 to +174
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

问题 (bug_risk): 清理中的裸 except 子句可能会抑制意外错误。

使用特定的异常类型,例如 OSError,以确保在清理过程中不会意外抑制不相关的错误。

Original comment in English

issue (bug_risk): Bare except clause in cleanup may suppress unexpected errors.

Use a specific exception type, such as OSError, to ensure unrelated errors are not inadvertently suppressed during cleanup.

"""
try:
import pilk
except ImportError as e:
raise Exception("pilk 模块未安装,请前往管理面板->控制台->安装pip库 安装 pilk 这个库") from e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

问题 (code-quality): 抛出特定错误而不是通用的 ExceptionBaseException (raise-specific-error)

解释如果一段代码抛出特定异常类型 而不是通用的 [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) 或 [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), 则调用代码可以:
  • 获取有关错误类型的更多信息
  • 为其定义特定的异常处理

这样,代码的调用者就可以适当地处理错误。

如何解决此问题?

因此,与其让代码抛出 ExceptionBaseException,例如

if incorrect_input(value):
    raise Exception("The input is incorrect")

你可以让代码抛出特定错误,例如

if incorrect_input(value):
    raise ValueError("The input is incorrect")

或者

class IncorrectInputError(Exception):
    pass


if incorrect_input(value):
    raise IncorrectInputError("The input is incorrect")
Original comment in English

issue (code-quality): Raise a specific error instead of the general Exception or BaseException (raise-specific-error)

ExplanationIf a piece of code raises a specific exception type rather than the generic [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) or [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), the calling code can:
  • get more information about what type of error it is
  • define specific exception handling for it

This way, callers of the code can handle the error appropriately.

How can you solve this?

So instead of having code raising Exception or BaseException like

if incorrect_input(value):
    raise Exception("The input is incorrect")

you can have code raising a specific error like

if incorrect_input(value):
    raise ValueError("The input is incorrect")

or

class IncorrectInputError(Exception):
    pass


if incorrect_input(value):
    raise IncorrectInputError("The input is incorrect")


# 确保输入文件存在
if not os.path.exists(audio_path):
raise FileNotFoundError(f"音频文件不存在: {audio_path}")

temp_dir = os.path.join(get_astrbot_data_path(), "temp")
os.makedirs(temp_dir, exist_ok=True)

# 检查文件扩展名
ext = os.path.splitext(audio_path)[1].lower()

# 创建临时 WAV 文件
temp_wav = tempfile.NamedTemporaryFile(
suffix=".wav", delete=False, dir=temp_dir
).name

wav_path = audio_path # 默认使用原文件路径

# 如果不是 WAV 格式,需要转换
if ext != ".wav":
try:
await convert_to_pcm_wav(audio_path, temp_wav)
wav_path = temp_wav
except Exception as e:
# 如果转换失败,清理临时文件
if os.path.exists(temp_wav):
os.remove(temp_wav)
raise Exception(f"音频格式转换失败: {e}") from e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

问题 (code-quality): 抛出特定错误而不是通用的 ExceptionBaseException (raise-specific-error)

解释如果一段代码抛出特定异常类型 而不是通用的 [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) 或 [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), 则调用代码可以:
  • 获取有关错误类型的更多信息
  • 为其定义特定的异常处理

这样,代码的调用者就可以适当地处理错误。

如何解决此问题?

因此,与其让代码抛出 ExceptionBaseException,例如

if incorrect_input(value):
    raise Exception("The input is incorrect")

你可以让代码抛出特定错误,例如

if incorrect_input(value):
    raise ValueError("The input is incorrect")

或者

class IncorrectInputError(Exception):
    pass


if incorrect_input(value):
    raise IncorrectInputError("The input is incorrect")
Original comment in English

issue (code-quality): Raise a specific error instead of the general Exception or BaseException (raise-specific-error)

ExplanationIf a piece of code raises a specific exception type rather than the generic [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) or [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), the calling code can:
  • get more information about what type of error it is
  • define specific exception handling for it

This way, callers of the code can handle the error appropriately.

How can you solve this?

So instead of having code raising Exception or BaseException like

if incorrect_input(value):
    raise Exception("The input is incorrect")

you can have code raising a specific error like

if incorrect_input(value):
    raise ValueError("The input is incorrect")

or

class IncorrectInputError(Exception):
    pass


if incorrect_input(value):
    raise IncorrectInputError("The input is incorrect")


try:
with wave.open(wav_path, "rb") as wav_file:
rate = wav_file.getframerate()

# 转换为 Silk 格式
silk_duration = await asyncio.to_thread(
pilk.encode, wav_path, output_path, pcm_rate=rate, tencent=True
)

return silk_duration

except Exception as e:
# 如果转换失败,删除可能已创建的部分输出文件
if os.path.exists(output_path):
os.remove(output_path)
raise Exception(f"Silk 格式转换失败: {e}") from e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

问题 (code-quality): 抛出特定错误而不是通用的 ExceptionBaseException (raise-specific-error)

解释如果一段代码抛出特定异常类型 而不是通用的 [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) 或 [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), 则调用代码可以:
  • 获取有关错误类型的更多信息
  • 为其定义特定的异常处理

这样,代码的调用者就可以适当地处理错误。

如何解决此问题?

因此,与其让代码抛出 ExceptionBaseException,例如

if incorrect_input(value):
    raise Exception("The input is incorrect")

你可以让代码抛出特定错误,例如

if incorrect_input(value):
    raise ValueError("The input is incorrect")

或者

class IncorrectInputError(Exception):
    pass


if incorrect_input(value):
    raise IncorrectInputError("The input is incorrect")
Original comment in English

issue (code-quality): Raise a specific error instead of the general Exception or BaseException (raise-specific-error)

ExplanationIf a piece of code raises a specific exception type rather than the generic [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) or [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), the calling code can:
  • get more information about what type of error it is
  • define specific exception handling for it

This way, callers of the code can handle the error appropriately.

How can you solve this?

So instead of having code raising Exception or BaseException like

if incorrect_input(value):
    raise Exception("The input is incorrect")

you can have code raising a specific error like

if incorrect_input(value):
    raise ValueError("The input is incorrect")

or

class IncorrectInputError(Exception):
    pass


if incorrect_input(value):
    raise IncorrectInputError("The input is incorrect")


finally:
# 清理临时 WAV 文件(如果是新创建的)
if wav_path != audio_path and os.path.exists(wav_path):
try:
os.remove(wav_path)
except:
pass # 忽略清理错误
Loading