mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-15 14:22:55 +08:00
284 lines
9.8 KiB
Python
284 lines
9.8 KiB
Python
import asyncio
|
||
from pathlib import Path
|
||
import random
|
||
|
||
from httpx import HTTPStatusError
|
||
from nonebot import on_message
|
||
from nonebot.adapters import Bot, Event
|
||
from nonebot.plugin import PluginMetadata
|
||
from nonebot_plugin_alconna import UniMsg, Voice
|
||
from nonebot_plugin_uninfo import Uninfo
|
||
|
||
from zhenxun.configs.config import BotConfig
|
||
from zhenxun.configs.path_config import IMAGE_PATH
|
||
from zhenxun.configs.utils import (
|
||
AICallableParam,
|
||
AICallableProperties,
|
||
AICallableTag,
|
||
PluginExtraData,
|
||
RegisterConfig,
|
||
)
|
||
from zhenxun.services.log import logger
|
||
from zhenxun.services.plugin_init import PluginInit
|
||
from zhenxun.utils.depends import CheckConfig, UserName
|
||
from zhenxun.utils.message import MessageUtils
|
||
|
||
from .bym_gift import ICON_PATH
|
||
from .bym_gift.data_source import send_gift
|
||
from .bym_gift.gift_reg import driver
|
||
from .config import Arparma, FunctionParam
|
||
from .data_source import ChatManager, base_config, split_text
|
||
from .exception import GiftRepeatSendException, NotResultException
|
||
from .goods_register import driver # noqa: F401
|
||
from .models.bym_chat import BymChat
|
||
|
||
__plugin_meta__ = PluginMetadata(
|
||
name="BYM_AI",
|
||
description=f"{BotConfig.self_nickname}想成为人类...",
|
||
usage=f"""
|
||
你问小真寻的愿望?
|
||
{BotConfig.self_nickname}说她想成为人类!
|
||
""".strip(),
|
||
extra=PluginExtraData(
|
||
author="Chtholly & HibiKier",
|
||
version="0.3",
|
||
ignore_prompt=True,
|
||
configs=[
|
||
RegisterConfig(
|
||
key="BYM_AI_CHAT_URL",
|
||
value=None,
|
||
help="ai聊天接口地址,可以填入url和平台名称,当你使用平台名称时,默认使用平台官方api, 目前有[gemini, DeepSeek, 硅基流动, 阿里云百炼, 百度智能云, 字节火山引擎], 填入对应名称即可, 如 gemini",
|
||
),
|
||
RegisterConfig(
|
||
key="BYM_AI_CHAT_TOKEN",
|
||
value=None,
|
||
help="ai聊天接口密钥,使用列表",
|
||
type=list[str],
|
||
),
|
||
RegisterConfig(
|
||
key="BYM_AI_CHAT_MODEL",
|
||
value=None,
|
||
help="ai聊天接口模型",
|
||
),
|
||
RegisterConfig(
|
||
key="BYM_AI_TOOL_MODEL",
|
||
value=None,
|
||
help="ai工具接口模型",
|
||
),
|
||
RegisterConfig(
|
||
key="BYM_AI_CHAT",
|
||
value=True,
|
||
help="是否开启伪人回复",
|
||
default_value=True,
|
||
type=bool,
|
||
),
|
||
RegisterConfig(
|
||
key="BYM_AI_CHAT_RATE",
|
||
value=0.05,
|
||
help="伪人回复概率 0-1",
|
||
default_value=0.05,
|
||
type=float,
|
||
),
|
||
RegisterConfig(
|
||
key="BYM_AI_CHAT_SMART",
|
||
value=False,
|
||
help="是否开启智能模式",
|
||
default_value=False,
|
||
type=bool,
|
||
),
|
||
RegisterConfig(
|
||
key="BYM_AI_TTS_URL",
|
||
value=None,
|
||
help="tts接口地址",
|
||
),
|
||
RegisterConfig(
|
||
key="BYM_AI_TTS_TOKEN",
|
||
value=None,
|
||
help="tts接口密钥",
|
||
),
|
||
RegisterConfig(
|
||
key="BYM_AI_TTS_VOICE",
|
||
value=None,
|
||
help="tts接口音色",
|
||
),
|
||
RegisterConfig(
|
||
key="ENABLE_IMPRESSION",
|
||
value=True,
|
||
help="使用签到数据作为基础好感度",
|
||
default_value=True,
|
||
type=bool,
|
||
),
|
||
RegisterConfig(
|
||
key="GROUP_CACHE_SIZE",
|
||
value=40,
|
||
help="群组内聊天记录数据大小",
|
||
default_value=40,
|
||
type=int,
|
||
),
|
||
RegisterConfig(
|
||
key="CACHE_SIZE",
|
||
value=40,
|
||
help="私聊下缓存聊天记录数据大小(每位用户)",
|
||
default_value=40,
|
||
type=int,
|
||
),
|
||
RegisterConfig(
|
||
key="ENABLE_GROUP_CHAT",
|
||
value=True,
|
||
help="在群组中时共用缓存",
|
||
default_value=True,
|
||
type=bool,
|
||
),
|
||
],
|
||
smart_tools=[
|
||
AICallableTag(
|
||
name="call_send_gift",
|
||
description="想给某人送礼物时,调用此方法,并且将返回值发送",
|
||
parameters=AICallableParam(
|
||
type="object",
|
||
properties={
|
||
"user_id": AICallableProperties(
|
||
type="string", description="用户的id"
|
||
),
|
||
},
|
||
required=["user_id"],
|
||
),
|
||
func=send_gift,
|
||
)
|
||
],
|
||
).to_dict(),
|
||
)
|
||
|
||
|
||
async def rule(event: Event, session: Uninfo) -> bool:
|
||
if event.is_tome():
|
||
"""at自身必定回复"""
|
||
return True
|
||
if not base_config.get("BYM_AI_CHAT"):
|
||
return False
|
||
if event.is_tome() and not session.group:
|
||
"""私聊过滤"""
|
||
return False
|
||
rate = base_config.get("BYM_AI_CHAT_RATE") or 0
|
||
return random.random() <= rate
|
||
|
||
|
||
_matcher = on_message(priority=998, rule=rule)
|
||
|
||
|
||
@_matcher.handle(parameterless=[CheckConfig(config="BYM_AI_CHAT_TOKEN")])
|
||
async def _(
|
||
bot: Bot,
|
||
event: Event,
|
||
message: UniMsg,
|
||
session: Uninfo,
|
||
uname: str = UserName(),
|
||
):
|
||
if not message.extract_plain_text().strip():
|
||
if event.is_tome():
|
||
await MessageUtils.build_message(ChatManager.hello()).finish()
|
||
return
|
||
fun_param = FunctionParam(
|
||
bot=bot,
|
||
event=event,
|
||
arparma=Arparma(head_result="BYM_AI"),
|
||
session=session,
|
||
message=message,
|
||
)
|
||
group_id = session.group.id if session.group else None
|
||
is_bym = not event.is_tome()
|
||
try:
|
||
try:
|
||
result = await ChatManager.get_result(
|
||
bot, session, group_id, uname, message, is_bym, fun_param
|
||
)
|
||
except HTTPStatusError as e:
|
||
logger.error("BYM AI 请求失败", "BYM_AI", session=session, e=e)
|
||
return await MessageUtils.build_message(
|
||
f"请求失败了哦,code: {e.response.status_code}"
|
||
).send(reply_to=True)
|
||
except NotResultException:
|
||
return await MessageUtils.build_message("请求没有结果呢...").send(
|
||
reply_to=True
|
||
)
|
||
if is_bym:
|
||
"""伪人回复,切割文本"""
|
||
if result:
|
||
for r, delay in split_text(result):
|
||
await MessageUtils.build_message(r).send()
|
||
await asyncio.sleep(delay)
|
||
else:
|
||
try:
|
||
if result:
|
||
await MessageUtils.build_message(result).send(
|
||
reply_to=bool(group_id)
|
||
)
|
||
if tts_data := await ChatManager.tts(result):
|
||
await MessageUtils.build_message(Voice(raw=tts_data)).send()
|
||
elif not base_config.get("BYM_AI_CHAT_SMART"):
|
||
await MessageUtils.build_message(ChatManager.no_result()).send()
|
||
else:
|
||
await MessageUtils.build_message(
|
||
f"{BotConfig.self_nickname}并不想理你..."
|
||
).send(reply_to=True)
|
||
if (
|
||
event.is_tome()
|
||
and result
|
||
and (plain_text := message.extract_plain_text())
|
||
):
|
||
await BymChat.create(
|
||
user_id=session.user.id,
|
||
group_id=group_id,
|
||
plain_text=plain_text,
|
||
result=result,
|
||
)
|
||
logger.info(
|
||
f"BYM AI 问题: {message} | 回答: {result}",
|
||
"BYM_AI",
|
||
session=session,
|
||
)
|
||
except HTTPStatusError as e:
|
||
logger.error("BYM AI 请求失败", "BYM_AI", session=session, e=e)
|
||
await MessageUtils.build_message(
|
||
f"请求失败了哦,code: {e.response.status_code}"
|
||
).send(reply_to=True)
|
||
except NotResultException:
|
||
await MessageUtils.build_message("请求没有结果呢...").send(
|
||
reply_to=True
|
||
)
|
||
except GiftRepeatSendException:
|
||
logger.warning("BYM AI 重复发送礼物", "BYM_AI", session=session)
|
||
await MessageUtils.build_message(
|
||
f"今天已经收过{BotConfig.self_nickname}的礼物了哦~"
|
||
).finish(reply_to=True)
|
||
except Exception as e:
|
||
logger.error("BYM AI 其他错误", "BYM_AI", session=session, e=e)
|
||
await MessageUtils.build_message("发生了一些异常,想要休息一下...").finish(
|
||
reply_to=True
|
||
)
|
||
|
||
|
||
RESOURCE_FILES = [
|
||
IMAGE_PATH / "shop_icon" / "reload_ai_card.png",
|
||
IMAGE_PATH / "shop_icon" / "reload_ai_card1.png",
|
||
]
|
||
|
||
GIFT_FILES = [ICON_PATH / "wallet.png", ICON_PATH / "hairpin.png"]
|
||
|
||
|
||
class MyPluginInit(PluginInit):
|
||
async def install(self):
|
||
for res_file in RESOURCE_FILES + GIFT_FILES:
|
||
res = Path(__file__).parent / res_file.name
|
||
if res.exists():
|
||
if res_file.exists():
|
||
res_file.unlink()
|
||
res.rename(res_file)
|
||
logger.info(f"更新 BYM_AI 资源文件成功 {res} -> {res_file}")
|
||
|
||
async def remove(self):
|
||
for res_file in RESOURCE_FILES + GIFT_FILES:
|
||
if res_file.exists():
|
||
res_file.unlink()
|
||
logger.info(f"删除 BYM_AI 资源文件成功 {res_file}")
|