diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..8de371d5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,85 @@ +name: Bug 反馈 +title: "Bug: 出现异常" +description: 提交 Bug 反馈以帮助我们改进代码 +labels: ["bug"] +body: + - type: dropdown + id: env-os + attributes: + label: 操作系统 + description: 选择运行 zhenxun_bot 的系统 + options: + - Windows + - MacOS + - Linux + - Other + validations: + required: true + + - type: input + id: env-python-ver + attributes: + label: Python 版本 + description: 填写运行 zhenxun_bot 的 Python 版本 + placeholder: e.g. 3.11.0 + validations: + required: true + + - type: input + id: env-zhenxun-ver + attributes: + label: zhenxun_bot 版本 + description: 填写 zhenxun_bot 版本 + placeholder: e.g. 0.1.0 + validations: + required: true + + - type: input + id: env-adapter + attributes: + label: 适配器 + description: 填写使用的适配器以及版本 + placeholder: e.g. OneBot v11 2.2.2 + validations: + required: true + + - type: input + id: env-protocol + attributes: + label: 协议端 + description: 填写连接 zhenxun_bot 的协议端及版本 + placeholder: e.g. NapCat V4.0.3 + validations: + required: true + + - type: textarea + id: describe + attributes: + label: 描述问题 + description: 清晰简洁地说明问题是什么 + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: 复现步骤 + description: 提供能复现此问题的详细操作步骤 + placeholder: | + 1. 首先…… + 2. 然后…… + 3. 发生…… + validations: + required: true + + - type: textarea + id: expected + attributes: + label: 期望的结果 + description: 清晰简洁地描述你期望发生的事情 + + - type: textarea + id: logs + attributes: + label: 截图或日志(请勿包含敏感信息如密码、令牌等) + description: 提供有助于诊断问题的任何日志和截图 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..ec4bb386 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/document.yml b/.github/ISSUE_TEMPLATE/document.yml new file mode 100644 index 00000000..352a13b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/document.yml @@ -0,0 +1,18 @@ +name: 文档改进 +title: "Docs: 描述" +description: 文档错误及改进意见反馈 +labels: ["documentation"] +body: + - type: textarea + id: problem + attributes: + label: 描述问题或主题 + validations: + required: true + + - type: textarea + id: improve + attributes: + label: 需做出的修改 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..4f2e79f7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,20 @@ +name: 功能建议 +title: "Feature: 功能描述" +description: 提出关于项目新功能的想法 +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: 希望能解决的问题 + description: 在使用中遇到什么问题而需要新的功能? + validations: + required: true + + - type: textarea + id: feature + attributes: + label: 描述所需要的功能 + description: 请说明需要的功能或解决方法 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/issue-template.md b/.github/ISSUE_TEMPLATE/issue-template.md deleted file mode 100644 index 087dc60a..00000000 --- a/.github/ISSUE_TEMPLATE/issue-template.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Issue template -about: " issue template's purpose here." -title: '' -labels: '' -assignees: '' - ---- - -### 系统版本:Ubuntu 20.04 -### 真寻版本:0.1.5.3 - -### 错误截图 - -[img] - -### 日志截图 - -[img] - -### 错误说明 - -发生了xx错误... diff --git a/.github/workflows/ISSUE_TEMPLATE.md b/.github/workflows/ISSUE_TEMPLATE.md deleted file mode 100644 index 00f57290..00000000 --- a/.github/workflows/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,9 +0,0 @@ -### 系统版本:Ubuntu 20.04 -### 真寻版本:0.1.5.3 - -### 错误截图 - -[img] - -### 错误说明 -发生了xx错误... diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..570303f6 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,87 @@ +# zhenxun_bot 贡献者公约 + +## 我们的承诺 + +身为社区成员、贡献者和负责人,我们承诺使社区参与者不受骚扰,无论其年龄、体型、可见或不可见的缺陷、族裔、性征、性别认同和表达、经验水平、教育程度、社会与经济地位、国籍、相貌、种族、种姓、肤色、宗教信仰、性倾向或性取向如何。 + +我们承诺以有助于建立开放、友善、多样化、包容、健康社区的方式行事和互动。 + +## 我们的准则 + +有助于为我们的社区创造积极环境的行为例子包括但不限于: + +* 表现出对他人的同情和善意 +* 尊重不同的主张、观点和感受 +* 提出和大方接受建设性意见 +* 承担责任并向受我们错误影响的人道歉 +* 注重社区共同诉求,而非个人得失 + +不当行为例子包括: + +* 使用情色化的语言或图像,及性引诱或挑逗 +* 嘲弄、侮辱或诋毁性评论,以及人身或政治攻击 +* 公开或私下的骚扰行为 +* 未经他人明确许可,公布他人的私人信息,如物理或电子邮件地址 +* 其他有理由认定为违反职业操守的不当行为 + +## 责任和权力 + +社区负责人有责任解释和落实我们所认可的行为准则,并妥善公正地对他们认为不当、威胁、冒犯或有害的任何行为采取纠正措施。 + +社区负责人有权力和责任删除、编辑或拒绝或拒绝与本行为准则不相符的评论(comment)、提交(commits)、代码、维基(wiki)编辑、议题(issues)或其他贡献,并在适当时机知采取措施的理由。 + +## 适用范围 + +本行为准则适用于所有社区场合,也适用于在公共场所代表社区时的个人。 + +代表社区的情形包括使用官方电子邮件地址、通过官方社交媒体帐户发帖或在线上或线下活动中担任指定代表。 + +## 监督 + +辱骂、骚扰或其他不可接受的行为可通过 775757368@qq.com 向负责监督的社区负责人报告。 +所有投诉都将得到及时和公平的审查和调查。 + +所有社区负责人都有义务尊重任何事件报告者的隐私和安全。 + +## 处理方针 + +社区负责人将遵循下列社区处理方针来明确他们所认定违反本行为准则的行为的处理方式: + +### 1. 纠正 + +**社区影响**:使用不恰当的语言或其他在社区中被认定为不符合职业道德或不受欢迎的行为。 + +**处理意见**:由社区负责人发出非公开的书面警告,明确说明违规行为的性质,并解释举止如何不妥。或将要求公开道歉。 + +### 2. 警告 + +**社区影响**:单个或一系列违规行为。 + +**处理意见**:警告并对连续性行为进行处理。在指定时间内,不得与相关人员互动,包括主动与行为准则执行者互动。这包括避免在社区场所和外部渠道中的互动。违反这些条款可能会导致临时或永久封禁。 + +### 3. 临时封禁 + +**社区影响**: 严重违反社区准则,包括持续的不当行为。 + +**处理意见**: 在指定时间内,暂时禁止与社区进行任何形式的互动或公开交流。在此期间,不得与相关人员进行公开或私下互动,包括主动与行为准则执行者互动。违反这些条款可能会导致永久封禁。 + +### 4. 永久封禁 + +**社区影响**:行为模式表现出违反社区准则,包括持续的不当行为、骚扰个人或攻击或贬低某个类别的个体。 + +**处理意见**:永久禁止在社区内进行任何形式的公开互动。 + +## 参见 + +本行为准则改编自 [Contributor Covenant][homepage] 2.1 版, 参见 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。 + +社区处理方针灵感来源于 [Mozilla's code of conduct enforcement ladder][Mozilla CoC]。 + +有关本行为准则的常见问题的答案,参见 [https://www.contributor-covenant.org/faq][FAQ]。 +其他语言翻译参见 [https://www.contributor-covenant.org/translations][translations]。 + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..0c3fb236 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,95 @@ +# zhenxun_bot 贡献指南 + +首先,感谢你愿意为 zhenxun_bot 贡献自己的一份力量! + +本指南旨在引导你更规范地向 zhenxun_bot 提交贡献,请务必认真阅读。 + +## 提交 Issue + +在提交 Issue 前,我们建议你先查看 [已有的 Issues](https://github.com/HibiKier/zhenxun_bot/issues),以防重复提交。 + +### 报告问题、故障与漏洞 + +如果你在使用过程中发现问题并确信是由 zhenxun_bot 引起的,欢迎提交 Issue。 + +请使用我们提供的 **Bug 反馈** 模板,并尽可能详细地描述: + +- 问题描述 +- 重现步骤 +- 你的环境信息(如操作系统、依赖版本等) + +### 建议功能 + +如果你有新的功能需求或改进建议,欢迎提出。 + +请使用 **功能建议** 模板,并详细描述你所需要的特性,可能的话可以提出你认为可行的解决方案。 + +### 文档相关 + +如果你觉得文档有误或缺乏更新,欢迎提出。 + +请使用 **文档改进** 模板,并详细描述问题或主题,希望我们做出的修改 + +## Pull Request + +### 分支管理 + +请从 `main` 分支创建新功能分支,例如: + +- 新功能:`feature/功能描述` +- 问题修复:`bugfix/问题描述` + +### 代码风格 + +zhenxun_bot 使用 `pre-commit` 进行代码格式化和检查,请在提交前确保代码通过检查。 + +```bash +# 在安装项目依赖后安装 pre-commit 钩子 +pre-commit install +``` + +> 未通过 `pre-commit` 检查的代码将无法合并。 + +### Commit 规范 + +请确保你的每一个 commit 都能清晰地描述其意图,一个 commit 尽量只有一个目的。 + +我们建议遵循 [gitmoji](https://gitmoji.dev/) 的 commit message 格式,在创建 commit 时请牢记这一点。 + +### 工作流程概述 + +`main` 分支为 zhenxun_bot 的主分支,在任何情况下都请不要直接修改 `main` 分支,而是创建一个目标分支为 `main` 的 Pull Request 来提交修改。Pull Request 标题请尽量清晰,以便维护者进行审核。 + +如果你不是 zhenxun_bot 团队的成员,可在 fork 本仓库后,向本仓库的 `main` 分支发起 Pull Request,注意遵循先前提到的 commit message 规范创建 commit。我们将在 code review 通过后合并你的贡献。 + +### 撰写文档 + +如果你对文档有改进建议,欢迎提交 Pull Request 或者 Issue。 + +[//]: # (我们使用 Markdown 编写文档,建议遵循以下规范:) + +[//]: # () +[//]: # (1. 中文与英文、数字、半角符号之间需要有空格。例:`zhenxun_bot 是一个高效的聊天机器人。`) + +[//]: # (2. 若非英文整句,使用全角标点符号。例:`现在你可以看到机器人回复你:“Hello,世界!”。`) + +[//]: # (3. 直引号`「」`和弯引号`“”`都可接受,但同一份文件里应使用同种引号。) + +[//]: # (4. **不要使用斜体**,你不需要一种与粗体不同的强调。) + +[//]: # (5. 文档中应以“我们”指代开发者,以“用户”指代机器人的使用者。) + +[//]: # () +[//]: # (如果你需要编辑器检查 Markdown 规范,可以在 VSCode 中安装 `markdownlint` 扩展。) + +### 参与开发 + +zhenxun_bot 的代码风格遵循 [PEP 8](https://www.python.org/dev/peps/pep-0008/) 与 [PEP 484](https://www.python.org/dev/peps/pep-0484/) 规范,请确保你的代码风格和项目已有的代码保持一致,变量命名清晰,有适当的注释与测试代码。 + +> 暂未搭建测试框架,因此暂不要求添加测试代码。 + +## 项目沟通 + +如有关于贡献流程的疑问或需要进一步指导,请通过 [QQ群](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) 联系我们。 + +再次感谢你的贡献! \ No newline at end of file diff --git a/__version__ b/__version__ index de4af84a..e24aaa87 100644 --- a/__version__ +++ b/__version__ @@ -1 +1 @@ -__version__: v0.2.4-fix2 +__version__: v0.2.4-dd39a2a diff --git a/data/genshin_alc/config.json b/data/genshin_alc/config.json deleted file mode 100644 index 2ddfea98..00000000 --- a/data/genshin_alc/config.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "抽卡":{ - "buff": ["欧气满满,十连出金","出金不歪"], - "debuff": ["武器大师","保底出金","金色会是痛苦大剑"] - }, - "刷世界boss":{ - "buff": ["双攻双爆角斗士"], - "debuff": ["只有保底材料","贪生怕死角斗士"] - }, - "刷风本":{ - "buff": ["会有极品猎人套","会掉真正的少女心","治疗加成少女头"], - "debuff": ["勇往直前少女心","少女飘摇的杀意","少女暴怒的容颜"] - }, - "刷火本":{ - "buff": ["魔女帽子火伤杯","暴伤魔女帽!","火伤魔女心!"], - "debuff": ["幡 然 醒 悟","这么阴间的地方真的会有魔女套吗?","不务正业火魔女","会匹配到3个卢姥爷"] - }, - "刷岩本":{ - "buff": ["悠久的磐岩伴你左右","岩神的庇护常在"], - "debuff": ["防御流星杯,你值得拥有"] - }, - "刷宗室":{ - "buff": ["物理伤害骑士道,元素精通宗室套"], - "debuff": ["贪生怕死骑士道,物理伤害宗室杯"] - }, - "刷冰本":{ - "buff": ["双暴词条概率up"], - "debuff": ["防御力船帽,无人可及"] - }, - "刷雷本":{ - "buff": ["愿雷鸟伴你左右"], - "debuff": ["来表演一个只掉平雷套的绝活","风神忽悠雷凶兆"] - }, - "锄大地":{ - "buff": ["会掉一大堆紫色材料"], - "debuff": ["深渊法师爱你哟","会被冰水法控到死"] - }, - "挖矿":{ - "buff": ["开矿出双材料"], - "debuff": ["去别人世界会被拒"] - }, - "刷天赋本":{ - "buff": ["金色!我看到了金色的书!"], - "debuff": ["2蓝2绿不会变"] - }, - "刷突破材料":{ - "buff": ["金色!我看到了金色的材料!"], - "debuff": ["2蓝2绿不会变"] - }, - "升级圣遗物":{ - "buff": ["稀有词条跳跳跳","会双爆拉满"], - "debuff": ["女 仆 狂 喜","无中生有防御力","生命拉满","完美避开双爆"] - }, - "打风魔龙":{ - "buff": ["看我一箭一个风魔鸡","今天特瓦林可以给想要的突破材料","5金加原胚!"], - "debuff": ["会不小心掉下平台","不小心被地板烫死了"] - }, - "打狼王":{ - "buff": ["今天安德琉斯的心情不错,可以py一下","5金加原胚!"], - "debuff": ["狼尾巴*1"] - }, - "打公子":{ - "buff": ["今天可以和公子py想要的突破材料","5金加原胚!"], - "debuff": ["要角没有!要命一条!"] - } -} \ No newline at end of file diff --git a/zhenxun/builtin_plugins/catchphrase.py b/zhenxun/builtin_plugins/catchphrase.py new file mode 100644 index 00000000..3cea8fa8 --- /dev/null +++ b/zhenxun/builtin_plugins/catchphrase.py @@ -0,0 +1,28 @@ +from typing import Any + +from nonebot.adapters import Bot + +from zhenxun.services.log import logger +from zhenxun.configs.config import Config + +Config.add_plugin_config( + "catchphrase", + "CATCHPHRASE", + "", + help="小真寻的口癖,在文本末尾添加指定文字~", + default_value="", +) + + +@Bot.on_calling_api +async def handle_api_call(bot: Bot, api: str, data: dict[str, Any]): + if api == "send_msg": + catchphrase = Config.get_config("catchphrase", "CATCHPHRASE") + if catchphrase and (message := data.get("message")): + for i in range(len(message) - 1, -1, -1): + if message[i].type == "text": + message[i].data["text"] += catchphrase + logger.debug( + f"文本: {message[i].data['text']} 添加口癖: {catchphrase}" + ) + break diff --git a/zhenxun/builtin_plugins/init/init_plugin.py b/zhenxun/builtin_plugins/init/init_plugin.py index fff73129..d99210d3 100644 --- a/zhenxun/builtin_plugins/init/init_plugin.py +++ b/zhenxun/builtin_plugins/init/init_plugin.py @@ -202,9 +202,9 @@ async def _(): manager.init() if limit_list: for limit in limit_list: - if not manager.exist(limit.module_path, limit.limit_type): + if not manager.exists(limit.module, limit.limit_type): """不存在,添加""" - manager.add(limit.module_path, limit) + manager.add(limit.module, limit) manager.save_file() await manager.load_to_db() diff --git a/zhenxun/builtin_plugins/init/manager.py b/zhenxun/builtin_plugins/init/manager.py index 0d77290d..039b2980 100644 --- a/zhenxun/builtin_plugins/init/manager.py +++ b/zhenxun/builtin_plugins/init/manager.py @@ -112,7 +112,7 @@ class Manager: elif isinstance(data, BaseBlock): self.block_data[module] = data - def exist(self, module: str, type: PluginLimitType): + def exists(self, module: str, type: PluginLimitType): """是否存在""" if type == PluginLimitType.CD: return module in self.cd_data diff --git a/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py b/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py index 43a887e5..a1729c9c 100644 --- a/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py +++ b/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py @@ -71,8 +71,18 @@ __plugin_meta__ = PluginMetadata( ), ], tasks=[ - Task(module="group_welcome", name="进群欢迎"), - Task(module="refund_group_remind", name="退群提醒"), + Task( + module="group_welcome", + name="进群欢迎", + create_status=False, + default_status=False, + ), + Task( + module="refund_group_remind", + name="退群提醒", + create_status=False, + default_status=False, + ), ], ).dict(), ) diff --git a/zhenxun/builtin_plugins/platform/qq_api/ug_watch.py b/zhenxun/builtin_plugins/platform/qq_api/ug_watch.py index 7890ceff..438cb2bc 100644 --- a/zhenxun/builtin_plugins/platform/qq_api/ug_watch.py +++ b/zhenxun/builtin_plugins/platform/qq_api/ug_watch.py @@ -24,5 +24,8 @@ async def do_something(session: Uninfo): ) logger.info("添加当前用户群组ID信息", "", session=session) elif not await FriendUser.exists(user_id=session.user.id, platform=platform): - await FriendUser.create(user_id=session.user.id, platform=platform) - logger.info("添加当前好友用户信息", "", session=session) + try: + await FriendUser.create(user_id=session.user.id, platform=platform) + logger.info("添加当前好友用户信息", "", session=session) + except Exception as e: + logger.error("添加当前好友用户信息失败", session=session, e=e) diff --git a/zhenxun/builtin_plugins/restart/__init__.py b/zhenxun/builtin_plugins/restart/__init__.py index 3c16091f..3adce4cc 100644 --- a/zhenxun/builtin_plugins/restart/__init__.py +++ b/zhenxun/builtin_plugins/restart/__init__.py @@ -3,25 +3,26 @@ import platform from pathlib import Path import nonebot +import aiofiles from nonebot import on_command +from nonebot.rule import to_me from nonebot.adapters import Bot from nonebot.params import ArgStr from nonebot.permission import SUPERUSER +from nonebot_plugin_uninfo import Uninfo from nonebot.plugin import PluginMetadata -from nonebot.rule import to_me -from nonebot_plugin_session import EventSession -from zhenxun.configs.config import BotConfig -from zhenxun.configs.utils import PluginExtraData from zhenxun.services.log import logger from zhenxun.utils.enum import PluginType +from zhenxun.configs.config import BotConfig from zhenxun.utils.message import MessageUtils from zhenxun.utils.platform import PlatformUtils +from zhenxun.configs.utils import PluginExtraData __plugin_meta__ = PluginMetadata( name="重启", description="执行脚本重启真寻", - usage=f""" + usage=""" 重启 """.strip(), extra=PluginExtraData( @@ -48,15 +49,15 @@ RESTART_FILE = Path() / "restart.sh" @_matcher.got( "flag", - prompt=f"确定是否重启{BotConfig.self_nickname}?确定请回复[是|好|确定](重启失败咱们将失去联系,请谨慎!)", + prompt=f"确定是否重启{BotConfig.self_nickname}?\n确定请回复[是|好|确定]\n(重启失败咱们将失去联系,请谨慎!)", ) -async def _(bot: Bot, session: EventSession, flag: str = ArgStr("flag")): - if flag.lower() in ["true", "是", "好", "确定", "确定是"]: +async def _(bot: Bot, session: Uninfo, flag: str = ArgStr("flag")): + if flag.lower() in {"true", "是", "好", "确定", "确定是"}: await MessageUtils.build_message( f"开始重启{BotConfig.self_nickname}..请稍等..." ).send() - with open(RESTART_MARK, "w", encoding="utf8") as f: - f.write(f"{bot.self_id} {session.id1}") + async with aiofiles.open(RESTART_MARK, "w", encoding="utf8") as f: + await f.write(f"{bot.self_id} {session.user.id}") logger.info("开始重启真寻...", "重启", session=session) if str(platform.system()).lower() == "windows": import sys @@ -64,34 +65,31 @@ async def _(bot: Bot, session: EventSession, flag: str = ArgStr("flag")): python = sys.executable os.execl(python, python, *sys.argv) else: - os.system("./restart.sh") + os.system("./restart.sh") # noqa: ASYNC221 else: await MessageUtils.build_message("已取消操作...").send() @driver.on_bot_connect async def _(bot: Bot): - if str(platform.system()).lower() != "windows": - if not RESTART_FILE.exists(): - with open(RESTART_FILE, "w", encoding="utf8") as f: - f.write( - f"pid=$(netstat -tunlp | grep " - + str(bot.config.port) - + " | awk '{print $7}')\n" - "pid=${pid%/*}\n" - "kill -9 $pid\n" - "sleep 3\n" - "python3 bot.py" - ) - os.system("chmod +x ./restart.sh") - logger.info( - "已自动生成 restart.sh(重启) 文件,请检查脚本是否与本地指令符合..." + if str(platform.system()).lower() != "windows" and not RESTART_FILE.exists(): + async with aiofiles.open(RESTART_FILE, "w", encoding="utf8") as f: + await f.write( + "pid=$(netstat -tunlp | grep " + + str(bot.config.port) + + " | awk '{print $7}')\n" + "pid=${pid%/*}\n" + "kill -9 $pid\n" + "sleep 3\n" + "python3 bot.py" ) + os.system("chmod +x ./restart.sh") # noqa: ASYNC221 + logger.info("已自动生成 restart.sh(重启) 文件,请检查脚本是否与本地指令符合...") if RESTART_MARK.exists(): - with open(RESTART_MARK, "r", encoding="utf8") as f: - bot_id, session_id = f.read().split() + async with aiofiles.open(RESTART_MARK, encoding="utf8") as f: + bot_id, user_id = (await f.read()).split() if bot := nonebot.get_bot(bot_id): - if target := PlatformUtils.get_target(bot, session_id): + if target := PlatformUtils.get_target(bot, user_id): await MessageUtils.build_message( f"{BotConfig.self_nickname}已成功重启!" ).send(target, bot=bot) diff --git a/zhenxun/builtin_plugins/scheduler/morning.py b/zhenxun/builtin_plugins/scheduler/morning.py index a37c7917..d3519219 100644 --- a/zhenxun/builtin_plugins/scheduler/morning.py +++ b/zhenxun/builtin_plugins/scheduler/morning.py @@ -20,7 +20,14 @@ __plugin_meta__ = PluginMetadata( author="HibiKier", version="0.1", plugin_type=PluginType.HIDDEN, - tasks=[Task(module="morning_goodnight", name="早晚安")], + tasks=[ + Task( + module="morning_goodnight", + name="早晚安", + create_status=False, + default_status=False, + ) + ], ).dict(), ) diff --git a/zhenxun/builtin_plugins/shop/_data_source.py b/zhenxun/builtin_plugins/shop/_data_source.py index afe2681d..289149f5 100644 --- a/zhenxun/builtin_plugins/shop/_data_source.py +++ b/zhenxun/builtin_plugins/shop/_data_source.py @@ -95,7 +95,10 @@ async def gold_rank( if not user_list: return "当前还没有人拥有金币哦..." user_id_list = [user[0] for user in user_list] - index = user_id_list.index(session.user.id) + 1 + if session.user.id in user_id_list: + index = user_id_list.index(session.user.id) + 1 + else: + index = "-1(未统计)" user_list = user_list[:num] if num < len(user_list) else user_list friend_user = await FriendUser.filter(user_id__in=user_id_list).values_list( "user_id", "user_name" diff --git a/zhenxun/builtin_plugins/sign_in/_data_source.py b/zhenxun/builtin_plugins/sign_in/_data_source.py index abf30c08..50b2e21b 100644 --- a/zhenxun/builtin_plugins/sign_in/_data_source.py +++ b/zhenxun/builtin_plugins/sign_in/_data_source.py @@ -59,7 +59,10 @@ class SignManage: if not user_list: return "当前还没有人签到过哦..." user_id_list = [user[0] for user in user_list] - index = user_id_list.index(session.user.id) + 1 + if session.user.id in user_id_list: + index = user_id_list.index(session.user.id) + 1 + else: + index = "-1(未统计)" user_list = user_list[:num] if num < len(user_list) else user_list column_name = ["排名", "-", "名称", "好感度", "签到次数", "平台"] friend_list = await FriendUser.filter(user_id__in=user_id_list).values_list( diff --git a/zhenxun/builtin_plugins/superuser/exec_sql.py b/zhenxun/builtin_plugins/superuser/exec_sql.py index 83a11689..379e3445 100644 --- a/zhenxun/builtin_plugins/superuser/exec_sql.py +++ b/zhenxun/builtin_plugins/superuser/exec_sql.py @@ -1,17 +1,18 @@ +from tortoise import Tortoise from nonebot import on_command +from nonebot.rule import to_me from nonebot.permission import SUPERUSER from nonebot.plugin import PluginMetadata -from nonebot.rule import to_me from nonebot_plugin_alconna import UniMsg from nonebot_plugin_session import EventSession -from tortoise import Tortoise -from zhenxun.configs.utils import PluginExtraData -from zhenxun.models.ban_console import BanConsole from zhenxun.services.log import logger from zhenxun.utils.enum import PluginType -from zhenxun.utils.image_utils import ImageTemplate +from zhenxun.configs.config import BotConfig from zhenxun.utils.message import MessageUtils +from zhenxun.configs.utils import PluginExtraData +from zhenxun.models.ban_console import BanConsole +from zhenxun.utils.image_utils import ImageTemplate __plugin_meta__ = PluginMetadata( name="数据库操作", @@ -43,13 +44,30 @@ _table_matcher = on_command( block=True, ) -SELECT_TABLE_SQL = """ +SELECT_TABLE_MYSQL_SQL = """ +SELECT table_name AS name, table_comment AS `desc` +FROM information_schema.tables +WHERE table_schema = DATABASE(); +""" + +SELECT_TABLE_SQLITE_SQL = """ +SELECT name FROM sqlite_master WHERE type='table'; +""" + +SELECT_TABLE_PSQL_SQL = """ select a.tablename as name,d.description as desc from pg_tables a left join pg_class c on relname=tablename left join pg_description d on oid=objoid and objsubid=0 where a.schemaname = 'public' """ +type2sql = { + "mysql": SELECT_TABLE_MYSQL_SQL, + "sqlite": SELECT_TABLE_SQLITE_SQL, + "postgres": SELECT_TABLE_PSQL_SQL, +} + + @_matcher.handle() async def _(session: EventSession, message: UniMsg): sql_text = message.extract_plain_text().strip() @@ -70,9 +88,7 @@ async def _(session: EventSession, message: UniMsg): _column = r.keys() data_list = [] for r in res: - data = [] - for c in _column: - data.append(r.get(c)) + data = [r.get(c) for c in _column] data_list.append(data) if not data_list: return await MessageUtils.build_message("查询结果为空!").send() @@ -90,11 +106,13 @@ async def _(session: EventSession, message: UniMsg): async def _(session: EventSession): try: db = Tortoise.get_connection("default") - query = await db.execute_query_dict(SELECT_TABLE_SQL) + sql_type = BotConfig.get_sql_type() + select_sql = type2sql[sql_type] + query = await db.execute_query_dict(select_sql) column_name = ["表名", "简介"] data_list = [] for table in query: - data_list.append([table["name"], table["desc"]]) + data_list.append([table["name"], table.get("desc")]) logger.info("查看数据库所有表", "查看所有表", session=session) table = await ImageTemplate.table_page( "数据库表", f"总共有 {len(data_list)} 张表捏", column_name, data_list diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/database/__init__.py b/zhenxun/builtin_plugins/web_ui/api/tabs/database/__init__.py index 53b8df04..8ec609e5 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/database/__init__.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/database/__init__.py @@ -5,12 +5,13 @@ from fastapi import Request, APIRouter from fastapi.responses import JSONResponse from tortoise.exceptions import OperationalError +from zhenxun.configs.config import BotConfig from zhenxun.models.task_info import TaskInfo from zhenxun.models.plugin_info import PluginInfo from .models.sql_log import SqlLog from ....utils import authentication -from .models.model import SqlText, SqlModel +from .models.model import Column, SqlText, SqlModel from ....base_model import Result, QueryModel, BaseResultModel router = APIRouter(prefix="/database") @@ -22,19 +23,48 @@ driver: Driver = nonebot.get_driver() SQL_DICT = {} -SELECT_TABLE_SQL = """ -select a.tablename as name,d.description as desc from pg_tables a - left join pg_class c on relname=tablename - left join pg_description d on oid=objoid - and objsubid=0 where a.schemaname = 'public' +SELECT_TABLE_MYSQL_SQL = """ +SELECT table_name AS name, table_comment AS `desc` +FROM information_schema.tables +WHERE table_schema = DATABASE(); """ -SELECT_TABLE_COLUMN_SQL = """ +SELECT_TABLE_SQLITE_SQL = """ +SELECT name FROM sqlite_master WHERE type='table'; +""" + +SELECT_TABLE_PSQL_SQL = """ +select a.tablename as name,d.description as desc from pg_tables a + left join pg_class c on relname=tablename + left join pg_description d on oid=objoid and objsubid=0 where a.schemaname='public' +""" + +SELECT_TABLE_COLUMN_PSQL_SQL = """ SELECT column_name, data_type, character_maximum_length as max_length, is_nullable FROM information_schema.columns WHERE table_name = '{}'; """ +SELECT_TABLE_COLUMN_MYSQL_SQL = """ +SHOW COLUMNS FROM {}; +""" + +SELECT_TABLE_COLUMN_SQLITE_SQL = """ +PRAGMA table_info({}); +""" + +type2sql = { + "mysql": SELECT_TABLE_MYSQL_SQL, + "sqlite": SELECT_TABLE_SQLITE_SQL, + "postgres": SELECT_TABLE_PSQL_SQL, +} + +type2sql_column = { + "mysql": SELECT_TABLE_COLUMN_MYSQL_SQL, + "sqlite": SELECT_TABLE_COLUMN_SQLITE_SQL, + "postgres": SELECT_TABLE_COLUMN_PSQL_SQL, +} + @driver.on_startup async def _(): @@ -71,22 +101,47 @@ async def _(): ) async def _() -> Result[list[dict]]: db = Tortoise.get_connection("default") - query = await db.execute_query_dict(SELECT_TABLE_SQL) + sql_type = BotConfig.get_sql_type() + query = await db.execute_query_dict(type2sql[sql_type]) return Result.ok(query) @router.get( "/get_table_column", dependencies=[authentication()], - response_model=Result[list[dict]], + response_model=Result[list[Column]], response_class=JSONResponse, description="获取表字段", ) -async def _(table_name: str) -> Result[list[dict]]: +async def _(table_name: str) -> Result[list[Column]]: db = Tortoise.get_connection("default") - # print(SELECT_TABLE_COLUMN_SQL.format(table_name)) - query = await db.execute_query_dict(SELECT_TABLE_COLUMN_SQL.format(table_name)) - return Result.ok(query) + sql_type = BotConfig.get_sql_type() + sql = type2sql_column[sql_type] + query = await db.execute_query_dict(sql.format(table_name)) + result_list = [] + if sql_type == "sqlite": + result_list.extend( + Column( + column_name=result["name"], + data_type=result["type"], + max_length=-1, + is_nullable="YES" if result["notnull"] == 1 else "NO", + ) + for result in query + ) + elif sql_type == "mysql": + result_list.extend( + Column( + column_name=result["Field"], + data_type=result["Type"], + max_length=-1, + is_nullable=result["Null"], + ) + for result in query + ) + else: + result_list.extend(Column(**result) for result in query) + return Result.ok(result_list) @router.post( diff --git a/zhenxun/builtin_plugins/web_ui/api/tabs/database/models/model.py b/zhenxun/builtin_plugins/web_ui/api/tabs/database/models/model.py index e18e4cfb..9a85f14f 100644 --- a/zhenxun/builtin_plugins/web_ui/api/tabs/database/models/model.py +++ b/zhenxun/builtin_plugins/web_ui/api/tabs/database/models/model.py @@ -22,3 +22,18 @@ class SqlModel(BaseModel): """插件名称""" sql_list: list[CommonSql] """插件列表""" + + +class Column(BaseModel): + """ + 列 + """ + + column_name: str + """列名""" + data_type: str + """数据类型""" + max_length: int | None + """最大长度""" + is_nullable: str + """是否可为空""" diff --git a/zhenxun/builtin_plugins/withdraw.py b/zhenxun/builtin_plugins/withdraw.py index 8186cfbc..81056e2c 100644 --- a/zhenxun/builtin_plugins/withdraw.py +++ b/zhenxun/builtin_plugins/withdraw.py @@ -46,10 +46,13 @@ _matcher = on_alconna(Alconna("撤回"), priority=5, block=True, rule=reply_chec @_matcher.handle() async def _(bot: Bot, event: Event, session: Uninfo, arparma: Arparma): if reply := await reply_fetch(event, bot): - if ( - MessageManager.check(session.user.id, reply.id) - or session.user.id in bot.config.superusers - ): + if session.user.id in bot.config.superusers: + try: + await bot.delete_msg(message_id=reply.id) + logger.info("撤回消息", arparma.header_result, session=session) + except Exception: + await MessageUtils.build_message("撤回失败,可能消息已过期...").send() + elif MessageManager.check(session.user.id, reply.id): try: await bot.delete_msg(message_id=reply.id) logger.info("撤回消息", arparma.header_result, session=session) diff --git a/zhenxun/configs/config.py b/zhenxun/configs/config.py index ccb6bf36..d0bc6982 100644 --- a/zhenxun/configs/config.py +++ b/zhenxun/configs/config.py @@ -46,7 +46,7 @@ class BotSetting(BaseModel): """获取数据库类型 返回: - str: 数据库类型, postgres, aiomysql, sqlite + str: 数据库类型, postgres, mysql, sqlite """ return self.db_url.split(":", 1)[0] if self.db_url else "" diff --git a/zhenxun/utils/_build_image.py b/zhenxun/utils/_build_image.py index 7f353d4b..3fdf4600 100644 --- a/zhenxun/utils/_build_image.py +++ b/zhenxun/utils/_build_image.py @@ -156,7 +156,8 @@ class BuildImage: """ if not img_list: raise ValueError("贴图类别为空...") - width, height = img_list[0].size + width = max(img.size[0] for img in img_list) + height = max(img.size[1] for img in img_list) background_width = width * row + space * (row - 1) + padding * 2 row_count = math.ceil(len(img_list) / row) if row_count == 1: @@ -168,12 +169,22 @@ class BuildImage: background_width, background_height, color=color, background=background ) _cur_width, _cur_height = padding, padding - for img in img_list: + row_num = 0 + for i in range(len(img_list)): + row_num += 1 + img: Self | tImage = img_list[i] await background_image.paste(img, (_cur_width, _cur_height)) _cur_width += space + img.width - if _cur_width + padding >= background_image.width: + next_image_width = 0 + if i != len(img_list) - 1: + next_image_width = img_list[i + 1].width + if ( + row_num == row + or _cur_width + padding + next_image_width >= background_image.width + 1 + ): _cur_height += space + img.height _cur_width = padding + row_num = 0 return background_image @classmethod @@ -719,3 +730,11 @@ class BuildImage: bytes: bytes """ return self.markImg.tobytes() + + def copy(self) -> "BuildImage": + """复制 + + 返回: + BuildImage: Self + """ + return BuildImage.open(self.pic2bytes()) diff --git a/zhenxun/utils/message.py b/zhenxun/utils/message.py index 0834c14d..d3c9802c 100644 --- a/zhenxun/utils/message.py +++ b/zhenxun/utils/message.py @@ -1,3 +1,4 @@ +import base64 from io import BytesIO from pathlib import Path @@ -59,7 +60,14 @@ class MessageUtils: for msg in msg_list: if isinstance(msg, Image | Text | At | AtAll | Video | Voice): message_list.append(msg) - elif isinstance(msg, str | int | float): + elif isinstance(msg, str): + if msg.startswith("base64://"): + message_list.append(Image(raw=BytesIO(base64.b64decode(msg[9:])))) + elif msg.startswith("http://"): + message_list.append(Image(url=msg)) + else: + message_list.append(Text(msg)) + elif isinstance(msg, int | float): message_list.append(Text(str(msg))) elif isinstance(msg, Path): if msg.exists(): @@ -124,6 +132,8 @@ class MessageUtils: _message[i] = Image( raw=BuildImage.open(_message[i]).pic2bytes() ) + elif isinstance(_message[i], BuildImage): + _message[i] = Image(raw=_message[i].pic2bytes()) node_list.append( CustomNode(uid=uin, name=name, content=UniMessage(_message)) )