Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c614e7d174 | ||
|
|
c1e620e0fc | ||
|
|
0077a0a246 | ||
|
|
dc8af2a6a3 | ||
|
|
1901adb82a | ||
|
|
7d05e3047c | ||
|
|
44cb3d7222 | ||
|
|
a0c6ba5ac0 | ||
|
|
13ee30f706 | ||
|
|
eca01e9d7f | ||
|
|
35a728e20e |
@@ -68,7 +68,7 @@
|
|||||||
|
|
||||||
### 1. 体验一下?
|
### 1. 体验一下?
|
||||||
|
|
||||||
这是一个免费的,版本为 dev 的 zhenxun,你可以通过 napcat 或拉格朗日等直接连接用于体验与测试
|
这是一个免费的,版本为 dev 的 zhenxun,你可以通过 [napcat](https://github.com/NapNeko/NapCatQQ) 或 [拉格朗日](https://github.com/LagrangeDev/Lagrange.Core) 以及 [matcha](https://github.com/A-kirami/matcha) 等直接连接用于体验与测试
|
||||||
(球球了测试君!)
|
(球球了测试君!)
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -95,13 +95,14 @@ AccessToken: PUBLIC_ZHENXUN_TEST
|
|||||||
<details>
|
<details>
|
||||||
<summary> <strong> WebUI </strong>后台示例图 </summary>
|
<summary> <strong> WebUI </strong>后台示例图 </summary>
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@@ -319,7 +320,7 @@ python bot.py
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>爱发电 以及 感谢投喂 </summary>
|
<summary>爱发电 以及 感谢投喂 </summary>
|
||||||
<img width="365px" height="450px" src="https://user-images.githubusercontent.com/45528451/175059389-cfeb8174-fa07-4939-80ab-a039087a50f6.png">
|
<img width="365px" height="450px" src="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/docs_image/afd.jpg">
|
||||||
|
|
||||||
### 感谢名单
|
### 感谢名单
|
||||||
|
|
||||||
@@ -369,6 +370,7 @@ python bot.py
|
|||||||
[哇](https://afdian.net/u/9b266244f23911eca19052540025c377)
|
[哇](https://afdian.net/u/9b266244f23911eca19052540025c377)
|
||||||
[yajiwa](https://github.com/yajiwa)
|
[yajiwa](https://github.com/yajiwa)
|
||||||
[爆金币](https://afdian.net/u/0d78879ef23711ecb22452540025c377)
|
[爆金币](https://afdian.net/u/0d78879ef23711ecb22452540025c377)
|
||||||
|
...
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__: v0.2.4-fix1
|
__version__: v0.2.4-fix2
|
||||||
|
|||||||
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 315 KiB |
|
After Width: | Height: | Size: 352 KiB |
|
After Width: | Height: | Size: 279 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 228 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
Before Width: | Height: | Size: 230 KiB |
|
Before Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 60 KiB |
@@ -235,7 +235,10 @@ class AuthChecker:
|
|||||||
return
|
return
|
||||||
if plugin := await PluginInfo.get_or_none(module_path=module_path):
|
if plugin := await PluginInfo.get_or_none(module_path=module_path):
|
||||||
if plugin.plugin_type == PluginType.HIDDEN:
|
if plugin.plugin_type == PluginType.HIDDEN:
|
||||||
logger.debug("插件为HIDDEN,已跳过...")
|
logger.debug(
|
||||||
|
f"插件: {plugin.name}:{plugin.module} "
|
||||||
|
"为HIDDEN,已跳过权限检查..."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
cost_gold = await self.auth_cost(user, plugin, session)
|
cost_gold = await self.auth_cost(user, plugin, session)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ async def _(
|
|||||||
if plugin := matcher.plugin:
|
if plugin := matcher.plugin:
|
||||||
if metadata := plugin.metadata:
|
if metadata := plugin.metadata:
|
||||||
extra = metadata.extra
|
extra = metadata.extra
|
||||||
if extra.get("plugin_type") in [PluginType.HIDDEN, PluginType.DEPENDANT]:
|
if extra.get("plugin_type") in [PluginType.HIDDEN]:
|
||||||
return
|
return
|
||||||
user_id = session.id1
|
user_id = session.id1
|
||||||
group_id = session.id3 or session.id2
|
group_id = session.id3 or session.id2
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
@@ -113,7 +113,7 @@ class GroupManager:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def add_bot(
|
async def add_bot(
|
||||||
cls, bot: Bot, operator_id: str, group_id: str, group: GroupConsole | None
|
cls, bot: Bot, operator_id: str, group_id: str, group: GroupConsole
|
||||||
):
|
):
|
||||||
"""拉入bot
|
"""拉入bot
|
||||||
|
|
||||||
@@ -123,7 +123,11 @@ class GroupManager:
|
|||||||
group_id: 群组id
|
group_id: 群组id
|
||||||
group: GroupConsole
|
group: GroupConsole
|
||||||
"""
|
"""
|
||||||
if base_config.get("flag") and operator_id not in bot.config.superusers:
|
if (
|
||||||
|
base_config.get("flag")
|
||||||
|
and operator_id not in bot.config.superusers
|
||||||
|
and group.group_flag != 1
|
||||||
|
):
|
||||||
"""退出群组"""
|
"""退出群组"""
|
||||||
try:
|
try:
|
||||||
if result_msg := base_config.get("message"):
|
if result_msg := base_config.get("message"):
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ async def _(
|
|||||||
elif isinstance(result, UniMessage):
|
elif isinstance(result, UniMessage):
|
||||||
await result.finish(reply_to=True)
|
await result.finish(reply_to=True)
|
||||||
except GoodsNotFound:
|
except GoodsNotFound:
|
||||||
await MessageUtils.build_message(f"没有找到道具 {name} 或道具数量不足...").send(
|
await MessageUtils.build_message(f"没有找到道具 {name.result} 或道具数量不足...").send(
|
||||||
reply_to=True
|
reply_to=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
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()
|
||||||
@@ -288,13 +288,6 @@ class ConfigsManager:
|
|||||||
config.value = value
|
config.value = value
|
||||||
config.default_value = default_value
|
config.default_value = default_value
|
||||||
else:
|
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()
|
key = key.upper()
|
||||||
if not self._data.get(module):
|
if not self._data.get(module):
|
||||||
self._data[module] = ConfigGroup(module=module)
|
self._data[module] = ConfigGroup(module=module)
|
||||||
@@ -311,7 +304,6 @@ class ConfigsManager:
|
|||||||
key: str,
|
key: str,
|
||||||
value: Any,
|
value: Any,
|
||||||
auto_save: bool = False,
|
auto_save: bool = False,
|
||||||
save_simple_data: bool = True,
|
|
||||||
):
|
):
|
||||||
"""设置配置值
|
"""设置配置值
|
||||||
|
|
||||||
@@ -320,15 +312,16 @@ class ConfigsManager:
|
|||||||
key: 配置名称
|
key: 配置名称
|
||||||
value: 值
|
value: 值
|
||||||
auto_save: 自动保存.
|
auto_save: 自动保存.
|
||||||
save_simple_data: 保存至config.yaml.
|
|
||||||
"""
|
"""
|
||||||
|
key = key.upper()
|
||||||
if module in self._data:
|
if module in self._data:
|
||||||
data = self._data[module].configs.get(key)
|
if self._data[module].configs.get(key):
|
||||||
if data and data != value:
|
|
||||||
self._data[module].configs[key].value = value
|
self._data[module].configs[key].value = value
|
||||||
self._simple_data[module][key] = value
|
else:
|
||||||
|
self.add_plugin_config(module, key, value)
|
||||||
|
self._simple_data[module][key] = value
|
||||||
if auto_save:
|
if auto_save:
|
||||||
self.save(save_simple_data=save_simple_data)
|
self.save(save_simple_data=True)
|
||||||
|
|
||||||
def get_config(self, module: str, key: str, default: Any = None) -> Any:
|
def get_config(self, module: str, key: str, default: Any = None) -> Any:
|
||||||
"""获取指定配置值
|
"""获取指定配置值
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ from ._build_image import BuildImage
|
|||||||
|
|
||||||
|
|
||||||
class RowStyle(BaseModel):
|
class RowStyle(BaseModel):
|
||||||
|
|
||||||
font: FreeTypeFont | str | Path | None = "HYWenHei-85W.ttf"
|
font: FreeTypeFont | str | Path | None = "HYWenHei-85W.ttf"
|
||||||
"""字体"""
|
"""字体"""
|
||||||
font_size: int = 20
|
font_size: int = 20
|
||||||
@@ -23,7 +22,6 @@ class RowStyle(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class ImageTemplate:
|
class ImageTemplate:
|
||||||
|
|
||||||
color_list = ["#C2CEFE", "#FFA94C", "#3FE6A0", "#D1D4F5"] # noqa: RUF012
|
color_list = ["#C2CEFE", "#FFA94C", "#3FE6A0", "#D1D4F5"] # noqa: RUF012
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -88,7 +86,7 @@ class ImageTemplate:
|
|||||||
head_text: str,
|
head_text: str,
|
||||||
tip_text: str | None,
|
tip_text: str | None,
|
||||||
column_name: list[str],
|
column_name: list[str],
|
||||||
data_list: list[list[str | tuple[Path | BuildImage, int, int]]],
|
data_list: list[list[str | int | tuple[Path | BuildImage, int, int]]],
|
||||||
row_space: int = 35,
|
row_space: int = 35,
|
||||||
column_space: int = 30,
|
column_space: int = 30,
|
||||||
padding: int = 5,
|
padding: int = 5,
|
||||||
@@ -137,7 +135,7 @@ class ImageTemplate:
|
|||||||
async def table(
|
async def table(
|
||||||
cls,
|
cls,
|
||||||
column_name: list[str],
|
column_name: list[str],
|
||||||
data_list: list[list[str | tuple[Path | BuildImage, int, int]]],
|
data_list: list[list[str | int | tuple[Path | BuildImage, int, int]]],
|
||||||
row_space: int = 25,
|
row_space: int = 25,
|
||||||
column_space: int = 10,
|
column_space: int = 10,
|
||||||
padding: int = 5,
|
padding: int = 5,
|
||||||
@@ -163,7 +161,7 @@ class ImageTemplate:
|
|||||||
c = []
|
c = []
|
||||||
for lst in data_list:
|
for lst in data_list:
|
||||||
if len(lst) > i:
|
if len(lst) > i:
|
||||||
c.append(lst[i])
|
c.append(str(lst[i]))
|
||||||
else:
|
else:
|
||||||
c.append("")
|
c.append("")
|
||||||
column_data.append(c)
|
column_data.append(c)
|
||||||
@@ -176,7 +174,7 @@ class ImageTemplate:
|
|||||||
if isinstance(s, tuple):
|
if isinstance(s, tuple):
|
||||||
w = s[1]
|
w = s[1]
|
||||||
else:
|
else:
|
||||||
w, _ = BuildImage.get_text_size(s, font)
|
w, _ = BuildImage.get_text_size(str(s), font)
|
||||||
if w > _temp["width"]:
|
if w > _temp["width"]:
|
||||||
_temp["width"] = w
|
_temp["width"] = w
|
||||||
build_data_list.append(_temp)
|
build_data_list.append(_temp)
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
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 []
|
||||||
@@ -4,7 +4,17 @@ from pathlib import Path
|
|||||||
import nonebot
|
import nonebot
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from nonebot.adapters.onebot.v11 import Message, MessageSegment
|
from nonebot.adapters.onebot.v11 import Message, MessageSegment
|
||||||
from nonebot_plugin_alconna import At, Text, AtAll, Image, Video, Voice, UniMessage
|
from nonebot_plugin_alconna import (
|
||||||
|
At,
|
||||||
|
Text,
|
||||||
|
AtAll,
|
||||||
|
Image,
|
||||||
|
Video,
|
||||||
|
Voice,
|
||||||
|
Reference,
|
||||||
|
CustomNode,
|
||||||
|
UniMessage,
|
||||||
|
)
|
||||||
|
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.configs.config import BotConfig
|
from zhenxun.configs.config import BotConfig
|
||||||
@@ -89,6 +99,36 @@ class MessageUtils:
|
|||||||
message_list += cls.__build_message(_data) # type: ignore
|
message_list += cls.__build_message(_data) # type: ignore
|
||||||
return UniMessage(message_list)
|
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
|
@classmethod
|
||||||
def custom_forward_msg(
|
def custom_forward_msg(
|
||||||
cls,
|
cls,
|
||||||
@@ -138,7 +178,7 @@ class MessageUtils:
|
|||||||
if isinstance(r, Text):
|
if isinstance(r, Text):
|
||||||
s += str(r)
|
s += str(r)
|
||||||
elif isinstance(r, Image):
|
elif isinstance(r, Image):
|
||||||
if v := r.url or r.path:
|
if v := r.url or r.path or r.raw:
|
||||||
s += MessageSegment.image(v)
|
s += MessageSegment.image(v)
|
||||||
elif isinstance(r_list, Image):
|
elif isinstance(r_list, Image):
|
||||||
if v := r_list.url or r_list.path:
|
if v := r_list.url or r_list.path:
|
||||||
|
|||||||
@@ -189,6 +189,7 @@ def change_pixiv_image_links(
|
|||||||
url = (
|
url = (
|
||||||
url.replace("i.pximg.net", nginx_url)
|
url.replace("i.pximg.net", nginx_url)
|
||||||
.replace("i.pixiv.cat", nginx_url)
|
.replace("i.pixiv.cat", nginx_url)
|
||||||
|
.replace("i.pixiv.re", nginx_url)
|
||||||
.replace("_webp", "")
|
.replace("_webp", "")
|
||||||
)
|
)
|
||||||
return url
|
return url
|
||||||
|
|||||||