Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion astrbot/core/agent/tool_image_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ def __init__(self) -> None:
self._initialized = True
self._cache_dir = os.path.join(get_astrbot_temp_path(), self.CACHE_DIR_NAME)
os.makedirs(self._cache_dir, exist_ok=True)
logger.debug(f"ToolImageCache initialized, cache dir: {self._cache_dir}")

def _get_file_extension(self, mime_type: str) -> str:
"""Get file extension from MIME type."""
Expand Down
11 changes: 8 additions & 3 deletions astrbot/core/config/astrbot_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ def check_config_integrity(self, refer_conf: dict, conf: dict, path=""):
for key, value in refer_conf.items():
if key not in conf:
# 配置项不存在,插入默认值
logger.info("检查到配置项不存在,已插入默认值")
path_ = path + "." + key if path else key
logger.info("Config key missing; added default.")
Comment on lines +106 to +107

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

变量 path_ 已定义但未在随后的日志消息中使用。建议在日志中包含该路径信息,以便用户明确是哪个配置项缺失。

Suggested change
path_ = path + "." + key if path else key
logger.info("Config key missing; added default.")
path_ = path + "." + key if path else key
logger.info("Config key missing; added default: %s", path_)

new_conf[key] = value
has_new = True
elif conf[key] is None:
Expand Down Expand Up @@ -132,12 +133,16 @@ def check_config_integrity(self, refer_conf: dict, conf: dict, path=""):
# 检查是否存在参考配置中没有的配置项
for key in list(conf.keys()):
if key not in refer_conf:
logger.info("检查到未知配置项,将从当前配置中删除")
path_ = path + "." + key if path else key
logger.info("Config key removed: %s", path_)
has_new = True

# 顺序不一致也算作变更
if list(conf.keys()) != list(new_conf.keys()):
logger.info("检查到配置项顺序不一致,已重新排序")
if path:
logger.info("Config key order fixed: %s", path)
else:
logger.info("Config key order fixed")
has_new = True

# 更新原始配置
Expand Down
2 changes: 1 addition & 1 deletion astrbot/core/core_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ async def start(self) -> None:
用load加载事件总线和任务并初始化, 执行启动完成事件钩子
"""
self._load()
logger.info("AstrBot 启动完成。")
logger.info("AstrBot started.")

# 执行启动完成事件钩子
handlers = star_handlers_registry.get_handlers_by_event_type(
Expand Down
2 changes: 1 addition & 1 deletion astrbot/core/persona_mgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __init__(self, db_helper: BaseDatabase, acm: AstrBotConfigManager) -> None:
async def initialize(self) -> None:
self.personas = await self.get_all_personas()
self.get_v3_persona_data()
logger.info(f"已加载 {len(self.personas)} 个人格。")
logger.info("Loaded %s personas.", len(self.personas))

async def get_persona(self, persona_id: str):
"""获取指定 persona 的信息"""
Expand Down
6 changes: 4 additions & 2 deletions astrbot/core/platform/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ async def load_platform(self, platform_config: dict) -> None:
return

logger.info(
f"载入 {platform_config['type']}({platform_config['id']}) 平台适配器 ...",
"Loading IM platform adapter %s(%s) ...",
platform_config["type"],
platform_config["id"],
)
match platform_config["type"]:
case "aiocqhttp":
Expand Down Expand Up @@ -201,7 +203,7 @@ async def load_platform(self, platform_config: dict) -> None:

if platform_config["type"] not in platform_cls_map:
logger.error(
f"未找到适用于 {platform_config['type']}({platform_config['id']}) 平台适配器,请检查是否已经安装或者名称填写错误",
f"Platform adapter not found: {platform_config['type']}({platform_config['id']}).",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

建议在此处统一使用延迟格式化(使用 %s 占位符),这与本次提交中其他日志风格的修改保持一致,也是 Python 日志记录的最佳实践。

Suggested change
f"Platform adapter not found: {platform_config['type']}({platform_config['id']}).",
"Platform adapter not found: %s(%s).", platform_config['type'], platform_config['id']
References
  1. Use lazy formatting (%s) for logging. (link)

)
return
cls_type = platform_cls_map[platform_config["type"]]
Expand Down
2 changes: 1 addition & 1 deletion astrbot/core/platform/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def decorator(cls):
)
platform_registry.append(pm)
platform_cls_map[adapter_name] = cls
logger.debug(f"平台适配器 {adapter_name} 已注册")
logger.debug("Platform adapter registered: %s", adapter_name)
return cls

return decorator
Expand Down
12 changes: 7 additions & 5 deletions astrbot/core/provider/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,9 @@ async def load_provider(self, provider_config: dict) -> None:
return

logger.info(
f"载入 {provider_config['type']}({provider_config['id']}) 服务提供商 ...",
"Loading model %s(%s) ...",
provider_config["type"],
provider_config["id"],
)

# 动态导入
Expand All @@ -591,7 +593,7 @@ async def load_provider(self, provider_config: dict) -> None:

if provider_config["type"] not in provider_cls_map:
logger.error(
f"未找到适用于 {provider_config['type']}({provider_config['id']}) 的提供商适配器,请检查是否已经安装或者名称填写错误。已跳过。",
f"Provider adapter not found: {provider_config['type']}({provider_config['id']}). Skipped.",
exc_info=True,
)
return
Expand Down Expand Up @@ -625,7 +627,7 @@ async def load_provider(self, provider_config: dict) -> None:
):
self.curr_stt_provider_inst = inst
logger.info(
f"已选择 {provider_config['type']}({provider_config['id']}) 作为当前语音转文本提供商适配器。",
f"Selected {provider_config['type']}({provider_config['id']}) as default STT provider",
)
if not self.curr_stt_provider_inst:
self.curr_stt_provider_inst = inst
Expand All @@ -648,7 +650,7 @@ async def load_provider(self, provider_config: dict) -> None:
):
self.curr_tts_provider_inst = inst
logger.info(
f"已选择 {provider_config['type']}({provider_config['id']}) 作为当前文本转语音提供商适配器。",
f"Selected {provider_config['type']}({provider_config['id']}) as default TTS provider",
)
if not self.curr_tts_provider_inst:
self.curr_tts_provider_inst = inst
Expand All @@ -674,7 +676,7 @@ async def load_provider(self, provider_config: dict) -> None:
):
self.curr_provider_inst = inst
logger.info(
f"已选择 {provider_config['type']}({provider_config['id']}) 作为当前提供商适配器。",
f"Selected {provider_config['type']}({provider_config['id']}) as default chat model provider",
)
if not self.curr_provider_inst:
self.curr_provider_inst = inst
Expand Down
2 changes: 1 addition & 1 deletion astrbot/core/provider/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def decorator(cls):
)
provider_registry.append(pm)
provider_cls_map[provider_type_name] = pm
logger.debug(f"服务提供商 Provider {provider_type_name} 已注册")
logger.debug("Model provider registered: %s", provider_type_name)
return cls

return decorator
10 changes: 5 additions & 5 deletions astrbot/core/star/star_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,21 +570,21 @@ def _validate_astrbot_version_specifier(
except InvalidSpecifier:
return (
False,
"astrbot_version 格式无效,请使用 PEP 440 版本范围格式,例如 >=4.16,<5",
"Invalid astrbot_version. Use a PEP 440 range, e.g. >=4.16,<5.",
)

try:
current_version = Version(VERSION)
except InvalidVersion:
return (
False,
f"AstrBot 当前版本 {VERSION} 无法被解析,无法校验插件版本范围。",
f"Invalid current AstrBot version: {VERSION}. Cannot check plugin version range.",
)

if not specifier.contains(current_version, prereleases=True):
return (
False,
f"当前 AstrBot 版本为 {VERSION},不满足插件要求的 astrbot_version: {normalized_spec}",
f"AstrBot {VERSION} does not satisfy plugin astrbot_version: {normalized_spec}",
)
return True, None

Expand Down Expand Up @@ -874,7 +874,7 @@ async def load(
if specified_dir_name and root_dir_name != specified_dir_name:
continue

logger.info(f"正在载入插件 {root_dir_name} ...")
logger.info("Loading plugin %s ...", root_dir_name)

# 尝试导入模块
try:
Expand Down Expand Up @@ -993,7 +993,7 @@ async def load(
setattr(metadata.star_cls, "author", p_author)
setattr(metadata.star_cls, "plugin_id", plugin_id)
else:
logger.info(f"插件 {metadata.name} 已被禁用。")
logger.info("Plugin %s is disabled.", metadata.name)

metadata.module = module
metadata.root_dir_name = root_dir_name
Expand Down
19 changes: 11 additions & 8 deletions astrbot/core/utils/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,14 @@ async def download_file(url: str, path: str, show_progress: bool = False) -> Non
) as session:
async with session.get(url, timeout=1800) as resp:
if resp.status != 200:
raise Exception(f"下载文件失败: {resp.status}")
logger.error(
f"Failed to download file from {url}. HTTP status code: {resp.status}"
Comment on lines +139 to +140

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Non-200 responses are logged but no longer abort the download, which can lead to writing error pages as files.

This change removes the early exit on non-200 responses, so the rest of the logic still runs and will likely save an error/empty response to path while reporting success. Please reintroduce an early return or exception when resp.status != 200 so failed downloads don’t produce misleading outputs.

)
Comment on lines +139 to +141

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

在检测到 HTTP 状态码不为 200 时,代码仅记录了错误日志而不再抛出异常。这会导致程序继续执行后续逻辑(如读取 content-length),可能会引发后续的 AttributeError 或导致下载损坏的文件。建议保留抛出异常的逻辑,以确保在下载失败时及时中断。此外,根据项目规则,新增的功能(如文件下载处理)应包含相应的单元测试。

                    raise Exception(f"Failed to download file from {url}. HTTP status code: {resp.status}")
References
  1. New functionality, such as handling attachments, should be accompanied by corresponding unit tests.

total_size = int(resp.headers.get("content-length", 0))
downloaded_size = 0
start_time = time.time()
if show_progress:
print(f"文件大小: {total_size / 1024:.2f} KB | 文件地址: {url}")
print(f"Downloading: {url} | Size: {total_size / 1024:.2f} KB")
with open(path, "wb") as f:
while True:
chunk = await resp.content.read(8192)
Expand All @@ -157,13 +159,14 @@ async def download_file(url: str, path: str, show_progress: bool = False) -> Non
)
speed = downloaded_size / 1024 / elapsed_time # KB/s
print(
f"\r下载进度: {downloaded_size / total_size:.2%} 速度: {speed:.2f} KB/s",
f"\rProgress: {downloaded_size / total_size:.2%} Speed: {speed:.2f} KB/s",
end="",
)
except (aiohttp.ClientConnectorSSLError, aiohttp.ClientConnectorCertificateError):
# 关闭SSL验证(仅在证书验证失败时作为fallback)
logger.warning(
"SSL 证书验证失败,已关闭 SSL 验证(不安全,仅用于临时下载)。请检查目标服务器的证书配置。"
f"SSL certificate verification failed for {url}. "
"Falling back to unverified connection (CERT_NONE). "
)
Comment on lines 167 to 170

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

这里新增的日志信息(第 167-170 行)与紧随其后的原有日志内容(第 171 行开始)功能重叠且内容几乎一致,导致了日志冗余。建议移除重复的日志记录。

logger.warning(
f"SSL certificate verification failed for {url}. "
Expand All @@ -180,7 +183,7 @@ async def download_file(url: str, path: str, show_progress: bool = False) -> Non
downloaded_size = 0
start_time = time.time()
if show_progress:
print(f"文件大小: {total_size / 1024:.2f} KB | 文件地址: {url}")
print(f"Size: {total_size / 1024:.2f} KB | URL: {url}")
with open(path, "wb") as f:
while True:
chunk = await resp.content.read(8192)
Expand All @@ -192,7 +195,7 @@ async def download_file(url: str, path: str, show_progress: bool = False) -> Non
elapsed_time = time.time() - start_time
speed = downloaded_size / 1024 / elapsed_time # KB/s
print(
f"\r下载进度: {downloaded_size / total_size:.2%} 速度: {speed:.2f} KB/s",
f"\rProgress: {downloaded_size / total_size:.2%} Speed: {speed:.2f} KB/s",
end="",
)
if show_progress:
Expand Down Expand Up @@ -252,7 +255,7 @@ async def download_dashboard(
ver_name = "latest" if latest else version
dashboard_release_url = f"https://astrbot-registry.soulter.top/download/astrbot-dashboard/{ver_name}/dist.zip"
logger.info(
f"准备下载指定发行版本的 AstrBot WebUI 文件: {dashboard_release_url}",
f"Downloading AstrBot WebUI from {dashboard_release_url}",
)
try:
await download_file(
Expand All @@ -274,7 +277,7 @@ async def download_dashboard(
)
else:
url = f"https://github.com/AstrBotDevs/astrbot-release-harbour/releases/download/release-{version}/dist.zip"
logger.info(f"准备下载指定版本的 AstrBot WebUI: {url}")
logger.info(f"Downloading AstrBot WebUI from {url}")
if proxy:
url = f"{proxy}/{url}"
await download_file(url, str(zip_path), show_progress=True)
Expand Down
24 changes: 12 additions & 12 deletions astrbot/dashboard/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,20 +322,20 @@ def _resolve_dashboard_ssl_config(

if not cert_file or not key_file:
logger.warning(
"dashboard.ssl.enable 已启用,但未同时配置 cert_file key_fileSSL 配置将不会生效。",
"dashboard.ssl.enable is set, but cert_file or key_file is missing. SSL disabled.",
)
return False, {}

cert_path = Path(cert_file).expanduser()
key_path = Path(key_file).expanduser()
if not cert_path.is_file():
logger.warning(
f"dashboard.ssl.enable 已启用,但 SSL 证书文件不存在: {cert_path}SSL 配置将不会生效。",
f"dashboard.ssl.enable is set, but cert file is missing: {cert_path}. SSL disabled.",
)
return False, {}
if not key_path.is_file():
logger.warning(
f"dashboard.ssl.enable 已启用,但 SSL 私钥文件不存在: {key_path}SSL 配置将不会生效。",
f"dashboard.ssl.enable is set, but key file is missing: {key_path}. SSL disabled.",
)
return False, {}

Expand All @@ -348,7 +348,7 @@ def _resolve_dashboard_ssl_config(
ca_path = Path(ca_certs).expanduser()
if not ca_path.is_file():
logger.warning(
f"dashboard.ssl.enable 已启用,但 SSL CA 证书文件不存在: {ca_path}SSL 配置将不会生效。",
f"dashboard.ssl.enable is set, but CA cert file is missing: {ca_path}. SSL disabled.",
)
return False, {}
resolved_ssl_config["ca_certs"] = str(ca_path.resolve())
Expand Down Expand Up @@ -385,13 +385,13 @@ def run(self):
scheme = "https" if ssl_enable else "http"

if not enable:
logger.info("WebUI 已被禁用")
logger.info("WebUI disabled.")
return None

logger.info(f"正在启动 WebUI, 监听地址: {scheme}://{host}:{port}")
logger.info("Starting WebUI at %s://%s:%s", scheme, host, port)
if host == "0.0.0.0":
logger.info(
"提示: WebUI 将监听所有网络接口,请注意安全。(可在 data/cmd_config.json 中配置 dashboard.host 以修改 host)",
"WebUI listens on all interfaces. Check security. Set dashboard.host in data/cmd_config.json to change it.",
)

if host not in ["localhost", "127.0.0.1"]:
Expand All @@ -415,16 +415,16 @@ def run(self):

raise Exception(f"端口 {port} 已被占用")

parts = [f"\n ✨✨✨\n AstrBot v{VERSION} WebUI 已启动,可访问\n\n"]
parts.append(f" ➜ 本地: {scheme}://localhost:{port}\n")
parts = [f"\n ✨✨✨\n AstrBot v{VERSION} WebUI is ready\n\n"]
parts.append(f" ➜ Local: {scheme}://localhost:{port}\n")
for ip in ip_addr:
parts.append(f" ➜ 网络: {scheme}://{ip}:{port}\n")
parts.append(" ➜ 默认用户名和密码: astrbot\n ✨✨✨\n")
parts.append(f" ➜ Network: {scheme}://{ip}:{port}\n")
parts.append(" ➜ Default username/password: astrbot / astrbot\n ✨✨✨\n")
display = "".join(parts)

if not ip_addr:
display += (
"可在 data/cmd_config.json 中配置 dashboard.host 以便远程访问。\n"
"Set dashboard.host in data/cmd_config.json to enable remote access.\n"
)

logger.info(display)
Expand Down
2 changes: 2 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ venv/
node_modules/
.vitepress/cache
*dist
scripts/deploy-cli.sh
scripts/deploy-cli.ps1
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"scripts": {
"docs:dev": "vitepress dev --host",
"docs:build": "vitepress build",
"docs:build": "node scripts/copy-deploy-cli.mjs && vitepress build",
"docs:preview": "vitepress preview"
},
"devDependencies": {
Expand Down
21 changes: 21 additions & 0 deletions docs/scripts/copy-deploy-cli.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { chmod, copyFile, mkdir } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const scriptDir = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(scriptDir, "../..");

const files = [
{ name: "deploy-cli.sh", mode: 0o755 },
{ name: "deploy-cli.ps1", mode: 0o644 },
];

await mkdir(scriptDir, { recursive: true });

for (const file of files) {
const source = resolve(repoRoot, "scripts", file.name);
const target = resolve(scriptDir, file.name);
await copyFile(source, target);
await chmod(target, file.mode);
console.log(`Copied ${file.name} to docs/scripts/`);
}
Loading
Loading