Files
NeoBot/web_static/html/404.html
镀铬酸钾 d9ad6af444 Dev (#37)
* 滚木

* feat: 重构核心架构,增强类型安全与插件管理

本次提交对核心模块进行了深度重构,引入 Pydantic 增强配置管理的类型安全性,并全面优化了插件管理系统。

主要变更详情:

1. 核心架构与配置
   - 重构配置加载模块:引入 Pydantic 模型 (`core/config_models.py`),提供严格的配置项类型检查、验证及默认值管理。
   - 统一模块结构:规范化模块导入路径,移除冗余的 `__init__.py` 文件,提升项目结构的清晰度。
   - 性能优化:集成 Redis 缓存支持 (`RedisManager`),有效降低高频 API 调用开销,提升响应速度。

2. 插件系统升级
   - 实现热重载机制:新增插件文件变更监听功能,支持开发过程中自动重载插件,提升开发效率。
   - 优化生命周期管理:改进插件加载与卸载逻辑,支持精确卸载指定插件及其关联的命令、事件处理器和定时任务。

3. 功能特性增强
   - 新增媒体 API:引入 `MediaAPI` 模块,封装图片、语音等富媒体资源的获取与处理接口。
   - 完善权限体系:重构权限管理系统,实现管理员与操作员的分级控制,支持更细粒度的命令权限校验。

4. 代码质量与稳定性
   - 全面类型修复:解决 `mypy` 静态类型检查发现的大量类型错误(包括 `CommandManager`、`EventFactory` 及 `Bot` API 签名不匹配问题)。
   - 增强错误处理:优化消息处理管道的异常捕获机制,完善关键路径的日志记录,提升系统运行稳定性。

* feat: 添加测试用例并优化代码结构

refactor(permission_manager): 调整初始化顺序和逻辑
fix(admin_manager): 修复初始化逻辑和目录创建问题
feat(ws): 优化Bot实例初始化条件
feat(message): 增强MessageSegment功能并添加测试
feat(events): 支持字符串格式的消息解析
test: 添加核心功能测试用例
refactor(plugin_manager): 改进插件路径处理
style: 清理无用导入和代码
chore: 更新依赖项

* refactor(handler): 移除TYPE_CHECKING并直接导入Bot类

简化类型注解,直接导入Bot类而非使用TYPE_CHECKING条件导入,提高代码可读性和维护性

* fix(command_manager): 修复插件卸载时元信息移除不精确的问题

修复 CommandManager 中 unload_plugin 方法移除插件元信息时使用 startswith 导致可能误删其他插件的问题,改为精确匹配
同时调整相关测试用例验证精确匹配行为

* refactor: 清理未使用的导入和更新文档结构

docs: 添加config_models.py到项目结构文档
docs: 调整数据目录位置到core/data下
docs: 更新权限管理器文档描述

* 文档更新

* 更新thpic插件 支持一次返回多张图

* feat: 添加测试覆盖率并修复相关问题

refactor(redis_manager): 移除冗余的ConnectionError处理
refactor(event_handler): 优化Bot类型注解
refactor(factory): 移除未使用的GroupCardNoticeEvent

test: 添加全面的单元测试覆盖
- 添加test_import.py测试模块导入
- 添加test_debug.py测试插件加载调试
- 添加test_plugin_error.py测试错误处理
- 添加test_config_loader.py测试配置加载
- 添加test_redis_manager.py测试Redis管理
- 添加test_bot.py测试Bot功能
- 扩展test_models.py测试消息模型
- 添加test_plugin_manager_coverage.py测试插件管理
- 添加test_executor.py测试代码执行器
- 添加test_ws.py测试WebSocket
- 添加test_api.py测试API接口
- 添加test_core_managers.py测试核心管理模块

fix(plugin_manager): 修复插件加载日志变量问题

覆盖率已到达86%(忽略插件)

* 更新/help指令,现在会发送图片

* feat(help): 重构帮助系统为图片渲染模式

添加浏览器管理器和图片管理器,用于通过 Playwright 渲染帮助菜单为图片
重构命令管理器以支持图片缓存和同步功能
添加 HTML 模板用于帮助菜单渲染

* build: 更新依赖文件 requirements.txt

* build: 更新依赖文件

* feat: 添加性能优化和架构文档,更新依赖和核心模块

refactor(browser_manager): 实现页面池机制以提升性能
refactor(image_manager): 添加模板缓存并集成页面池
refactor(bili_parser): 迁移到异步HTTP请求并实现会话复用
docs: 新增性能优化、架构设计和最佳实践文档
chore: 更新requirements.txt添加新依赖

* docs: 更新文档内容并优化语言风格

重构所有文档内容,使用更简洁直接的语言风格
更新架构、插件开发、部署等核心文档
优化代码示例和图表说明
统一术语和格式规范

* docs: 更新文档内容,简化语言并修正格式

- 简化插件开发指南中的描述,移除冗余内容
- 调整部署文档中的Python版本说明
- 优化最佳实践文档的措辞和格式
- 更新性能优化文档,删除不准确的数据
- 重构核心概念文档,使用更简洁的语言
- 修正README中的项目描述和技术栈说明
- 更新快速上手文档,简化安装步骤
- 调整事件流转文档的描述方式
- 简化架构文档内容
- 更新指令处理文档,添加参数注入示例
- 优化单例管理器文档的表述

* refactor(core): 优化权限管理和事件模型

- 重构 AdminManager 和 PermissionManager 以 Redis 为主要数据源
- 为所有事件模型添加 slots=True 提升性能
- 更新文档说明 Mypyc 编译注意事项
- 清理测试和调试文件
- 移动静态资源到 web_static 目录

---------

Co-authored-by: baby20162016 <2185823427@qq.com>
2026-01-13 08:37:30 +08:00

288 lines
12 KiB
HTML

<!DOCTYPE html>
<html><head><title></title><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>404 - Signal Lost</title><script src="./tailwindcss.js"></script><script src="./iconify-icon.min.js"></script></head><body>
<style id="style-404">
/* 核心背景:动态噪点与 CRT 效果 */
@keyframes noise {
0%, 100% { transform: translate(0, 0); }
10% { transform: translate(-5%, -5%); }
20% { transform: translate(-10%, 5%); }
30% { transform: translate(5%, -10%); }
40% { transform: translate(-5%, 15%); }
50% { transform: translate(-10%, 5%); }
60% { transform: translate(15%, 0); }
70% { transform: translate(0, 10%); }
80% { transform: translate(-15%, 0); }
90% { transform: translate(10%, 5%); }
}
.bg-noise {
position: fixed;
top: -50%;
left: -50%;
right: -50%;
bottom: -50%;
width: 200%;
height: 200vh;
background: transparent url('http://assets.iceable.com/img/noise-transparent.png') repeat 0 0;
background-repeat: repeat;
animation: noise .2s infinite;
opacity: .05;
visibility: visible;
pointer-events: none;
z-index: 1;
}
.crt-overlay {
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
background-size: 100% 2px, 3px 100%;
pointer-events: none;
}
.vignette {
background: radial-gradient(circle, rgba(0,0,0,0) 60%, rgba(0,0,0,1) 100%);
pointer-events: none;
}
/* 高级 Glitch 文本效果 */
.cyber-glitch {
position: relative;
color: #fff;
mix-blend-mode: lighten;
}
.cyber-glitch::before,
.cyber-glitch::after {
content: attr(data-text);
position: absolute;
top: 0;
width: 100%;
background: #050505;
clip: rect(0, 0, 0, 0);
}
.cyber-glitch::before {
left: -2px;
text-shadow: 2px 0 #ff00c1;
animation: glitch-anim-1 2s infinite linear alternate-reverse;
}
.cyber-glitch::after {
left: 2px;
text-shadow: -2px 0 #00fff9;
animation: glitch-anim-2 3s infinite linear alternate-reverse;
}
@keyframes glitch-anim-1 {
0% { clip: rect(20px, 9999px, 10px, 0); }
20% { clip: rect(50px, 9999px, 80px, 0); }
40% { clip: rect(10px, 9999px, 40px, 0); }
60% { clip: rect(80px, 9999px, 20px, 0); }
80% { clip: rect(30px, 9999px, 60px, 0); }
100% { clip: rect(60px, 9999px, 30px, 0); }
}
@keyframes glitch-anim-2 {
0% { clip: rect(60px, 9999px, 30px, 0); }
20% { clip: rect(10px, 9999px, 50px, 0); }
40% { clip: rect(70px, 9999px, 10px, 0); }
60% { clip: rect(30px, 9999px, 90px, 0); }
80% { clip: rect(90px, 9999px, 20px, 0); }
100% { clip: rect(20px, 9999px, 60px, 0); }
}
/* 装饰性扫描线 */
.scanline-bar {
width: 100%;
height: 5px;
background: rgba(0, 255, 249, 0.3);
position: absolute;
z-index: 10;
animation: scan 3s linear infinite;
opacity: 0.3;
box-shadow: 0 0 10px rgba(0, 255, 249, 0.5);
}
@keyframes scan {
0% { top: -10%; }
100% { top: 110%; }
}
/* 终端光标闪烁 */
.cursor-blink {
animation: blink 1s step-end infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
</style>
<div id="content-404" class="relative min-h-screen bg-[#050505] flex flex-col items-center justify-center overflow-hidden font-mono text-gray-300 selection:bg-electric/30 selection:text-white">
<!-- Environmental Effects -->
<div class="bg-noise"></div>
<div class="crt-overlay absolute inset-0 z-50 pointer-events-none"></div>
<div class="vignette absolute inset-0 z-40 pointer-events-none"></div>
<div class="scanline-bar pointer-events-none"></div>
<!-- Background Grid -->
<div class="absolute inset-0 bg-[linear-gradient(rgba(30,30,30,0.5)_1px,transparent_1px),linear-gradient(90deg,rgba(30,30,30,0.5)_1px,transparent_1px)] bg-[size:40px_40px] opacity-10 pointer-events-none z-0" style="perspective: 500px; transform: rotateX(20deg) scale(1.5);"></div>
<!-- Main Content -->
<div class="relative z-30 flex flex-col items-center w-full max-w-4xl px-6">
<!-- Glitch Title -->
<div class="relative mb-6 group cursor-default">
<h1 class="cyber-glitch text-[120px] md:text-[180px] font-black leading-none tracking-tighter opacity-90 select-none" data-text="404">
404
</h1>
<div class="absolute -bottom-4 left-0 w-full flex justify-between text-[10px] md:text-xs text-electric/40 uppercase tracking-[0.5em] font-bold">
<span>Sys.Malfunction</span>
<span>0x00_DEAD</span>
</div>
</div>
<!-- Terminal Window -->
<div class="w-full max-w-2xl mt-8 mb-12 backdrop-blur-md bg-black/40 border border-white/5 rounded-sm shadow-[0_0_30px_rgba(0,0,0,0.8)] overflow-hidden">
<!-- Terminal Header -->
<div class="flex items-center justify-between px-4 py-2 bg-white/5 border-b border-white/5">
<div class="flex gap-2">
<div class="w-3 h-3 rounded-full bg-red-900/50 border border-red-500/30"></div>
<div class="w-3 h-3 rounded-full bg-yellow-900/50 border border-yellow-500/30"></div>
<div class="w-3 h-3 rounded-full bg-green-900/50 border border-green-500/30"></div>
</div>
<div class="text-[10px] text-gray-600 font-mono">root@neobot:~/system/logs</div>
</div>
<!-- Terminal Body -->
<div class="p-6 font-mono text-sm md:text-base leading-relaxed h-48 overflow-y-auto custom-scrollbar">
<div id="terminal-content" class="space-y-1">
<!-- Content will be injected by JS -->
<div class="text-gray-500 transition-opacity duration-100">&gt; initiating_handshake...</div><div class="text-gray-400 transition-opacity duration-100">&gt; resolving_host: calglaubot.internal</div><div class="text-green-500/50 transition-opacity duration-100">&gt; connection_established (port: 443)</div><div class="text-blue-400/60 transition-opacity duration-100">&gt; GET /requested_resource HTTP/1.1</div><div class="text-gray-500 transition-opacity duration-100">&gt; waiting_for_response...</div><div class="text-red-500 font-bold transition-opacity duration-100">&gt; FATAL: endpoint_not_found</div><div class="text-gray-600 mt-2 transition-opacity duration-100">&gt; stack_trace_dump:</div><div class="text-gray-600 pl-4 transition-opacity duration-100">&gt; at Router.resolve (core.js:204)</div><div class="text-gray-600 pl-4 transition-opacity duration-100">&gt; at Neobot.Handler (main.py:404)</div><div class="text-electric/80 mt-2 transition-opacity duration-100">&gt; error: signal_lost_in_void</div></div>
<div class="flex items-center mt-2 text-electric">
<span class="mr-2"></span>
<span class="cursor-blink w-2 h-4 bg-electric block"></span>
</div>
</div>
</div>
<!-- Navigation -->
<div class="flex flex-col md:flex-row gap-6 items-center">
<a href="index.html" class="group relative px-8 py-3 bg-transparent overflow-hidden">
<!-- Button Borders -->
<div class="absolute top-0 left-0 w-2 h-2 border-t border-l border-electric/50 transition-all group-hover:w-full group-hover:h-full group-hover:border-electric"></div>
<div class="absolute bottom-0 right-0 w-2 h-2 border-b border-r border-electric/50 transition-all group-hover:w-full group-hover:h-full group-hover:border-electric"></div>
<!-- Button Content -->
<div class="relative flex items-center gap-3">
<iconify-icon icon="mdi:console-network" class="text-xl text-electric/70 group-hover:text-electric transition-colors"></iconify-icon>
<span class="font-bold tracking-widest text-sm text-gray-400 group-hover:text-white transition-colors">REBOOT_SYSTEM</span>
</div>
<!-- Hover Background -->
<div class="absolute inset-0 bg-electric/5 translate-y-full group-hover:translate-y-0 transition-transform duration-300"></div>
</a>
<a href="#" class="text-xs text-gray-600 hover:text-electric/60 transition-colors uppercase tracking-widest border-b border-transparent hover:border-electric/30 pb-0.5">
Report_Incident
</a>
</div>
</div>
<!-- Footer Stats -->
<div class="absolute bottom-6 left-6 right-6 flex justify-between text-[10px] text-gray-700 font-mono uppercase z-30">
<div>
<span>CPU: <span class="text-gray-500">98%</span></span>
<span class="mx-2">|</span>
<span>MEM: <span class="text-red-900 animate-pulse">OVERFLOW</span></span>
</div>
<div>
<span>NEOBOT FRAMEWORK</span>
</div>
</div>
</div><script id="script-404">(function() {
document.addEventListener('DOMContentLoaded', () => {
const terminalContent = document.getElementById('terminal-content');
const logs = [
{ text: 'initiating_handshake...', delay: 100, class: 'text-gray-500' },
{ text: 'resolving_host: calglaubot.internal', delay: 300, class: 'text-gray-400' },
{ text: 'connection_established (port: 443)', delay: 600, class: 'text-green-500/50' },
{ text: 'GET /requested_resource HTTP/1.1', delay: 900, class: 'text-blue-400/60' },
{ text: 'waiting_for_response...', delay: 1200, class: 'text-gray-500' },
{ text: 'FATAL: endpoint_not_found', delay: 2000, class: 'text-red-500 font-bold' },
{ text: 'stack_trace_dump:', delay: 2200, class: 'text-gray-600 mt-2' },
{ text: ' at Router.resolve (core.js:204)', delay: 2300, class: 'text-gray-600 pl-4' },
{ text: ' at Neobot.Handler (main.py:404)', delay: 2400, class: 'text-gray-600 pl-4' },
{ text: 'error: signal_lost_in_void', delay: 2800, class: 'text-electric/80 mt-2' }
];
let currentLine = 0;
function typeWriter() {
if (currentLine < logs.length) {
const line = logs[currentLine];
const p = document.createElement('div');
p.className = `${line.class} opacity-0 transition-opacity duration-100`;
p.textContent = `> ${line.text}`;
terminalContent.appendChild(p);
// Trigger reflow
void p.offsetWidth;
p.classList.remove('opacity-0');
// Auto scroll to bottom
const container = terminalContent.parentElement;
container.scrollTop = container.scrollHeight;
currentLine++;
setTimeout(typeWriter, Math.random() * 200 + 100); // Random typing speed variation
}
}
setTimeout(typeWriter, 500);
});
})();</script></body></html>