Skip to content

feat: 为 Edge TTS 新增正则表达式过滤功能#5512

Open
zgojin wants to merge 3 commits intoAstrBotDevs:masterfrom
zgojin:master
Open

feat: 为 Edge TTS 新增正则表达式过滤功能#5512
zgojin wants to merge 3 commits intoAstrBotDevs:masterfrom
zgojin:master

Conversation

@zgojin
Copy link

@zgojin zgojin commented Feb 27, 2026

在使用 Edge TTS 生成语音时,默认情况下 TTS 会将生成的全部文字一并读出,如果大模型的回复中包含角色扮演的动作描写或内心独白(例如 (微笑着说)你好啊*摸摸头*),严重影响语音的自然度和沉浸感。
此 PR 增加了一个正则表达式过滤功能,允许用户在面板自定义过滤规则,在文本送入 TTS 引擎前将其精准剔除。

Modifications / 改动点

  1. 面板配置:修改了 astrbot/core/config/default.py
    • 在 Edge TTS 的默认配置选项中新增了 filter_regex 字段(默认为空字符串 "")。
  2. edge_tts_source.py:修改了 astrbot/core/provider/sources/edge_tts_source.py
    • 引入了 re 模块。
    • ProviderEdgeTTS 类的 __init__ 方法中增加了对 filter_regex 配置项的读取。
    • get_audio 方法中,在执行 TTS 生成逻辑之前,加入 re.sub 处理,对传入的 text 进行正则匹配与替换空字符操作。
  • This is NOT a breaking change. / 这不是一个破坏性变更。

image

已在本地完成完整流程测试。
在 Web 面板的 Edge TTS 配置中填入正则表达式(如:\(.*?\)|\(.*?\))后,发送包含中英文括号的测试文本,生成的录音文件已成功跳过括号内的动作描写内容,且未触发异常报错。


Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

在进行音频生成之前,为 Edge TTS 添加可配置的基于正则表达式的文本过滤功能。

新功能:

  • 为 Edge TTS 引入 filter_regex 配置选项,用于在合成前移除匹配到的文本片段。

改进:

  • 当过滤后的文本变为空时,跳过 TTS 生成并抛出运行时错误,同时在正则处理过程中记录调试和错误日志。
Original summary in English

Summary by Sourcery

Add configurable regular-expression-based text filtering to Edge TTS before audio generation.

New Features:

  • Introduce a filter_regex configuration option for Edge TTS to remove matched text segments before synthesis.

Enhancements:

  • Skip TTS generation and raise a runtime error when the filtered text becomes empty, with debug and error logging around regex processing.

@dosubot dosubot bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Feb 27, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求为 Edge TTS 语音生成功能引入了强大的文本过滤机制。通过允许用户配置正则表达式,系统现在可以在将文本发送到 TTS 引擎之前,自动识别并移除大模型回复中常见的角色扮演动作描写或内心独白等干扰内容。这一改进显著提升了生成语音的质量和用户体验,使其听起来更加自然和流畅。

Highlights

  • 正则表达式过滤功能: 为 Edge TTS 引入了正则表达式过滤功能,允许用户自定义规则以剔除不需要的文本,例如大模型回复中的动作描写或内心独白。
  • 语音自然度提升: 在语音生成前对文本进行预处理,有效提升了生成语音的自然度和沉浸感。
  • 空文本处理: 增加了对过滤后文本为空的检查,避免了不必要的语音生成并抛出运行时错误。
Changelog
  • astrbot/core/config/default.py
    • 在 Edge TTS 的默认配置中新增了 "filter_regex" 字段,默认值为空字符串。
  • astrbot/core/provider/sources/edge_tts_source.py
    • 导入了 "re" 模块以支持正则表达式操作。
    • 在 ProviderEdgeTTS 类的初始化方法中读取了 "filter_regex" 配置。
    • 在 get_audio 方法中,实现了使用 re.sub 对输入文本进行正则过滤的功能。
    • 添加了逻辑以检测并跳过过滤后为空的文本,避免生成空语音。
Activity
  • 作者已在本地完成了完整的流程测试,并验证了正则表达式过滤功能在实际场景中的有效性。
  • 提供了详细的测试步骤和截图,展示了使用正则表达式 "(.?)|\(.?\)" 成功过滤中英文括号内内容的效果。
  • 确认了此变动不是破坏性变更,且没有引入新的依赖库。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - 我发现了 1 个问题,并留下了一些整体反馈:

  • 考虑在 __init__ 中只编译一次正则表达式(例如 self.filter_pattern = re.compile(...)),并在配置加载时进行校验,这样可以在正则无效时尽早失败,同时避免在每次调用 get_audio 时重复编译。
  • re.sub 外面捕获宽泛的 Exception 会让配置错误(无效正则)和其他意外故障之间难以区分;更清晰的做法是显式捕获 re.error 并进行处理(例如禁用过滤或回退),同时让其他异常继续向上抛出。
给 AI 代理的提示
Please address the comments from this code review:

## Overall Comments
- Consider compiling the regular expression once in `__init__` (e.g., `self.filter_pattern = re.compile(...)`) and validating it at config load time so you fail fast on invalid patterns and avoid recompiling on every `get_audio` call.
- Catching a broad `Exception` around `re.sub` makes it harder to distinguish configuration errors (invalid regex) from other unexpected failures; it would be clearer to catch `re.error` explicitly and handle it (e.g., disable filtering or fall back) while letting other exceptions surface.

## Individual Comments

### Comment 1
<location path="astrbot/core/provider/sources/edge_tts_source.py" line_range="50-56" />
<code_context>

     async def get_audio(self, text: str) -> str:
+        if self.filter_regex:
+            try:
+                # 使用 re.sub 将匹配到的内容替换为空字符串
+                text = re.sub(self.filter_regex, "", text)
+                logger.debug(f"正则过滤后的文本: {text}")
+            except Exception as e:
+                logger.error(f"正则表达式执行错误: {e}")
+        if not text.strip():
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Catch a more specific exception type when applying the regex filter.

Using a broad `Exception` here can hide unrelated bugs and makes it harder to tell regex issues from other failures. Since only `re.sub` is risky, catch `re.error` instead so that only regex problems are handled and other exceptions propagate.

```python
try:
    text = re.sub(self.filter_regex, "", text)
except re.error as e:
    logger.error(f"正则表达式执行错误: {e} | pattern={self.filter_regex!r}")
```

This keeps the handler scoped to regex failures and avoids masking future errors in this block.

```suggestion
        if self.filter_regex:
            try:
                # 使用 re.sub 将匹配到的内容替换为空字符串
                text = re.sub(self.filter_regex, "", text)
                logger.debug(f"正则过滤后的文本: {text}")
            except re.error as e:
                logger.error(
                    "正则表达式执行错误: %s | pattern=%r",
                    e,
                    self.filter_regex,
                )
```
</issue_to_address>

Sourcery 对开源项目免费——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • Consider compiling the regular expression once in __init__ (e.g., self.filter_pattern = re.compile(...)) and validating it at config load time so you fail fast on invalid patterns and avoid recompiling on every get_audio call.
  • Catching a broad Exception around re.sub makes it harder to distinguish configuration errors (invalid regex) from other unexpected failures; it would be clearer to catch re.error explicitly and handle it (e.g., disable filtering or fall back) while letting other exceptions surface.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider compiling the regular expression once in `__init__` (e.g., `self.filter_pattern = re.compile(...)`) and validating it at config load time so you fail fast on invalid patterns and avoid recompiling on every `get_audio` call.
- Catching a broad `Exception` around `re.sub` makes it harder to distinguish configuration errors (invalid regex) from other unexpected failures; it would be clearer to catch `re.error` explicitly and handle it (e.g., disable filtering or fall back) while letting other exceptions surface.

## Individual Comments

### Comment 1
<location path="astrbot/core/provider/sources/edge_tts_source.py" line_range="50-56" />
<code_context>

     async def get_audio(self, text: str) -> str:
+        if self.filter_regex:
+            try:
+                # 使用 re.sub 将匹配到的内容替换为空字符串
+                text = re.sub(self.filter_regex, "", text)
+                logger.debug(f"正则过滤后的文本: {text}")
+            except Exception as e:
+                logger.error(f"正则表达式执行错误: {e}")
+        if not text.strip():
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Catch a more specific exception type when applying the regex filter.

Using a broad `Exception` here can hide unrelated bugs and makes it harder to tell regex issues from other failures. Since only `re.sub` is risky, catch `re.error` instead so that only regex problems are handled and other exceptions propagate.

```python
try:
    text = re.sub(self.filter_regex, "", text)
except re.error as e:
    logger.error(f"正则表达式执行错误: {e} | pattern={self.filter_regex!r}")
```

This keeps the handler scoped to regex failures and avoids masking future errors in this block.

```suggestion
        if self.filter_regex:
            try:
                # 使用 re.sub 将匹配到的内容替换为空字符串
                text = re.sub(self.filter_regex, "", text)
                logger.debug(f"正则过滤后的文本: {text}")
            except re.error as e:
                logger.error(
                    "正则表达式执行错误: %s | pattern=%r",
                    e,
                    self.filter_regex,
                )
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@dosubot dosubot bot added the area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. label Feb 27, 2026
@dosubot
Copy link

dosubot bot commented Feb 27, 2026

Related Documentation

Checked 1 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This PR introduces a new feature to add regular expression filtering to Edge TTS, allowing users to remove unwanted text before speech generation. However, the implementation is vulnerable to Regular Expression Denial of Service (ReDoS) because it uses user-provided regular expressions directly in re.sub without validation or timeouts, which could allow a malicious regex to hang the application. Additionally, there is a performance overhead due to calling re.sub in the get_audio method on every invocation, and error handling could be improved. It is recommended to pre-compile the regular expression in __init__ for better performance and to handle invalid expressions early.

if self.filter_regex:
try:
# 使用 re.sub 将匹配到的内容替换为空字符串
text = re.sub(self.filter_regex, "", text)
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The filter_regex configuration option uses user-provided regular expressions directly in re.sub without validation or timeout, making it vulnerable to Regular Expression Denial of Service (ReDoS). A malicious regex could cause the application to hang. To mitigate this, consider implementing validation for the user-provided regular expression, using a ReDoS-resistant regex engine, or implementing a timeout for the regex operation. Additionally, after pre-compiling the regular expression in __init__, the logic here can be simplified, removing the need to handle potential compilation errors on every call.

self.pitch = provider_config.get("pitch")
self.timeout = provider_config.get("timeout", 30)

self.filter_regex = provider_config.get("filter_regex", "")
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

为了提高性能和实现更清晰的错误处理,建议在 __init__ 方法中预编译正则表达式。这样可以避免在每次调用 get_audio 时都重新编译,并且可以在服务启动时就捕获并记录无效的正则表达式,而不是在运行时才发现问题。

Suggested change
self.filter_regex = provider_config.get("filter_regex", "")
self.filter_regex_str = provider_config.get("filter_regex", "")
self.compiled_filter_regex = None
if self.filter_regex_str:
try:
self.compiled_filter_regex = re.compile(self.filter_regex_str)
except re.error as e:
logger.error(f"Edge TTS 的正则表达式 '{self.filter_regex_str}' 无效,过滤功能将被禁用: {e}")

Copy link
Author

Choose a reason for hiding this comment

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

except re.error确保了用户正则错误也能捕获报错,不会影响框架运行,对于tts 调用并不会非常频繁,是否预编译基本没有任何影响

采纳代码审查建议,将宽泛的 Exception 替换为更具体的 re.error

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant