Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upstream #123

Merged
merged 54 commits into from
Dec 12, 2024
Merged

Upstream #123

merged 54 commits into from
Dec 12, 2024

Conversation

DoroWolf
Copy link
Collaborator

@DoroWolf DoroWolf commented Dec 12, 2024

Summary by CodeRabbit

发布说明

  • 新功能

    • 增加了对特定资产目录的跟踪。
    • 引入了新的日志机制,增强了机器人管理和错误处理。
    • 改进了消息发送和删除逻辑,增强了错误处理。
  • 文档

    • 更新了多个类和方法的文档字符串,以提高可读性和理解。
  • 样式

    • 改进了代码的类型注释,提高了类型安全性。
  • 依赖更新

    • 更新了多个依赖包的版本,确保兼容性和功能改进。

Copy link

coderabbitai bot commented Dec 12, 2024

Walkthrough

此次更改涉及多个文件的修改,主要集中在代码结构优化、错误处理改进和功能增强。.gitignore 文件更新了被忽略的目录和文件,bot.py 引入了新的日志机制和进程管理功能,message.py 中的消息处理逻辑得到了改进。其他文件如 coremodules 目录下的多个文件也进行了函数签名更新、类型注解增强以及新功能的添加。整体上,这些修改旨在提升代码的可读性、可维护性和功能性。

Changes

文件路径 修改摘要
.gitignore 移除 assets/cytoid-avatar/assets/cytoid-cover/ 的忽略规则。
bot.py 引入 loguru 日志机制,更新 init_bot 函数,添加 multiprocess_run_until_complete 函数,重构 run_bot 函数。
bots/qqbot/message.py 更新 FinishedSessionMessageSession 类中的 deletesend_message 方法,改进错误处理和消息发送逻辑。
core/builtins/init.py 添加 Temp 变量,更新 send_message 方法的参数名称。
core/builtins/message/init.py 更新多个类的方法签名,增强类型注解。
core/builtins/message/chain.py 更新 MessageChain 类构造函数和 as_sendable 方法的参数类型。
core/builtins/message/elements.py 修改多个类的 assign 方法,移除 comment 参数,返回深拷贝实例。
core/component.py 修改 Module 类的实例化方法,添加重载以增强类型提示。
core/config/init.py 更新 CFGManager 类的方法,增强错误处理。
core/constants/info.py Info 类添加文档字符串。
core/joke.py 引入 check_apr_fools 函数,重构 joke 函数。
core/parser/message.py 改进错误处理和临时禁令管理逻辑。
core/types/message/init.py 更新 MsgInfo 类的 target_idsender_id 属性类型。
core/types/module/init.py 移除 convert2lst 函数,重构 Module 类以使用 attrs 库。
core/types/module/component_matches.py 引入 BaseMatches 基类,重构匹配类。
core/types/module/component_meta.py 引入 ModuleMeta 基类,重构多个元类。
core/types/module/utils.py 添加 convert2lst 函数。
core/utils/cooldown.py 修改 CoolDown 类,简化冷却管理逻辑。
core/utils/game.py 更新 PlayState 类,重命名变量和方法。
core/utils/http.py 更新 get_urlpost_urldownload 函数的默认参数值。
core/utils/i18n.py 更新 Locale 类构造函数的文档字符串。
core/utils/image_table.py 更新 ImageTable 类构造函数和 image_table_render 函数的参数类型。
core/utils/random.py Random 类添加文档字符串。
core/utils/text.py 移除 random_string 函数,更新 __all__ 导出。
core/utils/web_render.py 更新 webrender 函数的参数类型和逻辑。
modules/cytoid/profile.py 更新 cytoid_profile 函数,修改参数名和逻辑。
modules/cytoid/rating.py 更新路径处理和资源下载逻辑。
modules/cytoid/utils.py 更新 get_profile_name 函数的字符串拼接方法。
modules/maimai/libraries/chunithm_apidata.py 修改 get_record 函数的缓存目录路径处理。
modules/maimai/libraries/maimaidx_apidata.py 更新封面图片ID处理和缓存目录结构。
modules/maimai/libraries/maimaidx_best50.py 更新封面图片路径处理逻辑。
modules/maimai/libraries/maimaidx_music.py 更新 get_cover_len5_id 函数的参数类型。
modules/meme/moegirl.py 修改 moegirl 函数中 QueryInfo 的调用方式。
modules/wiki/audit.py 更新 list 命令的消息发送逻辑和错误处理。
modules/wiki/inline.py 增强维基查询的正则表达式处理和异步任务管理。
modules/wiki/search.py 更新 search_pages 函数的参数类型。
modules/wiki/utils/mapping.py 添加 infobox_elements 列表,移除 random_title_list
modules/wiki/utils/screenshot_image.py 修改 generate_screenshot_v2 函数以使用 infobox_elements
modules/wiki/utils/wikilib.py 重构多个类以使用 attrs 库。
modules/wiki/wiki.py 更新 query_pages 函数的可选参数处理。
modules/wikilog/init.py 添加新的命令处理程序以管理保持活动功能。
modules/wikilog/dbutils.py 更新 WikiLogUtil 类以处理 keep_alive 状态。
modules/wikilog/locales/zh_cn.json 添加新的本地化条目以增强用户体验。
modules/wordle/init.py 更新 WordleBoard 类以改进游戏结束逻辑。
pyproject.toml 更新多个依赖项的版本和来源。
requirements.txt 更新多个依赖项的版本和来源。
schedulers/wiki_bot.py login_bots 函数中添加保持活动机制。

Poem

在代码的世界里跳跃,
小兔子欢快地跑,
新功能如春花绽放,
错误处理更周到。
让我们一起欢庆,
代码更美好,
兔子们的梦想,
在此刻实现了! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 23

🧹 Outside diff range and nitpick comments (47)
modules/wiki/utils/mapping.py (2)

5-16: CSS 选择器列表结构清晰

新增的 infobox_elements 列表包含了多种维基页面上的信息框选择器,结构合理。建议考虑以下优化:

  1. 按照选择器的特异性(specificity)进行排序
  2. 为不同类型的信息框选择器添加注释,说明其用途
 infobox_elements = [
+    # 通用信息框
     'div#infoboxborder',
     '.infobox',
     '.infoboxtable',
+    # 游戏特定信息框
     '.arcaeabox',
     '.moe-infobox',
+    # 皮肤和模板信息框
     '.skin-infobox',
     '.tpl-infobox',
+    # 其他特殊信息框
     '.notaninfobox',
     '.portable-infobox',
     '.rotable',
     '.infotemplatebox',
 ]

18-18: 建议添加阻止列表说明注释

generate_screenshot_v2_blocklist 缺少对其用途的说明,建议添加注释以提高代码可维护性。

+# 不使用 v2 版本截图功能的网站列表
 generate_screenshot_v2_blocklist = ['https://mzh.moegirl.org.cn', 'https://zh.moegirl.org.cn']
modules/wiki/utils/screenshot_image.py (2)

20-20: 正确使用了导入的选择器列表

infobox_elements 的导入和使用方式正确,通过 copy 操作避免了对原始列表的修改。建议添加类型注解以提高代码的可维护性。

-from .mapping import infobox_elements
+from .mapping import infobox_elements  # type: List[str]

 async def generate_screenshot_v2(page_link: str, section: str = None, allow_special_page=False, content_mode=False, use_local=True,
-                                element=None) -> Union[List[PILImage], bool]:
+                                element: Optional[List[str]] = None) -> Union[List[PILImage], bool]:
     elements_ = infobox_elements.copy()

Also applies to: 25-27


Line range hint 24-83: 建议优化错误处理逻辑

当前的错误处理逻辑在本地渲染失败时会尝试远程渲染,建议将这个重试逻辑抽取为单独的函数,以提高代码可维护性。

+async def _retry_screenshot_generation(page_link: str, section: str, allow_special_page: bool, 
+                                     content_mode: bool) -> Union[List[PILImage], bool]:
+    """在本地渲染失败时尝试远程渲染"""
+    try:
+        return await generate_screenshot_v2(page_link, section, allow_special_page, content_mode,
+                                          use_local=False)
+    except Exception:
+        Logger.error(traceback.format_exc())
+        return False

 async def generate_screenshot_v2(...):
     try:
         # ... 现有代码 ...
     except aiohttp.ClientConnectorError:
         if use_local:
-            return await generate_screenshot_v2(page_link, section, allow_special_page, content_mode,
-                                              use_local=False)
+            return await _retry_screenshot_generation(page_link, section, allow_special_page, content_mode)
         else:
             return False
modules/maimai/libraries/chunithm_apidata.py (1)

25-27: 缓存目录结构优化

将缓存文件统一存放到专门的目录中是个好的改进。建议将目录名 'maimai-record' 定义为常量,以便于维护和复用。

+ # 在文件开头添加常量定义
+ MAIMAI_RECORD_DIR = 'maimai-record'

- dir = os.path.join(cache_path, 'maimai-record')
+ dir = os.path.join(cache_path, MAIMAI_RECORD_DIR)
🧰 Tools
🪛 Ruff (0.8.2)

25-25: os may be undefined, or defined from star imports

(F405)


26-26: os may be undefined, or defined from star imports

(F405)


27-27: os may be undefined, or defined from star imports

(F405)

modules/maimai/libraries/maimaidx_apidata.py (2)

22-22: 封面图片处理逻辑优化建议

当前的封面图片处理逻辑可以进一步改进:

  1. 建议添加重试机制以处理网络不稳定情况
  2. 考虑使用异步批量下载提高性能
+ from core.utils.retry import async_retry

- await download(url, status_code=200, path=mai_cover_path, filename=f'{id}.png', attempt=1, logging_err_resp=False)
+ @async_retry(attempts=3, delay=1)
+ async def download_cover(url: str, path: str, filename: str):
+     return await download(url, status_code=200, path=path, filename=filename, logging_err_resp=False)
+ 
+ await download_cover(url, mai_cover_path, f'{id}.png')

Also applies to: 27-27, 31-32


59-63: 优化封面图片回退逻辑

当前的封面图片回退逻辑可以更加优雅:

  1. 建议使用常量定义默认封面
  2. 可以添加日志记录找不到封面的情况
+ DEFAULT_COVER = '0.png'

- cover_path = os.path.join(mai_cover_path, '0.png')
+ cover_path = os.path.join(mai_cover_path, DEFAULT_COVER)
+ Logger.debug(f'Using default cover for music {music.id}')
🧰 Tools
🪛 Ruff (0.8.2)

59-59: mai_cover_path may be undefined, or defined from star imports

(F405)


63-63: mai_cover_path may be undefined, or defined from star imports

(F405)

modules/maimai/libraries/maimaidx_best50.py (1)

Line range hint 1-300: 建议优化代码结构和组织方式

代码结构可以通过以下几点改进:

  1. 提取常量配置:
    • 图片尺寸(如 860x1300, 150x100)
    • 颜色值
    • 字体大小
  2. 拆分 _drawBestList 方法,当前方法过长且职责过多
  3. 添加类型注解以提高代码可读性
  4. 考虑使用数据类来替代字典传递数据

建议添加配置类:

from dataclasses import dataclass
from typing import Tuple, List

@dataclass
class ImageConfig:
    WIDTH: int = 860
    HEIGHT: int = 1300
    ITEM_WIDTH: int = 150
    ITEM_HEIGHT: int = 100
    BG_COLOR: Tuple[int, ...] = (211, 211, 211, 255)
    DIFFICULTY_COLORS: List[Tuple[int, ...]] = [
        (69, 193, 36),
        (255, 186, 1),
        (255, 90, 102),
        (134, 49, 200),
        (217, 197, 233)
    ]

建议将绘制逻辑拆分为多个方法:

class DrawBest:
    def _draw_chart_title(self, draw: ImageDraw.Draw, title: str, x: int, y: int) -> None:
        """绘制歌曲标题"""
        pass

    def _draw_chart_stats(self, draw: ImageDraw.Draw, chart: ChartInfo, x: int, y: int) -> None:
        """绘制成绩数据"""
        pass

    def _draw_chart_item(self, chart: ChartInfo, position: Tuple[int, int]) -> None:
        """绘制单个歌曲项目"""
        pass
core/types/module/component_meta.py (2)

15-16: 建议为 ModuleMeta 类添加文档字符串

当前的 ModuleMeta 类为空类,缺少文档字符串。为其添加说明可以提高代码的可读性和可维护性。


22-22: 检查 help_doc 属性的转换器

help_doc 属性使用了 field(default=[], converter=convert2lst),请确保 convert2lst 函数能够正确地将输入转换为列表,防止出现意外的类型转换错误。

core/utils/cooldown.py (1)

25-31: _get_cd_dict 方法添加文档字符串

为了增加代码的可读性和维护性,建议为 _get_cd_dict 方法添加文档字符串,说明其功能和返回值。

bot.py (4)

10-10: loggerFallback 的命名风格问题

在第 10 行,使用了 loggerFallback 作为日志记录器的名称。为了保持命名风格的一致性,建议将其重命名为 logger_fallback


58-69: 将数据库更新逻辑封装为独立函数

在第 58-69 行,数据库更新逻辑直接写在 init_bot 函数中。为了提高代码的可维护性,建议将数据库更新部分封装为独立的函数或方法。


82-85: 简化嵌套的 if 语句

根据静态分析工具的提示,第 82-85 行的嵌套 if 语句可以合并为单个条件判断,从而简化代码结构。

应用以下修改:

- if 'enable' in CFGManager.values[t][t]:
-     if not CFGManager.values[t][t]['enable']:
+ if CFGManager.values[t][t].get('enable') is False:
🧰 Tools
🪛 Ruff (0.8.2)

82-84: Use a single if statement instead of nested if statements

(SIM102)


83-84: Use a single if statement instead of nested if statements

(SIM102)


124-134: 改进错误日志的信息

restart_process 函数中,当重启次数达到 3 次时,仅记录了简单的错误信息。建议在日志中包含失败原因和堆栈信息,方便调试。

modules/wikilog/__init__.py (1)

265-266: 优化异常处理,提供更具体的反馈

在第265-266行的异常处理代码中,直接捕获了所有 Exception,并在发生异常时给用户发送通用的失败消息。建议捕获更具体的异常类型,并根据不同的异常情况给予用户更详细的反馈,有助于用户理解问题所在。

core/builtins/message/__init__.py (1)

96-98: 简化条件判断

在第96-98行,嵌套的 if 语句可以合并为单个条件判断,以提高代码的简洁性和可读性。

建议将代码修改为:

if get_['active'] and (datetime.now().timestamp() - get_['ts'] > get_.get('timeout', 3600)):
    get_['active'] = False
    get_['flag'].set()
🧰 Tools
🪛 Ruff (0.8.2)

96-98: Use a single if statement instead of nested if statements

(SIM102)

core/parser/message.py (1)

492-496: 优化错误信息的组装方式以提高可读性

在组装 errmsg 时,代码的嵌套层次较深,可能影响可读性。建议简化代码结构,减少不必要的嵌套,提高代码的清晰度。

请参考以下修改:

- if Config('bug_report_url', bug_report_url_default, cfg_type=str):
-     errmsg += '\n' + msg.locale.t('error.prompt.address',
-                                   url=Url(Config('bug_report_url',
-                                                  bug_report_url_default,
-                                                  cfg_type=str),
-                                           use_mm=False))
+ bug_report_url = Config('bug_report_url', bug_report_url_default, cfg_type=str)
+ if bug_report_url:
+     url = Url(bug_report_url, use_mm=False)
+     errmsg += '\n' + msg.locale.t('error.prompt.address', url=url)
core/types/module/utils.py (1)

4-9: 函数 convert2lst 的参数类型注解可扩展为支持更多类型

当前 convert2lst 函数的参数类型注解为 Union[str, list, tuple],若未来需要支持更多的可迭代类型,建议更改类型注解为更通用的类型,如 SequenceIterable

请参考以下修改:

- from typing import Union
+ from typing import Union, Iterable

- def convert2lst(elements: Union[str, list, tuple]) -> list:
+ def convert2lst(elements: Union[str, Iterable]) -> list:
modules/cytoid/utils.py (1)

8-8: 建议统一字符串格式化方式

在第 8 行中,将字符串拼接方式改为了 f-string,这提高了代码的可读性。建议检查整个模块,确保字符串格式化方式的一致性,使代码风格统一。

core/utils/text.py (3)

7-9: 改进文档字符串格式

文档字符串的添加提高了代码可读性,但建议遵循更规范的格式。

建议按照以下格式修改:

def isfloat(num_str: Any) -> bool:
-    '''
-    检查字符串是否符合float。
-    '''
+    """检查字符串是否符合float格式。
+
+    Args:
+        num_str: 要检查的输入值
+
+    Returns:
+        bool: 如果可以转换为float则返回True,否则返回False
+    """

isint 函数也进行类似的改进。

Also applies to: 18-20


Line range hint 1-1: 移除未使用的导入

由于 random_string 函数已被移除,random 模块的导入现在是多余的。

建议删除未使用的导入:

-import random
from datetime import timedelta
from typing import Any

Line range hint 6-16: 改进类型提示和参数验证

isfloatisint 函数的类型提示可以更具体,并添加参数验证。

建议进行以下改进:

-def isfloat(num_str: Any) -> bool:
+def isfloat(num_str: Union[str, int, float]) -> bool:
     """检查字符串是否符合float格式。"""
+    if num_str is None:
+        return False
     try:
         float(num_str)
         return True
     except ValueError:
         return False

isint 函数也进行类似的改进。

Also applies to: 17-25

core/joke.py (1)

15-20: 建议优化条件判断的可读性

当前实现逻辑清晰,但可以通过提取变量名使代码更具表达性。

建议如下修改:

 def joke(text: str) -> str:
-    if check_apr_fools():
+    should_apply_joke = check_apr_fools()
+    if should_apply_joke:
         # 这里可能会增加使用不同玩笑方法的区分,但现在不太想做XD
         return shuffle_joke(text)
     else:
         return text
core/types/module/component_matches.py (2)

5-6: 需要优化导入语句

field 被导入但未使用,建议移除。

-from attrs import define, field
+from attrs import define
🧰 Tools
🪛 Ruff (0.8.2)

5-5: attrs.field imported but unused

Remove unused import: attrs.field

(F401)


Line range hint 22-71: 类继承结构优化得当,但建议统一类型定义

各个子类重复定义 set 属性是不必要的,可以使用泛型来优化。

建议如下修改:

from typing import TypeVar, Generic, List

T = TypeVar('T', CommandMeta, RegexMeta, ScheduleMeta, HookMeta)

@define
class BaseMatches(Generic[T]):
    set: List[T] = field(factory=list)
    
    def add(self, meta: T) -> List[T]:
        self.set.append(meta)
        return self.set

@define
class CommandMatches(BaseMatches[CommandMeta]):
    pass

# 其他类似...
🧰 Tools
🪛 Ruff (0.8.2)

66-66: ScheduleMeta may be undefined, or defined from star imports

(F405)


71-71: HookMeta may be undefined, or defined from star imports

(F405)

core/utils/web_render.py (3)

2-2: 移除未使用的导入

Union 类型未在代码中使用,建议将其从导入语句中移除。

-from typing import Tuple, Optional, Union
+from typing import Tuple, Optional
🧰 Tools
🪛 Ruff (0.8.2)

2-2: typing.Union imported but unused

Remove unused import: typing.Union

(F401)


13-20: 完善函数文档字符串

函数文档缺少新增参数 _ignore_status 的说明。建议更新文档以包含所有参数的完整描述。

    '''根据请求方法生成 WebRender URL。

    :param method: API 方法。
    :param url: 若 method 为 source,则指定请求的 URL。
    :param use_local: 是否使用本地 WebRender。
+   :param _ignore_status: 是否忽略渲染状态检查。
    :returns: 生成的 WebRender URL。
    '''

24-24: 优化条件表达式

可以使用更简洁的条件表达式来提高代码可读性。

-        url = '' if not url else url
+        url = url if url else ''
🧰 Tools
🪛 Ruff (0.8.2)

24-24: Use url if url else '' instead of '' if not url else url

Replace with url if url else ''

(SIM212)

modules/cytoid/profile.py (1)

7-7: 添加类型注解

建议为 username 参数添加类型注解,以提高代码的可维护性。

-async def cytoid_profile(msg: Bot.MessageSession, username):
+async def cytoid_profile(msg: Bot.MessageSession, username: Optional[str] = None):
modules/wikilog/locales/zh_cn.json (1)

42-43: 保持消息格式一致性

建议统一消息格式,将 "Keep alive" 翻译为中文,以保持与其他消息的一致性。

-  "wikilog.message.keepalive.logged.as": "[Keep alive] 以 ${name} 的身份登录:${wiki}",
-  "wikilog.message.keepalive.failed": "[Keep alive] 登录状态检查失败:${link}"
+  "wikilog.message.keepalive.logged.as": "[保持连接] 以 ${name} 的身份登录:${wiki}",
+  "wikilog.message.keepalive.failed": "[保持连接] 登录状态检查失败:${link}"
modules/wiki/search.py (2)

18-18: 建议完善函数文档

函数包含了复杂的 interwiki 链接处理逻辑,建议添加详细的函数文档说明参数用途和处理流程。

建议添加如下文档:

 async def search_pages(msg: Bot.MessageSession, title: Union[str, list, tuple], use_prefix: bool = True):
+    """
+    搜索页面并处理 interwiki 链接
+    
+    Args:
+        msg: 消息会话对象
+        title: 要搜索的页面标题,可以是字符串或字符串列表
+        use_prefix: 是否使用页面前缀,默认为 True
+    
+    Returns:
+        None
+    """

Line range hint 18-73: 建议增加错误处理

当前代码缺少对网络请求失败的处理机制。

建议在调用 WikiLib.search_page 时添加 try-except 块:

         tasks = []
         for rd in ready_for_query_pages:
-            tasks.append(asyncio.ensure_future(
-                WikiLib(q, headers).search_page(rd)))
+            try:
+                tasks.append(asyncio.ensure_future(
+                    WikiLib(q, headers).search_page(rd)))
+            except Exception as e:
+                Logger.error(f"搜索页面时发生错误: {e}")
+                continue
core/utils/random.py (2)

12-14: 建议完善类文档

当前文档简单说明了类的功能,但缺少对性能影响和安全性的说明。

建议扩展文档内容:

     """
-    机器人内置的随机数生成器。在配置文件中将`use_secrets_random`设为`true`时使用`secret`库,否则默认使用`random`库。
+    机器人内置的随机数生成器。
+    
+    在配置文件中将`use_secrets_random`设为`true`时使用`secret`库,否则默认使用`random`库。
+    
+    性能说明:
+    - secrets 库提供更高的安全性,但性能较低
+    - random 库性能更好,但不适用于安全敏感场景
+    
+    使用建议:
+    - 对于普通随机化场景(如随机表情),使用默认的 random 库
+    - 对于安全敏感场景(如生成令牌),建议启用 secrets 库
     """

Line range hint 17-24: 建议优化条件判断结构

多个方法中重复出现 if cls.use_secrets 判断,可以考虑使用装饰器模式优化。

建议添加装饰器来处理条件判断:

def use_secure_random(func):
    def wrapper(cls, *args, **kwargs):
        if cls.use_secrets:
            # 调用 secrets 版本的实现
            return func(cls, *args, **kwargs)
        else:
            # 调用 random 版本的实现
            return getattr(random, func.__name__)(*args, **kwargs)
    return wrapper

class Random:
    @classmethod
    @use_secure_random
    def random(cls) -> float:
        return secrets.randbelow(INF) / INF

    @classmethod
    @use_secure_random
    def randint(cls, a: int, b: int) -> int:
        return secrets.randbelow(b - a + 1) + a

Also applies to: 27-34, 36-43

core/utils/image_table.py (2)

21-26: 文档字符串格式需要改进

建议使用更规范的文档字符串格式,包括返回值说明和类的整体描述。

建议修改为:

-    '''
-    图片表格。
-    :param data: 表格内容,表格行数需与表格标头的数量相符。
-    :param headers: 表格表头。
-    '''
+    '''图片表格类,用于生成和管理表格数据。
+
+    Args:
+        data: 表格内容,表格行数需与表格标头的数量相符
+        headers: 表格表头
+
+    Attributes:
+        data: 表格内容
+        headers: 表格表头
+    '''

Line range hint 111-116: 建议增强错误处理机制

当前的错误处理过于简单,建议添加更详细的错误信息和重试机制。

建议在 try-except 块中添加:

  1. 具体的错误类型处理
  2. 重试机制
  3. 详细的错误日志
MAX_RETRIES = 3
retry_count = 0
while retry_count < MAX_RETRIES:
    try:
        # 现有的图片处理代码
        break
    except (IOError, ValueError) as e:
        retry_count += 1
        Logger.warning(f"处理图片时发生错误 (尝试 {retry_count}/{MAX_RETRIES}): {str(e)}")
        if retry_count == MAX_RETRIES:
            Logger.error("图片处理失败,已达到最大重试次数")
            raise
core/utils/game.py (2)

8-8: 建议优化状态管理机制

当前使用全局变量 _ps_lst 管理状态可能会导致线程安全问题。建议考虑使用更安全的状态管理方式。

建议考虑:

  1. 使用 Redis 等外部存储管理状态
  2. 实现状态管理类来封装状态操作
  3. 添加线程安全机制

示例实现:

from threading import Lock

class GameStateManager:
    def __init__(self):
        self._states = defaultdict(lambda: defaultdict(dict))
        self._lock = Lock()
    
    def get_state(self, target_id, sender_id, game):
        with self._lock:
            return self._states[target_id][sender_id].get(game)

Also applies to: 76-76


Line range hint 93-104: 建议改进过期检查逻辑

当前的过期检查逻辑可以更加灵活和可配置。

建议修改为:

+    @property
+    def _is_expired(self) -> bool:
+        """检查游戏状态是否已过期"""
+        ts = self.get('_timestamp') or 0.0
+        return datetime.now().timestamp() - ts >= GAME_EXPIRED
+
     def check(self) -> bool:
         '''
         检查游戏事件状态,若超过时间则自动关闭。
         '''
         if self.target_id not in _ps_lst:
             return False
-        target_dict = _ps_lst[self.target_id]
-        if self.all:
-            status = target_dict.get(self.game, {}).get('_status', False)
-            ts = target_dict.get(self.game, {}).get('_timestamp', 0.0)
-        else:
-            sender_dict = target_dict.get(self.sender_id, {})
-            status = sender_dict.get(self.game, {}).get('_status', False)
-            ts = sender_dict.get(self.game, {}).get('_timestamp', 0.0)
-        if datetime.now().timestamp() - ts >= GAME_EXPIRED:
+        status = self.get('_status') or False
+        if self._is_expired:
             self.disable(_auto=True)
         return status
core/builtins/__init__.py (2)

41-44: 建议优化参数传递方式

当前的参数传递方式较为冗长,可以使用字典解包来简化代码。

建议修改为:

-        await target.send_direct_message(message_chain=msg,
-                                         disable_secret_check=disable_secret_check,
-                                         enable_parse_message=enable_parse_message,
-                                         enable_split_image=enable_split_image)
+        params = {
+            'message_chain': msg,
+            'disable_secret_check': disable_secret_check,
+            'enable_parse_message': enable_parse_message,
+            'enable_split_image': enable_split_image
+        }
+        await target.send_direct_message(**params)

Line range hint 108-116: 建议增强错误处理

FetchedSession 初始化时,建议添加参数验证和更详细的错误处理。

建议添加:

def __init__(self,
             target_from: str,
             target_id: Union[int, str],
             sender_from: Optional[str] = None,
             sender_id: Optional[Union[int, str]] = None):
    if not target_from:
        raise ValueError("target_from 不能为空")
    if not target_id:
        raise ValueError("target_id 不能为空")
    
    sender_from = sender_from or target_from
    sender_id = sender_id or target_id
    
    try:
        self.target = MsgInfo(...)
    except Exception as e:
        Logger.error(f"初始化 MsgInfo 失败: {str(e)}")
        raise
🧰 Tools
🪛 Ruff (0.8.2)

24-24: ExecutionLockList may be undefined, or defined from star imports

(F405)


26-26: Temp may be undefined, or defined from star imports

(F405)

modules/wikilog/dbutils.py (2)

126-126: 需要重构的代码注释应该更具体

注释"will rewrite it in the future"过于模糊,建议添加具体的重构原因和计划。

建议修改为:

-def set_keep_alive(self, apilink: str, keep_alive: bool):  # oh no it smells shit, will rewrite it in the future
+def set_keep_alive(self, apilink: str, keep_alive: bool):  # TODO: 重构计划 - 将 keep_alive 相关逻辑抽象到独立的类中,提高代码复用性

140-141: 建议优化空值检查逻辑

当前的检查方式需要两次字典访问,可以使用 dict.get() 方法优化。

建议修改为:

-        if apilink in infos and 'keep_alive' in infos[apilink]:
-            return infos[apilink]['keep_alive']
+        return infos.get(apilink, {}).get('keep_alive', False)
core/utils/i18n.py (1)

118-121: 建议完善类文档说明

当前的文档说明过于简单,建议添加更详细的信息。

建议修改为:

-    """
-    创建一个本地化对象。
-    """
+    """
+    创建一个本地化对象,用于处理多语言翻译和本地化。
+    
+    属性:
+        locale (str): 当前使用的语言代码
+        fallback_lng (List[str]): 备选语言列表,当主要语言无法找到翻译时使用
+    
+    示例:
+        >>> locale = Locale('zh_cn')
+        >>> locale.t('message.hello')
+        '你好'
+    """
core/component.py (1)

187-202: 代码逻辑正确,但建议改进参数对齐

参数对齐存在不一致,建议统一使用相同的缩进级别以提高可读性。

 module = Module.assign(alias=alias,
-                           bind_prefix=bind_prefix,
-                           desc=desc,
-                           recommend_modules=recommend_modules,
-                           developers=developers,
-                           base=base,
-                           doc=doc,
-                           hidden=hidden,
-                           load=load,
-                           rss=rss,
-                           required_admin=required_admin,
-                           required_superuser=required_superuser,
-                           required_base_superuser=required_base_superuser,
-                           available_for=available_for,
-                           exclude_from=exclude_from,
-                           support_languages=support_languages)
+                      bind_prefix=bind_prefix,
+                      desc=desc,
+                      recommend_modules=recommend_modules,
+                      developers=developers,
+                      base=base,
+                      doc=doc,
+                      hidden=hidden,
+                      load=load,
+                      rss=rss,
+                      required_admin=required_admin,
+                      required_superuser=required_superuser,
+                      required_base_superuser=required_base_superuser,
+                      available_for=available_for,
+                      exclude_from=exclude_from,
+                      support_languages=support_languages)
core/config/__init__.py (1)

352-352: 建议改进超时处理机制

当前的超时处理直接抛出异常,建议添加重试机制或更优雅的错误恢复方式。

@classmethod
def wait(cls, _lock):
    count = 0
+   max_retries = 3
+   retry_count = 0
    while _lock:
        count += 1
        sleep(1)
        if count > 5:
+           if retry_count < max_retries:
+               count = 0
+               retry_count += 1
+               continue
            ConfigOperationError('Operation timeout.')
modules/wiki/wiki.py (1)

158-164: 建议优化模板和 MediaWiki 页面的处理逻辑

当前的实现方式直接在字符串前添加前缀,建议将这个逻辑抽象为独立的函数以提高可维护性。

+def get_page_title(title: str, is_template: bool = False, is_mediawiki: bool = False) -> str:
+    if is_template:
+        return f'Template:{title}'
+    if is_mediawiki:
+        return f'MediaWiki:{title}'
+    return title

-if template:
-    rd = f'Template:{rd}'
-if mediawiki:
-    rd = f'MediaWiki:{rd}'
+rd = get_page_title(rd, template, mediawiki)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1646358 and 79b8f4b.

⛔ Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (47)
  • .gitignore (0 hunks)
  • bot.py (4 hunks)
  • bots/qqbot/message.py (3 hunks)
  • core/builtins/__init__.py (2 hunks)
  • core/builtins/message/__init__.py (15 hunks)
  • core/builtins/message/chain.py (3 hunks)
  • core/builtins/message/elements.py (13 hunks)
  • core/component.py (1 hunks)
  • core/config/__init__.py (3 hunks)
  • core/constants/info.py (1 hunks)
  • core/joke.py (1 hunks)
  • core/parser/message.py (1 hunks)
  • core/types/message/__init__.py (1 hunks)
  • core/types/module/__init__.py (1 hunks)
  • core/types/module/component_matches.py (3 hunks)
  • core/types/module/component_meta.py (1 hunks)
  • core/types/module/utils.py (1 hunks)
  • core/utils/cooldown.py (1 hunks)
  • core/utils/game.py (6 hunks)
  • core/utils/http.py (3 hunks)
  • core/utils/i18n.py (1 hunks)
  • core/utils/image_table.py (4 hunks)
  • core/utils/random.py (1 hunks)
  • core/utils/text.py (3 hunks)
  • core/utils/web_render.py (3 hunks)
  • modules/cytoid/profile.py (1 hunks)
  • modules/cytoid/rating.py (3 hunks)
  • modules/cytoid/utils.py (1 hunks)
  • modules/maimai/libraries/chunithm_apidata.py (1 hunks)
  • modules/maimai/libraries/maimaidx_apidata.py (6 hunks)
  • modules/maimai/libraries/maimaidx_best50.py (3 hunks)
  • modules/maimai/libraries/maimaidx_music.py (1 hunks)
  • modules/meme/moegirl.py (2 hunks)
  • modules/wiki/audit.py (1 hunks)
  • modules/wiki/inline.py (1 hunks)
  • modules/wiki/search.py (1 hunks)
  • modules/wiki/utils/mapping.py (1 hunks)
  • modules/wiki/utils/screenshot_image.py (1 hunks)
  • modules/wiki/utils/wikilib.py (5 hunks)
  • modules/wiki/wiki.py (5 hunks)
  • modules/wikilog/__init__.py (3 hunks)
  • modules/wikilog/dbutils.py (2 hunks)
  • modules/wikilog/locales/zh_cn.json (1 hunks)
  • modules/wordle/__init__.py (1 hunks)
  • pyproject.toml (2 hunks)
  • requirements.txt (3 hunks)
  • schedulers/wiki_bot.py (1 hunks)
💤 Files with no reviewable changes (1)
  • .gitignore
✅ Files skipped from review due to trivial changes (1)
  • core/constants/info.py
🧰 Additional context used
🪛 Ruff (0.8.2)
modules/maimai/libraries/maimaidx_apidata.py

25-25: mai_cover_path may be undefined, or defined from star imports

(F405)


27-27: mai_cover_path may be undefined, or defined from star imports

(F405)


31-31: mai_cover_path may be undefined, or defined from star imports

(F405)


35-35: Config may be undefined, or defined from star imports

(F405)


59-59: mai_cover_path may be undefined, or defined from star imports

(F405)


63-63: mai_cover_path may be undefined, or defined from star imports

(F405)

modules/maimai/libraries/chunithm_apidata.py

25-25: os may be undefined, or defined from star imports

(F405)


26-26: os may be undefined, or defined from star imports

(F405)


27-27: os may be undefined, or defined from star imports

(F405)

bot.py

82-84: Use a single if statement instead of nested if statements

(SIM102)


83-84: Use a single if statement instead of nested if statements

(SIM102)

modules/cytoid/profile.py

11-11: Undefined name CytoidBindInfoManager

(F821)

core/types/module/component_matches.py

5-5: attrs.field imported but unused

Remove unused import: attrs.field

(F401)


11-11: ModuleMeta may be undefined, or defined from star imports

(F405)


24-24: CommandMeta may be undefined, or defined from star imports

(F405)


45-45: RegexMeta may be undefined, or defined from star imports

(F405)


66-66: ScheduleMeta may be undefined, or defined from star imports

(F405)


71-71: HookMeta may be undefined, or defined from star imports

(F405)

core/builtins/__init__.py

26-26: Temp may be undefined, or defined from star imports

(F405)

modules/wiki/wiki.py

267-267: Function definition does not bind loop variable forum_data

(B023)


268-268: Function definition does not bind loop variable forum_data

(B023)


268-268: Function definition does not bind loop variable r

(B023)

core/utils/web_render.py

2-2: typing.Union imported but unused

Remove unused import: typing.Union

(F401)


24-24: Use url if url else '' instead of '' if not url else url

Replace with url if url else ''

(SIM212)

bots/qqbot/message.py

27-27: target_guild_prefix may be undefined, or defined from star imports

(F405)


30-30: target_group_prefix may be undefined, or defined from star imports

(F405)

core/types/module/__init__.py

43-43: CommandMatches may be undefined, or defined from star imports

(F405)


43-43: CommandMatches may be undefined, or defined from star imports

(F405)


44-44: RegexMatches may be undefined, or defined from star imports

(F405)


44-44: RegexMatches may be undefined, or defined from star imports

(F405)


45-45: ScheduleMatches may be undefined, or defined from star imports

(F405)


45-45: ScheduleMatches may be undefined, or defined from star imports

(F405)


46-46: HookMatches may be undefined, or defined from star imports

(F405)


46-46: HookMatches may be undefined, or defined from star imports

(F405)

core/builtins/message/__init__.py

96-98: Use a single if statement instead of nested if statements

(SIM102)


169-169: MessageChain may be undefined, or defined from star imports

(F405)


208-208: MessageChain may be undefined, or defined from star imports

(F405)

🔇 Additional comments (44)
modules/wiki/utils/screenshot_image.py (1)

25-27: 验证元素列表的合并逻辑

当前的元素列表合并逻辑可能需要验证是否所有调用处都正确传递了兼容的元素格式。

✅ Verification successful

Let me gather more information about the element parameter usage and list handling.


元素列表合并逻辑验证完成,实现正确

根据代码分析,元素列表的合并逻辑是安全的:

  • infobox_elements 在 mapping.py 中定义为固定的 CSS 选择器列表
  • generate_screenshot_v2 函数中通过 copy() 创建新列表,避免修改原始列表
  • 仅在传入参数 element 为列表类型时才进行合并操作
  • 实际调用中(如 weekly/init.py)传入的确实是列表格式的 CSS 选择器
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 描述:检查 generate_screenshot_v2 函数的调用,验证 element 参数的使用方式

# 搜索函数调用
rg -A 2 "generate_screenshot_v2\(" --type py

# 搜索可能的元素列表定义
ast-grep --pattern 'element = $_'

Length of output: 2697


Script:

#!/bin/bash
# 检查 element 参数的定义和使用
rg -A 5 "infobox_elements" --type py

# 检查 generate_screenshot_v2 函数的完整实现
ast-grep --pattern 'async def generate_screenshot_v2($_) {
  $$$
}'

# 检查列表操作相关的代码
rg "elements_" --type py -A 2

Length of output: 4536

modules/maimai/libraries/maimaidx_music.py (1)

13-13: 类型注解改进

添加 Union[int, str] 类型提示增强了代码的类型安全性,使函数的参数要求更加明确。

core/types/module/component_meta.py (1)

19-31: 确认 CommandMeta 类属性的类型和默认值

CommandMeta 类中,属性如 functionhelp_docoptions_desc 等已定义。请确保所有属性的类型注解和默认值符合预期,避免在使用过程中出现类型错误。

core/utils/cooldown.py (1)

40-59: 验证 check 方法的逻辑正确性

check 方法中,使用了 _cd_lst 的新结构。由于 _cd_lstdefaultdict,可能永远不会出现 self.key 不在 _cd_lst 的情况。请验证逻辑是否符合预期,确保方法返回正确的冷却时间。

core/types/module/__init__.py (1)

10-10: 🛠️ Refactor suggestion

避免使用通配符导入

在第 10 行,使用了 from .component_matches import *。通配符导入可能导致命名空间污染,降低代码的可读性。建议明确导入所需的类。

应用以下修改:

- from .component_matches import *
+ from .component_matches import CommandMatches, RegexMatches, ScheduleMatches, HookMatches

Likely invalid or redundant comment.

bot.py (1)

88-99: 确保子进程在终止前正确关闭

multiprocess_run_until_complete 函数中,第 88-99 行可能存在子进程资源未正确释放的问题。建议在调用 p.terminate()p.join() 后,再调用 p.close(),以确保子进程资源完全释放。

modules/wikilog/__init__.py (2)

5-5: 导入 I18NContext 支持国际化

在第5行加入了对 I18NContext 的导入,为后续代码中的国际化消息处理提供了支持。


247-266: 注意 walrus 运算符的兼容性

第261行使用了 walrus 运算符 :=,该特性在 Python 3.8 及以上版本才支持。请确保运行环境满足版本要求,或者避免使用该语法以提高兼容性。

modules/wiki/inline.py (1)

204-204: 删除未使用的参数,提高代码简洁性

在第204行的 _callback 函数中,参数 forum_dataget_page 已被移除。这是一个很好的改进,使函数签名更加清晰,避免了未使用参数的干扰。

bots/qqbot/message.py (2)

25-34: 简化异常处理逻辑

在第25-34行的 delete 方法中,将异常捕获的范围扩大到了整个删除过程。这是一个良好的实践,可以捕获在群组和频道消息删除过程中可能发生的所有异常,并统一进行日志记录。

🧰 Tools
🪛 Ruff (0.8.2)

27-27: target_guild_prefix may be undefined, or defined from star imports

(F405)


30-30: target_group_prefix may be undefined, or defined from star imports

(F405)


232-233: 确保在非频道或私信的情况下返回适当的值

在第232-233行,增加了在目标不是频道或私信时,delete 方法显式返回 False。这提高了代码的可读性和可维护性,确保方法的返回值始终明确。

core/builtins/message/__init__.py (10)

1-2: 确保兼容未来特性

在第1-2行,添加了 from __future__ import annotations。这使得类型注解可以使用前向引用,对提升代码的可读性和未来兼容性非常有帮助。


21-23: 添加类的文档字符串

在第21-23行,为 ExecutionLockList 类添加了文档字符串。这有助于其他开发者理解类的用途和功能,提升代码的可维护性。


48-50: 添加类的文档字符串

在第48-50行,为 MessageTaskManager 类添加了文档字符串。这有助于明确类的作用,方便后续的维护和协作。


313-314: 改进类型注解,提高参数灵活性

在第313行,将参数 timeout 的类型从 int 修改为 Optional[float],允许传入更灵活的超时时间值。这提升了方法的适用性。


356-357: 改进类型注解,提高参数灵活性

在第356-357行,将 timeout 的类型从 int 修改为 Optional[float],使方法支持更灵活的超时时间设置,增强了代码的健壮性。


395-397: 添加缺失的参数类型注解

在第395-397行,为 wait_reply 方法的参数添加了类型注解,这有助于提高代码的可读性和可维护性。


436-437: 改进类型注解,提高参数灵活性

在第436行,将 timeout 的类型从 int 修改为 Optional[float],允许更灵活地设置超时时间。这提高了方法的适用性。


555-557: 添加类的文档字符串

在第555-557行,为 FetchedSession 类添加了文档字符串,有助于明确类的功能,方便他人理解和维护。


598-600: 添加类的文档字符串

在第598-600行,为 FetchTarget 类添加了文档字符串,提升了代码的可读性和可维护性。


96-98: 避免重复导入

根据静态分析提示,在第96-98行使用了变量 get_,请确保在作用域内未重复导入或定义,避免可能的变量覆盖或冲突。

🧰 Tools
🪛 Ruff (0.8.2)

96-98: Use a single if statement instead of nested if statements

(SIM102)

core/parser/message.py (1)

Line range hint 8-10: 修改 temp_ban_check 函数的参数以提高代码一致性

temp_ban_check 函数新增了 command 参数,但在函数内部并未使用该参数。建议检查参数的必要性,确保函数签名与实际使用一致。

请运行以下脚本以验证 temp_ban_check 函数中的 command 参数是否被使用:

schedulers/wiki_bot.py (1)

15-15: 验证 keepalive 钩子的实现和错误处理

新增的 keepalive 钩子调用需要确保具备适当的错误处理机制,以防止因连接问题影响整个登录流程。

运行以下脚本以验证 keepalive 钩子的实现:

✅ Verification successful

keepalive 钩子已正确实现并具备错误处理机制

从代码分析可以看出:

  • keepalive 钩子在 modules/wikilog/__init__.py 中有完整实现,包含了适当的错误处理
  • 错误处理使用了 try-except 机制,并通过 Logger 记录错误
  • 钩子会向用户发送登录状态反馈,包括成功和失败的情况
  • wiki_bot.py 中的调用位置合理,紧跟在登录操作之后
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 描述:检查 wikilog.keepalive 钩子的实现和错误处理

# 搜索 keepalive 钩子的实现
ast-grep --pattern 'async def keepalive($$$)'

# 搜索相关的错误处理代码
rg -A 5 'try:.*wikilog\.keepalive'

Length of output: 84


Script:

#!/bin/bash
# 搜索 wikilog.keepalive 相关的代码实现
rg -g '!{dist,build}/*' -l "wikilog.keepalive"

# 搜索包含 keepalive 的 Python 文件
fd -e py -x grep -l "keepalive"

# 搜索 JobQueue 的 trigger_hook_all 方法实现
ast-grep --pattern 'async def trigger_hook_all($$$)'

# 搜索 try-except 相关的错误处理
rg "try:.*trigger_hook_all.*wikilog"

Length of output: 250


Script:

#!/bin/bash
# 检查 wikilog 模块中的 keepalive 相关实现
cat modules/wikilog/__init__.py

# 检查 JobQueue 的错误处理机制
rg -A 5 "class JobQueue"
rg -A 5 "trigger_hook_all"

# 检查 wiki_bot.py 中的错误处理
cat schedulers/wiki_bot.py

Length of output: 24599

core/types/message/__init__.py (1)

10-11: ⚠️ Potential issue

注意:这是一个破坏性更改

target_idsender_id 的类型从 Union[int, str] 更改为 str 是一个破坏性更改,可能会影响现有代码。

运行以下脚本以检查可能受影响的代码:

建议:

  1. 考虑添加类型转换逻辑以保持向后兼容
  2. 更新相关文档以说明此更改
  3. 确保所有调用方都进行了相应的更新
✅ Verification successful

target_id 和 sender_id 的类型更改是安全的

根据代码库分析,所有的 target_idsender_id 都是以字符串形式使用的,主要用于以下模式:

  • 格式为 {prefix}|{id} 的字符串标识符
  • 通过正则表达式进行匹配和提取
  • 作为字典键值使用

没有发现任何将整数直接赋值给这些字段的情况,所有的 ID 都是以字符串形式构造和处理的。

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 描述:检查使用 MsgInfo.target_id 和 sender_id 的代码

# 搜索可能受影响的代码
rg -A 3 'target_id.*=.*\d+|sender_id.*=.*\d+'

# 搜索类型注解
ast-grep --pattern 'MsgInfo($$$)'

Length of output: 17375

core/joke.py (1)

8-12: 代码结构优化得当!

将愚人节检查逻辑抽离成独立函数是个很好的改进,提高了代码的可重用性和可维护性。

pyproject.toml (2)

Line range hint 55-65: 建议为依赖项添加版本锁定

直接使用 Git 仓库作为依赖源可能导致不可预期的更新。建议指定具体的提交哈希或标签。

建议修改为:

-langconv = {git = "https://github.com/OasisAkari/langconv.py.git"}
+langconv = {git = "https://github.com/OasisAkari/langconv.py.git", rev = "specific-commit-hash"}

运行以下脚本检查最新的稳定版本:

✅ Verification successful

建议将 langconv 依赖锁定到特定提交

建议修改为:

-langconv = {git = "https://github.com/OasisAkari/langconv.py.git"}
+langconv = {git = "https://github.com/OasisAkari/langconv.py.git", rev = "975ee0896096b63148f0930db762607a5a2113df"}

该仓库没有发布版本标签,但我们可以锁定到最新的稳定提交以确保依赖的一致性和可重现性。

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 检查 langconv 仓库的最新发布版本和提交
gh api repos/OasisAkari/langconv.py/releases/latest --jq '.tag_name'
gh api repos/OasisAkari/langconv.py/commits/main --jq '.sha'

Length of output: 467


Script:

#!/bin/bash
# Check if the repository exists and get its default branch
gh api repos/OasisAkari/langconv.py --jq '.default_branch'

# List all branches
gh api repos/OasisAkari/langconv.py/branches --jq '.[].name'

# Get the latest commit from master branch (alternative to main)
gh api repos/OasisAkari/langconv.py/commits/master --jq '.sha'

Length of output: 239


Line range hint 65-74: 验证依赖兼容性

新添加的依赖项 botpy 和更新的版本可能会影响其他包的兼容性。

运行以下脚本检查潜在的依赖冲突:

✅ Verification successful

依赖项兼容性验证结果

根据代码分析,botpyqq-botpy 的使用是相互独立的:

  • botpy 仅在 bots/qqbot/ 目录下使用,主要用于消息处理
  • 代码中没有发现 qq-botpy 的直接使用

由于:

  1. 这两个包的功能域是分离的
  2. 其他依赖项都指定了明确的版本约束
  3. Python 版本要求是 ^3.12.0,这是一个现代的稳定版本

这些依赖项的组合不太可能产生冲突。

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 检查依赖关系和版本约束
poetry check
poetry show --tree

Length of output: 229


Script:

#!/bin/bash
# 检查pyproject.toml中的依赖项
cat pyproject.toml | grep -A 20 "\[tool.poetry.dependencies\]"

# 搜索代码库中使用botpy的地方
rg "import botpy" -A 2
rg "from botpy" -A 2

# 搜索代码库中使用qq-botpy的地方
rg "import qq_botpy" -A 2
rg "from qq_botpy" -A 2

Length of output: 1154

core/builtins/__init__.py (1)

26-26: 需要验证 Temp 的导入

根据静态分析提示,Temp 可能未定义或来自星号导入。建议明确导入路径。

✅ Verification successful

Temp 的导入和使用是正确的

core/builtins/__init__.py 中通过 from .temp import * 导入了 Temp 类,该类定义在 core/builtins/temp/__init__.py 中。这是一个有效的相对导入,代码结构清晰。赋值语句 Temp = Temp 不会造成问题。

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 查找 Temp 的定义位置
rg -l "class Temp"

Length of output: 94


Script:

#!/bin/bash
# 检查 core/builtins/__init__.py 中的导入语句
rg -A 5 "^from|^import" core/builtins/__init__.py

# 检查 core/builtins/temp/__init__.py 中的 Temp 类定义
cat core/builtins/temp/__init__.py

Length of output: 689

🧰 Tools
🪛 Ruff (0.8.2)

26-26: Temp may be undefined, or defined from star imports

(F405)

modules/wikilog/dbutils.py (1)

34-38: 配置初始化逻辑更新

配置初始化逻辑清晰,使用 setdefault 确保了配置项的一致性。

modules/wiki/audit.py (1)

125-125: 消息发送逻辑位置调整合理

将消息发送逻辑移动到 if 块内,确保了只有在有内容时才发送消息,避免了发送空消息的情况。

core/utils/http.py (3)

47-47: 改进了类型安全性和默认值的明确性

  1. 将 status_code 的默认值从 False 改为 200 更符合 HTTP 成功状态码的语义
  2. 将 timeout 的类型从 int 改为 Optional[float] 提供了更灵活的超时控制

这些改动都是正确的改进。

Also applies to: 51-51


116-116: post_url 函数参数改进与 get_url 保持一致

参数的改进与 get_url 函数保持了一致性,这是一个好的实践。

Also applies to: 119-119


185-185: download 函数参数改进与其他 HTTP 函数保持一致

参数的改进与其他 HTTP 函数保持了一致性,这是一个好的实践。

Also applies to: 189-189

core/builtins/message/elements.py (3)

24-24: 新增 deepcopy 导入以支持对象深拷贝

良好的改进,通过深拷贝确保返回的实例是独立的,防止意外修改。


48-49: PlainElement.assign 方法的参数和返回值改进

  • 参数类型从 Tuple[str] 改为 *texts: Any,提供了更灵活的参数传递方式
  • 返回深拷贝实例而不是直接返回实例,确保了返回值的不可变性

Also applies to: 57-57


105-105: MessageSession 类型引用更新

将字符串字面量类型提示替换为实际类型引用,提高了代码的类型安全性。

Also applies to: 151-151

modules/cytoid/rating.py (2)

Line range hint 31-36: 用户查找错误处理优化

改进了用户不存在时的错误处理,返回更友好的错误消息。

🧰 Tools
🪛 Ruff (0.8.2)

33-34: Use a single if statement instead of nested if statements

(SIM102)


238-244: 头像下载功能优化

使用新的 download 函数简化了头像下载逻辑,提高了代码可维护性。

core/builtins/message/chain.py (2)

1-2: 启用延迟类型注解评估

添加 from __future__ import annotations 是一个好的实践,可以避免前向引用问题。


37-37: 类型提示改进

将字符串字面量类型替换为实际类型引用(MessageChain 和 MessageSession),提高了代码的类型安全性和可维护性。

Also applies to: 151-151

requirements.txt (2)

16-16: 版本更新提醒

attrs 包已更新到最新版本 24.2.0,这是一个正向的版本升级。


79-80: 请验证依赖项的兼容性

  1. langconv 包现在直接从 GitHub 仓库安装,这可能会影响构建的稳定性。
  2. langsmith 包版本从 0.1.140 降级到 0.1.139,这是一个回退操作。
core/config/__init__.py (1)

12-14: 导入语句组织合理

导入语句的组织结构清晰,遵循了 Python 的导入规范。

modules/wiki/wiki.py (1)

60-70: 函数签名优化合理

参数重新排序和类型注解的更新提高了代码的可读性和类型安全性。

Comment on lines +110 to +112
dir = os.path.join(cache_path, 'maimai-record')
os.makedirs(dir, exist_ok=True)
cache_dir = os.path.join(dir, f"{msg.target.sender_id.replace('|', '_')}_maimaidx_record.json")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

缓存目录结构统一化

缓存目录结构的统一化改进很好,但建议:

  1. 将目录创建逻辑封装成工具函数
  2. 添加目录清理机制,避免缓存无限增长
+ def ensure_record_cache_dir() -> str:
+     dir = os.path.join(cache_path, 'maimai-record')
+     os.makedirs(dir, exist_ok=True)
+     return dir

- dir = os.path.join(cache_path, 'maimai-record')
- os.makedirs(dir, exist_ok=True)
+ dir = ensure_record_cache_dir()

Also applies to: 153-155, 200-202, 246-248

Comment on lines +198 to +200
pngPath = os.path.join(mai_cover_path, f'{chartInfo.idNum}.png')
if not os.path.exists(pngPath):
pngPath = os.path.join(mai_cover_path, '01000.png')
pngPath = os.path.join(mai_cover_path, '0.png')
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议重构重复的图片处理逻辑

当前代码中 SD 和 DX 榜单的图片处理逻辑完全相同,建议提取为独立方法以提高代码可维护性。

建议添加以下辅助方法:

class DrawBest:
+    def _get_cover_image(self, chart_info: ChartInfo) -> Image.Image:
+        """获取并处理歌曲封面图片"""
+        itemW = 150
+        itemH = 100
+        pngPath = os.path.join(mai_cover_path, f'{chart_info.idNum}.png')
+        if not os.path.exists(pngPath):
+            pngPath = os.path.join(mai_cover_path, '0.png')
+
+        if os.path.exists(pngPath):
+            temp = Image.open(pngPath).convert('RGB')
+            temp = self._resizePic(temp, itemW / temp.size[0])
+            temp = temp.crop((0, (temp.size[1] - itemH) / 2, itemW, (temp.size[1] + itemH) / 2))
+            temp = temp.filter(ImageFilter.GaussianBlur(2))
+            temp = temp.point(lambda p: int(p * 0.72))
+        else:
+            temp = Image.new('RGB', (int(itemW), int(itemH)), (111, 111, 111, 255))
+        return temp

然后在原代码中调用此方法:

-            pngPath = os.path.join(mai_cover_path, f'{chartInfo.idNum}.png')
-            if not os.path.exists(pngPath):
-                pngPath = os.path.join(mai_cover_path, '0.png')
-
-            if os.path.exists(pngPath):
-                temp = Image.open(pngPath).convert('RGB')
-                temp = self._resizePic(temp, itemW / temp.size[0])
-                temp = temp.crop((0, (temp.size[1] - itemH) / 2, itemW, (temp.size[1] + itemH) / 2))
-                temp = temp.filter(ImageFilter.GaussianBlur(2))
-                temp = temp.point(lambda p: int(p * 0.72))
-            else:
-                temp = Image.new('RGB', (int(itemW), int(itemH)), (111, 111, 111, 255))
+            temp = self._get_cover_image(chartInfo)

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +146 to +148
pngPath = os.path.join(mai_cover_path, f'{chartInfo.idNum}.png')
if not os.path.exists(pngPath):
pngPath = os.path.join(mai_cover_path, '01000.png')
pngPath = os.path.join(mai_cover_path, '0.png')
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

发现封面图片系统存在潜在问题

根据代码分析发现以下问题:

  1. 默认封面图片 0.png 不存在,但代码中多处依赖此文件作为后备方案
  2. maimaidx_apidata.py 中仍然使用 get_cover_len5_id 下载图片,但新的访问逻辑已改为直接使用 idNum
  3. 缺少图片下载失败时的错误处理机制

建议:

  • 确保添加默认的 0.png 封面图片到 mai_cover_path 目录
  • 更新 maimaidx_apidata.py 中的下载逻辑,使其与新的命名规范保持一致
  • 考虑添加图片缺失时的日志记录
🔗 Analysis chain

请验证封面图片的命名规范和默认图片

建议确认以下几点:

  1. 所有封面图片是否都已按照新的命名规范(直接使用 idNum)重命名
  2. 默认图片 0.png 是否已放置在正确位置
  3. 建议添加日志记录,以便跟踪缺失的封面图片

Let me gather more information about the cover path handling to provide a comprehensive verification.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# 检查封面图片的命名规范和默认图片

# 验证默认图片是否存在
if [ -f "$(fd '0.png' -t f)" ]; then
    echo "默认图片存在"
else
    echo "警告:默认图片不存在"
fi

# 检查所有封面图片的命名格式
rg -g '*.py' 'idNum.*\.png'

Length of output: 578


Script:

#!/bin/bash
# 检查封面图片相关的路径处理代码

# 查找 mai_cover_path 的定义和使用
rg -g '*.py' 'mai_cover_path' -B 2 -A 2

# 查找与图片路径相关的其他常量或配置
rg -g '*.py' 'COVER_PATH|cover_path|IMAGE_PATH|image_path'

# 检查是否有其他使用 .png 的地方
rg -g '*.py' '\.png'

Length of output: 10066


from core.builtins import MessageSession

_cd_lst: Dict[str, Dict[MessageSession, float]] = {}
_cd_lst = defaultdict(lambda: defaultdict(dict))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

注意 _cd_lst 的线程安全性

_cd_lst 更改为嵌套的 defaultdict 后,可能在多线程或异步环境下产生线程安全问题。建议在访问或修改 _cd_lst 时使用锁机制,确保线程安全。

Comment on lines +43 to +46
command_list: CommandMatches = CommandMatches.init()
regex_list: RegexMatches = RegexMatches.init()
schedule_list: ScheduleMatches = ScheduleMatches.init()
hooks_list: HookMatches = HookMatches.init()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

修复未定义的名称问题

在第 43-46 行,使用了 CommandMatchesRegexMatchesScheduleMatchesHookMatches,但可能未明确导入这些名称。确保这些类已正确导入,避免在运行时出现 NameError

🧰 Tools
🪛 Ruff (0.8.2)

43-43: CommandMatches may be undefined, or defined from star imports

(F405)


43-43: CommandMatches may be undefined, or defined from star imports

(F405)


44-44: RegexMatches may be undefined, or defined from star imports

(F405)


44-44: RegexMatches may be undefined, or defined from star imports

(F405)


45-45: ScheduleMatches may be undefined, or defined from star imports

(F405)


45-45: ScheduleMatches may be undefined, or defined from star imports

(F405)


46-46: HookMatches may be undefined, or defined from star imports

(F405)


46-46: HookMatches may be undefined, or defined from star imports

(F405)

Comment on lines +28 to +34
wait = await query_pages(QueryInfo.assign('https://mzh.moegirl.org.cn/api.php', headers={'accept': '*/*',
'accept-encoding': 'gzip, deflate',
'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,en-GB;q=0.6',
'content-type': 'application/json',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62'
},
locale=locale.locale), redirect)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议复用已提取的常量

这里的 headers 配置与上面的完全相同,应该复用之前定义的常量。

建议修改为:

-                wait = await query_pages(QueryInfo.assign('https://mzh.moegirl.org.cn/api.php', headers={'accept': '*/*',
-                                                                                                         'accept-encoding': 'gzip, deflate',
-                                                                                                         'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,en-GB;q=0.6',
-                                                                                                         'content-type': 'application/json',
-                                                                                                         'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62'
-                                                                                                         },
-                                                          locale=locale.locale), redirect)
+                wait = await query_pages(QueryInfo.assign(MOEGIRL_API_URL,
+                                                        headers=MOEGIRL_HEADERS,
+                                                        locale=locale.locale), 
+                                       redirect)

Committable suggestion skipped: line range outside the PR's diff.

@@ -70,7 +86,7 @@ async def image_table_render(table: Union[ImageTable, List[ImageTable]], save_so

try:
pic = await download(
webrender(use_local=use_local),
webrender(use_local=use_local, method=''),
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议优化 webrender 方法参数

向 webrender 传递空字符串作为 method 参数可能会导致混淆。建议移除该参数或使用有意义的值。

建议修改为:

-                webrender(use_local=use_local, method=''),
+                webrender(use_local=use_local),

同样的修改也应用于第100行。

Also applies to: 100-100

@@ -124,7 +124,7 @@ def format_board(self):
return '\n'.join(''.join(row) for row in formatted)

def is_game_over(self):
return bool(len(self.board) != 0 and self.word == self.board[-1])
return bool(len(self.board) != 0 and (self.word == self.board[-1]) or (len(self.board) > 5))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

游戏结束条件的逻辑错误

当前的实现存在以下问题:

  1. 括号位置不正确导致逻辑运算顺序错误
  2. 使用 len(self.board) > 5 可能导致游戏在第6次尝试后立即结束,而不给玩家最后一次机会

建议修改为:

-        return bool(len(self.board) != 0 and (self.word == self.board[-1]) or (len(self.board) > 5))
+        return bool(len(self.board) != 0 and (self.word == self.board[-1] or len(self.board) >= 6))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return bool(len(self.board) != 0 and (self.word == self.board[-1]) or (len(self.board) > 5))
return bool(len(self.board) != 0 and (self.word == self.board[-1] or len(self.board) >= 6))

Comment on lines +222 to +231
filename = 'thumbnail.png'
d = os.path.join(cache_path, 'cytoid-cover', uid)
os.makedirs(d, exist_ok=True)
path = os.path.join(d, 'thumbnail.png')
path = os.path.join(d, filename)
if not os.path.exists(path):
level_url = 'http://services.cytoid.io/levels/' + uid
level_url = f'http://services.cytoid.io/levels/{uid}'
get_level = json.loads(await get_url(level_url))
cover_thumbnail = get_level['cover']['original'] + "?h=240&w=384"
async with aiohttp.ClientSession() as session, session.get(cover_thumbnail) as resp, async_open(path, 'wb+') as jpg:
await jpg.write(await resp.read())
return path
else:
return path
except BaseException:
cover_thumbnail = f"{get_level['cover']['original']}?h=240&w=384"
path = await download(cover_thumbnail, filename=filename, path=d, logging_err_resp=False)
return path
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

下载封面缩略图功能重构

  • 使用 cache_path 替代 assets_path
  • 使用新的 download 函数简化下载逻辑
  • 改进了路径处理和错误处理

建议添加缓存过期处理机制,避免缓存文件无限增长:

+from datetime import datetime, timedelta
+import os.path

 async def download_cover_thumb(uid):
     try:
         filename = 'thumbnail.png'
         d = os.path.join(cache_path, 'cytoid-cover', uid)
         os.makedirs(d, exist_ok=True)
         path = os.path.join(d, filename)
-        if not os.path.exists(path):
+        if not os.path.exists(path) or \
+           datetime.fromtimestamp(os.path.getmtime(path)) < datetime.now() - timedelta(days=7):
             level_url = f'http://services.cytoid.io/levels/{uid}'
             get_level = json.loads(await get_url(level_url))
             cover_thumbnail = f"{get_level['cover']['original']}?h=240&w=384"
             path = await download(cover_thumbnail, filename=filename, path=d, logging_err_resp=False)
         return path
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
filename = 'thumbnail.png'
d = os.path.join(cache_path, 'cytoid-cover', uid)
os.makedirs(d, exist_ok=True)
path = os.path.join(d, 'thumbnail.png')
path = os.path.join(d, filename)
if not os.path.exists(path):
level_url = 'http://services.cytoid.io/levels/' + uid
level_url = f'http://services.cytoid.io/levels/{uid}'
get_level = json.loads(await get_url(level_url))
cover_thumbnail = get_level['cover']['original'] + "?h=240&w=384"
async with aiohttp.ClientSession() as session, session.get(cover_thumbnail) as resp, async_open(path, 'wb+') as jpg:
await jpg.write(await resp.read())
return path
else:
return path
except BaseException:
cover_thumbnail = f"{get_level['cover']['original']}?h=240&w=384"
path = await download(cover_thumbnail, filename=filename, path=d, logging_err_resp=False)
return path
from datetime import datetime, timedelta
import os.path
filename = 'thumbnail.png'
d = os.path.join(cache_path, 'cytoid-cover', uid)
os.makedirs(d, exist_ok=True)
path = os.path.join(d, filename)
if not os.path.exists(path) or \
datetime.fromtimestamp(os.path.getmtime(path)) < datetime.now() - timedelta(days=7):
level_url = f'http://services.cytoid.io/levels/{uid}'
get_level = json.loads(await get_url(level_url))
cover_thumbnail = f"{get_level['cover']['original']}?h=240&w=384"
path = await download(cover_thumbnail, filename=filename, path=d, logging_err_resp=False)
return path

Comment on lines +265 to +270
async def _callback(msg: Bot.MessageSession):
display = msg.as_display(text_only=True)
if isint(display) and int(display) <= len(forum_data) - 1:
await query_pages(session, title=forum_data[display]['text'], start_wiki_api=r.info.api)

await session.send_message(i_msg_lst, callback=_callback)
await session.send_message(i_msg_lst, callback=_callback)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

修复回调函数中的变量绑定问题

回调函数中存在循环变量绑定问题,这可能导致意外的行为。

建议修改为:

-async def _callback(msg: Bot.MessageSession):
-    display = msg.as_display(text_only=True)
-    if isint(display) and int(display) <= len(forum_data) - 1:
-        await query_pages(session, title=forum_data[display]['text'], start_wiki_api=r.info.api)
+async def create_callback(current_forum_data, current_r):
+    async def _callback(msg: Bot.MessageSession):
+        display = msg.as_display(text_only=True)
+        if isint(display) and int(display) <= len(current_forum_data) - 1:
+            await query_pages(session, title=current_forum_data[display]['text'], 
+                            start_wiki_api=current_r.info.api)
+    return _callback

await session.send_message(i_msg_lst, callback=await create_callback(forum_data, r))

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.8.2)

267-267: Function definition does not bind loop variable forum_data

(B023)


268-268: Function definition does not bind loop variable forum_data

(B023)


268-268: Function definition does not bind loop variable r

(B023)

@DoroWolf DoroWolf merged commit 2e90e67 into master Dec 12, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants