Skip to content

Commit

Permalink
✨ 添加Bot和状态支持,重构Caller类以实现依赖注入;新增获取设备信息和运行代码的功能
Browse files Browse the repository at this point in the history
  • Loading branch information
snowykami committed Dec 15, 2024
1 parent 5fc4140 commit eb5dcb4
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 14 deletions.
18 changes: 15 additions & 3 deletions nonebot_plugin_marshoai/azure.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
)
from azure.core.credentials import AzureKeyCredential
from nonebot import get_driver, logger, on_command, on_message
from nonebot.adapters import Event, Message
from nonebot.adapters import Bot, Event, Message
from nonebot.params import CommandArg
from nonebot.permission import SUPERUSER
from nonebot.rule import Rule, to_me
from nonebot.typing import T_State
from nonebot_plugin_alconna import MsgTarget, UniMessage, UniMsg, on_alconna

from nonebot_plugin_marshoai.plugin.func_call.caller import get_function_calls
Expand Down Expand Up @@ -203,7 +204,13 @@ async def refresh_data():

@marsho_at.handle()
@marsho_cmd.handle()
async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None):
async def marsho(
target: MsgTarget,
event: Event,
bot: Bot,
state: T_State,
text: Optional[UniMsg] = None,
):
global target_list
if event.get_message().extract_plain_text() and (
not text
Expand Down Expand Up @@ -330,8 +337,13 @@ async def marsho(target: MsgTarget, event: Event, text: Optional[UniMsg] = None)
tool_call.function.name
):
logger.debug(f"调用插件函数 {tool_call.function.name}")
# 权限检查,规则检查 TODO
# 实现依赖注入,检查函数参数及参数注解类型,对Event类型的参数进行注入
caller.event = event
caller.event, caller.bot, caller.state = (
event,
bot,
state,
)
func_return = await caller.call(**function_args)
else:
logger.error(f"未找到函数 {tool_call.function.name}")
Expand Down
50 changes: 40 additions & 10 deletions nonebot_plugin_marshoai/plugin/func_call/caller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
from typing import Any

from nonebot import logger
from nonebot.adapters import Event
from nonebot.adapters import Bot, Event
from nonebot.permission import Permission
from nonebot.rule import Rule
from nonebot.typing import T_State

from ..typing import ASYNC_FUNCTION_CALL_FUNC, F
from .utils import async_wrap, is_coroutine_callable
Expand All @@ -17,23 +20,36 @@ def __init__(self, name: str | None = None, description: str | None = None):
self.func: ASYNC_FUNCTION_CALL_FUNC | None = None
self._parameters: dict[str, Any] = {}
"""依赖注入的参数"""
self.bot: Bot | None = None
self.event: Event | None = None
self.state: T_State | None = None

self._permission: Permission | None = None
self._rule: Rule | None = None

def params(self, **kwargs: Any) -> "Caller":
self._parameters.update(kwargs)
return self

def param(self, name: str, param: Any) -> "Caller":
"""设置一个函数参数
def permission(self, permission: Permission) -> "Caller":
self._permission = self._permission or permission
return self

Args:
name (str): 参数名
param (Any): 参数对象
async def pre_check(self) -> tuple[bool, str]:
if self.bot is None or self.event is None:
return False, "Context is None"
if self._permission and not await self._permission(self.bot, self.event):
return False, "Permission Denied 权限不足"

Returns:
Caller: Caller对象
"""
self._parameters[name] = param
if self.state is None:
return False, "State is None"
if self._rule and not await self._rule(self.bot, self.event, self.state):
return False, "Rule Denied 规则不匹配"

return True, ""

def rule(self, rule: Rule) -> "Caller":
self._rule = self._rule and rule
return self

def name(self, name: str) -> "Caller":
Expand Down Expand Up @@ -113,12 +129,19 @@ def data(self) -> dict[str, Any]:
def set_event(self, event: Event):
self.event = event

def set_bot(self, bot: Bot):
self.bot = bot

async def call(self, *args: Any, **kwargs: Any) -> Any:
"""调用函数
Returns:
Any: 函数返回值
"""
y, r = await self.pre_check()
if not y:
return r

if self.func is None:
raise ValueError("未注册函数对象")
sig = inspect.signature(self.func)
Expand All @@ -127,11 +150,18 @@ async def call(self, *args: Any, **kwargs: Any) -> Any:
param.annotation, Event
):
kwargs[name] = self.event

if issubclass(param.annotation, Caller) or isinstance(
param.annotation, Caller
):
kwargs[name] = self

if issubclass(param.annotation, Bot) or isinstance(param.annotation, Bot):
kwargs[name] = self.bot

if param.annotation == T_State:
kwargs[name] = self.state

# 检查形参是否有默认值或传入,若没有则用parameters中的默认值填充
for name, param in sig.parameters.items():
if name not in kwargs:
Expand Down
53 changes: 52 additions & 1 deletion nonebot_plugin_marshoai/plugins/snowykami_testplugin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import os
import platform

import psutil
from nonebot.adapters import Bot
from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.permission import SUPERUSER

from nonebot_plugin_marshoai.plugin import (
Integer,
Expand Down Expand Up @@ -53,4 +59,49 @@ def get_location() -> str:

@on_function_call(description="获取聊天者个人信息及发送的消息和function call调用参数")
async def get_user_info(e: MessageEvent, c: Caller) -> str:
return f"用户ID: {e.user_id} 用户昵称: {e.sender.nickname} FC调用参数:{c._parameters} 消息内容: {e.raw_message}"
return (
f"用户ID: {e.user_id} "
"用户昵称: {e.sender.nickname} "
"FC调用参数:{c._parameters} "
"消息内容: {e.raw_message}"
)


@on_function_call(description="获取设备信息")
def get_device_info() -> str:
"""获取机器人所运行的设备信息"""

# 进行一系列获取设备信息操作...

data = {
"cpu 性能": f"{psutil.cpu_percent()}% {psutil.cpu_freq().current:.2f}MHz {psutil.cpu_count()}线程 {psutil.cpu_count(logical=False)}物理核",
"memory 内存": f"{psutil.virtual_memory().percent}% {psutil.virtual_memory().available / 1024 / 1024 / 1024:.2f}/{psutil.virtual_memory().total / 1024 / 1024 / 1024:.2f}GB",
"swap 交换分区": f"{psutil.swap_memory().percent}% {psutil.swap_memory().used / 1024 / 1024 / 1024:.2f}/{psutil.swap_memory().total / 1024 / 1024 / 1024:.2f}GB",
"cpu 信息": f"{psutil.cpu_stats()}",
"system 系统": f"system: {platform.system()}, version: {platform.version()}, arch: {platform.architecture()}, machine: {platform.machine()}",
}
return str(data)


@on_function_call(description="在设备上运行Python代码,需要超级用户权限").params(
code=String(description="Python代码内容")
).permission(SUPERUSER)
async def run_python_code(code: str, b: Bot, e: MessageEvent) -> str:
"""运行Python代码"""
try:
r = eval(code)
except Exception as e:
return "运行出错: " + str(e)
return "运行成功: " + str(r)


@on_function_call(description="运行shell命令,需要超级用户权限").params(
command=String(description="shell命令内容")
).permission(SUPERUSER)
async def run_shell_command(command: str, b: Bot, e: MessageEvent) -> str:
"""运行shell命令"""
try:
r = os.popen(command).read()
except Exception as e:
return "运行出错: " + str(e)
return "运行成功: " + str(r)

0 comments on commit eb5dcb4

Please sign in to comment.