mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-15 14:22:55 +08:00
新增插件智能模式适配 (#1850)
* 新增插件智能模式适配 * 🚨 auto fix by pre-commit hooks * 更改类名,命名更清晰 * 🎨 添加模块化参数 * 🎨 AI模块化修改 * 🩹 道具调用修复 * 🩹 修复商品使用前检测 * ✨ retry增加参数适配 * ✨ 修改道具使用函数参数传递 * ✨ 捕获道具无法使用异常 * 🐛 添加依赖require * 🐛 修复插件使用问题 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: HibiKier <45528451+HibiKier@users.noreply.github.com>
This commit is contained in:
parent
78df9ed086
commit
a6ddb726d3
@ -18,6 +18,7 @@ from nonebot_plugin_uninfo import Uninfo
|
|||||||
|
|
||||||
from zhenxun.configs.utils import BaseBlock, Command, PluginExtraData, RegisterConfig
|
from zhenxun.configs.utils import BaseBlock, Command, PluginExtraData, RegisterConfig
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
|
from zhenxun.utils.decorator.shop import NotMeetUseConditionsException
|
||||||
from zhenxun.utils.depends import UserName
|
from zhenxun.utils.depends import UserName
|
||||||
from zhenxun.utils.enum import BlockType, PluginType
|
from zhenxun.utils.enum import BlockType, PluginType
|
||||||
from zhenxun.utils.exception import GoodsNotFound
|
from zhenxun.utils.exception import GoodsNotFound
|
||||||
@ -202,6 +203,12 @@ async def _(
|
|||||||
await MessageUtils.build_message(
|
await MessageUtils.build_message(
|
||||||
f"没有找到道具 {name.result} 或道具数量不足..."
|
f"没有找到道具 {name.result} 或道具数量不足..."
|
||||||
).send(reply_to=True)
|
).send(reply_to=True)
|
||||||
|
except NotMeetUseConditionsException as e:
|
||||||
|
if info := e.get_info():
|
||||||
|
await MessageUtils.build_message(info).finish() # type: ignore
|
||||||
|
await MessageUtils.build_message(
|
||||||
|
f"使用道具 {name.result} 的条件不满足..."
|
||||||
|
).send(reply_to=True)
|
||||||
|
|
||||||
|
|
||||||
@_matcher.assign("gold-list")
|
@_matcher.assign("gold-list")
|
||||||
|
|||||||
@ -194,7 +194,6 @@ class ShopManage:
|
|||||||
"num": num,
|
"num": num,
|
||||||
"text": text,
|
"text": text,
|
||||||
"goods_name": goods.name,
|
"goods_name": goods.name,
|
||||||
"message": message,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -203,8 +202,9 @@ class ShopManage:
|
|||||||
args: MappingProxyType,
|
args: MappingProxyType,
|
||||||
param: ShopParam,
|
param: ShopParam,
|
||||||
session: Uninfo,
|
session: Uninfo,
|
||||||
|
message: UniMsg,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> list[Any]:
|
) -> dict:
|
||||||
"""解析参数
|
"""解析参数
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
@ -212,31 +212,30 @@ class ShopManage:
|
|||||||
param: ShopParam
|
param: ShopParam
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
list[Any]: 参数
|
dict: 参数
|
||||||
"""
|
"""
|
||||||
param_list = []
|
|
||||||
_bot = param.bot
|
_bot = param.bot
|
||||||
param.bot = None
|
param.bot = None
|
||||||
param_json = {**param.to_dict(), **param.extra_data}
|
param_json = {
|
||||||
param_json["bot"] = _bot
|
"bot": _bot,
|
||||||
for par in args.keys():
|
"kwargs": kwargs,
|
||||||
if par in ["shop_param"]:
|
**param.to_dict(),
|
||||||
param_list.append(param)
|
**param.extra_data,
|
||||||
elif par in ["session"]:
|
"session": session,
|
||||||
param_list.append(session)
|
"message": message,
|
||||||
elif par in ["message"]:
|
}
|
||||||
param_list.append(kwargs.get("message"))
|
for key in list(param_json.keys()):
|
||||||
elif par not in ["args", "kwargs"]:
|
if key not in args:
|
||||||
param_list.append(param_json.get(par))
|
del param_json[key]
|
||||||
if kwargs.get(par) is not None:
|
return param_json
|
||||||
del kwargs[par]
|
|
||||||
return param_list
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def run_before_after(
|
async def run_before_after(
|
||||||
cls,
|
cls,
|
||||||
goods: Goods,
|
goods: Goods,
|
||||||
param: ShopParam,
|
param: ShopParam,
|
||||||
|
session: Uninfo,
|
||||||
|
message: UniMsg,
|
||||||
run_type: Literal["after", "before"],
|
run_type: Literal["after", "before"],
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
@ -250,16 +249,19 @@ class ShopManage:
|
|||||||
fun_list = goods.before_handle if run_type == "before" else goods.after_handle
|
fun_list = goods.before_handle if run_type == "before" else goods.after_handle
|
||||||
if fun_list:
|
if fun_list:
|
||||||
for func in fun_list:
|
for func in fun_list:
|
||||||
args = inspect.signature(func).parameters
|
if args := inspect.signature(func).parameters:
|
||||||
if args and next(iter(args.keys())) != "kwargs":
|
|
||||||
if asyncio.iscoroutinefunction(func):
|
if asyncio.iscoroutinefunction(func):
|
||||||
await func(*cls.__parse_args(args, param, **kwargs))
|
await func(
|
||||||
|
**cls.__parse_args(args, param, session, message, **kwargs)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
func(*cls.__parse_args(args, param, **kwargs))
|
func(
|
||||||
|
**cls.__parse_args(args, param, session, message, **kwargs)
|
||||||
|
)
|
||||||
elif asyncio.iscoroutinefunction(func):
|
elif asyncio.iscoroutinefunction(func):
|
||||||
await func(**kwargs)
|
await func()
|
||||||
else:
|
else:
|
||||||
func(**kwargs)
|
func()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def __run(
|
async def __run(
|
||||||
@ -267,6 +269,7 @@ class ShopManage:
|
|||||||
goods: Goods,
|
goods: Goods,
|
||||||
param: ShopParam,
|
param: ShopParam,
|
||||||
session: Uninfo,
|
session: Uninfo,
|
||||||
|
message: UniMsg,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> str | UniMessage | None:
|
) -> str | UniMessage | None:
|
||||||
"""运行道具函数
|
"""运行道具函数
|
||||||
@ -280,18 +283,20 @@ class ShopManage:
|
|||||||
"""
|
"""
|
||||||
args = inspect.signature(goods.func).parameters # type: ignore
|
args = inspect.signature(goods.func).parameters # type: ignore
|
||||||
if goods.func:
|
if goods.func:
|
||||||
if args and next(iter(args.keys())) != "kwargs":
|
if args:
|
||||||
return (
|
return (
|
||||||
await goods.func(*cls.__parse_args(args, param, session, **kwargs))
|
await goods.func(
|
||||||
|
**cls.__parse_args(args, param, session, message, **kwargs)
|
||||||
|
)
|
||||||
if asyncio.iscoroutinefunction(goods.func)
|
if asyncio.iscoroutinefunction(goods.func)
|
||||||
else goods.func(*cls.__parse_args(args, param, session, **kwargs))
|
else goods.func(
|
||||||
|
**cls.__parse_args(args, param, session, message, **kwargs)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if asyncio.iscoroutinefunction(goods.func):
|
if asyncio.iscoroutinefunction(goods.func):
|
||||||
return await goods.func(
|
return await goods.func()
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return goods.func(**kwargs)
|
return goods.func()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def use(
|
async def use(
|
||||||
@ -339,12 +344,12 @@ class ShopManage:
|
|||||||
)
|
)
|
||||||
if num > param.max_num_limit:
|
if num > param.max_num_limit:
|
||||||
return f"{goods_info.goods_name} 单次使用最大数量为{param.max_num_limit}..."
|
return f"{goods_info.goods_name} 单次使用最大数量为{param.max_num_limit}..."
|
||||||
await cls.run_before_after(goods, param, "before", **kwargs)
|
await cls.run_before_after(goods, param, session, message, "before", **kwargs)
|
||||||
result = await cls.__run(goods, param, session, **kwargs)
|
result = await cls.__run(goods, param, session, message, **kwargs)
|
||||||
await UserConsole.use_props(
|
await UserConsole.use_props(
|
||||||
session.user.id, goods_info.uuid, num, PlatformUtils.get_platform(session)
|
session.user.id, goods_info.uuid, num, PlatformUtils.get_platform(session)
|
||||||
)
|
)
|
||||||
await cls.run_before_after(goods, param, "after", **kwargs)
|
await cls.run_before_after(goods, param, session, message, "after", **kwargs)
|
||||||
if not result and param.send_success_msg:
|
if not result and param.send_success_msg:
|
||||||
result = f"使用道具 {goods.name} {num} 次成功!"
|
result = f"使用道具 {goods.name} {num} 次成功!"
|
||||||
return result
|
return result
|
||||||
|
|||||||
@ -170,6 +170,40 @@ class PluginSetting(BaseModel):
|
|||||||
"""调用插件好感度限制"""
|
"""调用插件好感度限制"""
|
||||||
|
|
||||||
|
|
||||||
|
class AICallableProperties(BaseModel):
|
||||||
|
type: str
|
||||||
|
"""参数类型"""
|
||||||
|
description: str
|
||||||
|
"""参数描述"""
|
||||||
|
enums: list[str] | None = None
|
||||||
|
"""参数枚举"""
|
||||||
|
|
||||||
|
|
||||||
|
class AICallableParam(BaseModel):
|
||||||
|
type: str
|
||||||
|
"""类型"""
|
||||||
|
properties: dict[str, AICallableProperties]
|
||||||
|
"""参数列表"""
|
||||||
|
required: list[str]
|
||||||
|
"""必要参数"""
|
||||||
|
|
||||||
|
|
||||||
|
class AICallableTag(BaseModel):
|
||||||
|
name: str
|
||||||
|
"""工具名称"""
|
||||||
|
parameters: AICallableParam | None = None
|
||||||
|
"""工具参数"""
|
||||||
|
description: str
|
||||||
|
"""工具描述"""
|
||||||
|
func: Callable | None = None
|
||||||
|
"""工具函数"""
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
result = model_dump(self)
|
||||||
|
del result["func"]
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class SchedulerModel(BaseModel):
|
class SchedulerModel(BaseModel):
|
||||||
trigger: Literal["date", "interval", "cron"]
|
trigger: Literal["date", "interval", "cron"]
|
||||||
"""trigger"""
|
"""trigger"""
|
||||||
@ -249,6 +283,8 @@ class PluginExtraData(BaseModel):
|
|||||||
"""常用sql"""
|
"""常用sql"""
|
||||||
is_show: bool = True
|
is_show: bool = True
|
||||||
"""是否显示在菜单中"""
|
"""是否显示在菜单中"""
|
||||||
|
smart_tools: list[AICallableTag] | None = None
|
||||||
|
"""智能模式函数工具集"""
|
||||||
|
|
||||||
def to_dict(self, **kwargs):
|
def to_dict(self, **kwargs):
|
||||||
return model_dump(self, **kwargs)
|
return model_dump(self, **kwargs)
|
||||||
|
|||||||
@ -5,3 +5,4 @@ require("nonebot_plugin_alconna")
|
|||||||
require("nonebot_plugin_session")
|
require("nonebot_plugin_session")
|
||||||
require("nonebot_plugin_htmlrender")
|
require("nonebot_plugin_htmlrender")
|
||||||
require("nonebot_plugin_uninfo")
|
require("nonebot_plugin_uninfo")
|
||||||
|
require("nonebot_plugin_waiter")
|
||||||
|
|||||||
@ -1,16 +1,24 @@
|
|||||||
|
from anyio import EndOfStream
|
||||||
from httpx import ConnectError, HTTPStatusError, TimeoutException
|
from httpx import ConnectError, HTTPStatusError, TimeoutException
|
||||||
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed
|
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed
|
||||||
|
|
||||||
|
|
||||||
class Retry:
|
class Retry:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def api():
|
def api(
|
||||||
|
retry_count: int = 3, wait: int = 1, exception: tuple[type[Exception], ...] = ()
|
||||||
|
):
|
||||||
"""接口调用重试"""
|
"""接口调用重试"""
|
||||||
|
base_exceptions = (
|
||||||
|
TimeoutException,
|
||||||
|
ConnectError,
|
||||||
|
HTTPStatusError,
|
||||||
|
EndOfStream,
|
||||||
|
*exception,
|
||||||
|
)
|
||||||
return retry(
|
return retry(
|
||||||
reraise=True,
|
reraise=True,
|
||||||
stop=stop_after_attempt(3),
|
stop=stop_after_attempt(retry_count),
|
||||||
wait=wait_fixed(1),
|
wait=wait_fixed(wait),
|
||||||
retry=retry_if_exception_type(
|
retry=retry_if_exception_type(base_exceptions),
|
||||||
(TimeoutException, ConnectError, HTTPStatusError)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user