Compare commits

..
15 Commits
Author SHA1 Message Date
HibiKier d594d17bf6 版本更新 2024-10-20 21:11:16 +08:00
4e33bf3a50 版本更新 (#1666)
* ✨ 父级插件加载

* ✅ 添加测试:更新与添加插件 (#1594)

* ✅ 测试更新与添加插件

* ✅ Sourcery建议

* 👷 添加pytest

* 🎨 优化代码

* 🐛 bug修复

* 🐛修复添加插件返回403的问题 (#1595)

* 完善测试方法
* vscode测试配置
* 重构插件安装过程

* 🎨 修改readme

* Update README.md

* 🐛 修改bug与版本锁定

* 🐛 修复超级用户对群组功能开关

* 🐛 修复插件商店检查插件更新问题 (#1597)

* 🐛 修复插件商店检查插件更新问题

* 🐛 恶意命令检测问题

* 🐛 增加插件状态检查 (#1598)

* ✅ 优化测试用例

* 🐛 更改插件更新与安装逻辑

* 🐛 修复更新群组成员信息

* 🎨 代码优化

* 🚀 更新Dockerfile (#1599)

* 🎨 更新requirements

* ➕ 添加依赖aiocache

* ⚡ 添加github镜像

* ✨ 添加仓库目录多获取渠道

* 🐛 修复测试用例

* ✨ 添加API缓存

* 🎨 采取Sourcery建议

* 🐛 文件下载逻辑修改

* 🎨 优化代码

* 🐛 修复插件开关有时出现错误

* ✨ 重构自检ui

* 🐛 自检html修正

* 修复签到逻辑bug,并使代码更灵活以适应签到好感度等级配置 (#1606)

* 修复签到功能已知问题

* 修复签到功能已知问题

* 修改参数名称

* 修改uid判断

---------

Co-authored-by: HibiKier <45528451+HibiKier@users.noreply.github.com>

* 🎨 代码结构优化

* 🐛 私聊时修改插件时删除私聊帮助

* 🐛 过滤父插件

* 🐛 修复自检在ARM上的问题 (#1607)

* 🐛 修复自检在ARM上的问题

* ✅ 优化测试

* ✨ 支持mysql,psql,sqlite随机函数

* 🔧 VSCode配置修改

* 🔧 VSCode配置修改

* ✨ 添加金币排行

Co-Authored-By: HibiKier <45528451+HibiKier@users.noreply.github.com>

* 📝 修改README

Co-Authored-By: HibiKier <45528451+HibiKier@users.noreply.github.com>

* 🔨 提取GitHub相关操作 (#1609)

* 🔨 提取GitHub相关操作

* 🔨 重构API策略

* ✨ 签到/金币排行限制最大数量 (#1616)

* ✨ 签到/金币排行限制最大数量

* 🐛 修复超级用户id获取问题

* 🐛 修复路径解压与挂载 (#1619)

* 🐛 修复功能少时zhenxun帮助图片排序问题 (#1620)

* 🐛 签到文本适应 (#1622)

* 🐛 好感度排行提供默认值 (#1624)

* 🎈 优先使用github api (#1625)

* ✨ 重构帮助,限制普通用户查询管理插件 (#1626)

* 🐛 修复群权限与插件等级匹配 (#1627)

* ✨ 当管理员尝试ban真寻时将被反杀 (#1628)

* ✨ 群组发言时间检测提供开关配置 (#1630)

* 🐳 chore: 支持自动修改版本号 (#1629)

* 🎈 perf(github_utils): 支持github url下载遍历 (#1632)

* 🎈 perf(github_utils): 支持github url下载遍历

* 🐞 fix(http_utils): 修复一些下载问题

* 🦄 refactor(http_utils): 部分重构

* chore(version): Update version to v0.2.2-e6f17c4

---------

Co-authored-by: AkashiCoin <AkashiCoin@users.noreply.github.com>

* 🧪 test(auto_update): 修复测试用例 (#1633)

* 🐛 修复商店商品为空时报错 (#1634)

* 🐛 修复群权限与插件等级匹配 (#1635)

* ✨ message_build支持AtAll (#1639)

* 🎈 perf: 使用commit号下载插件 (#1641)

* 🎈 perf: 使用commit号下载插件

* chore(version): Update version to v0.2.2-f9c7360

---------

Co-authored-by: AkashiCoin <AkashiCoin@users.noreply.github.com>

* 🐳 chore: 修改运行检查触发路径 (#1642)

* 🐳 chore: 修改运行检查触发路径

* 🐳 chore: 添加tests目录

* ✨ 重构qq群事件处理 (#1643)

* 🐛 签到名称自适应 (#1644)

* 🎨  更新README (#1645)

* 🐛 fix(http_utils): 流式下载Content-Length错误 (#1647)

* 🐛 修复群组中帮助功能状态显示问题 (#1650)

* 🐛 修复群欢迎消息设置 (#1651)

* 🐛 修复webui下载后首次启动错误 (#1652)

* 🐛 修复webui下载后首次启动错误

* chore(version): Update version to v0.2.2-4a8ef85

---------

Co-authored-by: HibiKier <HibiKier@users.noreply.github.com>

* ✨ 移除默认图片文件夹:爬 (#1653)

* ✨ 安装/移除插件提供插件安装/卸载方法用于插件初始化 (#1654)

* ✨ 新增超级用户与管理员帮助模板 (#1655)

* ✨ 新增个人信息命令 (#1657)

* ✨ 修改个人信息菜单名称 (#1658)

* ✨ 新增插件商店api (#1659)

* ✨ 新增插件商店api

* chore(version): Update version to v0.2.2-7e15f20

---------

Co-authored-by: HibiKier <HibiKier@users.noreply.github.com>

* ✨ 将cd,block,count限制复原配置文件 (#1662)

* 🎨 修改README (#1663)

* 🎨 修改版本号 (#1664)

* 🎨 修改requirements (#1665)

---------

Co-authored-by: AkashiCoin <l1040186796@gmail.com>
Co-authored-by: fanyinrumeng <42991257+fanyinrumeng@users.noreply.github.com>
Co-authored-by: AkashiCoin <i@loli.vet>
Co-authored-by: Elaga <1728903318@qq.com>
Co-authored-by: AkashiCoin <AkashiCoin@users.noreply.github.com>
Co-authored-by: HibiKier <HibiKier@users.noreply.github.com>
2024-10-01 00:42:23 +08:00
HibiKier daa40ae8b8 Merge pull request #1592 from HibiKier/dev
🐛 版本锁定
2024-08-30 23:15:09 +08:00
HibiKier 21bdb011e0 Merge pull request #1591 from HibiKier/dev
🎨 修改方法名称
2024-08-30 02:48:53 +08:00
HibiKier 47f3518b04 Merge pull request #1590 from HibiKier/dev
🎨 更新版本号
2024-08-29 23:22:50 +08:00
HibiKier 997ce52d69 Merge pull request #1589 from HibiKier/dev
🎨 添加__init__.py
2024-08-29 22:50:01 +08:00
HibiKier 4bc433f049 Merge pull request #1588 from HibiKier/dev
Dev
2024-08-29 22:41:58 +08:00
HibiKier 4d6013ca02 Merge pull request #1561 from HibiKier/dev
🐛 修复使用道具后未减去道具数量
2024-08-19 18:57:31 +08:00
HibiKier f09f2c3162 Merge pull request #1559 from HibiKier/dev
⚡️ 优化权限判断
2024-08-18 22:38:53 +08:00
HibiKier 67fdbb824b Merge pull request #1557 from HibiKier/dev
🎨 代码优化
2024-08-16 20:16:30 +08:00
HibiKier b4ccbb0f80 Merge pull request #1556 from HibiKier/dev
Dev
2024-08-15 23:54:50 +08:00
HibiKier 6326d888c8 Merge pull request #1555 from HibiKier/dev
Dev
2024-08-15 00:18:09 +08:00
HibiKier e32f3dd22a Merge pull request #1551 from HibiKier/dev
🐛 修改b站解析
2024-08-14 01:05:42 +08:00
HibiKier c7e073baea Merge pull request #1550 from HibiKier/dev
Dev
2024-08-13 22:40:03 +08:00
HibiKier 1be99ac64d Merge pull request #1549 from HibiKier/dev
Dev
2024-08-12 22:36:01 +08:00
41 changed files with 466 additions and 313 deletions
+1 -8
View File
@@ -8,19 +8,12 @@ NICKNAME=["真寻", "小真寻", "绪山真寻", "小寻子"]
SESSION_EXPIRE_TIMEOUT=30
ALCONNA_USE_COMMAND_START=True
# 全局图片统一使用bytes发送,当真寻与协议端不在同一服务器上时为True
IMAGE_TO_BYTES = True
IMAGE_TO_BYTES = False
# 回复消息时自称
SELF_NICKNAME="小真寻"
# 官bot appid:bot账号
QBOT_ID_DATA = '{
}'
# 数据库配置
# 示例: "postgres://user:password@127.0.0.1:5432/database"
# 示例: "mysql://user:password@127.0.0.1:3306/database"
+65
View File
@@ -0,0 +1,65 @@
name: Update Version
on:
pull_request_target:
paths:
- zhenxun/**
- resources/**
- bot.py
types:
- opened
- synchronize
branches:
- main
- dev
jobs:
update-version:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Read current version
id: read_version
run: |
version_line=$(grep '__version__' __version__)
version=$(echo $version_line | sed -E 's/__version__:\s*v([0-9]+\.[0-9]+\.[0-9]+)(-.+)?/\1/')
echo "Current version: $version"
echo "current_version=$version" >> $GITHUB_OUTPUT
- name: Check for version file changes
id: check_diff
run: |
if git diff --name-only HEAD~1 HEAD | grep -q '__version__'; then
echo "Version file has changes"
echo "version_changed=true" >> $GITHUB_OUTPUT
else
echo "Version file has no changes"
echo "version_changed=false" >> $GITHUB_OUTPUT
fi
- name: Get commit hash
id: get_commit_hash
run: echo "commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Update version file
if: steps.check_diff.outputs.version_changed == 'false'
run: |
current_version="${{ steps.read_version.outputs.current_version }}"
commit_hash="${{ steps.get_commit_hash.outputs.commit_hash }}"
new_version="v${current_version}-${commit_hash}"
echo "Updating version to: $new_version"
echo "__version__: $new_version" > __version__
git config --global user.name "${{ github.event.pull_request.user.login }}"
git config --global user.email "${{ github.event.pull_request.user.login }}@users.noreply.github.com"
git add __version__
git remote set-url origin https://github.com/${{ github.event.pull_request.head.repo.full_name }}.git
git commit -m "chore(version): Update version to $new_version"
git push origin HEAD:${{ github.event.pull_request.head.ref }}
- name: Check updated version
if: steps.check_diff.outputs.version_changed == 'false'
run: cat __version__
+2 -3
View File
@@ -13,19 +13,18 @@
"getbbox",
"hibiapi",
"httpx",
"jsdelivr",
"kaiheila",
"lolicon",
"nonebot",
"onebot",
"pixiv",
"qbot",
"Setu",
"tobytes",
"ujson",
"unban",
"userinfo",
"zhenxun"
"zhenxun",
"jsdelivr"
],
"python.analysis.autoImportCompletions": true,
"python.testing.pytestArgs": ["tests"],
+9 -11
View File
@@ -68,7 +68,7 @@
### 1. 体验一下?
这是一个免费的,版本为 dev 的 zhenxun,你可以通过 [napcat](https://github.com/NapNeko/NapCatQQ) 或 [拉格朗日](https://github.com/LagrangeDev/Lagrange.Core) 以及 [matcha](https://github.com/A-kirami/matcha) 等直接连接用于体验与测试
这是一个免费的,版本为 dev 的 zhenxun,你可以通过 napcat 或拉格朗日等直接连接用于体验与测试
(球球了测试君!)
```
@@ -95,14 +95,13 @@ AccessToken: PUBLIC_ZHENXUN_TEST
<details>
<summary> <strong> WebUI </strong>后台示例图 </summary>
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui00.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui01.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui02.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui03.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui04.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui05.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui06.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui07.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui1.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui2.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui3.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui4.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui5.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui6.png)
![x](https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/webui7.png)
</details>
@@ -320,7 +319,7 @@ python bot.py
<details>
<summary>爱发电 以及 感谢投喂 </summary>
<img width="365px" height="450px" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/afd.jpg">
<img width="365px" height="450px" src="https://user-images.githubusercontent.com/45528451/175059389-cfeb8174-fa07-4939-80ab-a039087a50f6.png">
### 感谢名单
@@ -370,7 +369,6 @@ python bot.py
[哇](https://afdian.net/u/9b266244f23911eca19052540025c377)
[yajiwa](https://github.com/yajiwa)
[爆金币](https://afdian.net/u/0d78879ef23711ecb22452540025c377)
...
</details>
+1 -1
View File
@@ -1 +1 @@
__version__: v0.2.4-fix2
__version__: v0.2.4
Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
View File
Binary file not shown.
-6
View File
@@ -19,8 +19,6 @@ require("nonebot_plugin_htmlrender")
from zhenxun.services.log import logger
from zhenxun.models.sign_user import SignUser
from zhenxun.models.goods_info import GoodsInfo
from zhenxun.utils.platform import PlatformUtils
from zhenxun.models.bot_console import BotConsole
from zhenxun.models.user_console import UserConsole
from zhenxun.utils.decorator.shop import shop_register
from zhenxun.models.bot_connect_log import BotConnectLog
@@ -35,10 +33,6 @@ async def _(bot: Bot):
await BotConnectLog.create(
bot_id=bot.self_id, platform=bot.adapter, connect_time=datetime.now(), type=1
)
if not await BotConsole.exists(bot_id=bot.self_id):
await BotConsole.create(
bot_id=bot.self_id, platform=PlatformUtils.get_platform(bot)
)
@driver.on_bot_disconnect
+1 -16
View File
@@ -11,7 +11,6 @@ from zhenxun.services.log import logger
from zhenxun.configs.config import Config
from zhenxun.utils.message import MessageUtils
from zhenxun.models.level_user import LevelUser
from zhenxun.models.bot_console import BotConsole
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.models.plugin_limit import PluginLimit
from zhenxun.models.user_console import UserConsole
@@ -235,10 +234,7 @@ class AuthChecker:
return
if plugin := await PluginInfo.get_or_none(module_path=module_path):
if plugin.plugin_type == PluginType.HIDDEN:
logger.debug(
f"插件: {plugin.name}:{plugin.module} "
"为HIDDEN,已跳过权限检查..."
)
logger.debug("插件为HIDDEN,已跳过...")
return
try:
cost_gold = await self.auth_cost(user, plugin, session)
@@ -248,7 +244,6 @@ class AuthChecker:
if not plugin.limit_superuser:
cost_gold = 0
raise IsSuperuserException()
await self.auth_bot(plugin, bot.self_id)
await self.auth_group(plugin, session, message)
await self.auth_admin(plugin, session)
await self.auth_plugin(plugin, session, event)
@@ -283,16 +278,6 @@ class AuthChecker:
if is_ignore:
raise IgnoredException("权限检测 ignore")
async def auth_bot(self, plugin: PluginInfo, bot_id: str):
"""机器人权限
参数:
plugin: PluginInfo
bot_id: bot_id
"""
if await BotConsole.is_block_plugin(plugin.module, bot_id):
raise IgnoredException("机器人权限检测 ignore")
async def auth_limit(self, plugin: PluginInfo, session: EventSession):
"""插件限制
+1 -1
View File
@@ -32,7 +32,7 @@ async def _(
if plugin := matcher.plugin:
if metadata := plugin.metadata:
extra = metadata.extra
if extra.get("plugin_type") in [PluginType.HIDDEN]:
if extra.get("plugin_type") in [PluginType.HIDDEN, PluginType.DEPENDANT]:
return
user_id = session.id1
group_id = session.id3 or session.id2
@@ -1,23 +0,0 @@
from typing import Any
from nonebot.adapters import Bot
from zhenxun.services.log import logger
from zhenxun.utils.manager.message_manager import MessageManager
@Bot.on_called_api
async def handle_api_result(
bot: Bot, exception: Exception | None, api: str, data: dict[str, Any], result: Any
):
if not exception and api == "send_msg":
try:
if (uid := data.get("user_id")) and (msg_id := result.get("message_id")):
MessageManager.add(str(uid), str(msg_id))
logger.debug(
f"收集消息id,user_id: {uid}, msg_id: {msg_id}", "msg_hook"
)
except Exception as e:
logger.warning(
f"收集消息id发生错误...data: {data}, result: {result}", "msg_hook", e=e
)
@@ -0,0 +1,321 @@
import os
import re
import random
from datetime import datetime
import ujson as json
from nonebot.adapters import Bot
from nonebot_plugin_alconna import At
from nonebot import on_notice, on_request
from nonebot.plugin import PluginMetadata
from nonebot.adapters.onebot.v11 import (
GroupDecreaseNoticeEvent,
GroupIncreaseNoticeEvent,
)
from nonebot.adapters.onebot.v12 import (
GroupMemberDecreaseEvent,
GroupMemberIncreaseEvent,
)
from zhenxun.services.log import logger
from zhenxun.utils.utils import FreqLimiter
from zhenxun.utils.message import MessageUtils
from zhenxun.models.fg_request import FgRequest
from zhenxun.models.level_user import LevelUser
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.utils.common_utils import CommonUtils
from zhenxun.configs.config import Config, BotConfig
from zhenxun.models.group_console import GroupConsole
from zhenxun.models.group_member_info import GroupInfoUser
from zhenxun.utils.enum import PluginType, RequestHandleType
from zhenxun.configs.path_config import DATA_PATH, IMAGE_PATH
from zhenxun.configs.utils import Task, RegisterConfig, PluginExtraData
__plugin_meta__ = PluginMetadata(
name="QQ群事件处理",
description="群事件处理",
usage="",
extra=PluginExtraData(
author="HibiKier",
version="0.1",
plugin_type=PluginType.HIDDEN,
configs=[
RegisterConfig(
module="invite_manager",
key="message",
value=f"请不要未经同意就拉{BotConfig.self_nickname}入群!告辞!",
help="强制拉群后进群回复的内容",
),
RegisterConfig(
module="invite_manager",
key="flag",
value=True,
help="强制拉群后进群退出并回复内容",
default_value=True,
type=bool,
),
RegisterConfig(
module="invite_manager",
key="welcome_msg_cd",
value=5,
help="群欢迎消息cd",
default_value=5,
type=int,
),
RegisterConfig(
module="_task",
key="DEFAULT_GROUP_WELCOME",
value=True,
help="被动 进群欢迎 进群默认开关状态",
default_value=True,
type=bool,
),
RegisterConfig(
module="_task",
key="DEFAULT_REFUND_GROUP_REMIND",
value=True,
help="被动 退群提醒 进群默认开关状态",
default_value=True,
type=bool,
),
],
tasks=[
Task(module="group_welcome", name="进群欢迎"),
Task(module="refund_group_remind", name="退群提醒"),
],
).dict(),
)
base_config = Config.get("invite_manager")
limit_cd = base_config.get("welcome_msg_cd")
_flmt = FreqLimiter(limit_cd)
group_increase_handle = on_notice(priority=1, block=False)
"""群员增加处理"""
group_decrease_handle = on_notice(priority=1, block=False)
"""群员减少处理"""
add_group = on_request(priority=1, block=False)
"""加群同意请求"""
@group_increase_handle.handle()
async def _(bot: Bot, event: GroupIncreaseNoticeEvent | GroupMemberIncreaseEvent):
superusers = BotConfig.get_superuser("qq")
user_id = str(event.user_id)
group_id = str(event.group_id)
if user_id == bot.self_id:
"""新成员为bot本身"""
group = await GroupConsole.get_or_none(
group_id=group_id, channel_id__isnull=True
)
if not group or group.group_flag == 0:
"""群聊不存在或被强制拉群"""
if base_config.get("flag"):
"""退出群组"""
try:
if result_msg := base_config.get("message"):
await bot.send_group_msg(
group_id=event.group_id, message=result_msg
)
await bot.set_group_leave(group_id=event.group_id)
if superusers:
await bot.send_private_msg(
user_id=int(superusers[0]),
message=f"触发强制入群保护,已成功退出群聊 {group_id}...",
)
logger.info(
"强制拉群或未有群信息,退出群聊成功",
"入群检测",
group_id=event.group_id,
)
if req := await FgRequest.get_or_none(
group_id=group_id, handle_type__isnull=True
):
req.handle_type = RequestHandleType.IGNORE
await req.save(update_fields=["handle_type"])
except Exception as e:
logger.error(
"强制拉群或未有群信息,退出群聊失败",
"入群检测",
group_id=event.group_id,
e=e,
)
if superusers:
await bot.send_private_msg(
user_id=int(superusers[0]),
message="触发强制入群保护,"
f"退出群聊 {event.group_id} 失败...",
)
await GroupConsole.filter(group_id=group_id).delete()
else:
"""允许群组并设置群认证,默认群功能开关"""
if group:
await GroupConsole.filter(
group_id=group_id, channel_id__isnull=True
).update(group_flag=1)
else:
block_plugin = ""
if plugin_list := await PluginInfo.filter(
default_status=False
).all():
for plugin in plugin_list:
block_plugin += f"{plugin.module},"
group_info = await bot.get_group_info(group_id=event.group_id)
await GroupConsole.create(
group_id=group_info["group_id"],
group_name=group_info["group_name"],
max_member_count=group_info["max_member_count"],
member_count=group_info["member_count"],
group_flag=1,
block_plugin=block_plugin,
platform="qq",
)
"""刷新群管理员权限"""
admin_default_auth = Config.get_config(
"admin_bot_manage", "ADMIN_DEFAULT_AUTH"
)
# 即刻刷新权限
for user_info in await bot.get_group_member_list(
group_id=event.group_id
):
"""即刻刷新权限"""
if (
user_info["role"]
in [
"owner",
"admin",
]
and not await LevelUser.is_group_flag(
user_info["user_id"], group_id
)
and admin_default_auth is not None
):
await LevelUser.set_level(
user_info["user_id"],
user_info["group_id"],
admin_default_auth,
)
logger.debug(
f"添加默认群管理员权限: {admin_default_auth}",
"入群检测",
session=user_info["user_id"],
group_id=user_info["group_id"],
)
if str(user_info["user_id"]) in bot.config.superusers:
await LevelUser.set_level(
user_info["user_id"], user_info["group_id"], 9
)
logger.debug(
"添加超级用户权限: 9",
"入群检测",
session=user_info["user_id"],
group_id=user_info["group_id"],
)
else:
join_time = datetime.now()
user_info = await bot.get_group_member_info(
group_id=event.group_id, user_id=event.user_id
)
await GroupInfoUser.update_or_create(
user_id=str(user_info["user_id"]),
group_id=str(user_info["group_id"]),
defaults={"user_name": user_info["nickname"], "user_join_time": join_time},
)
logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功")
if _flmt.check(group_id):
"""群欢迎消息"""
_flmt.start_cd(group_id)
path = DATA_PATH / "welcome_message" / "qq" / f"{group_id}"
file = path / "text.json"
msg_list = []
if file.exists():
data = json.load((path / "text.json").open(encoding="utf-8"))
message = data["message"]
msg_split = re.split(r"\[image:\d+\]", message)
if data["at"]:
msg_list.append(At(flag="user", target=user_id))
for i, text in enumerate(msg_split):
msg_list.append(text)
img_file = path / f"{i}.png"
if img_file.exists():
msg_list.append(img_file)
if not await CommonUtils.task_is_block("group_welcome", group_id):
logger.info("发送群欢迎消息...", "入群检测", group_id=group_id)
if msg_list:
await MessageUtils.build_message(msg_list).send()
else:
image = (
IMAGE_PATH
/ "qxz"
/ random.choice(os.listdir(IMAGE_PATH / "qxz"))
)
await MessageUtils.build_message(
[
"新人快跑啊!!本群现状↓(快使用自定义!)",
image,
]
).send()
@group_decrease_handle.handle()
async def _(bot: Bot, event: GroupDecreaseNoticeEvent | GroupMemberDecreaseEvent):
if event.sub_type == "kick_me":
"""踢出Bot"""
group_id = event.group_id
operator_id = event.operator_id
if user := await GroupInfoUser.get_or_none(
user_id=str(event.operator_id), group_id=str(event.group_id)
):
operator_name = user.user_name
else:
operator_name = "None"
group = await GroupConsole.filter(group_id=str(group_id)).first()
group_name = group.group_name if group else ""
if superusers := BotConfig.get_superuser("qq"):
coffee = int(superusers[0])
await bot.send_private_msg(
user_id=coffee,
message=f"****呜..一份踢出报告****\n"
f"我被 {operator_name}({operator_id})\n"
f"踢出了 {group_name}({group_id})\n"
f"日期:{str(datetime.now()).split('.')[0]}",
)
if group:
await group.delete()
return
if str(event.user_id) == bot.self_id:
"""踢出Bot"""
await GroupConsole.filter(group_id=str(event.group_id)).delete()
return
if user := await GroupInfoUser.get_or_none(
user_id=str(event.user_id), group_id=str(event.group_id)
):
user_name = user.user_name
else:
user_name = f"{event.user_id}"
await GroupInfoUser.filter(
user_id=str(event.user_id), group_id=str(event.group_id)
).delete()
logger.info(
f"名称: {user_name} 退出群聊",
"group_decrease_handle",
session=event.user_id,
group_id=event.group_id,
)
result = ""
if event.sub_type == "leave":
result = f"{user_name}离开了我们..."
if event.sub_type == "kick":
operator = await bot.get_group_member_info(
user_id=event.operator_id, group_id=event.group_id
)
operator_name = operator["card"] or operator["nickname"]
result = f"{user_name} 被 {operator_name} 送走了."
if not await CommonUtils.task_is_block("refund_group_remind", str(event.group_id)):
await group_decrease_handle.send(f"{result}")
@@ -113,7 +113,7 @@ class GroupManager:
@classmethod
async def add_bot(
cls, bot: Bot, operator_id: str, group_id: str, group: GroupConsole
cls, bot: Bot, operator_id: str, group_id: str, group: GroupConsole | None
):
"""拉入bot
@@ -123,11 +123,7 @@ class GroupManager:
group_id: 群组id
group: GroupConsole
"""
if (
base_config.get("flag")
and operator_id not in bot.config.superusers
and group.group_flag != 1
):
if base_config.get("flag") and operator_id not in bot.config.superusers:
"""退出群组"""
try:
if result_msg := base_config.get("message"):
+1 -1
View File
@@ -184,7 +184,7 @@ async def _(
elif isinstance(result, UniMessage):
await result.finish(reply_to=True)
except GoodsNotFound:
await MessageUtils.build_message(f"没有找到道具 {name.result} 或道具数量不足...").send(
await MessageUtils.build_message(f"没有找到道具 {name} 或道具数量不足...").send(
reply_to=True
)
@@ -1,30 +0,0 @@
import os
import time
from nonebot.rule import to_me
from nonebot.utils import run_sync
from nonebot.permission import SUPERUSER
from nonebot.plugin import PluginMetadata
from nonebot_plugin_session import EventSession
from nonebot_plugin_apscheduler import scheduler
from nonebot_plugin_alconna import Alconna, on_alconna
from zhenxun.services.log import logger
from zhenxun.utils.enum import PluginType
from zhenxun.utils.message import MessageUtils
from zhenxun.configs.path_config import TEMP_PATH
from zhenxun.configs.utils import PluginExtraData
from zhenxun.utils.utils import ResourceDirManager
__plugin_meta__ = PluginMetadata(
name="Bot管理",
description="指定bot对象的功能/被动开关和状态",
usage="""
清理临时数据
""".strip(),
extra=PluginExtraData(
author="",
version="0.1",
plugin_type=PluginType.SUPERUSER,
).dict(),
)
-61
View File
@@ -1,61 +0,0 @@
from nonebot.rule import Rule
from nonebot.adapters import Bot, Event
from nonebot_plugin_uninfo import Uninfo
from nonebot.plugin import PluginMetadata
from nonebot_plugin_alconna.uniseg.tools import reply_fetch
from nonebot_plugin_alconna import Alconna, Arparma, on_alconna
from zhenxun.services.log import logger
from zhenxun.utils.message import MessageUtils
from zhenxun.utils.platform import PlatformUtils
from zhenxun.configs.utils import PluginExtraData
from zhenxun.utils.manager.message_manager import MessageManager
__plugin_meta__ = PluginMetadata(
name="消息撤回",
description="撤回自己触发的消息撤回,不允许撤回其他人触发消息的撤回哦",
usage="""
引用消息 撤回
""".strip(),
extra=PluginExtraData(author="HibiKier", version="0.1", menu_type="其他").dict(),
)
def reply_check() -> Rule:
"""
检查是否存在回复消息
返回:
Rule: Rule
"""
async def _rule(bot: Bot, event: Event, session: Uninfo):
if event.get_type() == "message":
return (
bool(await reply_fetch(event, bot))
and PlatformUtils.get_platform(session) == "qq"
)
return False
return Rule(_rule)
_matcher = on_alconna(Alconna("撤回"), priority=5, block=True, rule=reply_check())
@_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
):
try:
await bot.delete_msg(message_id=reply.id)
logger.info("撤回消息", arparma.header_result, session=session)
except Exception:
await MessageUtils.build_message("撤回失败,可能消息已过期...").send()
else:
await MessageUtils.build_message(
"权限不足,不是你触发的消息不要胡乱撤回哦..."
).send()
+13 -6
View File
@@ -288,6 +288,13 @@ class ConfigsManager:
config.value = value
config.default_value = default_value
else:
_module = None
if ":" in module:
module_split = module.split(":")
if len(module_split) < 2:
raise ValueError(f"module: {module} 填写错误")
_module = module_split[-1]
module = module_split[0]
key = key.upper()
if not self._data.get(module):
self._data[module] = ConfigGroup(module=module)
@@ -304,6 +311,7 @@ class ConfigsManager:
key: str,
value: Any,
auto_save: bool = False,
save_simple_data: bool = True,
):
"""设置配置值
@@ -312,16 +320,15 @@ class ConfigsManager:
key: 配置名称
value: 值
auto_save: 自动保存.
save_simple_data: 保存至config.yaml.
"""
key = key.upper()
if module in self._data:
if self._data[module].configs.get(key):
data = self._data[module].configs.get(key)
if data and data != value:
self._data[module].configs[key].value = value
else:
self.add_plugin_config(module, key, value)
self._simple_data[module][key] = value
self._simple_data[module][key] = value
if auto_save:
self.save(save_simple_data=True)
self.save(save_simple_data=save_simple_data)
def get_config(self, module: str, key: str, default: Any = None) -> Any:
"""获取指定配置值
-60
View File
@@ -1,60 +0,0 @@
from tortoise import fields
from zhenxun.services.db_context import Model
class BotConsole(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id"""
bot_id = fields.CharField(255, unique=True, description="bot_id")
"""bot_id"""
status = fields.BooleanField(default=True, description="Bot状态")
"""Bot状态"""
block_plugin = fields.TextField(default="", description="禁用插件")
"""禁用插件"""
block_task = fields.TextField(default="", description="禁用被动技能")
"""禁用被动技能"""
create_time = fields.DatetimeField(auto_now_add=True, description="创建时间")
"""创建时间"""
platform = fields.CharField(255, null=True, description="平台")
"""平台"""
class Meta: # type: ignore
table = "bot_console"
table_description = "Bot数据表"
@classmethod
async def get_bot_status(cls, bot_id: str) -> bool:
result = await cls.get_or_none(bot_id=bot_id)
return result.status if result else False
@classmethod
async def set_block_plugin(cls, bot_id: str, module: str):
bot_data, _ = await cls.get_or_create(bot_id=bot_id)
if f"<{module}," not in bot_data.block_plugin:
bot_data.block_plugin += f"<{module},"
await bot_data.save(update_fields=["block_plugin"])
@classmethod
async def set_unblock_plugin(cls, bot_id: str, module: str):
bot_data, _ = await cls.get_or_create(bot_id=bot_id)
if f"<{module}," in bot_data.block_plugin:
bot_data.block_plugin = bot_data.block_plugin.replace(f"<{module},", "")
await bot_data.save(update_fields=["block_plugin"])
@classmethod
async def set_block_task(cls, bot_id: str, task: str):
bot_data, _ = await cls.get_or_create(bot_id=bot_id)
if f"<{task}," not in bot_data.block_task:
bot_data.block_plugin += f"<{task},"
await bot_data.save(update_fields=["block_task"])
@classmethod
async def is_block_plugin(cls, bot_id: str, task: str) -> bool:
bot_data, _ = await cls.get_or_create(bot_id=bot_id)
return f"<{task}," in bot_data.block_plugin
@classmethod
async def is_block_task(cls, bot_id: str, task: str) -> bool:
bot_data, _ = await cls.get_or_create(bot_id=bot_id)
return f"<{task}," in bot_data.block_task
+2 -2
View File
@@ -37,8 +37,8 @@ class GroupConsole(Model):
default="", description="超级用户禁用插件"
)
"""超级用户禁用插件"""
block_task = fields.TextField(default="", description="禁用被动技能")
"""禁用被动技能"""
block_task = fields.TextField(default="", description="禁用插件")
"""禁用插件"""
superuser_block_task = fields.TextField(default="", description="超级用户禁用被动")
"""超级用户禁用被动"""
platform = fields.CharField(255, default="qq", description="所属平台")
+6 -4
View File
@@ -10,6 +10,7 @@ from ._build_image import BuildImage
class RowStyle(BaseModel):
font: FreeTypeFont | str | Path | None = "HYWenHei-85W.ttf"
"""字体"""
font_size: int = 20
@@ -22,6 +23,7 @@ class RowStyle(BaseModel):
class ImageTemplate:
color_list = ["#C2CEFE", "#FFA94C", "#3FE6A0", "#D1D4F5"] # noqa: RUF012
@classmethod
@@ -86,7 +88,7 @@ class ImageTemplate:
head_text: str,
tip_text: str | None,
column_name: list[str],
data_list: list[list[str | int | tuple[Path | BuildImage, int, int]]],
data_list: list[list[str | tuple[Path | BuildImage, int, int]]],
row_space: int = 35,
column_space: int = 30,
padding: int = 5,
@@ -135,7 +137,7 @@ class ImageTemplate:
async def table(
cls,
column_name: list[str],
data_list: list[list[str | int | tuple[Path | BuildImage, int, int]]],
data_list: list[list[str | tuple[Path | BuildImage, int, int]]],
row_space: int = 25,
column_space: int = 10,
padding: int = 5,
@@ -161,7 +163,7 @@ class ImageTemplate:
c = []
for lst in data_list:
if len(lst) > i:
c.append(str(lst[i]))
c.append(lst[i])
else:
c.append("")
column_data.append(c)
@@ -174,7 +176,7 @@ class ImageTemplate:
if isinstance(s, tuple):
w = s[1]
else:
w, _ = BuildImage.get_text_size(str(s), font)
w, _ = BuildImage.get_text_size(s, font)
if w > _temp["width"]:
_temp["width"] = w
build_data_list.append(_temp)
+35
View File
@@ -0,0 +1,35 @@
import re
GITHUB_REPO_URL_PATTERN = re.compile(
r"^https://github.com/(?P<owner>[^/]+)/(?P<repo>[^/]+)(/tree/(?P<branch>[^/]+))?$"
)
"""github仓库地址正则"""
JSD_PACKAGE_API_FORMAT = (
"https://data.jsdelivr.com/v1/packages/gh/{owner}/{repo}@{branch}"
)
"""jsdelivr包地址格式"""
GIT_API_TREES_FORMAT = (
"https://api.github.com/repos/{owner}/{repo}/git/trees/{branch}?recursive=1"
)
"""git api trees地址格式"""
CACHED_API_TTL = 300
"""缓存api ttl"""
RAW_CONTENT_FORMAT = "https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}"
"""raw content格式"""
ARCHIVE_URL_FORMAT = "https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.zip"
"""archive url格式"""
RELEASE_ASSETS_FORMAT = (
"https://github.com/{owner}/{repo}/releases/download/{version}/{filename}"
)
"""release assets格式"""
RELEASE_SOURCE_FORMAT = (
"https://codeload.github.com/{owner}/{repo}/legacy.{compress}/refs/tags/{version}"
)
"""release 源码格式"""
+2 -2
View File
@@ -390,7 +390,7 @@ def get_img_hash(image_file: str | Path) -> str:
return str(hash_value)
async def get_download_image_hash(url: str, mark: str, use_proxy: bool = False) -> str:
async def get_download_image_hash(url: str, mark: str) -> str:
"""下载图片获取哈希值
参数:
@@ -402,7 +402,7 @@ async def get_download_image_hash(url: str, mark: str, use_proxy: bool = False)
"""
try:
if await AsyncHttpx.download_file(
url, TEMP_PATH / f"compare_download_{mark}_img.jpg", use_proxy=use_proxy
url, TEMP_PATH / f"compare_download_{mark}_img.jpg"
):
img_hash = get_img_hash(TEMP_PATH / f"compare_download_{mark}_img.jpg")
return str(img_hash)
-27
View File
@@ -1,27 +0,0 @@
from typing import ClassVar
class MessageManager:
data: ClassVar[dict[str, list[str]]] = {}
@classmethod
def add(cls, uid: str, msg_id: str):
if uid not in cls.data:
cls.data[uid] = []
cls.data[uid].append(msg_id)
cls.remove_check(uid)
@classmethod
def check(cls, uid: str, msg_id: str) -> bool:
return msg_id in cls.data.get(uid, [])
@classmethod
def remove_check(cls, uid: str):
if len(cls.data[uid]) > 200:
cls.data[uid] = cls.data[uid][100:]
@classmethod
def get(cls, uid: str) -> list[str]:
if uid in cls.data:
return cls.data[uid]
return []
+2 -42
View File
@@ -4,17 +4,7 @@ from pathlib import Path
import nonebot
from pydantic import BaseModel
from nonebot.adapters.onebot.v11 import Message, MessageSegment
from nonebot_plugin_alconna import (
At,
Text,
AtAll,
Image,
Video,
Voice,
Reference,
CustomNode,
UniMessage,
)
from nonebot_plugin_alconna import At, Text, AtAll, Image, Video, Voice, UniMessage
from zhenxun.services.log import logger
from zhenxun.configs.config import BotConfig
@@ -99,36 +89,6 @@ class MessageUtils:
message_list += cls.__build_message(_data) # type: ignore
return UniMessage(message_list)
@classmethod
def alc_forward_msg(
cls,
msg_list: list,
uin: str,
name: str,
) -> UniMessage:
"""生成自定义合并消息
参数:
msg_list: 消息列表
uin: 发送者 QQ
name: 自定义名称
返回:
list[dict]: 转发消息
"""
node_list = []
for _message in msg_list:
if isinstance(_message, list):
for i in range(len(_message.copy())):
if isinstance(_message[i], Path):
_message[i] = Image(
raw=BuildImage.open(_message[i]).pic2bytes()
)
node_list.append(
CustomNode(uid=uin, name=name, content=UniMessage(_message))
)
return UniMessage(Reference(nodes=node_list))
@classmethod
def custom_forward_msg(
cls,
@@ -178,7 +138,7 @@ class MessageUtils:
if isinstance(r, Text):
s += str(r)
elif isinstance(r, Image):
if v := r.url or r.path or r.raw:
if v := r.url or r.path:
s += MessageSegment.image(v)
elif isinstance(r_list, Image):
if v := r_list.url or r_list.path:
+2 -2
View File
@@ -27,14 +27,14 @@ def admin_check(a: int | str, key: str | None = None) -> Rule:
if PlatformUtils.is_qbot(session):
"""官bot接口,放弃所有权限检查"""
return False
if session.group:
if session.id and session.group:
level = a
if isinstance(a, str) and key:
level = Config.get_config(a, key)
if level is not None:
return bool(
await LevelUser.check_level(
session.user.id, session.group.id, int(level)
session.id, session.group.id, int(level)
)
)
return False
-1
View File
@@ -189,7 +189,6 @@ def change_pixiv_image_links(
url = (
url.replace("i.pximg.net", nginx_url)
.replace("i.pixiv.cat", nginx_url)
.replace("i.pixiv.re", nginx_url)
.replace("_webp", "")
)
return url