diff --git a/src/shared/__init__.py b/src/shared/__init__.py index 763c6ef..6b26a1f 100644 --- a/src/shared/__init__.py +++ b/src/shared/__init__.py @@ -1,5 +1,5 @@ """ -共享模块 - 通用工具和常量 +Mô-đun chia sẻ - Công cụ dùng chung và hằng số. """ from .constants import ContentType, Platform, ReportFormat, TaskStatus diff --git a/src/shared/constants.py b/src/shared/constants.py index 4b7249e..6b95a4b 100644 --- a/src/shared/constants.py +++ b/src/shared/constants.py @@ -1,5 +1,5 @@ """ -常量 - 插件中使用的共享常量 +Hằng số - Các hằng số dùng chung trong plugin. """ from enum import Enum @@ -7,9 +7,9 @@ from enum import Enum class Platform(str, Enum): """ - 支持的聊天平台枚举 + Liệt kê các nền tảng trò chuyện được hỗ trợ. - 定义了插件适配的所有基础通讯平台标识。 + Định nghĩa mã định danh của các nền tảng giao tiếp cơ bản mà plugin hỗ trợ. """ ONEBOT = "onebot" @@ -22,9 +22,10 @@ class Platform(str, Enum): class TaskStatus(str, Enum): """ - 分析任务执行状态枚举 + Liệt kê các trạng thái thực thi của tác vụ phân tích. - 用于在异步处理流水线中标记分析任务的生命阶段。 + Dùng để đánh dấu giai đoạn trong vòng đời của tác vụ phân tích + thuộc quy trình xử lý bất đồng bộ. """ PENDING = "pending" @@ -36,9 +37,10 @@ class TaskStatus(str, Enum): class ContentType(str, Enum): """ - 统一消息内容类型枚举 + Liệt kê các loại nội dung tin nhắn hợp nhất. - 将不同平台(OneBot, Discord 等)的消息片段抽象为统一的类型体系。 + Trừu tượng hóa các thành phần tin nhắn từ nhiều nền tảng + (OneBot, Discord, v.v.) thành một hệ thống kiểu thống nhất. """ TEXT = "text" @@ -55,9 +57,9 @@ class ContentType(str, Enum): class ReportFormat(str, Enum): """ - 分析报告导出格式枚举 + Liệt kê các định dạng xuất báo cáo phân tích. - 控制最终呈现给用户的报告呈现形式。 + Kiểm soát hình thức trình bày báo cáo cuối cùng cho người dùng. """ TEXT = "text" @@ -66,25 +68,25 @@ class ReportFormat(str, Enum): HTML = "html" -# 插件元数据 +# Siêu dữ liệu của plugin PLUGIN_NAME = "astrbot_plugin_qq_group_daily_analysis" PLUGIN_VERSION = "2.0.0" -# 平台标识符 +# Mã định danh nền tảng SUPPORTED_PLATFORMS = [ Platform.ONEBOT.value, Platform.TELEGRAM.value, Platform.DISCORD.value, ] -# 分析默认值 +# Giá trị phân tích mặc định DEFAULT_MAX_TOPICS = 5 DEFAULT_MAX_USER_TITLES = 10 DEFAULT_MAX_GOLDEN_QUOTES = 5 DEFAULT_MIN_MESSAGES = 50 DEFAULT_MAX_TOKENS = 2000 -# 时间段 +# Các khoảng thời gian HOUR_RANGES = { "morning": (6, 12), "afternoon": (12, 18), @@ -92,30 +94,30 @@ HOUR_RANGES = { "night": (0, 6), } -# 错误代码 +# Mã lỗi ERROR_INSUFFICIENT_DATA = "INSUFFICIENT_DATA" ERROR_LLM_FAILED = "LLM_FAILED" ERROR_PLATFORM_ERROR = "PLATFORM_ERROR" ERROR_CONFIG_ERROR = "CONFIG_ERROR" ERROR_TIMEOUT = "TIMEOUT" -# 缓存 TTL(秒) -CACHE_TTL_SHORT = 60 # 1 分钟 -CACHE_TTL_MEDIUM = 300 # 5 分钟 -CACHE_TTL_LONG = 3600 # 1 小时 -CACHE_TTL_DAY = 86400 # 24 小时 +# Thời gian tồn tại của bộ nhớ đệm (giây) +CACHE_TTL_SHORT = 60 # 1 phút +CACHE_TTL_MEDIUM = 300 # 5 phút +CACHE_TTL_LONG = 3600 # 1 giờ +CACHE_TTL_DAY = 86400 # 24 giờ -# 速率限制默认值 -RATE_LIMIT_LLM_CALLS = 10 # 每分钟调用次数 -RATE_LIMIT_API_CALLS = 60 # 每分钟调用次数 -RATE_LIMIT_BURST = 5 # 突发大小 +# Giá trị giới hạn tốc độ mặc định +RATE_LIMIT_LLM_CALLS = 10 # Số lượt gọi mỗi phút +RATE_LIMIT_API_CALLS = 60 # Số lượt gọi mỗi phút +RATE_LIMIT_BURST = 5 # Quy mô gọi đột biến -# 重试默认值 +# Giá trị thử lại mặc định RETRY_MAX_ATTEMPTS = 3 RETRY_BASE_DELAY = 1.0 RETRY_MAX_DELAY = 30.0 -# 文件路径 +# Đường dẫn tệp HISTORY_DIR = "history" CACHE_DIR = "cache" TEMP_DIR = "temp" diff --git a/src/shared/trace_context.py b/src/shared/trace_context.py index f096b45..4fce98f 100644 --- a/src/shared/trace_context.py +++ b/src/shared/trace_context.py @@ -1,7 +1,7 @@ """ -追踪上下文 - 请求追踪和关联 +Ngữ cảnh truy vết - Theo dõi và liên kết yêu cầu. -提供用于在插件中跟踪请求的上下文。 +Cung cấp ngữ cảnh dùng để theo dõi yêu cầu trong plugin. """ import functools @@ -13,14 +13,14 @@ from dataclasses import dataclass, field from datetime import datetime from typing import Any, Optional -# Trace ID 中群名的最大长度(平衡可读性和日志宽度) +# Độ dài tối đa của tên nhóm trong Trace ID (cân bằng khả năng đọc và độ rộng nhật ký) _MAX_GROUP_NAME_LEN = 10 -# 用于匹配报告 Caption 中去重 Token 的正则模式 -# 格式: "| MM-DD HH:MM:SS" +# Mẫu biểu thức chính quy dùng để khớp token khử trùng lặp trong Caption báo cáo +# Định dạng: "| MM-DD HH:MM:SS" REPORT_CAPTION_PATTERN = re.compile(r"\| (\d{2}-\d{2} \d{2}:\d{2}:\d{2})") -# 当前追踪的上下文变量 +# Biến ngữ cảnh đang được truy vết _current_trace: ContextVar[Optional["TraceContext"]] = ContextVar( "current_trace", default=None ) @@ -29,18 +29,20 @@ _current_trace: ContextVar[Optional["TraceContext"]] = ContextVar( @dataclass class TraceContext: """ - 核心组件:全链路追踪上下文 (Tracing Context) + Thành phần cốt lõi: ngữ cảnh truy vết toàn bộ chuỗi (Tracing Context). - 该组件用于在复杂的异步分析流程中关联日志、耗时统计及元数据。 - 它不仅提供了 TraceId 的生成与传递,还集成了毫秒级的性能打点(Checkpoint)功能。 + Thành phần này liên kết nhật ký, thống kê thời gian thực thi và siêu dữ liệu + trong các quy trình phân tích bất đồng bộ phức tạp. + Ngoài khả năng tạo và truyền Trace ID, thành phần còn tích hợp chức năng + ghi nhận hiệu năng ở độ chính xác mili giây (Checkpoint). Attributes: - trace_id (str): 链路唯一标识码,默认为 UUID 前 8 位 - group_id (str): 当前关联的群组 ID - platform (str): 当前消息所属平台 - operation (str): 当前执行的操作名称 (如 'DAILY_ANALYSIS') - start_time (datetime): 追踪开始的具体时刻 - metadata (dict[str, Any]): 随链路传递的额外上下文数据 + trace_id (str): Mã định danh duy nhất của chuỗi, mặc định là 8 ký tự đầu của UUID. + group_id (str): ID của nhóm đang được liên kết. + platform (str): Nền tảng của tin nhắn hiện tại. + operation (str): Tên thao tác đang thực hiện, ví dụ: 'DAILY_ANALYSIS'. + start_time (datetime): Thời điểm cụ thể bắt đầu truy vết. + metadata (dict[str, Any]): Dữ liệu ngữ cảnh bổ sung được truyền theo chuỗi. """ trace_id: str = field(default_factory=lambda: str(uuid.uuid4())[:8]) @@ -50,27 +52,28 @@ class TraceContext: start_time: datetime = field(default_factory=datetime.now) metadata: dict[str, Any] = field(default_factory=dict) - # 内部计时器,用于多阶段耗时分析 + # Bộ hẹn giờ nội bộ, dùng để phân tích thời gian thực thi qua nhiều giai đoạn _checkpoints: dict[str, datetime] = field(default_factory=dict, init=False) def checkpoint(self, name: str) -> None: """ - 在当前时间轴上设置一个命名锚点(打点)。 + Đặt một mốc có tên trên trục thời gian hiện tại. Args: - name (str): 锚点标识符,如 'LLM_REPLY_RECEIVED' + name (str): Mã định danh của mốc, ví dụ: 'LLM_REPLY_RECEIVED'. """ self._checkpoints[name] = datetime.now() def elapsed_ms(self, from_checkpoint: str | None = None) -> float: """ - 计算从开始或指定锚点到当前时刻经过的毫秒数。 + Tính số mili giây đã trôi qua từ lúc bắt đầu hoặc từ một mốc chỉ định đến hiện tại. Args: - from_checkpoint (str, optional): 起始锚点名称。若为 None 则从链路启动时算起。 + from_checkpoint (str, optional): Tên mốc bắt đầu. Nếu là None, + tính từ thời điểm chuỗi được khởi tạo. Returns: - float: 经过的毫秒数 + float: Số mili giây đã trôi qua. """ start = self.start_time if from_checkpoint and from_checkpoint in self._checkpoints: @@ -81,10 +84,11 @@ class TraceContext: def to_dict(self) -> dict[str, Any]: """ - 将链路快照序列化为字典格式,便于持久化或 JSON 日志输出。 + Tuần tự hóa ảnh chụp trạng thái của chuỗi thành dạng từ điển, + thuận tiện cho việc lưu trữ hoặc xuất nhật ký JSON. Returns: - dict[str, Any]: 序列化后的追踪状态 + dict[str, Any]: Trạng thái truy vết sau khi tuần tự hóa. """ return { "trace_id": self.trace_id, @@ -100,12 +104,12 @@ class TraceContext: _token: Token | None = field(default=None, init=False, repr=False) def __enter__(self) -> "TraceContext": - """进入上下文管理器,将当前实例绑定到当前协程上下文。""" + """Bước vào trình quản lý ngữ cảnh và liên kết instance hiện tại với ngữ cảnh coroutine.""" self._token = _current_trace.set(self) return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: - """退出上下文管理器,清理绑定状态。""" + """Thoát khỏi trình quản lý ngữ cảnh và dọn dẹp trạng thái liên kết.""" if self._token: _current_trace.reset(self._token) self._token = None @@ -113,10 +117,11 @@ class TraceContext: @classmethod def current(cls) -> Optional["TraceContext"]: """ - 静态获取当前协程活跃的追踪上下文。 + Lấy ngữ cảnh truy vết đang hoạt động trong coroutine hiện tại. Returns: - Optional[TraceContext]: 若当前处于追踪链路中则返回实例,否则返回 None + Optional[TraceContext]: Instance nếu hiện tại đang ở trong chuỗi truy vết, + ngược lại trả về None. """ return _current_trace.get() @@ -129,16 +134,17 @@ class TraceContext: auto_bind: bool = False, ) -> "TraceContext": """ - 尝试获取现有链路,若不存在则按需创建一个。 + Thử lấy chuỗi hiện có; nếu không tồn tại thì tạo một chuỗi mới khi cần. Args: - group_id (str): 群组 ID - platform (str): 平台名称 - operation (str): 操作描述 - auto_bind (bool): 若新建,是否自动绑定到当前上下文(仅在 non-with 场景有用,谨慎使用) + group_id (str): ID nhóm. + platform (str): Tên nền tảng. + operation (str): Mô tả thao tác. + auto_bind (bool): Nếu tạo mới, có tự động liên kết với ngữ cảnh hiện tại + hay không (chỉ hữu ích trong trường hợp không dùng with, hãy thận trọng). Returns: - TraceContext: 活跃或新生成的实例 + TraceContext: Instance đang hoạt động hoặc vừa được tạo. """ current = cls.current() if current: @@ -156,20 +162,21 @@ class TraceContext: @staticmethod def generate(prefix: str = "", group_name: str = "") -> str: """ - 生成语义化、易读的追踪 ID。 + Tạo Trace ID có ngữ nghĩa và dễ đọc. - 格式: {来源}_{群名}_{时间点} - 示例: manual_系统交流群_1733 + Định dạng: {nguồn}_{tên_nhóm}_{thời_điểm} + Ví dụ: manual_Nhóm_hệ_thống_1733 - 由于插件存在任务锁 (DuplicateGroupTaskError),确保了一个群同一时间只有一个分析任务, - 因此 时间点 (HHmm) 已足够提供唯一性,无需 UUID 缀。 + Do plugin có khóa tác vụ (DuplicateGroupTaskError), bảo đảm mỗi nhóm + chỉ có một tác vụ phân tích tại một thời điểm. Vì vậy, thời điểm (HHmm) + đã đủ để tạo tính duy nhất và không cần thêm UUID. Args: - prefix (str): 来源标识,如 'manual', 'group', 'incr', 'report' - group_name (str): 可选群名,用于日志中快速识别 + prefix (str): Mã nguồn, ví dụ: 'manual', 'group', 'incr', 'report'. + group_name (str): Tên nhóm tùy chọn, dùng để nhận diện nhanh trong nhật ký. Returns: - str: 语义化 TraceID 字符串 + str: Chuỗi Trace ID có ngữ nghĩa. """ timestamp = datetime.now().strftime("%H%M") @@ -177,7 +184,7 @@ class TraceContext: if prefix: parts.append(prefix) if group_name: - # 清理:移除空白符和文件系统不安全字符 + # Làm sạch: loại bỏ khoảng trắng và các ký tự không an toàn cho hệ thống tệp safe_name = re.sub(r'[\s\n\r\t/\\:*?"<>|\[\]{}]', "", group_name) safe_name = safe_name[:_MAX_GROUP_NAME_LEN] if safe_name: @@ -189,13 +196,15 @@ class TraceContext: @staticmethod def make_report_caption() -> str: """ - 生成整洁的、面向用户的报告 Caption,包含用于去重的隐式时间戳。 + Tạo Caption báo cáo gọn gàng hướng đến người dùng, + có chứa dấu thời gian ẩn dùng để khử trùng lặp. - 该时间戳用作图片去重检查的 Token。 - 格式: "📊 每日群聊分析报告已生成 | MM-DD HH:MM:SS" + Dấu thời gian này được dùng làm token kiểm tra trùng lặp hình ảnh. + Định dạng gồm biểu tượng báo cáo, nội dung Caption hiện tại và dấu thời gian + theo mẫu ``MM-DD HH:MM:SS``. Returns: - str: 报告 Caption 字符串 + str: Chuỗi Caption của báo cáo. """ ts = datetime.now().strftime("%m-%d %H:%M:%S") return f"📊 每日群聊分析报告已生成 | {ts}" @@ -203,29 +212,30 @@ class TraceContext: @classmethod def set(cls, trace_id: str) -> None: """ - [兼容性接口] 直接设置当前上下文的 TraceID。 - 这会创建一个新的 TraceContext 实例并将其推入 ContextVar。 + [Giao diện tương thích] Đặt trực tiếp Trace ID của ngữ cảnh hiện tại. + Thao tác này tạo một instance TraceContext mới và đưa vào ContextVar. Args: - trace_id (str): 要设置的追踪 ID 字符串 + trace_id (str): Chuỗi Trace ID cần đặt. """ ctx = cls(trace_id=trace_id) - # 注意:此处不手动存储 Token,依靠异步任务结束时 ContextVar 的自动清理。 + # Lưu ý: không lưu Token thủ công ở đây; dựa vào việc ContextVar tự dọn dẹp + # khi tác vụ bất đồng bộ kết thúc. _current_trace.set(ctx) @classmethod def get(cls) -> str: """ - [兼容性接口] 获取当前活跃的追踪 ID 字符串。 + [Giao diện tương thích] Lấy chuỗi Trace ID đang hoạt động hiện tại. """ return get_trace_id() class TraceLogFilter(logging.Filter): """ - 日志过滤器:自动将当前的 TraceID 注入每一条日志记录中。 + Bộ lọc nhật ký: tự động chèn Trace ID hiện tại vào mỗi bản ghi nhật ký. - 配合日志格式化字符串 `[%(trace_id)s]` 使用。 + Được sử dụng cùng chuỗi định dạng nhật ký `[%(trace_id)s]`. """ def filter(self, record: logging.LogRecord) -> bool: @@ -235,10 +245,11 @@ class TraceLogFilter(logging.Filter): def get_trace_id() -> str: """ - 便捷接口:快速获取当前活跃的 TraceID 或零时生成一个临时 ID。 + Giao diện tiện ích: nhanh chóng lấy Trace ID đang hoạt động, + hoặc tạm thời tạo một ID mới nếu chưa có. Returns: - str: 8 位十六进制追踪 ID + str: Trace ID hệ thập lục phân gồm 8 ký tự. """ trace = TraceContext.current() if trace: @@ -252,21 +263,22 @@ def with_trace( operation: str = "", ): """ - 装饰器:自动为异步函数包裹追踪上下文。 + Decorator: tự động bao bọc hàm bất đồng bộ bằng ngữ cảnh truy vết. Args: - group_id (str): 设置追踪的群组 - platform (str): 设置追踪的平台 - operation (str): 操作名称,默认为函数名 + group_id (str): Nhóm cần truy vết. + platform (str): Nền tảng cần truy vết. + operation (str): Tên thao tác, mặc định là tên hàm. Returns: - Callable: 装饰后的函数 + Callable: Hàm sau khi được trang trí. """ def decorator(func): @functools.wraps(func) async def wrapper(*args, **kwargs): - # 优先使用装饰器声明的 operation,否则取函数原始名称 + # Ưu tiên operation được khai báo trong decorator; nếu không có, + # sử dụng tên gốc của hàm. op_name = operation or func.__name__ with TraceContext( group_id=group_id,