Compare commits

...
60 Commits
Author SHA1 Message Date
HibiKier 41642589ae Update __version__ 2022-05-22 20:56:03 +08:00
HibiKier 619fef3bff update README.md 2022-05-22 20:52:59 +08:00
HibiKier 38c0da0cf8 update v0.1.5.4 2022-05-22 20:51:35 +08:00
HibiKier b241a58e84 update check_update 2022-05-22 19:04:58 +08:00
HibiKier 35c9585b22 delete WindowsSelectorEventLoopPolicy 2022-05-22 19:03:22 +08:00
HibiKier d2fe95cc05 fix query_resource_points and sign_in 2022-05-22 18:08:06 +08:00
HibiKier 92e60ba141 fix epic 2022-05-22 15:22:57 +08:00
HibiKier 2356527791 update word_clouds 2022-05-22 12:51:53 +08:00
HibiKier cab0f7b522 update change_pixiv_image_links 2022-05-22 12:49:32 +08:00
HibiKier c511ef50a2 update wbtop 2022-05-21 23:34:48 +08:00
HibiKier 82902e0cde update invite_manager 2022-05-21 22:40:05 +08:00
HibiKier 33d1334174 fix what_anime 2022-05-21 19:33:39 +08:00
HibiKier 324b58c21b update README.md 2022-05-21 16:42:19 +08:00
HibiKier b7b1651709 fix what_anime 2022-05-21 16:39:06 +08:00
HibiKier b5a4a6a9ff fix mute and open_case 2022-05-21 15:58:58 +08:00
HibiKier fe2c82b03c fix mute and open_case 2022-05-21 15:58:29 +08:00
HibiKier 81e4bca9ef fix mute 2022-05-21 13:46:29 +08:00
HibiKier c0d055b825 add get_message_img_file 2022-05-21 13:20:58 +08:00
HibiKier 188ae38525 fix bug 2022-05-21 13:15:53 +08:00
HibiKier 86a8fe7096 Merge pull request #462 from yajiwa/main
Update poetry file
2022-05-21 11:47:50 +08:00
yajiwa 0bb2b45f5c Update poetry.lock 2022-05-21 00:23:41 +08:00
yajiwa a17d8731c8 Update pyproject.toml 2022-05-21 00:22:44 +08:00
HibiKier 57baea18b5 Merge pull request #348 from sihuan/fix-bilibili_sub
Fix wrong live streamer name, fixes #284
2022-05-19 22:11:59 +08:00
HibiKier 0f3efe29c4 Merge pull request #357 from MingxuanGame/main
fix: mihoyo bbs api changed
2022-05-19 22:04:36 +08:00
HibiKier 89b87a38b3 Merge pull request #342 from yajiwa/main
fix bug
2022-05-19 21:03:23 +08:00
MingxuanGame c52b7ca2e9 fix: mihoyo bbs api changed 2022-05-19 17:47:20 +08:00
SiHuan 371b55dc9e Fix wrong live streamer name, fixes #284 2022-05-19 16:03:33 +08:00
yajiwa 53e572353c fix bug 2022-05-19 15:14:20 +08:00
yajiwa 3ae71c74e9 x 2022-05-19 15:10:42 +08:00
HibiKier cb643c04f7 Merge pull request #265 from yajiwa/main
Add word_clouds
2022-05-19 09:27:54 +08:00
yajiwa faf9ee159b update wordclouds url 2022-05-18 23:10:12 +08:00
yajiwa ffb3b2d73e Add word_clouds 2022-05-18 20:31:14 +08:00
HibiKier ffcc327089 Merge pull request #252 from yajiwa/main
Add wordclouds file
2022-05-18 20:04:13 +08:00
yajiwa dfd5a6a86a Add wordclouds file 2022-05-18 18:45:48 +08:00
HibiKier d43f89b067 Update README.md 2022-05-17 19:52:33 +08:00
HibiKier f15c3ab6a2 Update README.md 2022-05-16 19:20:01 +08:00
HibiKier e27809298e Merge pull request #160 from yajiwa/yajiwa
update word_bank
2022-05-16 19:19:20 +08:00
HibiKier 4b3e28e14b fix shop setu 2022-05-15 21:10:35 +08:00
yajiwa 71ce9153ed no message 2022-05-14 15:54:10 +08:00
yajiwa 25d63966de update word_bank 2022-05-14 15:47:33 +08:00
yajiwa 98afbb54b6 add get imgfile 2022-05-14 15:41:44 +08:00
HibiKier 03a51ca5dd Update README.md 2022-05-14 14:48:11 +08:00
HibiKier 428481b2d5 Merge pull request #159 from noahzark/patch-2
修复B站动态生成失败的问题
2022-05-14 14:47:30 +08:00
Feliciano Long f87c60e6db 修复B站动态生成失败的问题
B站动态样式更改
1. card已变更为bili-dyn-list__item
2. 置顶消息变成了一个tag(此处存疑,暂未发现其他tag
2022-05-13 19:38:24 +08:00
HibiKier 6d824e38bc Update README.md 2022-05-11 22:45:20 +08:00
HibiKier f0fd5bfb9e Merge pull request #155 from AkashiCoin/main
fix: 更改p搜api,解决p搜无法使用的问题
2022-05-11 22:44:32 +08:00
HibiKier 33374df671 Update pyproject.toml 2022-05-10 22:25:43 +08:00
AkashiCoin c035b1b19c Update pid_search.py 2022-05-10 18:47:39 +08:00
HibiKier b3b09ed666 update v0.1.5.3 2022-05-09 21:42:24 +08:00
HibiKier 8029c00a02 Update README.md 2022-05-05 20:32:22 +08:00
HibiKier a2c0921013 Merge pull request #153 from NumberSir/main
修改bilibili_sub插件在windows平台下报错
2022-05-05 20:32:17 +08:00
Number_Sir 31cdabc229 修改bilibili_sub插件在windows平台下报错
改用bilireq库,同时补充部分bilireq暂无的方法如获取番剧、获取用户投稿视频
2022-05-04 17:02:56 +08:00
HibiKier 1059a0aae0 Update README.md 2022-05-03 15:40:19 +08:00
HibiKier e6576e7f40 Update README.md 2022-05-03 15:34:47 +08:00
HibiKier 5837144095 Update data_source.py 2022-05-03 15:31:34 +08:00
HibiKier d30a5c4207 Update data_source.py 2022-05-03 10:03:27 +08:00
HibiKier 7d169b0e5e Update README.md 2022-05-03 09:46:06 +08:00
HibiKier 1f54226673 update ShopRegister 2022-05-03 09:43:38 +08:00
HibiKier 901a90ff13 update v0.1.5.1 2022-05-03 02:03:06 +08:00
HibiKier 56573d1d34 update black_word 2022-05-01 15:53:52 +08:00
81 changed files with 4534 additions and 1742 deletions
-1
View File
@@ -142,6 +142,5 @@ test.py
server_ip.py server_ip.py
member_activity_handle.py member_activity_handle.py
Yu-Gi-Oh/ Yu-Gi-Oh/
black_word/
csgo/ csgo/
fantasy_card/ fantasy_card/
+72 -5
View File
@@ -18,10 +18,6 @@
## 声明 ## 声明
此项目仅用于学习交流,请勿用于非法用途 此项目仅用于学习交流,请勿用于非法用途
## Gitee
# [Gitee](https://gitee.com/two_Dimension/zhenxun_bot)
## 未完成的文档 ## 未完成的文档
# [传送门](https://hibikier.github.io/zhenxun_bot/) # [传送门](https://hibikier.github.io/zhenxun_bot/)
@@ -242,6 +238,77 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
## 更新 ## 更新
### 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 ### 2022/4/26
* 修复了群白名单无法正确添加 * 修复了群白名单无法正确添加
@@ -477,4 +544,4 @@ __..... 更多更新信息请查看文档__
[NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件 [NothAmor / nonebot2_luxun_says](https://github.com/NothAmor/nonebot2_luxun_says) :基于nonebot2机器人框架的鲁迅说插件
[Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库 [Kyomotoi / AnimeThesaurus](https://github.com/Kyomotoi/AnimeThesaurus) :一个~~特二刺螈~~(文爱)的适用于任何bot的词库
[Ailitonia / omega-miya](https://github.com/Ailitonia/omega-miya) :基于nonebot2的qq机器人 [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
View File
@@ -1 +1 @@
__version__: v0.1.5.0 __version__: v0.1.5.4
@@ -232,10 +232,7 @@ async def update_member_info(group_id: int, remind_superuser: bool = False) -> b
_exist_member_list = [] _exist_member_list = []
# try: # try:
for user_info in _group_user_list: for user_info in _group_user_list:
if user_info["card"] == "": nickname = user_info["card"] or user_info["nickname"]
nickname = user_info["nickname"]
else:
nickname = user_info["card"]
async with db.transaction(): async with db.transaction():
# 更新权限 # 更新权限
if ( if (
@@ -321,3 +318,4 @@ def set_group_bot_status(group_id: int, status: bool) -> str:
# for x in group_manager.get_task_data(): # for x in group_manager.get_task_data():
# group_manager.close_group_task(group_id, x) # group_manager.close_group_task(group_id, x)
return "那我先睡觉了..." return "那我先睡觉了..."
@@ -2,6 +2,7 @@ from nonebot import on_command, on_notice
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, GROUP, GroupIncreaseNoticeEvent
from ._data_source import update_member_info from ._data_source import update_member_info
__zx_plugin_name__ = "更新群组成员列表 [Admin]" __zx_plugin_name__ = "更新群组成员列表 [Admin]"
__plugin_usage__ = """ __plugin_usage__ = """
usage: usage:
+6 -4
View File
@@ -125,11 +125,13 @@ async def _(
msg = arg.extract_plain_text().strip() msg = arg.extract_plain_text().strip()
if msg: if msg:
if str(event.user_id) in bot.config.superusers: if str(event.user_id) in bot.config.superusers:
if is_number(arg.extract_plain_text().strip().split()[0]): msg_splt = msg.split()
qq = int(msg[0]) if is_number(msg_splt[0]):
msg = msg[1:]
qq = int(msg_splt[0])
msg = msg_splt[1:]
if cmd in [".ban", "/ban"]: if cmd in [".ban", "/ban"]:
time = parse_ban_time(msg) time = parse_ban_time(" ".join(msg))
if isinstance(time, str): if isinstance(time, str):
await ban.finish(time) await ban.finish(time)
result = await a_ban(qq, time, str(qq), event, 9) result = await a_ban(qq, time, str(qq), event, 9)
+5 -12
View File
@@ -25,7 +25,9 @@ def parse_ban_time(msg: str) -> Union[int, str]:
return int(msg[0]) * 60 * 60 + int(msg[1]) * 60 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 ban
:param qq: qq :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) ban_level = await LevelUser.get_user_level(event.user_id, event.group_id)
if await BanUser.ban(qq, ban_level, time): if await BanUser.ban(qq, ban_level, time):
logger.info( 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}的黑名单了!" result = f"已经将 {user_name} 加入{NICKNAME}的黑名单了!"
if time != -1: if time != -1:
@@ -57,13 +60,3 @@ async def a_ban(qq: int, time: int, user_name: str, event: MessageEvent, ban_lev
time += " 分钟" time += " 分钟"
result = f"{user_name} 已在黑名单!预计 {time}后解封" result = f"{user_name} 已在黑名单!预计 {time}后解封"
return result return result
@@ -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() @msg_handler.handle()
+19 -3
View File
@@ -1,5 +1,6 @@
from nonebot import on_notice, on_request from nonebot import on_notice, on_request
from configs.path_config import IMAGE_PATH, DATA_PATH from configs.path_config import IMAGE_PATH, DATA_PATH
from models.level_user import LevelUser
from utils.message_builder import image from utils.message_builder import image
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from datetime import datetime from datetime import datetime
@@ -96,6 +97,23 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
for plugin in data.keys(): for plugin in data.keys():
if not data[plugin]["default_status"]: if not data[plugin]["default_status"]:
group_manager.block_plugin(plugin, event.group_id) 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: else:
join_time = datetime.now() join_time = datetime.now()
user_info = await bot.get_group_member_info( user_info = await bot.get_group_member_info(
@@ -128,9 +146,7 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent):
msg = msg.replace("[at]", "") msg = msg.replace("[at]", "")
at_flag = True at_flag = True
if (DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg").exists(): if (DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg").exists():
img = image( img = image(DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg")
DATA_PATH / "custom_welcome_msg" / f"{event.group_id}.jpg"
)
if msg or img: if msg or img:
msg = msg.strip() + img msg = msg.strip() + img
msg = "\n" + msg if at_flag else msg msg = "\n" + msg if at_flag else msg
+5 -2
View File
@@ -56,6 +56,9 @@ def _create_help_img(
for matcher in _matchers: for matcher in _matchers:
plugin_name = None plugin_name = None
_plugin = nonebot.plugin.get_plugin(matcher.plugin_name) _plugin = nonebot.plugin.get_plugin(matcher.plugin_name)
if not _plugin:
logger.warning(f"获取 功能:{matcher.plugin_name} 失败...")
continue
_module = _plugin.module _module = _plugin.module
try: try:
plugin_name = _module.__getattribute__("__zx_plugin_name__") plugin_name = _module.__getattribute__("__zx_plugin_name__")
@@ -277,7 +280,7 @@ def _create_help_img(
0, 0,
plain_text=msg, plain_text=msg,
font_size=24, font_size=24,
font="yuanshen.ttf", font="HYWenHei-85W.ttf",
) )
B.paste(text, (w, h), True) B.paste(text, (w, h), True)
h += 50 h += 50
@@ -289,7 +292,7 @@ def _create_help_img(
0, 0,
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护", plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
font_size=24, font_size=24,
font="yuanshen.ttf", font="HYWenHei-85W.ttf",
font_color=(231, 74, 57) font_color=(231, 74, 57)
), ),
(300, 10), (300, 10),
+2 -2
View File
@@ -106,7 +106,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
raise IgnoredException("功能总开关关闭状态") raise IgnoredException("功能总开关关闭状态")
except AttributeError: except AttributeError:
pass 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 isinstance(event, GroupMessageEvent):
# 个人权限 # 个人权限
if ( if (
@@ -146,7 +146,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if event.is_tome(): if event.is_tome():
status_message_manager.add(event.user_id) status_message_manager.add(event.user_id)
raise IgnoredException("权限不足") 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 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: if status_message_manager.get(event.group_id) is None:
+3 -3
View File
@@ -31,7 +31,7 @@ _flmt = FreqLimiter(300)
async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State): async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if ( if (
(isinstance(event, MessageEvent) or isinstance(event, PokeNotifyEvent)) (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: ) or matcher.plugin_name in other_limit_plugins:
try: try:
if ( if (
@@ -58,7 +58,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if not static_flmt.check(event.user_id): if not static_flmt.check(event.user_id):
raise IgnoredException("用户处于黑名单中") raise IgnoredException("用户处于黑名单中")
static_flmt.start_cd(event.user_id) static_flmt.start_cd(event.user_id)
if matcher.priority != 9: if matcher.priority != 999:
try: try:
ban_result = Config.get_config("hook", "BAN_RESULT") ban_result = Config.get_config("hook", "BAN_RESULT")
if ( if (
@@ -79,7 +79,7 @@ async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State):
if not static_flmt.check(event.user_id): if not static_flmt.check(event.user_id):
raise IgnoredException("用户处于黑名单中") raise IgnoredException("用户处于黑名单中")
static_flmt.start_cd(event.user_id) static_flmt.start_cd(event.user_id)
if matcher.priority != 9: if matcher.priority != 999:
try: try:
ban_result = Config.get_config("hook", "BAN_RESULT") ban_result = Config.get_config("hook", "BAN_RESULT")
if ban_result and matcher.plugin_name not in ignore_rst_module: if ban_result and matcher.plugin_name not in ignore_rst_module:
+1 -1
View File
@@ -25,7 +25,7 @@ _blmt = BanCheckLimiter(
async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State): async def _(matcher: Matcher, bot: Bot, event: GroupMessageEvent, state: T_State):
if not isinstance(event, MessageEvent): if not isinstance(event, MessageEvent):
return 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 state["_prefix"]["raw_command"]:
if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'): if _blmt.check(f'{event.user_id}{state["_prefix"]["raw_command"]}'):
if await BanUser.ban( if await BanUser.ban(
+1 -1
View File
@@ -25,7 +25,7 @@ __plugin_configs__ = {
friend_req = on_request(priority=5, block=True) friend_req = on_request(priority=5, block=True)
group_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": {}} exists_data = {"private": {}, "group": {}}
+54 -42
View File
@@ -7,11 +7,13 @@ from typing import Optional, Union
from configs.config import Config from configs.config import Config
from nonebot import Driver from nonebot import Driver
from nonebot.plugin import require from nonebot.plugin import require
from utils.decorator.shop import shop_register
import nonebot import nonebot
import time import time
driver: Driver = nonebot.get_driver() driver: Driver = nonebot.get_driver()
use = require("use") use = require("use")
@@ -20,24 +22,26 @@ async def init_default_shop_goods():
""" """
导入内置的三个商品 导入内置的三个商品
""" """
async def sign_card(**kwargs):
user_id = kwargs['user_id'] @shop_register(
group_id = kwargs['group_id'] name=("好感度双倍加持卡Ⅰ", "好感度双倍加持卡Ⅱ", "好感度双倍加持卡Ⅲ"),
prob = kwargs["prob"] 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) user = await SignGroupUser.ensure(user_id, group_id)
await user.update(add_probability=prob).apply() await user.update(add_probability=prob).apply()
if Config.get_config("shop", "IMPORT_DEFAULT_SHOP_GOODS"):
await register_goods( @driver.on_bot_connect
"好感度双倍加持卡Ⅰ", 30, "下次签到双倍好感度概率 + 10%(谁才是真命天子?)(同类商品将覆盖)" async def _():
) await shop_register.load_register()
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})
# 创建商店界面 # 创建商店界面
@@ -59,9 +63,7 @@ async def create_shop_help() -> str:
A = BuildImage(1000, h, color="#f9f6f2") A = BuildImage(1000, h, color="#f9f6f2")
current_h = 0 current_h = 0
for goods in _list: for goods in _list:
bk = BuildImage( bk = BuildImage(700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf")
700, 80, font_size=15, color="#f9f6f2", font="CJGaoDeGuo.otf"
)
goods_image = BuildImage( goods_image = BuildImage(
600, 80, font_size=20, color="#a29ad6", font="CJGaoDeGuo.otf" 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) await bk.apaste(goods_image, alpha=True)
# 添加限时图标和时间 # 添加限时图标和时间
if goods.goods_limit_time > 0: 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(_limit_time_logo, (600, 0), True)
await bk.apaste(BuildImage(0, 0, plain_text="限时!", font_size=23, font="CJGaoDeGuo.otf"), (640, 10), True) await bk.apaste(
limit_time = time.strftime("%Y-%m-%d %H:%M", time.localtime(goods.goods_limit_time)).split() 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] y_m_d = limit_time[0]
_h_m = limit_time[1].split(":") _h_m = limit_time[1].split(":")
h_m = _h_m[0] + "时 " + _h_m[1] + "分" h_m = _h_m[0] + "时 " + _h_m[1] + "分"
@@ -125,13 +135,12 @@ async def create_shop_help() -> str:
async def register_goods( async def register_goods(
name: str, name: str,
price: int, price: int,
des: str, des: str,
discount: Optional[float] = 1, discount: Optional[float] = 1,
limit_time: Optional[int] = 0, limit_time: Optional[int] = 0,
**kwargs, ) -> bool:
):
""" """
添加商品 添加商品
例如: 折扣:可选参数↓ 限时时间:可选,单位为小时 例如: 折扣:可选参数↓ 限时时间:可选,单位为小时
@@ -142,21 +151,20 @@ async def register_goods(
:param des: 商品简介 :param des: 商品简介
:param discount: 商品折扣 :param discount: 商品折扣
:param limit_time: 商品限时销售时间,单位为小时 :param limit_time: 商品限时销售时间,单位为小时
:param kwargs: kwargs
:return: 是否添加成功 :return: 是否添加成功
""" """
if kwargs: if not await GoodsInfo.get_goods_info(name):
name = kwargs.get("name") limit_time = float(limit_time) if limit_time else limit_time
price = kwargs.get("price") discount = discount if discount is not None else 1
des = kwargs.get("des") limit_time = (
discount = kwargs.get("discount") int(time.time() + limit_time * 60 * 60)
limit_time = kwargs.get("time_limit") if limit_time is not None and limit_time != 0
limit_time = float(limit_time) if limit_time else limit_time else 0
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(
return await GoodsInfo.add_goods( name, int(price), des, float(discount), limit_time
name, int(price), des, float(discount), limit_time )
) return False
# 删除商品 # 删除商品
@@ -230,7 +238,11 @@ async def update_goods(**kwargs) -> "str, str, int":
int(price), int(price),
des, des,
float(discount), 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, name,
tmp[:-1], tmp[:-1],
+80 -31
View File
@@ -1,7 +1,11 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment
from services.log import logger from services.log import logger
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.onebot.v11 import Bot
from pydantic import create_model
from utils.models import ShopParam
from typing import Optional, Union from typing import Optional, Union
from types import MappingProxyType
import inspect
import asyncio import asyncio
@@ -30,24 +34,52 @@ class GoodsUseFuncManager:
:param goods_name: 商品名称 :param goods_name: 商品名称
""" """
if self.exists(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 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 :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 self.exists(goods_name):
if asyncio.iscoroutinefunction(self._data[goods_name]["func"]): args = inspect.signature(self._data[goods_name]["func"]).parameters
return await self._data[goods_name]["func"]( if args and list(args.keys())[0] != "kwargs":
**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: else:
return self._data[goods_name]["func"]( if asyncio.iscoroutinefunction(self._data[goods_name]["func"]):
**kwargs, 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: def check_send_success_message(self, goods_name: str) -> bool:
""" """
@@ -67,6 +99,18 @@ class GoodsUseFuncManager:
return self._data[goods_name]["kwargs"] return self._data[goods_name]["kwargs"]
return {} 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() func_manager = GoodsUseFuncManager()
@@ -83,22 +127,23 @@ async def effect(
:return: 使用是否成功 :return: 使用是否成功
""" """
# 优先使用注册的商品插件 # 优先使用注册的商品插件
try: # try:
if func_manager.exists(goods_name): if func_manager.exists(goods_name):
_kwargs = func_manager.get_kwargs(goods_name) _kwargs = func_manager.get_kwargs(goods_name)
return await func_manager.use( return await func_manager.use(
**{ func_manager.init_model(goods_name, bot, event, num),
**_kwargs, **{
"_bot": bot, **_kwargs,
"event": event, "_bot": bot,
"group_id": event.group_id, "event": event,
"user_id": event.user_id, "group_id": event.group_id,
"num": num, "user_id": event.user_id,
"goods_name": goods_name, "num": num,
} "goods_name": goods_name,
) },
except Exception as e: )
logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}") # except Exception as e:
# logger.error(f"use 商品生效函数effect 发生错误 {type(e)}:{e}")
return None return None
@@ -112,10 +157,14 @@ def register_use(goods_name: str, func, **kwargs):
if func_manager.exists(goods_name): if func_manager.exists(goods_name):
raise ValueError("该商品使用函数已被注册!") raise ValueError("该商品使用函数已被注册!")
# 发送使用成功信息 # 发送使用成功信息
if kwargs.get("send_success_msg") is None: kwargs["send_success_msg"] = kwargs.get("send_success_msg", True)
kwargs["send_success_msg"] = True kwargs["max_num_limit"] = kwargs.get("max_num_limit", 1)
kwargs["_max_num_limit"] = ( func_manager.register_use(
kwargs.get("_max_num_limit") if kwargs.get("_max_num_limit") else 1 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} 的使用函数") logger.info(f"register_use 成功注册商品:{goods_name} 的使用函数")
+1 -1
View File
@@ -92,7 +92,7 @@ async def _(bot: Bot, event: MessageEvent, arg: Message = CommandArg()):
level = 0 level = 0
if not msg: if not msg:
await add_group_level.finish("用法:修改群权限 [group] [level]") await add_group_level.finish("用法:修改群权限 [group] [level]")
msg = msg.split(" ") msg = msg.split()
if len(msg) < 2: if len(msg) < 2:
await add_group_level.finish("参数不完全..[group] [level]") await add_group_level.finish("参数不完全..[group] [level]")
if is_number(msg[0]) and is_number(msg[1]): if is_number(msg[0]) and is_number(msg[1]):
@@ -45,6 +45,7 @@ async def _():
group_info["group_name"], group_info["group_name"],
group_info["max_member_count"], group_info["max_member_count"],
group_info["member_count"], group_info["member_count"],
1
): ):
num += 1 num += 1
logger.info(f"自动更新群组 {g} 信息成功") logger.info(f"自动更新群组 {g} 信息成功")
+1450 -155
View File
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -114,8 +114,7 @@ class GoodsInfo(db.Model):
参数: 参数:
:param goods_name: 商品名称 :param goods_name: 商品名称
""" """
query = await cls.query.where(cls.goods_name == goods_name).gino.first() return await cls.query.where(cls.goods_name == goods_name).gino.first()
return query
@classmethod @classmethod
async def get_all_goods(cls) -> List["GoodsInfo"]: async def get_all_goods(cls) -> List["GoodsInfo"]:
+15 -10
View File
@@ -1,3 +1,5 @@
from asyncpg import UniqueViolationError
from services.db_context import db 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 = cls.query.where((cls.user_qq == user_qq) & (cls.group_id == group_id))
query = query.with_for_update() query = query.with_for_update()
user = await query.gino.first() user = await query.gino.first()
if user is None: try:
await cls.create( if not user:
user_qq=user_qq, await cls.create(
group_id=group_id, user_qq=user_qq,
user_level=level, group_id=group_id,
group_flag=group_flag, user_level=level,
) group_flag=group_flag,
return True )
else: return True
await user.update(user_level=level, group_flag=group_flag).apply() else:
await user.update(user_level=level, group_flag=group_flag).apply()
return False
except UniqueViolationError:
return False return False
@classmethod @classmethod
+1 -1
View File
@@ -37,7 +37,7 @@ Config.add_plugin_config(
"alapi", "ALAPI_TOKEN", None, help_="在 https://admin.alapi.cn/user/login 登录后获取token" "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() @ai.handle()
+33 -29
View File
@@ -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}") res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}")
content = "" content = ""
data = json.loads(res.text) try:
if data["result"] == 0: data = json.loads(res.text)
content = data["content"] if data["result"] == 0:
if "菲菲" in content: content = data["content"]
content = content.replace("菲菲", NICKNAME) if "菲菲" in content:
if "艳儿" in content: content = content.replace("菲菲", NICKNAME)
content = content.replace("艳儿", NICKNAME) if "艳儿" in content:
if "公众号" in content: content = content.replace("艳儿", NICKNAME)
content = "" if "公众号" in content:
if "{br}" in content: content = ""
content = content.replace("{br}", "\n") if "{br}" in content:
if "提示" in content: content = content.replace("{br}", "\n")
content = content[: content.find("提示")] if "提示" in content:
if "淘宝" in content or "taobao.com" in content: content = content[: content.find("提示")]
return "" if "淘宝" in content or "taobao.com" in content:
while True: return ""
r = re.search("{face:(.*)}", content) while True:
if r: r = re.search("{face:(.*)}", content)
id_ = r.group(1) if r:
content = content.replace( id_ = r.group(1)
"{" + f"face:{id_}" + "}", str(face(int(id_))) content = content.replace(
) "{" + f"face:{id_}" + "}", str(face(int(id_)))
else: )
break else:
return ( break
content return (
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK") content
else await check_text(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: def hello() -> str:
+2
View File
@@ -23,6 +23,8 @@ async def get_data(url: str, params: Optional[dict] = None) -> Tuple[Union[dict,
return "没有搜索到...", 997 return "没有搜索到...", 997
return data, 200 return data, 200
else: else:
if data["code"] == 101:
return "缺失ALAPI TOKEN,请在配置文件中填写!", 999
return f'发生了错误...code:{data["code"]}', 999 return f'发生了错误...code:{data["code"]}', 999
except TimeoutError: except TimeoutError:
return "超时了....", 998 return "超时了....", 998
+2 -2
View File
@@ -60,8 +60,8 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
img = await AsyncPlaywright.screenshot( img = await AsyncPlaywright.screenshot(
url, url,
f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png", f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
"#pl_feedlist_index", "#pl_feed_main",
wait_time=5 wait_time=12
) )
if img: if img:
await wbtop.send(img) await wbtop.send(img)
+1 -1
View File
@@ -38,7 +38,7 @@ usage:
__plugin_des__ = "非常便利的B站订阅通知" __plugin_des__ = "非常便利的B站订阅通知"
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"] __plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"]
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier & NumberSir"
__plugin_settings__ = { __plugin_settings__ = {
"level": 5, "level": 5,
"default_status": True, "default_status": True,
+39 -32
View File
@@ -1,12 +1,13 @@
from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException from bilireq.exceptions import ResponseCodeError
from utils.manager import resources_manager from utils.manager import resources_manager
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from .model import BilibiliSub from .model import BilibiliSub
from bilibili_api.live import LiveRoom from bilireq.live import get_room_info_by_id
from bilibili_api import bangumi from .utils import get_meta
from utils.message_builder import image from utils.message_builder import image
from bilibili_api.user import User from bilireq.user import get_user_info
from bilibili_api import user from bilireq import dynamic
from .utils import get_videos
from typing import Optional from typing import Optional
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from datetime import datetime from datetime import datetime
@@ -36,9 +37,9 @@ async def add_live_sub(live_id: int, sub_user: str) -> str:
try: try:
async with db.transaction(): async with db.transaction():
try: try:
live = LiveRoom(live_id) """bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
live_info = (await live.get_room_info())["room_info"] live_info = await get_room_info_by_id(live_id)
except ResponseCodeException: except ResponseCodeError:
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确" return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
uid = live_info["uid"] uid = live_info["uid"]
room_id = live_info["room_id"] room_id = live_info["room_id"]
@@ -78,16 +79,18 @@ async def add_up_sub(uid: int, sub_user: str) -> str:
try: try:
async with db.transaction(): async with db.transaction():
try: try:
u = user.User(uid) """bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
user_info = await u.get_user_info() user_info = await get_user_info(uid)
except ResponseCodeException: except ResponseCodeError:
return f"未找到UpId:{uid} 的信息,请检查Id是否正确" return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
uname = user_info["name"] 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 dynamic_upload_time = 0
if dynamic_info.get("cards"): if dynamic_info.get("cards"):
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"] 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 latest_video_created = 0
if video_info["list"].get("vlist"): if video_info["list"].get("vlist"):
latest_video_created = video_info["list"]["vlist"][0]["created"] 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: try:
async with db.transaction(): async with db.transaction():
try: try:
season_info = await bangumi.get_meta(media_id) """bilibili_api.bangumi库中get_meta改为bilireq.bangumi库的get_meta方法"""
except ResponseCodeException: season_info = await get_meta(media_id)
except ResponseCodeError:
return f"未找到media_id:{media_id} 的信息,请检查Id是否正确" return f"未找到media_id:{media_id} 的信息,请检查Id是否正确"
season_id = season_info["media"]["season_id"] season_id = season_info["media"]["season_id"]
season_current_episode = season_info["media"]["new_ep"]["index"] 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_) return await _get_up_status(id_)
elif sub_type == "season": elif sub_type == "season":
return await _get_season_status(id_) return await _get_season_status(id_)
except ResponseCodeException: except ResponseCodeError:
return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..." return f"Id:{id_} 获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e: # except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{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 :param id_: 直播间 id
""" """
live = LiveRoom(id_) """bilibili_api.live库的LiveRoom类中get_room_info改为bilireq.live库的get_room_info_by_id方法"""
live_info = (await live.get_room_info())["room_info"] live_info = await get_room_info_by_id(id_)
title = live_info["title"] title = live_info["title"]
room_id = live_info["room_id"] room_id = live_info["room_id"]
live_status = live_info["live_status"] live_status = live_info["live_status"]
cover = live_info["cover"] cover = live_info["user_cover"]
sub = await BilibiliSub.get_sub(id_) sub = await BilibiliSub.get_sub(id_)
if sub.live_status != live_status: if sub.live_status != live_status:
await BilibiliSub.update_sub_info(id_, 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: :return:
""" """
_user = await BilibiliSub.get_sub(id_) _user = await BilibiliSub.get_sub(id_)
u = user.User(_user.uid) """bilibili_api.user库中User类的get_user_info改为bilireq.user库的get_user_info方法"""
user_info = await u.get_user_info() user_info = await get_user_info(_user.uid)
uname = user_info["name"] 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 latest_video_created = 0
video = None video = None
if _user.uname != uname: if _user.uname != uname:
await BilibiliSub.update_sub_info(id_, 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"): if video_info["list"].get("vlist"):
video = video_info["list"]["vlist"][0] video = video_info["list"]["vlist"][0]
latest_video_created = video["created"] latest_video_created = video["created"]
@@ -280,7 +285,8 @@ async def _get_season_status(id_) -> Optional[str]:
获取 番剧 更新状态 获取 番剧 更新状态
:param id_: 番剧 id :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"] title = season_info["media"]["title"]
_idx = (await BilibiliSub.get_sub(id_)).season_current_episode _idx = (await BilibiliSub.get_sub(id_)).season_current_episode
new_ep = season_info["media"]["new_ep"]["index"] 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( async def get_user_dynamic(
u: User, local_user: BilibiliSub uid: int, local_user: BilibiliSub
) -> "Optional[MessageSegment], int": ) -> "Optional[MessageSegment], int":
""" """
获取用户动态 获取用户动态
:param u: 用户类 :param uid: 用户uid
:param local_user: 数据库存储的用户数据 :param local_user: 数据库存储的用户数据
:return: 最新动态截图与时间 :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() browser = await get_browser()
if dynamic_info.get("cards") and browser: if dynamic_info.get("cards") and browser:
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"] dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
@@ -317,21 +324,21 @@ async def get_user_dynamic(
wait_until="networkidle", wait_until="networkidle",
timeout=10000, 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( await page.evaluate(
""" """
xs = document.getElementsByClassName('first-card-with-title'); xs = document.getElementsByClassName('bili-dyn-item__tag');
for (x of xs) { for (x of xs) {
x.remove(); x.parentNode.remove();
} }
""" """
) )
card = await page.query_selector(".card") card = page.locator(".bili-dyn-list__item").first
await card.wait_for()
# 截图并保存 # 截图并保存
await card.screenshot( await card.screenshot(
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg", path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
timeout=100000,
) )
except Exception as e: except Exception as e:
logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}") logger.error(f"B站订阅:获取用户动态 发送错误 {type(e)}:{e}")
+48 -3
View File
@@ -1,7 +1,8 @@
from utils.image_utils import BuildImage from utils.image_utils import BuildImage
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx 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 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: 直播简介 :param des: 直播简介
:return: :return:
""" """
u = user.User(uid)
user_info = await u.get_user_info() user_info = await get_user_info(uid)
name = user_info["name"] name = user_info["name"]
sex = user_info["sex"] sex = user_info["sex"]
face = user_info["face"] face = user_info["face"]
@@ -68,3 +69,47 @@ def _create_live_des_image(
border_img = BuildImage(1772, 2657, background=border) border_img = BuildImage(1772, 2657, background=border)
bk = BuildImage(1772, 2657, font_size=30) bk = BuildImage(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type="by_width") 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)
+249
View File
@@ -0,0 +1,249 @@
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])
print(uid, id_, punish_level)
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}"
)
+118
View File
@@ -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或敏感词"
+149
View File
@@ -0,0 +1,149 @@
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()
print(len(user_list))
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()
+324
View File
@@ -0,0 +1,324 @@
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: 消息
"""
print(user_id, group_id, message)
if data := self._check(message):
print(data)
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")
+4 -4
View File
@@ -3,10 +3,10 @@ from configs.config import Config
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import platform import platform
if platform.system() == "Windows": # if platform.system() == "Windows":
import asyncio # import asyncio
#
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
url = "http://www.eclzz.mobi" url = "http://www.eclzz.mobi"
+3 -3
View File
@@ -14,9 +14,9 @@ import tarfile
import shutil import shutil
import os import os
if str(platform.system()).lower() == "windows": # if str(platform.system()).lower() == "windows":
policy = asyncio.WindowsSelectorEventLoopPolicy() # policy = asyncio.WindowsSelectorEventLoopPolicy()
asyncio.set_event_loop_policy(policy) # asyncio.set_event_loop_policy(policy)
driver = nonebot.get_driver() driver = nonebot.get_driver()
+24 -37
View File
@@ -1,58 +1,34 @@
from httpx import AsyncClient
from datetime import datetime from datetime import datetime
from nonebot.log import logger from nonebot.log import logger
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.onebot.v11 import Bot
from configs.config import NICKNAME from configs.config import NICKNAME
from utils.http_utils import AsyncHttpx
# 获取所有 Epic Game Store 促销游戏 # 获取所有 Epic Game Store 促销游戏
# 方法参考:RSSHub /epicgames 路由 # 方法参考:RSSHub /epicgames 路由
# https://github.com/DIYgod/RSSHub/blob/master/lib/routes/epicgames/index.js # https://github.com/DIYgod/RSSHub/blob/master/lib/routes/epicgames/index.js
async def get_epic_game(): 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" epic_url = "https://store-site-backend-static-ipv4.ak.epicgames.com/freeGamesPromotions?locale=zh-CN&country=CN&allowCountries=CN"
headers = { headers = {
"Referer": "https://www.epicgames.com/store/zh-CN/", "Referer": "https://www.epicgames.com/store/zh-CN/",
"Content-Type": "application/json; charset=utf-8", "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", "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:
try: res = await AsyncHttpx.get(epic_url, headers=headers, timeout=10)
res = await client.get(epic_url, timeout=10.0) res_json = res.json()
res_json = res.json() games = res_json["data"]["Catalog"]["searchStore"]["elements"]
games = res_json["data"]["Catalog"]["searchStore"]["elements"] return games
return games except Exception as e:
except Exception as e: logger.error(f"Epic 访问接口错误 {type(e)}:{e}")
logger.error(str(e)) return None
return None
# 获取 Epic Game Store 免费游戏信息 # 获取 Epic Game Store 免费游戏信息
# 处理免费游戏的信息方法借鉴 pip 包 epicstore_api 示例 # 处理免费游戏的信息方法借鉴 pip 包 epicstore_api 示例
# https://github.com/SD4RK/epicstore_api/blob/master/examples/free_games_example.py # 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() games = await get_epic_game()
if not games: if not games:
return "Epic 可能又抽风啦,请稍后再试(", 404 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( end_date = datetime.fromisoformat(end_date_iso).strftime(
"%b.%d %H:%M" "%b.%d %H:%M"
) )
if Type_Event == "Group": if type_event == "Group":
_message = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format( _message = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format(
game_corp, game_name, game_price, start_date, end_date 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) msg_list.append(msg)
else: else:
for image in game["keyImages"]: 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"] game_thumbnail = image["url"]
break
for pair in game["customAttributes"]: for pair in game["customAttributes"]:
if pair["key"] == "developerName": if pair["key"] == "developerName":
game_dev = pair["value"] 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 = "https://www.epicgames.com/store/zh-CN/p/{}".format(
game_url_part game_url_part
) )
if Type_Event == "Group": if type_event == "Group":
_message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format( _message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format(
game_thumbnail, game_thumbnail,
game_name, game_name,
+4 -3
View File
@@ -9,6 +9,7 @@ from nonebot.adapters.onebot.v11 import GroupMessageEvent
from configs.config import Config from configs.config import Config
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
from services.log import logger from services.log import logger
from configs.config import NICKNAME
__zx_plugin_name__ = "复读" __zx_plugin_name__ = "复读"
@@ -74,15 +75,15 @@ class Fudu:
_fudu_list = Fudu() _fudu_list = Fudu()
fudu = on_message(permission=GROUP, priority=9) fudu = on_message(permission=GROUP, priority=999)
@fudu.handle() @fudu.handle()
async def _(event: GroupMessageEvent): async def _(event: GroupMessageEvent):
if event.is_tome(): if event.is_tome():
return return
if get_message_text(event.json()): if msg := get_message_text(event.json()):
if get_message_text(event.json()).find("@可爱的小真寻") != -1: if msg.startswith(f"@可爱的{NICKNAME}"):
await fudu.finish("复制粘贴的虚空艾特?", at_sender=True) await fudu.finish("复制粘贴的虚空艾特?", at_sender=True)
img = get_message_img(event.json()) img = get_message_img(event.json())
msg = get_message_text(event.json()) msg = get_message_text(event.json())
+1 -1
View File
@@ -35,7 +35,7 @@ Config.add_plugin_config(
default_value=True, default_value=True,
) )
almanac = on_command("原神黄历", priority=5, block=True) almanac = on_command("原神黄历", priority=15, block=True)
ALC_PATH = IMAGE_PATH / "genshin" / "alc" ALC_PATH = IMAGE_PATH / "genshin" / "alc"
@@ -1,6 +1,6 @@
from pathlib import Path
from typing import Tuple, Optional, List from typing import Tuple, Optional, List
from configs.path_config import IMAGE_PATH, TEXT_PATH from configs.path_config import IMAGE_PATH, TEXT_PATH
from PIL.Image import UnidentifiedImageError
from utils.message_builder import image from utils.message_builder import image
from services.log import logger from services.log import logger
from utils.image_utils import BuildImage from utils.image_utils import BuildImage
@@ -137,7 +137,7 @@ async def download_resource_data(semaphore: Semaphore):
asyncio.ensure_future( asyncio.ensure_future(
download_image( download_image(
img_url, img_url,
f"{icon_path}/{id_}.png", icon_path / f"{id_}.png",
semaphore, semaphore,
True, True,
) )
@@ -179,7 +179,7 @@ async def download_map_init(semaphore: Semaphore, flag: bool = False):
map_url = _map_data["url"] map_url = _map_data["url"]
await download_image( await download_image(
map_url, map_url,
f"{map_path}/{idx}.png", map_path / f"{idx}.png",
semaphore, semaphore,
force_flag=flag, force_flag=flag,
) )
@@ -228,15 +228,14 @@ async def download_resource_type():
except (TimeoutError, ConnectTimeout): except (TimeoutError, ConnectTimeout):
logger.warning("下载原神资源类型数据超时....") logger.warning("下载原神资源类型数据超时....")
except Exception as e: 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") A = BuildImage(0, 0, background=f"{icon_path}/box.png")
B = BuildImage(0, 0, background=f"{icon_path}/box_alpha.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() icon_img.circle()
B.paste(icon_img, (17, 10), True) B.paste(icon_img, (17, 10), True)
B.paste(A, alpha=True) B.paste(A, alpha=True)
@@ -247,22 +246,22 @@ def gen_icon(icon: str):
# 下载图片 # 下载图片
async def download_image( async def download_image(
img_url: str, img_url: str,
path: str, path: Path,
semaphore: Semaphore, semaphore: Semaphore,
gen_flag: bool = False, gen_flag: bool = False,
force_flag: bool = False, force_flag: bool = False,
): ):
async with semaphore: async with semaphore:
try: 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): if await AsyncHttpx.download_file(img_url, path, timeout=10):
logger.info(f"下载原神资源图标:{img_url}") logger.info(f"下载原神资源图标:{img_url}")
if gen_flag: if gen_flag:
gen_icon(path) gen_icon(path)
else: else:
logger.info(f"下载原神资源图标:{img_url} 失败,等待下次更新...") logger.info(f"下载原神资源图标:{img_url} 失败,等待下次更新...")
except UnidentifiedImageError: except Exception as e:
logger.warning(f"原神图片打开错误..已删除,等待下次更新... file: {path}") logger.warning(f"原神图片错误..已删除,等待下次更新... file: {path} {type(e)}:{e}")
if os.path.exists(path): if os.path.exists(path):
os.remove(path) os.remove(path)
@@ -128,7 +128,7 @@ async def get_character(
) -> Optional[dict]: ) -> Optional[dict]:
try: try:
req = await AsyncHttpx.post( 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={ headers={
"Accept": "application/json, text/plain, */*", "Accept": "application/json, text/plain, */*",
"DS": get_ds( "DS": get_ds(
@@ -232,7 +232,7 @@ async def get_mys_data(uid: str, mys_id: Optional[str]) -> Optional[List[Dict]]:
if mys_id: if mys_id:
try: try:
req = await AsyncHttpx.get( 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={ headers={
"DS": get_ds(f"uid={mys_id}"), "DS": get_ds(f"uid={mys_id}"),
"x-rpc-app_version": Config.get_config("genshin", "mhyVersion"), "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") region = BuildImage(790, 267 * (len(world_data_dict) - 1), color="#F9F6F2")
height = 0 height = 0
for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫"]: for country in ["蒙德", "龙脊雪山", "璃月", "层岩巨渊", "稻妻", "渊下宫"]:
if not world_data_dict.get(country):
continue
x = BuildImage(790, 250, color="#3A4467") x = BuildImage(790, 250, color="#3A4467")
logo = BuildImage(180, 180, background=image_path / "logo" / f"{country}.png") logo = BuildImage(180, 180, background=image_path / "logo" / f"{country}.png")
tmp_bk = BuildImage(770, 230, color="#606779") 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.utils import get_bot, scheduler
from utils.message_builder import at from utils.message_builder import at
from models.group_member_info import GroupInfoUser 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) group_list = await GroupInfoUser.get_user_all_group(user_id)
if group_list: if group_list:
group_id = group_list[0] group_id = group_list[0]
await bot.send_group_msg( try:
group_id=group_id, await bot.send_group_msg(
message=at(user_id) + 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 not next_time:
if user_manager.check(uid) and Config.get_config("resin_remind", "AUTO_CLOSE_QUERY_FAIL_RESIN_REMIND"): 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) await Genshin.set_resin_remind(int(uid), False)
-34
View File
@@ -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()
-67
View File
@@ -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
View File
@@ -1,8 +1,7 @@
from nonebot import on_message, on_command from nonebot import on_message, on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import GROUP from nonebot.adapters.onebot.v11.permission import GROUP
from utils.utils import is_number, get_message_img from utils.utils import is_number, get_message_img, get_message_text
from nonebot.typing import T_State
from nonebot.adapters.onebot.v11.exception import ActionFailed from nonebot.adapters.onebot.v11.exception import ActionFailed
from configs.path_config import DATA_PATH, TEMP_PATH from configs.path_config import DATA_PATH, TEMP_PATH
from utils.image_utils import get_img_hash from utils.image_utils import get_img_hash
@@ -10,7 +9,7 @@ from services.log import logger
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
from nonebot.params import CommandArg, Command from nonebot.params import CommandArg, Command
from typing import Tuple from typing import Tuple, Dict, Any
import time import time
try: try:
@@ -57,7 +56,7 @@ mute_setting = on_command(
) )
def get_data(): def get_data() -> Dict[Any, Any]:
try: try:
with open(DATA_PATH / "group_mute_data.json", "r", encoding="utf8") as f: with open(DATA_PATH / "group_mute_data.json", "r", encoding="utf8") as f:
data = json.load(f) data = json.load(f)
@@ -72,7 +71,7 @@ def save_data():
json.dump(mute_data, f, indent=4) 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( if await AsyncHttpx.download_file(
url, TEMP_PATH / f"mute_{group_id}_img.jpg" url, TEMP_PATH / f"mute_{group_id}_img.jpg"
): ):
@@ -85,9 +84,9 @@ mute_data = get_data()
@mute.handle() @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) 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_list = get_message_img(event.json())
img_hash = "" img_hash = ""
for img in img_list: 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): if not mute_dict.get(event.user_id):
mute_dict[event.user_id] = {"time": time.time(), "count": 1, "msg": msg} mute_dict[event.user_id] = {"time": time.time(), "count": 1, "msg": msg}
else: else:
if cmd or not msg:
return
if msg and msg.find(mute_dict[event.user_id]["msg"]) != -1: if msg and msg.find(mute_dict[event.user_id]["msg"]) != -1:
mute_dict[event.user_id]["count"] += 1 mute_dict[event.user_id]["count"] += 1
else: else:
@@ -123,7 +120,7 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
await bot.set_group_ban( await bot.set_group_ban(
group_id=event.group_id, group_id=event.group_id,
user_id=event.user_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) await mute.send(f"检测到恶意刷屏,{NICKNAME}要把你关进小黑屋!", at_sender=True)
mute_dict[event.user_id]["count"] = 0 mute_dict[event.user_id]["count"] = 0
@@ -137,27 +134,28 @@ async def _(bot: Bot, event: GroupMessageEvent, cmd: Tuple[str, ...] = Command()
@mute_setting.handle() @mute_setting.handle()
async def _(event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()): async def _(event: GroupMessageEvent, cmd: Tuple[str, ...] = Command(), arg: Message = CommandArg()):
global mute_data
group_id = str(event.group_id) group_id = str(event.group_id)
if not mute_data.get(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() msg = arg.extract_plain_text().strip()
if cmd[0] == "刷屏检测设置": if cmd[0] == "刷屏检测设置":
await mute_setting.finish( await mute_setting.finish(
f'最大次数:{mute_data[group_id]["count"]} 次\n' f'最大次数:{mute_data[group_id]["count"]} 次\n'
f'规定时间:{mute_data[group_id]["time"]} 秒\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时关闭此功能】" f"【在规定时间内发送相同消息超过最大次数则禁言\n当禁言时长为0时关闭此功能】"
) )
if not is_number(msg): if not is_number(msg):
await mute.finish("设置的参数必须是数字啊!", at_sender=True) await mute.finish("设置的参数必须是数字啊!", at_sender=True)
if cmd[0] == "设置检测时间": if cmd[0] == "设置刷屏检测时间":
mute_data[group_id]["time"] = int(msg) mute_data[group_id]["time"] = int(msg)
msg += "秒" msg += "秒"
if cmd[0] == "设置检测次数": if cmd[0] == "设置刷屏检测次数":
mute_data[group_id]["count"] = int(msg) mute_data[group_id]["count"] = int(msg)
msg += " 次" msg += " 次"
if cmd[0] == "设置禁言时长": if cmd[0] == "设置刷屏禁言时长":
mute_data[group_id]["duration"] = int(msg) * 60 mute_data[group_id]["duration"] = int(msg)
msg += " 分钟" msg += " 分钟"
await mute_setting.send(f'刷屏检测:{cmd[0]}为 {msg}') await mute_setting.send(f'刷屏检测:{cmd[0]}为 {msg}')
logger.info( logger.info(
+5 -6
View File
@@ -1,4 +1,4 @@
from typing import Type from typing import Type, Tuple, Any
from nonebot import on_command from nonebot import on_command
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from utils.utils import scheduler, is_number from utils.utils import scheduler, is_number
@@ -9,7 +9,7 @@ from nonebot.permission import SUPERUSER
import random import random
from nonebot.plugin import MatcherGroup from nonebot.plugin import MatcherGroup
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from nonebot.params import CommandArg from nonebot.params import CommandArg, RegexGroup
from .open_cases_c import ( from .open_cases_c import (
open_case, open_case,
total_open_statistics, 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() @open_shilian.handle()
async def _(event: GroupMessageEvent, state: T_State): async def _(event: GroupMessageEvent, state: T_State, reg_group: Tuple[Any, ...] = RegexGroup()):
num = state["_matched_groups"][0].strip() num, case_name = reg_group
if is_number(num) or num_dict.get(num): if is_number(num) or num_dict.get(num):
try: try:
num = num_dict[num] num = num_dict[num]
@@ -162,7 +162,6 @@ async def _(event: GroupMessageEvent, state: T_State):
await open_shilian.finish("再负开箱就扣你明天开箱数了!", at_sender=True) await open_shilian.finish("再负开箱就扣你明天开箱数了!", at_sender=True)
else: else:
await open_shilian.finish("必须要是数字切不要超过30啊笨蛋!中文也可!", at_sender=True) await open_shilian.finish("必须要是数字切不要超过30啊笨蛋!中文也可!", at_sender=True)
case_name = state["_matched_groups"][1].strip()
case_name = case_name.replace("武器箱", "").strip() case_name = case_name.replace("武器箱", "").strip()
if not case_name: if not case_name:
case_name = random.choice(cases_name) case_name = random.choice(cases_name)
+166 -171
View File
@@ -1,171 +1,166 @@
from nonebot import on_message # from nonebot import on_message
from services.log import logger # from services.log import logger
from nonebot.adapters.onebot.v11 import GroupMessageEvent # from nonebot.adapters.onebot.v11 import GroupMessageEvent
from utils.utils import get_message_json, get_local_proxy, is_number, get_message_text # from utils.utils import get_message_json, get_local_proxy, is_number, get_message_text
from nonebot.adapters.onebot.v11.permission import GROUP # from nonebot.adapters.onebot.v11.permission import GROUP
from bilibili_api import video # from utils.message_builder import image
from utils.message_builder import image # from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot.adapters.onebot.v11.exception import ActionFailed # from utils.image_utils import BuildImage
from utils.image_utils import BuildImage # from utils.browser import get_browser
from utils.browser import get_browser # from configs.path_config import IMAGE_PATH
from configs.path_config import IMAGE_PATH # from utils.http_utils import AsyncHttpx
from utils.http_utils import AsyncHttpx # from configs.config import Config
from configs.config import Config # from utils.user_agent import get_user_agent
from utils.user_agent import get_user_agent # import aiohttp
import aiohttp # import asyncio
import asyncio # import time
import time # import ujson as json
from bilibili_api import settings #
import ujson as json #
# __zx_plugin_name__ = "B站转发解析"
# __plugin_usage__ = """
__zx_plugin_name__ = "B站转发解析" # usage:
__plugin_usage__ = """ # B站转发解析,解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv,且5分钟内不解析相同url
usage: # """.strip()
B站转发解析,解析b站分享信息,支持bv,bilibili链接,b站手机端转发卡片,cv,b23.tv,且5分钟内不解析相同url # __plugin_des__ = "B站转发解析"
""".strip() # __plugin_type__ = ("其他",)
__plugin_des__ = "B站转发解析" # __plugin_version__ = 0.1
__plugin_type__ = ("其他",) # __plugin_author__ = "HibiKier"
__plugin_version__ = 0.1 # __plugin_task__ = {"bilibili_parse": "b站转发解析"}
__plugin_author__ = "HibiKier" # Config.add_plugin_config(
__plugin_task__ = {"bilibili_parse": "b站转发解析"} # "_task",
Config.add_plugin_config( # "DEFAULT_BILIBILI_PARSE",
"_task", # True,
"DEFAULT_BILIBILI_PARSE", # help_="被动 B站转发解析 进群默认开关状态",
True, # default_value=True,
help_="被动 B站转发解析 进群默认开关状态", # )
default_value=True, #
) #
# parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False)
#
if get_local_proxy(): # _tmp = {}
settings.proxy = get_local_proxy() #
#
parse_bilibili_json = on_message(priority=1, permission=GROUP, block=False) # @parse_bilibili_json.handle()
# async def _(event: GroupMessageEvent):
_tmp = {} # vd_info = None
# url = None
# if get_message_json(event.json()):
@parse_bilibili_json.handle() # try:
async def _(event: GroupMessageEvent): # data = json.loads(get_message_json(event.json())[0]["data"])
vd_info = None # except (IndexError, KeyError):
url = None # data = None
if get_message_json(event.json()): # if data:
try: # # 转发视频
data = json.loads(get_message_json(event.json())[0]["data"]) # if data.get("desc") == "哔哩哔哩":
except (IndexError, KeyError): # async with aiohttp.ClientSession(
data = None # headers=get_user_agent()
if data: # ) as session:
# 转发视频 # async with session.get(
if data.get("desc") == "哔哩哔哩": # data["meta"]["detail_1"]["qqdocurl"],
async with aiohttp.ClientSession( # proxy=get_local_proxy(),
headers=get_user_agent() # timeout=7,
) as session: # ) as response:
async with session.get( # url = str(response.url).split("?")[0]
data["meta"]["detail_1"]["qqdocurl"], # bvid = url.split("/")[-1]
proxy=get_local_proxy(), # vd_info = await video.Video(bvid=bvid).get_info()
timeout=7, # # response = await AsyncHttpx.get(
) as response: # # data["meta"]["detail_1"]["qqdocurl"], timeout=7
url = str(response.url).split("?")[0] # # )
bvid = url.split("/")[-1] # # url = str(response.url).split("?")[0]
vd_info = await video.Video(bvid=bvid).get_info() # # bvid = url.split("/")[-1]
# response = await AsyncHttpx.get( # # vd_info = await video.Video(bvid=bvid).get_info()
# data["meta"]["detail_1"]["qqdocurl"], timeout=7 # # 转发专栏
# ) # if (
# url = str(response.url).split("?")[0] # data.get("meta")
# bvid = url.split("/")[-1] # and data["meta"].get("news")
# vd_info = await video.Video(bvid=bvid).get_info() # and data["meta"]["news"].get("desc") == "哔哩哔哩专栏"
# 转发专栏 # ):
if ( # url = data["meta"]["news"]["jumpUrl"]
data.get("meta") # page = None
and data["meta"].get("news") # try:
and data["meta"]["news"].get("desc") == "哔哩哔哩专栏" # browser = await get_browser()
): # if not browser:
url = data["meta"]["news"]["jumpUrl"] # return
page = None # page = await browser.new_page(
try: # user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
browser = await get_browser() # " (KHTML, like Gecko) Chrome/93.0.4530.0 Safari/537.36"
if not browser: # )
return # await page.goto(url, wait_until="networkidle", timeout=10000)
page = await browser.new_page( # await page.set_viewport_size({"width": 2560, "height": 1080})
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" # await page.click("#app > div")
" (KHTML, like Gecko) Chrome/93.0.4530.0 Safari/537.36" # div = await page.query_selector("#app > div")
) # await div.screenshot(
await page.goto(url, wait_until="networkidle", timeout=10000) # path=f"{IMAGE_PATH}/temp/cv_{event.user_id}.png",
await page.set_viewport_size({"width": 2560, "height": 1080}) # timeout=100000,
await page.click("#app > div") # )
div = await page.query_selector("#app > div") # await asyncio.get_event_loop().run_in_executor(
await div.screenshot( # None, resize, f"{IMAGE_PATH}/temp/cv_{event.user_id}.png"
path=f"{IMAGE_PATH}/temp/cv_{event.user_id}.png", # )
timeout=100000, # await parse_bilibili_json.send(
) # "[[_task|bilibili_parse]]" + image(f"cv_{event.user_id}.png", "temp")
await asyncio.get_event_loop().run_in_executor( # )
None, resize, f"{IMAGE_PATH}/temp/cv_{event.user_id}.png" # await page.close()
) # logger.info(
await parse_bilibili_json.send( # f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
"[[_task|bilibili_parse]]" + image(f"cv_{event.user_id}.png", "temp") # )
) # except Exception as e:
await page.close() # logger.error(f"尝试解析bilibili专栏 {url} 失败 {type(e)}:{e}")
logger.info( # if page:
f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}" # await page.close()
) # return
except Exception as e: # # BV
logger.error(f"尝试解析bilibili专栏 {url} 失败 {type(e)}:{e}") # if msg := get_message_text(event.json()):
if page: # if "BV" in msg:
await page.close() # index = msg.find("BV")
return # if len(msg[index + 2 :]) >= 10:
# BV # msg = msg[index : index + 12]
if msg := get_message_text(event.json()): # url = f"https://www.bilibili.com/video/{msg}"
if "BV" in msg: # vd_info = await video.Video(bvid=msg).get_info()
index = msg.find("BV") # elif "av" in msg:
if len(msg[index + 2 :]) >= 10: # index = msg.find("av")
msg = msg[index : index + 12] # if len(msg[index + 2 :]) >= 9:
url = f"https://www.bilibili.com/video/{msg}" # msg = msg[index + 2 : index + 11]
vd_info = await video.Video(bvid=msg).get_info() # if is_number(msg):
elif "av" in msg: # url = f"https://www.bilibili.com/video/{msg}"
index = msg.find("av") # vd_info = await video.Video(aid=int(msg)).get_info()
if len(msg[index + 2 :]) >= 9: # elif "https://b23.tv" in msg:
msg = msg[index + 2 : index + 11] # url = "https://" + msg[msg.find("b23.tv") : msg.find("b23.tv") + 13]
if is_number(msg): # res = await AsyncHttpx.get(url, timeout=7)
url = f"https://www.bilibili.com/video/{msg}" # url = str(res.url).split("?")[0]
vd_info = await video.Video(aid=int(msg)).get_info() # bvid = url.split("/")[-1]
elif "https://b23.tv" in msg: # vd_info = await video.Video(bvid=bvid).get_info()
url = "https://" + msg[msg.find("b23.tv") : msg.find("b23.tv") + 13] # if vd_info:
res = await AsyncHttpx.get(url, timeout=7) # if (
url = str(res.url).split("?")[0] # url in _tmp.keys() and time.time() - _tmp[url] > 30
bvid = url.split("/")[-1] # ) or url not in _tmp.keys():
vd_info = await video.Video(bvid=bvid).get_info() # _tmp[url] = time.time()
if vd_info: # aid = vd_info["aid"]
if ( # title = vd_info["title"]
url in _tmp.keys() and time.time() - _tmp[url] > 30 # author = vd_info["owner"]["name"]
) or url not in _tmp.keys(): # reply = vd_info["stat"]["reply"] # 回复
_tmp[url] = time.time() # favorite = vd_info["stat"]["favorite"] # 收藏
aid = vd_info["aid"] # coin = vd_info["stat"]["coin"] # 投币
title = vd_info["title"] # # like = vd_info['stat']['like'] # 点赞
author = vd_info["owner"]["name"] # # danmu = vd_info['stat']['danmaku'] # 弹幕
reply = vd_info["stat"]["reply"] # 回复 # date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"]))
favorite = vd_info["stat"]["favorite"] # 收藏 # try:
coin = vd_info["stat"]["coin"] # 投币 # await parse_bilibili_json.send(
# like = vd_info['stat']['like'] # 点赞 # "[[_task|bilibili_parse]]" +
# danmu = vd_info['stat']['danmaku'] # 弹幕 # image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n"
date = time.strftime("%Y-%m-%d", time.localtime(vd_info["ctime"])) # f"UP:{author}\n"
try: # f"上传日期:{date}\n"
await parse_bilibili_json.send( # f"回复:{reply},收藏:{favorite},投币:{coin}\n"
"[[_task|bilibili_parse]]" + # f"{url}"
image(vd_info["pic"]) + f"\nav{aid}\n标题:{title}\n" # )
f"UP:{author}\n" # except ActionFailed:
f"上传日期:{date}\n" # logger.warning(f"{event.group_id} 发送bilibili解析失败")
f"回复:{reply},收藏:{favorite},投币:{coin}\n" # else:
f"{url}" # logger.info(
) # f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}"
except ActionFailed: # )
logger.warning(f"{event.group_id} 发送bilibili解析失败") #
else: #
logger.info( # def resize(path: str):
f"USER {event.user_id} GROUP {event.group_id} 解析bilibili转发 {url}" # A = BuildImage(0, 0, background=path, ratio=0.5)
) # A.save(path)
def resize(path: str):
A = BuildImage(0, 0, background=path, ratio=0.5)
A.save(path)
+117 -100
View File
@@ -1,100 +1,117 @@
from asyncio.exceptions import TimeoutError from nonebot import on_command
from nonebot import on_command from nonebot.adapters.onebot.v11 import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.adapters.onebot.v11 import MessageEvent, Message, GroupMessageEvent from nonebot.typing import T_State
from nonebot.typing import T_State
from configs.path_config import TEMP_PATH from configs.config import Config
from services.log import logger from utils.utils import is_number, change_pixiv_image_links
from utils.message_builder import image from utils.message_builder import image
from utils.utils import is_number from services.log import logger
from utils.manager import withdraw_message_manager from asyncio.exceptions import TimeoutError
from utils.http_utils import AsyncHttpx from configs.path_config import IMAGE_PATH
from nonebot.params import CommandArg, Arg from utils.manager import withdraw_message_manager
from utils.http_utils import AsyncHttpx
try: from nonebot.params import CommandArg, Arg
import ujson as json
except ModuleNotFoundError:
import json __zx_plugin_name__ = "pid搜索"
__plugin_usage__ = """
usage:
__zx_plugin_name__ = "pid搜索" 通过 pid 搜索图片
__plugin_usage__ = """ 指令:
usage: p搜 [pid]
通过 pid 搜索图片 """.strip()
指令: __plugin_des__ = "通过 pid 搜索图片"
p搜 [pid] __plugin_cmd__ = ["p搜 [pid]"]
""".strip() __plugin_version__ = 0.1
__plugin_des__ = "通过 pid 搜索图片" __plugin_author__ = "HibiKier"
__plugin_cmd__ = ["p搜 [pid]"] __plugin_settings__ = {
__plugin_version__ = 0.1 "level": 5,
__plugin_author__ = "HibiKier" "default_status": True,
__plugin_settings__ = { "limit_superuser": False,
"level": 5, "cmd": ["p搜"],
"default_status": True, }
"limit_superuser": False,
"cmd": ["p搜"], pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True)
}
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()):
url = "https://api.fantasyzone.cc/tu/search.php" pid = arg.extract_plain_text().strip()
if pid:
state["pid"] = pid
@pid_search.handle()
async def _(state: T_State, arg: Message = CommandArg()):
pid = arg.extract_plain_text().strip() headers = {
if pid: "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;"
state["pid"] = pid " rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
"Referer": "https://www.pixiv.net",
}
@pid_search.got("pid", prompt="需要查询的图片PID是?")
async def _(event: MessageEvent, state: T_State, pid: str = Arg("pid")):
if pid in ["取消", "算了"]: @pid_search.got("pid", prompt="需要查询的图片PID是?")
await pid_search.finish("已取消操作...") async def _g(event: MessageEvent, state: T_State, pid: str = Arg("pid")):
if is_number(pid): url = Config.get_config("hibiapi", "HIBIAPI") + "/api/pixiv/"
await pid_search.reject_arg("pid", "笨蛋,重新输入数!字!") if pid in ["取消", "算了"]:
params = { await pid_search.finish("已取消操作...")
"id": pid, if not is_number(pid):
"p": 1, await pid_search.reject_arg("pid", "笨蛋,重新输入数!字!")
} for _ in range(3):
for _ in range(3): try:
try: data = (
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json() await AsyncHttpx.get(
except TimeoutError: url,
pass params={"id": pid},
except Exception as e: timeout=5,
await pid_search.finish(f"发生了一些错误..{type(e)}:{e}") )
else: ).json()
if not data["width"] and not data["height"]: except TimeoutError:
await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True) pass
pid = data["id"] except Exception as e:
title = data["title"] await pid_search.finish(f"发生了一些错误..{type(e)}:{e}")
author = data["userName"] else:
author_id = data["userId"] if data.get("error"):
img_url = data["url"] await pid_search.finish(data["error"]["user_message"], at_sender=True)
if not await AsyncHttpx.download_file( data = data["illust"]
img_url, TEMP_PATH / f"pid_search_{event.user_id}.png" if not data["width"] and not data["height"]:
): await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True)
await pid_search.finish("图片下载失败了....", at_sender=True) pid = data["id"]
tmp = "" title = data["title"]
if isinstance(event, GroupMessageEvent): author = data["user"]["name"]
tmp = "\n【注】将在30后撤回......" author_id = data["user"]["id"]
msg_id = await pid_search.send( image_list = []
Message( try:
f"title:{title}\n" image_list.append(data["meta_single_page"]["original_image_url"])
f"pid:{pid}\n" except KeyError:
f"author:{author}\n" for image_url in data["meta_pages"]:
f"author_id:{author_id}\n" image_list.append(image_url["image_urls"]["original"])
f'{image(TEMP_PATH / f"pid_search_{event.user_id}.png")}' for i, img_url in enumerate(image_list):
f"{tmp}" img_url = change_pixiv_image_links(img_url)
) if not await AsyncHttpx.download_file(
) img_url,
logger.info( IMAGE_PATH / "temp" / f"pid_search_{event.user_id}_{i}.png",
f"(USER {event.user_id}, " headers=headers,
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" ):
f" 查询图片 PID:{pid}" await pid_search.send("图片下载失败了....", at_sender=True)
) tmp = ""
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
withdraw_message_manager.append((msg_id, 30)) tmp = "\n【注】将在30后撤回......"
break msg_id = await pid_search.send(
else: Message(
await pid_search.finish("图片下载失败了....", at_sender=True) 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)
+3 -3
View File
@@ -20,9 +20,9 @@ try:
except ModuleNotFoundError: except ModuleNotFoundError:
import json import json
if str(platform.system()).lower() == "windows": # if str(platform.system()).lower() == "windows":
policy = asyncio.WindowsSelectorEventLoopPolicy() # policy = asyncio.WindowsSelectorEventLoopPolicy()
asyncio.set_event_loop_policy(policy) # asyncio.set_event_loop_policy(policy)
headers = { headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;" "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;"
+4 -4
View File
@@ -8,10 +8,10 @@ from services.log import logger
from pathlib import Path from pathlib import Path
import platform import platform
if platform.system() == "Windows": # if platform.system() == "Windows":
import asyncio # import asyncio
#
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
headers = { headers = {
+2
View File
@@ -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"), Config.get_config("search_anime", "SEARCH_ANIME_MAX_INFO"),
) )
if anime_report: if anime_report:
if isinstance(anime_report, str):
await search_anime.finish(anime_report)
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
mes_list = custom_forward_msg(anime_report, bot.self_id) mes_list = custom_forward_msg(anime_report, bot.self_id)
await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list) await bot.send_group_forward_msg(group_id=event.group_id, messages=mes_list)
+4 -4
View File
@@ -3,18 +3,18 @@ import feedparser
from urllib import parse from urllib import parse
from services.log import logger from services.log import logger
from utils.http_utils import AsyncHttpx from utils.http_utils import AsyncHttpx
from typing import List from typing import List, Union
import time 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() s_time = time.time()
repass = ""
url = "https://share.dmhy.org/topics/rss/rss.xml?keyword=" + parse.quote(key_word) url = "https://share.dmhy.org/topics/rss/rss.xml?keyword=" + parse.quote(key_word)
try: try:
repass = await get_repass(url, max_) repass = await get_repass(url, max_)
except Exception as e: 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") repass.insert(0, f"搜索 {key_word} 结果(耗时 {int(time.time() - s_time)} 秒):\n")
return repass return repass
+1 -1
View File
@@ -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() data = (await AsyncHttpx.post(API_URL_SAUCENAO, params=params)).json()
if data["header"]["status"] != 0: if data["header"]["status"] != 0:
return "Saucenao识图失败.." return f"Saucenao识图失败..status:{data['header']['status']}"
data = data["results"] data = data["results"]
data = ( data = (
data data
+12 -25
View File
@@ -4,7 +4,7 @@ from services.log import logger
from models.sign_group_user import SignGroupUser from models.sign_group_user import SignGroupUser
from nonebot.message import run_postprocessor from nonebot.message import run_postprocessor
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from typing import Optional, Type from typing import Optional, Type, Any
from gino.exceptions import UninitializedError from gino.exceptions import UninitializedError
from utils.utils import ( from utils.utils import (
is_number, is_number,
@@ -33,7 +33,7 @@ from .data_source import (
from nonebot.adapters.onebot.v11.exception import ActionFailed from nonebot.adapters.onebot.v11.exception import ActionFailed
from configs.config import Config, NICKNAME from configs.config import Config, NICKNAME
from utils.manager import withdraw_message_manager from utils.manager import withdraw_message_manager
from nonebot.params import CommandArg, Command from nonebot.params import CommandArg, Command, RegexGroup
from typing import Tuple from typing import Tuple
import re import re
@@ -190,7 +190,7 @@ num_key = {
@setu_reg.handle() @setu_reg.handle()
async def _(event: MessageEvent, arg: Message = CommandArg()): async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
if isinstance(event, GroupMessageEvent): if isinstance(event, GroupMessageEvent):
impression = ( impression = (
await SignGroupUser.ensure(event.user_id, event.group_id) await SignGroupUser.ensure(event.user_id, event.group_id)
@@ -198,28 +198,15 @@ async def _(event: MessageEvent, arg: Message = CommandArg()):
luox = get_luoxiang(impression) luox = get_luoxiang(impression)
if luox: if luox:
await setu.finish(luox, at_sender=True) await setu.finish(luox, at_sender=True)
msg = arg.extract_plain_text().strip() num, tags = reg_group
num = 1 num = num or 1
msg = re.search(r"(.*)[份发张个次点](.*)[瑟涩色]图", msg) tags = tags[:-1] if tags and tags[-1] == "的" else tags
# 解析 tags 以及 num if num_key.get(num):
if msg: num = num_key[num]
num = msg.group(1) try:
tags = msg.group(2) num = int(num)
if tags: except ValueError:
tags = tags[:-1] if tags[-1] == "的" else tags num = 1
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
await send_setu_handle(setu_reg, event, "色图", tags, num, 0) 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" f"--> /{path}/{image.local_id}.jpg"
) )
os.rename( os.rename(
TEMP_PATH / f"/{image.local_id}.jpg", TEMP_PATH / f"{image.local_id}.jpg",
path / f"{image.local_id}.jpg", path / f"{image.local_id}.jpg",
) )
except FileNotFoundError: except FileNotFoundError:
+7 -5
View File
@@ -1,3 +1,5 @@
from typing import Tuple, Any
from .group_user_checkin import ( from .group_user_checkin import (
group_user_check_in, group_user_check_in,
group_user_check, 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 import GroupMessageEvent, Message
from nonebot.adapters.onebot.v11.permission import GROUP from nonebot.adapters.onebot.v11.permission import GROUP
from utils.message_builder import image from utils.message_builder import image
from nonebot import on_command from nonebot import on_command, on_regex
from utils.utils import scheduler from utils.utils import scheduler
from nonebot.params import CommandArg from nonebot.params import CommandArg, RegexGroup
from pathlib import Path from pathlib import Path
from configs.path_config import DATA_PATH from configs.path_config import DATA_PATH
from services.log import logger from services.log import logger
@@ -68,7 +70,7 @@ except (FileNotFoundError, ValueError, TypeError):
data = {"0": []} 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( my_sign = on_command(
cmd="我的签到", aliases={"好感度"}, priority=5, permission=GROUP, block=True cmd="我的签到", aliases={"好感度"}, priority=5, permission=GROUP, block=True
) )
@@ -85,13 +87,13 @@ total_sign_rank = on_command(
@sign.handle() @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 nickname = event.sender.card or event.sender.nickname
await sign.send( await sign.send(
await group_user_check_in(nickname, event.user_id, event.group_id), await group_user_check_in(nickname, event.user_id, event.group_id),
at_sender=True, at_sender=True,
) )
if arg.extract_plain_text().strip() == "all": if reg_group[0]:
await check_in_all(nickname, event.user_id) await check_in_all(nickname, event.user_id)
+13 -16
View File
@@ -55,13 +55,10 @@ async def get_card(
date = datetime.now().date() date = datetime.now().date()
_type = "view" if is_card_view else "sign" _type = "view" if is_card_view else "sign"
card_file = ( card_file = (
Path(SIGN_TODAY_CARD_PATH) Path(SIGN_TODAY_CARD_PATH) / f"{user_id}_{user.group_id}_{_type}_{date}.png"
/ f"{user_id}_{user.group_id}_{_type}_{date}.png"
) )
if card_file.exists(): if card_file.exists():
return image( return image(f"{user_id}_{user.group_id}_{_type}_{date}.png", "sign/today_card")
f"{user_id}_{user.group_id}_{_type}_{date}.png", "sign/today_card"
)
else: else:
if add_impression == -1: if add_impression == -1:
card_file = ( card_file = (
@@ -75,9 +72,7 @@ async def get_card(
) )
is_card_view = True is_card_view = True
ava = BytesIO(await get_user_avatar(user_id)) ava = BytesIO(await get_user_avatar(user_id))
uid = await GroupInfoUser.get_group_member_uid( uid = await GroupInfoUser.get_group_member_uid(user.user_qq, user.group_id)
user.user_qq, user.group_id
)
impression_list = None impression_list = None
if is_card_view: if is_card_view:
_, impression_list, _ = await SignGroupUser.get_all_impression( _, impression_list, _ = await SignGroupUser.get_all_impression(
@@ -128,9 +123,13 @@ def _generate_card(
level, next_impression, previous_impression = get_level_and_next_impression( level, next_impression, previous_impression = get_level_and_next_impression(
user.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, 0), f"· 好感度等级:{level} [{lik2relation[level]}]")
info_img.text((0, 20), f"· {NICKNAME}对你的态度:{level2attitude[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_bk = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar_white.png")
bar = BuildImage(220, 20, background=SIGN_RESOURCE_PATH / "bar.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()}", f"上次签到日期:{'从未' if user.checkin_time_last == datetime.min else user.checkin_time_last.date()}",
) )
today_data.text((0, 25), f"总金币:{gold}") 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( today_data.text(
(0, 50), (0, 50),
f"色图概率:{(default_setu_prob + user.impression if user.impression < 100 else 100):.2f}%", 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_sign_text_img, (550, 180), True)
bk.paste(today_data, (580, 220), True) bk.paste(today_data, (580, 220), True)
bk.paste(watermark, (15, 400), True) bk.paste(watermark, (15, 400), True)
bk.save( bk.save(SIGN_TODAY_CARD_PATH / f"{user_id}_{user.group_id}_{_type}_{data}.png")
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")
)
return image(
f"{user_id}_{user.group_id}_{_type}_{data}.png", "sign/today_card"
)
def generate_progress_bar_pic(): def generate_progress_bar_pic():
+1 -1
View File
@@ -96,7 +96,7 @@ async def _(
global _prefix_count_dict global _prefix_count_dict
if ( if (
matcher.type == "message" 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"] and matcher.plugin_name not in ["update_info", "statistics_handle"]
): ):
module = matcher.plugin_name module = matcher.plugin_name
+2 -2
View File
@@ -5,8 +5,8 @@ import asyncio
import platform import platform
import os import os
if platform.system() == "Windows": # if platform.system() == "Windows":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
url = "https://github.com/Mrs4s/go-cqhttp/releases" url = "https://github.com/Mrs4s/go-cqhttp/releases"
+3 -3
View File
@@ -12,7 +12,7 @@ __plugin_usage__ = """
usage: usage:
普普通通的查天气吧 普普通通的查天气吧
指令: 指令:
[城市]天气/天气[城市] [城市]天气
""".strip() """.strip()
__plugin_des__ = "出门要看看天气,不要忘了带伞" __plugin_des__ = "出门要看看天气,不要忘了带伞"
__plugin_cmd__ = ["[城市]天气/天气[城市]"] __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() @weather.handle()
async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()): async def _(event: MessageEvent, reg_group: Tuple[Any, ...] = RegexGroup()):
msg = reg_group[0] msg = reg_group[0]
if msg[-1] != "市": if msg and msg[-1] != "市":
msg += "市" msg += "市"
city = "" city = ""
if msg: if msg:
+5 -3
View File
@@ -1,8 +1,10 @@
from nonebot.adapters.onebot.v11 import MessageEvent, GroupMessageEvent, Message, Bot 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 .data_source import get_anime
from nonebot import on_command from nonebot import on_command
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.params import Arg
from utils.utils import get_message_img from utils.utils import get_message_img
from services.log import logger from services.log import logger
@@ -31,10 +33,10 @@ what_anime = on_command("识番", priority=5, block=True)
@what_anime.handle() @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()) img_url = get_message_img(event.json())
if img_url: if img_url:
state["img_url"] = img_url[0] state["img_url"] = args
@what_anime.got("img_url", prompt="虚空识番?来图来图GKD") @what_anime.got("img_url", prompt="虚空识番?来图来图GKD")
+11 -5
View File
@@ -7,9 +7,8 @@ class WordBankBuilder:
def __init__(self, user_id: int, group_id: int, problem: str): def __init__(self, user_id: int, group_id: int, problem: str):
self._data = { self._data = {
"user_id": user_id, "user_id": user_id,
"group_id": group_id, "group_id": group_id}
"problem": problem self.problem = problem
}
def set_placeholder(self, id_: int, placeholder: Union[str, int]): def set_placeholder(self, id_: int, placeholder: Union[str, int]):
""" """
@@ -28,13 +27,20 @@ class WordBankBuilder:
""" """
self._data["answer"] = answer 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"] user_id = self._data["user_id"]
group_id = self._data["group_id"] group_id = self._data["group_id"]
problem = self._data["problem"] problem = self._data["problem"]
answer = self._data["answer"] answer = self._data["answer"]
placeholder = self._data.get("placeholder") 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): async def update(self, index):
user_id = self._data["user_id"] user_id = self._data["user_id"]
+10 -2
View File
@@ -1,12 +1,20 @@
import re
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Event 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 nonebot.typing import T_State
from .model import WordBank from .model import WordBank
async def check(bot: Bot, event: Event, state: T_State) -> bool: async def check(bot: Bot, event: Event, state: T_State) -> bool:
if isinstance(event, GroupMessageEvent): 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( 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 return False
+75 -2
View File
@@ -3,9 +3,11 @@ from ._rule import check
from .model import WordBank from .model import WordBank
from configs.path_config import DATA_PATH from configs.path_config import DATA_PATH
from nonebot.adapters.onebot.v11 import GroupMessageEvent 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 nonebot import on_message
from models.group_member_info import GroupInfoUser from models.group_member_info import GroupInfoUser
from utils.utils import get_message_img_file, is_number
import re
__zx_plugin_name__ = "词库问答回复操作 [Hidden]" __zx_plugin_name__ = "词库问答回复操作 [Hidden]"
@@ -17,8 +19,14 @@ message_handle = on_message(priority=6, block=True, rule=check)
@message_handle.handle() @message_handle.handle()
async def _(event: GroupMessageEvent): 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( 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)) 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 answer += _a[: _a.find(f"[__placeholder_{idx}]")] + "@" + q.user_name
_a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):] _a = _a[_a.find(f"[__placeholder_{idx}]") + len(f"[__placeholder_{idx}]"):]
return answer + _a 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
-5
View File
@@ -108,11 +108,6 @@ class WordBank(db.Model):
:param group_id: 群号 :param group_id: 群号
:param problem: 问题 :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( q = await cls.query.where(
(cls.group_id == group_id) & (cls.problem == problem) (cls.group_id == group_id) & (cls.problem == problem)
).gino.all() ).gino.all()
+56 -23
View File
@@ -6,7 +6,7 @@ from utils.http_utils import AsyncHttpx
from ._data_source import WordBankBuilder from ._data_source import WordBankBuilder
from utils.message_builder import image from utils.message_builder import image
from utils.image_utils import text2image 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 .model import WordBank
from nonebot.adapters.onebot.v11 import ( from nonebot.adapters.onebot.v11 import (
Bot, Bot,
@@ -19,6 +19,7 @@ import random
import os import os
import re import re
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
from models.group_member_info import GroupInfoUser
__zx_plugin_name__ = "词库问答 [Admin]" __zx_plugin_name__ = "词库问答 [Admin]"
__plugin_usage__ = """ __plugin_usage__ = """
@@ -26,7 +27,7 @@ usage:
对指定问题的随机回答,对相同问题可以设置多个不同回答 对指定问题的随机回答,对相同问题可以设置多个不同回答
删除词条后每个词条的id可能会变化,请查看后再删除 删除词条后每个词条的id可能会变化,请查看后再删除
指令: 指令:
添加词条 ?[模糊/关键字]...答...:添加问答词条,可重复添加相同问题的不同回答 添加词条 ?[模糊/关键字|词]...答...:添加问答词条,可重复添加相同问题的不同回答
删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答 删除词条 [问题/下标] ?[下标]:删除指定词条指定或全部回答
修改词条 [问题/下标] ?[下标/新回答] [新回答]:修改指定词条指定回答默认修改为第一条 修改词条 [问题/下标] ?[下标/新回答] [新回答]:修改指定词条指定回答默认修改为第一条
查看词条 ?[问题/下标]:查看全部词条或对应词条回答 查看词条 ?[问题/下标]:查看全部词条或对应词条回答
@@ -48,8 +49,8 @@ __plugin_cmd__ = [
"修改词条 [问题/下标] ?[下标/新回答] [新回答]", "修改词条 [问题/下标] ?[下标/新回答] [新回答]",
"查看词条 ?[问题/下标]", "查看词条 ?[问题/下标]",
] ]
__plugin_version__ = 0.2 __plugin_version__ = 0.3
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier & yajiwa"
__plugin_settings__ = { __plugin_settings__ = {
"admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL [LEVEL]"), "admin_level": Config.get_config("word_bank", "WORD_BANK_LEVEL [LEVEL]"),
"cmd": ["词库问答", "添加词条", "删除词条", "修改词条", "查看词条"], "cmd": ["词库问答", "添加词条", "删除词条", "修改词条", "查看词条"],
@@ -87,21 +88,21 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State, arg: Message = C
else: else:
_problem = problem _problem = problem
search_type = 0 search_type = 0
if re.search("^关键字(.*)", msg): if re.search("^关键字|词(.*)", msg):
search_type = 1 search_type = 1
elif re.search("^模糊(.*)", msg): elif re.search("^模糊(.*)", msg):
search_type = 2 search_type = 2
_builder = await get__builder(event, _problem, answer, idx) _builder = await get__builder(event, _problem, answer, idx)
if await _builder.save(search_type): if await _builder.save(search_type):
logger.info(f"已保存词条 问:{problem} 答:{msg}") logger.info(f"已保存词条 问:{_builder.problem} 答:{msg}")
await add_word.send(f"已保存词条:{problem}") await add_word.send("已保存词条:" + _builder.problem)
else: else:
await delete_word.send("保存失败,可能是回答重复") await delete_word.send("保存失败,可能是回答重复")
@delete_word.handle() @delete_word.handle()
async def _(event: GroupMessageEvent, arg: Message = CommandArg()): async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip() msg = str(arg).strip()
if not msg: if not msg:
await delete_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看") await delete_word.finish("此命令之后需要跟随指定词条,通过“显示词条“查看")
index = None index = None
@@ -119,17 +120,18 @@ async def _(event: GroupMessageEvent, arg: Message = CommandArg()):
if p: if p:
problem = p[int(x)] problem = p[int(x)]
try: try:
_problem, problem = await get_one_problem(event, problem)
if answer := await WordBank.delete_problem_answer( 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( logger.info(
f"(USER {event.user_id}, GROUP " f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除词条: {problem}" f" 删除词条: {problem}"
) )
else: else:
await delete_word.send(f"删除词条:{problem} 失败,可能该词条不存在") await delete_word.send("删除词条:" + problem + "失败,可能该词条不存在")
except IndexError: except IndexError:
await delete_word.send("指定下标错误...请通过查看词条来确定..") await delete_word.send("指定下标错误...请通过查看词条来确定..")
@@ -165,21 +167,21 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
try: try:
if await _builder.update(index): if await _builder.update(index):
await update_word.send(f"修改词条成功:{problem}") await update_word.send(f"修改词条成功:" + _builder.problem)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP " f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 修改词条: {problem}" f" 修改词条: {problem}"
) )
else: else:
await update_word.send(f"修改词条:{problem} 失败,可能该词条不存在") await update_word.send(f"修改词条:" + _builder.problem + f"失败,可能该词条不存在")
except IndexError: except IndexError:
await update_word.send("指定下标错误...请通过查看词条来确定..") await update_word.send("指定下标错误...请通过查看词条来确定..")
@show_word.handle() @show_word.handle()
async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()): async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
msg = arg.extract_plain_text().strip() msg = str(arg).strip()
if not msg: if not msg:
_problem_list = await WordBank.get_group_all_problem(event.group_id) _problem_list = await WordBank.get_group_all_problem(event.group_id)
if not _problem_list: if not _problem_list:
@@ -225,17 +227,28 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
) )
) )
else: 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: if not _answer_list:
await show_word.send("未收录该词条...") await show_word.send("未收录该词条...")
else: else:
# 解析图片和@ # 解析图片和@
_answer_img_nu_list = [await get_one_answer(event, format, answer, 0) for answer, format in _answer_list] _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) word_nu = len(_answer_img_nu_list)
img_nu = 0 img_nu = 0
answer = f"词条 {msg} 回答:" answer = "词条" + msg + "回答:"
for i, x, in enumerate(_answer_img_nu_list): for i, x, in enumerate(_answer_img_nu_list):
r = re.findall(rf"\[CQ:image,file=", str(x)) r = re.findall(rf"\[CQ:image,file=", str(x))
if r: if r:
@@ -254,15 +267,25 @@ async def _(bot: Bot, event: GroupMessageEvent, arg: Message = CommandArg()):
async def get__builder(event, _problem, answer, idx): 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}").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) _builder = WordBankBuilder(event.user_id, event.group_id, _problem)
problem = ''
_p = _problem
for at_ in get_message_at(event.json()): for at_ in get_message_at(event.json()):
r = re.search(rf"\[CQ:at,qq={at_}]", answer) r = re.search(rf"\[CQ:at,qq={at_}]", answer)
if r: if r:
answer = answer.replace(f"[CQ:at,qq={at_}]", f"[__placeholder_{idx}]", 1) answer = answer.replace(f"[CQ:at,qq={at_}]", f"[__placeholder_{idx}]", 1)
_builder.set_placeholder(idx, at_) _builder.set_placeholder(idx, at_)
idx += 1 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()): for img in get_message_img(event.json()):
_x = img.split("?")[0] _x = img.split("?")[0]
_x_list = img.split("?")
r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", answer) r = re.search(rf"\[CQ:image,file=(.*),url={_x}.*?]", answer)
if r: if r:
rand = random.randint(1, 10000) + random.randint(1, 114514) 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}"): if f"__placeholder_{rand}_{idx}.jpg" not in os.listdir(data_dir / f"{event.group_id}"):
break break
rand = random.randint(1, 10000) + random.randint(1, 114514) rand = random.randint(1, 10000) + random.randint(1, 114514)
for i in range(3): strinfo = re.compile(f"\[CQ:image,file={r.group(1)},url={_x_list[0]}\?{_x_list[1]},subType=\d*?]")
answer = answer.replace(f",subType={i}", "") answer = strinfo.sub(f"[__placeholder_{idx}]", answer)
answer = answer.replace(
rf"[CQ:image,file={r.group(1)},url={img}]",
f"[__placeholder_{idx}]",
)
await AsyncHttpx.download_file( await AsyncHttpx.download_file(
img, data_dir / f"{event.group_id}" / f"__placeholder_{rand}_{idx}.jpg" img, data_dir / f"{event.group_id}" / f"__placeholder_{rand}_{idx}.jpg"
) )
_builder.set_placeholder(idx, f"__placeholder_{rand}_{idx}.jpg") _builder.set_placeholder(idx, f"__placeholder_{rand}_{idx}.jpg")
idx += 1 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_answer(answer)
_builder.set_problem(_problem)
_builder.problem = problem + _p
return _builder return _builder
+196
View File
@@ -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)
+121
View File
@@ -0,0 +1,121 @@
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 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)
# 去除&#91&#93
msg = re.sub("[&#9(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:
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:
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
+8 -8
View File
@@ -11,7 +11,7 @@ china_city = TEXT_PATH / "china_city.json"
data = {} 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]: 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]: if area in data[p]:
province = p province = p
city = area 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"] last_update_time = epidemic_data["lastUpdateTime"]
if area == "中国": if area == "中国":
data_ = epidemic_data["areaTree"][0] data_ = epidemic_data["areaTree"][0]
@@ -59,12 +59,12 @@ async def get_yiqing_data(area: str) -> Union[str, MessageSegment]:
add_confirm = data_["today"]["confirm"] # 新增确诊 add_confirm = data_["today"]["confirm"] # 新增确诊
grade = "" grade = ""
_grade_color = "" _grade_color = ""
if data_["total"].get("grade"): # if data_["total"].get("grade"):
grade = data_["total"]["grade"] # grade = data_["total"]["grade"]
if "中风险" in grade: # if "中风险" in grade:
_grade_color = "#fa9424" # _grade_color = "#fa9424"
else: # else:
_grade_color = "red" # _grade_color = "red"
dead_rate = f"{dead / confirm * 100:.2f}" # 死亡率 dead_rate = f"{dead / confirm * 100:.2f}" # 死亡率
heal_rate = f"{heal / confirm * 100:.2f}" # 治愈率 heal_rate = f"{heal / confirm * 100:.2f}" # 治愈率
Generated
+764 -804
View File
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -6,15 +6,14 @@ authors = ["HibiKier <775757368@qq.com>"]
license = "AGPL" license = "AGPL"
[[tool.poetry.source]] [[tool.poetry.source]]
name = "tsinghua" name = "ali"
default = true default = true
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://mirrors.aliyun.com/pypi/simple/"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.8" python = "^3.8"
nonebot2 = "^2.0.0-beta.2" nonebot2 = "^2.0.0-beta.2"
nonebot-adapter-onebot = "^2.0.0-beta.1" nonebot-adapter-onebot = "^2.0.0-beta.1"
bilibili-api = "^9.1.0"
aiofiles = "^0.8.0" aiofiles = "^0.8.0"
aiohttp = "3.7.4.post0" aiohttp = "3.7.4.post0"
beautifulsoup4 = "4.9.3" beautifulsoup4 = "4.9.3"
@@ -40,6 +39,9 @@ dateparser = "^1.1.0"
cn2an = "^0.5.16" cn2an = "^0.5.16"
python-jose = "^3.3.0" python-jose = "^3.3.0"
python-multipart = "^0.0.5" python-multipart = "^0.0.5"
bilireq = "^0.1.2"
emoji = "^1.7.0"
wordcloud = "^1.8.1"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

-2
View File
@@ -15,8 +15,6 @@ _browser: Optional[Browser] = None
async def init(**kwargs) -> Optional[Browser]: async def init(**kwargs) -> Optional[Browser]:
global _browser global _browser
if platform.system() == "Windows":
return None
try: try:
browser = await async_playwright().start() browser = await async_playwright().start()
_browser = await browser.chromium.launch(**kwargs) _browser = await browser.chromium.launch(**kwargs)
View File
+6
View File
@@ -0,0 +1,6 @@
+126
View File
@@ -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()
View File
+2 -2
View File
@@ -327,14 +327,14 @@ class AsyncPlaywright:
await page.set_viewport_size(viewport_size) await page.set_viewport_size(viewport_size)
if isinstance(element, str): if isinstance(element, str):
if wait_time: 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: else:
card = await page.query_selector(element) card = await page.query_selector(element)
else: else:
card = page card = page
for e in element: for e in element:
if wait_time: 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: else:
card = await card.query_selector(e) card = await card.query_selector(e)
await card.screenshot(path=path, timeout=timeout, type=type_) await card.screenshot(path=path, timeout=timeout, type=type_)
+10 -7
View File
@@ -121,7 +121,7 @@ def fig2b64(plt_: plt) -> str:
return "base64://" + base64_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: str = "CJGaoDeGuo.otf",
font_color: Union[str, Tuple[int, int, int]] = "black", font_color: Union[str, Tuple[int, int, int]] = "black",
padding: Union[int, Tuple[int, int, int, int]] = 0, padding: Union[int, Tuple[int, int, int, int]] = 0,
_add_height: float = 0,
) -> BuildImage: ) -> BuildImage:
""" """
说明: 说明:
@@ -1358,6 +1359,7 @@ async def text2image(
:param font: 普通字体 :param font: 普通字体
:param font_color: 普通字体颜色 :param font_color: 普通字体颜色
:param padding: 文本外边距,元组类型时为 (上,左,下,右) :param padding: 文本外边距,元组类型时为 (上,左,下,右)
:param _add_height: 由于get_size无法返回正确的高度,采用手动方式额外添加高度
""" """
pw = ph = top_padding = left_padding = 0 pw = ph = top_padding = left_padding = 0
if padding: if padding:
@@ -1489,17 +1491,18 @@ async def text2image(
else: else:
width = 0 width = 0
height = 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"): for x in text.split("\n"):
x = x if x.strip() else "正" if x:
w, h = _tmp.getsize(x) w, _ = _tmp.getsize(x)
height += h _, h = _tmp.getsize("正")
width = width if width > w else w height += h + _add_height
width = width if width > w else w
width += pw width += pw
height += ph height += ph
A = BuildImage( A = BuildImage(
width + left_padding, width + left_padding,
height + top_padding, height + top_padding + 2,
font_size=font_size, font_size=font_size,
color=color, color=color,
font=font, font=font,
+14
View File
@@ -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
View File
@@ -1,7 +1,7 @@
from datetime import datetime from datetime import datetime
from collections import defaultdict from collections import defaultdict
from nonebot import require 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 typing import List, Union, Optional, Type, Any
from nonebot.adapters.onebot.v11 import Bot, Message from nonebot.adapters.onebot.v11 import Bot, Message
from nonebot.matcher import matchers, Matcher 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"])) qq_list.append(int(msg["data"]["qq"]))
else: else:
for seg in data: for seg in data:
if seg.type == "image": if seg.type == "at":
qq_list.append(seg.data["url"]) qq_list.append(seg.data["qq"])
return qq_list return qq_list
@@ -229,6 +229,25 @@ def get_message_img(data: Union[str, Message]) -> List[str]:
return img_list 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: def get_message_text(data: Union[str, Message]) -> str:
""" """
说明: 说明:
@@ -370,6 +389,8 @@ def change_pixiv_image_links(
url = img_sp[0] url = img_sp[0]
img_type = img_sp[1] img_type = img_sp[1]
url = url.replace("original", "master") + f"_master1200.{img_type}" 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: if nginx_url:
url = ( url = (
url.replace("i.pximg.net", nginx_url) url.replace("i.pximg.net", nginx_url)