mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-16 01:40:15 +08:00
* fix: unify media reference handling * fix: accept bare base64 record media refs * chore: update agents.md * fix: unify file URI handling across media components and utilities * fix: unify media reference type handling with MediaRefStr alias * Potential fix for pull request finding 'CodeQL / Incomplete URL substring sanitization' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update astrbot/core/platform/sources/discord/discord_platform_adapter.py Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * fix: unify media handling and improve base64 decoding across components * fix: simplify client_kwargs type definition and enhance media message handling in platform adapter * fix: unify media utility documentation and enhance function descriptions * perf: drop "pilk" requirement, improve audio outbound for tencent-related IM apps which using silk * fix: unify Tencent Silk audio handling and enhance media resolver functionality --- - Centralize media reference materialization and base64 resolution for local paths, http(s), base64://, data URIs, and legacy bare base64 payloads. - Normalize incoming Record audio to wav and Image media to temporary jpg during preprocess, with event-scoped cleanup. - Reuse the shared media resolver across OpenAI, Gemini, Anthropic, MiMo, DeerFlow, STT, and platform media paths while sanitizing logs and cleaning temporary conversion outputs. - Ensure generated TTS audio is tracked for cleanup after the event finishes. fix #8676 fix #8543 fix #7588 fix #7580 fix #8030 fix #8034 fix #7461 fix #7565 fix #6509 fix #7144 fix #7795 --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
93 lines
3.0 KiB
Python
93 lines
3.0 KiB
Python
import asyncio
|
|
import os
|
|
import time
|
|
import uuid
|
|
|
|
|
|
class FileTokenService:
|
|
"""维护一个简单的基于令牌的文件下载服务,支持超时和懒清除。"""
|
|
|
|
def __init__(self, default_timeout: float = 300) -> None:
|
|
self.lock = asyncio.Lock()
|
|
self.staged_files = {} # token: (file_path, expire_time)
|
|
self.default_timeout = default_timeout
|
|
|
|
async def _cleanup_expired_tokens(self) -> None:
|
|
"""清理过期的令牌"""
|
|
now = time.time()
|
|
expired_tokens = [
|
|
token for token, (_, expire) in self.staged_files.items() if expire < now
|
|
]
|
|
for token in expired_tokens:
|
|
self.staged_files.pop(token, None)
|
|
|
|
async def check_token_expired(self, file_token: str) -> bool:
|
|
async with self.lock:
|
|
await self._cleanup_expired_tokens()
|
|
return file_token not in self.staged_files
|
|
|
|
async def register_file(self, file_path: str, timeout: float | None = None) -> str:
|
|
"""向令牌服务注册一个文件。
|
|
|
|
Args:
|
|
file_path(str): 文件路径
|
|
timeout(float): 超时时间,单位秒(可选)
|
|
|
|
Returns:
|
|
str: 一个单次令牌
|
|
|
|
Raises:
|
|
FileNotFoundError: 当路径不存在时抛出
|
|
|
|
"""
|
|
try:
|
|
from astrbot.core.utils.media_utils import file_uri_to_path, is_file_uri
|
|
|
|
local_path = (
|
|
file_uri_to_path(file_path) if is_file_uri(file_path) else file_path
|
|
)
|
|
except Exception:
|
|
# Fall back to the original path if URL parsing fails.
|
|
local_path = file_path
|
|
|
|
async with self.lock:
|
|
await self._cleanup_expired_tokens()
|
|
|
|
if not os.path.exists(local_path):
|
|
raise FileNotFoundError(
|
|
f"文件不存在: {local_path} (原始输入: {file_path})",
|
|
)
|
|
|
|
file_token = str(uuid.uuid4())
|
|
expire_time = time.time() + (
|
|
timeout if timeout is not None else self.default_timeout
|
|
)
|
|
# 存储转换后的真实路径
|
|
self.staged_files[file_token] = (local_path, expire_time)
|
|
return file_token
|
|
|
|
async def handle_file(self, file_token: str) -> str:
|
|
"""根据令牌获取文件路径,使用后令牌失效。
|
|
|
|
Args:
|
|
file_token(str): 注册时返回的令牌
|
|
|
|
Returns:
|
|
str: 文件路径
|
|
|
|
Raises:
|
|
KeyError: 当令牌不存在或已过期时抛出
|
|
FileNotFoundError: 当文件本身已被删除时抛出
|
|
|
|
"""
|
|
async with self.lock:
|
|
await self._cleanup_expired_tokens()
|
|
|
|
if file_token not in self.staged_files:
|
|
raise KeyError(f"无效或过期的文件 token: {file_token}")
|
|
|
|
file_path, _ = self.staged_files.pop(file_token)
|
|
if not os.path.exists(file_path):
|
|
raise FileNotFoundError(f"文件不存在: {file_path}")
|
|
return file_path
|