mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-29 08:39:59 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8381858742 | ||
|
|
1040bd0d58 | ||
|
|
404e47332e | ||
|
|
3db128785e | ||
|
|
2189ea11df | ||
|
|
49b9bd65b0 | ||
|
|
b3fdb1ff58 | ||
|
|
c79e3fdcf0 | ||
|
|
d7509f7030 | ||
|
|
5c99b55502 | ||
|
|
fd255f022b | ||
|
|
41642589ae | ||
|
|
619fef3bff | ||
|
|
38c0da0cf8 | ||
|
|
b241a58e84 | ||
|
|
35c9585b22 | ||
|
|
d2fe95cc05 | ||
|
|
92e60ba141 | ||
|
|
2356527791 | ||
|
|
cab0f7b522 | ||
|
|
02743d5955 | ||
|
|
a7ffebea35 | ||
|
|
c511ef50a2 | ||
|
|
82902e0cde | ||
|
|
1de5ae09bc | ||
|
|
8b735f1cb5 | ||
|
|
33d1334174 | ||
|
|
324b58c21b | ||
|
|
b7b1651709 | ||
|
|
b5a4a6a9ff | ||
|
|
fe2c82b03c | ||
|
|
81e4bca9ef | ||
|
|
c0d055b825 | ||
|
|
188ae38525 | ||
|
|
86a8fe7096 | ||
|
|
0bb2b45f5c | ||
|
|
a17d8731c8 | ||
|
|
9cdd2326c8 | ||
|
|
d75653631d | ||
|
|
e234c8906e | ||
|
|
57baea18b5 | ||
|
|
0f3efe29c4 | ||
|
|
89b87a38b3 | ||
|
|
c52b7ca2e9 | ||
|
|
371b55dc9e | ||
|
|
53e572353c | ||
|
|
3ae71c74e9 | ||
|
|
cb643c04f7 | ||
|
|
faf9ee159b | ||
|
|
ffb3b2d73e | ||
|
|
ffcc327089 | ||
|
|
dfd5a6a86a | ||
|
|
d43f89b067 | ||
|
|
f15c3ab6a2 | ||
|
|
e27809298e | ||
|
|
4b3e28e14b | ||
|
|
71ce9153ed | ||
|
|
25d63966de | ||
|
|
98afbb54b6 | ||
|
|
03a51ca5dd | ||
|
|
428481b2d5 | ||
|
|
f87c60e6db | ||
|
|
6d824e38bc | ||
|
|
f0fd5bfb9e | ||
|
|
33374df671 | ||
|
|
c035b1b19c | ||
|
|
b3b09ed666 | ||
|
|
8029c00a02 | ||
|
|
a2c0921013 | ||
|
|
31cdabc229 | ||
|
|
1059a0aae0 | ||
|
|
e6576e7f40 | ||
|
|
5837144095 | ||
|
|
d30a5c4207 | ||
|
|
7d169b0e5e | ||
|
|
1f54226673 | ||
|
|
901a90ff13 | ||
|
|
56573d1d34 |
@@ -142,6 +142,5 @@ test.py
|
||||
server_ip.py
|
||||
member_activity_handle.py
|
||||
Yu-Gi-Oh/
|
||||
black_word/
|
||||
csgo/
|
||||
fantasy_card/
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
## 声明
|
||||
此项目仅用于学习交流,请勿用于非法用途
|
||||
|
||||
## Gitee
|
||||
|
||||
# [Gitee](https://gitee.com/two_Dimension/zhenxun_bot)
|
||||
|
||||
## 未完成的文档
|
||||
|
||||
# [传送门](https://hibikier.github.io/zhenxun_bot/)
|
||||
@@ -242,6 +238,88 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
|
||||
|
||||
## 更新
|
||||
|
||||
### 2022/5/23 \[v1.5.6]
|
||||
|
||||
* 修复"清除已删除插件数据" [@pull/545](https://github.com/HibiKier/zhenxun_bot/pull/545)
|
||||
* 修复有置顶的up主B站动态获取失败 [@pull/552](https://github.com/HibiKier/zhenxun_bot/pull/552)
|
||||
* 添加pixiv搜图多关键词支持;修复p站搜图数量参数问题 [@pull/441](https://github.com/HibiKier/zhenxun_bot/pull/441)
|
||||
* 修复开箱更新价格错误传参
|
||||
* 修复pix无法正确查询uid
|
||||
* 新增色图插件添加配置项ALLOW_GROUP_R18:允许群聊中使用色图r
|
||||
* 新增PIX插件添加配置项ALLOW_GROUP_SETU:允许非超级用户使用-s参数
|
||||
* 新增PIX插件添加配置项ALLOW_GROUP_R18:允许非超级用户使用-r参数
|
||||
|
||||
### 2022/5/22 \[v0.1.5.4]
|
||||
|
||||
* 使用action自动更新poetry.lock [@pull/515](https://github.com/HibiKier/zhenxun_bot/pull/515)
|
||||
* fix(bilibili_sub): card is None and timeout [@pull/516](https://github.com/HibiKier/zhenxun_bot/pull/516)
|
||||
* 修复了epic有时获取新免费游戏消息时获取不到图片
|
||||
* 修复好感度满时签到出错(虽然是不可能满的
|
||||
* 修复原神资源图标下载路径错误
|
||||
* 修复自动更新群组可能失败
|
||||
|
||||
### 2022/5/21
|
||||
|
||||
* 修复搜番无结果时报错无正确反馈
|
||||
* 解锁了windows上无法使用playwright的限制
|
||||
* 修复p搜对应pid有多张图时出错,改为连续发送图片
|
||||
* 修复p搜对数字的错误判断
|
||||
* 修复添加商品折扣无法正确添加
|
||||
* 修复了bilibili订阅直播间订阅up名称不一致的问题
|
||||
* 修复原神玩家查询没开地图时报错
|
||||
* 最低priority修改为 999
|
||||
* 修复刷屏检测失效
|
||||
* 修复刷屏检测设置命令无法生效
|
||||
* 优化刷屏显示设置禁言时长显示,并改为分钟
|
||||
* 修复了多连开箱无法指定武器箱
|
||||
* 修复识番链接无法正确获取
|
||||
* 新增真寻入群时即刻刷新权限
|
||||
* 提高了微博热搜截图的等待时间
|
||||
|
||||
### 2022/5/19
|
||||
|
||||
* fix: mihoyo bbs api changed [@pull/357](https://github.com/HibiKier/zhenxun_bot/pull/357)
|
||||
* Add word_clouds [@pull/265](https://github.com/HibiKier/zhenxun_bot/pull/265)
|
||||
* Fix wrong live streamer name [@pull/284](https://github.com/HibiKier/zhenxun_bot/pull/284)
|
||||
|
||||
### 2022/5/16
|
||||
|
||||
* 词条支持图片和@问题 [@pull/160](https://github.com/HibiKier/zhenxun_bot/pull/160)
|
||||
|
||||
### 2022/5/15
|
||||
|
||||
* 修复了商店商品无法正确添加
|
||||
* 修复了多张色图无法正确发送
|
||||
|
||||
### 2022/5/14
|
||||
|
||||
* 修复B站动态生成失败的问题 [@pull/159](https://github.com/HibiKier/zhenxun_bot/pull/159)
|
||||
|
||||
### 2022/5/11
|
||||
|
||||
* fix: 更改p搜api,解决p搜无法使用的问题 [@pull/155](https://github.com/HibiKier/zhenxun_bot/pull/155)
|
||||
|
||||
### 2022/5/9 \[v0.1.5.3]
|
||||
|
||||
* 替换了疫情API
|
||||
* 修复了私聊.ban/.unban出错
|
||||
|
||||
### 2022/5/5
|
||||
|
||||
* 修改bilibili_sub插件在windows平台下报错 [@pull/153](https://github.com/HibiKier/zhenxun_bot/pull/153)
|
||||
|
||||
### 2022/5/3 \[v0.1.5.2]
|
||||
|
||||
* 商品使用函数可以添加特定参数,例如:user_id, group_id, ShopParam等以及自己提供的参数
|
||||
* 添加商品注册装饰器shop_register
|
||||
* 修复商品函数kwargs无法获取参数值
|
||||
|
||||
|
||||
### 2022/5/1
|
||||
|
||||
* 删除了`group_last_chat`插件(该功能可由`chat_history`替代
|
||||
* 新增敏感词检测(全新反击系统,是时候重拳出击了
|
||||
|
||||
### 2022/4/26
|
||||
|
||||
* 修复了群白名单无法正确添加
|
||||
@@ -477,4 +555,4 @@ __..... 更多更新信息请查看文档__
|
||||
[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件
|
||||
[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库
|
||||
[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人
|
||||
[KimigaiiWuyi / GenshinUID]("https://github.com/KimigaiiWuyi/GenshinUID") :一个基于HoshinoBot/NoneBot2的原神UID查询插件
|
||||
[KimigaiiWuyi / GenshinUID](https://github.com/KimigaiiWuyi/GenshinUID) :一个基于HoshinoBot/NoneBot2的原神UID查询插件
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__: v0.1.5.0
|
||||
__version__: v0.1.5.6
|
||||
@@ -232,10 +232,7 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
|
||||
_exist_member_list = []
|
||||
# try:
|
||||
for user_info in _group_user_list:
|
||||
if user_info["card"] == "":
|
||||
nickname = user_info["nickname"]
|
||||
else:
|
||||
nickname = user_info["card"]
|
||||
nickname = user_info["card"] or user_info["nickname"]
|
||||
async with db.transaction():
|
||||
# 更新权限
|
||||
if (
|
||||
@@ -321,3 +318,4 @@ def set_group_bot_status(group_id: int, status: bool) -> str:
|
||||
# for x in group_manager.get_task_data():
|
||||
# group_manager.close_group_task(group_id, x)
|
||||
return "那我先睡觉了..."
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ from nonebot import on_command, on_notice
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent
|
||||
from ._data_source import update_member_info
|
||||
|
||||
|
||||
__zx_plugin_name__ = "更新群组成员列表 [Admin]"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
|
||||
@@ -125,11 +125,13 @@ async def _(
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if msg:
|
||||
if str(event.user_id) in bot.config.superusers:
|
||||
if is_number(arg.extract_plain_text().strip().split()[0]):
|
||||
qq = int(msg[0])
|
||||
msg = msg[1:]
|
||||
msg_splt = msg.split()
|
||||
if is_number(msg_splt[0]):
|
||||
|
||||
qq = int(msg_splt[0])
|
||||
msg = msg_splt[1:]
|
||||
if cmd in [".ban", "/ban"]:
|
||||
time = parse_ban_time(msg)
|
||||
time = parse_ban_time(" ".join(msg))
|
||||
if isinstance(time, str):
|
||||
await ban.finish(time)
|
||||
result = await a_ban(qq, time, str(qq), event, 9)
|
||||
|
||||
@@ -25,7 +25,9 @@ def parse_ban_time(msg: str) -> Union[int, str]:
|
||||
return int(msg[0]) * 60 * 60 + int(msg[1]) * 60
|
||||
|
||||
|
||||
async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_level: int = None) -> str:
|
||||
async def a_ban(
|
||||
qq: int, time: int, user_name: str, event: MessageEvent, ban_level: int = None
|
||||
) -> str:
|
||||
"""
|
||||
ban
|
||||
:param qq: qq
|
||||
@@ -38,7 +40,8 @@ async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_lev
|
||||
ban_level = await LevelUser.get_user_level(event.user_id, event.group_id)
|
||||
if await BanUser.ban(qq, ban_level, time):
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 将 USER {qq} 封禁 时长 {time / 60} 分钟"
|
||||
f"USER {event.user_id} GROUP"
|
||||
f" {event.group_id if isinstance(event, GroupMessageEvent) else ''} 将 USER {qq} 封禁 时长 {time / 60} 分钟"
|
||||
)
|
||||
result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
|
||||
if time != -1:
|
||||
@@ -57,13 +60,3 @@ async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_lev
|
||||
time += " 分钟"
|
||||
result = f"{user_name} 已在黑名单!预计 {time}后解封"
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ from configs.config import Config
|
||||
from models.chat_history import ChatHistory
|
||||
from nonebot import on_message
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageEvent
|
||||
from utils.utils import get_message_text
|
||||
|
||||
from ._rule import rule
|
||||
|
||||
@@ -22,10 +23,10 @@ chat_history = on_message(rule=rule, priority=1, block=False)
|
||||
async def _(event: MessageEvent):
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
await ChatHistory.add_chat_msg(
|
||||
event.user_id, event.group_id, str(event.get_message())
|
||||
event.user_id, event.group_id, str(event.get_message()), get_message_text(event.json())
|
||||
)
|
||||
else:
|
||||
await ChatHistory.add_chat_msg(event.user_id, None, str(event.get_message()))
|
||||
await ChatHistory.add_chat_msg(event.user_id, None, str(event.get_message()), get_message_text(event.json()))
|
||||
|
||||
|
||||
# @test.handle()
|
||||
|
||||
@@ -42,7 +42,7 @@ __plugin_settings__ = {
|
||||
}
|
||||
|
||||
|
||||
msg_handler = on_regex(r"(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?", priority=5, block=True)
|
||||
msg_handler = on_regex(r"^(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?$", priority=5, block=True)
|
||||
|
||||
|
||||
@msg_handler.handle()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from nonebot import on_notice, on_request
|
||||
from configs.path_config import IMAGE_PATH, DATA_PATH
|
||||
from models.level_user import LevelUser
|
||||
from utils.message_builder import image
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from datetime import datetime
|
||||
@@ -96,6 +97,23 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
|
||||
for plugin in data.keys():
|
||||
if not data[plugin]["default_status"]:
|
||||
group_manager.block_plugin(plugin, event.group_id)
|
||||
# 即刻刷新权限
|
||||
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"], event.group_id
|
||||
):
|
||||
await LevelUser.set_level(
|
||||
user_info["user_id"],
|
||||
user_info["group_id"],
|
||||
Config.get_config("admin_bot_manage", "ADMIN_DEFAULT_AUTH"),
|
||||
)
|
||||
if str(user_info["user_id"]) in bot.config.superusers:
|
||||
await LevelUser.set_level(
|
||||
user_info["user_id"], user_info["group_id"], 9
|
||||
)
|
||||
else:
|
||||
join_time = datetime.now()
|
||||
user_info = await bot.get_group_member_info(
|
||||
@@ -128,9 +146,7 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
|
||||
msg = msg.replace("[at]", "")
|
||||
at_flag = True
|
||||
if (DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg").exists():
|
||||
img = image(
|
||||
DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg"
|
||||
)
|
||||
img = image(DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg")
|
||||
if msg or img:
|
||||
msg = msg.strip() + img
|
||||
msg = "\n" + msg if at_flag else msg
|
||||
|
||||
@@ -56,6 +56,9 @@ def _create_help_img(
|
||||
for matcher in _matchers:
|
||||
plugin_name = None
|
||||
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
|
||||
if not _plugin:
|
||||
logger.warning(f"获取 功能:{matcher.plugin_name} 失败...")
|
||||
continue
|
||||
_module = _plugin.module
|
||||
try:
|
||||
plugin_name = _module.__getattribute__("__zx_plugin_name__")
|
||||
@@ -277,7 +280,7 @@ def _create_help_img(
|
||||
0,
|
||||
plain_text=msg,
|
||||
font_size=24,
|
||||
font="yuanshen.ttf",
|
||||
font="HYWenHei-85W.ttf",
|
||||
)
|
||||
B.paste(text, (w, h), True)
|
||||
h += 50
|
||||
@@ -289,7 +292,7 @@ def _create_help_img(
|
||||
0,
|
||||
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
|
||||
font_size=24,
|
||||
font="yuanshen.ttf",
|
||||
font="HYWenHei-85W.ttf",
|
||||
font_color=(231, 74, 57)
|
||||
),
|
||||
(300, 10),
|
||||
|
||||
@@ -106,7 +106,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
raise IgnoredException("功能总开关关闭状态")
|
||||
except AttributeError:
|
||||
pass
|
||||
if module in admin_manager.keys() and matcher.priority not in [1, 9]:
|
||||
if module in admin_manager.keys() and matcher.priority not in [1, 999]:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
# 个人权限
|
||||
if (
|
||||
@@ -146,7 +146,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if event.is_tome():
|
||||
status_message_manager.add(event.user_id)
|
||||
raise IgnoredException("权限不足")
|
||||
if module in plugins2info_dict.keys() and matcher.priority not in [1, 9]:
|
||||
if module in plugins2info_dict.keys() and matcher.priority not in [1, 999]:
|
||||
# 戳一戳单独判断
|
||||
if isinstance(event, GroupMessageEvent) or isinstance(event, PokeNotifyEvent) or matcher.plugin_name in other_limit_plugins:
|
||||
if status_message_manager.get(event.group_id) is None:
|
||||
|
||||
@@ -31,7 +31,7 @@ _flmt = FreqLimiter(300)
|
||||
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if (
|
||||
(isinstance(event, MessageEvent) or isinstance(event, PokeNotifyEvent))
|
||||
and matcher.priority not in [1, 9]
|
||||
and matcher.priority not in [1, 999]
|
||||
) or matcher.plugin_name in other_limit_plugins:
|
||||
try:
|
||||
if (
|
||||
@@ -58,7 +58,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if not static_flmt.check(event.user_id):
|
||||
raise IgnoredException("用户处于黑名单中")
|
||||
static_flmt.start_cd(event.user_id)
|
||||
if matcher.priority != 9:
|
||||
if matcher.priority != 999:
|
||||
try:
|
||||
ban_result = Config.get_config("hook", "BAN_RESULT")
|
||||
if (
|
||||
@@ -79,7 +79,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
if not static_flmt.check(event.user_id):
|
||||
raise IgnoredException("用户处于黑名单中")
|
||||
static_flmt.start_cd(event.user_id)
|
||||
if matcher.priority != 9:
|
||||
if matcher.priority != 999:
|
||||
try:
|
||||
ban_result = Config.get_config("hook", "BAN_RESULT")
|
||||
if ban_result and matcher.plugin_name not in ignore_rst_module:
|
||||
|
||||
@@ -25,7 +25,7 @@ _blmt = BanCheckLimiter(
|
||||
async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State):
|
||||
if not isinstance(event, MessageEvent):
|
||||
return
|
||||
if matcher.type == "message" and matcher.priority not in [1, 9]:
|
||||
if matcher.type == "message" and matcher.priority not in [1, 999]:
|
||||
if state["_prefix"]["raw_command"]:
|
||||
if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'):
|
||||
if await BanUser.ban(
|
||||
|
||||
@@ -20,26 +20,36 @@ def init_none_plugin_count_manager():
|
||||
"""
|
||||
清除已删除插件数据
|
||||
"""
|
||||
modules = [x.module for x in get_matchers()]
|
||||
for module in none_plugin_count_manager.keys():
|
||||
if module not in modules:
|
||||
none_plugin_count_manager.add_count(module)
|
||||
else:
|
||||
none_plugin_count_manager.reset(module)
|
||||
if none_plugin_count_manager.check(module):
|
||||
modules = [x.plugin_name for x in get_matchers()]
|
||||
plugins_manager_list = list(plugins_manager.keys())
|
||||
for module in plugins_manager_list:
|
||||
if module not in modules or none_plugin_count_manager.check(module):
|
||||
try:
|
||||
plugin_name = plugins_manager.get(module)["plugin_name"]
|
||||
except (AttributeError, KeyError):
|
||||
plugin_name = ""
|
||||
try:
|
||||
plugins2settings_manager.delete(module)
|
||||
plugins2count_manager.delete(module)
|
||||
plugins2cd_manager.delete(module)
|
||||
plugins2block_manager.delete(module)
|
||||
plugins_manager.delete(module)
|
||||
resources_manager.remove_resource(module)
|
||||
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}"
|
||||
)
|
||||
if none_plugin_count_manager.check(module):
|
||||
try:
|
||||
plugins2settings_manager.delete(module)
|
||||
plugins2settings_manager.save()
|
||||
plugins2count_manager.delete(module)
|
||||
plugins2count_manager.save()
|
||||
plugins2cd_manager.delete(module)
|
||||
plugins2cd_manager.save()
|
||||
plugins2block_manager.delete(module)
|
||||
plugins2block_manager.save()
|
||||
plugins_manager.delete(module)
|
||||
plugins_manager.save()
|
||||
resources_manager.remove_resource(module)
|
||||
none_plugin_count_manager.delete(module)
|
||||
logger.info(f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据...")
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
f"{module}:{plugin_name} 插件疑似已删除,清除对应插件数据失败...{type(e)}:{e}")
|
||||
else:
|
||||
none_plugin_count_manager.add_count(module)
|
||||
logger.info(
|
||||
f"{module}:{plugin_name} 插件疑似已删除,加载{none_plugin_count_manager._max_count}次失败后将清除对应插件数据,当前次数:{none_plugin_count_manager._data[module]}")
|
||||
else:
|
||||
none_plugin_count_manager.reset(module)
|
||||
none_plugin_count_manager.save()
|
||||
|
||||
@@ -25,7 +25,7 @@ __plugin_configs__ = {
|
||||
|
||||
friend_req = on_request(priority=5, block=True)
|
||||
group_req = on_request(priority=5, block=True)
|
||||
x = on_message(priority=9, block=False)
|
||||
x = on_message(priority=999, block=False)
|
||||
|
||||
exists_data = {"private": {}, "group": {}}
|
||||
|
||||
|
||||
@@ -108,6 +108,10 @@ async def _():
|
||||
"ALTER TABLE genshin ADD bind_group Integer;",
|
||||
"genshin"
|
||||
), # 新增原神群号绑定字段
|
||||
(
|
||||
"ALTER TABLE chat_history ADD plain_text Text;",
|
||||
"chat_history"
|
||||
), # 新增纯文本
|
||||
]
|
||||
for sql in sql_str:
|
||||
try:
|
||||
|
||||
@@ -7,11 +7,13 @@ from typing import Optional, Union
|
||||
from configs.config import Config
|
||||
from nonebot import Driver
|
||||
from nonebot.plugin import require
|
||||
from utils.decorator.shop import shop_register
|
||||
import nonebot
|
||||
import time
|
||||
|
||||
driver: Driver = nonebot.get_driver()
|
||||
|
||||
|
||||
use = require("use")
|
||||
|
||||
|
||||
@@ -20,24 +22,26 @@ async def init_default_shop_goods():
|
||||
"""
|
||||
导入内置的三个商品
|
||||
"""
|
||||
async def sign_card(**kwargs):
|
||||
user_id = kwargs['user_id']
|
||||
group_id = kwargs['group_id']
|
||||
prob = kwargs["prob"]
|
||||
|
||||
@shop_register(
|
||||
name=("好感度双倍加持卡Ⅰ", "好感度双倍加持卡Ⅱ", "好感度双倍加持卡Ⅲ"),
|
||||
price=(30, 150, 250),
|
||||
des=(
|
||||
"下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)",
|
||||
"下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)",
|
||||
"下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)",
|
||||
),
|
||||
load_status=Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"),
|
||||
** {"好感度双倍加持卡Ⅰ_prob": 0.1, "好感度双倍加持卡Ⅱ_prob": 0.2, "好感度双倍加持卡Ⅲ_prob": 0.3},
|
||||
)
|
||||
async def sign_card(user_id: int, group_id: int, prob: float):
|
||||
user = await SignGroupUser.ensure(user_id, group_id)
|
||||
await user.update(add_probability=prob).apply()
|
||||
|
||||
if Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"):
|
||||
await register_goods(
|
||||
"好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)"
|
||||
)
|
||||
use.register_use("好感度双倍加持卡Ⅰ", sign_card, **{"prob": 0.1})
|
||||
await register_goods("好感度双倍加持卡Ⅱ", 150, "下次签到双倍好感度概率 + 20%(平平庸庸)(同类商品将覆盖)")
|
||||
use.register_use("好感度双倍加持卡Ⅱ", sign_card, **{"prob": 0.2})
|
||||
await register_goods(
|
||||
"好感度双倍加持卡Ⅲ", 250, "下次签到双倍好感度概率 + 30%(金币才是真命天子!)(同类商品将覆盖)"
|
||||
)
|
||||
use.register_use("好感度双倍加持卡Ⅲ", sign_card, **{"prob": 0.3})
|
||||
|
||||
@driver.on_bot_connect
|
||||
async def _():
|
||||
await shop_register.load_register()
|
||||
|
||||
|
||||
# 创建商店界面
|
||||
@@ -59,9 +63,7 @@ async def create_shop_help() -> str:
|
||||
A = BuildImage(1000, h, color="#f9f6f2")
|
||||
current_h = 0
|
||||
for goods in _list:
|
||||
bk = BuildImage(
|
||||
700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf"
|
||||
)
|
||||
bk = BuildImage(700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf")
|
||||
goods_image = BuildImage(
|
||||
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf"
|
||||
)
|
||||
@@ -92,10 +94,18 @@ async def create_shop_help() -> str:
|
||||
await bk.apaste(goods_image, alpha=True)
|
||||
# 添加限时图标和时间
|
||||
if goods.goods_limit_time > 0:
|
||||
_limit_time_logo = BuildImage(40, 40, background=f"{IMAGE_PATH}/other/time.png")
|
||||
_limit_time_logo = BuildImage(
|
||||
40, 40, background=f"{IMAGE_PATH}/other/time.png"
|
||||
)
|
||||
await bk.apaste(_limit_time_logo, (600, 0), True)
|
||||
await bk.apaste(BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"), (640, 10), True)
|
||||
limit_time = time.strftime("%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)).split()
|
||||
await bk.apaste(
|
||||
BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"),
|
||||
(640, 10),
|
||||
True,
|
||||
)
|
||||
limit_time = time.strftime(
|
||||
"%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)
|
||||
).split()
|
||||
y_m_d = limit_time[0]
|
||||
_h_m = limit_time[1].split(":")
|
||||
h_m = _h_m[0] + "时 " + _h_m[1] + "分"
|
||||
@@ -125,13 +135,12 @@ async def create_shop_help() -> str:
|
||||
|
||||
|
||||
async def register_goods(
|
||||
name: str,
|
||||
price: int,
|
||||
des: str,
|
||||
discount: Optional[float] = 1,
|
||||
limit_time: Optional[int] = 0,
|
||||
**kwargs,
|
||||
):
|
||||
name: str,
|
||||
price: int,
|
||||
des: str,
|
||||
discount: Optional[float] = 1,
|
||||
limit_time: Optional[int] = 0,
|
||||
) -> bool:
|
||||
"""
|
||||
添加商品
|
||||
例如: 折扣:可选参数↓ 限时时间:可选,单位为小时
|
||||
@@ -142,21 +151,20 @@ async def register_goods(
|
||||
:param des: 商品简介
|
||||
:param discount: 商品折扣
|
||||
:param limit_time: 商品限时销售时间,单位为小时
|
||||
:param kwargs: kwargs
|
||||
:return: 是否添加成功
|
||||
"""
|
||||
if kwargs:
|
||||
name = kwargs.get("name")
|
||||
price = kwargs.get("price")
|
||||
des = kwargs.get("des")
|
||||
discount = kwargs.get("discount")
|
||||
limit_time = kwargs.get("time_limit")
|
||||
limit_time = float(limit_time) if limit_time else limit_time
|
||||
discount = discount if discount is None else 1
|
||||
limit_time = int(time.time() + limit_time * 60 * 60) if limit_time is not None and limit_time != 0 else 0
|
||||
return await GoodsInfo.add_goods(
|
||||
name, int(price), des, float(discount), limit_time
|
||||
)
|
||||
if not await GoodsInfo.get_goods_info(name):
|
||||
limit_time = float(limit_time) if limit_time else limit_time
|
||||
discount = discount if discount is not None else 1
|
||||
limit_time = (
|
||||
int(time.time() + limit_time * 60 * 60)
|
||||
if limit_time is not None and limit_time != 0
|
||||
else 0
|
||||
)
|
||||
return await GoodsInfo.add_goods(
|
||||
name, int(price), des, float(discount), limit_time
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
# 删除商品
|
||||
@@ -230,7 +238,11 @@ async def update_goods(**kwargs) -> "str, str, int":
|
||||
int(price),
|
||||
des,
|
||||
float(discount),
|
||||
int(time.time() + limit_time * 60 * 60 if limit_time != 0 and new_time else 0),
|
||||
int(
|
||||
time.time() + limit_time * 60 * 60
|
||||
if limit_time != 0 and new_time
|
||||
else 0
|
||||
),
|
||||
),
|
||||
name,
|
||||
tmp[:-1],
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from pydantic import create_model
|
||||
from utils.models import ShopParam
|
||||
from typing import Optional, Union
|
||||
from types import MappingProxyType
|
||||
import inspect
|
||||
import asyncio
|
||||
|
||||
|
||||
@@ -30,24 +34,52 @@ class GoodsUseFuncManager:
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
if self.exists(goods_name):
|
||||
return self._data[goods_name]["kwargs"]["_max_num_limit"]
|
||||
return self._data[goods_name]["kwargs"]["max_num_limit"]
|
||||
return 1
|
||||
|
||||
async def use(self, **kwargs) -> Optional[Union[str, MessageSegment]]:
|
||||
async def use(
|
||||
self, param: ShopParam, **kwargs
|
||||
) -> Optional[Union[str, MessageSegment]]:
|
||||
"""
|
||||
使用道具
|
||||
:param param: BaseModel
|
||||
:param kwargs: kwargs
|
||||
"""
|
||||
goods_name = kwargs.get("goods_name")
|
||||
def parse_args(args_: MappingProxyType):
|
||||
param_list_ = []
|
||||
_bot = param.bot
|
||||
param.bot = None
|
||||
param_json = param.dict()
|
||||
param_json["bot"] = _bot
|
||||
for par in args_.keys():
|
||||
if par in ["shop_param"]:
|
||||
param_list_.append(param)
|
||||
elif par not in ["args", "kwargs"]:
|
||||
param_list_.append(param_json.get(par))
|
||||
if kwargs.get(par) is not None:
|
||||
del kwargs[par]
|
||||
return param_list_
|
||||
goods_name = param.goods_name
|
||||
if self.exists(goods_name):
|
||||
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
|
||||
return await self._data[goods_name]["func"](
|
||||
**kwargs,
|
||||
)
|
||||
args = inspect.signature(self._data[goods_name]["func"]).parameters
|
||||
if args and list(args.keys())[0] != "kwargs":
|
||||
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
|
||||
return await self._data[goods_name]["func"](
|
||||
*parse_args(args)
|
||||
)
|
||||
else:
|
||||
return self._data[goods_name]["func"](
|
||||
*parse_args(args)
|
||||
)
|
||||
else:
|
||||
return self._data[goods_name]["func"](
|
||||
**kwargs,
|
||||
)
|
||||
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
|
||||
return await self._data[goods_name]["func"](
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
return self._data[goods_name]["func"](
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def check_send_success_message(self, goods_name: str) -> bool:
|
||||
"""
|
||||
@@ -67,6 +99,18 @@ class GoodsUseFuncManager:
|
||||
return self._data[goods_name]["kwargs"]
|
||||
return {}
|
||||
|
||||
def init_model(self, goods_name: str, bot: Bot, event: GroupMessageEvent, num: int):
|
||||
return self._data[goods_name]["model"](
|
||||
**{
|
||||
"goods_name": goods_name,
|
||||
"bot": bot,
|
||||
"event": event,
|
||||
"user_id": event.user_id,
|
||||
"group_id": event.group_id,
|
||||
"num": num,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
func_manager = GoodsUseFuncManager()
|
||||
|
||||
@@ -83,22 +127,23 @@ async def effect(
|
||||
:return: 使用是否成功
|
||||
"""
|
||||
# 优先使用注册的商品插件
|
||||
try:
|
||||
if func_manager.exists(goods_name):
|
||||
_kwargs = func_manager.get_kwargs(goods_name)
|
||||
return await func_manager.use(
|
||||
**{
|
||||
**_kwargs,
|
||||
"_bot": bot,
|
||||
"event": event,
|
||||
"group_id": event.group_id,
|
||||
"user_id": event.user_id,
|
||||
"num": num,
|
||||
"goods_name": goods_name,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
|
||||
# try:
|
||||
if func_manager.exists(goods_name):
|
||||
_kwargs = func_manager.get_kwargs(goods_name)
|
||||
return await func_manager.use(
|
||||
func_manager.init_model(goods_name, bot, event, num),
|
||||
**{
|
||||
**_kwargs,
|
||||
"_bot": bot,
|
||||
"event": event,
|
||||
"group_id": event.group_id,
|
||||
"user_id": event.user_id,
|
||||
"num": num,
|
||||
"goods_name": goods_name,
|
||||
},
|
||||
)
|
||||
# except Exception as e:
|
||||
# logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
|
||||
return None
|
||||
|
||||
|
||||
@@ -112,10 +157,14 @@ def register_use(goods_name: str, func, **kwargs):
|
||||
if func_manager.exists(goods_name):
|
||||
raise ValueError("该商品使用函数已被注册!")
|
||||
# 发送使用成功信息
|
||||
if kwargs.get("send_success_msg") is None:
|
||||
kwargs["send_success_msg"] = True
|
||||
kwargs["_max_num_limit"] = (
|
||||
kwargs.get("_max_num_limit") if kwargs.get("_max_num_limit") else 1
|
||||
kwargs["send_success_msg"] = kwargs.get("send_success_msg", True)
|
||||
kwargs["max_num_limit"] = kwargs.get("max_num_limit", 1)
|
||||
func_manager.register_use(
|
||||
goods_name,
|
||||
**{
|
||||
"func": func,
|
||||
"model": create_model(f"{goods_name}_model", __base__=ShopParam, **kwargs),
|
||||
"kwargs": kwargs,
|
||||
},
|
||||
)
|
||||
func_manager.register_use(goods_name, **{"func": func, "kwargs": kwargs})
|
||||
logger.info(f"register_use 成功注册商品:{goods_name} 的使用函数")
|
||||
|
||||
@@ -92,7 +92,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
level = 0
|
||||
if not msg:
|
||||
await add_group_level.finish("用法:修改群权限 [group] [level]")
|
||||
msg = msg.split(" ")
|
||||
msg = msg.split()
|
||||
if len(msg) < 2:
|
||||
await add_group_level.finish("参数不完全..[group] [level]")
|
||||
if is_number(msg[0]) and is_number(msg[1]):
|
||||
|
||||
@@ -45,6 +45,7 @@ async def _():
|
||||
group_info["group_name"],
|
||||
group_info["max_member_count"],
|
||||
group_info["member_count"],
|
||||
1
|
||||
):
|
||||
num += 1
|
||||
logger.info(f"自动更新群组 {g} 信息成功")
|
||||
|
||||
+1450
-155
File diff suppressed because it is too large
Load Diff
@@ -11,12 +11,13 @@ class ChatHistory(db.Model):
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False)
|
||||
group_id = db.Column(db.BigInteger())
|
||||
text = db.Column(db.Text())
|
||||
plain_text = db.Column(db.Text())
|
||||
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
|
||||
|
||||
@classmethod
|
||||
async def add_chat_msg(cls, user_qq: int, group_id: Optional[int], text: str):
|
||||
async def add_chat_msg(cls, user_qq: int, group_id: Optional[int], text: str, plain_text: str):
|
||||
await cls.create(
|
||||
user_qq=user_qq, group_id=group_id, text=text, create_time=datetime.now()
|
||||
user_qq=user_qq, group_id=group_id, text=text, plain_text=plain_text, create_time=datetime.now()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -97,7 +98,6 @@ class ChatHistory(db.Model):
|
||||
if date_scope:
|
||||
sql += f"AND create_time BETWEEN '{date_scope[0]}' AND '{date_scope[1]}' "
|
||||
sql += f"GROUP BY user_qq ORDER BY sum {order if order and order.upper() != 'DES' else ''} LIMIT {limit}"
|
||||
print(sql)
|
||||
return await db.all(db.text(sql))
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -114,8 +114,7 @@ class GoodsInfo(db.Model):
|
||||
参数:
|
||||
:param goods_name: 商品名称
|
||||
"""
|
||||
query = await cls.query.where(cls.goods_name == goods_name).gino.first()
|
||||
return query
|
||||
return await cls.query.where(cls.goods_name == goods_name).gino.first()
|
||||
|
||||
@classmethod
|
||||
async def get_all_goods(cls) -> List["GoodsInfo"]:
|
||||
|
||||
+15
-10
@@ -1,3 +1,5 @@
|
||||
from asyncpg import UniqueViolationError
|
||||
|
||||
from services.db_context import db
|
||||
|
||||
|
||||
@@ -44,16 +46,19 @@ class LevelUser(db.Model):
|
||||
query = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if user is None:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
user_level=level,
|
||||
group_flag=group_flag,
|
||||
)
|
||||
return True
|
||||
else:
|
||||
await user.update(user_level=level, group_flag=group_flag).apply()
|
||||
try:
|
||||
if not user:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
user_level=level,
|
||||
group_flag=group_flag,
|
||||
)
|
||||
return True
|
||||
else:
|
||||
await user.update(user_level=level, group_flag=group_flag).apply()
|
||||
return False
|
||||
except UniqueViolationError:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -37,7 +37,7 @@ Config.add_plugin_config(
|
||||
"alapi", "ALAPI_TOKEN", None, help_="在 https://admin.alapi.cn/user/login 登录后获取token"
|
||||
)
|
||||
|
||||
ai = on_message(rule=to_me(), priority=8)
|
||||
ai = on_message(rule=to_me(), priority=998)
|
||||
|
||||
|
||||
@ai.handle()
|
||||
|
||||
+33
-29
@@ -126,35 +126,39 @@ async def xie_ai(text: str) -> str:
|
||||
"""
|
||||
res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}")
|
||||
content = ""
|
||||
data = json.loads(res.text)
|
||||
if data["result"] == 0:
|
||||
content = data["content"]
|
||||
if "菲菲" in content:
|
||||
content = content.replace("菲菲", NICKNAME)
|
||||
if "艳儿" in content:
|
||||
content = content.replace("艳儿", NICKNAME)
|
||||
if "公众号" in content:
|
||||
content = ""
|
||||
if "{br}" in content:
|
||||
content = content.replace("{br}", "\n")
|
||||
if "提示" in content:
|
||||
content = content[: content.find("提示")]
|
||||
if "淘宝" in content or "taobao.com" in content:
|
||||
return ""
|
||||
while True:
|
||||
r = re.search("{face:(.*)}", content)
|
||||
if r:
|
||||
id_ = r.group(1)
|
||||
content = content.replace(
|
||||
"{" + f"face:{id_}" + "}", str(face(int(id_)))
|
||||
)
|
||||
else:
|
||||
break
|
||||
return (
|
||||
content
|
||||
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
|
||||
else await check_text(content)
|
||||
)
|
||||
try:
|
||||
data = json.loads(res.text)
|
||||
if data["result"] == 0:
|
||||
content = data["content"]
|
||||
if "菲菲" in content:
|
||||
content = content.replace("菲菲", NICKNAME)
|
||||
if "艳儿" in content:
|
||||
content = content.replace("艳儿", NICKNAME)
|
||||
if "公众号" in content:
|
||||
content = ""
|
||||
if "{br}" in content:
|
||||
content = content.replace("{br}", "\n")
|
||||
if "提示" in content:
|
||||
content = content[: content.find("提示")]
|
||||
if "淘宝" in content or "taobao.com" in content:
|
||||
return ""
|
||||
while True:
|
||||
r = re.search("{face:(.*)}", content)
|
||||
if r:
|
||||
id_ = r.group(1)
|
||||
content = content.replace(
|
||||
"{" + f"face:{id_}" + "}", str(face(int(id_)))
|
||||
)
|
||||
else:
|
||||
break
|
||||
return (
|
||||
content
|
||||
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
|
||||
else await check_text(content)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Ai xie_ai 发生错误 {type(e)}:{e}")
|
||||
return ""
|
||||
|
||||
|
||||
def hello() -> str:
|
||||
|
||||
@@ -23,6 +23,8 @@ async def get_data(url: str, params: Optional[dict] = None) -> Tuple[Union[dict,
|
||||
return "没有搜索到...", 997
|
||||
return data, 200
|
||||
else:
|
||||
if data["code"] == 101:
|
||||
return "缺失ALAPI TOKEN,请在配置文件中填写!", 999
|
||||
return f'发生了错误...code:{data["code"]}', 999
|
||||
except TimeoutError:
|
||||
return "超时了....", 998
|
||||
|
||||
@@ -60,8 +60,8 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
img = await AsyncPlaywright.screenshot(
|
||||
url,
|
||||
f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
|
||||
"#pl_feedlist_index",
|
||||
wait_time=5
|
||||
"#pl_feed_main",
|
||||
wait_time=12
|
||||
)
|
||||
if img:
|
||||
await wbtop.send(img)
|
||||
|
||||
@@ -38,7 +38,7 @@ usage:
|
||||
__plugin_des__ = "非常便利的B站订阅通知"
|
||||
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_author__ = "HibiKier & NumberSir"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException
|
||||
from bilireq.exceptions import ResponseCodeError
|
||||
from utils.manager import resources_manager
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from .model import BilibiliSub
|
||||
from bilibili_api.live import LiveRoom
|
||||
from bilibili_api import bangumi
|
||||
from bilireq.live import get_room_info_by_id
|
||||
from .utils import get_meta
|
||||
from utils.message_builder import image
|
||||
from bilibili_api.user import User
|
||||
from bilibili_api import user
|
||||
from bilireq.user import get_user_info
|
||||
from bilireq import dynamic
|
||||
from .utils import get_videos
|
||||
from typing import Optional
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from datetime import datetime
|
||||
@@ -36,9 +37,9 @@ async def add_live_sub(live_id: int, sub_user: str) -> str:
|
||||
try:
|
||||
async with db.transaction():
|
||||
try:
|
||||
live = LiveRoom(live_id)
|
||||
live_info = (await live.get_room_info())["room_info"]
|
||||
except ResponseCodeException:
|
||||
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
|
||||
live_info = await get_room_info_by_id(live_id)
|
||||
except ResponseCodeError:
|
||||
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
|
||||
uid = live_info["uid"]
|
||||
room_id = live_info["room_id"]
|
||||
@@ -78,16 +79,18 @@ async def add_up_sub(uid: int, sub_user: str) -> str:
|
||||
try:
|
||||
async with db.transaction():
|
||||
try:
|
||||
u = user.User(uid)
|
||||
user_info = await u.get_user_info()
|
||||
except ResponseCodeException:
|
||||
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
|
||||
user_info = await get_user_info(uid)
|
||||
except ResponseCodeError:
|
||||
return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
|
||||
uname = user_info["name"]
|
||||
dynamic_info = await u.get_dynamics(0)
|
||||
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法"""
|
||||
dynamic_info = await dynamic.get_user_dynamics(uid)
|
||||
dynamic_upload_time = 0
|
||||
if dynamic_info.get("cards"):
|
||||
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
|
||||
video_info = await u.get_videos()
|
||||
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
|
||||
video_info = await get_videos(uid)
|
||||
latest_video_created = 0
|
||||
if video_info["list"].get("vlist"):
|
||||
latest_video_created = video_info["list"]["vlist"][0]["created"]
|
||||
@@ -117,8 +120,9 @@ async def add_season_sub(media_id: int, sub_user: str) -> str:
|
||||
try:
|
||||
async with db.transaction():
|
||||
try:
|
||||
season_info = await bangumi.get_meta(media_id)
|
||||
except ResponseCodeException:
|
||||
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法"""
|
||||
season_info = await get_meta(media_id)
|
||||
except ResponseCodeError:
|
||||
return f"未找到media_id:{media_id} 的信息,请检查Id是否正确"
|
||||
season_id = season_info["media"]["season_id"]
|
||||
season_current_episode = season_info["media"]["new_ep"]["index"]
|
||||
@@ -200,7 +204,7 @@ async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
|
||||
return await _get_up_status(id_)
|
||||
elif sub_type == "season":
|
||||
return await _get_season_status(id_)
|
||||
except ResponseCodeException:
|
||||
except ResponseCodeError:
|
||||
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
|
||||
# except Exception as e:
|
||||
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
|
||||
@@ -212,12 +216,12 @@ async def _get_live_status(id_: int) -> Optional[str]:
|
||||
获取直播订阅状态
|
||||
:param id_: 直播间 id
|
||||
"""
|
||||
live = LiveRoom(id_)
|
||||
live_info = (await live.get_room_info())["room_info"]
|
||||
"""bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
|
||||
live_info = await get_room_info_by_id(id_)
|
||||
title = live_info["title"]
|
||||
room_id = live_info["room_id"]
|
||||
live_status = live_info["live_status"]
|
||||
cover = live_info["cover"]
|
||||
cover = live_info["user_cover"]
|
||||
sub = await BilibiliSub.get_sub(id_)
|
||||
if sub.live_status != live_status:
|
||||
await BilibiliSub.update_sub_info(id_, live_status=live_status)
|
||||
@@ -238,15 +242,16 @@ async def _get_up_status(id_: int) -> Optional[str]:
|
||||
:return:
|
||||
"""
|
||||
_user = await BilibiliSub.get_sub(id_)
|
||||
u = user.User(_user.uid)
|
||||
user_info = await u.get_user_info()
|
||||
"""bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
|
||||
user_info = await get_user_info(_user.uid)
|
||||
uname = user_info["name"]
|
||||
video_info = await u.get_videos()
|
||||
"""bilibili_api.user库中User类的get_videos改为bilireq.user库的get_videos方法"""
|
||||
video_info = await get_videos(id_)
|
||||
latest_video_created = 0
|
||||
video = None
|
||||
if _user.uname != uname:
|
||||
await BilibiliSub.update_sub_info(id_, uname=uname)
|
||||
dynamic_img, dynamic_upload_time = await get_user_dynamic(u, _user)
|
||||
dynamic_img, dynamic_upload_time = await get_user_dynamic(id_, _user)
|
||||
if video_info["list"].get("vlist"):
|
||||
video = video_info["list"]["vlist"][0]
|
||||
latest_video_created = video["created"]
|
||||
@@ -280,7 +285,8 @@ async def _get_season_status(id_) -> Optional[str]:
|
||||
获取 番剧 更新状态
|
||||
:param id_: 番剧 id
|
||||
"""
|
||||
season_info = await bangumi.get_meta(id_)
|
||||
"""bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法"""
|
||||
season_info = await get_meta(id_)
|
||||
title = season_info["media"]["title"]
|
||||
_idx = (await BilibiliSub.get_sub(id_)).season_current_episode
|
||||
new_ep = season_info["media"]["new_ep"]["index"]
|
||||
@@ -297,15 +303,16 @@ async def _get_season_status(id_) -> Optional[str]:
|
||||
|
||||
|
||||
async def get_user_dynamic(
|
||||
u: User, local_user: BilibiliSub
|
||||
uid: int, local_user: BilibiliSub
|
||||
) -> "Optional[MessageSegment], int":
|
||||
"""
|
||||
获取用户动态
|
||||
:param u: 用户类
|
||||
:param uid: 用户uid
|
||||
:param local_user: 数据库存储的用户数据
|
||||
:return: 最新动态截图与时间
|
||||
"""
|
||||
dynamic_info = await u.get_dynamics(0)
|
||||
"""bilibili_api.user库中User类的get_dynamics改为bilireq.dynamic库的get_user_dynamics方法"""
|
||||
dynamic_info = await dynamic.get_user_dynamics(uid)
|
||||
browser = await get_browser()
|
||||
if dynamic_info.get("cards") and browser:
|
||||
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
|
||||
@@ -317,21 +324,21 @@ async def get_user_dynamic(
|
||||
wait_until="networkidle",
|
||||
timeout=10000,
|
||||
)
|
||||
await page.set_viewport_size({"width": 2560, "height": 1080})
|
||||
await page.set_viewport_size({"width": 2560, "height": 1080, "timeout": 10000*20}) # timeout: 200s
|
||||
# 删除置顶
|
||||
await page.evaluate(
|
||||
"""
|
||||
xs = document.getElementsByClassName('first-card-with-title');
|
||||
xs = document.getElementsByClassName('bili-dyn-item__tag');
|
||||
for (x of xs) {
|
||||
x.remove();
|
||||
x.parentNode.parentNode.remove();
|
||||
}
|
||||
"""
|
||||
)
|
||||
card = await page.query_selector(".card")
|
||||
card = page.locator(".bili-dyn-list__item").first
|
||||
await card.wait_for()
|
||||
# 截图并保存
|
||||
await card.screenshot(
|
||||
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
|
||||
timeout=100000,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}")
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from utils.image_utils import BuildImage
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from bilibili_api import user
|
||||
# from bilibili_api import user
|
||||
from bilireq.user import get_user_info
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
@@ -28,8 +29,8 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
|
||||
:param des: 直播简介
|
||||
:return:
|
||||
"""
|
||||
u = user.User(uid)
|
||||
user_info = await u.get_user_info()
|
||||
|
||||
user_info = await get_user_info(uid)
|
||||
name = user_info["name"]
|
||||
sex = user_info["sex"]
|
||||
face = user_info["face"]
|
||||
@@ -37,7 +38,6 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
|
||||
ava = BuildImage(100, 100, background=BytesIO(await get_pic(face)))
|
||||
ava.circle()
|
||||
cover = BuildImage(470, 265, background=BytesIO(await get_pic(cover)))
|
||||
print()
|
||||
|
||||
|
||||
def _create_live_des_image(
|
||||
@@ -68,3 +68,47 @@ def _create_live_des_image(
|
||||
border_img = BuildImage(1772, 2657, background=border)
|
||||
bk = BuildImage(1772, 2657, font_size=30)
|
||||
bk.paste(cover, (0, 100), center_type="by_width")
|
||||
|
||||
|
||||
async def get_meta(media_id: int, auth=None, reqtype="both", **kwargs):
|
||||
"""
|
||||
根据番剧 ID 获取番剧元数据信息,
|
||||
作为bilibili_api和bilireq的替代品。
|
||||
如果bilireq.bangumi更新了,可以转为调用bilireq.bangumi的get_meta方法,两者完全一致。
|
||||
"""
|
||||
from bilireq.utils import get
|
||||
|
||||
BASE_URL = "https://api.bilibili.com"
|
||||
url = f"{BASE_URL}/pgc/review/user"
|
||||
params = {"media_id": media_id}
|
||||
raw_json = await get(url, raw=True, params=params, auth=auth, reqtype=reqtype, **kwargs)
|
||||
return raw_json['result']
|
||||
|
||||
|
||||
async def get_videos(
|
||||
uid: int, tid: int = 0, pn: int = 1, keyword: str = "", order: str = "pubdate",
|
||||
*, auth=None, reqtype="both", **kwargs
|
||||
):
|
||||
"""
|
||||
获取用户投该视频信息
|
||||
作为bilibili_api和bilireq的替代品。
|
||||
如果bilireq.user更新了,可以转为调用bilireq.user的get_videos方法,两者完全一致。
|
||||
|
||||
:param uid: 用户 UID
|
||||
:param tid: 分区 ID
|
||||
:param pn: 页码
|
||||
:param keyword: 搜索关键词
|
||||
:param order: 排序方式,可以为 “pubdate(上传日期从新到旧), stow(收藏从多到少), click(播放量从多到少)”
|
||||
"""
|
||||
from bilireq.utils import get
|
||||
BASE_URL = "https://api.bilibili.com"
|
||||
url = f"{BASE_URL}/x/space/arc/search"
|
||||
params = {
|
||||
"mid": uid,
|
||||
"ps": 30,
|
||||
"tid": tid,
|
||||
"pn": pn,
|
||||
"keyword": keyword,
|
||||
"order": order
|
||||
}
|
||||
return await get(url, params=params, auth=auth, reqtype=reqtype, **kwargs)
|
||||
@@ -0,0 +1,248 @@
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Event,
|
||||
MessageEvent,
|
||||
GroupMessageEvent,
|
||||
Message,
|
||||
Bot,
|
||||
)
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.message import run_preprocessor
|
||||
from utils.image_utils import BuildImage
|
||||
from utils.utils import get_message_text, is_number
|
||||
from nonebot.params import RegexGroup, CommandArg
|
||||
from .utils import black_word_manager
|
||||
from nonebot import on_command, on_message, on_regex
|
||||
from configs.config import Config, NICKNAME
|
||||
from nonebot.permission import SUPERUSER
|
||||
from .data_source import show_black_text_image, set_user_punish
|
||||
from services.log import logger
|
||||
from models.ban_user import BanUser
|
||||
from datetime import datetime
|
||||
from utils.message_builder import image
|
||||
from .model import BlackWord
|
||||
from typing import Tuple, Any
|
||||
|
||||
|
||||
__zx_plugin_name__ = "敏感词检测"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
注意你的发言!
|
||||
指令:
|
||||
惩罚机制
|
||||
""".strip()
|
||||
__plugin_superuser_usage__ = """
|
||||
usage:
|
||||
查看和设置惩罚
|
||||
Regex:^记录名单(u:\d*)?(g:\d*)?(d[=><]\d*-\d{1,2}-\d{1,2})?$
|
||||
设置惩罚id需要通过 '记录名单u:xxxxxxxx' 获取
|
||||
指令:
|
||||
记录名单
|
||||
设置惩罚 [user_id] [id] [punish_level]
|
||||
示例:记录名单
|
||||
示例:记录名单u:12345678
|
||||
示例:设置惩罚 12345678 1 4
|
||||
""".strip()
|
||||
__plugin_des__ = "请注意你的发言!!"
|
||||
__plugin_type__ = ("其他",)
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_cmd__ = ["惩罚机制", "记录名单 [_superuser]", "设置惩罚 [_superuser]"]
|
||||
__plugin_settings__ = {
|
||||
"cmd": ["敏感词检测"],
|
||||
}
|
||||
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word", "CYCLE_DAYS", 30, name="敏感词检测与惩罚", help_="黑名单词汇记录周期", default_value=30
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"TOLERATE_COUNT",
|
||||
[5, 1, 1, 1, 1],
|
||||
help_="各个级别惩罚的容忍次数,依次为:1, 2, 3, 4, 5",
|
||||
default_value=[5, 1, 1, 1, 1],
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word", "AUTO_PUNISH", True, help_="是否启动自动惩罚机制", default_value=True
|
||||
)
|
||||
|
||||
# Config.add_plugin_config(
|
||||
# "black_word", "IGNORE_GROUP", [], help_="退出群聊惩罚中忽略的群聊,即不会退出的群聊", default_value=[]
|
||||
# )
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"BAN_4_DURATION",
|
||||
360,
|
||||
help_="Union[int, List[int, int]]Ban时长(分钟),四级惩罚,可以为指定数字或指定列表区间(随机),例如 [30, 360]",
|
||||
default_value=360,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"BAN_3_DURATION",
|
||||
7,
|
||||
help_="Union[int, List[int, int]]Ban时长(天),三级惩罚,可以为指定数字或指定列表区间(随机),例如 [7, 30]",
|
||||
default_value=360,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"WARNING_RESULT",
|
||||
f"请注意对{NICKNAME}的发言内容",
|
||||
help_="口头警告内容",
|
||||
default_value=f"请注意对{NICKNAME}的发言内容",
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"AUTO_ADD_PUNISH_LEVEL",
|
||||
True,
|
||||
help_="自动提级机制,当周期内处罚次数大于某一特定值就提升惩罚等级",
|
||||
default_value=True,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"ADD_PUNISH_LEVEL_TO_COUNT",
|
||||
3,
|
||||
help_="在CYCLE_DAYS周期内触发指定惩罚次数后提升惩罚等级",
|
||||
default_value=3,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"ALAPI_CHECK_FLAG",
|
||||
False,
|
||||
help_="当未检测到已收录的敏感词时,开启ALAPI文本检测并将疑似文本发送给超级用户",
|
||||
default_value=False,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"CONTAIN_BLACK_STOP_PROPAGATION",
|
||||
True,
|
||||
help_="当文本包含任意敏感词时,停止向下级插件传递,即不触发ai",
|
||||
default_value=True,
|
||||
)
|
||||
|
||||
message_matcher = on_message(priority=1, block=False)
|
||||
|
||||
set_punish = on_command("设置惩罚", priority=1, permission=SUPERUSER, block=True)
|
||||
|
||||
show_black = on_regex(
|
||||
r"^记录名单(u:\d*)?(g:\d*)?(d[=><]\d*-\d{1,2}-\d{1,2})?$",
|
||||
priority=1,
|
||||
permission=SUPERUSER,
|
||||
block=True,
|
||||
)
|
||||
|
||||
show_punish = on_command("惩罚机制", aliases={"敏感词检测"}, priority=1, block=True)
|
||||
|
||||
|
||||
# 黑名单词汇检测
|
||||
@run_preprocessor
|
||||
async def _(
|
||||
bot: Bot,
|
||||
matcher: Matcher,
|
||||
event: Event,
|
||||
):
|
||||
if (
|
||||
isinstance(event, MessageEvent)
|
||||
and event.is_tome()
|
||||
and matcher.plugin_name == "black_word"
|
||||
and not await BanUser.is_ban(event.user_id)
|
||||
and str(event.user_id) not in bot.config.superusers
|
||||
):
|
||||
user_id = event.user_id
|
||||
group_id = event.group_id if isinstance(event, GroupMessageEvent) else None
|
||||
msg = get_message_text(event.json())
|
||||
if await black_word_manager.check(user_id, group_id, msg) and Config.get_config(
|
||||
"black_word", "CONTAIN_BLACK_STOP_PROPAGATION"
|
||||
):
|
||||
matcher.stop_propagation()
|
||||
|
||||
|
||||
@show_black.handle()
|
||||
async def _(bot: Bot, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
user_id, group_id, date = reg_group
|
||||
date_type = "="
|
||||
if date:
|
||||
date_type = date[1]
|
||||
date = date[2:]
|
||||
try:
|
||||
date = datetime.strptime(date, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
await show_black.finish("日期格式错误,需要:年-月-日")
|
||||
pic = await show_black_text_image(
|
||||
bot,
|
||||
int(user_id.split(":")[1]) if user_id else None,
|
||||
int(group_id.split(":")[1]) if group_id else None,
|
||||
date,
|
||||
date_type,
|
||||
)
|
||||
await show_black.send(image(b64=pic.pic2bs4()))
|
||||
|
||||
|
||||
@show_punish.handle()
|
||||
async def _():
|
||||
text = f"""
|
||||
** 惩罚机制 **
|
||||
|
||||
惩罚前包含容忍机制,在指定周期内会容忍偶尔少次数的敏感词只会进行警告提醒
|
||||
|
||||
多次触发同级惩罚会使惩罚等级提高,即惩罚自动提级机制
|
||||
|
||||
目前公开的惩罚等级:
|
||||
|
||||
1级:永久ban
|
||||
|
||||
2级:删除好友
|
||||
|
||||
3级:ban指定/随机天数
|
||||
|
||||
4级:ban指定/随机时长
|
||||
|
||||
5级:警告
|
||||
|
||||
备注:
|
||||
|
||||
该功能为测试阶段,如果你有被误封情况,请联系管理员,会从数据库中提取出你的数据进行审核后判断
|
||||
|
||||
目前该功能暂不完善,部分情况会由管理员鉴定,请注意对真寻的发言
|
||||
|
||||
关于敏感词:
|
||||
|
||||
记住不要骂{NICKNAME}就对了!
|
||||
""".strip()
|
||||
max_width = 0
|
||||
for m in text.split("\n"):
|
||||
max_width = len(m) * 20 if len(m) * 20 > max_width else max_width
|
||||
max_height = len(text.split("\n")) * 24
|
||||
A = BuildImage(
|
||||
max_width, max_height, font="CJGaoDeGuo.otf", font_size=24, color="#E3DBD1"
|
||||
)
|
||||
A.text((10, 10), text)
|
||||
await show_punish.send(image(b64=A.pic2bs4()))
|
||||
|
||||
|
||||
@set_punish.handle()
|
||||
async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip().split()
|
||||
if (
|
||||
len(msg) < 3
|
||||
or not is_number(msg[0])
|
||||
or not is_number(msg[1])
|
||||
or not is_number(msg[2])
|
||||
):
|
||||
await set_punish.finish("参数错误,请查看帮助...", at_sender=True)
|
||||
uid = int(msg[0])
|
||||
id_ = int(msg[1])
|
||||
punish_level = int(msg[2])
|
||||
rst = await set_user_punish(uid, id_, punish_level)
|
||||
await set_punish.send(rst)
|
||||
logger.info(
|
||||
f"USER {event.user_id} 设置惩罚 uid:{uid} id_:{id_} punish_level:{punish_level} --> {rst}"
|
||||
)
|
||||
@@ -0,0 +1,118 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from utils.image_utils import BuildImage, text2image
|
||||
from services.log import logger
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
from .model import BlackWord
|
||||
from .utils import _get_punish, Config
|
||||
|
||||
|
||||
async def show_black_text_image(
|
||||
bot: Bot,
|
||||
user: Optional[int],
|
||||
group_id: Optional[int],
|
||||
date: Optional[datetime],
|
||||
data_type: str = "=",
|
||||
) -> BuildImage:
|
||||
"""
|
||||
展示记录名单
|
||||
:param bot: bot
|
||||
:param user: 用户qq
|
||||
:param group_id: 群聊
|
||||
:param date: 日期
|
||||
:param data_type: 日期搜索类型
|
||||
:return:
|
||||
"""
|
||||
data = await BlackWord.get_black_data(user, group_id, date, data_type)
|
||||
A = BuildImage(0, 0, color="#f9f6f2", font_size=20)
|
||||
image_list = []
|
||||
friend_str = await bot.get_friend_list()
|
||||
id_str = ""
|
||||
uname_str = ""
|
||||
uid_str = ""
|
||||
gid_str = ""
|
||||
plant_text_str = ""
|
||||
black_word_str = ""
|
||||
punish_str = ""
|
||||
punish_level_str = ""
|
||||
create_time_str = ""
|
||||
for i, x in enumerate(data):
|
||||
try:
|
||||
if x.group_id:
|
||||
user_name = (
|
||||
await bot.get_group_member_info(
|
||||
group_id=x.group_id, user_id=x.user_qq
|
||||
)
|
||||
)["card"]
|
||||
else:
|
||||
user_name = [
|
||||
u["nickname"] for u in friend_str if u["user_id"] == x.user_qq
|
||||
][0]
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"show_black_text_image 获取 USER {x.user_qq} user_name 失败 {type(e)}:{e}"
|
||||
)
|
||||
user_name = x.user_qq
|
||||
id_str += f"{i}\n"
|
||||
uname_str += f"{user_name}\n"
|
||||
uid_str += f"{x.user_qq}\n"
|
||||
gid_str += f"{x.group_id}\n"
|
||||
plant_text = " ".join(x.plant_text.split("\n"))
|
||||
if A.getsize(plant_text)[0] > 200:
|
||||
plant_text = plant_text[:20] + "..."
|
||||
plant_text_str += f"{plant_text}\n"
|
||||
black_word_str += f"{x.black_word}\n"
|
||||
punish_str += f"{x.punish}\n"
|
||||
punish_level_str += f"{x.punish_level}\n"
|
||||
create_time_str += f"{x.create_time.replace(microsecond=0)}\n"
|
||||
_tmp_img = BuildImage(0, 0, font_size=35, font="CJGaoDeGuo.otf")
|
||||
for s, type_ in [
|
||||
(id_str, "Id"),
|
||||
(uname_str, "昵称"),
|
||||
(uid_str, "UID"),
|
||||
(gid_str, "GID"),
|
||||
(plant_text_str, "文本"),
|
||||
(black_word_str, "检测"),
|
||||
(punish_str, "惩罚"),
|
||||
(punish_level_str, "等级"),
|
||||
(create_time_str, "记录日期"),
|
||||
]:
|
||||
img = await text2image(s, color="#f9f6f2", _add_height=2.1)
|
||||
w = _tmp_img.getsize(type_)[0] if _tmp_img.getsize(type_)[0] > img.w else img.w
|
||||
A = BuildImage(w + 11, img.h + 50, color="#f9f6f2", font_size=35, font="CJGaoDeGuo.otf")
|
||||
await A.atext((10, 10), type_)
|
||||
await A.apaste(img, (0, 50))
|
||||
image_list.append(A)
|
||||
horizontal_line = []
|
||||
w, h = 0, 0
|
||||
for img in image_list:
|
||||
w += img.w + 20
|
||||
h = img.h if img.h > h else h
|
||||
horizontal_line.append(img.w)
|
||||
A = BuildImage(w, h, color="#f9f6f2")
|
||||
current_w = 0
|
||||
for img in image_list:
|
||||
await A.apaste(img, (current_w, 0))
|
||||
current_w += img.w + 20
|
||||
return A
|
||||
|
||||
|
||||
async def set_user_punish(user_id: int, id_: int, punish_level: int) -> str:
|
||||
"""
|
||||
设置惩罚
|
||||
:param user_id: 用户id
|
||||
:param id_: 记录下标
|
||||
:param punish_level: 惩罚等级
|
||||
"""
|
||||
result = await _get_punish(punish_level, user_id)
|
||||
punish = {
|
||||
1: "永久ban",
|
||||
2: "删除好友",
|
||||
3: f"ban {result} 天",
|
||||
4: f"ban {result} 分钟",
|
||||
5: "口头警告"
|
||||
}
|
||||
if await BlackWord.set_user_punish(user_id, punish[punish_level], id_=id_):
|
||||
return f"已对 USER {user_id} 进行 {punish[punish_level]} 处罚。"
|
||||
else:
|
||||
return "操作失败,可能未找到用户,id或敏感词"
|
||||
@@ -0,0 +1,148 @@
|
||||
from services.db_context import db
|
||||
from typing import Optional, List
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class BlackWord(db.Model):
|
||||
__tablename__ = "black_word"
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True)
|
||||
group_id = db.Column(db.BigInteger())
|
||||
plant_text = db.Column(db.String())
|
||||
black_word = db.Column(db.String())
|
||||
punish = db.Column(db.String(), default="")
|
||||
punish_level = db.Column(db.Integer())
|
||||
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
|
||||
|
||||
@classmethod
|
||||
async def add_user_black_word(
|
||||
cls,
|
||||
user_qq: int,
|
||||
group_id: Optional[int],
|
||||
black_word: str,
|
||||
plant_text: str,
|
||||
punish_level: int,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
添加用户发送的敏感词
|
||||
参数:
|
||||
:param user_qq: 用户id
|
||||
:param group_id: 群号
|
||||
:param black_word: 黑名单词汇
|
||||
:param plant_text: 消息文本
|
||||
:param punish_level: 惩罚等级
|
||||
"""
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
plant_text=plant_text,
|
||||
black_word=black_word,
|
||||
punish_level=punish_level,
|
||||
create_time=datetime.now(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def set_user_punish(
|
||||
cls,
|
||||
user_qq: int,
|
||||
punish: str,
|
||||
black_word: Optional[str] = None,
|
||||
id_: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
设置处罚
|
||||
参数:
|
||||
:param user_qq: 用户id
|
||||
:param punish: 处罚
|
||||
:param black_word: 黑名单词汇
|
||||
:param id_: 记录下标
|
||||
"""
|
||||
user = None
|
||||
if (not black_word and not id_) or not punish:
|
||||
return False
|
||||
query = cls.query.where(cls.user_qq == user_qq).with_for_update()
|
||||
if black_word:
|
||||
user = await query.where(cls.black_word == black_word).order_by(cls.id.desc()).gino.first()
|
||||
elif id_:
|
||||
user_list = await query.gino.all()
|
||||
if len(user_list) == 0 or (id_ < 0 or id_ > len(user_list)):
|
||||
return False
|
||||
user = user_list[id_]
|
||||
if not user:
|
||||
return False
|
||||
await user.update(punish=cls.punish + punish + " ").apply()
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def get_user_count(
|
||||
cls, user_qq: int, days: int = 7, punish_level: Optional[int] = None
|
||||
) -> int:
|
||||
"""
|
||||
说明:
|
||||
获取用户规定周期内的犯事次数
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param days: 周期天数
|
||||
:param punish_level: 惩罚等级
|
||||
"""
|
||||
setattr(BlackWord, "count", db.func.count(cls.id).label("count"))
|
||||
query = cls.select("count").where(
|
||||
(cls.user_qq == user_qq)
|
||||
& (cls.punish_level != -1)
|
||||
& (cls.create_time > datetime.now() - timedelta(days=days))
|
||||
)
|
||||
if punish_level is not None:
|
||||
query = query.where(cls.punish_level == punish_level)
|
||||
return (await query.gino.first())[0]
|
||||
|
||||
@classmethod
|
||||
async def get_user_punish_level(cls, user_qq: int, days: int = 7) -> Optional[int]:
|
||||
"""
|
||||
说明:
|
||||
获取用户最近一次的惩罚记录等级
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param days: 周期天数
|
||||
"""
|
||||
if (
|
||||
query := await cls.query.where(cls.user_qq == user_qq)
|
||||
.where(cls.create_time > datetime.now() - timedelta(days=days))
|
||||
.order_by(cls.id.desc())
|
||||
.gino.first()
|
||||
):
|
||||
return query.punish_level
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def get_black_data(
|
||||
cls,
|
||||
user_qq: Optional[int],
|
||||
group_id: Optional[int],
|
||||
date: Optional[datetime],
|
||||
date_type: str = "=",
|
||||
) -> List["BlackWord"]:
|
||||
"""
|
||||
说明:
|
||||
通过指定条件查询数据
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param group_id: 群号
|
||||
:param date: 日期
|
||||
:param date_type: 日期查询类型
|
||||
"""
|
||||
query = cls.query
|
||||
if user_qq:
|
||||
query = query.where(cls.user_qq == user_qq)
|
||||
if group_id:
|
||||
query = query.where(cls.group_id == group_id)
|
||||
if date:
|
||||
if date_type == "=":
|
||||
query = query.where(cls.create_time == date)
|
||||
elif date_type == ">":
|
||||
query = query.where(cls.create_time > date)
|
||||
elif date_type == "<":
|
||||
query = query.where(cls.create_time < date)
|
||||
return await query.gino.all()
|
||||
@@ -0,0 +1,322 @@
|
||||
from utils.utils import cn2py, get_bot
|
||||
from configs.path_config import DATA_PATH
|
||||
from typing import Optional, Union, Tuple
|
||||
from .model import BlackWord
|
||||
from configs.config import Config
|
||||
from pathlib import Path
|
||||
from services.log import logger
|
||||
from models.ban_user import BanUser
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from utils.http_utils import AsyncHttpx
|
||||
import random
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
|
||||
class BlackWordManager:
|
||||
|
||||
"""
|
||||
敏感词管理( 拒绝恶意
|
||||
"""
|
||||
|
||||
def __init__(self, word_file: Path, py_file: Path):
|
||||
self._word_list = {
|
||||
"1": [],
|
||||
"2": [],
|
||||
"3": [],
|
||||
"4": ["sb", "nmsl", "mdzz", "2b", "jb", "操", "废物", "憨憨", "cnm", "rnm"],
|
||||
"5": [],
|
||||
}
|
||||
self._py_list = {
|
||||
"1": [],
|
||||
"2": [],
|
||||
"3": [],
|
||||
"4": [
|
||||
"shabi",
|
||||
"wocaonima",
|
||||
"sima",
|
||||
"sabi",
|
||||
"zhizhang",
|
||||
"naocan",
|
||||
"caonima",
|
||||
"rinima",
|
||||
"simadongxi",
|
||||
"simawanyi",
|
||||
"hanbi",
|
||||
"hanpi",
|
||||
"laji",
|
||||
"fw"
|
||||
],
|
||||
"5": [],
|
||||
}
|
||||
word_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
if word_file.exists():
|
||||
# 清空默认配置
|
||||
with open(word_file, "r", encoding="utf8") as f:
|
||||
self._word_list = json.load(f)
|
||||
else:
|
||||
with open(word_file, "w", encoding="utf8") as f:
|
||||
json.dump(
|
||||
self._word_list,
|
||||
f,
|
||||
ensure_ascii=False,
|
||||
indent=4,
|
||||
)
|
||||
if py_file.exists():
|
||||
# 清空默认配置
|
||||
with open(py_file, "r", encoding="utf8") as f:
|
||||
self._py_list = json.load(f)
|
||||
else:
|
||||
with open(py_file, "w", encoding="utf8") as f:
|
||||
json.dump(
|
||||
self._py_list,
|
||||
f,
|
||||
ensure_ascii=False,
|
||||
indent=4,
|
||||
)
|
||||
|
||||
async def check(
|
||||
self, user_id: int, group_id: Optional[int], message: str
|
||||
) -> Optional[Union[str, bool]]:
|
||||
"""
|
||||
检查是否包含黑名单词汇
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param message: 消息
|
||||
"""
|
||||
if data := self._check(message):
|
||||
if data[0]:
|
||||
await _add_user_black_word(
|
||||
user_id, group_id, data[0], message, int(data[1])
|
||||
)
|
||||
return True
|
||||
if Config.get_config(
|
||||
"black_word", "ALAPI_CHECK_FLAG"
|
||||
) and not await check_text(message):
|
||||
await send_msg(
|
||||
0, None, f"USER {user_id} GROUP {group_id} ALAPI 疑似检测:{message}"
|
||||
)
|
||||
return False
|
||||
|
||||
def _check(self, message: str) -> Tuple[Optional[str], int]:
|
||||
"""
|
||||
检测文本是否违规
|
||||
:param message: 检测消息
|
||||
"""
|
||||
# 移除空格
|
||||
message = message.replace(" ", "")
|
||||
py_msg = cn2py(message).lower()
|
||||
# 完全匹配
|
||||
for x in [self._word_list, self._py_list]:
|
||||
for level in x:
|
||||
if message in x[level] or py_msg in x[level]:
|
||||
return message if message in x[level] else py_msg, level
|
||||
# 模糊匹配
|
||||
for x in [self._word_list, self._py_list]:
|
||||
for level in x:
|
||||
for m in x[level]:
|
||||
if m in message or m in py_msg:
|
||||
return m, -1
|
||||
return None, 0
|
||||
|
||||
|
||||
async def _add_user_black_word(
|
||||
user_id: int,
|
||||
group_id: Optional[int],
|
||||
black_word: str,
|
||||
message: str,
|
||||
punish_level: int,
|
||||
):
|
||||
"""
|
||||
添加敏感词数据
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param black_word: 触发的黑名单词汇
|
||||
:param message: 原始文本
|
||||
:param punish_level: 惩罚等级
|
||||
"""
|
||||
cycle_days = Config.get_config("black_word", "CYCLE_DAYS") or 7
|
||||
user_count = await BlackWord.get_user_count(user_id, cycle_days, punish_level)
|
||||
# 周期内超过次数直接提升惩罚
|
||||
if Config.get_config(
|
||||
"black_word", "AUTO_ADD_PUNISH_LEVEL"
|
||||
) and user_count > Config.get_config("black_word", "ADD_PUNISH_LEVEL_TO_COUNT"):
|
||||
punish_level -= 1
|
||||
await BlackWord.add_user_black_word(
|
||||
user_id, group_id, black_word, message, punish_level
|
||||
)
|
||||
logger.info(
|
||||
f"已将 USER {user_id} GROUP {group_id} 添加至黑名单词汇记录 Black_word:{black_word} Plant_text:{message}"
|
||||
)
|
||||
# 自动惩罚
|
||||
if Config.get_config("black_word", "AUTO_PUNISH") and punish_level != -1:
|
||||
await _punish_handle(user_id, group_id, punish_level, black_word)
|
||||
|
||||
|
||||
async def _punish_handle(
|
||||
user_id: int, group_id: Optional[int], punish_level: int, black_word: str
|
||||
):
|
||||
"""
|
||||
惩罚措施,级别越低惩罚越严
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param black_word: 触发的黑名单词汇
|
||||
"""
|
||||
logger.info(f"BlackWord USER {user_id} 触发 {punish_level} 级惩罚...")
|
||||
# 周期天数
|
||||
cycle_days = Config.get_config("black_word", "CYCLE_DAYS") or 7
|
||||
# 用户周期内触发punish_level级惩罚的次数
|
||||
user_count = await BlackWord.get_user_count(user_id, cycle_days, punish_level)
|
||||
# 获取最近一次的惩罚等级,将在此基础上增加
|
||||
punish_level = await BlackWord.get_user_punish_level(user_id, cycle_days) or punish_level
|
||||
# 容忍次数:List[int]
|
||||
tolerate_count = Config.get_config("black_word", "TOLERATE_COUNT")
|
||||
if not tolerate_count or len(tolerate_count) < 5:
|
||||
tolerate_count = [5, 2, 2, 2, 2]
|
||||
if punish_level == 1 and user_count > tolerate_count[punish_level - 1]:
|
||||
# 永久ban
|
||||
await _get_punish(1, user_id, group_id)
|
||||
await BlackWord.set_user_punish(user_id, "永久ban 删除好友", black_word)
|
||||
elif punish_level == 2 and user_count > tolerate_count[punish_level - 1]:
|
||||
# 删除好友
|
||||
await _get_punish(2, user_id, group_id)
|
||||
await BlackWord.set_user_punish(user_id, "删除好友", black_word)
|
||||
elif punish_level == 3 and user_count > tolerate_count[punish_level - 1]:
|
||||
# 永久ban
|
||||
ban_day = await _get_punish(3, user_id, group_id)
|
||||
await BlackWord.set_user_punish(user_id, f"ban {ban_day} 天", black_word)
|
||||
elif punish_level == 4 and user_count > tolerate_count[punish_level - 1]:
|
||||
# ban指定时长
|
||||
ban_time = await _get_punish(4, user_id, group_id)
|
||||
await BlackWord.set_user_punish(user_id, f"ban {ban_time} 分钟", black_word)
|
||||
elif punish_level == 5 and user_count > tolerate_count[punish_level - 1]:
|
||||
# 口头警告
|
||||
warning_result = await _get_punish(5, user_id, group_id)
|
||||
await BlackWord.set_user_punish(user_id, f"口头警告:{warning_result}", black_word)
|
||||
else:
|
||||
await BlackWord.set_user_punish(user_id, f"提示!", black_word)
|
||||
await send_msg(
|
||||
user_id,
|
||||
group_id,
|
||||
f"BlackWordChecker:该条发言已被记录,目前你在{cycle_days}天内的发表{punish_level}级"
|
||||
f"言论记录次数为:{user_count}次,请注意你的发言\n"
|
||||
f"* 如果你不清楚惩罚机制,请发送“惩罚机制” *",
|
||||
)
|
||||
|
||||
|
||||
async def _get_punish(
|
||||
id_: int, user_id: int, group_id: Optional[int] = None
|
||||
) -> Optional[Union[int, str]]:
|
||||
"""
|
||||
通过id_获取惩罚
|
||||
:param id_: id
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
"""
|
||||
bot = get_bot()
|
||||
# 忽略的群聊
|
||||
# _ignore_group = Config.get_config("black_word", "IGNORE_GROUP")
|
||||
# 处罚 id 4 ban 时间:int,List[int]
|
||||
ban_3_duration = Config.get_config("black_word", "BAN_3_DURATION")
|
||||
# 处罚 id 4 ban 时间:int,List[int]
|
||||
ban_4_duration = Config.get_config("black_word", "BAN_4_DURATION")
|
||||
# 口头警告内容
|
||||
warning_result = Config.get_config("black_word", "WARNING_RESULT")
|
||||
try:
|
||||
uname = (await GroupInfoUser.get_member_info(user_id, group_id)).user_name
|
||||
except AttributeError:
|
||||
uname = user_id
|
||||
# 永久ban
|
||||
if id_ == 1:
|
||||
if str(user_id) not in bot.config.superusers:
|
||||
await BanUser.ban(user_id, 10, 99999999)
|
||||
await send_msg(user_id, group_id, f"BlackWordChecker 永久ban USER {uname}({user_id})")
|
||||
logger.info(f"BlackWord 永久封禁 USER {user_id}...")
|
||||
# 删除好友(有的话
|
||||
elif id_ == 2:
|
||||
if str(user_id) not in bot.config.superusers:
|
||||
try:
|
||||
await bot.delete_friend(user_id=user_id)
|
||||
await send_msg(
|
||||
user_id, group_id, f"BlackWordChecker 删除好友 USER {uname}({user_id})"
|
||||
)
|
||||
logger.info(f"BlackWord 删除好友 {user_id}...")
|
||||
except ActionFailed:
|
||||
pass
|
||||
# 封禁用户指定时间,默认7天
|
||||
elif id_ == 3:
|
||||
if isinstance(ban_3_duration, list):
|
||||
ban_3_duration = random.randint(ban_3_duration[0], ban_3_duration[1])
|
||||
await BanUser.ban(user_id, 9, ban_4_duration * 60 * 60 * 24)
|
||||
await send_msg(
|
||||
user_id,
|
||||
group_id,
|
||||
f"BlackWordChecker 对用户 USER {uname}({user_id}) 进行封禁 {ban_3_duration} 天处罚。",
|
||||
)
|
||||
logger.info(f"BlackWord 封禁 USER {uname}({user_id}) {ban_3_duration} 天...")
|
||||
return ban_3_duration
|
||||
# 封禁用户指定时间,默认360分钟
|
||||
elif id_ == 4:
|
||||
if isinstance(ban_4_duration, list):
|
||||
ban_4_duration = random.randint(ban_4_duration[0], ban_4_duration[1])
|
||||
await BanUser.ban(user_id, 9, ban_4_duration * 60)
|
||||
await send_msg(
|
||||
user_id,
|
||||
group_id,
|
||||
f"BlackWordChecker 对用户 USER {uname}({user_id}) 进行封禁 {ban_4_duration} 分钟处罚。",
|
||||
)
|
||||
logger.info(f"BlackWord 封禁 USER {uname}({user_id}) {ban_4_duration} 分钟...")
|
||||
return ban_4_duration
|
||||
# 口头警告
|
||||
elif id_ == 5:
|
||||
if group_id:
|
||||
await bot.send_group_msg(group_id=group_id, message=warning_result)
|
||||
else:
|
||||
await bot.send_private_msg(user_id=user_id, message=warning_result)
|
||||
logger.info(f"BlackWord 口头警告 USER {user_id}")
|
||||
return warning_result
|
||||
return None
|
||||
|
||||
|
||||
async def send_msg(user_id: int, group_id: Optional[int], message: str):
|
||||
"""
|
||||
发送消息
|
||||
:param user_id: user_id
|
||||
:param group_id: group_id
|
||||
:param message: message
|
||||
"""
|
||||
bot = get_bot()
|
||||
if not user_id:
|
||||
user_id = int(list(bot.config.superusers)[0])
|
||||
if group_id:
|
||||
await bot.send_group_msg(group_id=group_id, message=message)
|
||||
else:
|
||||
await bot.send_private_msg(user_id=user_id, message=message)
|
||||
|
||||
|
||||
async def check_text(text: str) -> bool:
|
||||
"""
|
||||
ALAPI文本检测,检测输入违规
|
||||
:param text: 回复
|
||||
"""
|
||||
if not Config.get_config("alapi", "ALAPI_TOKEN"):
|
||||
return True
|
||||
params = {"token": Config.get_config("alapi", "ALAPI_TOKEN"), "text": text}
|
||||
try:
|
||||
data = (
|
||||
await AsyncHttpx.get(
|
||||
"https://v2.alapi.cn/api/censor/text", timeout=4, params=params
|
||||
)
|
||||
).json()
|
||||
if data["code"] == 200:
|
||||
return data["data"]["conclusion_type"] == 2
|
||||
except Exception as e:
|
||||
logger.error(f"检测违规文本错误...{type(e)}:{e}")
|
||||
return True
|
||||
|
||||
|
||||
black_word_manager = BlackWordManager(DATA_PATH / "black_word" / "black_word.json", DATA_PATH / "black_word" / "black_py.json")
|
||||
@@ -3,10 +3,10 @@ from configs.config import Config
|
||||
from bs4 import BeautifulSoup
|
||||
import platform
|
||||
|
||||
if platform.system() == "Windows":
|
||||
import asyncio
|
||||
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
# if platform.system() == "Windows":
|
||||
# import asyncio
|
||||
#
|
||||
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
|
||||
url = "http://www.eclzz.mobi"
|
||||
|
||||
@@ -14,9 +14,9 @@ import tarfile
|
||||
import shutil
|
||||
import os
|
||||
|
||||
if str(platform.system()).lower() == "windows":
|
||||
policy = asyncio.WindowsSelectorEventLoopPolicy()
|
||||
asyncio.set_event_loop_policy(policy)
|
||||
# if str(platform.system()).lower() == "windows":
|
||||
# policy = asyncio.WindowsSelectorEventLoopPolicy()
|
||||
# asyncio.set_event_loop_policy(policy)
|
||||
|
||||
|
||||
driver = nonebot.get_driver()
|
||||
|
||||
+24
-37
@@ -1,58 +1,34 @@
|
||||
from httpx import AsyncClient
|
||||
from datetime import datetime
|
||||
from nonebot.log import logger
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from configs.config import NICKNAME
|
||||
from utils.http_utils import AsyncHttpx
|
||||
|
||||
|
||||
# 获取所有 Epic Game Store 促销游戏
|
||||
# 方法参考:RSSHub /epicgames 路由
|
||||
# https://github.com/DIYgod/RSSHub/blob/master/lib/routes/epicgames/index.js
|
||||
async def get_epic_game():
|
||||
# 现在没用 graphql 辣
|
||||
"""prv_graphql Code
|
||||
epic_url = "https://www.epicgames.com/store/backend/graphql-proxy"
|
||||
headers = {
|
||||
"Referer": "https://www.epicgames.com/store/zh-CN/",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
}
|
||||
data = {
|
||||
"query": "query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, $keywords: String, $locale: String, $namespace: String, $sortBy: String, $sortDir: String, $start: Int, $tag: String, $withPrice: Boolean = false, $withPromotions: Boolean = false) {\n Catalog {\n searchStore(allowCountries: $allowCountries, category: $category, count: $count, country: $country, keywords: $keywords, locale: $locale, namespace: $namespace, sortBy: $sortBy, sortDir: $sortDir, start: $start, tag: $tag) {\n elements {\n title\n id\n namespace\n description\n effectiveDate\n keyImages {\n type\n url\n }\n seller {\n id\n name\n }\n productSlug\n urlSlug\n url\n items {\n id\n namespace\n }\n customAttributes {\n key\n value\n }\n categories {\n path\n }\n price(country: $country) @include(if: $withPrice) {\n totalPrice {\n discountPrice\n originalPrice\n voucherDiscount\n discount\n currencyCode\n currencyInfo {\n decimals\n }\n fmtPrice(locale: $locale) {\n originalPrice\n discountPrice\n intermediatePrice\n }\n }\n lineOffers {\n appliedRules {\n id\n endDate\n discountSetting {\n discountType\n }\n }\n }\n }\n promotions(category: $category) @include(if: $withPromotions) {\n promotionalOffers {\n promotionalOffers {\n startDate\n endDate\n discountSetting {\n discountType\n discountPercentage\n }\n }\n }\n upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n endDate\n discountSetting {\n discountType\n discountPercentage\n }\n }\n }\n }\n }\n paging {\n count\n total\n }\n }\n }\n}\n",
|
||||
"variables": {
|
||||
"allowCountries": "CN",
|
||||
"category": "freegames",
|
||||
"count": 1000,
|
||||
"country": "CN",
|
||||
"locale": "zh-CN",
|
||||
"sortBy": "effectiveDate",
|
||||
"sortDir": "asc",
|
||||
"withPrice": True,
|
||||
"withPromotions": True,
|
||||
},
|
||||
}
|
||||
"""
|
||||
|
||||
epic_url = "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions?locale=zh-CN&country=CN&allowCountries=CN"
|
||||
headers = {
|
||||
"Referer": "https://www.epicgames.com/store/zh-CN/",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36",
|
||||
}
|
||||
async with AsyncClient(headers=headers) as client:
|
||||
try:
|
||||
res = await client.get(epic_url, timeout=10.0)
|
||||
res_json = res.json()
|
||||
games = res_json["data"]["Catalog"]["searchStore"]["elements"]
|
||||
return games
|
||||
except Exception as e:
|
||||
logger.error(str(e))
|
||||
return None
|
||||
try:
|
||||
res = await AsyncHttpx.get(epic_url, headers=headers, timeout=10)
|
||||
res_json = res.json()
|
||||
games = res_json["data"]["Catalog"]["searchStore"]["elements"]
|
||||
return games
|
||||
except Exception as e:
|
||||
logger.error(f"Epic 访问接口错误 {type(e)}:{e}")
|
||||
return None
|
||||
|
||||
|
||||
# 获取 Epic Game Store 免费游戏信息
|
||||
# 处理免费游戏的信息方法借鉴 pip 包 epicstore_api 示例
|
||||
# https://github.com/SD4RK/epicstore_api/blob/master/examples/free_games_example.py
|
||||
async def get_epic_free(bot: Bot, Type_Event: str):
|
||||
async def get_epic_free(bot: Bot, type_event: str):
|
||||
games = await get_epic_game()
|
||||
if not games:
|
||||
return "Epic 可能又抽风啦,请稍后再试(", 404
|
||||
@@ -81,7 +57,7 @@ async def get_epic_free(bot: Bot, Type_Event: str):
|
||||
end_date = datetime.fromisoformat(end_date_iso).strftime(
|
||||
"%b.%d %H:%M"
|
||||
)
|
||||
if Type_Event == "Group":
|
||||
if type_event == "Group":
|
||||
_message = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format(
|
||||
game_corp, game_name, game_price, start_date, end_date
|
||||
)
|
||||
@@ -101,8 +77,19 @@ async def get_epic_free(bot: Bot, Type_Event: str):
|
||||
msg_list.append(msg)
|
||||
else:
|
||||
for image in game["keyImages"]:
|
||||
if image["type"] == "Thumbnail":
|
||||
if (
|
||||
image.get("url")
|
||||
and not game_thumbnail
|
||||
and image["type"]
|
||||
in [
|
||||
"Thumbnail",
|
||||
"VaultOpened",
|
||||
"DieselStoreFrontWide",
|
||||
"OfferImageWide",
|
||||
]
|
||||
):
|
||||
game_thumbnail = image["url"]
|
||||
break
|
||||
for pair in game["customAttributes"]:
|
||||
if pair["key"] == "developerName":
|
||||
game_dev = pair["value"]
|
||||
@@ -127,7 +114,7 @@ async def get_epic_free(bot: Bot, Type_Event: str):
|
||||
game_url = "https://www.epicgames.com/store/zh-CN/p/{}".format(
|
||||
game_url_part
|
||||
)
|
||||
if Type_Event == "Group":
|
||||
if type_event == "Group":
|
||||
_message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format(
|
||||
game_thumbnail,
|
||||
game_name,
|
||||
|
||||
+4
-3
@@ -9,6 +9,7 @@ from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from configs.config import Config
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from services.log import logger
|
||||
from configs.config import NICKNAME
|
||||
|
||||
|
||||
__zx_plugin_name__ = "复读"
|
||||
@@ -74,15 +75,15 @@ class Fudu:
|
||||
_fudu_list = Fudu()
|
||||
|
||||
|
||||
fudu = on_message(permission=GROUP, priority=9)
|
||||
fudu = on_message(permission=GROUP, priority=999)
|
||||
|
||||
|
||||
@fudu.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
if event.is_tome():
|
||||
return
|
||||
if get_message_text(event.json()):
|
||||
if get_message_text(event.json()).find("@可爱的小真寻") != -1:
|
||||
if msg := get_message_text(event.json()):
|
||||
if msg.startswith(f"@可爱的{NICKNAME}"):
|
||||
await fudu.finish("复制粘贴的虚空艾特?", at_sender=True)
|
||||
img = get_message_img(event.json())
|
||||
msg = get_message_text(event.json())
|
||||
|
||||
@@ -35,7 +35,7 @@ Config.add_plugin_config(
|
||||
default_value=True,
|
||||
)
|
||||
|
||||
almanac = on_command("原神黄历", priority=5, block=True)
|
||||
almanac = on_command("原神黄历", priority=15, block=True)
|
||||
|
||||
|
||||
ALC_PATH = IMAGE_PATH / "genshin" / "alc"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pathlib import Path
|
||||
from typing import Tuple, Optional, List
|
||||
from configs.path_config import IMAGE_PATH, TEXT_PATH
|
||||
from PIL.Image import UnidentifiedImageError
|
||||
from utils.message_builder import image
|
||||
from services.log import logger
|
||||
from utils.image_utils import BuildImage
|
||||
@@ -137,7 +137,7 @@ async def download_resource_data(semaphore: Semaphore):
|
||||
asyncio.ensure_future(
|
||||
download_image(
|
||||
img_url,
|
||||
f"{icon_path}/{id_}.png",
|
||||
icon_path / f"{id_}.png",
|
||||
semaphore,
|
||||
True,
|
||||
)
|
||||
@@ -179,7 +179,7 @@ async def download_map_init(semaphore: Semaphore, flag: bool = False):
|
||||
map_url = _map_data["url"]
|
||||
await download_image(
|
||||
map_url,
|
||||
f"{map_path}/{idx}.png",
|
||||
map_path / f"{idx}.png",
|
||||
semaphore,
|
||||
force_flag=flag,
|
||||
)
|
||||
@@ -228,15 +228,14 @@ async def download_resource_type():
|
||||
except (TimeoutError, ConnectTimeout):
|
||||
logger.warning("下载原神资源类型数据超时....")
|
||||
except Exception as e:
|
||||
logger.error(f"载原神资源类型数据超时 {type(e)}:{e}")
|
||||
logger.error(f"载原神资源类型数据错误 {type(e)}:{e}")
|
||||
|
||||
|
||||
# 初始化资源图标
|
||||
def gen_icon(icon: str):
|
||||
def gen_icon(icon: Path):
|
||||
A = BuildImage(0, 0, background=f"{icon_path}/box.png")
|
||||
B = BuildImage(0, 0, background=f"{icon_path}/box_alpha.png")
|
||||
icon_ = icon_path / f"{icon}"
|
||||
icon_img = BuildImage(115, 115, background=icon_)
|
||||
icon_img = BuildImage(115, 115, background=icon)
|
||||
icon_img.circle()
|
||||
B.paste(icon_img, (17, 10), True)
|
||||
B.paste(A, alpha=True)
|
||||
@@ -247,22 +246,22 @@ def gen_icon(icon: str):
|
||||
# 下载图片
|
||||
async def download_image(
|
||||
img_url: str,
|
||||
path: str,
|
||||
path: Path,
|
||||
semaphore: Semaphore,
|
||||
gen_flag: bool = False,
|
||||
force_flag: bool = False,
|
||||
):
|
||||
async with semaphore:
|
||||
try:
|
||||
if not os.path.exists(path) or not is_valid or force_flag:
|
||||
if not path.exists() or not is_valid(path) or force_flag:
|
||||
if await AsyncHttpx.download_file(img_url, path, timeout=10):
|
||||
logger.info(f"下载原神资源图标:{img_url}")
|
||||
if gen_flag:
|
||||
gen_icon(path)
|
||||
else:
|
||||
logger.info(f"下载原神资源图标:{img_url} 失败,等待下次更新...")
|
||||
except UnidentifiedImageError:
|
||||
logger.warning(f"原神图片打开错误..已删除,等待下次更新... file: {path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"原神图片错误..已删除,等待下次更新... file: {path} {type(e)}:{e}")
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ async def get_character(
|
||||
) -> Optional[dict]:
|
||||
try:
|
||||
req = await AsyncHttpx.post(
|
||||
url="https://api-takumi.mihoyo.com/game_record/app/genshin/api/character",
|
||||
url="https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/character",
|
||||
headers={
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"DS": get_ds(
|
||||
@@ -232,7 +232,7 @@ async def get_mys_data(uid: str, mys_id: Optional[str]) -> Optional[List[Dict]]:
|
||||
if mys_id:
|
||||
try:
|
||||
req = await AsyncHttpx.get(
|
||||
url=f"https://api-takumi.mihoyo.com/game_record/card/wapi/getGameRecordCard?uid={mys_id}",
|
||||
url=f"https://api-takumi-record.mihoyo.com/game_record/card/wapi/getGameRecordCard?uid={mys_id}",
|
||||
headers={
|
||||
"DS": get_ds(f"uid={mys_id}"),
|
||||
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"),
|
||||
|
||||
@@ -300,6 +300,8 @@ def get_country_data_image(world_data_dict: Dict) -> BuildImage:
|
||||
region = BuildImage(790, 267 * (len(world_data_dict) - 1), color="#F9F6F2")
|
||||
height = 0
|
||||
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫"]:
|
||||
if not world_data_dict.get(country):
|
||||
continue
|
||||
x = BuildImage(790, 250, color="#3A4467")
|
||||
logo = BuildImage(180, 180, background=image_path / "logo" / f"{country}.png")
|
||||
tmp_bk = BuildImage(770, 230, color="#606779")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from nonebot.adapters.onebot.v11 import ActionFailed
|
||||
|
||||
from utils.utils import get_bot, scheduler
|
||||
from utils.message_builder import at
|
||||
from models.group_member_info import GroupInfoUser
|
||||
@@ -178,10 +180,13 @@ async def _remind(user_id: int, uid: str):
|
||||
group_list = await GroupInfoUser.get_user_all_group(user_id)
|
||||
if group_list:
|
||||
group_id = group_list[0]
|
||||
await bot.send_group_msg(
|
||||
group_id=group_id,
|
||||
message=at(user_id) + msg
|
||||
)
|
||||
try:
|
||||
await bot.send_group_msg(
|
||||
group_id=group_id,
|
||||
message=at(user_id) + msg
|
||||
)
|
||||
except ActionFailed as e:
|
||||
logger.error(f"树脂提醒推送发生错误 {type(e)}:{e}")
|
||||
if not next_time:
|
||||
if user_manager.check(uid) and Config.get_config("resin_remind", "AUTO_CLOSE_QUERY_FAIL_RESIN_REMIND"):
|
||||
await Genshin.set_resin_remind(int(uid), False)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
from nonebot import on_message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from .data_source import cancel_all_notice, save_data, get_data, set_data_value
|
||||
from services.log import logger
|
||||
import time
|
||||
|
||||
|
||||
__zx_plugin_name__ = "群聊最后聊天时间记录 [Hidden]"
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
|
||||
|
||||
last_chat = on_message(priority=1, block=False, permission=GROUP)
|
||||
|
||||
|
||||
@last_chat.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
time_data = await get_data()
|
||||
set_data_value(event.group_id, time.time())
|
||||
if event.group_id in time_data["_group"]:
|
||||
time_data["_group"].remove(event.group_id)
|
||||
set_data_value("_group", time_data["_group"])
|
||||
for key in time_data.keys():
|
||||
if key not in ["check_time", "_group"]:
|
||||
if key not in time_data["_group"]:
|
||||
if time.time() - time_data[key] > 60 * 60 * 36:
|
||||
await cancel_all_notice(key)
|
||||
time_data["_group"].append(key)
|
||||
set_data_value("_group", time_data["_group"])
|
||||
logger.info(f"GROUP {event.group_id} 因群内发言时间大于36小时被取消全部通知")
|
||||
if time.time() - time_data["check_time"] > 60 * 60 * 1:
|
||||
set_data_value("check_time", time.time())
|
||||
save_data()
|
||||
@@ -1,67 +0,0 @@
|
||||
from configs.path_config import DATA_PATH
|
||||
from utils.utils import get_bot
|
||||
from datetime import datetime
|
||||
import time
|
||||
from services.log import logger
|
||||
from utils.manager import group_manager
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
|
||||
time_data = {}
|
||||
|
||||
|
||||
async def init():
|
||||
global time_data
|
||||
bot = get_bot()
|
||||
gl = await bot.get_group_list()
|
||||
gl = [g["group_id"] for g in gl]
|
||||
data = read_data("group_last_chat_time.json")
|
||||
for g in gl:
|
||||
if not data.get(g):
|
||||
time_data[g] = time.time()
|
||||
if not time_data.get("check_time"):
|
||||
time_data["check_time"] = time.time()
|
||||
if not time_data.get("_group"):
|
||||
time_data["_group"] = []
|
||||
save_data()
|
||||
return time_data
|
||||
|
||||
|
||||
def read_data(file_name: str):
|
||||
try:
|
||||
with open(DATA_PATH / file_name, "r", encoding="utf8") as f:
|
||||
return json.load(f)
|
||||
except (ValueError, FileNotFoundError):
|
||||
return {}
|
||||
|
||||
|
||||
def save_data():
|
||||
with open(DATA_PATH / "group_last_chat_time.json", "w") as f:
|
||||
json.dump(time_data, f, indent=4)
|
||||
logger.info(
|
||||
f'自动存储 group_last_chat_time.json 时间:{str(datetime.now()).split(".")[0]}'
|
||||
)
|
||||
|
||||
|
||||
# 取消全部通知
|
||||
async def cancel_all_notice(group_id):
|
||||
group_id = int(group_id)
|
||||
for command in group_manager.get_task_data():
|
||||
if await group_manager.check_group_task_status(group_id, command):
|
||||
await group_manager.close_group_task(group_id, command)
|
||||
logger.info(f"关闭了 {group_id} 群的全部通知")
|
||||
|
||||
|
||||
async def get_data():
|
||||
global time_data
|
||||
if not time_data:
|
||||
time_data = await init()
|
||||
return time_data
|
||||
|
||||
|
||||
def set_data_value(key, value):
|
||||
global time_data
|
||||
time_data[key] = value
|
||||
+14
-16
@@ -1,8 +1,7 @@
|
||||
from nonebot import on_message, on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from utils.utils import is_number, get_message_img
|
||||
from nonebot.typing import T_State
|
||||
from utils.utils import is_number, get_message_img, get_message_text
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from configs.path_config import DATA_PATH, TEMP_PATH
|
||||
from utils.image_utils import get_img_hash
|
||||
@@ -10,7 +9,7 @@ from services.log import logger
|
||||
from configs.config import NICKNAME, Config
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from nonebot.params import CommandArg, Command
|
||||
from typing import Tuple
|
||||
from typing import Tuple, Dict, Any
|
||||
import time
|
||||
|
||||
try:
|
||||
@@ -57,7 +56,7 @@ mute_setting = on_command(
|
||||
)
|
||||
|
||||
|
||||
def get_data():
|
||||
def get_data() -> Dict[Any, Any]:
|
||||
try:
|
||||
with open(DATA_PATH / "group_mute_data.json", "r", encoding="utf8") as f:
|
||||
data = json.load(f)
|
||||
@@ -72,7 +71,7 @@ def save_data():
|
||||
json.dump(mute_data, f, indent=4)
|
||||
|
||||
|
||||
async def download_img_and_hash(url, group_id):
|
||||
async def download_img_and_hash(url, group_id) -> str:
|
||||
if await AsyncHttpx.download_file(
|
||||
url, TEMP_PATH / f"mute_{group_id}_img.jpg"
|
||||
):
|
||||
@@ -85,9 +84,9 @@ mute_data = get_data()
|
||||
|
||||
|
||||
@mute.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
async def _(bot: Bot, event: GroupMessageEvent):
|
||||
group_id = str(event.group_id)
|
||||
msg = arg.extract_plain_text().strip()
|
||||
msg = get_message_text(event.json())
|
||||
img_list = get_message_img(event.json())
|
||||
img_hash = ""
|
||||
for img in img_list:
|
||||
@@ -102,8 +101,6 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
|
||||
if not mute_dict.get(event.user_id):
|
||||
mute_dict[event.user_id] = {"time": time.time(), "count": 1, "msg": msg}
|
||||
else:
|
||||
if cmd or not msg:
|
||||
return
|
||||
if msg and msg.find(mute_dict[event.user_id]["msg"]) != -1:
|
||||
mute_dict[event.user_id]["count"] += 1
|
||||
else:
|
||||
@@ -123,7 +120,7 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
|
||||
await bot.set_group_ban(
|
||||
group_id=event.group_id,
|
||||
user_id=event.user_id,
|
||||
duration=mute_data[group_id]["duration"],
|
||||
duration=mute_data[group_id]["duration"] * 60,
|
||||
)
|
||||
await mute.send(f"检测到恶意刷屏,{NICKNAME}要把你关进小黑屋!", at_sender=True)
|
||||
mute_dict[event.user_id]["count"] = 0
|
||||
@@ -137,27 +134,28 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
|
||||
|
||||
@mute_setting.handle()
|
||||
async def _(event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
|
||||
global mute_data
|
||||
group_id = str(event.group_id)
|
||||
if not mute_data.get(group_id):
|
||||
mute_data[group_id] = {"count": 10, "time": 7, "duration": 0}
|
||||
mute_data[group_id] = {"count": Config.get_config("mute", "MUTE_DEFAULT_COUNT"), "time": Config.get_config("mute", "MUTE_DEFAULT_TIME"), "duration": Config.get_config("mute", "MUTE_DEFAULT_DURATION")}
|
||||
msg = arg.extract_plain_text().strip()
|
||||
if cmd[0] == "刷屏检测设置":
|
||||
await mute_setting.finish(
|
||||
f'最大次数:{mute_data[group_id]["count"]} 次\n'
|
||||
f'规定时间:{mute_data[group_id]["time"]} 秒\n'
|
||||
f'禁言时长:{mute_data[group_id]["duration"] / 60} 分钟\n'
|
||||
f'禁言时长:{mute_data[group_id]["duration"]:.2f} 分钟\n'
|
||||
f"【在规定时间内发送相同消息超过最大次数则禁言\n当禁言时长为0时关闭此功能】"
|
||||
)
|
||||
if not is_number(msg):
|
||||
await mute.finish("设置的参数必须是数字啊!", at_sender=True)
|
||||
if cmd[0] == "设置检测时间":
|
||||
if cmd[0] == "设置刷屏检测时间":
|
||||
mute_data[group_id]["time"] = int(msg)
|
||||
msg += "秒"
|
||||
if cmd[0] == "设置检测次数":
|
||||
if cmd[0] == "设置刷屏检测次数":
|
||||
mute_data[group_id]["count"] = int(msg)
|
||||
msg += " 次"
|
||||
if cmd[0] == "设置禁言时长":
|
||||
mute_data[group_id]["duration"] = int(msg) * 60
|
||||
if cmd[0] == "设置刷屏禁言时长":
|
||||
mute_data[group_id]["duration"] = int(msg)
|
||||
msg += " 分钟"
|
||||
await mute_setting.send(f'刷屏检测:{cmd[0]}为 {msg}')
|
||||
logger.info(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Type
|
||||
from typing import Type, Tuple, Any
|
||||
from nonebot import on_command
|
||||
from nonebot.matcher import Matcher
|
||||
from utils.utils import scheduler, is_number
|
||||
@@ -9,7 +9,7 @@ from nonebot.permission import SUPERUSER
|
||||
import random
|
||||
from nonebot.plugin import MatcherGroup
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.params import CommandArg, RegexGroup
|
||||
from .open_cases_c import (
|
||||
open_case,
|
||||
total_open_statistics,
|
||||
@@ -145,12 +145,12 @@ async def _(event: GroupMessageEvent):
|
||||
)
|
||||
|
||||
|
||||
open_shilian: Type[Matcher] = cases_matcher_group.on_regex("(.*)连开箱(.*?)")
|
||||
open_shilian: Type[Matcher] = cases_matcher_group.on_regex("(.*)连开箱(.*)?")
|
||||
|
||||
|
||||
@open_shilian.handle()
|
||||
async def _(event: GroupMessageEvent, state: T_State):
|
||||
num = state["_matched_groups"][0].strip()
|
||||
async def _(event: GroupMessageEvent, state: T_State, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
num, case_name = reg_group
|
||||
if is_number(num) or num_dict.get(num):
|
||||
try:
|
||||
num = num_dict[num]
|
||||
@@ -162,7 +162,6 @@ async def _(event: GroupMessageEvent, state: T_State):
|
||||
await open_shilian.finish("再负开箱就扣你明天开箱数了!", at_sender=True)
|
||||
else:
|
||||
await open_shilian.finish("必须要是数字切不要超过30啊笨蛋!中文也可!", at_sender=True)
|
||||
case_name = state["_matched_groups"][1].strip()
|
||||
case_name = case_name.replace("武器箱", "").strip()
|
||||
if not case_name:
|
||||
case_name = random.choice(cases_name)
|
||||
@@ -212,16 +211,16 @@ update_price = on_command("更新开箱价格", priority=1, permission=SUPERUSER
|
||||
|
||||
|
||||
@update_price.handle()
|
||||
async def _( event: MessageEvent):
|
||||
await update_price.send(await util_get_buff_price(str(event.get_message())))
|
||||
async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
await update_price.send(await util_get_buff_price(arg.extract_plain_text().strip()))
|
||||
|
||||
|
||||
update_img = on_command("更新开箱图片", priority=1, permission=SUPERUSER, block=True)
|
||||
|
||||
|
||||
@update_img.handle()
|
||||
async def _(event: MessageEvent):
|
||||
await update_img.send(await util_get_buff_img(str(event.get_message())))
|
||||
async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
await update_img.send(await util_get_buff_img(str(arg.extract_plain_text().strip())))
|
||||
|
||||
|
||||
# 重置开箱
|
||||
|
||||
+166
-171
@@ -1,171 +1,166 @@
|
||||
from nonebot import on_message
|
||||
from services.log import logger
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from utils.utils import get_message_json, get_local_proxy, is_number, get_message_text
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from bilibili_api import video
|
||||
from utils.message_builder import image
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from utils.image_utils import BuildImage
|
||||
from utils.browser import get_browser
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from configs.config import Config
|
||||
from utils.user_agent import get_user_agent
|
||||
import aiohttp
|
||||
import asyncio
|
||||
import time
|
||||
from bilibili_api import settings
|
||||
import ujson as json
|
||||
|
||||
|
||||
__zx_plugin_name__ = "B站转发解析"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
B站转发解析,解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv,且5分钟内不解析相同url
|
||||
""".strip()
|
||||
__plugin_des__ = "B站转发解析"
|
||||
__plugin_type__ = ("其他",)
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_task__ = {"bilibili_parse": "b站转发解析"}
|
||||
Config.add_plugin_config(
|
||||
"_task",
|
||||
"DEFAULT_BILIBILI_PARSE",
|
||||
True,
|
||||
help_="被动 B站转发解析 进群默认开关状态",
|
||||
default_value=True,
|
||||
)
|
||||
|
||||
|
||||
if get_local_proxy():
|
||||
settings.proxy = get_local_proxy()
|
||||
|
||||
parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False)
|
||||
|
||||
_tmp = {}
|
||||
|
||||
|
||||
@parse_bilibili_json.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
vd_info = None
|
||||
url = None
|
||||
if get_message_json(event.json()):
|
||||
try:
|
||||
data = json.loads(get_message_json(event.json())[0]["data"])
|
||||
except (IndexError, KeyError):
|
||||
data = None
|
||||
if data:
|
||||
# 转发视频
|
||||
if data.get("desc") == "哔哩哔哩":
|
||||
async with aiohttp.ClientSession(
|
||||
headers=get_user_agent()
|
||||
) as session:
|
||||
async with session.get(
|
||||
data["meta"]["detail_1"]["qqdocurl"],
|
||||
proxy=get_local_proxy(),
|
||||
timeout=7,
|
||||
) as response:
|
||||
url = str(response.url).split("?")[0]
|
||||
bvid = url.split("/")[-1]
|
||||
vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# response = await AsyncHttpx.get(
|
||||
# data["meta"]["detail_1"]["qqdocurl"], timeout=7
|
||||
# )
|
||||
# url = str(response.url).split("?")[0]
|
||||
# bvid = url.split("/")[-1]
|
||||
# vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# 转发专栏
|
||||
if (
|
||||
data.get("meta")
|
||||
and data["meta"].get("news")
|
||||
and data["meta"]["news"].get("desc") == "哔哩哔哩专栏"
|
||||
):
|
||||
url = data["meta"]["news"]["jumpUrl"]
|
||||
page = None
|
||||
try:
|
||||
browser = await get_browser()
|
||||
if not browser:
|
||||
return
|
||||
page = await browser.new_page(
|
||||
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
" (KHTML, like Gecko) Chrome/93.0.4530.0 Safari/537.36"
|
||||
)
|
||||
await page.goto(url, wait_until="networkidle", timeout=10000)
|
||||
await page.set_viewport_size({"width": 2560, "height": 1080})
|
||||
await page.click("#app > div")
|
||||
div = await page.query_selector("#app > div")
|
||||
await div.screenshot(
|
||||
path=f"{IMAGE_PATH}/temp/cv_{event.user_id}.png",
|
||||
timeout=100000,
|
||||
)
|
||||
await asyncio.get_event_loop().run_in_executor(
|
||||
None, resize, f"{IMAGE_PATH}/temp/cv_{event.user_id}.png"
|
||||
)
|
||||
await parse_bilibili_json.send(
|
||||
"[[_task|bilibili_parse]]" + image(f"cv_{event.user_id}.png", "temp")
|
||||
)
|
||||
await page.close()
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"尝试解析bilibili专栏 {url} 失败 {type(e)}:{e}")
|
||||
if page:
|
||||
await page.close()
|
||||
return
|
||||
# BV
|
||||
if msg := get_message_text(event.json()):
|
||||
if "BV" in msg:
|
||||
index = msg.find("BV")
|
||||
if len(msg[index + 2 :]) >= 10:
|
||||
msg = msg[index : index + 12]
|
||||
url = f"https://www.bilibili.com/video/{msg}"
|
||||
vd_info = await video.Video(bvid=msg).get_info()
|
||||
elif "av" in msg:
|
||||
index = msg.find("av")
|
||||
if len(msg[index + 2 :]) >= 9:
|
||||
msg = msg[index + 2 : index + 11]
|
||||
if is_number(msg):
|
||||
url = f"https://www.bilibili.com/video/{msg}"
|
||||
vd_info = await video.Video(aid=int(msg)).get_info()
|
||||
elif "https://b23.tv" in msg:
|
||||
url = "https://" + msg[msg.find("b23.tv") : msg.find("b23.tv") + 13]
|
||||
res = await AsyncHttpx.get(url, timeout=7)
|
||||
url = str(res.url).split("?")[0]
|
||||
bvid = url.split("/")[-1]
|
||||
vd_info = await video.Video(bvid=bvid).get_info()
|
||||
if vd_info:
|
||||
if (
|
||||
url in _tmp.keys() and time.time() - _tmp[url] > 30
|
||||
) or url not in _tmp.keys():
|
||||
_tmp[url] = time.time()
|
||||
aid = vd_info["aid"]
|
||||
title = vd_info["title"]
|
||||
author = vd_info["owner"]["name"]
|
||||
reply = vd_info["stat"]["reply"] # 回复
|
||||
favorite = vd_info["stat"]["favorite"] # 收藏
|
||||
coin = vd_info["stat"]["coin"] # 投币
|
||||
# like = vd_info['stat']['like'] # 点赞
|
||||
# danmu = vd_info['stat']['danmaku'] # 弹幕
|
||||
date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"]))
|
||||
try:
|
||||
await parse_bilibili_json.send(
|
||||
"[[_task|bilibili_parse]]" +
|
||||
image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n"
|
||||
f"UP:{author}\n"
|
||||
f"上传日期:{date}\n"
|
||||
f"回复:{reply},收藏:{favorite},投币:{coin}\n"
|
||||
f"{url}"
|
||||
)
|
||||
except ActionFailed:
|
||||
logger.warning(f"{event.group_id} 发送bilibili解析失败")
|
||||
else:
|
||||
logger.info(
|
||||
f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
)
|
||||
|
||||
|
||||
def resize(path: str):
|
||||
A = BuildImage(0, 0, background=path, ratio=0.5)
|
||||
A.save(path)
|
||||
# from nonebot import on_message
|
||||
# from services.log import logger
|
||||
# from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
# from utils.utils import get_message_json, get_local_proxy, is_number, get_message_text
|
||||
# from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
# from utils.message_builder import image
|
||||
# from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
# from utils.image_utils import BuildImage
|
||||
# from utils.browser import get_browser
|
||||
# from configs.path_config import IMAGE_PATH
|
||||
# from utils.http_utils import AsyncHttpx
|
||||
# from configs.config import Config
|
||||
# from utils.user_agent import get_user_agent
|
||||
# import aiohttp
|
||||
# import asyncio
|
||||
# import time
|
||||
# import ujson as json
|
||||
#
|
||||
#
|
||||
# __zx_plugin_name__ = "B站转发解析"
|
||||
# __plugin_usage__ = """
|
||||
# usage:
|
||||
# B站转发解析,解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv,且5分钟内不解析相同url
|
||||
# """.strip()
|
||||
# __plugin_des__ = "B站转发解析"
|
||||
# __plugin_type__ = ("其他",)
|
||||
# __plugin_version__ = 0.1
|
||||
# __plugin_author__ = "HibiKier"
|
||||
# __plugin_task__ = {"bilibili_parse": "b站转发解析"}
|
||||
# Config.add_plugin_config(
|
||||
# "_task",
|
||||
# "DEFAULT_BILIBILI_PARSE",
|
||||
# True,
|
||||
# help_="被动 B站转发解析 进群默认开关状态",
|
||||
# default_value=True,
|
||||
# )
|
||||
#
|
||||
#
|
||||
# parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False)
|
||||
#
|
||||
# _tmp = {}
|
||||
#
|
||||
#
|
||||
# @parse_bilibili_json.handle()
|
||||
# async def _(event: GroupMessageEvent):
|
||||
# vd_info = None
|
||||
# url = None
|
||||
# if get_message_json(event.json()):
|
||||
# try:
|
||||
# data = json.loads(get_message_json(event.json())[0]["data"])
|
||||
# except (IndexError, KeyError):
|
||||
# data = None
|
||||
# if data:
|
||||
# # 转发视频
|
||||
# if data.get("desc") == "哔哩哔哩":
|
||||
# async with aiohttp.ClientSession(
|
||||
# headers=get_user_agent()
|
||||
# ) as session:
|
||||
# async with session.get(
|
||||
# data["meta"]["detail_1"]["qqdocurl"],
|
||||
# proxy=get_local_proxy(),
|
||||
# timeout=7,
|
||||
# ) as response:
|
||||
# url = str(response.url).split("?")[0]
|
||||
# bvid = url.split("/")[-1]
|
||||
# vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# # response = await AsyncHttpx.get(
|
||||
# # data["meta"]["detail_1"]["qqdocurl"], timeout=7
|
||||
# # )
|
||||
# # url = str(response.url).split("?")[0]
|
||||
# # bvid = url.split("/")[-1]
|
||||
# # vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# # 转发专栏
|
||||
# if (
|
||||
# data.get("meta")
|
||||
# and data["meta"].get("news")
|
||||
# and data["meta"]["news"].get("desc") == "哔哩哔哩专栏"
|
||||
# ):
|
||||
# url = data["meta"]["news"]["jumpUrl"]
|
||||
# page = None
|
||||
# try:
|
||||
# browser = await get_browser()
|
||||
# if not browser:
|
||||
# return
|
||||
# page = await browser.new_page(
|
||||
# user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
# " (KHTML, like Gecko) Chrome/93.0.4530.0 Safari/537.36"
|
||||
# )
|
||||
# await page.goto(url, wait_until="networkidle", timeout=10000)
|
||||
# await page.set_viewport_size({"width": 2560, "height": 1080})
|
||||
# await page.click("#app > div")
|
||||
# div = await page.query_selector("#app > div")
|
||||
# await div.screenshot(
|
||||
# path=f"{IMAGE_PATH}/temp/cv_{event.user_id}.png",
|
||||
# timeout=100000,
|
||||
# )
|
||||
# await asyncio.get_event_loop().run_in_executor(
|
||||
# None, resize, f"{IMAGE_PATH}/temp/cv_{event.user_id}.png"
|
||||
# )
|
||||
# await parse_bilibili_json.send(
|
||||
# "[[_task|bilibili_parse]]" + image(f"cv_{event.user_id}.png", "temp")
|
||||
# )
|
||||
# await page.close()
|
||||
# logger.info(
|
||||
# f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
# )
|
||||
# except Exception as e:
|
||||
# logger.error(f"尝试解析bilibili专栏 {url} 失败 {type(e)}:{e}")
|
||||
# if page:
|
||||
# await page.close()
|
||||
# return
|
||||
# # BV
|
||||
# if msg := get_message_text(event.json()):
|
||||
# if "BV" in msg:
|
||||
# index = msg.find("BV")
|
||||
# if len(msg[index + 2 :]) >= 10:
|
||||
# msg = msg[index : index + 12]
|
||||
# url = f"https://www.bilibili.com/video/{msg}"
|
||||
# vd_info = await video.Video(bvid=msg).get_info()
|
||||
# elif "av" in msg:
|
||||
# index = msg.find("av")
|
||||
# if len(msg[index + 2 :]) >= 9:
|
||||
# msg = msg[index + 2 : index + 11]
|
||||
# if is_number(msg):
|
||||
# url = f"https://www.bilibili.com/video/{msg}"
|
||||
# vd_info = await video.Video(aid=int(msg)).get_info()
|
||||
# elif "https://b23.tv" in msg:
|
||||
# url = "https://" + msg[msg.find("b23.tv") : msg.find("b23.tv") + 13]
|
||||
# res = await AsyncHttpx.get(url, timeout=7)
|
||||
# url = str(res.url).split("?")[0]
|
||||
# bvid = url.split("/")[-1]
|
||||
# vd_info = await video.Video(bvid=bvid).get_info()
|
||||
# if vd_info:
|
||||
# if (
|
||||
# url in _tmp.keys() and time.time() - _tmp[url] > 30
|
||||
# ) or url not in _tmp.keys():
|
||||
# _tmp[url] = time.time()
|
||||
# aid = vd_info["aid"]
|
||||
# title = vd_info["title"]
|
||||
# author = vd_info["owner"]["name"]
|
||||
# reply = vd_info["stat"]["reply"] # 回复
|
||||
# favorite = vd_info["stat"]["favorite"] # 收藏
|
||||
# coin = vd_info["stat"]["coin"] # 投币
|
||||
# # like = vd_info['stat']['like'] # 点赞
|
||||
# # danmu = vd_info['stat']['danmaku'] # 弹幕
|
||||
# date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"]))
|
||||
# try:
|
||||
# await parse_bilibili_json.send(
|
||||
# "[[_task|bilibili_parse]]" +
|
||||
# image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n"
|
||||
# f"UP:{author}\n"
|
||||
# f"上传日期:{date}\n"
|
||||
# f"回复:{reply},收藏:{favorite},投币:{coin}\n"
|
||||
# f"{url}"
|
||||
# )
|
||||
# except ActionFailed:
|
||||
# logger.warning(f"{event.group_id} 发送bilibili解析失败")
|
||||
# else:
|
||||
# logger.info(
|
||||
# f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
|
||||
# )
|
||||
#
|
||||
#
|
||||
# def resize(path: str):
|
||||
# A = BuildImage(0, 0, background=path, ratio=0.5)
|
||||
# A.save(path)
|
||||
|
||||
+117
-100
@@ -1,100 +1,117 @@
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent, Message, GroupMessageEvent
|
||||
from nonebot.typing import T_State
|
||||
from configs.path_config import TEMP_PATH
|
||||
from services.log import logger
|
||||
from utils.message_builder import image
|
||||
from utils.utils import is_number
|
||||
from utils.manager import withdraw_message_manager
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from nonebot.params import CommandArg, Arg
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
|
||||
__zx_plugin_name__ = "pid搜索"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
通过 pid 搜索图片
|
||||
指令:
|
||||
p搜 [pid]
|
||||
""".strip()
|
||||
__plugin_des__ = "通过 pid 搜索图片"
|
||||
__plugin_cmd__ = ["p搜 [pid]"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
"limit_superuser": False,
|
||||
"cmd": ["p搜"],
|
||||
}
|
||||
|
||||
pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True)
|
||||
|
||||
url = "https://api.fantasyzone.cc/tu/search.php"
|
||||
|
||||
|
||||
@pid_search.handle()
|
||||
async def _(state: T_State, arg: Message = CommandArg()):
|
||||
pid = arg.extract_plain_text().strip()
|
||||
if pid:
|
||||
state["pid"] = pid
|
||||
|
||||
|
||||
@pid_search.got("pid", prompt="需要查询的图片PID是?")
|
||||
async def _(event: MessageEvent, state: T_State, pid: str = Arg("pid")):
|
||||
if pid in ["取消", "算了"]:
|
||||
await pid_search.finish("已取消操作...")
|
||||
if is_number(pid):
|
||||
await pid_search.reject_arg("pid", "笨蛋,重新输入数!字!")
|
||||
params = {
|
||||
"id": pid,
|
||||
"p": 1,
|
||||
}
|
||||
for _ in range(3):
|
||||
try:
|
||||
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
|
||||
except TimeoutError:
|
||||
pass
|
||||
except Exception as e:
|
||||
await pid_search.finish(f"发生了一些错误..{type(e)}:{e}")
|
||||
else:
|
||||
if not data["width"] and not data["height"]:
|
||||
await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True)
|
||||
pid = data["id"]
|
||||
title = data["title"]
|
||||
author = data["userName"]
|
||||
author_id = data["userId"]
|
||||
img_url = data["url"]
|
||||
if not await AsyncHttpx.download_file(
|
||||
img_url, TEMP_PATH / f"pid_search_{event.user_id}.png"
|
||||
):
|
||||
await pid_search.finish("图片下载失败了....", at_sender=True)
|
||||
tmp = ""
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
tmp = "\n【注】将在30后撤回......"
|
||||
msg_id = await pid_search.send(
|
||||
Message(
|
||||
f"title:{title}\n"
|
||||
f"pid:{pid}\n"
|
||||
f"author:{author}\n"
|
||||
f"author_id:{author_id}\n"
|
||||
f'{image(TEMP_PATH / f"pid_search_{event.user_id}.png")}'
|
||||
f"{tmp}"
|
||||
)
|
||||
)
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, "
|
||||
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 查询图片 PID:{pid}"
|
||||
)
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
withdraw_message_manager.append((msg_id, 30))
|
||||
break
|
||||
else:
|
||||
await pid_search.finish("图片下载失败了....", at_sender=True)
|
||||
from nonebot import on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message, GroupMessageEvent
|
||||
from nonebot.typing import T_State
|
||||
|
||||
from configs.config import Config
|
||||
from utils.utils import is_number, change_pixiv_image_links
|
||||
from utils.message_builder import image
|
||||
from services.log import logger
|
||||
from asyncio.exceptions import TimeoutError
|
||||
from configs.path_config import IMAGE_PATH
|
||||
from utils.manager import withdraw_message_manager
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from nonebot.params import CommandArg, Arg
|
||||
|
||||
|
||||
__zx_plugin_name__ = "pid搜索"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
通过 pid 搜索图片
|
||||
指令:
|
||||
p搜 [pid]
|
||||
""".strip()
|
||||
__plugin_des__ = "通过 pid 搜索图片"
|
||||
__plugin_cmd__ = ["p搜 [pid]"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
"limit_superuser": False,
|
||||
"cmd": ["p搜"],
|
||||
}
|
||||
|
||||
pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True)
|
||||
|
||||
|
||||
@pid_search.handle()
|
||||
async def _h(event: MessageEvent, state: T_State, arg: Message = CommandArg()):
|
||||
pid = arg.extract_plain_text().strip()
|
||||
if pid:
|
||||
state["pid"] = pid
|
||||
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;"
|
||||
" rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Referer": "https://www.pixiv.net",
|
||||
}
|
||||
|
||||
|
||||
@pid_search.got("pid", prompt="需要查询的图片PID是?")
|
||||
async def _g(event: MessageEvent, state: T_State, pid: str = Arg("pid")):
|
||||
url = Config.get_config("hibiapi", "HIBIAPI") + "/api/pixiv/"
|
||||
if pid in ["取消", "算了"]:
|
||||
await pid_search.finish("已取消操作...")
|
||||
if not is_number(pid):
|
||||
await pid_search.reject_arg("pid", "笨蛋,重新输入数!字!")
|
||||
for _ in range(3):
|
||||
try:
|
||||
data = (
|
||||
await AsyncHttpx.get(
|
||||
url,
|
||||
params={"id": pid},
|
||||
timeout=5,
|
||||
)
|
||||
).json()
|
||||
except TimeoutError:
|
||||
pass
|
||||
except Exception as e:
|
||||
await pid_search.finish(f"发生了一些错误..{type(e)}:{e}")
|
||||
else:
|
||||
if data.get("error"):
|
||||
await pid_search.finish(data["error"]["user_message"], at_sender=True)
|
||||
data = data["illust"]
|
||||
if not data["width"] and not data["height"]:
|
||||
await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True)
|
||||
pid = data["id"]
|
||||
title = data["title"]
|
||||
author = data["user"]["name"]
|
||||
author_id = data["user"]["id"]
|
||||
image_list = []
|
||||
try:
|
||||
image_list.append(data["meta_single_page"]["original_image_url"])
|
||||
except KeyError:
|
||||
for image_url in data["meta_pages"]:
|
||||
image_list.append(image_url["image_urls"]["original"])
|
||||
for i, img_url in enumerate(image_list):
|
||||
img_url = change_pixiv_image_links(img_url)
|
||||
if not await AsyncHttpx.download_file(
|
||||
img_url,
|
||||
IMAGE_PATH / "temp" / f"pid_search_{event.user_id}_{i}.png",
|
||||
headers=headers,
|
||||
):
|
||||
await pid_search.send("图片下载失败了....", at_sender=True)
|
||||
tmp = ""
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
tmp = "\n【注】将在30后撤回......"
|
||||
msg_id = await pid_search.send(
|
||||
Message(
|
||||
f"title:{title}\n"
|
||||
f"pid:{pid}\n"
|
||||
f"author:{author}\n"
|
||||
f"author_id:{author_id}\n"
|
||||
f'{image(f"pid_search_{event.user_id}_{i}.png", "temp")}'
|
||||
f"{tmp}"
|
||||
)
|
||||
)
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, "
|
||||
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 查询图片 PID:{pid}"
|
||||
)
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
withdraw_message_manager.append((msg_id, 30))
|
||||
break
|
||||
else:
|
||||
await pid_search.finish("图片下载失败了....", at_sender=True)
|
||||
|
||||
@@ -20,9 +20,9 @@ try:
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
if str(platform.system()).lower() == "windows":
|
||||
policy = asyncio.WindowsSelectorEventLoopPolicy()
|
||||
asyncio.set_event_loop_policy(policy)
|
||||
# if str(platform.system()).lower() == "windows":
|
||||
# policy = asyncio.WindowsSelectorEventLoopPolicy()
|
||||
# asyncio.set_event_loop_policy(policy)
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;"
|
||||
|
||||
@@ -139,15 +139,14 @@ class OmegaPixivIllusts(db.Model):
|
||||
data = await cls.select('pid').gino.all()
|
||||
return [x[0] for x in data]
|
||||
|
||||
@classmethod
|
||||
async def test(cls, nsfw_tag: int = 1):
|
||||
if nsfw_tag is not None:
|
||||
query = cls.query.where(cls.nsfw_tag == nsfw_tag)
|
||||
else:
|
||||
query = cls.query
|
||||
query = query.where((cls.width - cls.height) < 50)
|
||||
for x in await query.gino.all():
|
||||
print(x.pid)
|
||||
# async def test(cls, nsfw_tag: int = 1):
|
||||
# if nsfw_tag is not None:
|
||||
# query = cls.query.where(cls.nsfw_tag == nsfw_tag)
|
||||
# else:
|
||||
# query = cls.query
|
||||
# query = query.where((cls.width - cls.height) < 50)
|
||||
# for x in await query.gino.all():
|
||||
# print(x.pid)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ usage:
|
||||
查看 pix 好康图库
|
||||
指令:
|
||||
pix ?*[tags]: 通过 tag 获取相似图片,不含tag时随机抽取
|
||||
pid [uid]: 通过uid获取图片
|
||||
pix pid[pid]: 查看图库中指定pid图片
|
||||
""".strip()
|
||||
__plugin_superuser_usage__ = """
|
||||
@@ -49,7 +50,9 @@ __plugin_configs__ = {
|
||||
"value": None,
|
||||
"help": "单次发送的图片数量达到指定值时转发为合并消息",
|
||||
"default_value": None,
|
||||
}
|
||||
},
|
||||
"ALLOW_GROUP_SETU": {"value": False, "help": "允许非超级用户使用-s参数", "default_value": False},
|
||||
"ALLOW_GROUP_R18": {"value": False, "help": "允许非超级用户使用-r参数", "default_value": False},
|
||||
}
|
||||
|
||||
|
||||
@@ -80,10 +83,13 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
nsfw_tag = 2
|
||||
else:
|
||||
nsfw_tag = 0
|
||||
if nsfw_tag != 0 and str(event.user_id) not in bot.config.superusers:
|
||||
await pix.finish("你不能看这些噢,这些都是是留给管理员看的...")
|
||||
if n := len(x) == 1 and is_number(x[0]):
|
||||
num = int(x[-1])
|
||||
if str(event.user_id) not in bot.config.superusers:
|
||||
if (nsfw_tag == 1 and not Config.get_config("pix", "ALLOW_GROUP_SETU")) or (
|
||||
nsfw_tag == 2 and not Config.get_config("pix", "ALLOW_GROUP_R18")
|
||||
):
|
||||
await pix.finish("你不能看这些噢,这些都是是留给管理员看的...")
|
||||
if n := len(x) == 1 and is_number(x[0]) and int(x[0]) < 100:
|
||||
num = int(x[0])
|
||||
keyword = ""
|
||||
elif n > 1:
|
||||
if is_number(x[-1]):
|
||||
|
||||
@@ -44,6 +44,8 @@ usage:
|
||||
搜图 樱岛麻衣
|
||||
搜图 樱岛麻衣 5
|
||||
搜图 樱岛麻衣 5 r18
|
||||
搜图 樱岛麻衣#1000users 5
|
||||
【多个关键词用#分割】
|
||||
【默认为 热度排序】
|
||||
【注意空格!!】【在线搜索会较慢】【数量可能不符?可能该页数量不够,也可能被R-18屏蔽】
|
||||
""".strip()
|
||||
@@ -161,8 +163,8 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
|
||||
info_list = None
|
||||
num = 10
|
||||
page = 1
|
||||
if (n := len(msg)) == 1:
|
||||
keyword = msg[0]
|
||||
if (n := len(msg)) > 0:
|
||||
keyword = msg[0].replace("#"," ")
|
||||
if n > 1:
|
||||
if not is_number(msg[1]):
|
||||
await pixiv_keyword.finish("图片数量必须是数字!", at_sender=True)
|
||||
|
||||
@@ -8,10 +8,10 @@ from services.log import logger
|
||||
from pathlib import Path
|
||||
import platform
|
||||
|
||||
if platform.system() == "Windows":
|
||||
import asyncio
|
||||
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
# if platform.system() == "Windows":
|
||||
# import asyncio
|
||||
#
|
||||
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
|
||||
headers = {
|
||||
|
||||
@@ -49,6 +49,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State, key_word: str = ArgSt
|
||||
Config.get_config("search_anime", "SEARCH_ANIME_MAX_INFO"),
|
||||
)
|
||||
if anime_report:
|
||||
if isinstance(anime_report, str):
|
||||
await search_anime.finish(anime_report)
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
mes_list = custom_forward_msg(anime_report, bot.self_id)
|
||||
await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list)
|
||||
|
||||
@@ -3,18 +3,18 @@ import feedparser
|
||||
from urllib import parse
|
||||
from services.log import logger
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
import time
|
||||
|
||||
|
||||
async def from_anime_get_info(key_word: str, max_: int) -> List[str]:
|
||||
async def from_anime_get_info(key_word: str, max_: int) -> Union[str, List[str]]:
|
||||
s_time = time.time()
|
||||
repass = ""
|
||||
url = "https://share.dmhy.org/topics/rss/rss.xml?keyword=" + parse.quote(key_word)
|
||||
try:
|
||||
repass = await get_repass(url, max_)
|
||||
except Exception as e:
|
||||
logger.error("Timeout! {}".format(e))
|
||||
logger.error(f"发生了一些错误 {type(e)}:{e}")
|
||||
return "发生了一些错误!"
|
||||
repass.insert(0, f"搜索 {key_word} 结果(耗时 {int(time.time() - s_time)} 秒):\n")
|
||||
return repass
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ async def get_saucenao_image(url: str) -> Union[str, List[str]]:
|
||||
}
|
||||
data = (await AsyncHttpx.post(API_URL_SAUCENAO, params=params)).json()
|
||||
if data["header"]["status"] != 0:
|
||||
return "Saucenao识图失败.."
|
||||
return f"Saucenao识图失败..status:{data['header']['status']}"
|
||||
data = data["results"]
|
||||
data = (
|
||||
data
|
||||
|
||||
@@ -4,7 +4,7 @@ from services.log import logger
|
||||
from models.sign_group_user import SignGroupUser
|
||||
from nonebot.message import run_postprocessor
|
||||
from nonebot.matcher import Matcher
|
||||
from typing import Optional, Type
|
||||
from typing import Optional, Type, Any
|
||||
from gino.exceptions import UninitializedError
|
||||
from utils.utils import (
|
||||
is_number,
|
||||
@@ -33,7 +33,7 @@ from .data_source import (
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from configs.config import Config, NICKNAME
|
||||
from utils.manager import withdraw_message_manager
|
||||
from nonebot.params import CommandArg, Command
|
||||
from nonebot.params import CommandArg, Command, RegexGroup
|
||||
from typing import Tuple
|
||||
import re
|
||||
|
||||
@@ -97,6 +97,7 @@ __plugin_configs__ = {
|
||||
},
|
||||
"TIMEOUT": {"value": 10, "help": "色图下载超时限制(秒)", "default_value": 10},
|
||||
"SHOW_INFO": {"value": True, "help": "是否显示色图的基本信息,如PID等", "default_value": True},
|
||||
"ALLOW_GROUP_R18": {"value": False, "help": "在群聊中启用R18权限", "default_value": False},
|
||||
}
|
||||
Config.add_plugin_config("pixiv", "PIXIV_NGINX_URL", "i.pixiv.re", help_="Pixiv反向代理")
|
||||
|
||||
@@ -148,10 +149,16 @@ async def _(
|
||||
if cmd[0] == "色图r" and isinstance(event, PrivateMessageEvent):
|
||||
r18 = 1
|
||||
num = 10
|
||||
elif cmd[0] == "色图r" and isinstance(event, GroupMessageEvent):
|
||||
await setu.finish(
|
||||
random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"])
|
||||
)
|
||||
elif (
|
||||
cmd[0] == "色图r"
|
||||
and isinstance(event, GroupMessageEvent)
|
||||
):
|
||||
if not Config.get_config("send_setu", "ALLOW_GROUP_R18"):
|
||||
await setu.finish(
|
||||
random.choice(["这种不好意思的东西怎么可能给这么多人看啦", "羞羞脸!给我滚出克私聊!", "变态变态变态变态大变态!"])
|
||||
)
|
||||
else:
|
||||
r18 = 1
|
||||
# 有 数字 的话先尝试本地色图id
|
||||
if msg and is_number(msg):
|
||||
setu_list, code = await get_setu_list(int(msg), r18=r18)
|
||||
@@ -190,7 +197,7 @@ num_key = {
|
||||
|
||||
|
||||
@setu_reg.handle()
|
||||
async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
impression = (
|
||||
await SignGroupUser.ensure(event.user_id, event.group_id)
|
||||
@@ -198,28 +205,15 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
luox = get_luoxiang(impression)
|
||||
if luox:
|
||||
await setu.finish(luox, at_sender=True)
|
||||
msg = arg.extract_plain_text().strip()
|
||||
num = 1
|
||||
msg = re.search(r"(.*)[份发张个次点](.*)[瑟涩色]图", msg)
|
||||
# 解析 tags 以及 num
|
||||
if msg:
|
||||
num = msg.group(1)
|
||||
tags = msg.group(2)
|
||||
if tags:
|
||||
tags = tags[:-1] if tags[-1] == "的" else tags
|
||||
if num:
|
||||
num = num[-1]
|
||||
if num_key.get(num):
|
||||
num = num_key[num]
|
||||
elif is_number(num):
|
||||
try:
|
||||
num = int(num)
|
||||
except ValueError:
|
||||
num = 1
|
||||
else:
|
||||
num = 1
|
||||
else:
|
||||
return
|
||||
num, tags = reg_group
|
||||
num = num or 1
|
||||
tags = tags[:-1] if tags and tags[-1] == "的" else tags
|
||||
if num_key.get(num):
|
||||
num = num_key[num]
|
||||
try:
|
||||
num = int(num)
|
||||
except ValueError:
|
||||
num = 1
|
||||
await send_setu_handle(setu_reg, event, "色图", tags, num, 0)
|
||||
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ async def update_setu_img(flag: bool = False):
|
||||
f"--> /{path}/{image.local_id}.jpg"
|
||||
)
|
||||
os.rename(
|
||||
TEMP_PATH / f"/{image.local_id}.jpg",
|
||||
TEMP_PATH / f"{image.local_id}.jpg",
|
||||
path / f"{image.local_id}.jpg",
|
||||
)
|
||||
except FileNotFoundError:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import Tuple, Any
|
||||
|
||||
from .group_user_checkin import (
|
||||
group_user_check_in,
|
||||
group_user_check,
|
||||
@@ -8,9 +10,9 @@ from .group_user_checkin import (
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from utils.message_builder import image
|
||||
from nonebot import on_command
|
||||
from nonebot import on_command, on_regex
|
||||
from utils.utils import scheduler
|
||||
from nonebot.params import CommandArg
|
||||
from nonebot.params import CommandArg, RegexGroup
|
||||
from pathlib import Path
|
||||
from configs.path_config import DATA_PATH
|
||||
from services.log import logger
|
||||
@@ -68,7 +70,7 @@ except (FileNotFoundError, ValueError, TypeError):
|
||||
data = {"0": []}
|
||||
|
||||
|
||||
sign = on_command("签到", priority=5, permission=GROUP, block=True)
|
||||
sign = on_regex("^签到(all)?$", priority=5, permission=GROUP, block=True)
|
||||
my_sign = on_command(
|
||||
cmd="我的签到", aliases={"好感度"}, priority=5, permission=GROUP, block=True
|
||||
)
|
||||
@@ -85,13 +87,13 @@ total_sign_rank = on_command(
|
||||
|
||||
|
||||
@sign.handle()
|
||||
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
async def _(event: GroupMessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
nickname = event.sender.card or event.sender.nickname
|
||||
await sign.send(
|
||||
await group_user_check_in(nickname, event.user_id, event.group_id),
|
||||
at_sender=True,
|
||||
)
|
||||
if arg.extract_plain_text().strip() == "all":
|
||||
if reg_group[0]:
|
||||
await check_in_all(nickname, event.user_id)
|
||||
|
||||
|
||||
|
||||
+13
-16
@@ -55,13 +55,10 @@ async def get_card(
|
||||
date = datetime.now().date()
|
||||
_type = "view" if is_card_view else "sign"
|
||||
card_file = (
|
||||
Path(SIGN_TODAY_CARD_PATH)
|
||||
/ f"{user_id}_{user.group_id}_{_type}_{date}.png"
|
||||
Path(SIGN_TODAY_CARD_PATH) / f"{user_id}_{user.group_id}_{_type}_{date}.png"
|
||||
)
|
||||
if card_file.exists():
|
||||
return image(
|
||||
f"{user_id}_{user.group_id}_{_type}_{date}.png", "sign/today_card"
|
||||
)
|
||||
return image(f"{user_id}_{user.group_id}_{_type}_{date}.png", "sign/today_card")
|
||||
else:
|
||||
if add_impression == -1:
|
||||
card_file = (
|
||||
@@ -75,9 +72,7 @@ async def get_card(
|
||||
)
|
||||
is_card_view = True
|
||||
ava = BytesIO(await get_user_avatar(user_id))
|
||||
uid = await GroupInfoUser.get_group_member_uid(
|
||||
user.user_qq, user.group_id
|
||||
)
|
||||
uid = await GroupInfoUser.get_group_member_uid(user.user_qq, user.group_id)
|
||||
impression_list = None
|
||||
if is_card_view:
|
||||
_, impression_list, _ = await SignGroupUser.get_all_impression(
|
||||
@@ -128,9 +123,13 @@ def _generate_card(
|
||||
level, next_impression, previous_impression = get_level_and_next_impression(
|
||||
user.impression
|
||||
)
|
||||
interpolation = next_impression - user.impression
|
||||
if level == "9":
|
||||
level = "8"
|
||||
interpolation = 0
|
||||
info_img.text((0, 0), f"· 好感度等级:{level} [{lik2relation[level]}]")
|
||||
info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[level]}")
|
||||
info_img.text((0, 40), f"· 距离升级还差 {next_impression - user.impression:.2f} 好感度")
|
||||
info_img.text((0, 40), f"· 距离升级还差 {interpolation:.2f} 好感度")
|
||||
|
||||
bar_bk = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png")
|
||||
bar = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar.png")
|
||||
@@ -240,7 +239,9 @@ def _generate_card(
|
||||
f"上次签到日期:{'从未' if user.checkin_time_last == datetime.min else user.checkin_time_last.date()}",
|
||||
)
|
||||
today_data.text((0, 25), f"总金币:{gold}")
|
||||
default_setu_prob = Config.get_config("send_setu", "INITIAL_SETU_PROBABILITY") * 100
|
||||
default_setu_prob = (
|
||||
Config.get_config("send_setu", "INITIAL_SETU_PROBABILITY") * 100
|
||||
)
|
||||
today_data.text(
|
||||
(0, 50),
|
||||
f"色图概率:{(default_setu_prob + user.impression if user.impression < 100 else 100):.2f}%",
|
||||
@@ -286,12 +287,8 @@ def _generate_card(
|
||||
bk.paste(today_sign_text_img, (550, 180), True)
|
||||
bk.paste(today_data, (580, 220), True)
|
||||
bk.paste(watermark, (15, 400), True)
|
||||
bk.save(
|
||||
SIGN_TODAY_CARD_PATH / f"{user_id}_{user.group_id}_{_type}_{data}.png"
|
||||
)
|
||||
return image(
|
||||
f"{user_id}_{user.group_id}_{_type}_{data}.png", "sign/today_card"
|
||||
)
|
||||
bk.save(SIGN_TODAY_CARD_PATH / f"{user_id}_{user.group_id}_{_type}_{data}.png")
|
||||
return image(f"{user_id}_{user.group_id}_{_type}_{data}.png", "sign/today_card")
|
||||
|
||||
|
||||
def generate_progress_bar_pic():
|
||||
|
||||
@@ -96,7 +96,7 @@ async def _(
|
||||
global _prefix_count_dict
|
||||
if (
|
||||
matcher.type == "message"
|
||||
and matcher.priority not in [1, 9]
|
||||
and matcher.priority not in [1, 999]
|
||||
and matcher.plugin_name not in ["update_info", "statistics_handle"]
|
||||
):
|
||||
module = matcher.plugin_name
|
||||
|
||||
@@ -5,8 +5,8 @@ import asyncio
|
||||
import platform
|
||||
import os
|
||||
|
||||
if platform.system() == "Windows":
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
# if platform.system() == "Windows":
|
||||
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
|
||||
url = "https://github.com/Mrs4s/go-cqhttp/releases"
|
||||
|
||||
@@ -12,7 +12,7 @@ __plugin_usage__ = """
|
||||
usage:
|
||||
普普通通的查天气吧
|
||||
指令:
|
||||
[城市]天气/天气[城市]
|
||||
[城市]天气
|
||||
""".strip()
|
||||
__plugin_des__ = "出门要看看天气,不要忘了带伞"
|
||||
__plugin_cmd__ = ["[城市]天气/天气[城市]"]
|
||||
@@ -27,13 +27,13 @@ __plugin_settings__ = {
|
||||
}
|
||||
|
||||
|
||||
weather = on_regex(r".{0,10}?(.*)的?天气.*?.{0,10}", priority=5, block=True)
|
||||
weather = on_regex(r".{0,10}?(.*)的?天气.{0,10}", priority=5, block=True)
|
||||
|
||||
|
||||
@weather.handle()
|
||||
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
msg = reg_group[0]
|
||||
if msg[-1] != "市":
|
||||
if msg and msg[-1] != "市":
|
||||
msg += "市"
|
||||
city = ""
|
||||
if msg:
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message, Bot
|
||||
from nonebot.internal.params import ArgStr, Arg
|
||||
from nonebot.params import CommandArg
|
||||
|
||||
from .data_source import get_anime
|
||||
from nonebot import on_command
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.params import Arg
|
||||
from utils.utils import get_message_img
|
||||
from services.log import logger
|
||||
|
||||
@@ -31,10 +33,10 @@ what_anime = on_command("识番", priority=5, block=True)
|
||||
|
||||
|
||||
@what_anime.handle()
|
||||
async def _(bot: Bot, event: MessageEvent, state: T_State):
|
||||
async def _(bot: Bot, event: MessageEvent, state: T_State, args: Message = CommandArg()):
|
||||
img_url = get_message_img(event.json())
|
||||
if img_url:
|
||||
state["img_url"] = img_url[0]
|
||||
state["img_url"] = args
|
||||
|
||||
|
||||
@what_anime.got("img_url", prompt="虚空识番?来图来图GKD")
|
||||
|
||||
@@ -7,9 +7,8 @@ class WordBankBuilder:
|
||||
def __init__(self, user_id: int, group_id: int, problem: str):
|
||||
self._data = {
|
||||
"user_id": user_id,
|
||||
"group_id": group_id,
|
||||
"problem": problem
|
||||
}
|
||||
"group_id": group_id}
|
||||
self.problem = problem
|
||||
|
||||
def set_placeholder(self, id_: int, placeholder: Union[str, int]):
|
||||
"""
|
||||
@@ -28,13 +27,20 @@ class WordBankBuilder:
|
||||
"""
|
||||
self._data["answer"] = answer
|
||||
|
||||
async def save(self,search_type):
|
||||
def set_problem(self, problem: str):
|
||||
"""
|
||||
设置问题
|
||||
:param problem: 问题
|
||||
"""
|
||||
self._data["problem"] = problem
|
||||
|
||||
async def save(self, search_type):
|
||||
user_id = self._data["user_id"]
|
||||
group_id = self._data["group_id"]
|
||||
problem = self._data["problem"]
|
||||
answer = self._data["answer"]
|
||||
placeholder = self._data.get("placeholder")
|
||||
return await WordBank.add_problem_answer(user_id, group_id, search_type,problem, answer, placeholder)
|
||||
return await WordBank.add_problem_answer(user_id, group_id, search_type, problem, answer, placeholder)
|
||||
|
||||
async def update(self, index):
|
||||
user_id = self._data["user_id"]
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import re
|
||||
|
||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Event
|
||||
from utils.utils import get_message_text
|
||||
from utils.utils import get_message_img_file
|
||||
from nonebot.typing import T_State
|
||||
from .model import WordBank
|
||||
|
||||
|
||||
async def check(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
msg = event.raw_message
|
||||
list_img = get_message_img_file(event.json())
|
||||
if list_img:
|
||||
for img_file in list_img:
|
||||
strinfo = re.compile(f"{img_file},subType=\d*]")
|
||||
msg = strinfo.sub(f'{img_file}]', msg)
|
||||
return bool(
|
||||
await WordBank.check(event.group_id, get_message_text(event.json()), event.is_tome())
|
||||
await WordBank.check(event.group_id, msg, event.is_tome())
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -3,9 +3,11 @@ from ._rule import check
|
||||
from .model import WordBank
|
||||
from configs.path_config import DATA_PATH
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from utils.utils import get_message_text
|
||||
from utils.utils import get_message_at, get_message_img
|
||||
from nonebot import on_message
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from utils.utils import get_message_img_file, is_number
|
||||
import re
|
||||
|
||||
__zx_plugin_name__ = "词库问答回复操作 [Hidden]"
|
||||
|
||||
@@ -17,8 +19,14 @@ message_handle = on_message(priority=6, block=True, rule=check)
|
||||
|
||||
@message_handle.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
msg = event.raw_message
|
||||
list_img = get_message_img_file(event.json())
|
||||
if list_img:
|
||||
for img_file in list_img:
|
||||
strinfo = re.compile(f"{img_file},subType=\d*]")
|
||||
msg = strinfo.sub(f'{img_file}]', msg)
|
||||
q = await WordBank.check(
|
||||
event.group_id, get_message_text(event.json()), event.is_tome()
|
||||
event.group_id, msg, event.is_tome()
|
||||
)
|
||||
await message_handle.send(await get_one_answer(event, q.format, q.answer))
|
||||
|
||||
@@ -54,3 +62,68 @@ async def get_one_answer(event, format, _answer, all=1):
|
||||
answer += _a[: _a.find(f"[__placeholder_{idx}]")] + "@" + q.user_name
|
||||
_a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):]
|
||||
return answer + _a
|
||||
|
||||
|
||||
# 处理单条问题
|
||||
async def get_one_problem(event, problem):
|
||||
strinfo = re.compile(f",subType=\d")
|
||||
problem = strinfo.sub('', problem)
|
||||
_problem = problem
|
||||
_p = problem
|
||||
problem = ''
|
||||
for img in get_message_img(event.json()):
|
||||
_x = img.split("?")[0]
|
||||
r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", _p)
|
||||
if r:
|
||||
_problem = _problem.replace(
|
||||
rf",url={img}",
|
||||
f"",
|
||||
)
|
||||
problem += _p[: _p.find(f"[CQ:image,file={r.group(1)},url={img}]")] + image(img)
|
||||
_p = _p[
|
||||
_p.find(f"[CQ:image,file={r.group(1)},url={img}]") + len(f"[CQ:image,file={r.group(1)},url={img}]"):]
|
||||
for at_ in get_message_at(event.json()):
|
||||
r = re.search(rf"\[CQ:at,qq={at_}]", problem)
|
||||
if r:
|
||||
q = await GroupInfoUser.get_member_info(
|
||||
int(at_), event.group_id)
|
||||
problem += _p[: _p.find(f"[CQ:at,qq={at_}]")] + "@" + q.user_name
|
||||
_p = _p[_p.find(f"[CQ:at,qq={at_}]") + len(f"[CQ:at,qq={at_}]"):]
|
||||
return _problem, problem + _p
|
||||
|
||||
|
||||
# 显示单条数据库问题
|
||||
async def get_one_image_problem(event, problem):
|
||||
path = data_dir / f"{event.group_id}" / "problem"
|
||||
placeholder_list = []
|
||||
idx = 0
|
||||
img_list = re.findall(rf"\[CQ:image,file=(.*?)]", problem)
|
||||
at_list = re.findall(rf"\[CQ:at,qq=(.*?)]", problem)
|
||||
if img_list:
|
||||
for img in img_list:
|
||||
problem = problem.replace(f'[CQ:image,file={img}]', f'[__placeholder_{idx}]', 1)
|
||||
placeholder_list.append([idx, img])
|
||||
idx += 1
|
||||
if at_list:
|
||||
for ats in at_list:
|
||||
problem = problem.replace(f'[CQ:at,qq={ats}]', f'[__placeholder_{idx}]', 1)
|
||||
placeholder_list.append([idx, ats])
|
||||
idx += 1
|
||||
_p = problem
|
||||
problem = ''
|
||||
if not placeholder_list:
|
||||
problem = _p
|
||||
return problem
|
||||
else:
|
||||
for idx, placeholder in placeholder_list:
|
||||
if is_number(placeholder):
|
||||
q = await GroupInfoUser.get_member_info(
|
||||
int(placeholder), event.group_id)
|
||||
problem += _p[: _p.find(f"[__placeholder_{idx}]")] + "@" + q.user_name
|
||||
else:
|
||||
problem += _p[: _p.find(f"[__placeholder_{idx}]")] + image(
|
||||
path / f"{placeholder}.jpg"
|
||||
)
|
||||
_p = _p[_p.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):]
|
||||
|
||||
return problem + _p
|
||||
|
||||
@@ -108,11 +108,6 @@ class WordBank(db.Model):
|
||||
:param group_id: 群号
|
||||
:param problem: 问题
|
||||
"""
|
||||
if problem.startswith("id:"):
|
||||
problem_index = int(problem.split(":")[-1])
|
||||
q = await cls.get_group_all_problem(group_id)
|
||||
if len(q) > problem_index:
|
||||
problem = q[problem_index]
|
||||
q = await cls.query.where(
|
||||
(cls.group_id == group_id) & (cls.problem == problem)
|
||||
).gino.all()
|
||||
|
||||
@@ -6,7 +6,7 @@ from utils.http_utils import AsyncHttpx
|
||||
from ._data_source import WordBankBuilder
|
||||
from utils.message_builder import image
|
||||
from utils.image_utils import text2image
|
||||
from .message_handle import get_one_answer
|
||||
from .message_handle import get_one_answer, get_one_problem, get_one_image_problem
|
||||
from .model import WordBank
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
@@ -19,6 +19,7 @@ import random
|
||||
import os
|
||||
import re
|
||||
from configs.config import NICKNAME, Config
|
||||
from models.group_member_info import GroupInfoUser
|
||||
|
||||
__zx_plugin_name__ = "词库问答 [Admin]"
|
||||
__plugin_usage__ = """
|
||||
@@ -26,7 +27,7 @@ usage:
|
||||
对指定问题的随机回答,对相同问题可以设置多个不同回答
|
||||
删除词条后每个词条的id可能会变化,请查看后再删除
|
||||
指令:
|
||||
添加词条 ?[模糊/关键字]...答...:添加问答词条,可重复添加相同问题的不同回答
|
||||
添加词条 ?[模糊/关键字|词]...答...:添加问答词条,可重复添加相同问题的不同回答
|
||||
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
|
||||
修改词条 [问题/下标] ?[下标/新回答] [新回答]:修改指定词条指定回答默认修改为第一条
|
||||
查看词条 ?[问题/下标]:查看全部词条或对应词条回答
|
||||
@@ -48,8 +49,8 @@ __plugin_cmd__ = [
|
||||
"修改词条 [问题/下标] ?[下标/新回答] [新回答]",
|
||||
"查看词条 ?[问题/下标]",
|
||||
]
|
||||
__plugin_version__ = 0.2
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_version__ = 0.3
|
||||
__plugin_author__ = "HibiKier & yajiwa"
|
||||
__plugin_settings__ = {
|
||||
"admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL [LEVEL]"),
|
||||
"cmd": ["词库问答", "添加词条", "删除词条", "修改词条", "查看词条"],
|
||||
@@ -87,21 +88,21 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State, arg: Message = C
|
||||
else:
|
||||
_problem = problem
|
||||
search_type = 0
|
||||
if re.search("^关键字(.*)", msg):
|
||||
if re.search("^关键字|词(.*)", msg):
|
||||
search_type = 1
|
||||
elif re.search("^模糊(.*)", msg):
|
||||
search_type = 2
|
||||
_builder = await get__builder(event, _problem, answer, idx)
|
||||
if await _builder.save(search_type):
|
||||
logger.info(f"已保存词条 问:{problem} 答:{msg}")
|
||||
await add_word.send(f"已保存词条:{problem}")
|
||||
logger.info(f"已保存词条 问:{_builder.problem} 答:{msg}")
|
||||
await add_word.send("已保存词条:" + _builder.problem)
|
||||
else:
|
||||
await delete_word.send("保存失败,可能是回答重复")
|
||||
|
||||
|
||||
@delete_word.handle()
|
||||
async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
msg = str(arg).strip()
|
||||
if not msg:
|
||||
await delete_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
|
||||
index = None
|
||||
@@ -119,17 +120,18 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
if p:
|
||||
problem = p[int(x)]
|
||||
try:
|
||||
_problem, problem = await get_one_problem(event, problem)
|
||||
if answer := await WordBank.delete_problem_answer(
|
||||
event.user_id, event.group_id, problem, index
|
||||
event.user_id, event.group_id, _problem, index
|
||||
):
|
||||
await delete_word.send(f"删除词条成功:{problem}\n回答:\n{answer}")
|
||||
await delete_word.send("删除词条成功:" + problem + f"\n回答:\n{answer}")
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, GROUP "
|
||||
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 删除词条: {problem}"
|
||||
)
|
||||
else:
|
||||
await delete_word.send(f"删除词条:{problem} 失败,可能该词条不存在")
|
||||
await delete_word.send("删除词条:" + problem + "失败,可能该词条不存在")
|
||||
except IndexError:
|
||||
await delete_word.send("指定下标错误...请通过查看词条来确定..")
|
||||
|
||||
@@ -165,21 +167,21 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
|
||||
try:
|
||||
if await _builder.update(index):
|
||||
await update_word.send(f"修改词条成功:{problem}")
|
||||
await update_word.send(f"修改词条成功:" + _builder.problem)
|
||||
logger.info(
|
||||
f"(USER {event.user_id}, GROUP "
|
||||
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
|
||||
f" 修改词条: {problem}"
|
||||
)
|
||||
else:
|
||||
await update_word.send(f"修改词条:{problem} 失败,可能该词条不存在")
|
||||
await update_word.send(f"修改词条:" + _builder.problem + f"失败,可能该词条不存在")
|
||||
except IndexError:
|
||||
await update_word.send("指定下标错误...请通过查看词条来确定..")
|
||||
|
||||
|
||||
@show_word.handle()
|
||||
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip()
|
||||
msg = str(arg).strip()
|
||||
if not msg:
|
||||
_problem_list = await WordBank.get_group_all_problem(event.group_id)
|
||||
if not _problem_list:
|
||||
@@ -225,17 +227,28 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
)
|
||||
)
|
||||
else:
|
||||
_answer_list = await WordBank.get_group_all_answer(event.group_id, msg)
|
||||
_answer_list = []
|
||||
if msg.startswith("id:"):
|
||||
x = msg.split(":")[-1]
|
||||
if not is_number(x) or int(x) < 0:
|
||||
return await delete_word.finish("id必须为数字且符合规范!")
|
||||
p = await WordBank.get_group_all_problem(event.group_id)
|
||||
if p:
|
||||
_problem = p[int(x)]
|
||||
_answer_list = await WordBank.get_group_all_answer(event.group_id, _problem)
|
||||
msg += '问' + await get_one_image_problem(event, _problem)
|
||||
else:
|
||||
_problem, msg = await get_one_problem(event, msg)
|
||||
_answer_list = await WordBank.get_group_all_answer(event.group_id, _problem)
|
||||
if not _answer_list:
|
||||
await show_word.send("未收录该词条...")
|
||||
|
||||
else:
|
||||
|
||||
# 解析图片和@
|
||||
_answer_img_nu_list = [await get_one_answer(event, format, answer, 0) for answer, format in _answer_list]
|
||||
word_nu = len(_answer_img_nu_list)
|
||||
img_nu = 0
|
||||
answer = f"词条 {msg} 回答:"
|
||||
answer = "词条" + msg + "回答:"
|
||||
for i, x, in enumerate(_answer_img_nu_list):
|
||||
r = re.findall(rf"\[CQ:image,file=", str(x))
|
||||
if r:
|
||||
@@ -254,15 +267,25 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
|
||||
|
||||
async def get__builder(event, _problem, answer, idx):
|
||||
(data_dir / f"{event.group_id}").mkdir(exist_ok=True, parents=True)
|
||||
(data_dir / f"{event.group_id}" / "problem").mkdir(exist_ok=True, parents=True)
|
||||
_builder = WordBankBuilder(event.user_id, event.group_id, _problem)
|
||||
problem = ''
|
||||
_p = _problem
|
||||
for at_ in get_message_at(event.json()):
|
||||
r = re.search(rf"\[CQ:at,qq={at_}]", answer)
|
||||
if r:
|
||||
answer = answer.replace(f"[CQ:at,qq={at_}]", f"[__placeholder_{idx}]", 1)
|
||||
_builder.set_placeholder(idx, at_)
|
||||
idx += 1
|
||||
r_problem = re.search(rf"\[CQ:at,qq={at_}]", _problem)
|
||||
if r_problem:
|
||||
q = await GroupInfoUser.get_member_info(
|
||||
int(at_), event.group_id)
|
||||
problem += _p[: _p.find(f"[CQ:at,qq={at_}]")] + "@" + q.user_name
|
||||
_p = _p[_p.find(f"[CQ:at,qq={at_}]") + len(f"[CQ:at,qq={at_}]"):]
|
||||
for img in get_message_img(event.json()):
|
||||
_x = img.split("?")[0]
|
||||
_x_list = img.split("?")
|
||||
r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", answer)
|
||||
if r:
|
||||
rand = random.randint(1, 10000) + random.randint(1, 114514)
|
||||
@@ -270,16 +293,26 @@ async def get__builder(event, _problem, answer, idx):
|
||||
if f"__placeholder_{rand}_{idx}.jpg" not in os.listdir(data_dir / f"{event.group_id}"):
|
||||
break
|
||||
rand = random.randint(1, 10000) + random.randint(1, 114514)
|
||||
for i in range(3):
|
||||
answer = answer.replace(f",subType={i}", "")
|
||||
answer = answer.replace(
|
||||
rf"[CQ:image,file={r.group(1)},url={img}]",
|
||||
f"[__placeholder_{idx}]",
|
||||
)
|
||||
strinfo = re.compile(f"\[CQ:image,file={r.group(1)},url={_x_list[0]}\?{_x_list[1]},subType=\d*?]")
|
||||
answer = strinfo.sub(f"[__placeholder_{idx}]", answer)
|
||||
await AsyncHttpx.download_file(
|
||||
img, data_dir / f"{event.group_id}" / f"__placeholder_{rand}_{idx}.jpg"
|
||||
)
|
||||
_builder.set_placeholder(idx, f"__placeholder_{rand}_{idx}.jpg")
|
||||
idx += 1
|
||||
r_problem = re.search(rf"\[CQ:image,file=(.*?),url={_x}.*?]", _p)
|
||||
if r_problem:
|
||||
strinfo = re.compile(f",url={_x_list[0]}\?{_x_list[1]},subType=\d*?]")
|
||||
_problem = strinfo.sub(f"]", _problem)
|
||||
_p = strinfo.sub(f"]", _p)
|
||||
problem += _p[: _p.find(f"[CQ:image,file={r_problem.group(1)}]")] + image(img)
|
||||
_p = _p[_p.find(f"[CQ:image,file={r_problem.group(1)}]") + len(f"[CQ:image,file={r_problem.group(1)}]"):]
|
||||
problem_img = r_problem.group(1)
|
||||
if f"{problem_img}.jpg" not in os.listdir(data_dir / f"{event.group_id}" / f"problem"):
|
||||
await AsyncHttpx.download_file(
|
||||
img, data_dir / f"{event.group_id}" / f"problem" / f"{problem_img}.jpg"
|
||||
)
|
||||
_builder.set_answer(answer)
|
||||
_builder.set_problem(_problem)
|
||||
_builder.problem = problem + _p
|
||||
return _builder
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Tuple, Union
|
||||
import pytz
|
||||
from nonebot import on_command, get_driver
|
||||
from nonebot.adapters.onebot.v11 import Message, MessageSegment
|
||||
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import Arg, Command, CommandArg, Depends
|
||||
from nonebot.typing import T_State
|
||||
from .data_source import draw_word_cloud, get_list_msg
|
||||
from configs.config import Config
|
||||
|
||||
__zx_plugin_name__ = "词云"
|
||||
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
词云
|
||||
指令:
|
||||
今日词云:获取今天的词云
|
||||
昨日词云:获取昨天的词云
|
||||
本周词云:获取本周词云
|
||||
本月词云:获取本月词云
|
||||
年度词云:获取年度词云
|
||||
|
||||
历史词云(支持 ISO8601 格式的日期与时间,如 2022-02-22T22:22:22)
|
||||
获取某日的词云
|
||||
历史词云 2022-01-01
|
||||
获取指定时间段的词云
|
||||
历史词云
|
||||
示例:历史词云 2022-01-01~2022-02-22
|
||||
示例:历史词云 2022-02-22T11:11:11~2022-02-22T22:22:22
|
||||
|
||||
如果想要获取自己的发言,可在命令前添加 我的
|
||||
示例:我的今日词云
|
||||
""".strip()
|
||||
__plugin_des__ = "词云"
|
||||
__plugin_cmd__ = ["今日词云", "昨日词云", "本周词云"]
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "yajiwa"
|
||||
__plugin_settings__ = {
|
||||
"level": 5,
|
||||
"default_status": True,
|
||||
"limit_superuser": False,
|
||||
"cmd": __plugin_cmd__,
|
||||
}
|
||||
wordcloud_cmd = on_command(
|
||||
"wordcloud",
|
||||
aliases={
|
||||
"词云",
|
||||
"今日词云",
|
||||
"昨日词云",
|
||||
"本周词云",
|
||||
"本月词云",
|
||||
"年度词云",
|
||||
"历史词云",
|
||||
"我的今日词云",
|
||||
"我的昨日词云",
|
||||
"我的本周词云",
|
||||
"我的本月词云",
|
||||
"我的年度词云",
|
||||
"我的历史词云",
|
||||
}, block=True, priority=5
|
||||
)
|
||||
Config.add_plugin_config(
|
||||
"word_clouds",
|
||||
"WORD_CLOUDS_TEMPLATE",
|
||||
1,
|
||||
help_="词云模板 参1:图片生成,默认使用真寻图片,可在项目路径resources/image/wordcloud下配置图片,多张则随机 | 参2/其他:黑底图片"
|
||||
)
|
||||
|
||||
|
||||
def parse_datetime(key: str):
|
||||
"""解析数字,并将结果存入 state 中"""
|
||||
|
||||
async def _key_parser(
|
||||
matcher: Matcher,
|
||||
state: T_State,
|
||||
input_: Union[datetime, Message] = Arg(key),
|
||||
):
|
||||
if isinstance(input_, datetime):
|
||||
return
|
||||
|
||||
plaintext = input_.extract_plain_text()
|
||||
try:
|
||||
state[key] = get_datetime_fromisoformat_with_timezone(plaintext)
|
||||
except ValueError:
|
||||
await matcher.reject_arg(key, "请输入正确的日期,不然我没法理解呢!")
|
||||
|
||||
return _key_parser
|
||||
|
||||
|
||||
def get_datetime_now_with_timezone() -> datetime:
|
||||
"""获取当前时间,并包含时区信息"""
|
||||
return datetime.now().astimezone()
|
||||
|
||||
|
||||
def get_datetime_fromisoformat_with_timezone(date_string: str) -> datetime:
|
||||
"""从 iso8601 格式字符串中获取时间,并包含时区信息"""
|
||||
return datetime.fromisoformat(date_string).astimezone()
|
||||
|
||||
|
||||
@wordcloud_cmd.handle()
|
||||
async def handle_first_receive(
|
||||
event: GroupMessageEvent,
|
||||
state: T_State,
|
||||
commands: Tuple[str, ...] = Command(),
|
||||
args: Message = CommandArg(),
|
||||
):
|
||||
command = commands[0]
|
||||
|
||||
if command.startswith("我的"):
|
||||
state["my"] = True
|
||||
command = command[2:]
|
||||
else:
|
||||
state["my"] = False
|
||||
|
||||
if command == "今日词云":
|
||||
dt = get_datetime_now_with_timezone()
|
||||
state["start"] = dt.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
state["stop"] = dt
|
||||
elif command == "昨日词云":
|
||||
dt = get_datetime_now_with_timezone()
|
||||
state["stop"] = dt.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
state["start"] = state["stop"] - timedelta(days=1)
|
||||
elif command == "本周词云":
|
||||
dt = get_datetime_now_with_timezone()
|
||||
state["start"] = dt.replace(
|
||||
hour=0, minute=0, second=0, microsecond=0
|
||||
) - timedelta(days=dt.weekday())
|
||||
state["stop"] = dt
|
||||
elif command == "本月词云":
|
||||
dt = get_datetime_now_with_timezone()
|
||||
state["start"] = dt.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
state["stop"] = dt
|
||||
elif command == "年度词云":
|
||||
dt = get_datetime_now_with_timezone()
|
||||
state["start"] = dt.replace(
|
||||
month=1, day=1, hour=0, minute=0, second=0, microsecond=0
|
||||
)
|
||||
state["stop"] = dt
|
||||
elif command == "历史词云":
|
||||
plaintext = args.extract_plain_text().strip()
|
||||
match = re.match(r"^(.+?)(?:~(.+))?$", plaintext)
|
||||
if match:
|
||||
start = match.group(1)
|
||||
stop = match.group(2)
|
||||
try:
|
||||
state["start"] = get_datetime_fromisoformat_with_timezone(start)
|
||||
if stop:
|
||||
state["stop"] = get_datetime_fromisoformat_with_timezone(stop)
|
||||
else:
|
||||
# 如果没有指定结束日期,则认为是指查询这一天的词云
|
||||
state["start"] = state["start"].replace(
|
||||
hour=0, minute=0, second=0, microsecond=0
|
||||
)
|
||||
state["stop"] = state["start"] + timedelta(days=1)
|
||||
except ValueError:
|
||||
await wordcloud_cmd.finish("请输入正确的日期,不然我没法理解呢!")
|
||||
else:
|
||||
await wordcloud_cmd.finish()
|
||||
|
||||
|
||||
@wordcloud_cmd.got(
|
||||
"start",
|
||||
prompt="请输入你要查询的起始日期(如 2022-01-01)",
|
||||
parameterless=[Depends(parse_datetime("start"))],
|
||||
)
|
||||
@wordcloud_cmd.got(
|
||||
"stop",
|
||||
prompt="请输入你要查询的结束日期(如 2022-02-22)",
|
||||
parameterless=[Depends(parse_datetime("stop"))],
|
||||
)
|
||||
async def handle_message(
|
||||
event: GroupMessageEvent,
|
||||
start: datetime = Arg(),
|
||||
stop: datetime = Arg(),
|
||||
my: bool = Arg(),
|
||||
):
|
||||
# 是否只查询自己的记录
|
||||
if my:
|
||||
user_id = int(event.user_id)
|
||||
else:
|
||||
user_id = None
|
||||
# 将时间转换到 东八 时区
|
||||
messages = await get_list_msg(user_id, int(event.group_id),
|
||||
days=(start.astimezone(pytz.timezone("Asia/Shanghai")),
|
||||
stop.astimezone(pytz.timezone("Asia/Shanghai"))))
|
||||
if messages:
|
||||
image_bytes = await draw_word_cloud(messages, get_driver().config)
|
||||
if image_bytes:
|
||||
await wordcloud_cmd.finish(MessageSegment.image(image_bytes), at_sender=my)
|
||||
else:
|
||||
await wordcloud_cmd.finish("生成词云失败", at_sender=my)
|
||||
else:
|
||||
await wordcloud_cmd.finish("没有获取到词云数据", at_sender=my)
|
||||
@@ -0,0 +1,129 @@
|
||||
import asyncio
|
||||
import os
|
||||
import random
|
||||
import jieba.analyse
|
||||
import re
|
||||
from typing import List
|
||||
from PIL import Image as IMG
|
||||
import jieba
|
||||
from emoji import replace_emoji # type: ignore
|
||||
from wordcloud import WordCloud, ImageColorGenerator
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from io import BytesIO
|
||||
from configs.path_config import IMAGE_PATH, FONT_PATH
|
||||
from services import logger
|
||||
from utils.http_utils import AsyncHttpx
|
||||
from models.chat_history import ChatHistory
|
||||
from configs.config import Config
|
||||
|
||||
|
||||
async def pre_precess(msg: List[str], config) -> str:
|
||||
return await asyncio.get_event_loop().run_in_executor(
|
||||
None, _pre_precess, msg, config
|
||||
)
|
||||
|
||||
|
||||
def _pre_precess(msg: List[str], config) -> str:
|
||||
"""对消息进行预处理"""
|
||||
# 过滤掉命令
|
||||
command_start = tuple([i for i in config.command_start if i])
|
||||
msg = " ".join([m for m in msg if not m.startswith(command_start)])
|
||||
|
||||
# 去除网址
|
||||
msg = re.sub(r"https?://[\w/:%#\$&\?\(\)~\.=\+\-]+", "", msg)
|
||||
|
||||
# 去除 \u200b
|
||||
msg = re.sub(r"[\u200b]", "", msg)
|
||||
|
||||
# 去除cq码
|
||||
msg = re.sub(r"\[CQ:.*?]", "", msg)
|
||||
|
||||
# 去除[]
|
||||
msg = re.sub("[	(1|3);]", "", msg)
|
||||
|
||||
# 去除 emoji
|
||||
# https://github.com/carpedm20/emoji
|
||||
msg = replace_emoji(msg)
|
||||
return msg
|
||||
|
||||
|
||||
async def draw_word_cloud(messages, config):
|
||||
wordcloud_dir = IMAGE_PATH / "wordcloud"
|
||||
wordcloud_dir.mkdir(exist_ok=True, parents=True)
|
||||
# 默认用真寻图片
|
||||
zx_logo_path = wordcloud_dir / "default.png"
|
||||
wordcloud_ttf = FONT_PATH / "STKAITI.TTF"
|
||||
if not os.listdir(wordcloud_dir):
|
||||
url = "https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/image/wordcloud/default.png"
|
||||
try:
|
||||
await AsyncHttpx.download_file(url, zx_logo_path)
|
||||
except Exception as e:
|
||||
logger.error(f"词云图片资源下载发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
if not wordcloud_ttf.exists():
|
||||
ttf_url = "https://ghproxy.com/https://raw.githubusercontent.com/HibiKier/zhenxun_bot/main/resources/font/STKAITI.TTF"
|
||||
try:
|
||||
await AsyncHttpx.download_file(ttf_url, wordcloud_ttf)
|
||||
except Exception as e:
|
||||
logger.error(f"词云字体资源下载发生错误 {type(e)}:{e}")
|
||||
return False
|
||||
|
||||
topK = min(int(len(messages)), 100000)
|
||||
read_name = jieba.analyse.extract_tags(
|
||||
await pre_precess(messages, config), topK=topK, withWeight=True, allowPOS=()
|
||||
)
|
||||
name = []
|
||||
value = []
|
||||
for t in read_name:
|
||||
name.append(t[0])
|
||||
value.append(t[1])
|
||||
for i in range(len(name)):
|
||||
name[i] = str(name[i])
|
||||
dic = dict(zip(name, value))
|
||||
if Config.get_config("word_clouds", "WORD_CLOUDS_TEMPLATE") == 1:
|
||||
|
||||
def random_pic(base_path: str) -> str:
|
||||
path_dir = os.listdir(base_path)
|
||||
path = random.sample(path_dir, 1)[0]
|
||||
return str(base_path) + "/" + str(path)
|
||||
|
||||
mask = np.array(IMG.open(random_pic(wordcloud_dir)))
|
||||
wc = WordCloud(
|
||||
font_path=f"{wordcloud_ttf}",
|
||||
background_color="white",
|
||||
max_font_size=100,
|
||||
width=1920,
|
||||
height=1080,
|
||||
mask=mask,
|
||||
)
|
||||
wc.generate_from_frequencies(dic)
|
||||
image_colors = ImageColorGenerator(mask, default_color=(255, 255, 255))
|
||||
wc.recolor(color_func=image_colors)
|
||||
plt.imshow(wc.recolor(color_func=image_colors), interpolation="bilinear")
|
||||
plt.axis("off")
|
||||
else:
|
||||
wc = WordCloud(
|
||||
font_path=str(wordcloud_ttf),
|
||||
width=1920,
|
||||
height=1200,
|
||||
background_color="black",
|
||||
)
|
||||
wc.generate_from_frequencies(dic)
|
||||
bytes_io = BytesIO()
|
||||
img = wc.to_image()
|
||||
img.save(bytes_io, format="PNG")
|
||||
return bytes_io.getvalue()
|
||||
|
||||
|
||||
async def get_list_msg(user_id, group_id, days):
|
||||
messages_list = (
|
||||
await ChatHistory()
|
||||
._get_msg(uid=user_id, gid=group_id, type_="group", days=days)
|
||||
.gino.all()
|
||||
)
|
||||
if messages_list:
|
||||
messages = [i.text for i in messages_list]
|
||||
return messages
|
||||
else:
|
||||
return False
|
||||
@@ -11,7 +11,7 @@ china_city = TEXT_PATH / "china_city.json"
|
||||
data = {}
|
||||
|
||||
|
||||
url = "https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5"
|
||||
url = "https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=diseaseh5Shelf"
|
||||
|
||||
|
||||
async def get_yiqing_data(area: str) -> Union[str, MessageSegment]:
|
||||
@@ -37,7 +37,7 @@ async def get_yiqing_data(area: str) -> Union[str, MessageSegment]:
|
||||
if area in data[p]:
|
||||
province = p
|
||||
city = area
|
||||
epidemic_data = json.loads((await AsyncHttpx.get(url)).json()["data"])
|
||||
epidemic_data = (await AsyncHttpx.get(url)).json()["data"]["diseaseh5Shelf"]
|
||||
last_update_time = epidemic_data["lastUpdateTime"]
|
||||
if area == "中国":
|
||||
data_ = epidemic_data["areaTree"][0]
|
||||
@@ -59,12 +59,12 @@ async def get_yiqing_data(area: str) -> Union[str, MessageSegment]:
|
||||
add_confirm = data_["today"]["confirm"] # 新增确诊
|
||||
grade = ""
|
||||
_grade_color = ""
|
||||
if data_["total"].get("grade"):
|
||||
grade = data_["total"]["grade"]
|
||||
if "中风险" in grade:
|
||||
_grade_color = "#fa9424"
|
||||
else:
|
||||
_grade_color = "red"
|
||||
# if data_["total"].get("grade"):
|
||||
# grade = data_["total"]["grade"]
|
||||
# if "中风险" in grade:
|
||||
# _grade_color = "#fa9424"
|
||||
# else:
|
||||
# _grade_color = "red"
|
||||
|
||||
dead_rate = f"{dead / confirm * 100:.2f}" # 死亡率
|
||||
heal_rate = f"{heal / confirm * 100:.2f}" # 治愈率
|
||||
|
||||
Generated
+764
-804
File diff suppressed because it is too large
Load Diff
+5
-3
@@ -6,15 +6,14 @@ authors = ["HibiKier <775757368@qq.com>"]
|
||||
license = "AGPL"
|
||||
|
||||
[[tool.poetry.source]]
|
||||
name = "tsinghua"
|
||||
name = "ali"
|
||||
default = true
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple/"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
nonebot2 = "^2.0.0-beta.2"
|
||||
nonebot-adapter-onebot = "^2.0.0-beta.1"
|
||||
bilibili-api = "^9.1.0"
|
||||
aiofiles = "^0.8.0"
|
||||
aiohttp = "3.7.4.post0"
|
||||
beautifulsoup4 = "4.9.3"
|
||||
@@ -40,6 +39,9 @@ dateparser = "^1.1.0"
|
||||
cn2an = "^0.5.16"
|
||||
python-jose = "^3.3.0"
|
||||
python-multipart = "^0.0.5"
|
||||
bilireq = "^0.1.2"
|
||||
emoji = "^1.7.0"
|
||||
wordcloud = "^1.8.1"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 728 KiB |
@@ -15,8 +15,6 @@ _browser: Optional[Browser] = None
|
||||
|
||||
async def init(**kwargs) -> Optional[Browser]:
|
||||
global _browser
|
||||
if platform.system() == "Windows":
|
||||
return None
|
||||
try:
|
||||
browser = await async_playwright().start()
|
||||
_browser = await browser.chromium.launch(**kwargs)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
from typing import Callable, Union, Tuple
|
||||
from nonebot.plugin import require
|
||||
|
||||
|
||||
use = require("use")
|
||||
shop = require("shop_handle")
|
||||
|
||||
|
||||
class ShopRegister(dict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ShopRegister, self).__init__(*args, **kwargs)
|
||||
self._data = {}
|
||||
self._flag = True
|
||||
|
||||
def register(
|
||||
self,
|
||||
name: Tuple[str, ...],
|
||||
price: Tuple[float, ...],
|
||||
des: Tuple[str, ...],
|
||||
discount: Tuple[float, ...],
|
||||
limit_time: Tuple[int, ...],
|
||||
load_status: Tuple[bool, ...],
|
||||
**kwargs,
|
||||
):
|
||||
def add_register_item(func: Callable):
|
||||
if name in self._data.keys():
|
||||
raise ValueError("该商品已注册,请替换其他名称!")
|
||||
for n, p, d, dd, l, s in zip(name, price, des, discount, limit_time, load_status):
|
||||
if s:
|
||||
_temp_kwargs = {}
|
||||
for key, value in kwargs.items():
|
||||
if key.startswith(f"{n}_"):
|
||||
_temp_kwargs[key.split("_", maxsplit=1)[-1]] = value
|
||||
self._data[n] = {
|
||||
"price": p,
|
||||
"des": d,
|
||||
"discount": dd,
|
||||
"limit_time": l,
|
||||
"func": func,
|
||||
"kwargs": _temp_kwargs,
|
||||
}
|
||||
return func
|
||||
|
||||
return lambda func: add_register_item(func)
|
||||
|
||||
async def load_register(self):
|
||||
# 统一进行注册
|
||||
if self._flag:
|
||||
# 只进行一次注册
|
||||
self._flag = False
|
||||
for name in self._data.keys():
|
||||
await shop.register_goods(
|
||||
name, self._data[name]["price"], self._data[name]["des"], self._data[name]["discount"], self._data[name]["limit_time"]
|
||||
)
|
||||
use.register_use(
|
||||
name, self._data[name]["func"], **self._data[name]["kwargs"]
|
||||
)
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
name: Union[str, Tuple[str, ...]],
|
||||
price: Union[float, Tuple[float, ...]],
|
||||
des: Union[str, Tuple[str, ...]],
|
||||
discount: Union[float, Tuple[float, ...]] = 1,
|
||||
limit_time: Union[int, Tuple[int, ...]] = 0,
|
||||
load_status: Union[bool, Tuple[bool, ...]] = True,
|
||||
**kwargs,
|
||||
):
|
||||
_tuple_list = []
|
||||
_current_len = -1
|
||||
for x in [name, price, des, discount, limit_time, load_status]:
|
||||
if isinstance(x, tuple):
|
||||
if _current_len == -1:
|
||||
_current_len = len(x)
|
||||
if _current_len != len(x):
|
||||
raise ValueError(f"注册商品 {name} 中 name,price,des,discount,limit_time,load_status 数量不符!")
|
||||
_current_len = _current_len if _current_len > -1 else 1
|
||||
_name = name if isinstance(name, tuple) else (name,)
|
||||
_price = (
|
||||
price
|
||||
if isinstance(price, tuple)
|
||||
else tuple([price for _ in range(_current_len)])
|
||||
)
|
||||
_discount = (
|
||||
discount
|
||||
if isinstance(discount, tuple)
|
||||
else tuple([discount for _ in range(_current_len)])
|
||||
)
|
||||
_limit_time = (
|
||||
limit_time
|
||||
if isinstance(limit_time, tuple)
|
||||
else tuple([limit_time for _ in range(_current_len)])
|
||||
)
|
||||
_des = (
|
||||
des if isinstance(des, tuple) else tuple([des for _ in range(_current_len)])
|
||||
)
|
||||
_load_status = (
|
||||
load_status
|
||||
if isinstance(load_status, tuple)
|
||||
else tuple([load_status for _ in range(_current_len)])
|
||||
)
|
||||
return self.register(_name, _price, _des, _discount, _limit_time, _load_status, **kwargs)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._data[key] = value
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._data[key]
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._data
|
||||
|
||||
def __str__(self):
|
||||
return str(self._data)
|
||||
|
||||
def keys(self):
|
||||
return self._data.keys()
|
||||
|
||||
def values(self):
|
||||
return self._data.values()
|
||||
|
||||
def items(self):
|
||||
return self._data.items()
|
||||
|
||||
|
||||
shop_register = ShopRegister()
|
||||
+2
-2
@@ -327,14 +327,14 @@ class AsyncPlaywright:
|
||||
await page.set_viewport_size(viewport_size)
|
||||
if isinstance(element, str):
|
||||
if wait_time:
|
||||
card = await page.wait_for_selector(element, timeout=wait_time)
|
||||
card = await page.wait_for_selector(element, timeout=wait_time * 1000)
|
||||
else:
|
||||
card = await page.query_selector(element)
|
||||
else:
|
||||
card = page
|
||||
for e in element:
|
||||
if wait_time:
|
||||
card = await card.wait_for_selector(e, timeout=wait_time)
|
||||
card = await card.wait_for_selector(e, timeout=wait_time * 1000)
|
||||
else:
|
||||
card = await card.query_selector(e)
|
||||
await card.screenshot(path=path, timeout=timeout, type=type_)
|
||||
|
||||
+10
-7
@@ -121,7 +121,7 @@ def fig2b64(plt_: plt) -> str:
|
||||
return "base64://" + base64_str
|
||||
|
||||
|
||||
def is_valid(file: str) -> bool:
|
||||
def is_valid(file: Union[str, Path]) -> bool:
|
||||
"""
|
||||
说明:
|
||||
判断图片是否损坏
|
||||
@@ -1336,6 +1336,7 @@ async def text2image(
|
||||
font: str = "CJGaoDeGuo.otf",
|
||||
font_color: Union[str, Tuple[int, int, int]] = "black",
|
||||
padding: Union[int, Tuple[int, int, int, int]] = 0,
|
||||
_add_height: float = 0,
|
||||
) -> BuildImage:
|
||||
"""
|
||||
说明:
|
||||
@@ -1358,6 +1359,7 @@ async def text2image(
|
||||
:param font: 普通字体
|
||||
:param font_color: 普通字体颜色
|
||||
:param padding: 文本外边距,元组类型时为 (上,左,下,右)
|
||||
:param _add_height: 由于get_size无法返回正确的高度,采用手动方式额外添加高度
|
||||
"""
|
||||
pw = ph = top_padding = left_padding = 0
|
||||
if padding:
|
||||
@@ -1489,17 +1491,18 @@ async def text2image(
|
||||
else:
|
||||
width = 0
|
||||
height = 0
|
||||
_tmp = BuildImage(0, 0, font_size=font_size)
|
||||
_tmp = BuildImage(0, 0, font=font, font_size=font_size)
|
||||
for x in text.split("\n"):
|
||||
x = x if x.strip() else "正"
|
||||
w, h = _tmp.getsize(x)
|
||||
height += h
|
||||
width = width if width > w else w
|
||||
if x:
|
||||
w, _ = _tmp.getsize(x)
|
||||
_, h = _tmp.getsize("正")
|
||||
height += h + _add_height
|
||||
width = width if width > w else w
|
||||
width += pw
|
||||
height += ph
|
||||
A = BuildImage(
|
||||
width + left_padding,
|
||||
height + top_padding,
|
||||
height + top_padding + 2,
|
||||
font_size=font_size,
|
||||
color=color,
|
||||
font=font,
|
||||
|
||||
@@ -4,7 +4,6 @@ from pathlib import Path
|
||||
|
||||
|
||||
class NonePluginCountManager(StaticData):
|
||||
|
||||
"""
|
||||
插件加载容忍管理器,当连续 max_count 次插件加载,视为删除插件,清楚数据
|
||||
"""
|
||||
@@ -15,6 +14,8 @@ class NonePluginCountManager(StaticData):
|
||||
:param max_count: 容忍最大次数
|
||||
"""
|
||||
super().__init__(file)
|
||||
if not self._data:
|
||||
self._data = {}
|
||||
self._max_count = max_count
|
||||
|
||||
def add_count(self, module: str, count: int = 1):
|
||||
@@ -42,10 +43,5 @@ class NonePluginCountManager(StaticData):
|
||||
:param module: 模块
|
||||
"""
|
||||
if module in self._data.keys():
|
||||
return self._data.keys() > self._max_count
|
||||
return self._data[module] >= self._max_count
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot, MessageEvent
|
||||
from pydantic import BaseModel, create_model
|
||||
from typing import Any
|
||||
|
||||
|
||||
class ShopParam(BaseModel):
|
||||
goods_name: str
|
||||
user_id: int
|
||||
group_id: int
|
||||
bot: Any
|
||||
event: MessageEvent
|
||||
num: int # 道具单次使用数量
|
||||
send_success_msg: bool = True # 是否发送使用成功信息
|
||||
max_num_limit: int = 1 # 单次使用最大次数
|
||||
+24
-3
@@ -1,7 +1,7 @@
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
from nonebot import require
|
||||
from configs.config import SYSTEM_PROXY
|
||||
from configs.config import SYSTEM_PROXY, Config
|
||||
from typing import List, Union, Optional, Type, Any
|
||||
from nonebot.adapters.onebot.v11 import Bot, Message
|
||||
from nonebot.matcher import matchers, Matcher
|
||||
@@ -205,8 +205,8 @@ def get_message_at(data: Union[str, Message]) -> List[int]:
|
||||
qq_list.append(int(msg["data"]["qq"]))
|
||||
else:
|
||||
for seg in data:
|
||||
if seg.type == "image":
|
||||
qq_list.append(seg.data["url"])
|
||||
if seg.type == "at":
|
||||
qq_list.append(seg.data["qq"])
|
||||
return qq_list
|
||||
|
||||
|
||||
@@ -229,6 +229,25 @@ def get_message_img(data: Union[str, Message]) -> List[str]:
|
||||
return img_list
|
||||
|
||||
|
||||
def get_message_img_file(data: Union[str, Message]) -> List[str]:
|
||||
"""
|
||||
说明:
|
||||
获取消息中所有的 图片file
|
||||
参数:
|
||||
:param data: event.json()
|
||||
"""
|
||||
file_list = []
|
||||
if isinstance(data, str):
|
||||
data = json.loads(data)
|
||||
for msg in data["message"]:
|
||||
if msg["type"] == "image":
|
||||
file_list.append(msg["data"]["file"])
|
||||
else:
|
||||
for seg in data["image"]:
|
||||
file_list.append(seg.data["file"])
|
||||
return file_list
|
||||
|
||||
|
||||
def get_message_text(data: Union[str, Message]) -> str:
|
||||
"""
|
||||
说明:
|
||||
@@ -370,6 +389,8 @@ def change_pixiv_image_links(
|
||||
url = img_sp[0]
|
||||
img_type = img_sp[1]
|
||||
url = url.replace("original", "master") + f"_master1200.{img_type}"
|
||||
if not nginx_url:
|
||||
nginx_url = Config.get_config("pixiv", "PIXIV_NGINX_URL")
|
||||
if nginx_url:
|
||||
url = (
|
||||
url.replace("i.pximg.net", nginx_url)
|
||||
|
||||
Reference in New Issue
Block a user