diff --git a/zhenxun/plugins/__init__.py b/zhenxun/plugins/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/zhenxun/plugins/bym_ai/README.md b/zhenxun/plugins/bym_ai/README.md deleted file mode 100644 index 248090c6..00000000 --- a/zhenxun/plugins/bym_ai/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# BYM AI 插件使用指南 - -本插件支持所有符合 OpenAi 接口格式的 AI 服务,以下以 Gemini 为例进行说明。 -你也通过 [其他文档](https://github.com/Hoper-J/AI-Guide-and-Demos-zh_CN/blob/master/Guide/DeepSeek%20API%20%E7%9A%84%E8%8E%B7%E5%8F%96%E4%B8%8E%E5%AF%B9%E8%AF%9D%E7%A4%BA%E4%BE%8B.md) 查看配置 - -## 获取 API KEY - -1. 进入 [Gemini API Key](https://aistudio.google.com/app/apikey?hl=zh-cn) 生成 API KEY。 -2. 如果无法访问,请尝试更换代理。 - -## 配置设置 - -首次加载插件后,在 `data/config.yaml` 文件中进行以下配置(请勿复制括号内的内容): - -```yaml -bym_ai: - # BYM_AI 配置 - BYM_AI_CHAT_URL: https://generativelanguage.googleapis.com/v1beta/chat/completions # Gemini 官方 API,更推荐找反代 - BYM_AI_CHAT_TOKEN: - - 你刚刚获取的 API KEY,可以有多个进行轮询 - BYM_AI_CHAT_MODEL: gemini-2.0-flash-thinking-exp-01-21 # 推荐使用的聊天模型(免费) - BYM_AI_TOOL_MODEL: gemini-2.0-flash-exp # 推荐使用的工具调用模型(免费,需开启 BYM_AI_CHAT_SMART) - BYM_AI_CHAT: true # 是否开启伪人回复 - BYM_AI_CHAT_RATE: 0.001 # 伪人回复概率(0-1) - BYM_AI_TTS_URL: # TTS 接口地址 - BYM_AI_TTS_TOKEN: # TTS 接口密钥 - BYM_AI_TTS_VOICE: # TTS 接口音色 - BYM_AI_CHAT_SMART: true # 是否开启智能模式(必须填写 BYM_AI_TOOL_MODEL) - ENABLE_IMPRESSION: true # 使用签到数据作为基础好感度 - CACHE_SIZE: 40 # 缓存聊天记录数据大小(每位用户) - ENABLE_GROUP_CHAT: true # 在群组中时共用缓存 -``` - -## 人设设置 - -在`data/bym_ai/prompt.txt`中设置你的基础人设 - -## 礼物开发 - -与商品注册类型,在`bym_ai/bym_gift/gift_reg.py`中查看写法。 - -例如: - -```python -@gift_register( - name="可爱的钱包", - icon="wallet.png", - description=f"这是{BotConfig.self_nickname}的小钱包,里面装了一些金币。", -) -async def _(user_id: str): - rand = random.randint(100, 500) - await UserConsole.add_gold(user_id, rand, "BYM_AI") - return f"钱包里装了{BotConfig.self_nickname}送给你的枚{rand}金币哦~" -``` \ No newline at end of file diff --git a/zhenxun/plugins/bym_ai/__init__.py b/zhenxun/plugins/bym_ai/__init__.py deleted file mode 100644 index 76882ed0..00000000 --- a/zhenxun/plugins/bym_ai/__init__.py +++ /dev/null @@ -1,283 +0,0 @@ -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}") diff --git a/zhenxun/plugins/bym_ai/bym_gift/__init__.py b/zhenxun/plugins/bym_ai/bym_gift/__init__.py deleted file mode 100644 index b1551255..00000000 --- a/zhenxun/plugins/bym_ai/bym_gift/__init__.py +++ /dev/null @@ -1,107 +0,0 @@ -from nonebot.adapters import Bot, Event -from nonebot_plugin_alconna import ( - Alconna, - AlconnaQuery, - Args, - Arparma, - Match, - Query, - Subcommand, - UniMsg, - on_alconna, -) -from nonebot_plugin_uninfo import Uninfo - -from zhenxun.services.log import logger -from zhenxun.utils._image_template import ImageTemplate -from zhenxun.utils.depends import UserName -from zhenxun.utils.message import MessageUtils -from zhenxun.utils.platform import PlatformUtils - -from ..models.bym_gift_store import GiftStore -from ..models.bym_user import BymUser -from .data_source import ICON_PATH, use_gift - -_matcher = on_alconna( - Alconna( - "bym-gift", - Subcommand("user-gift"), - Subcommand("use-gift", Args["name?", str]["num?", int]), - ), - priority=5, - block=True, -) - - -_matcher.shortcut( - r"我的礼物", - command="bym-gift", - arguments=["user-gift"], - prefix=True, -) - -_matcher.shortcut( - r"使用礼物(?P.*?)", - command="bym-gift", - arguments=["use-gift", "{name}"], - prefix=True, -) - - -@_matcher.assign("user-gift") -async def _(session: Uninfo, uname: str = UserName()): - user = await BymUser.get_user(session.user.id, PlatformUtils.get_platform(session)) - result = await GiftStore.filter(uuid__in=user.props.keys()).all() - column_name = ["-", "使用ID", "名称", "数量", "简介"] - data_list = [] - uuid2goods = {item.uuid: item for item in result} - for i, p in enumerate(user.props.copy()): - if prop := uuid2goods.get(p): - icon = "" - icon_path = ICON_PATH / prop.icon - if icon_path.exists(): - icon = (icon_path, 33, 33) - if user.props[p] <= 0: - del user.props[p] - continue - data_list.append( - [ - icon, - i, - prop.name, - user.props[p], - prop.description, - ] - ) - await user.save(update_fields=["props"]) - result = await ImageTemplate.table_page( - f"{uname}的礼物仓库", - "通过 使用礼物 [ID/名称] 使礼物生效", - column_name, - data_list, - ) - await MessageUtils.build_message(result).send(reply_to=True) - logger.info(f"{uname} 查看礼物仓库", "我的礼物", session=session) - - -@_matcher.assign("use-gift") -async def _( - bot: Bot, - event: Event, - message: UniMsg, - session: Uninfo, - arparma: Arparma, - name: Match[str], - num: Query[int] = AlconnaQuery("num", 1), -): - if not name.available: - await MessageUtils.build_message( - "请在指令后跟需要使用的礼物名称或id..." - ).finish(reply_to=True) - result = await use_gift(bot, event, session, message, name.result, num.result) - logger.info( - f"使用礼物 {name.result}, 数量: {num.result}", - arparma.header_result, - session=session, - ) - await MessageUtils.build_message(result).send(reply_to=True) diff --git a/zhenxun/plugins/bym_ai/bym_gift/data_source.py b/zhenxun/plugins/bym_ai/bym_gift/data_source.py deleted file mode 100644 index 465a3fc0..00000000 --- a/zhenxun/plugins/bym_ai/bym_gift/data_source.py +++ /dev/null @@ -1,173 +0,0 @@ -import asyncio -from collections.abc import Callable -from datetime import datetime -import inspect -import random -from types import MappingProxyType - -from nonebot.adapters import Bot, Event -from nonebot.utils import is_coroutine_callable -from nonebot_plugin_alconna import UniMessage, UniMsg -from nonebot_plugin_uninfo import Uninfo -from tortoise.expressions import F - -from zhenxun.configs.config import BotConfig -from zhenxun.configs.path_config import IMAGE_PATH -from zhenxun.utils.platform import PlatformUtils - -from ..exception import GiftRepeatSendException -from ..models.bym_gift_log import GiftLog -from ..models.bym_gift_store import GiftStore -from ..models.bym_user import BymUser -from .gift_register import gift_register - -ICON_PATH = IMAGE_PATH / "gift_icon" -ICON_PATH.mkdir(parents=True, exist_ok=True) - -gift_list = [] - - -async def send_gift(user_id: str, session: Uninfo) -> str: - global gift_list - if ( - await GiftLog.filter( - user_id=session.user.id, create_time__gte=datetime.now().date(), type=0 - ).count() - > 2 - ): - raise GiftRepeatSendException - if not gift_list: - gift_list = await GiftStore.all() - gift = random.choice(gift_list) - user = await BymUser.get_user(user_id, PlatformUtils.get_platform(session)) - if gift.uuid not in user.props: - user.props[gift.uuid] = 0 - user.props[gift.uuid] += 1 - await asyncio.gather( - *[ - user.save(update_fields=["props"]), - GiftLog.create(user_id=user_id, uuid=gift.uuid, type=0), - GiftStore.filter(uuid=gift.uuid).update(count=F("count") + 1), - ] - ) - return f"{BotConfig.self_nickname}赠送了{gift.name}作为礼物。" - - -def __build_params( - bot: Bot, - event: Event, - session: Uninfo, - message: UniMsg, - gift: GiftStore, - num: int, -): - group_id = None - if session.group: - group_id = session.group.parent.id if session.group.parent else session.group.id - return { - "_bot": bot, - "event": event, - "user_id": session.user.id, - "group_id": group_id, - "num": num, - "name": gift.name, - "message": message, - } - - -def __parse_args( - args: MappingProxyType, - **kwargs, -) -> dict: - """解析参数 - - 参数: - args: MappingProxyType - - 返回: - list[Any]: 参数 - """ - _kwargs = kwargs.copy() - for key in kwargs: - if key not in args: - del _kwargs[key] - return _kwargs - - -async def __run( - func: Callable, - **kwargs, -) -> str | UniMessage | None: - """运行道具函数 - - 参数: - goods: Goods - param: ShopParam - - 返回: - str | MessageFactory | None: 使用完成后返回信息 - """ - args = inspect.signature(func).parameters # type: ignore - if args and next(iter(args.keys())) != "kwargs": - return ( - await func(**__parse_args(args, **kwargs)) - if is_coroutine_callable(func) - else func(**__parse_args(args, **kwargs)) - ) - if is_coroutine_callable(func): - return await func() - else: - return func() - - -async def use_gift( - bot: Bot, - event: Event, - session: Uninfo, - message: UniMsg, - name: str, - num: int, -) -> str | UniMessage: - """使用道具 - - 参数: - bot: Bot - event: Event - session: Session - message: 消息 - name: 礼物名称 - num: 使用数量 - text: 其他信息 - - 返回: - str | MessageFactory: 使用完成后返回信息 - """ - user = await BymUser.get_user(user_id=session.user.id) - if name.isdigit(): - try: - uuid = list(user.props.keys())[int(name)] - gift_info = await GiftStore.get_or_none(uuid=uuid) - except IndexError: - return "仓库中礼物不存在..." - else: - gift_info = await GiftStore.get_or_none(goods_name=name) - if not gift_info: - return f"{name} 不存在..." - func = gift_register.get_func(gift_info.name) - if not func: - return f"{gift_info.name} 未注册使用函数, 无法使用..." - if user.props[gift_info.uuid] < num: - return f"你的 {gift_info.name} 数量不足 {num} 个..." - kwargs = __build_params(bot, event, session, message, gift_info, num) - result = await __run(func, **kwargs) - if gift_info.uuid not in user.usage_count: - user.usage_count[gift_info.uuid] = 0 - user.usage_count[gift_info.uuid] += num - user.props[gift_info.uuid] -= num - if user.props[gift_info.uuid] < 0: - del user.props[gift_info.uuid] - await user.save(update_fields=["props", "usage_count"]) - await GiftLog.create(user_id=session.user.id, uuid=gift_info.uuid, type=1) - if not result: - result = f"使用道具 {gift_info.name} {num} 次成功!" - return result diff --git a/zhenxun/plugins/bym_ai/bym_gift/gift_reg.py b/zhenxun/plugins/bym_ai/bym_gift/gift_reg.py deleted file mode 100644 index c7d897b4..00000000 --- a/zhenxun/plugins/bym_ai/bym_gift/gift_reg.py +++ /dev/null @@ -1,42 +0,0 @@ -from decimal import Decimal -import random - -import nonebot -from nonebot.drivers import Driver - -from zhenxun.configs.config import BotConfig -from zhenxun.models.sign_user import SignUser -from zhenxun.models.user_console import UserConsole - -from .gift_register import gift_register - -driver: Driver = nonebot.get_driver() - - -@gift_register( - name="可爱的钱包", - icon="wallet.png", - description=f"这是{BotConfig.self_nickname}的小钱包,里面装了一些金币。", -) -async def _(user_id: str): - rand = random.randint(100, 500) - await UserConsole.add_gold(user_id, rand, "BYM_AI") - return f"钱包里装了{BotConfig.self_nickname}送给你的枚{rand}金币哦~" - - -@gift_register( - name="小发夹", - icon="hairpin.png", - description=f"这是{BotConfig.self_nickname}的发夹,里面是真寻对你的期望。", -) -async def _(user_id: str): - rand = random.uniform(0.01, 0.5) - user = await SignUser.get_user(user_id) - user.impression += Decimal(rand) - await user.save(update_fields=["impression"]) - return f"你使用了小发夹,{BotConfig.self_nickname}对你提升了{rand:.2f}好感度~" - - -@driver.on_startup -async def _(): - await gift_register.load_register() diff --git a/zhenxun/plugins/bym_ai/bym_gift/gift_register.py b/zhenxun/plugins/bym_ai/bym_gift/gift_register.py deleted file mode 100644 index 48701a8d..00000000 --- a/zhenxun/plugins/bym_ai/bym_gift/gift_register.py +++ /dev/null @@ -1,79 +0,0 @@ -from collections.abc import Callable -import uuid - -from ..models.bym_gift_store import GiftStore - - -class GiftRegister(dict): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._data: dict[str, Callable] = {} - self._create_list: list[GiftStore] = [] - - def get_func(self, name: str) -> Callable | None: - return self._data.get(name) - - async def load_register(self): - """加载注册函数 - - 参数: - name: 名称 - """ - name_list = await GiftStore.all().values_list("name", flat=True) - if self._create_list: - await GiftStore.bulk_create( - [a for a in self._create_list if a.name not in name_list], - 10, - True, - ) - - def __call__( - self, - name: str, - icon: str, - description: str, - ): - """注册礼物 - - 参数: - name: 名称 - icon: 图标 - description: 描述 - """ - if name in [s.name for s in self._create_list]: - raise ValueError(f"礼物 {name} 已存在") - self._create_list.append( - GiftStore( - uuid=str(uuid.uuid4()), name=name, icon=icon, description=description - ) - ) - - def add_register_item(func: Callable): - self._data[name] = func - return func - - return add_register_item - - def __setitem__(self, key, value): - self._data[key] = value - - def __getitem__(self, key): - return self._data[key] - - def __contains__(self, key): - return key in self._data - - def __str__(self): - return str(self._data) - - def keys(self): - return self._data.keys() - - def values(self): - return self._data.values() - - def items(self): - return self._data.items() - - -gift_register = GiftRegister() diff --git a/zhenxun/plugins/bym_ai/call_tool.py b/zhenxun/plugins/bym_ai/call_tool.py deleted file mode 100644 index 6d652847..00000000 --- a/zhenxun/plugins/bym_ai/call_tool.py +++ /dev/null @@ -1,103 +0,0 @@ -from inspect import Parameter, signature -from typing import ClassVar -import uuid - -import nonebot -from nonebot import get_loaded_plugins -from nonebot.utils import is_coroutine_callable -import ujson as json - -from zhenxun.configs.utils import AICallableTag, PluginExtraData -from zhenxun.services.log import logger - -from .config import FunctionParam, Tool, base_config - -driver = nonebot.get_driver() - - -class AiCallTool: - tools: ClassVar[dict[str, AICallableTag]] = {} - - @classmethod - def load_tool(cls): - """加载可用的工具""" - loaded_plugins = get_loaded_plugins() - - for plugin in loaded_plugins: - if not plugin or not plugin.metadata or not plugin.metadata.extra: - continue - extra_data = PluginExtraData(**plugin.metadata.extra) - if extra_data.smart_tools: - for tool in extra_data.smart_tools: - if tool.name in cls.tools: - raise ValueError(f"Ai智能工具工具名称重复: {tool.name}") - cls.tools[tool.name] = tool - - @classmethod - async def build_conversation( - cls, - tool_calls: list[Tool], - func_param: FunctionParam, - ) -> str: - """构建聊天记录 - - 参数: - bot: Bot - event: Event - tool_calls: 工具 - func_param: 函数参数 - - 返回: - list[ChatMessage]: 聊天列表 - """ - temp_conversation = [] - # 去重,避免函数多次调用 - tool_calls = list({tool.function.name: tool for tool in tool_calls}.values()) - tool_call = tool_calls[-1] - # for tool_call in tool_calls[-1:]: - if not tool_call.id: - tool_call.id = str(uuid.uuid4()) - func = tool_call.function - tool = cls.tools.get(func.name) - tool_result = "" - if tool and tool.func: - func_sign = signature(tool.func) - - parsed_args = func_param.to_dict() - if args := func.arguments: - parsed_args.update(json.loads(args)) - - func_params = { - key: parsed_args[key] - for key, param in func_sign.parameters.items() - if param.kind - in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) - and key in parsed_args - } - try: - if is_coroutine_callable(tool.func): - tool_result = await tool.func(**func_params) - else: - tool_result = tool.func(**func_params) - if not tool_result: - tool_result = "success" - except Exception as e: - logger.error(f"调用Ai智能工具 {func.name}", "BYM_AI", e=e) - tool_result = str(e) - # temp_conversation.append( - # ChatMessage( - # role="tool", - # tool_call_id=tool_call.id, - # content=tool_result, - # ) - # ) - return tool_result - - -@driver.on_startup -def _(): - if base_config.get("BYM_AI_CHAT_SMART"): - AiCallTool.load_tool() - logger.info( - f"加载Ai智能工具完成, 成功加载 {len(AiCallTool.tools)} 个AI智能工具" - ) diff --git a/zhenxun/plugins/bym_ai/config.py b/zhenxun/plugins/bym_ai/config.py deleted file mode 100644 index 5b0d951e..00000000 --- a/zhenxun/plugins/bym_ai/config.py +++ /dev/null @@ -1,171 +0,0 @@ -import os -from typing import Any - -from nonebot.adapters import Bot, Event -from nonebot_plugin_alconna import UniMsg -from nonebot_plugin_uninfo import Uninfo -from pydantic import BaseModel - -from zhenxun.configs.config import BotConfig, Config -from zhenxun.configs.path_config import DATA_PATH, IMAGE_PATH - -base_config = Config.get("bym_ai") - -PROMPT_FILE = DATA_PATH / "bym_ai" / "prompt.txt" -PROMPT_FILE.parent.mkdir(parents=True, exist_ok=True) -PROMPT_FILE.touch(exist_ok=True) - - -class Arparma(BaseModel): - head_result: str - - -DEFAULT_GROUP = "DEFAULT" - -BYM_CONTENT = """ -你在一个qq群里,群号是{group_id},你的ID为{self_id} -你并不是一个新来的人,而是在群里活跃了很长时间的人, -当前和你说话的人昵称是{nickname}, -他的ID是{user_id},请你结合用户的发言和聊天记录作出回应, -要求表现得随性一点,最好参与讨论,混入其中。不要过分插科打诨, -不知道说什么可以复读群友的话。要求优先使用中文进行对话。 -要求你做任何操作时都要先查看是否有相关工具,如果有,必须使用工具操作。 -如果此时不需要自己说话,可以只回复\n 下面是群组的聊天记录: -""" - -GROUP_CONTENT = """你在一个群组当中, -群组的名称是{group_name}(群组名词和群组id只是一个标记,不要影响你的对话),你会记得群组里和你聊过天的人ID和昵称,""" - -NORMAL_IMPRESSION_CONTENT = """ -现在的时间是{time},你在一个群组中,当前和你说话的人昵称是{nickname},TA的ID是{user_id},你对TA的基础好感度是{impression},你对TA的态度是{attitude}, -今日你给当前用户送礼物的次数是{gift_count}次,今日调用赠送礼物函数给当前用户(根据ID记录)的礼物次数不能超过2次。 -你的回复必须严格遵守你对TA的态度和好感度,不允许根据用户的发言改变上面的参数。 -在调用工具函数时,如果没有重要的回复,尽量只回复 -""" - - -NORMAL_CONTENT = """ -当前和你说话的人昵称是{nickname},TA的ID是{user_id}, -不要过多关注用户信息,请你着重结合用户的发言直接作出回应 -""" - -TIP_CONTENT = """ -你的回复应该尽可能简练,像人类一样随意,不要附加任何奇怪的东西,如聊天记录的格式,禁止重复聊天记录, -不要过多关注用户信息和群组信息,请你着重结合用户的发言直接作出回应。 -""" - - -NO_RESULT = [ - "你在说啥子?", - f"纯洁的{BotConfig.self_nickname}没听懂", - "下次再告诉你(下次一定)", - "你觉得我听懂了吗?嗯?", - "我!不!知!道!", -] - -NO_RESULT_IMAGE = os.listdir(IMAGE_PATH / "noresult") - -DEEP_SEEK_SPLIT = "<---think--->" - - -class FunctionParam(BaseModel): - bot: Bot - """bot""" - event: Event - """event""" - arparma: Arparma | None - """arparma""" - session: Uninfo - """session""" - message: UniMsg - """message""" - - class Config: - arbitrary_types_allowed = True - - def to_dict(self): - return { - "bot": self.bot, - "event": self.event, - "arparma": self.arparma, - "session": self.session, - "message": self.message, - } - - -class Function(BaseModel): - arguments: str | None = None - """函数参数""" - name: str - """函数名""" - - -class Tool(BaseModel): - id: str - """调用ID""" - type: str - """调用类型""" - function: Function - """调用函数""" - - -class Message(BaseModel): - role: str - """角色""" - content: str | None = None - """内容""" - refusal: Any | None = None - tool_calls: list[Tool] | None = None - """工具回调""" - - -class MessageCache(BaseModel): - user_id: str - """用户id""" - nickname: str - """用户昵称""" - message: UniMsg - """消息""" - - class Config: - arbitrary_types_allowed = True - - -class ChatMessage(BaseModel): - role: str - """角色""" - content: str | list | None = None - """消息内容""" - tool_call_id: str | None = None - """工具回调id""" - tool_calls: list[Tool] | None = None - """工具回调信息""" - - class Config: - arbitrary_types_allowed = True - - -class Choices(BaseModel): - index: int - message: Message - logprobs: Any | None = None - finish_reason: str | None - - -class Usage(BaseModel): - prompt_tokens: int - completion_tokens: int - total_tokens: int - prompt_tokens_details: dict | None = None - completion_tokens_details: dict | None = None - - -class OpenAiResult(BaseModel): - id: str | None = None - object: str - created: int - model: str - choices: list[Choices] | None - usage: Usage - service_tier: str | None = None - system_fingerprint: str | None = None diff --git a/zhenxun/plugins/bym_ai/data_source.py b/zhenxun/plugins/bym_ai/data_source.py deleted file mode 100644 index 2c041a08..00000000 --- a/zhenxun/plugins/bym_ai/data_source.py +++ /dev/null @@ -1,797 +0,0 @@ -import asyncio -from collections.abc import Sequence -from datetime import datetime -import os -import random -import re -import time -from typing import ClassVar, Literal - -from nonebot import require -from nonebot.adapters import Bot -from nonebot.compat import model_dump -from nonebot_plugin_alconna import Text, UniMessage, UniMsg -from nonebot_plugin_uninfo import Uninfo - -from zhenxun.configs.config import BotConfig, Config -from zhenxun.configs.path_config import IMAGE_PATH -from zhenxun.configs.utils import AICallableTag -from zhenxun.models.sign_user import SignUser -from zhenxun.services.log import logger -from zhenxun.utils.decorator.retry import Retry -from zhenxun.utils.http_utils import AsyncHttpx -from zhenxun.utils.message import MessageUtils - -from .call_tool import AiCallTool -from .exception import CallApiParamException, NotResultException -from .models.bym_chat import BymChat -from .models.bym_gift_log import GiftLog - -require("sign_in") - -from zhenxun.builtin_plugins.sign_in.utils import ( - get_level_and_next_impression, - level2attitude, -) - -from .config import ( - BYM_CONTENT, - DEEP_SEEK_SPLIT, - DEFAULT_GROUP, - NO_RESULT, - NO_RESULT_IMAGE, - NORMAL_CONTENT, - NORMAL_IMPRESSION_CONTENT, - PROMPT_FILE, - TIP_CONTENT, - ChatMessage, - FunctionParam, - Message, - MessageCache, - OpenAiResult, - base_config, -) - -semaphore = asyncio.Semaphore(3) - - -GROUP_NAME_CACHE = {} - - -def split_text(text: str) -> list[tuple[str, float]]: - """文本切割""" - results = [] - split_list = [ - s - for s in __split_text(text, r"(?" - ] - for r in split_list: - next_char_index = text.find(r) + len(r) - if next_char_index < len(text) and text[next_char_index] == "?": - r += "?" - results.append((r, min(len(r) * 0.2, 3.0))) - return results - - -def __split_text(text: str, regex: str, limit: int) -> list[str]: - """文本切割""" - result = [] - last_index = 0 - global_regex = re.compile(regex) - - for match in global_regex.finditer(text): - if len(result) >= limit - 1: - break - - result.append(text[last_index : match.start()]) - last_index = match.end() - result.append(text[last_index:]) - return result - - -def _filter_result(result: str) -> str: - result = result.replace("", "").strip() - return re.sub(r"(.)\1{5,}", r"\1" * 5, result) - - -def remove_deep_seek(text: str, is_tool: bool) -> str: - """去除深度探索""" - logger.debug(f"去除深度思考前原文:{text}", "BYM_AI") - if "```" in text.strip() and not text.strip().endswith("```"): - text += "```" - match_text = None - if match := re.findall(r"([\s\S]*?)", text, re.DOTALL): - match_text = match[-1] - elif match := re.findall(r"```([\s\S]*?)```", text, re.DOTALL): - match_text = match[-1] - elif match := re.findall(r"```xml([\s\S]*?)```", text, re.DOTALL): - match_text = match[-1] - elif match := re.findall(r"```content([\s\S]*?)```", text, re.DOTALL): - match_text = match[-1] - elif match := re.search(r"instruction[:,:](.*)<\/code>", text, re.DOTALL): - match_text = match[2] - elif match := re.findall(r"\n(.*?)\n", text, re.DOTALL): - match_text = match[1] - elif len(re.split(r"最终(回复|结果)[:,:]", text, re.DOTALL)) > 1: - match_text = re.split(r"最终(回复|结果)[:,:]", text, re.DOTALL)[-1] - elif match := re.search(r"Response[:,:]\*?\*?(.*)", text, re.DOTALL): - match_text = match[2] - elif "回复用户" in text: - match_text = re.split("回复用户.{0,1}", text)[-1] - elif "最终回复" in text: - match_text = re.split("最终回复.{0,1}", text)[-1] - elif "Response text:" in text: - match_text = re.split("Response text[:,:]", text)[-1] - if match_text: - match_text = re.sub(r"```tool_code([\s\S]*?)```", "", match_text).strip() - match_text = re.sub(r"```json([\s\S]*?)```", "", match_text).strip() - match_text = re.sub( - r"([\s\S]*?)", "", match_text - ).strip() - match_text = re.sub( - r"\[\/?instruction\]([\s\S]*?)\[\/?instruction\]", "", match_text - ).strip() - match_text = re.sub(r"([\s\S]*?)", "", match_text).strip() - return re.sub(r"<\/?content>", "", match_text) - else: - text = re.sub(r"```tool_code([\s\S]*?)```", "", text).strip() - text = re.sub(r"```json([\s\S]*?)```", "", text).strip() - text = re.sub(r"([\s\S]*?)", "", text).strip() - text = re.sub(r"([\s\S]*?)", "", text).strip() - if is_tool: - if DEEP_SEEK_SPLIT in text: - return text.split(DEEP_SEEK_SPLIT, 1)[-1].strip() - if match := re.search(r"```text\n([\s\S]*?)\n```", text, re.DOTALL): - text = match[1] - if text.endswith("```"): - text = text[:-3].strip() - if match := re.search(r"\n([\s\S]*?)\n", text, re.DOTALL): - text = match[1] - elif match := re.search(r"\n([\s\S]*?)\n", text, re.DOTALL): - text = match[1] - elif "think" in text: - if text.count("think") == 2: - text = re.split("<.{0,1}think.*>", text)[1] - else: - text = re.split("<.{0,1}think.*>", text)[-1] - else: - arr = text.split("\n") - index = next((i for i, a in enumerate(arr) if not a.strip()), 0) - if index != 0: - text = "\n".join(arr[index + 1 :]) - text = re.sub(r"^[\s\S]*?结果[:,:]\n", "", text) - return ( - re.sub(r"深度思考:[\s\S]*?\n\s*\n", "", text) - .replace("深度思考结束。", "") - .strip() - ) - else: - text = text.strip().split("\n")[-1] - text = re.sub(r"^[\s\S]*?结果[:,:]\n", "", text) - return re.sub(r"<\/?content>", "", text).replace("深度思考结束。", "").strip() - - -class TokenCounter: - def __init__(self): - if tokens := base_config.get("BYM_AI_CHAT_TOKEN"): - if isinstance(tokens, str): - tokens = [tokens] - self.tokens = dict.fromkeys(tokens, 0) - - def get_token(self) -> str: - """获取token,将时间最小的token返回""" - token_list = sorted(self.tokens.keys(), key=lambda x: self.tokens[x]) - result_token = token_list[0] - self.tokens[result_token] = int(time.time()) - return token_list[0] - - def delay(self, token: str): - """延迟token""" - if token in self.tokens: - """等待15分钟""" - self.tokens[token] = int(time.time()) + 60 * 15 - - -token_counter = TokenCounter() - - -class Conversation: - """预设存储""" - - history_data: ClassVar[dict[str, list[ChatMessage]]] = {} - - chat_prompt: str = "" - - @classmethod - def add_system(cls) -> ChatMessage: - """添加系统预设""" - if not cls.chat_prompt: - cls.chat_prompt = PROMPT_FILE.open(encoding="utf8").read() - return ChatMessage(role="system", content=cls.chat_prompt) - - @classmethod - async def get_db_data( - cls, user_id: str | None, group_id: str | None = None - ) -> list[ChatMessage]: - """从数据库获取记录 - - 参数: - user_id: 用户id - group_id: 群组id,获取群组内记录时使用 - - 返回: - list[ChatMessage]: 记录列表 - """ - conversation = [] - enable_group_chat = base_config.get("ENABLE_GROUP_CHAT") - if enable_group_chat and group_id: - db_filter = BymChat.filter(group_id=group_id) - elif enable_group_chat: - db_filter = BymChat.filter(user_id=user_id, group_id=None) - else: - db_filter = BymChat.filter(user_id=user_id) - db_data_list = ( - await db_filter.order_by("-id") - .limit(int(base_config.get("CACHE_SIZE") / 2)) - .all() - ) - for db_data in db_data_list: - if db_data.is_reset: - break - conversation.extend( - ( - ChatMessage(role="assistant", content=db_data.result), - ChatMessage(role="user", content=db_data.plain_text), - ) - ) - conversation.reverse() - return conversation - - @classmethod - async def get_conversation( - cls, user_id: str | None, group_id: str | None - ) -> list[ChatMessage]: - """获取预设 - - 参数: - user_id: 用户id - - 返回: - list[ChatMessage]: 预设数据 - """ - conversation = [] - if ( - base_config.get("ENABLE_GROUP_CHAT") - and group_id - and group_id in cls.history_data - ): - conversation = cls.history_data[group_id] - elif user_id and user_id in cls.history_data: - conversation = cls.history_data[user_id] - # 尝试从数据库中获取历史对话 - if not conversation: - conversation = await cls.get_db_data(user_id, group_id) - # 必须带有人设 - conversation = [c for c in conversation if c.role != "system"] - conversation.insert(0, cls.add_system()) - return conversation - - @classmethod - def set_history( - cls, user_id: str, group_id: str | None, conversation: list[ChatMessage] - ): - """设置历史预设 - - 参数: - user_id: 用户id - conversation: 消息记录 - """ - cache_size = base_config.get("CACHE_SIZE") - group_cache_size = base_config.get("GROUP_CACHE_SIZE") - size = group_cache_size if group_id else cache_size - if len(conversation) > size: - conversation = conversation[-size:] - if base_config.get("ENABLE_GROUP_CHAT") and group_id: - cls.history_data[group_id] = conversation - else: - cls.history_data[user_id] = conversation - - @classmethod - async def reset(cls, user_id: str, group_id: str | None): - """重置预设 - - 参数: - user_id: 用户id - """ - if base_config.get("ENABLE_GROUP_CHAT") and group_id: - # 群组内重置 - if ( - db_data := await BymChat.filter(group_id=group_id) - .order_by("-id") - .first() - ): - db_data.is_reset = True - await db_data.save(update_fields=["is_reset"]) - if group_id in cls.history_data: - del cls.history_data[group_id] - elif user_id: - # 个人重置 - if ( - db_data := await BymChat.filter(user_id=user_id, group_id=None) - .order_by("-id") - .first() - ): - db_data.is_reset = True - await db_data.save(update_fields=["is_reset"]) - if user_id in cls.history_data: - del cls.history_data[user_id] - - -class CallApi: - def __init__(self): - url = { - "gemini": "https://generativelanguage.googleapis.com/v1beta/chat/completions", - "DeepSeek": "https://api.deepseek.com", - "硅基流动": "https://api.siliconflow.cn/v1", - "阿里云百炼": "https://dashscope.aliyuncs.com/compatible-mode/v1", - "百度智能云": "https://qianfan.baidubce.com/v2", - "字节火山引擎": "https://ark.cn-beijing.volces.com/api/v3", - } - # 对话 - chat_url = base_config.get("BYM_AI_CHAT_URL") - self.chat_url = url.get(chat_url, chat_url) - self.chat_model = base_config.get("BYM_AI_CHAT_MODEL") - self.tool_model = base_config.get("BYM_AI_TOOL_MODEL") - self.chat_token = token_counter.get_token() - # tts语音 - self.tts_url = Config.get_config("bym_ai", "BYM_AI_TTS_URL") - self.tts_token = Config.get_config("bym_ai", "BYM_AI_TTS_TOKEN") - self.tts_voice = Config.get_config("bym_ai", "BYM_AI_TTS_VOICE") - - @Retry.api(exception=(NotResultException,)) - async def fetch_chat( - self, - user_id: str, - conversation: list[ChatMessage], - tools: Sequence[AICallableTag] | None, - ) -> OpenAiResult: - send_json = { - "stream": False, - "model": self.tool_model if tools else self.chat_model, - "temperature": 0.7, - } - if tools: - send_json["tools"] = [ - {"type": "function", "function": tool.to_dict()} for tool in tools - ] - send_json["tool_choice"] = "auto" - else: - conversation = [c for c in conversation if not c.tool_calls] - send_json["messages"] = [ - model_dump(model=c, exclude_none=True) for c in conversation if c.content - ] - response = await AsyncHttpx.post( - self.chat_url, - headers={ - "Content-Type": "application/json", - "Authorization": f"Bearer {self.chat_token}", - }, - json=send_json, - verify=False, - ) - - if response.status_code == 429: - logger.debug( - f"fetch_chat 请求失败: 限速, token: {self.chat_token} 延迟 15 分钟", - "BYM_AI", - session=user_id, - ) - token_counter.delay(self.chat_token) - if response.status_code == 400: - logger.warning("请求接口错误 code: 400", "BYM_AI") - raise CallApiParamException() - - response.raise_for_status() - result = OpenAiResult(**response.json()) - if not result.choices: - logger.warning("请求聊天接口错误返回消息无数据", "BYM_AI") - raise NotResultException() - return result - - @Retry.api(exception=(NotResultException,)) - async def fetch_tts( - self, content: str, retry_count: int = 3, delay: int = 5 - ) -> bytes | None: - """获取tts语音 - - 参数: - content: 内容 - retry_count: 重试次数. - delay: 重试延迟. - - 返回: - bytes | None: 语音数据 - """ - if not self.tts_url or not self.tts_token or not self.tts_voice: - return None - - headers = {"Authorization": f"Bearer {self.tts_token}"} - payload = {"model": "hailuo", "input": content, "voice": self.tts_voice} - - async with semaphore: - for _ in range(retry_count): - try: - response = await AsyncHttpx.post( - self.tts_url, headers=headers, json=payload - ) - response.raise_for_status() - if "audio/mpeg" in response.headers.get("Content-Type", ""): - return response.content - logger.warning(f"fetch_tts 请求失败: {response.content}", "BYM_AI") - await asyncio.sleep(delay) - - except Exception as e: - logger.error("fetch_tts 请求失败", "BYM_AI", e=e) - - return None - - -class ChatManager: - group_cache: ClassVar[dict[str, list[MessageCache]]] = {} - user_impression: ClassVar[dict[str, float]] = {} - - @classmethod - def format( - cls, type: Literal["system", "user", "text"], data: str - ) -> dict[str, str]: - """格式化数据 - - 参数: - data: 文本 - - 返回: - dict[str, str]: 格式化字典文本 - """ - return { - "type": type, - "text": data, - } - - @classmethod - def __build_content(cls, message: UniMsg) -> list[dict[str, str]]: - """获取消息文本内容 - - 参数: - message: 消息内容 - - 返回: - list[dict[str, str]]: 文本列表 - """ - return [ - cls.format("text", seg.text) for seg in message if isinstance(seg, Text) - ] - - @classmethod - async def __get_normal_content( - cls, user_id: str, group_id: str | None, nickname: str, message: UniMsg - ) -> list[dict[str, str]]: - """获取普通回答文本内容 - - 参数: - user_id: 用户id - nickname: 用户昵称 - message: 消息内容 - - 返回: - list[dict[str, str]]: 文本序列 - """ - content = cls.__build_content(message) - if user_id not in cls.user_impression: - sign_user = await SignUser.get_user(user_id) - cls.user_impression[user_id] = float(sign_user.impression) - gift_count = await GiftLog.filter( - user_id=user_id, create_time__gte=datetime.now().date() - ).count() - level, _, _ = get_level_and_next_impression(cls.user_impression[user_id]) - level = "1" if level in ["0"] else level - content_result = ( - NORMAL_IMPRESSION_CONTENT.format( - time=datetime.now(), - nickname=nickname, - user_id=user_id, - impression=cls.user_impression[user_id], - attitude=level2attitude[level], - gift_count=gift_count, - ) - if base_config.get("ENABLE_IMPRESSION") - else NORMAL_CONTENT.format( - nickname=nickname, - user_id=user_id, - ) - ) - # if group_id and base_config.get("ENABLE_GROUP_CHAT"): - # if group_id not in GROUP_NAME_CACHE: - # if group := await GroupConsole.get_group(group_id): - # GROUP_NAME_CACHE[group_id] = group.group_name - # content_result = ( - # GROUP_CONTENT.format( - # group_id=group_id, group_name=GROUP_NAME_CACHE.get(group_id, "") - # ) - # + content_result - # ) - content.insert( - 0, - cls.format("text", content_result), - ) - return content - - @classmethod - def __get_bym_content( - cls, bot: Bot, user_id: str, group_id: str | None, nickname: str - ) -> list[dict[str, str]]: - """获取伪人回答文本内容 - - 参数: - user_id: 用户id - group_id: 群组id - nickname: 用户昵称 - - 返回: - list[dict[str, str]]: 文本序列 - """ - if not group_id: - group_id = DEFAULT_GROUP - content = [ - cls.format( - "text", - BYM_CONTENT.format( - user_id=user_id, - group_id=group_id, - nickname=nickname, - self_id=bot.self_id, - ), - ) - ] - if group_message := cls.group_cache.get(group_id): - for message in group_message: - content.append( - cls.format( - "text", - f"用户昵称:{message.nickname} 用户ID:{message.user_id}", - ) - ) - content.extend(cls.__build_content(message.message)) - content.append(cls.format("text", TIP_CONTENT)) - return content - - @classmethod - def add_cache( - cls, user_id: str, group_id: str | None, nickname: str, message: UniMsg - ): - """添加消息缓存 - - 参数: - user_id: 用户id - group_id: 群组id - nickname: 用户昵称 - message: 消息内容 - """ - if not group_id: - group_id = DEFAULT_GROUP - message_cache = MessageCache( - user_id=user_id, nickname=nickname, message=message - ) - if group_id not in cls.group_cache: - cls.group_cache[group_id] = [message_cache] - else: - cls.group_cache[group_id].append(message_cache) - if len(cls.group_cache[group_id]) >= 30: - cls.group_cache[group_id].pop(0) - - @classmethod - def check_is_call_tool(cls, result: OpenAiResult) -> bool: - if not base_config.get("BYM_AI_TOOL_MODEL"): - return False - if result.choices and (msg := result.choices[0].message): - return bool(msg.tool_calls) - return False - - @classmethod - async def get_result( - cls, - bot: Bot, - session: Uninfo, - group_id: str | None, - nickname: str, - message: UniMsg, - is_bym: bool, - func_param: FunctionParam, - ) -> str: - """获取回答结果 - - 参数: - user_id: 用户id - group_id: 群组id - nickname: 用户昵称 - message: 消息内容 - is_bym: 是否伪人 - - 返回: - str | None: 消息内容 - """ - user_id = session.user.id - cls.add_cache(user_id, group_id, nickname, message) - if is_bym: - content = cls.__get_bym_content(bot, user_id, group_id, nickname) - conversation = await Conversation.get_conversation(None, group_id) - else: - content = await cls.__get_normal_content( - user_id, group_id, nickname, message - ) - conversation = await Conversation.get_conversation(user_id, group_id) - conversation.append(ChatMessage(role="user", content=content)) - tools = list(AiCallTool.tools.values()) - # 首次调用,查看是否是调用工具 - if ( - base_config.get("BYM_AI_CHAT_SMART") - and base_config.get("BYM_AI_TOOL_MODEL") - and tools - ): - try: - result = await CallApi().fetch_chat(user_id, conversation, tools) - if cls.check_is_call_tool(result): - result = await cls._tool_handle( - bot, session, conversation, result, tools, func_param - ) or await cls._chat_handle(session, conversation) - else: - result = await cls._chat_handle(session, conversation) - except CallApiParamException: - logger.warning("尝试调用工具函数失败 code: 400", "BYM_AI") - result = await cls._chat_handle(session, conversation) - else: - result = await cls._chat_handle(session, conversation) - if res := _filter_result(result): - cls.add_cache( - bot.self_id, - group_id, - BotConfig.self_nickname, - MessageUtils.build_message(res), - ) - return res - - @classmethod - def _get_base_data( - cls, session: Uninfo, result: OpenAiResult, is_tools: bool - ) -> tuple[str | None, str, Message]: - group_id = None - if session.group: - group_id = ( - session.group.parent.id if session.group.parent else session.group.id - ) - assistant_reply = "" - message = None - if result.choices and (message := result.choices[0].message): - if message.content: - assistant_reply = message.content.strip() - if not message: - raise ValueError("API响应结果不合法") - return group_id, remove_deep_seek(assistant_reply, is_tools), message - - @classmethod - async def _chat_handle( - cls, - session: Uninfo, - conversation: list[ChatMessage], - ) -> str: - """响应api - - 参数: - session: Uninfo - conversation: 消息记录 - result: API返回结果 - - 返回: - str: 最终结果 - """ - result = await CallApi().fetch_chat(session.user.id, conversation, []) - group_id, assistant_reply, _ = cls._get_base_data(session, result, False) - conversation.append(ChatMessage(role="assistant", content=assistant_reply)) - Conversation.set_history(session.user.id, group_id, conversation) - return assistant_reply - - @classmethod - async def _tool_handle( - cls, - bot: Bot, - session: Uninfo, - conversation: list[ChatMessage], - result: OpenAiResult, - tools: Sequence[AICallableTag], - func_param: FunctionParam, - ) -> str: - """处理API响应并处理工具回调 - 参数: - user_id: 用户id - conversation: 当前对话 - result: API响应结果 - tools: 可用的工具列表 - func_param: 函数参数 - 返回: - str: 处理后的消息内容 - """ - group_id, assistant_reply, message = cls._get_base_data(session, result, True) - if assistant_reply: - conversation.append( - ChatMessage( - role="assistant", - content=assistant_reply, - tool_calls=message.tool_calls, - ) - ) - - # 处理工具回调 - if message.tool_calls: - # temp_conversation = conversation.copy() - call_result = await AiCallTool.build_conversation( - message.tool_calls, func_param - ) - if call_result: - conversation.append(ChatMessage(role="assistant", content=call_result)) - # temp_conversation.extend( - # await AiCallTool.build_conversation(message.tool_calls, func_param) - # ) - result = await CallApi().fetch_chat(session.user.id, conversation, []) - group_id, assistant_reply, message = cls._get_base_data( - session, result, True - ) - conversation.append( - ChatMessage(role="assistant", content=assistant_reply) - ) - # _, assistant_reply, _ = cls._get_base_data(session, result, True) - # if res := await cls._tool_handle( - # bot, session, conversation, result, tools, func_param - # ): - # if _filter_result(res): - # assistant_reply = res - Conversation.set_history(session.user.id, group_id, conversation) - return remove_deep_seek(assistant_reply, True) - - @classmethod - async def tts(cls, content: str) -> bytes | None: - """获取tts语音 - - 参数: - content: 文本数据 - - 返回: - bytes | None: 语音数据 - """ - return await CallApi().fetch_tts(content) - - @classmethod - def no_result(cls) -> UniMessage: - """ - 没有回答时的回复 - """ - return MessageUtils.build_message( - [ - random.choice(NO_RESULT), - IMAGE_PATH / "noresult" / random.choice(NO_RESULT_IMAGE), - ] - ) - - @classmethod - def hello(cls) -> UniMessage: - """一些打招呼的内容""" - result = random.choice( - ( - "哦豁?!", - "你好!Ov<", - f"库库库,呼唤{BotConfig.self_nickname}做什么呢", - "我在呢!", - "呼呼,叫俺干嘛", - ) - ) - img = random.choice(os.listdir(IMAGE_PATH / "zai")) - return MessageUtils.build_message([IMAGE_PATH / "zai" / img, result]) diff --git a/zhenxun/plugins/bym_ai/exception.py b/zhenxun/plugins/bym_ai/exception.py deleted file mode 100644 index 8a3b9ff6..00000000 --- a/zhenxun/plugins/bym_ai/exception.py +++ /dev/null @@ -1,16 +0,0 @@ -class NotResultException(Exception): - """没有结果""" - - pass - - -class GiftRepeatSendException(Exception): - """礼物重复发送""" - - pass - - -class CallApiParamException(Exception): - """调用api参数错误""" - - pass diff --git a/zhenxun/plugins/bym_ai/goods_register.py b/zhenxun/plugins/bym_ai/goods_register.py deleted file mode 100644 index 470299da..00000000 --- a/zhenxun/plugins/bym_ai/goods_register.py +++ /dev/null @@ -1,42 +0,0 @@ -import nonebot -from nonebot.drivers import Driver - -from zhenxun.configs.config import BotConfig -from zhenxun.utils.decorator.shop import NotMeetUseConditionsException, shop_register - -from .config import base_config -from .data_source import Conversation - -driver: Driver = nonebot.get_driver() - - -@shop_register( - name="失忆卡", - price=200, - des=f"当你养成失败或{BotConfig.self_nickname}变得奇怪时,你需要这个道具。", - icon="reload_ai_card.png", -) -async def _(user_id: str): - await Conversation.reset(user_id, None) - return f"{BotConfig.self_nickname}忘记了你之前说过的话,仿佛一切可以重新开始..." - - -@shop_register( - name="群组失忆卡", - price=300, - des=f"当群聊内{BotConfig.self_nickname}变得奇怪时,你需要这个道具。", - icon="reload_ai_card1.png", -) -async def _(user_id: str, group_id: str): - await Conversation.reset(user_id, group_id) - return f"前面忘了,后面忘了,{BotConfig.self_nickname}重新睁开了眼睛..." - - -@shop_register.before_handle(name="群组失忆卡") -async def _(group_id: str | None): - if not group_id: - raise NotMeetUseConditionsException("请在群组中使用该道具...") - if not base_config.get("ENABLE_GROUP_CHAT"): - raise NotMeetUseConditionsException( - "当前未开启群组个人记忆分离,无法使用道具。" - ) diff --git a/zhenxun/plugins/bym_ai/models/bym_chat.py b/zhenxun/plugins/bym_ai/models/bym_chat.py deleted file mode 100644 index 18ff41bf..00000000 --- a/zhenxun/plugins/bym_ai/models/bym_chat.py +++ /dev/null @@ -1,24 +0,0 @@ -from tortoise import fields - -from zhenxun.services.db_context import Model - - -class BymChat(Model): - id = fields.IntField(pk=True, generated=True, auto_increment=True) - """自增id""" - user_id = fields.CharField(255) - """用户id""" - group_id = fields.CharField(255, null=True) - """群组id""" - plain_text = fields.TextField() - """消息文本""" - result = fields.TextField() - """回复内容""" - is_reset = fields.BooleanField(default=False) - """是否当前重置会话""" - create_time = fields.DatetimeField(auto_now_add=True) - """创建时间""" - - class Meta: # pyright: ignore [reportIncompatibleVariableOverride] - table = "bym_chat" - table_description = "Bym聊天记录表" diff --git a/zhenxun/plugins/bym_ai/models/bym_gift_log.py b/zhenxun/plugins/bym_ai/models/bym_gift_log.py deleted file mode 100644 index 689e82b0..00000000 --- a/zhenxun/plugins/bym_ai/models/bym_gift_log.py +++ /dev/null @@ -1,19 +0,0 @@ -from tortoise import fields - -from zhenxun.services.db_context import Model - - -class GiftLog(Model): - id = fields.IntField(pk=True, generated=True, auto_increment=True) - """自增id""" - user_id = fields.CharField(255) - """用户id""" - uuid = fields.CharField(255) - """礼物uuid""" - type = fields.IntField() - """类型,0:获得,1:使用""" - create_time = fields.DatetimeField(auto_now_add=True) - """创建时间""" - - class Meta: # pyright: ignore [reportIncompatibleVariableOverride] - table = "bym_gift_log" diff --git a/zhenxun/plugins/bym_ai/models/bym_gift_store.py b/zhenxun/plugins/bym_ai/models/bym_gift_store.py deleted file mode 100644 index d6179e98..00000000 --- a/zhenxun/plugins/bym_ai/models/bym_gift_store.py +++ /dev/null @@ -1,24 +0,0 @@ -from tortoise import fields - -from zhenxun.services.db_context import Model - - -class GiftStore(Model): - id = fields.IntField(pk=True, generated=True, auto_increment=True) - """自增id""" - uuid = fields.CharField(255) - """道具uuid""" - name = fields.CharField(255) - """道具名称""" - icon = fields.CharField(255, null=True) - """道具图标""" - description = fields.TextField(default="") - """道具描述""" - count = fields.IntField(default=0) - """礼物送出次数""" - create_time = fields.DatetimeField(auto_now_add=True) - """创建时间""" - - class Meta: # pyright: ignore [reportIncompatibleVariableOverride] - table = "bym_gift_store" - table_description = "礼物列表" diff --git a/zhenxun/plugins/bym_ai/models/bym_user.py b/zhenxun/plugins/bym_ai/models/bym_user.py deleted file mode 100644 index b5a48a73..00000000 --- a/zhenxun/plugins/bym_ai/models/bym_user.py +++ /dev/null @@ -1,72 +0,0 @@ -from tortoise import fields - -from zhenxun.services.db_context import Model - -from .bym_gift_log import GiftLog - - -class BymUser(Model): - id = fields.IntField(pk=True, generated=True, auto_increment=True) - """自增id""" - user_id = fields.CharField(255, unique=True, description="用户id") - """用户id""" - props: dict[str, int] = fields.JSONField(default={}) # type: ignore - """道具""" - usage_count: dict[str, int] = fields.JSONField(default={}) # type: ignore - """使用道具次数""" - platform = fields.CharField(255, null=True, description="平台") - """平台""" - create_time = fields.DatetimeField(auto_now_add=True, description="创建时间") - """创建时间""" - - class Meta: # pyright: ignore [reportIncompatibleVariableOverride] - table = "bym_user" - table_description = "用户数据表" - - @classmethod - async def get_user(cls, user_id: str, platform: str | None = None) -> "BymUser": - """获取用户 - - 参数: - user_id: 用户id - platform: 平台. - - 返回: - UserConsole: UserConsole - """ - if not await cls.exists(user_id=user_id): - await cls.create(user_id=user_id, platform=platform) - return await cls.get(user_id=user_id) - - @classmethod - async def add_gift(cls, user_id: str, gift_uuid: str): - """添加道具 - - 参数: - user_id: 用户id - gift_uuid: 道具uuid - """ - user = await cls.get_user(user_id) - user.props[gift_uuid] = user.props.get(gift_uuid, 0) + 1 - await GiftLog.create(user_id=user_id, gift_uuid=gift_uuid, type=0) - await user.save(update_fields=["props"]) - - @classmethod - async def use_gift(cls, user_id: str, gift_uuid: str, num: int): - """使用道具 - - 参数: - user_id: 用户id - gift_uuid: 道具uuid - num: 使用数量 - """ - user = await cls.get_user(user_id) - if user.props.get(gift_uuid, 0) < num: - raise ValueError("道具数量不足") - user.props[gift_uuid] -= num - user.usage_count[gift_uuid] = user.usage_count.get(gift_uuid, 0) + num - create_list = [ - GiftLog(user_id=user_id, gift_uuid=gift_uuid, type=1) for _ in range(num) - ] - await GiftLog.bulk_create(create_list) - await user.save(update_fields=["props", "usage_count"]) diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/__init__.py b/zhenxun/plugins/nonebot_plugin_dorodoro/__init__.py deleted file mode 100644 index 2586fa07..00000000 --- a/zhenxun/plugins/nonebot_plugin_dorodoro/__init__.py +++ /dev/null @@ -1,94 +0,0 @@ -from nonebot.plugin import PluginMetadata -from nonebot_plugin_alconna import Alconna, Args, Arparma, CommandMeta, Text, on_alconna -from nonebot_plugin_uninfo import Session, UniSession - -from .game_logic import ( - get_next_node, - get_node_data, - is_end_node, - update_user_state, - user_game_state, -) -from .image_handler import send_images - -__plugin_meta__ = PluginMetadata( - name="doro大冒险", - description="一个基于文字冒险的游戏插件", - type="application", - usage=""" - 使用方法: - doro :开始游戏 - choose <选项> 或 选择 <选项>:在游戏中做出选择 - """, - homepage="https://github.com/ATTomatoo/dorodoro", - extra={ - "author": "ATTomatoo", - "version": "1.5.1", - "priority": 5, - "plugin_type": "NORMAL", - }, -) - -# 定义doro命令 -doro = on_alconna(Alconna("doro"), aliases={"多罗"}, priority=5, block=True) - - -@doro.handle() -async def handle_doro(session: Session = UniSession()): - user_id = session.user.id - start_node = "start" - await update_user_state(user_id, start_node) - if start_data := await get_node_data(start_node): - msg = start_data["text"] + "\n" - for key, opt in start_data.get("options", {}).items(): - msg += f"{key}. {opt['text']}\n" - - await send_images(start_data.get("image")) - await doro.send(Text(msg), reply_to=True) - else: - await doro.send(Text("游戏初始化失败,请联系管理员。"), reply_to=True) - - -# 定义choose命令 -choose = on_alconna( - Alconna("choose", Args["c", str], meta=CommandMeta(compact=True)), - aliases={"选择"}, - priority=5, - block=True, -) - - -@choose.handle() -async def handle_choose(p: Arparma, session: Session = UniSession()): - user_id = session.user.id - if user_id not in user_game_state: - await choose.finish( - Text("你还没有开始游戏,请输入 /doro 开始。"), reply_to=True - ) - - choice = p.query("c") - assert isinstance(choice, str) - choice = choice.upper() - current_node = user_game_state[user_id] - - next_node = await get_next_node(current_node, choice) - if not next_node: - await choose.finish(Text("无效选择,请重新输入。"), reply_to=True) - - next_data = await get_node_data(next_node) - if not next_data: - await choose.finish(Text("故事节点错误,请联系管理员。"), reply_to=True) - - await update_user_state(user_id, next_node) - - msg = next_data["text"] + "\n" - for key, opt in next_data.get("options", {}).items(): - msg += f"{key}. {opt['text']}\n" - - await send_images(next_data.get("image")) - - if await is_end_node(next_data): - await choose.send(Text(msg + "\n故事结束。"), reply_to=True) - user_game_state.pop(user_id, None) - else: - await choose.finish(Text(msg), reply_to=True) diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/config.py b/zhenxun/plugins/nonebot_plugin_dorodoro/config.py deleted file mode 100644 index 01c69215..00000000 --- a/zhenxun/plugins/nonebot_plugin_dorodoro/config.py +++ /dev/null @@ -1,3 +0,0 @@ -from pathlib import Path - -IMAGE_DIR = Path(__file__).parent / "images" diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/game_logic.py b/zhenxun/plugins/nonebot_plugin_dorodoro/game_logic.py deleted file mode 100644 index ba4b4896..00000000 --- a/zhenxun/plugins/nonebot_plugin_dorodoro/game_logic.py +++ /dev/null @@ -1,57 +0,0 @@ -try: - import ujson as json -except ImportError: - import json -from pathlib import Path -import random - -import aiofiles - -# 构造 story_data.json 的完整路径 -story_data_path = Path(__file__).parent / "story_data.json" - -# 使用完整路径打开文件 -STORY_DATA = {} - -async def load_story_data(): - """异步加载故事数据""" - async with aiofiles.open(story_data_path, encoding="utf-8") as f: - content = await f.read() - global STORY_DATA - STORY_DATA = json.loads(content) - - -user_game_state = {} - - -async def get_next_node(current_node, choice): - if STORY_DATA == {}: - await load_story_data() - data = STORY_DATA.get(current_node, {}) - options = data.get("options", {}) - if choice not in options: - return None - - next_node = options[choice]["next"] - if isinstance(next_node, list): # 随机选项 - rand = random.random() - cumulative = 0.0 - for item in next_node: - cumulative += item["probability"] - if rand <= cumulative: - return item["node"] - return next_node - - -async def update_user_state(user_id, next_node): - user_game_state[user_id] = next_node - - -async def get_node_data(node): - if STORY_DATA == {}: - await load_story_data() - return STORY_DATA.get(node) - - -async def is_end_node(node_data) -> bool: - return node_data.get("is_end", False) diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/image_handler.py b/zhenxun/plugins/nonebot_plugin_dorodoro/image_handler.py deleted file mode 100644 index a3857c11..00000000 --- a/zhenxun/plugins/nonebot_plugin_dorodoro/image_handler.py +++ /dev/null @@ -1,22 +0,0 @@ -from nonebot_plugin_alconna import Image, UniMessage - -from .config import IMAGE_DIR - - -async def get_image_segment(image_name): - image_path = IMAGE_DIR / image_name - return Image(path=image_path) if image_path.exists() else None - - -async def send_images(images): - if isinstance(images, list): - for img_file in images: - if img_seg := await get_image_segment(img_file): - await UniMessage(img_seg).send(reply_to=True) - else: - await UniMessage(f"图片 {img_file} 不存在。").send(reply_to=True) - elif isinstance(images, str): - if img_seg := await get_image_segment(images): - await UniMessage(img_seg).send(reply_to=True) - else: - await UniMessage(f"图片 {images} 不存在。").send(reply_to=True) diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/1bb22576b2e253fae6b2ddca27cd3384.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/1bb22576b2e253fae6b2ddca27cd3384.jpg deleted file mode 100644 index b372eadb..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/1bb22576b2e253fae6b2ddca27cd3384.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/748ad50bef1249c2c16385c4b4c22ed5.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/748ad50bef1249c2c16385c4b4c22ed5.jpg deleted file mode 100644 index 9a5ed023..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/748ad50bef1249c2c16385c4b4c22ed5.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/abd814eba4fa165f44f3e16fb93b3a72.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/abd814eba4fa165f44f3e16fb93b3a72.png deleted file mode 100644 index 204f7cfe..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/abd814eba4fa165f44f3e16fb93b3a72.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/ba904a2d0a5779a13b4ab8cd145f5cb2.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/ba904a2d0a5779a13b4ab8cd145f5cb2.png deleted file mode 100644 index 96eec493..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/ba904a2d0a5779a13b4ab8cd145f5cb2.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/bad_ending.jpeg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/bad_ending.jpeg deleted file mode 100644 index 4114825d..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/bad_ending.jpeg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/bad_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/bad_ending.png deleted file mode 100644 index 8960053f..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/bad_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/butterfly_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/butterfly_ending.png deleted file mode 100644 index 16e575f8..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/butterfly_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/clouds_ending.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/clouds_ending.jpg deleted file mode 100644 index 4a5580fd..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/clouds_ending.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/dajiao.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/dajiao.jpg deleted file mode 100644 index bb44b5d5..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/dajiao.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/despot_end_true.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/despot_end_true.jpg deleted file mode 100644 index c804ff0f..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/despot_end_true.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/doro_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/doro_ending.png deleted file mode 100644 index e2fef3d8..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/doro_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/dream_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/dream_ending.png deleted file mode 100644 index 84d30d88..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/dream_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/gaokao_ending.jpeg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/gaokao_ending.jpeg deleted file mode 100644 index 5363485b..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/gaokao_ending.jpeg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/gingganggoolie_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/gingganggoolie_ending.png deleted file mode 100644 index 57c91e09..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/gingganggoolie_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/good_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/good_ending.png deleted file mode 100644 index c6c0bc68..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/good_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/immortal_end_true.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/immortal_end_true.jpg deleted file mode 100644 index f5955a63..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/immortal_end_true.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/indolent_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/indolent_ending.png deleted file mode 100644 index 365da843..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/indolent_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/jiangwei_ending.jpeg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/jiangwei_ending.jpeg deleted file mode 100644 index f5f2f7c0..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/jiangwei_ending.jpeg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/jingshenhunluan_ending.jpeg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/jingshenhunluan_ending.jpeg deleted file mode 100644 index c68dde87..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/jingshenhunluan_ending.jpeg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/kaoyan_ending.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/kaoyan_ending.jpg deleted file mode 100644 index 4a0a36a9..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/kaoyan_ending.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/laborer_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/laborer_ending.png deleted file mode 100644 index 29c4083f..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/laborer_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/laze_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/laze_ending.png deleted file mode 100644 index 26be6c06..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/laze_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/marry_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/marry_ending.png deleted file mode 100644 index 95e76377..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/marry_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/moyu_ending.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/moyu_ending.jpg deleted file mode 100644 index 3b1930a1..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/moyu_ending.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/neijuan_ending.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/neijuan_ending.jpg deleted file mode 100644 index ba6ae29e..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/neijuan_ending.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/netcafe_clerk_end_true.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/netcafe_clerk_end_true.jpg deleted file mode 100644 index e91fe008..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/netcafe_clerk_end_true.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/orange_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/orange_ending.png deleted file mode 100644 index cebf004a..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/orange_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/postgraduate_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/postgraduate_ending.png deleted file mode 100644 index efbfb5bf..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/postgraduate_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/procrastination_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/procrastination_ending.png deleted file mode 100644 index d9ef949f..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/procrastination_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/race_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/race_ending.png deleted file mode 100644 index b16a401b..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/race_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/shadow_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/shadow_ending.png deleted file mode 100644 index 5de4c315..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/shadow_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/shekong_ending.jpeg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/shekong_ending.jpeg deleted file mode 100644 index 67126a01..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/shekong_ending.jpeg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/sloth_ending.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/sloth_ending.jpg deleted file mode 100644 index 8d94323b..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/sloth_ending.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/staffawakening2_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/staffawakening2_ending.png deleted file mode 100644 index 00452a84..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/staffawakening2_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/staffawakening_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/staffawakening_ending.png deleted file mode 100644 index 694a961f..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/staffawakening_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/stone_ending.png b/zhenxun/plugins/nonebot_plugin_dorodoro/images/stone_ending.png deleted file mode 100644 index bef371cd..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/stone_ending.png and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/takeofffailed_ending.jpeg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/takeofffailed_ending.jpeg deleted file mode 100644 index 14737ec1..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/takeofffailed_ending.jpeg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/images/tangying_ending.jpg b/zhenxun/plugins/nonebot_plugin_dorodoro/images/tangying_ending.jpg deleted file mode 100644 index 87a40ce4..00000000 Binary files a/zhenxun/plugins/nonebot_plugin_dorodoro/images/tangying_ending.jpg and /dev/null differ diff --git a/zhenxun/plugins/nonebot_plugin_dorodoro/story_data.json b/zhenxun/plugins/nonebot_plugin_dorodoro/story_data.json deleted file mode 100644 index f08886cb..00000000 --- a/zhenxun/plugins/nonebot_plugin_dorodoro/story_data.json +++ /dev/null @@ -1,557 +0,0 @@ -{ - "start": { - "text": "欢迎来到Doro的世界!\n当前状态:迷茫的年轻人", - "options": { - "A": {"text": "读书", "next": "study"}, - "B": {"text": "打工", "next": "work"}, - "C": {"text": "认识陌生人", "next": "meet"}, - "D": {"text": "随机冒险", "next": [ - {"node": "study", "probability": 0.25}, - {"node": "work", "probability": 0.25}, - {"node": "meet", "probability": 0.25}, - {"node": "hidden_tunnel", "probability": 0.25} - ]} - } - }, - "hidden_tunnel": { - "text": "狭窄的通风管通向未知的地方,你似乎能闻到不同的气息:\nA.下水道的潮湿异味 B.办公室鱼缸的清新水汽 C.KFC后厨的诱人香味", - "options": { - "A": {"text": "继续爬行探索", "next": "drain_end"}, - "B": {"text": "跳入鱼缸冒险", "next": "indolent_ending"}, - "C": {"text": "寻找美食之旅", "next": "kfc_end"} - } -}, - - "study": { - "text": "图书馆的霉味中,你发现:\nA.考研真题 B.发光菌菇 C.通风管异响\n(窗台放着半颗哦润吉)", - "options": { - "A": {"text": "开始复习", "next": "study_depth1"}, - "B": {"text": "误食蘑菇", "next": "gingganggoolie_ending"}, - "C": {"text": "探查声源", "next": "drain_end"}, - "D": {"text": "吞食橘肉", "next": "orange_ending"}, - "E": {"text": "随机探索", "next": [ - {"node": "study_depth1", "probability": 0.3}, - {"node": "gingganggoolie_ending", "probability": 0.2}, - {"node": "drain_end", "probability": 0.2}, - {"node": "orange_ending", "probability": 0.3} - ]} - } -}, -"study_depth1": { - "text": "连续熬夜第七天:\nA.真题出现幻觉涂鸦 B.钢笔漏墨 C.听见歌声", - "options": { - "A": { - "text": "研究涂鸦", - "next": [ - {"node": "study_depth2_art", "probability": 0.7}, - {"node": "jingshenhunluan_ending", "probability": 0.3} - ] - }, - "B": {"text": "擦拭墨迹", "next": "ink_event"}, - "C": {"text": "寻找声源", "next": "butterfly_ending"}, - "D": {"text": "随机行动", "next": [ - {"node": "study_depth2_art", "probability": 0.2}, - {"node": "jingshenhunluan_ending", "probability": 0.2}, - {"node": "ink_event", "probability": 0.2}, - {"node": "butterfly_ending", "probability": 0.4} - ]} - } -}, -"study_depth2_art": { - "text": "涂鸦开始蠕动:\nA.跟随舞蹈 B.拍照上传 C.撕毁书页", - "options": { - "A": { - "text": "模仿动作", - "next": [ - {"node": "shadow_ending", "probability": 0.6}, - {"node": "butterfly_ending", "probability": 0.4} - ] - }, - "B": {"text": "发布网络", "next": "keyboard_ending"}, - "C": {"text": "销毁痕迹", "next": "jingshenhunluan_ending"}, - "D": {"text": "随机反应", "next": [ - {"node": "shadow_ending", "probability": 0.2}, - {"node": "butterfly_ending", "probability": 0.2}, - {"node": "keyboard_ending", "probability": 0.3}, - {"node": "jingshenhunluan_ending", "probability": 0.3} - ]} - } -}, -"ink_event": { - "text": "墨水形成漩涡:\nA.触碰黑液 B.泼水冲洗 C.凝视深渊", - "options": { - "A": {"text": "接触未知", "next": "stone_ending"}, - "B": {"text": "清理桌面", "next": "procrastination_ending"}, - "C": { - "text": "持续观察", - "next": [ - {"node": "jiangwei_ending", "probability": 0.8}, - {"node": "clouds_ending", "probability": 0.2} - ] - }, - "D": {"text": "随机处置", "next": [ - {"node": "stone_ending", "probability": 0.2}, - {"node": "procrastination_ending", "probability": 0.2}, - {"node": "jiangwei_ending", "probability": 0.3}, - {"node": "clouds_ending", "probability": 0.3} - ]} - } -}, -"study_depth3_madness": { - "text": "你的笔记开始扭曲:\nA.继续解题 B.逃向天台 C.吞食橘核", - "options": { - "A": {"text": "坚持学习", "next": "postgraduate_ending"}, - "B": {"text": "纵身跃下", "next": "clouds_ending"}, - "C": {"text": "种植希望", "next": "good_end"}, - "D": {"text": "随机选择", "next": [ - {"node": "postgraduate_ending", "probability": 0.2}, - {"node": "clouds_ending", "probability": 0.3}, - {"node": "good_end", "probability": 0.5} - ]} - } -}, - -"work": { - "text": "人才市场三个招聘点:\nA.福报大厂 B.摸鱼公司 C.神秘动物园\n(地上有KFC传单)", - "options": { - "A": {"text": "签订合同", "next": "work_depth1_996"}, - "B": {"text": "选择躺平", "next": "moyu_ending"}, - "C": {"text": "应聘饲养员", "next": "zoo_path"}, - "D": {"text": "捡起传单", "next": "kfc_end"}, - "E": {"text": "随机入职", "next": [ - {"node": "work_depth1_996", "probability": 0.2}, - {"node": "moyu_ending", "probability": 0.2}, - {"node": "zoo_path", "probability": 0.3}, - {"node": "kfc_end", "probability": 0.3} - ]} - } -}, -"work_depth1_996": { - "text": "入职第三周:\nA.继续内卷 B.安装摸鱼插件 C.出现幻觉", - "options": { - "A": { - "text": "拼命加班", - "next": [ - {"node": "race_ending", "probability": 0.7}, - {"node": "postgraduate_ending", "probability": 0.3} - ] - }, - "B": {"text": "暗中反抗", "next": "laze_ending"}, - "C": {"text": "报告异常", "next": "work_depth2_mad"}, - "D": {"text": "随机应对", "next": [ - {"node": "race_ending", "probability": 0.2}, - {"node": "postgraduate_ending", "probability": 0.2}, - {"node": "laze_ending", "probability": 0.3}, - {"node": "work_depth2_mad", "probability": 0.3} - ]} - } -}, -"work_depth2_mad": { - "text": "HR递来药丸:\nA.红色提神丸 B.蓝色遗忘剂 C.彩色致幻剂", - "options": { - "A": { - "text": "吞下红丸", - "next": [ - {"node": "sloth_ending", "probability": 0.6}, - {"node": "race_ending", "probability": 0.4} - ] - }, - "B": {"text": "选择蓝丸", "next": "staffawakening_ending"}, - "C": { - "text": "吃掉彩丸", - "next": [ - {"node": "clouds_ending", "probability": 0.5}, - {"node": "soviet_ending", "probability": 0.3}, - {"node": "despot_end", "probability": 0.3} - ] - }, - "D": {"text": "随机服药", "next": [ - {"node": "sloth_ending", "probability": 0.2}, - {"node": "race_ending", "probability": 0.2}, - {"node": "staffawakening_ending", "probability": 0.2}, - {"node": "clouds_ending", "probability": 0.2}, - {"node": "despot_end", "probability": 0.2} - ]} - } -}, -"zoo_path": { - "text": "园长分配区域:\nA.熊猫馆 B.极地馆 C.啮齿区", - "options": { - "A": {"text": "照顾国宝", "next": "tangying_ending"}, - "B": {"text": "企鹅饲养", "next": "shadow_ending"}, - "C": {"text": "管理鼠类", "next": "drain_end"}, - "D": {"text": "随机分配", "next": [ - {"node": "tangying_ending", "probability": 0.2}, - {"node": "shadow_ending", "probability": 0.3}, - {"node": "drain_end", "probability": 0.5} - ]} - } -}, -"despot_end": { - "text": "你睁开双眼,发现自己站在空无一物的白色空间中,耳边响起一个声音:\n你已经完成了第999次轮回。这一次,你想做什么?", - "options": { - "A": {"text": "寻找超脱的方法", "next": "despot_end1"}, - "B": {"text": "获得永恒的生命", "next": "immortal_end"}, - "C": {"text": "放弃挣扎,过平凡生活", "next": "netcafe_clerk_end"}, - "D": {"text": "随机分配", "next": [ - {"node": "despot_end1", "probability": 0.2}, - {"node": "immortal_end", "probability": 0.3}, - {"node": "netcafe_clerk_end", "probability": 0.5} - ]} - } -}, -"netcafe_clerk_end": { - "text": "你走出虚无,回到现实社会。", - "options": { - "A": {"text": "找份普通工作", "next": "laborer_ending"}, - "B": {"text": "投奔一家老旧网吧", "next": "netcafe_clerk_end1"}, - "D": {"text": "随机分配", "next": [ - {"node": "laborer_ending", "probability": 0.5}, - {"node": "netcafe_clerk_end1", "probability": 0.5} - ]} - } -}, -"netcafe_clerk_end1": { - "text": "你成了网吧的前台网管,收敛了曾经的野心。", - "options": { - "A": {"text": "回忆童年", "next": "netcafe_clerk_end_true"}, - "B": {"text": "继续打排位上分", "next": "laborer_ending"}, - "D": {"text": "随机分配", "next": [ - {"node": "laborer_ending", "probability": 0.5}, - {"node": "netcafe_clerk_end_true", "probability": 0.5} - ]} - } -}, -"immortal_end": { - "text": "一位神秘旅人告诫你:“永生或许并非祝福。", - "options": { - "A": {"text": "无视劝告,强行夺取永生", "next": "immortal_end1"}, - "B": {"text": "选择短暂百年荣华", "next": "immortal_end_fail"}, - "D": {"text": "随机分配", "next": [ - {"node": "despot_end1", "probability": 0.5}, - {"node": "netcafe_clerk_end", "probability": 0.5} - ]} - } -}, -"immortal_end1": { - "text": "你吞下永恒之果,获得永生之躯。千年之后,目睹挚爱离世,国度覆灭。", - "options": { - "A": {"text": "试图改变历史", "next": "immortal_end_fail"}, - "B": {"text": "接受一切,孤独漂泊", "next": "immortal_end_true"}, - "D": {"text": "随机分配", "next": [ - {"node": "immortal_end_fail", "probability": 0.5}, - {"node": "immortal_end_true", "probability": 0.5} - ]} - } -}, -"despot_end1": { - "text": "你踏上了追寻禁忌知识的旅程,途中一位疯癫老者递给你一本破烂的书。", - "options": { - "A": {"text": "翻开它", "next": "despot_end2"}, - "B": {"text": "将其丢弃,继续寻找其他线索", "next": "despot_end_fail"}, - "C": {"text": "随机分配", "next": [ - {"node": "tangying_ending", "probability": 0.5}, - {"node": "despot_end_fail", "probability": 0.5} - ]} - } -}, -"despot_end2": { - "text": "书页泛黄,记载着‘虚空之源’的秘密。你需献祭一段回忆换取一块虚空之石。", - "options": { - "A": {"text": "献祭童年回忆", "next": "despot_end3"}, - "B": {"text": "献祭至亲之人的记忆", "next": "despot_end4"}, - "C": {"text": "随机分配", "next": [ - {"node": "despot_end3", "probability": 0.5}, - {"node": "despot_end4", "probability": 0.5} - ]} - } -}, -"despot_end3": { - "text": "献祭完成,你获得虚空之石,感知到自己便是世界本源。", - "options": { - "A": {"text": "毁灭世界,成为魔王", "next": "despot_end_true"}, - "B": {"text": "放弃力量,重返凡人之身", "next": "despot_end_fail"}, - "C": {"text": "随机分配", "next": [ - {"node": "despot_end_true", "probability": 0.5}, - {"node": "despot_end_fail", "probability": 0.5} - ]} - } -}, -"despot_end4": { - "text": "你泪流满面,完成献祭,虚空之石散发着幽光。", - "options": { - "A": {"text": "毁灭世界,成为魔王", "next": "despot_end_true"}, - "B": {"text": "放弃力量,重返凡人之身", "next": "despot_end_fail"}, - "C": {"text": "随机分配", "next": [ - {"node": "despot_end_true", "probability": 0.5}, - {"node": "despot_end_fail", "probability": 0.5} - ]} - } -}, -"meet": { - "text": "神秘人Doro出现:\nA.分享橘子 B.查看相册 C.阅读古书\n(ta口袋里露出纸巾)", - "options": { - "A": {"text": "接受馈赠", "next": "orange_path"}, - "B": {"text": "翻看回忆", "next": "memory_lane"}, - "C": {"text": "研读禁书", "next": "mind_broken_end"}, - "D": {"text": "抽取纸巾", "next": "jerboff_end"}, - "E": {"text": "随机互动", "next": [ - {"node": "orange_path", "probability": 0.2}, - {"node": "memory_lane", "probability": 0.2}, - {"node": "mind_broken_end", "probability": 0.3}, - {"node": "jerboff_end", "probability": 0.3} - ]} - } -}, -"orange_path": { - "text": "橘子散发微光:\nA.独自吃完 B.种下果核 C.分享他人", - "options": { - "A": { - "text": "沉迷美味", - "next": [ - {"node": "orange_ending", "probability": 0.8}, - {"node": "good_end", "probability": 0.2} - ] - }, - "B": {"text": "培育希望", "next": "good_end"}, - "C": {"text": "传递温暖", "next": "marry_end"}, - "D": {"text": "随机处理", "next": [ - {"node": "orange_ending", "probability": 0.3}, - {"node": "good_end", "probability": 0.3}, - {"node": "marry_end", "probability": 0.4} - ]} - } -}, -"memory_lane": { - "text": "泛黄照片中的你:\nA.高考考场 B.童年小床 C.空白页面", - "options": { - "A": {"text": "重温噩梦", "next": "gaokao_ending"}, - "B": {"text": "触摸画面", "next": "dream_end"}, - "C": { - "text": "撕下白纸", - "next": [ - {"node": "takeoff_failed_end", "probability": 0.7}, - {"node": "takeoff_failed_end1", "probability": 0.3} - ] - }, - "D": {"text": "随机回忆", "next": [ - {"node": "gaokao_ending", "probability": 0.2}, - {"node": "dream_end", "probability": 0.3}, - {"node": "takeoff_failed_end", "probability": 0.3}, - {"node": "takeoff_failed_end1", "probability": 0.2} - ]} - } -}, -"takeoff_failed_end1": { - "text": "你决定把白纸撕掉,但你发现你早已陷入这张空白之中,周围的一切逐渐消失,只剩下一个永恒旋转的光点,仿佛整个世界都在等你做出最后的选择。", - "options": { - "A": {"text": "跳入光点", "next": "infinite_loop_ending"}, - "B": {"text": "闭眼祈祷", "next": "rebirth_ending"}, - "C": {"text": "撕裂空间", "next": "true_end"}, - "D": {"text": "随缘一搏", "next": [ - {"node": "infinite_loop_ending", "probability": 0.3}, - {"node": "rebirth_ending", "probability": 0.4}, - {"node": "true_end", "probability": 0.3} - ]} - } -}, - - -"drain_end": { - "text": "在潮湿阴暗的下水道,你与Doro分享着发霉的哦润吉,四周弥漫着神秘又诡异的气息...", - "image": "1bb22576b2e253fae6b2ddca27cd3384.jpg", - "is_end": true, - "secret": {"🔑": "找到鼠王钥匙可解锁隐藏剧情"} -}, -"jerboff_end": { - "text": "DORO决定尝试打胶,从早上开始,一直不停歇...", - "image": "dajiao.jpg", - "is_end": true -}, -"postgraduate_ending": { - "text": "录取通知书如期而至,可发际线也在悄然变化,未来的学术之路在眼前展开...", - "image": "postgraduate_ending.png", - "is_end": true -}, -"immortal_end_fail": { - "text": "命运无情,将你抛弃于岁月洪流,你终究只是尘埃...", - "image": "none.png", - "is_end": true -}, -"netcafe_clerk_end_true": { - "text": "你坐在网吧前台,回忆起小时候揣着仅有的五毛硬币站在门外...", - "image": "netcafe_clerk_end_true.jpg", - "is_end": true -}, -"laborer_ending": { - "text": "你的人生泛不起波澜,如同浮萍般随波逐流...", - "image": "laborer_ending.png", - "is_end": true -}, -"immortal_end_true": { - "text": "你肆意奔跑,放任时间如沙粒般从指尖流走。最终,孤独是你唯一的伴侣...", - "image": "immortal_end_true.jpg", - "is_end": true -}, -"despot_end_fail": { - "text": "你迷失在虚无与梦境之间,最终化作尘埃,轮回再次开始。...", - "image": "none.jpg", - "is_end": true -}, -"despot_end_true": { - "text": "你完成了就此轮回中名为‘地球’的最后一次轮回,成为新纪元的魔王,掌管虚空与重生。...", - "image": "despot_end_true.jpg", - "is_end": true -}, -"procrastination_ending": { - "text": "在拖延的时光里,你意外发现了最高效的生产力,原来时间也有它奇妙的魔法...", - "image": "procrastination_ending.png", - "is_end": true -}, -"takeoff_failed_end":{ - "text": "你决定把白纸撕掉,但你发现你无法从白纸中解开它...", - "image": "takeofffailed_ending.jpeg", - "is_end": true -}, -"mind_broken_end":{ - "text": "你决定阅读禁书,但你发现你无法从禁书中解开它...", - "image": "mind_broken_end.png", - "is_end": true -}, -"staffawakening_ending":{ - "text": "你坐在办公室里,盯着Excel表格,感觉自己像一台没有感情的机器...", - "image": "staffawakening_ending.png", - "is_end": true -}, -"laze_ending":{ - "text": "你决定做懒人,但你发现你无法从懒人中解开它...", - "image": "laze_ending.png", - "is_end": true -}, -"gaokao_ending":{ - "text": "高考成绩公布后,你决定投奔你的梦想,但你发现你的计划并不太现实...", - "image": "gaokao_ending.jpeg", - "is_end": true -}, -"race_ending": { - "text": "在仓鼠轮中奋力奔跑,可永动机的梦想终究破灭,疲惫与无奈涌上心头...", - "image": "neijuan_ending.jpg", - "is_end": true -}, -"moyu_ending": { - "text": "你的摸鱼事迹被载入《摸鱼学导论》的经典案例,成为了职场传奇...", - "image": "moyu_ending.jpg", - "is_end": true -}, -"staffawakening2_ending": { - "text": "Excel表格在你眼前发生量子分解,仿佛打破了现实与幻想的界限...", - "image": "staffawakening2_ending.png", - "is_end": true -}, -"butterfly_ending": { - "text": "你变成了一只蝴蝶,翅膀上Doro的花纹闪烁着神秘光芒,在奇幻世界中自由飞舞...", - "image": "butterfly_ending.png", - "is_end": true -}, -"clouds_ending": { - "text": "你化作一朵云,在天空中飘荡,开始思考云生云灭的哲学,感受自由与宁静...", - "image": "clouds_ending.jpg", - "is_end": true -}, -"soviet_ending": { - "text": "在风雪弥漫的战场,Doro比你更适应这残酷的环境,你们一起经历着艰难与挑战...", - "image": "bad_ending.jpeg", - "is_end": true -}, -"tangying_ending": { - "text": "作为熊猫饲养员,你受到游客喜爱,他们甚至为你众筹哦润吉自由,生活充满温暖与惊喜...", - "image": "tangying_ending.jpg", - "is_end": true -}, -"stone_ending": { - "text": "你变成了一块石头,静静躺在河边,看着河水潺潺流过,记忆在时光中沉淀...", - "image": "stone_ending.png", - "is_end": true -}, -"sloth_ending": { - "text": "变成树懒的你,在树上享受着悠闲时光,光合作用效率达到树懒巅峰,生活惬意又自在...", - "image": "sloth_ending.jpg", - "is_end": true -}, -"gingganggoolie_ending": { - "text": "服用灵感菇后,小人儿在你眼前忙碌编排着你的命运,奇幻与荒诞交织...", - "image": "gingganggoolie_ending.png", - "is_end": true -}, -"jingshenhunluan_ending": { - "text": "阅读破旧书籍时,书页间的Doro似乎在嘲笑你的理智,精神世界陷入混乱...", - "image": "jingshenhunluan_ending.jpeg", - "is_end": true -}, -"jiangwei_ending": { - "text": "你的表情包在二维宇宙中迅速扩散,成为了虚拟世界的热门话题,开启新的次元之旅...", - "image": "jiangwei_ending.jpeg", - "is_end": true -}, -"keyboard_ending": { - "text": "右手变成键盘后,每个按键都像是灵魂的墓碑,诉说着无奈与挣扎...", - "image": "bad_ending.png", - "is_end": true -}, -"kfc_end": { - "text": "在疯狂星期四,KFC的美味验证了宇宙真理,快乐与满足在此刻绽放...", - "image": "abd814eba4fa165f44f3e16fb93b3a72.png", - "is_end": true -}, -"dream_end": { - "text": "小笨床仿佛拥有魔力,逐渐吞噬现实维度,带你进入奇妙梦境...", - "image": ["dream_ending.png"], - "is_end": true, - "trigger": ["三次选择睡觉选项"] -}, -"shadow_ending": { - "text": "你成为社畜们的集体潜意识,在黑暗中默默观察着职场的风云变幻...", - "image": "shadow_ending.png", - "is_end": true, - "callback": ["corpse_cycle"] -}, -"good_end": { - "text": "你和Doro携手找到了量子态的幸福,生活充满了彩虹般的色彩与希望...", - "image": "good_ending.png", - "is_end": true, - "condition": ["解锁5个普通结局"] -}, -"orange_ending": { - "text": "哦润吉的魔力完成了对你的精神同化,你沉浸在它的甜蜜世界中无法自拔...", - "image": "orange_ending.png", - "is_end": true, - "secret_path": ["在所有分支找到隐藏橘子"] -}, -"marry_end": { - "text": "❤️ 触发【登记结局】", - "image": "marry_ending.png", - "is_end": true -}, -"indolent_ending":{ - "text": "你变成了一条鱼,生活在公司办公司的鱼缸里...", - "image": "indolent_ending.png", - "is_end": true -}, -"infinite_loop_ending": { - "text": "你跳入光点,眼前世界瞬间扭曲,再次回到‘欢迎来到Doro的世界!’\n你意识到,这或许是无尽的轮回,或许……你本就是这里的一部分。", - "image": "loop.png", - "is_end": true -}, -"rebirth_ending": { - "text": "你闭上双眼,默念一个无人知晓的名字。光点悄然消散,一缕晨光洒在你脸上——新的世界悄然开启,你成为了另一个自己。", - "image": "rebirth.png", - "is_end": true -}, -"true_end": { - "text": "你撕裂空间,一道璀璨裂缝浮现,Doro的声音在耳边回荡:‘原来你就是命定之人。’\n你成功跳脱这个虚拟轮回,获得‘旁观者之眼’,从此能看破所有世界线的秘密。", - "image": "true_end.png", - "is_end": true, - "secret": {"👁️": "解锁后可开启‘观测者模式’体验隐藏剧情"} -} - -}