已经可以顺利运行

This commit is contained in:
2025-11-06 10:55:39 +08:00
parent 48c3b0a798
commit 63cd095f1b
5 changed files with 78 additions and 49 deletions

View File

@@ -9,6 +9,9 @@ from ..Convention.Runtime.GlobalConfig import *
from ..Convention.Runtime.Architecture import Architecture from ..Convention.Runtime.Architecture import Architecture
config = ProjectConfig() config = ProjectConfig()
APP_CONFIG = {
"docs_url": "/docs",
}
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(app: FastAPI):
@@ -34,11 +37,12 @@ async def lifespan(app: FastAPI):
config.Log("Info", "关闭应用完成...") config.Log("Info", "关闭应用完成...")
# db.close() # db.close()
def generate_app(APP_CONFIG: dict) -> FastAPI: def generate_app(kwargs: dict) -> FastAPI:
''' '''
生成FastAPI应用 生成FastAPI应用
''' '''
app = FastAPI(**APP_CONFIG, lifespan=lifespan) kwargs.update(**APP_CONFIG)
app = FastAPI(**kwargs, lifespan=lifespan)
# 添加并发限制中间件 # 添加并发限制中间件
app.add_middleware(ConcurrencyLimitMiddleware) app.add_middleware(ConcurrencyLimitMiddleware)
@@ -46,7 +50,7 @@ def generate_app(APP_CONFIG: dict) -> FastAPI:
# 注册路由 # 注册路由
app.include_router(callback.router, prefix="/api", tags=["callback"]) app.include_router(callback.router, prefix="/api", tags=["callback"])
app.include_router(health.router, tags=["health"]) app.include_router(health.router, tags=["health"])
ImportPlugins(app, config.FindItem("plugin_dir", "Plugins/")) ImportPlugins(app, config.FindItem("plugin_dir", "Plugins"))
# 注册至框架中 # 注册至框架中
Architecture.Register(FastAPI, app, lambda: None) Architecture.Register(FastAPI, app, lambda: None)
@@ -60,9 +64,12 @@ app: FastAPI = generate_app(config.FindItem("app_config", {}))
@app.get("/") @app.get("/")
async def root(): async def root():
"""根路径""" """根路径"""
app_name = config.FindItem("app_name", "Application")
app_version = config.FindItem("app_version", "0.0.0")
config.SaveProperties()
return JSONResponse({ return JSONResponse({
"message": config.FindItem("app_name", "Application"), "message": app_name,
"version": config.FindItem("app_version", "0.0.0"), "version": app_version,
"status": "running" "status": "running"
}) })

View File

@@ -1,3 +1,6 @@
from pickle import NONE
from ..Convention.Runtime.Config import *
from ..Convention.Runtime.GlobalConfig import ProjectConfig from ..Convention.Runtime.GlobalConfig import ProjectConfig
from ..Convention.Runtime.Architecture import Architecture from ..Convention.Runtime.Architecture import Architecture
from ..Convention.Runtime.File import ToolFile from ..Convention.Runtime.File import ToolFile
@@ -19,7 +22,7 @@ class DatabaseModel(BaseModel):
class PluginInterface(ABC): class PluginInterface(ABC):
plugin_instances: Dict[str, "PluginInterface"] = {} plugin_instances: Dict[str, "PluginInterface"] = {}
def callback(self, message: str, chat_id: int, user_id: int) -> str: async def callback(self, message: str, chat_id: int, user_id: int) -> str|None:
''' '''
继承后重写该方法接受消息并返回消息 继承后重写该方法接受消息并返回消息
返回空字符串代表不进行反馈 返回空字符串代表不进行反馈
@@ -27,9 +30,8 @@ class PluginInterface(ABC):
message: 消息内容 message: 消息内容
chat_id: 会话ID chat_id: 会话ID
user_id: 用户ID user_id: 用户ID
Returns:
str: 消息内容
''' '''
config.Log("Warning", f"插件{self.__class__.__name__}未实现callback方法")
return "" return ""
def execute(self, path:str) -> Optional[APIRouter]: def execute(self, path:str) -> Optional[APIRouter]:
@@ -39,7 +41,7 @@ class PluginInterface(ABC):
''' '''
Architecture.Register(self.__class__, self, self.wake_up, *self.dependencies()) Architecture.Register(self.__class__, self, self.wake_up, *self.dependencies())
router = APIRouter() router = APIRouter()
router.get(path)(self.generate_router_callback()) router.post(path)(self.generate_router_callback())
# 在数据库保证必要的表和列存在 # 在数据库保证必要的表和列存在
db = get_db() db = get_db()
db_model = self.register_db_model() db_model = self.register_db_model()
@@ -54,8 +56,8 @@ class PluginInterface(ABC):
''' '''
继承后重写该方法生成路由回调函数 继承后重写该方法生成路由回调函数
''' '''
async def callback(*args: Any, **kwargs: Any) -> Any: async def callback(message: str, chat_id: int, user_id: int) -> Any:
pass return await self.callback(message, chat_id, user_id)
return callback return callback
def dependencies(self) -> List[Type]: def dependencies(self) -> List[Type]:
@@ -83,7 +85,16 @@ class PluginInterface(ABC):
''' '''
return DatabaseModel() return DatabaseModel()
def ImportPlugins(app: FastAPI, plugin_dir:str = "Plugins/") -> None: def is_enable_plugin(self) -> bool:
'''
继承后重写该方法判断是否启用该插件
'''
return False
def get_plugin_tag(self) -> str:
return "plugin"
def ImportPlugins(app: FastAPI, plugin_dir:str = "Plugins") -> None:
''' '''
导入插件 导入插件
@@ -91,26 +102,31 @@ def ImportPlugins(app: FastAPI, plugin_dir:str = "Plugins/") -> None:
app: FastAPI应用 app: FastAPI应用
plugin_dir: 插件目录 plugin_dir: 插件目录
''' '''
plugin_tool_dir = ToolFile(plugin_dir) plugin_tool_dir = ToolFile(plugin_dir)|None
if plugin_tool_dir.Exists() == False: if plugin_tool_dir.Exists() == False:
plugin_tool_dir.MustExistsPath() plugin_tool_dir.MustExistsPath()
return return
if plugin_tool_dir.IsDir() == False: if plugin_tool_dir.IsDir() == False:
config.Log("Error", f"插件目录不是目录: {plugin_tool_dir.GetFullPath()}") config.Log("Error", f"插件目录不是目录: {plugin_tool_dir.GetFullPath()}")
return return
for file in plugin_tool_dir.DirIter(): for dir_name, sub_dirs, files in plugin_tool_dir.DirWalk():
if file.endswith(".py") and not file.startswith("__"): for file_name in files:
module_name = file[:-3] module_file = ToolFile(dir_name)|file_name
try: if file_name.endswith(".py") and not file_name.startswith("__"):
module = importlib.import_module(module_name) try:
for class_name in dir(module): module = importlib.import_module(f"{module_file.GetFullPath().replace(".py", '').replace('/', '.').replace('\\', '.')}")
plugin_class = getattr(module, class_name) for class_name in dir(module):
if issubclass(plugin_class, PluginInterface): plugin_class = getattr(module, class_name)
plugin = plugin_class() if not isinstance(plugin_class, type):
router = plugin.execute(f"/{module_name}") continue
if router: if issubclass(plugin_class, PluginInterface):
app.include_router(router, prefix=f"/api", tags=[module_name]) plugin = plugin_class()
except Exception as e: if plugin.is_enable_plugin() == False:
config.Log("Error", f"加载插件{module_name}失败: {e}") continue
router = plugin.execute(f"/{module_file}")
if router:
app.include_router(router, prefix=f"/api", tags=[plugin.get_plugin_tag()])
except Exception as e:
config.Log("Error", f"{ConsoleFrontColor.RED}加载插件 {module_file} 失败: {e}{ConsoleFrontColor.RESET}")
__all__ = ["ImportPlugins", "PluginInterface", "DatabaseModel"] __all__ = ["ImportPlugins", "PluginInterface", "DatabaseModel"]

View File

@@ -40,36 +40,42 @@ async def callback_receive(request: Request):
# 解析指令 # 解析指令
content = callback_data.content content = callback_data.content
command = content.split(" ")[0] command = content.split(" ")[0]
message = content[len(command):].strip()
config.Log("Info", f"识别指令: command={command}") config.Log("Info", f"识别指令: command={command}")
# TODO: 处理指令 # 处理指令
return await handle_command(command, content, callback_data.chatid, callback_data.creator) result = await handle_command(command, message, callback_data.chatid, callback_data.creator)
if result:
return JSONResponse({"result": "ok", "message": result})
else:
return JSONResponse({"result": "ok"})
except Exception as e: except Exception as e:
config.Log("Error", f"处理Callback异常: {e}", exc_info=True) config.Log("Error", f"处理Callback异常: {e}")
if ALWAYS_RETURN_OK: if ALWAYS_RETURN_OK:
return JSONResponse({"result": "ok"}) return JSONResponse({"result": "ok"})
else: else:
return JSONResponse({"result": "error", "message": str(e)}) return JSONResponse({"result": "error", "message": str(e)})
async def handle_command(command: str, content: str, async def handle_command(command: str, message: str,
chat_id: int, user_id: int) -> str: chat_id: int, user_id: int) -> str|None:
"""处理游戏指令 """处理指令
Args: Args:
command: 指令 command: 指令
content: 消息内容 message: 消息内容
chat_id: 会话ID chat_id: 会话ID
user_id: 用户ID user_id: 用户ID
Returns: Returns:
回复文本 回复文本或None
""" """
try: try:
plugin = PluginInterface.plugin_instances.get(command, None) plugin = PluginInterface.plugin_instances.get(command, None)
if plugin: if plugin:
return plugin.callback(content, chat_id, user_id) config.Log("Info", f"已找到插件注册指令: {command}, class: {plugin.__class__.__name__}")
return await plugin.callback(message, chat_id, user_id)
else: else:
return f"❌ 未识别指令: {command}" return f"❌ 未识别指令: {command}"
except Exception as e: except Exception as e:

View File

@@ -1,18 +1,21 @@
# README # PWF
Plugin-based Web Framework
## Clone ## Clone
use recursive add as submodule
```bash ```bash
git clone --recursive <repository_url> git init
git submodule add <repository_url> PWF
``` ```
or or
```bash ```bash
git clone <repository_url> git clone <repository_url> PWF
cd Convention cd PWF/Convention
git submodule update --init --recursive git submodule update --init --recursive
``` ```
@@ -22,6 +25,8 @@ use
```bash ```bash
python app.py python app.py
# from PWF.Application.app import main
# main()
``` ```
to start and generate **Assets** Folder(generate by [ProjectConfig](Convention/Runtime/GlobalConfig.py)) to start and generate **Assets** Folder(generate by [ProjectConfig](Convention/Runtime/GlobalConfig.py))
@@ -38,7 +43,6 @@ just because program not running to the place where argument been referenced
### Commandline and Config ### Commandline and Config
- **--main-webhook-url** main target of the message will be send, **needed**
- **--host** default: 0.0.0.0 - **--host** default: 0.0.0.0
- **--port** default: 8000 - **--port** default: 8000
- **--verbose** default: false - **--verbose** default: false
@@ -46,7 +50,7 @@ just because program not running to the place where argument been referenced
### Only Config ### Only Config
- **max_concurrent_requests** default: 100 - **max_concurrent_requests** default: 100
- **database_path** file on [Assets](Assets), default: db.db - **database_path** file on Assets, default: db.db
- **plugin_dir** where plugins load, default: Plugins - **plugin_dir** where plugins load, default: Plugins
- **always_return_ok** default: true - **always_return_ok** default: true
@@ -58,13 +62,11 @@ from CoreModules.plugin_interface import PluginInterface, DatabaseModel
class MyPlugin(PluginInterface): class MyPlugin(PluginInterface):
def generate_router_callback(self): def generate_router_callback(self):
"""生成路由回调函数,必需实现"""
async def callback(): async def callback():
return {"message": "Hello from MyPlugin"} return {"message": "Hello from MyPlugin"}
return callback return callback
def register_db_model(self): def register_db_model(self):
"""注册数据库模型,可选实现"""
return DatabaseModel( return DatabaseModel(
table_name="my_plugin_table", table_name="my_plugin_table",
column_names=["id", "data", "created_at"], column_names=["id", "data", "created_at"],
@@ -76,10 +78,8 @@ class MyPlugin(PluginInterface):
) )
def dependencies(self): def dependencies(self):
"""定义依赖的插件类,可选实现""" return []
return [] # 返回空列表表示无依赖
def wake_up(self): def wake_up(self):
"""依赖插件注册完成后调用,可选实现"""
pass pass
``` ```