From 8b0d5de95489b2af41100ae5969ba48bec5eaa1f Mon Sep 17 00:00:00 2001 From: baby20162016 <2185823427@qq.com> Date: Fri, 2 Jan 2026 15:48:15 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=88=A0=E9=99=A4base=5Fplugins=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=B9=EF=BC=8C=E8=BF=81=E7=A7=BB=E5=88=B0plugins?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9=E4=B8=8B=EF=BC=8C=E6=9B=B4=E6=94=B9?= =?UTF-8?q?models=E4=B8=AD=E7=9A=84init=E6=96=87=E4=BB=B6=E5=92=8Cmessage?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- License | 33 +++++++++--------- models/__init__.py | 55 ++++++++++++++++++++++-------- models/message.py | 3 +- {base_plugins => plugins}/jrcd.py | 22 +++++++----- {base_plugins => plugins}/thpic.py | 6 ++++ 5 files changed, 80 insertions(+), 39 deletions(-) rename {base_plugins => plugins}/jrcd.py (89%) rename {base_plugins => plugins}/thpic.py (75%) diff --git a/License b/License index 5300149..75d6d78 100644 --- a/License +++ b/License @@ -1,20 +1,21 @@ -# Plugin License (私有授权声明) -Copyright (c) 2026 Fairy Oracle Sanctuary Development Group (插件专属版权归属) +MIT License -All Rights Reserved. +Copyright (c) 2026 Fairy Oracle Sanctuary Development Group -## 授权规则 -1. 本插件为闭源私有软件,仅授权获得我方书面许可的个人/实体使用; -2. 未经许可,任何方不得复制、修改、分发、销售本插件的源代码/二进制文件,不得将插件用于任何商业/非商业用途; -3. 禁止对插件进行逆向工程、反编译、反汇编等破解行为。 +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -## 框架与插件的关系说明 -- 本插件基于我方(Fairy Oracle Sanctuary Development Group)开发的 MIT 协议框架构建; -- 框架以 MIT 协议开源(可自由使用/修改/分发),但插件为框架专属配套闭源模块,其授权规则独立于框架; -- 框架的 MIT 协议不覆盖插件,插件的使用需单独获得我方授权。 +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -## 免责声明 -本插件按“原样”提供,我方不对插件的稳定性、兼容性、安全性承担任何担保责任;因使用插件导致的任何损失,我方不承担法律责任。 - -## 授权申请 -如需获取插件使用授权(商用/非商用),请联系我们 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/models/__init__.py b/models/__init__.py index 7103dd1..3541531 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,20 +1,47 @@ from .events.base import OneBotEvent -from .events.message import MessageEvent, PrivateMessageEvent, GroupMessageEvent -from .events.notice import ( - NoticeEvent, FriendAddNoticeEvent, FriendRecallNoticeEvent, - GroupRecallNoticeEvent, GroupIncreaseNoticeEvent, - GroupDecreaseNoticeEvent, GroupAdminNoticeEvent, GroupBanNoticeEvent, - GroupUploadNoticeEvent, GroupUploadFile, - NotifyNoticeEvent, PokeNotifyEvent, LuckyKingNotifyEvent, HonorNotifyEvent, - GroupCardNoticeEvent, OfflineFileNoticeEvent, OfflineFile, - ClientStatusNoticeEvent, ClientStatus, EssenceNoticeEvent -) -from .events.request import RequestEvent, FriendRequestEvent, GroupRequestEvent -from .events.meta import MetaEvent, HeartbeatEvent, LifeCycleEvent, HeartbeatStatus from .events.factory import EventFactory +from .events.message import ( + GroupMessageEvent, + MessageEvent, + MessageSegment, + PrivateMessageEvent, +) +from .events.meta import HeartbeatEvent, HeartbeatStatus, LifeCycleEvent, MetaEvent +from .events.notice import ( + ClientStatus, + ClientStatusNoticeEvent, + EssenceNoticeEvent, + FriendAddNoticeEvent, + FriendRecallNoticeEvent, + GroupAdminNoticeEvent, + GroupBanNoticeEvent, + GroupCardNoticeEvent, + GroupDecreaseNoticeEvent, + GroupIncreaseNoticeEvent, + GroupRecallNoticeEvent, + GroupUploadFile, + GroupUploadNoticeEvent, + HonorNotifyEvent, + LuckyKingNotifyEvent, + NoticeEvent, + NotifyNoticeEvent, + OfflineFile, + OfflineFileNoticeEvent, + PokeNotifyEvent, +) +from .events.request import FriendRequestEvent, GroupRequestEvent, RequestEvent from .objects import ( - GroupInfo, GroupMemberInfo, FriendInfo, StrangerInfo, LoginInfo, - VersionInfo, Status, EssenceMessage, GroupHonorInfo, CurrentTalkative, HonorInfo + CurrentTalkative, + EssenceMessage, + FriendInfo, + GroupHonorInfo, + GroupInfo, + GroupMemberInfo, + HonorInfo, + LoginInfo, + Status, + StrangerInfo, + VersionInfo, ) # Alias for backward compatibility diff --git a/models/message.py b/models/message.py index 914a149..8669fad 100644 --- a/models/message.py +++ b/models/message.py @@ -3,6 +3,7 @@ 定义了 MessageSegment 类,用于封装 OneBot 11 的消息段。 """ + from dataclasses import dataclass from typing import Any, Dict @@ -56,7 +57,7 @@ class MessageSegment: # --- 快捷构造方法 --- @staticmethod - def text(text: str) -> "MessageSegment": + def text(text: str) -> "MessageSegment": # noqa: F811 """ 构造文本消息段 diff --git a/base_plugins/jrcd.py b/plugins/jrcd.py similarity index 89% rename from base_plugins/jrcd.py rename to plugins/jrcd.py index 2528643..4c9d422 100644 --- a/base_plugins/jrcd.py +++ b/plugins/jrcd.py @@ -5,6 +5,12 @@ from core.bot import Bot from core.command_manager import matcher from models import MessageEvent, MessageSegment +__plugin_meta__ = { + "name": "jrcd", + "description": "来看看你的长度吧!", + "usage": "/jrcd\n/bbcd [@某人]", +} + # jrcd JRCDMSG_1 = [ "今天的长度是%scm,可以让我一口吃掉吗罒ω罒", @@ -23,15 +29,15 @@ JRCDMSG_3 = [ "今天的长度是%scm,单是看到哥哥的长度就....(〃w〃)", ] -# bbcd short -BBCDMSG1 = ["还行,可以尝试一下(๑‾ ꇴ ‾๑)"] -BBCDMSG2 = ["差的不多,富贵险中求一下(*°ー°)v?"] -BBCDMSG3 = ["快逃!!!!!!!!(o(*°▽°*)o)"] - # bbcd long -BBCDMSG4 = ["差的不多,富贵险中求一下(*°ー°)v?", "还行,可以尝试一下(๑‾ ꇴ ‾๑)"] -BBCDMSG5 = ["不错的成绩,努力一下或许可以让他受孕哦..(〃w〃)"] -BBCDMSG6 = ["好猛,试试强制让他受孕吧!!!(((o(*°▽°*)o)))"] +BBCDMSG1 = ["还行,可以尝试一下(๑‾ ꇴ ‾๑)"] +BBCDMSG2 = ["不错的成绩,努力一下或许可以让他受孕哦..(〃w〃)"] +BBCDMSG3 = ["好猛,试试强制让他受孕吧!!!(((o(*°▽°*)o)))"] + +# bbcd short +BBCDMSG4 = ["差的不多,富贵险中求一下(*°ー°)v?"] +BBCDMSG5 = ["还行,可以尝试一下(๑‾ ꇴ ‾๑)"] +BBCDMSG6 = ["快逃!!!!!!!!(o(*°▽°*)o)"] # bbcd equal BBCDMSG7 = ["试试刺刀看看谁能赢吧!"] diff --git a/base_plugins/thpic.py b/plugins/thpic.py similarity index 75% rename from base_plugins/thpic.py rename to plugins/thpic.py index caa752d..1d8c462 100644 --- a/base_plugins/thpic.py +++ b/plugins/thpic.py @@ -5,6 +5,12 @@ thpic 插件 """ +__plugin_meta__ = { + "name": "thpic", + "description": "来看看东方Project的图片吧!", + "usage": "/thpic", +} + from core.bot import Bot from core.command_manager import matcher from models import MessageEvent, MessageSegment From 026ce8c773a019815ae64859c1193fd89c1b4224 Mon Sep 17 00:00:00 2001 From: baby20162016 <2185823427@qq.com> Date: Fri, 2 Jan 2026 17:40:57 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8F=92=E4=BB=B6admin?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E5=9C=A8models=E4=B8=AD=E6=96=B0=E5=A2=9EPlu?= =?UTF-8?q?ginDataManager=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/__init__.py | 3 +- core/plugin_manager.py | 85 +++++++++++++++++++++++++++-- plugins/admin.py | 115 ++++++++++++++++++++++++++++++++++++++++ plugins/data/admin.json | 1 + plugins/thpic.py | 8 +-- 5 files changed, 202 insertions(+), 10 deletions(-) create mode 100644 plugins/admin.py create mode 100644 plugins/data/admin.json diff --git a/core/__init__.py b/core/__init__.py index dc3ae5d..032d0c6 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,5 +1,6 @@ from .command_manager import matcher from .config_loader import global_config +from .plugin_manager import PluginDataManager from .ws import WS -__all__ = ["WS", "matcher", "global_config"] +__all__ = ["WS", "matcher", "global_config", "PluginDataManager"] diff --git a/core/plugin_manager.py b/core/plugin_manager.py index df15828..9a1e039 100644 --- a/core/plugin_manager.py +++ b/core/plugin_manager.py @@ -3,7 +3,9 @@ 负责扫描、加载和管理 `base_plugins` 目录下的所有插件。 """ + import importlib +import json import os import pkgutil import sys @@ -18,10 +20,12 @@ def load_all_plugins(): 该函数会遍历 `plugins` 目录下的所有模块: 1. 如果模块已加载,则执行 reload 操作(用于热重载)。 2. 如果模块未加载,则执行 import 操作。 - + 加载过程中会提取插件元数据 `__plugin_meta__` 并注册到 CommandManager。 """ - plugin_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "plugins") + plugin_dir = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "..", "plugins" + ) package_name = "plugins" print(f" 正在从 {package_name} 加载插件...") @@ -36,13 +40,84 @@ def load_all_plugins(): else: module = importlib.import_module(full_module_name) action = "加载" - + # 提取插件元数据 if hasattr(module, "__plugin_meta__"): meta = getattr(module, "__plugin_meta__") matcher.plugins[full_module_name] = meta - + type_str = "包" if is_pkg else "文件" print(f" [{type_str}] 成功{action}: {module_name}") except Exception as e: - print(f" {action if 'action' in locals() else '加载'}插件 {module_name} 失败: {e}") + print( + f" {action if 'action' in locals() else '加载'}插件 {module_name} 失败: {e}" + ) + + +class PluginDataManager: + """ + 用于管理插件产生的数据文件的类 + """ + + def __init__(self, plugin_name: str): + """ + 初始化插件数据管理器 + + :param plugin_name: 插件名称 + """ + self.plugin_name = plugin_name + self.data_file = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "..", + "plugins", + "data", + self.plugin_name + ".json", + ) + self.data = {} + self.load() + + def load(self): + """读取配置文件""" + if not os.path.exists(self.data_file): + with open(self.data_file, "w", encoding="utf-8") as f: + self.set(self.plugin_name, []) + try: + with open(self.data_file, "r", encoding="utf-8") as f: + self.data = json.load(f) + except json.JSONDecodeError: + self.data = {} + + def save(self): + """保存配置到文件""" + with open(self.data_file, "w", encoding="utf-8") as f: + json.dump(self.data, f, indent=2, ensure_ascii=False) + + def get(self, key, default=None): + """获取配置项""" + return self.data.get(key, default) + + def set(self, key, value): + """设置配置项""" + self.data[key] = value + self.save() + + def add(self, key, value): + """添加配置项""" + if key not in self.data: + self.data[key] = [] + self.data[key].append(value) + self.save() + + def remove(self, key): + """删除配置项""" + if key in self.data: + del self.data[key] + self.save() + + def clear(self): + """清空所有配置""" + self.data.clear() + self.save() + + def get_all(self): + return self.data.copy() diff --git a/plugins/admin.py b/plugins/admin.py new file mode 100644 index 0000000..8e1b0e6 --- /dev/null +++ b/plugins/admin.py @@ -0,0 +1,115 @@ +from core import PluginDataManager +from core.bot import Bot +from core.command_manager import matcher +from models import GroupMessageEvent + +__plugin_meta__ = { + "name": "admin", + "description": "机器人权限管理插件", + "usage": "/admin", +} + +data = PluginDataManager("admin") + + +@matcher.command("admin") +async def handle_permission(bot: Bot, event: GroupMessageEvent, args: list[str]): + if not args: + await event.reply( + "机器人权限管理插件指令:\n/admin list 列出所有权限\n/admin add member 添加群成员权限\n/admin remove member 删除群成员权限\n/admin add group <群号> 添加群权限\n/admin remove group <群号> 删除群权限\n/admin clear member 清空群成员权限\n/admin clear group 清空群权限\n/admin clear all 清空所有权限" + ) + return + + if str(event.user_id) not in data.get("members", []): + await event.reply("你没有权限使用此命令。") + return + if str(event.group_id) not in data.get("groups", []): + await event.reply("群聊不在权限中") + return + + action = args[0].lower() + + # ensure storage keys exist + members = data.get("members", []) or [] + groups = data.get("groups", []) or [] + + if action == "list": + msg_lines = ["当前权限列表:"] + msg_lines.append( + f"群成员权限 ({len(members)}): {', '.join(members) if members else '无'}" + ) + msg_lines.append( + f"群权限 ({len(groups)}): {', '.join(groups) if groups else '无'}" + ) + await event.reply("\n".join(msg_lines)) + return + + if action in ("add", "remove"): + if len(args) < 3: + await event.reply("参数错误,示例:/admin add member 123456") + return + + target = args[1].lower() + value = args[2] + + if target == "member": + # operate on members list + if action == "add": + if str(value) in members: + await event.reply(f"成员 {value} 已存在,无需重复添加。") + return + members.append(str(value)) + data.set("members", members) + await event.reply(f"已添加群成员权限:{value}") + return + else: # remove + if str(value) not in members: + await event.reply(f"成员 {value} 不在权限列表中。") + return + members = [m for m in members if m != str(value)] + data.set("members", members) + await event.reply(f"已移除群成员权限:{value}") + return + + if target == "group": + if action == "add": + if str(value) in groups: + await event.reply(f"群 {value} 已存在,无需重复添加。") + return + groups.append(str(value)) + data.set("groups", groups) + await event.reply(f"已添加群权限:{value}") + return + else: # remove + if str(value) not in groups: + await event.reply(f"群 {value} 不在权限列表中。") + return + groups = [g for g in groups if g != str(value)] + data.set("groups", groups) + await event.reply(f"已移除群权限:{value}") + return + + await event.reply("未知目标类型,请使用 member 或 group") + return + + if action == "clear": + if len(args) < 2: + await event.reply("参数错误,示例:/admin clear member") + return + target = args[1].lower() + if target == "member": + data.set("members", []) + await event.reply("已清空群成员权限。") + return + if target == "group": + data.set("groups", []) + await event.reply("已清空群权限。") + return + if target == "all": + data.clear() + await event.reply("已清空所有权限。") + return + await event.reply("未知清空目标,请使用 member/group/all") + return + + await event.reply("未知指令,使用 /admin 查看帮助") diff --git a/plugins/data/admin.json b/plugins/data/admin.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/plugins/data/admin.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/plugins/thpic.py b/plugins/thpic.py index 1d8c462..80e2cfd 100644 --- a/plugins/thpic.py +++ b/plugins/thpic.py @@ -5,16 +5,16 @@ thpic 插件 """ +from core.bot import Bot +from core.command_manager import matcher +from models import MessageEvent, MessageSegment + __plugin_meta__ = { "name": "thpic", "description": "来看看东方Project的图片吧!", "usage": "/thpic", } -from core.bot import Bot -from core.command_manager import matcher -from models import MessageEvent, MessageSegment - @matcher.command("thpic") async def handle_echo(bot: Bot, event: MessageEvent, args: list[str]):