update 0.0.6.2

This commit is contained in:
HibiKi 2021-11-23 21:44:59 +08:00
parent c802dfda22
commit 3de487fb24
251 changed files with 14413 additions and 14002 deletions

View File

@ -100,6 +100,7 @@
- [x] 移动图片 (同上) - [x] 移动图片 (同上)
- [x] 删除图片 (同上) - [x] 删除图片 (同上)
- [x] 群内B站订阅 - [x] 群内B站订阅
- [x] 群词条
### 已实现的超级用户功能 ### 已实现的超级用户功能
- [x] 添加/删除权限(是真寻的管理员权限,不是群管理员) - [x] 添加/删除权限(是真寻的管理员权限,不是群管理员)
@ -272,7 +273,7 @@
</details> </details>
## 详细配置请前往文档,以下为最简部署和配置 ## 详细配置请前往文档,以下为最简部署和配置如果你有基础并学习过nonebot2的话
## 简单部署 ## 简单部署
@ -320,6 +321,18 @@ python bot.py
## 更新 ## 更新
### 2021/11/23
* 替换cos API
* 提供私聊b了即跨群b了用户
* 修复游戏抽卡导入角色失败(原神)
* 修复无Pixiv代理时报错
* 将项目中大部分aiohttp替换为httpx
* 删除了丘丘人翻译插件
* 新增群词条
* 修复游戏抽卡碧蓝航线bwiki格式更改导致获取报错
* 首次启动会生成配置文件后停止程序,配置后再次启动即可
### 2021/11/18 ### 2021/11/18
* 修复超级用户无法正确拉真寻入群 * 修复超级用户无法正确拉真寻入群

View File

@ -1 +1 @@
__version__: v0.0.6.1 __version__: v0.0.6.2

0
basic_plugins/__init__.py Normal file → Executable file
View File

2
basic_plugins/admin_bot_manage/__init__.py Normal file → Executable file
View File

@ -4,7 +4,7 @@ import nonebot
Config.add_plugin_config( Config.add_plugin_config(
"admin_bot_manage:custom_welcome_message", "admin_bot_manage:custom_welcome_message",
"SET_GROUP_WELCOME_MESSAGE_LEVEL", "SET_GROUP_WELCOME_MESSAGE_LEVEL [LEVEL]",
2, 2,
name="群管理员操作", name="群管理员操作",
help_="设置群欢迎消息权限", help_="设置群欢迎消息权限",

0
basic_plugins/admin_bot_manage/admin_config.py Normal file → Executable file
View File

View File

10
basic_plugins/admin_bot_manage/data_source.py Normal file → Executable file
View File

@ -12,8 +12,7 @@ from models.level_user import LevelUser
from configs.config import Config from configs.config import Config
from utils.manager import group_manager, plugins2settings_manager, plugins_manager from utils.manager import group_manager, plugins2settings_manager, plugins_manager
from utils.image_utils import CreateImg from utils.image_utils import CreateImg
import aiofiles from utils.http_utils import AsyncHttpx
import aiohttp
import asyncio import asyncio
import time import time
import os import os
@ -73,12 +72,7 @@ async def custom_group_welcome(
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}") logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息 {msg}")
result += msg result += msg
if img: if img:
async with aiohttp.ClientSession() as session: await AsyncHttpx.download_file(img, DATA_PATH + f"custom_welcome_msg/{group_id}.jpg")
async with session.get(img, proxy=get_local_proxy()) as response:
async with aiofiles.open(
DATA_PATH + f"custom_welcome_msg/{group_id}.jpg", "wb"
) as f:
await f.write(await response.read())
img_result = image(abspath=DATA_PATH + f"custom_welcome_msg/{group_id}.jpg") img_result = image(abspath=DATA_PATH + f"custom_welcome_msg/{group_id}.jpg")
logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片") logger.info(f"USER {user_id} GROUP {group_id} 更换群欢迎消息图片")
except Exception as e: except Exception as e:

0
basic_plugins/admin_bot_manage/rule.py Normal file → Executable file
View File

0
basic_plugins/admin_bot_manage/switch_rule.py Normal file → Executable file
View File

0
basic_plugins/admin_bot_manage/timing_task.py Normal file → Executable file
View File

View File

0
basic_plugins/admin_help/__init__.py Normal file → Executable file
View File

0
basic_plugins/admin_help/data_source.py Normal file → Executable file
View File

0
basic_plugins/apscheduler/__init__.py Normal file → Executable file
View File

20
basic_plugins/ban/__init__.py Normal file → Executable file
View File

@ -3,7 +3,7 @@ from models.ban_user import BanUser
from models.level_user import LevelUser from models.level_user import LevelUser
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot from nonebot.adapters.cqhttp import Bot
from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent from nonebot.adapters.cqhttp import GroupMessageEvent, PrivateMessageEvent, MessageEvent
from utils.utils import get_message_at, get_message_text, is_number from utils.utils import get_message_at, get_message_text, is_number
from configs.config import NICKNAME, Config from configs.config import NICKNAME, Config
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
@ -24,10 +24,14 @@ usage
""".strip() """.strip()
__plugin_superuser_usage__ = """ __plugin_superuser_usage__ = """
usage usage
屏蔽用户消息相当于最上级.ban b了=屏蔽用户消息相当于最上级.ban
跨群ban以及跨群b了
指令 指令
b了 [at] b了 [at/qq]
.ban [user_id] ?[小时] ?[分钟]
示例b了 @user 示例b了 @user
示例b了 1234567
示例.ban 12345567
""".strip() """.strip()
__plugin_des__ = '你被逮捕了!丢进小黑屋!' __plugin_des__ = '你被逮捕了!丢进小黑屋!'
__plugin_cmd__ = ['.ban [at] ?[小时] ?[分钟]', '.unban [at]', 'b了 [at] [_superuser]'] __plugin_cmd__ = ['.ban [at] ?[小时] ?[分钟]', '.unban [at]', 'b了 [at] [_superuser]']
@ -182,8 +186,14 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
@super_ban.handle() @super_ban.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
qq = get_message_at(event.json()) if isinstance(event, GroupMessageEvent):
qq = get_message_at(event.json())
else:
qq = get_message_text(event.json())
if not is_number(qq):
await super_ban.finish("对象qq必须为纯数字...")
qq = [qq]
if qq: if qq:
qq = qq[0] qq = qq[0]
user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq) user = await bot.get_group_member_info(group_id=event.group_id, user_id=qq)

0
basic_plugins/broadcast/__init__.py Normal file → Executable file
View File

12
basic_plugins/group_handle/__init__.py Normal file → Executable file
View File

@ -30,15 +30,15 @@ __plugin_version__ = 0.1
__plugin_author__ = "HibiKier" __plugin_author__ = "HibiKier"
__plugin_task__ = {"group_welcome": "进群欢迎", "refund_group_remind": "退群提醒"} __plugin_task__ = {"group_welcome": "进群欢迎", "refund_group_remind": "退群提醒"}
Config.add_plugin_config( Config.add_plugin_config(
"auto_invite", "message", f"请不要未经同意就拉{NICKNAME}入群!告辞!", help_="强制拉群后进群回复的内容.." "invite_manager", "message", f"请不要未经同意就拉{NICKNAME}入群!告辞!", help_="强制拉群后进群回复的内容.."
) )
Config.add_plugin_config( Config.add_plugin_config(
"auto_invite", "flag", True, help_="被强制拉群后是否直接退出", default_value=True "invite_manager", "flag", True, help_="被强制拉群后是否直接退出", default_value=True
) )
Config.add_plugin_config( Config.add_plugin_config(
"auto_invite", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5 "invite_manager", "welcome_msg_cd", 5, help_="群欢迎消息cd", default_value=5
) )
_flmt = FreqLimiter(Config.get_config("auto_invite", "welcome_msg_cd")) _flmt = FreqLimiter(Config.get_config("invite_manager", "welcome_msg_cd"))
# 群员增加处理 # 群员增加处理
@ -55,10 +55,10 @@ async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict):
group = await GroupInfo.get_group_info(event.group_id) group = await GroupInfo.get_group_info(event.group_id)
# 群聊不存在或被强制拉群,退出该群 # 群聊不存在或被强制拉群,退出该群
if (not group or group.group_flag == 0) and Config.get_config( if (not group or group.group_flag == 0) and Config.get_config(
"auto_invite", "flag" "invite_manager", "flag"
): ):
try: try:
msg = Config.get_config("auto_invite", "message") msg = Config.get_config("invite_manager", "message")
if msg: if msg:
await bot.send_group_msg(group_id=event.group_id, message=msg) await bot.send_group_msg(group_id=event.group_id, message=msg)
await bot.set_group_leave(group_id=event.group_id) await bot.set_group_leave(group_id=event.group_id)

0
basic_plugins/help/__init__.py Normal file → Executable file
View File

0
basic_plugins/help/data_source.py Normal file → Executable file
View File

0
basic_plugins/hooks/__init__.py Normal file → Executable file
View File

0
basic_plugins/hooks/auth_hook.py Normal file → Executable file
View File

0
basic_plugins/hooks/ban_hook.py Normal file → Executable file
View File

0
basic_plugins/hooks/chkdsk_hook.py Normal file → Executable file
View File

0
basic_plugins/hooks/limit_hook.py Normal file → Executable file
View File

0
basic_plugins/hooks/other_hook.py Normal file → Executable file
View File

0
basic_plugins/hooks/utils.py Normal file → Executable file
View File

0
basic_plugins/hooks/withdraw_message_hook.py Normal file → Executable file
View File

9
basic_plugins/init_plugin_config/__init__.py Normal file → Executable file
View File

@ -9,10 +9,12 @@ from .init_plugins_limit import (
init_plugins_count_limit, init_plugins_count_limit,
init_plugins_cd_limit, init_plugins_cd_limit,
) )
from .init import init
from .check_plugin_status import check_plugin_status from .check_plugin_status import check_plugin_status
from nonebot.adapters.cqhttp import Bot from nonebot.adapters.cqhttp import Bot
from configs.path_config import DATA_PATH from configs.path_config import DATA_PATH
from services.log import logger from services.log import logger
from pathlib import Path
from nonebot import Driver from nonebot import Driver
import nonebot import nonebot
@ -30,6 +32,11 @@ def _():
""" """
初始化数据 初始化数据
""" """
_flag = False
config_file = Path(DATA_PATH) / "configs" / "plugins2config.yaml"
if not config_file.exists():
_flag = True
init()
init_plugins_settings(DATA_PATH) init_plugins_settings(DATA_PATH)
init_plugins_cd_limit(DATA_PATH) init_plugins_cd_limit(DATA_PATH)
init_plugins_block_limit(DATA_PATH) init_plugins_block_limit(DATA_PATH)
@ -42,6 +49,8 @@ def _():
if x: if x:
for key in x.keys(): for key in x.keys():
plugins_manager.block_plugin(key, block_type=x[key]) plugins_manager.block_plugin(key, block_type=x[key])
if _flag:
raise Exception("首次运行已在configs目录下生成配置文件config.yaml修改后重启即可...")
logger.info("初始化数据完成...") logger.info("初始化数据完成...")

View File

View File

@ -0,0 +1,14 @@
from utils.manager import plugins2settings_manager
def init():
if plugins2settings_manager.get("update_pic"):
plugins2settings_manager["update_picture"] = plugins2settings_manager["update_pic"]
plugins2settings_manager.delete("update_pic")
if plugins2settings_manager.get("white2black_img"):
plugins2settings_manager["white2black_image"] = plugins2settings_manager["white2black_img"]
plugins2settings_manager.delete("white2black_img")
if plugins2settings_manager.get("send_img"):
plugins2settings_manager["send_image"] = plugins2settings_manager["send_img"]
plugins2settings_manager.delete("send_img")

0
basic_plugins/init_plugin_config/init_group_manager.py Normal file → Executable file
View File

View File

View File

0
basic_plugins/init_plugin_config/init_plugins_data.py Normal file → Executable file
View File

0
basic_plugins/init_plugin_config/init_plugins_limit.py Normal file → Executable file
View File

View File

View File

@ -17,23 +17,19 @@ def init_plugins_settings(data_path: str):
plugins2settings_file = Path(data_path) / "configs" / "plugins2settings.yaml" plugins2settings_file = Path(data_path) / "configs" / "plugins2settings.yaml"
plugins2settings_file.parent.mkdir(exist_ok=True, parents=True) plugins2settings_file.parent.mkdir(exist_ok=True, parents=True)
_matchers = get_matchers() _matchers = get_matchers()
_data = {}
if plugins2settings_file.exists():
with open(plugins2settings_file, "r", encoding="utf8") as f:
_data = _yaml.load(f)
_data = _data["PluginSettings"] if _data else {}
_tmp_module = {} _tmp_module = {}
_tmp = [] _tmp = []
for x in plugins2settings_manager.keys():
try:
_plugin = nonebot.plugin.get_plugin(x)
_module = _plugin.module
plugin_name = _module.__getattribute__("__zx_plugin_name__")
_tmp_module[x] = plugin_name
except (KeyError, AttributeError) as e:
logger.error(f"配置文件 模块:{x} 获取 plugin_name 失败...{e}")
_tmp_module[x] = ""
for matcher in _matchers: for matcher in _matchers:
if matcher.module in _data.keys(): if matcher.module not in plugins2settings_manager.keys():
plugins2settings_manager.add_plugin_settings(
matcher.module,
plugin_type=_data[matcher.module]["plugin_type"],
data_dict=_data[matcher.module],
)
if _data[matcher.module]["cmd"]:
_tmp_module[matcher.module] = _data[matcher.module]["cmd"][0]
else:
_plugin = nonebot.plugin.get_plugin(matcher.module) _plugin = nonebot.plugin.get_plugin(matcher.module)
try: try:
_module = _plugin.module _module = _plugin.module
@ -52,7 +48,7 @@ def init_plugins_settings(data_path: str):
except (AttributeError, KeyError): except (AttributeError, KeyError):
level = 5 level = 5
cmd = None cmd = None
if not level: if level is None:
level = 5 level = 5
admin_manager.add_admin_plugin_settings( admin_manager.add_admin_plugin_settings(
matcher.module, cmd, level matcher.module, cmd, level
@ -76,7 +72,7 @@ def init_plugins_settings(data_path: str):
"__plugin_settings__" "__plugin_settings__"
) )
if ( if (
plugin_settings["cmd"] plugin_settings["cmd"] is not None
and plugin_name not in plugin_settings["cmd"] and plugin_name not in plugin_settings["cmd"]
): ):
plugin_settings["cmd"].append(plugin_name) plugin_settings["cmd"].append(plugin_name)

View File

@ -46,11 +46,11 @@ async def _(bot: Bot, event: FriendRequestEvent, state: dict):
user_id=int(list(bot.config.superusers)[0]), user_id=int(list(bot.config.superusers)[0]),
message=f"*****一份好友申请*****\n" message=f"*****一份好友申请*****\n"
f"昵称:{nickname}({event.user_id})\n" f"昵称:{nickname}({event.user_id})\n"
f"自动同意:{'' if Config.get_config('auto_invite', 'AUTO_ADD_FRIEND') else '×'}\n" f"自动同意:{'' if Config.get_config('invite_manager', 'AUTO_ADD_FRIEND') else '×'}\n"
f"日期:{str(datetime.now()).split('.')[0]}\n" f"日期:{str(datetime.now()).split('.')[0]}\n"
f"备注:{event.comment}", f"备注:{event.comment}",
) )
if Config.get_config("auto_invite", "AUTO_ADD_FRIEND"): if Config.get_config("invite_manager", "AUTO_ADD_FRIEND"):
await bot.set_friend_add_request(flag=event.flag, approve=True) await bot.set_friend_add_request(flag=event.flag, approve=True)
await FriendUser.add_friend_info(user["user_id"], user["nickname"]) await FriendUser.add_friend_info(user["user_id"], user["nickname"])
else: else:

0
basic_plugins/nickname.py Normal file → Executable file
View File

56
basic_plugins/scripts.py Normal file → Executable file
View File

@ -3,12 +3,11 @@ from services.db_context import db
from asyncpg.exceptions import DuplicateColumnError from asyncpg.exceptions import DuplicateColumnError
from models.group_info import GroupInfo from models.group_info import GroupInfo
from nonebot.adapters.cqhttp import Bot from nonebot.adapters.cqhttp import Bot
from utils.user_agent import get_user_agent
from services.log import logger from services.log import logger
from configs.path_config import TEXT_PATH from configs.path_config import TEXT_PATH
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from utils.http_utils import AsyncHttpx
from pathlib import Path from pathlib import Path
import aiohttp
import nonebot import nonebot
try: try:
@ -25,30 +24,34 @@ async def update_city():
""" """
部分插件需要中国省份城市 部分插件需要中国省份城市
这里直接更新避免插件内代码重复 这里直接更新避免插件内代码重复
:return:
""" """
china_city = Path(TEXT_PATH) / "china_city.json" china_city = Path(TEXT_PATH) / "china_city.json"
data = {} data = {}
try: try:
async with aiohttp.ClientSession(headers=get_user_agent()) as session: res = await AsyncHttpx.get(
async with session.get( "http://www.weather.com.cn/data/city3jdata/china.html", timeout=5
"http://www.weather.com.cn/data/city3jdata/china.html", timeout=5 )
) as res: res.encoding = "utf8"
provinces_data = json.loads(await res.text(encoding="utf8")) provinces_data = json.loads(res.text)
for province in provinces_data.keys(): for province in provinces_data.keys():
data[provinces_data[province]] = [] data[provinces_data[province]] = []
async with session.get( res = await AsyncHttpx.get(
f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html", f"http://www.weather.com.cn/data/city3jdata/provshi/{province}.html",
timeout=5, timeout=5,
) as res: )
city_data = json.loads(await res.text(encoding="utf8")) res.encoding = "utf8"
for city in city_data.keys(): city_data = json.loads(res.text)
data[provinces_data[province]].append(city_data[city]) for city in city_data.keys():
data[provinces_data[province]].append(city_data[city])
with open(china_city, "w", encoding="utf8") as f: with open(china_city, "w", encoding="utf8") as f:
json.dump(data, f, indent=4, ensure_ascii=False) json.dump(data, f, indent=4, ensure_ascii=False)
logger.info("自动更新城市列表完成.....") logger.info("自动更新城市列表完成.....")
except TimeoutError: except TimeoutError:
logger.info("自动更新城市列表超时.....") logger.warning("自动更新城市列表超时.....")
except ValueError:
logger.warning("自动城市列表失败.....")
except Exception as e:
logger.error(f"自动城市列表未知错误 {type(e)}{e}")
@driver.on_startup @driver.on_startup
@ -57,7 +60,7 @@ async def _():
数据库表结构变换 数据库表结构变换
""" """
sql_str = [ sql_str = [
"ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;" # group_info表添加一个group_flag "ALTER TABLE group_info ADD group_flag Integer NOT NULL DEFAULT 0;" # group_info表添加一个group_flag
] ]
for sql in sql_str: for sql in sql_str:
try: try:
@ -75,13 +78,7 @@ async def _(bot: Bot):
# 清空不存在的群聊信息并将已所有已存在的群聊group_flag设置为1认证所有已存在的群 # 清空不存在的群聊信息并将已所有已存在的群聊group_flag设置为1认证所有已存在的群
if not await GroupInfo.get_group_info(114514): if not await GroupInfo.get_group_info(114514):
# 标识符,该功能只需执行一次 # 标识符,该功能只需执行一次
await GroupInfo.add_group_info( await GroupInfo.add_group_info(114514, "114514", 114514, 114514, 1)
114514,
"114514",
114514,
114514,
1
)
group_list = await bot.get_group_list() group_list = await bot.get_group_list()
group_list = [g["group_id"] for g in group_list] group_list = [g["group_id"] for g in group_list]
_gl = [x.group_id for x in await GroupInfo.get_all_group()] _gl = [x.group_id for x in await GroupInfo.get_all_group()]
@ -98,14 +95,9 @@ async def _(bot: Bot):
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 1,
) )
logger.info(f"已将群聊 {group_id} 添加认证...") logger.info(f"已将群聊 {group_id} 添加认证...")
else: else:
await GroupInfo.delete_group_info(group_id) await GroupInfo.delete_group_info(group_id)
logger.info(f"移除不存在的群聊信息:{group_id}") logger.info(f"移除不存在的群聊信息:{group_id}")

0
basic_plugins/super_cmd/__init__.py Normal file → Executable file
View File

0
basic_plugins/super_cmd/bot_friend_group.py Normal file → Executable file
View File

0
basic_plugins/super_cmd/clear_data.py Normal file → Executable file
View File

0
basic_plugins/super_cmd/data_source.py Normal file → Executable file
View File

0
basic_plugins/super_cmd/manager_group.py Normal file → Executable file
View File

0
basic_plugins/super_cmd/reload_setting.py Normal file → Executable file
View File

0
basic_plugins/super_cmd/set_admin_permissions.py Normal file → Executable file
View File

0
basic_plugins/super_cmd/super_task_switch.py Normal file → Executable file
View File

0
basic_plugins/super_cmd/update_friend_group_info.py Normal file → Executable file
View File

0
basic_plugins/super_help/__init__.py Normal file → Executable file
View File

0
basic_plugins/super_help/data_source.py Normal file → Executable file
View File

0
basic_plugins/update_info.py Normal file → Executable file
View File

0
models/bag_user.py Normal file → Executable file
View File

2
models/ban_user.py Normal file → Executable file
View File

@ -82,7 +82,7 @@ class BanUser(db.Model):
参数 参数
:param user_qq: 目标用户qq号 :param user_qq: 目标用户qq号
:param ban_level: 使用ban命令用户的权限 :param ban_level: 使用ban命令用户的权限
:param duration: ban时长 :param duration: ban时长
""" """
query = cls.query.where((cls.user_qq == user_qq)) query = cls.query.where((cls.user_qq == user_qq))
query = query.with_for_update() query = query.with_for_update()

0
models/friend_user.py Normal file → Executable file
View File

0
models/group_info.py Normal file → Executable file
View File

0
models/group_member_info.py Normal file → Executable file
View File

0
models/level_user.py Normal file → Executable file
View File

0
models/sign_group_user.py Normal file → Executable file
View File

0
plugins/__init__.py Normal file → Executable file
View File

0
plugins/aconfig/__init__.py Normal file → Executable file
View File

2
plugins/ai/__init__.py Normal file → Executable file
View File

@ -24,8 +24,6 @@ __plugin_version__ = 0.1
__plugin_author__ = 'HibiKier' __plugin_author__ = 'HibiKier'
__plugin_settings__ = { __plugin_settings__ = {
"level": 5, "level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["Ai", "ai", "AI", "aI"], "cmd": ["Ai", "ai", "AI", "aI"],
} }
__plugin_configs__ = { __plugin_configs__ = {

130
plugins/ai/data_source.py Normal file → Executable file
View File

@ -1,13 +1,12 @@
import os import os
import random import random
import re import re
from utils.http_utils import AsyncHttpx
import aiohttp
from aiohttp.client import ClientSession
from configs.path_config import IMAGE_PATH, DATA_PATH from configs.path_config import IMAGE_PATH, DATA_PATH
from services.log import logger from services.log import logger
from utils.message_builder import image, face from utils.message_builder import image, face
from configs.config import Config, NICKNAME from configs.config import Config, NICKNAME
from .utils import ai_message_manager
try: try:
import ujson as json import ujson as json
@ -26,7 +25,7 @@ anime_data = json.load(open(DATA_PATH + "anime.json", "r", encoding="utf8"))
async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) -> str: async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str) -> str:
""" """
获取 AI 返回值顺序图灵 -> 青云客 获取 AI 返回值顺序 特殊回复 -> 图灵 -> 青云客
:param text: 问题 :param text: 问题
:param img_url: 图片链接 :param img_url: 图片链接
:param user_id: 用户id :param user_id: 用户id
@ -34,6 +33,11 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
:return: 回答 :return: 回答
""" """
global index global index
ai_message_manager.add_message(user_id, text)
special_rst = await ai_message_manager.get_result(user_id, nickname)
if special_rst:
ai_message_manager.add_result(user_id, special_rst)
return special_rst
if index == 5: if index == 5:
index = 0 index = 0
if len(text) < 6 and random.random() < 0.6: if len(text) < 6 and random.random() < 0.6:
@ -41,10 +45,9 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
for key in keys: for key in keys:
if text.find(key) != -1: if text.find(key) != -1:
return random.choice(anime_data[key]).replace("", nickname) return random.choice(anime_data[key]).replace("", nickname)
async with aiohttp.ClientSession() as sess: rst = await tu_ling(text, img_url, user_id)
rst = await tu_ling(text, img_url, user_id, sess) if not rst:
if not rst: rst = await xie_ai(text)
rst = await xie_ai(text, sess)
if not rst: if not rst:
return no_result() return no_result()
if nickname: if nickname:
@ -55,23 +58,25 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
if nickname.find("大人") == -1: if nickname.find("大人") == -1:
nickname += "大~人~" nickname += "大~人~"
rst = rst.replace("小主人", nickname).replace("小朋友", nickname) rst = rst.replace("小主人", nickname).replace("小朋友", nickname)
ai_message_manager.add_result(user_id, rst)
print(ai_message_manager)
return rst return rst
# 图灵接口 # 图灵接口
async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession) -> str: async def tu_ling(text: str, img_url: str, user_id: int) -> str:
""" """
获取图灵接口的回复 获取图灵接口的回复
:param text: 问题 :param text: 问题
:param img_url: 图片链接 :param img_url: 图片链接
:param user_id: 用户id :param user_id: 用户id
:param sess: AIOHTTP SESSION
:return: 图灵回复 :return: 图灵回复
""" """
global index global index
TL_KEY = Config.get_config("ai", "TL_KEY") TL_KEY = Config.get_config("ai", "TL_KEY")
req = None
if not TL_KEY: if not TL_KEY:
return '' return ""
try: try:
if text: if text:
req = { req = {
@ -98,62 +103,59 @@ async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession) ->
index = 0 index = 0
return "" return ""
text = "" text = ""
async with sess.post(url, json=req) as response: response = await AsyncHttpx.post(url, json=req)
if response.status != 200: if response.status_code != 200:
return no_result() return no_result()
resp_payload = json.loads(await response.text()) resp_payload = json.loads(response.text)
if int(resp_payload["intent"]["code"]) in [4003]: if int(resp_payload["intent"]["code"]) in [4003]:
return "" return ""
if resp_payload["results"]: if resp_payload["results"]:
for result in resp_payload["results"]: for result in resp_payload["results"]:
if result["resultType"] == "text": if result["resultType"] == "text":
text = result["values"]["text"] text = result["values"]["text"]
if "请求次数超过" in text: if "请求次数超过" in text:
text = "" text = ""
return text return text
# 屑 AI # 屑 AI
async def xie_ai(text: str, sess: ClientSession) -> str: async def xie_ai(text: str) -> str:
""" """
获取青云客回复 获取青云客回复
:param text: 问题 :param text: 问题
:param sess: AIOHTTP SESSION
:return: 青云可回复 :return: 青云可回复
""" """
async with sess.get( res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}")
f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}" content = ""
) as res: data = json.loads(res.text)
content = "" if data["result"] == 0:
data = json.loads(await 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:
content = content[: content.find("提示")] return ""
if "淘宝" 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, sess)
)
def hello() -> str: def hello() -> str:
@ -196,21 +198,19 @@ def no_result() -> str:
) )
async def check_text(text: str, sess: ClientSession) -> str: async def check_text(text: str) -> str:
""" """
ALAPI文本检测主要针对青云客API检测为恶俗文本改为无回复的回答 ALAPI文本检测主要针对青云客API检测为恶俗文本改为无回复的回答
:param text: 回复 :param text: 回复
:param sess: AIOHTTP SESSION
""" """
if not Config.get_config("alapi", "ALAPI_TOKEN"): if not Config.get_config("alapi", "ALAPI_TOKEN"):
return text return text
params = {"token": Config.get_config("alapi", "ALAPI_TOKEN"), "text": text} params = {"token": Config.get_config("alapi", "ALAPI_TOKEN"), "text": text}
try: try:
async with sess.get(check_url, timeout=2, params=params) as response: data = (await AsyncHttpx.get(check_url, timeout=2, params=params)).json()
data = await response.json() if data["code"] == 200:
if data["code"] == 200: if data["data"]["conclusion_type"] == 2:
if data["data"]["conclusion_type"] == 2: return ""
return ""
except Exception as e: except Exception as e:
logger.error(f"检测违规文本错误...{type(e)}{e}") logger.error(f"检测违规文本错误...{type(e)}{e}")
return text return text

137
plugins/ai/utils.py Executable file
View File

@ -0,0 +1,137 @@
from utils.manager import StaticData
from configs.config import NICKNAME
from models.ban_user import BanUser
from typing import Optional
import random
import time
class AiMessageManager(StaticData):
def __init__(self):
super().__init__(None)
self._same_message = [
"为什么要发一样的话?",
"请不要再重复对我说一句话了,不然我就要生气了!",
"别再发这句话了,我已经知道了...",
"你是只会说这一句话吗?",
"[*],你发我也发!",
"[uname][*]",
f"救命!有笨蛋一直给{NICKNAME}发一样的话!",
"这句话你已经给我发了{}次了,再发就生气!",
]
self._repeat_message = [
f"请不要学{NICKNAME}说话",
f"为什么要一直学{NICKNAME}说话?",
"你再学!你再学我就生气了!",
f"呜呜,你是想欺负{NICKNAME}嘛..",
"[uname]不要再学我说话了!",
"再学我说话,我就把你拉进黑名单(生气",
"你再学![uname]是个笨蛋!",
"你已经学我说话{}次了!别再学了!",
]
def add_message(self, user_id: int, message: str):
"""
添加用户消息
:param user_id: 用户id
:param message: 消息内容
"""
if message:
if self._data.get(user_id) is None:
self._data[user_id] = {
"time": time.time(),
"message": [],
"result": [],
"repeat_count": 0,
}
if time.time() - self._data[user_id]["time"] > 60 * 10:
self._data[user_id]["message"].clear()
self._data[user_id]["time"] = time.time()
self._data[user_id]["message"].append(message.strip())
def add_result(self, user_id: int, message: str):
"""
添加回复用户的消息
:param user_id: 用户id
:param message: 回复消息内容
"""
if message:
if self._data.get(user_id) is None:
self._data[user_id] = {
"time": time.time(),
"message": [],
"result": [],
"repeat_count": 0,
}
if time.time() - self._data[user_id]["time"] > 60 * 10:
self._data[user_id]["result"].clear()
self._data[user_id]["repeat_count"] = 0
self._data[user_id]["time"] = time.time()
self._data[user_id]["result"].append(message.strip())
async def get_result(self, user_id: int, nickname: str) -> Optional[str]:
"""
特殊消息特殊回复
:param user_id: 用户id
:param nickname: 用户昵称
"""
if len(self._data[user_id]["message"]) < 2:
return None
msg = await self._get_user_repeat_message_result(user_id)
if not msg:
msg = await self._get_user_same_message_result(user_id)
if msg:
if "[uname]" in msg:
msg = msg.replace("[uname]", nickname)
if not msg.startswith("生气了!你好烦,闭嘴!") and "[*]" in msg:
msg = msg.replace("[*]", self._data[user_id]["message"][-1])
return msg
async def _get_user_same_message_result(self, user_id: int) -> Optional[str]:
"""
重复消息回复
:param user_id: 用户id
"""
msg = self._data[user_id]["message"][-1]
cnt = 0
_tmp = self._data[user_id]["message"][:-1]
_tmp.reverse()
for s in _tmp:
if s == msg:
cnt += 1
else:
break
if cnt > 1:
if random.random() < 0.5 and cnt > 3:
rand = random.randint(60, 300)
await BanUser.ban(user_id, 9, rand)
self._data[user_id]["message"].clear()
return f"生气了!你好烦,闭嘴!给我老实安静{rand}"
return random.choice(self._same_message).format(cnt)
return None
async def _get_user_repeat_message_result(self, user_id: int) -> Optional[str]:
"""
复读真寻的消息回复
:param user_id: 用户id
"""
msg = self._data[user_id]["message"][-1]
if self._data[user_id]["result"]:
rst = self._data[user_id]["result"][-1]
else:
return None
if msg == rst:
self._data[user_id]["repeat_count"] += 1
cnt = self._data[user_id]["repeat_count"]
if cnt > 1:
if random.random() < 0.5 and cnt > 3:
rand = random.randint(60, 300)
await BanUser.ban(user_id, 9, rand)
self._data[user_id]["result"].clear()
self._data[user_id]["repeat_count"] = 0
return f"生气了!你好烦,闭嘴!给我老实安静{rand}"
return random.choice(self._repeat_message).format(cnt)
return None
ai_message_manager = AiMessageManager()

0
plugins/alapi/__init__.py Normal file → Executable file
View File

0
plugins/alapi/comments_163.py Normal file → Executable file
View File

0
plugins/alapi/cover.py Normal file → Executable file
View File

24
plugins/alapi/data_source.py Normal file → Executable file
View File

@ -4,7 +4,7 @@ from utils.message_builder import image
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from typing import Optional from typing import Optional
from configs.config import Config from configs.config import Config
import aiohttp from utils.http_utils import AsyncHttpx
async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str], int": async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str], int":
@ -16,18 +16,16 @@ async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str]
if not params: if not params:
params = {} params = {}
params["token"] = Config.get_config("alapi", "ALAPI_TOKEN") params["token"] = Config.get_config("alapi", "ALAPI_TOKEN")
async with aiohttp.ClientSession() as session: try:
try: data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
async with session.get(url, timeout=2, params=params) as response: if data["code"] == 200:
data = await response.json() if not data["data"]:
if data["code"] == 200: return "没有搜索到...", 997
if not data["data"]: return data, 200
return "没有搜索到...", 997 else:
return data, 200 return f'发生了错误...code{data["code"]}', 999
else: except TimeoutError:
return f'发生了错误...code{data["code"]}', 999 return "超时了....", 998
except TimeoutError:
return "超时了....", 998
def gen_wbtop_pic(data: dict) -> MessageSegment: def gen_wbtop_pic(data: dict) -> MessageSegment:

48
plugins/alapi/jitang.py Executable file
View File

@ -0,0 +1,48 @@
from nonebot import on_command
from services.log import logger
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from utils.http_utils import AsyncHttpx
from configs.config import Config
from .data_source import get_data
__zx_plugin_name__ = "鸡汤"
__plugin_usage__ = """
usage
不喝点什么感觉有点不舒服
指令
鸡汤
""".strip()
__plugin_des__ = "喏,亲手为你煮的鸡汤"
__plugin_cmd__ = ["鸡汤"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["鸡汤", "毒鸡汤"],
}
url = "https://v2.alapi.cn/api/soul"
jitang = on_command("鸡汤", aliases={"毒鸡汤"}, priority=5, block=True)
@jitang.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
try:
data, code = await get_data(url)
if code != 200:
await jitang.finish(data, at_sender=True)
await jitang.send(data["data"]["content"])
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送鸡汤:" + data["data"]["content"]
)
except Exception as e:
await jitang.send("鸡汤煮坏掉了...")
logger.error(f"鸡汤煮坏掉了 {type(e)}{e}")

0
plugins/alapi/poetry.py Normal file → Executable file
View File

55
plugins/alapi/wbtop.py Normal file → Executable file
View File

@ -3,13 +3,13 @@ from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State from nonebot.typing import T_State
from services.log import logger from services.log import logger
from .data_source import get_data, gen_wbtop_pic from .data_source import get_data, gen_wbtop_pic
from utils.browser import get_browser
from utils.utils import get_message_text, is_number from utils.utils import get_message_text, is_number
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.message_builder import image from utils.message_builder import image
from utils.http_utils import AsyncPlaywright
import asyncio import asyncio
__zx_plugin_name__ = '微博热搜' __zx_plugin_name__ = "微博热搜"
__plugin_usage__ = """ __plugin_usage__ = """
usage usage
在QQ上吃个瓜 在QQ上吃个瓜
@ -18,21 +18,21 @@ usage
微博热搜 [id]截图该热搜页面 微博热搜 [id]截图该热搜页面
示例微博热搜 5 示例微博热搜 5
""".strip() """.strip()
__plugin_des__ = '刚买完瓜,在吃瓜现场' __plugin_des__ = "刚买完瓜,在吃瓜现场"
__plugin_cmd__ = ['微博热搜', '微博热搜 [id]'] __plugin_cmd__ = ["微博热搜", "微博热搜 [id]"]
__plugin_version__ = 0.1 __plugin_version__ = 0.1
__plugin_author__ = 'HibiKier' __plugin_author__ = "HibiKier"
__plugin_settings__ = { __plugin_settings__ = {
"level": 5, "level": 5,
"default_status": True, "default_status": True,
"limit_superuser": False, "limit_superuser": False,
"cmd": ['微博热搜'], "cmd": ["微博热搜"],
} }
wbtop = on_command("wbtop", aliases={'微博热搜'}, priority=5, block=True) wbtop = on_command("wbtop", aliases={"微博热搜"}, priority=5, block=True)
wbtop_url = 'https://v2.alapi.cn/api/new/wbtop' wbtop_url = "https://v2.alapi.cn/api/new/wbtop"
wbtop_data = [] wbtop_data = []
@ -45,32 +45,27 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
data, code = await get_data(wbtop_url) data, code = await get_data(wbtop_url)
if code != 200: if code != 200:
await wbtop.finish(data, at_sender=True) await wbtop.finish(data, at_sender=True)
wbtop_data = data['data'] wbtop_data = data["data"]
if not msg: if not msg:
img = await asyncio.get_event_loop().run_in_executor(None, gen_wbtop_pic, wbtop_data) img = await asyncio.get_event_loop().run_in_executor(
None, gen_wbtop_pic, wbtop_data
)
await wbtop.send(img) await wbtop.send(img)
logger.info( logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查询微博热搜") f" 查询微博热搜"
)
if is_number(msg) and 0 < int(msg) <= 50: if is_number(msg) and 0 < int(msg) <= 50:
url = wbtop_data[int(msg) - 1]['url'] url = wbtop_data[int(msg) - 1]["url"]
browser = await get_browser()
page = None
try: try:
if not browser: await wbtop.send("开始截取数据...")
logger.warning('获取 browser 失败,请部署至 linux 环境....') img = await AsyncPlaywright.screenshot(
await wbtop.finish('获取 browser 对象失败...') url,
page = await browser.new_page() f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
await page.goto(url, wait_until='networkidle', timeout=10000) "#pl_feedlist_index",
await page.set_viewport_size({"width": 2560, "height": 1080}) sleep=5
await asyncio.sleep(5) )
div = await page.query_selector("#pl_feedlist_index") await wbtop.send(img)
await div.screenshot(path=f'{IMAGE_PATH}/temp/wbtop_{event.user_id}.png', timeout=100000)
await page.close()
await wbtop.send(image(f'wbtop_{event.user_id}.png', 'temp'))
except Exception as e: except Exception as e:
logger.error(f'微博热搜截图出错... {type(e)}: {e}') logger.error(f"微博热搜截图出错... {type(e)}: {e}")
if page: await wbtop.send("发生了一些错误.....")
await page.close()
await wbtop.send('发生了一些错误.....')

1
plugins/bilibili_sub/__init__.py Normal file → Executable file
View File

@ -206,6 +206,7 @@ async def _():
await sub_manager.reload_sub_data() await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data() sub = await sub_manager.random_sub_data()
if sub: if sub:
logger.info(f"Bilibili订阅开始检测{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type) rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot) await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live": if sub.sub_type == "live":

49
plugins/bilibili_sub/data_source.py Normal file → Executable file
View File

@ -14,7 +14,7 @@ from datetime import datetime
from utils.browser import get_browser from utils.browser import get_browser
from services.db_context import db from services.db_context import db
from services.log import logger from services.log import logger
import aiohttp from utils.http_utils import AsyncHttpx
import random import random
@ -150,7 +150,7 @@ async def delete_sub(sub_id: str, sub_user: str) -> str:
:param sub_id: 订阅 id :param sub_id: 订阅 id
:param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group) :param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group)
""" """
if await BilibiliSub.delete_bilibili_sub(sub_id, sub_user): if await BilibiliSub.delete_bilibili_sub(int(sub_id), sub_user):
return f"已成功取消订阅:{sub_id}" return f"已成功取消订阅:{sub_id}"
else: else:
return f"取消订阅:{sub_id} 失败请检查是否订阅过该Id...." return f"取消订阅:{sub_id} 失败请检查是否订阅过该Id...."
@ -162,30 +162,27 @@ async def get_media_id(keyword: str) -> dict:
:param keyword: 番剧名称 :param keyword: 番剧名称
""" """
params = {"keyword": keyword} params = {"keyword": keyword}
async with aiohttp.ClientSession() as session: for _ in range(3):
for _ in range(3): try:
try: _season_data = {}
_season_data = {} response = await AsyncHttpx.get(bilibili_search_url, params=params, timeout=5)
async with session.get( if response.status_code == 200:
bilibili_search_url, timeout=5, params=params data = response.json()
) as response: if data.get("data"):
if response.status == 200: for item in data["data"]["result"]:
data = await response.json() if item["result_type"] == "media_bangumi":
if data.get("data"): idx = 0
for item in data["data"]["result"]: for x in item["data"]:
if item["result_type"] == "media_bangumi": _season_data[idx] = {
idx = 0 "media_id": x["media_id"],
for x in item["data"]: "title": x["title"]
_season_data[idx] = { .replace('<em class="keyword">', "")
"media_id": x["media_id"], .replace("</em>", ""),
"title": x["title"] }
.replace('<em class="keyword">', "") idx += 1
.replace("</em>", ""), return _season_data
} except TimeoutError:
idx += 1 pass
return _season_data
except TimeoutError:
pass
return {} return {}

0
plugins/bilibili_sub/model.py Normal file → Executable file
View File

39
plugins/bilibili_sub/utils.py Normal file → Executable file
View File

@ -1,12 +1,12 @@
from utils.image_utils import CreateImg from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx
from pathlib import Path from pathlib import Path
from bilibili_api import user from bilibili_api import user
from io import BytesIO from io import BytesIO
import aiohttp
BORDER_PATH = Path(IMAGE_PATH) / 'border' BORDER_PATH = Path(IMAGE_PATH) / "border"
BORDER_PATH.mkdir(parents=True, exist_ok=True) BORDER_PATH.mkdir(parents=True, exist_ok=True)
@ -16,9 +16,7 @@ async def get_pic(url: str) -> bytes:
:param url: 图像链接 :param url: 图像链接
:return: 图像二进制 :return: 图像二进制
""" """
async with aiohttp.ClientSession() as session: return (await AsyncHttpx.get(url, timeout=10)).content
async with session.get(url, timeout=2) as response:
return await response.read()
async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des: str): async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des: str):
@ -33,17 +31,26 @@ async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des
""" """
u = user.User(uid) u = user.User(uid)
user_info = await u.get_user_info() user_info = await u.get_user_info()
name = user_info['name'] name = user_info["name"]
sex = user_info['sex'] sex = user_info["sex"]
face = user_info['face'] face = user_info["face"]
sign = user_info['sign'] sign = user_info["sign"]
ava = CreateImg(100, 100, background=BytesIO(await get_pic(face))) ava = CreateImg(100, 100, background=BytesIO(await get_pic(face)))
ava.circle() ava.circle()
cover = CreateImg(470, 265, background=BytesIO(await get_pic(cover))) cover = CreateImg(470, 265, background=BytesIO(await get_pic(cover)))
print() print()
def _create_live_des_image(title: str, cover: CreateImg, tags: str, des: str, user_name: str, sex: str, sign: str, ava: CreateImg): def _create_live_des_image(
title: str,
cover: CreateImg,
tags: str,
des: str,
user_name: str,
sex: str,
sign: str,
ava: CreateImg,
):
""" """
生成主播简介图片 生成主播简介图片
:param title: 直播间标题 :param title: 直播间标题
@ -56,17 +63,9 @@ def _create_live_des_image(title: str, cover: CreateImg, tags: str, des: str, us
:param ava: 主播头像 :param ava: 主播头像
:return: :return:
""" """
border = BORDER_PATH / '0.png' border = BORDER_PATH / "0.png"
border_img = None border_img = None
if border.exists(): if border.exists():
border_img = CreateImg(1772, 2657, background=border) border_img = CreateImg(1772, 2657, background=border)
bk = CreateImg(1772, 2657, font_size=30) bk = CreateImg(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type='by_width') bk.paste(cover, (0, 100), center_type="by_width")

5
plugins/bt/__init__.py Normal file → Executable file
View File

@ -7,7 +7,6 @@ from nonebot.adapters.cqhttp import PrivateMessageEvent
from utils.utils import get_message_text from utils.utils import get_message_text
from nonebot.adapters.cqhttp.permission import PRIVATE from nonebot.adapters.cqhttp.permission import PRIVATE
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from aiohttp.client_exceptions import ServerDisconnectedError
__zx_plugin_name__ = "磁力搜索" __zx_plugin_name__ = "磁力搜索"
__plugin_usage__ = """ __plugin_usage__ = """
@ -89,11 +88,9 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
send_flag = True send_flag = True
except TimeoutError: except TimeoutError:
await bt.finish(f"搜索 {keyword} 超时...") await bt.finish(f"搜索 {keyword} 超时...")
except ServerDisconnectedError:
await bt.finish(f"搜索 {keyword} 连接失败")
except Exception as e: except Exception as e:
await bt.finish(f"bt 其他未知错误..") await bt.finish(f"bt 其他未知错误..")
logger.error(f"bt 错误 e{e}") logger.error(f"bt 错误 {type(e)}{e}")
if not send_flag: if not send_flag:
await bt.send(f"{keyword} 未搜索到...") await bt.send(f"{keyword} 未搜索到...")
logger.info(f"USER {event.user_id} BT搜索 {keyword}{page}") logger.info(f"USER {event.user_id} BT搜索 {keyword}{page}")

69
plugins/bt/data_source.py Normal file → Executable file
View File

@ -1,8 +1,6 @@
from utils.user_agent import get_user_agent from utils.http_utils import AsyncHttpx
import aiohttp
from configs.config import Config from configs.config import Config
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from utils.utils import get_local_proxy
import platform import platform
if platform.system() == "Windows": if platform.system() == "Windows":
@ -15,36 +13,39 @@ url = "http://www.eclzz.world"
async def get_bt_info(keyword: str, page: str): async def get_bt_info(keyword: str, page: str):
async with aiohttp.ClientSession(headers=get_user_agent()) as session: """
async with session.get( 获取资源信息
f"{url}/s/{keyword}_rel_{page}.html", proxy=get_local_proxy(), timeout=5 :param keyword: 关键词
) as response: :param page: 页数
text = await response.text() """
if text.find("大约0条结果") != -1: text = (await AsyncHttpx.get(f"{url}/s/{keyword}_rel_{page}.html", timeout=5)).text
return if text.find("大约0条结果") != -1:
soup = BeautifulSoup(text, "lxml") return
item_lst = soup.find_all("div", {"class": "search-item"}) soup = BeautifulSoup(text, "lxml")
bt_max_num = Config.get_config("bt", "BT_MAX_NUM") item_lst = soup.find_all("div", {"class": "search-item"})
bt_max_num = bt_max_num if bt_max_num < len(item_lst) else len(item_lst) bt_max_num = Config.get_config("bt", "BT_MAX_NUM")
for item in item_lst[:bt_max_num]: bt_max_num = bt_max_num if bt_max_num < len(item_lst) else len(item_lst)
divs = item.find_all("div") for item in item_lst[:bt_max_num]:
title = ( divs = item.find_all("div")
str(divs[0].find("a").text) title = (
.replace("<em>", "") str(divs[0].find("a").text)
.replace("</em>", "") .replace("<em>", "")
.strip() .replace("</em>", "")
) .strip()
spans = divs[2].find_all("span") )
itype = spans[0].text spans = divs[2].find_all("span")
create_time = spans[1].find("b").text itype = spans[0].text
file_size = spans[2].find("b").text create_time = spans[1].find("b").text
link = await get_download_link(divs[0].find("a")["href"], session) file_size = spans[2].find("b").text
yield title, itype, create_time, file_size, link link = await get_download_link(divs[0].find("a")["href"])
yield title, itype, create_time, file_size, link
async def get_download_link(_url: str, session) -> str: async def get_download_link(_url: str) -> str:
async with session.get( """
f"{url}{_url}", proxy=get_local_proxy(), timeout=30 获取资源下载地址
) as response: :param _url: 链接
soup = BeautifulSoup(await response.text(), "lxml") """
return soup.find("a", {"id": "down-url"})["href"] text = (await AsyncHttpx.get(f"{url}{_url}")).text
soup = BeautifulSoup(text, "lxml")
return soup.find("a", {"id": "down-url"})["href"]

12
plugins/c_song/__init__.py Normal file → Executable file
View File

@ -25,22 +25,22 @@ __plugin_settings__ = {
} }
songpicker = on_command("点歌", priority=5, block=True) music = on_command("点歌", priority=5, block=True)
@songpicker.handle() @music.handle()
async def handle_first_receive(bot: Bot, event: Event, state: T_State): async def handle_first_receive(bot: Bot, event: Event, state: T_State):
args = str(event.get_message()).strip() args = str(event.get_message()).strip()
if args: if args:
state["song_name"] = args state["song_name"] = args
@songpicker.got("song_name", prompt="歌名是?") @music.got("song_name", prompt="歌名是?")
async def _(bot: Bot, event: Event, state: T_State): async def _(bot: Bot, event: Event, state: T_State):
song = state["song_name"] song = state["song_name"]
song_id = await get_song_id(song) song_id = await get_song_id(song)
if not song_id: if not song_id:
await songpicker.finish("没有找到这首歌!", at_sender=True) await music.finish("没有找到这首歌!", at_sender=True)
for _ in range(3): for _ in range(3):
song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}] song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}]
logger.info( logger.info(
@ -48,9 +48,9 @@ async def _(bot: Bot, event: Event, state: T_State):
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})" f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 点歌 :{song}" f" 点歌 :{song}"
) )
await songpicker.finish(song_content) await music.finish(song_content)
else: else:
await songpicker.finish("网易云繁忙...") await music.finish("网易云繁忙...")

51
plugins/c_song/music_163.py Normal file → Executable file
View File

@ -1,4 +1,4 @@
import aiohttp from utils.http_utils import AsyncHttpx
import json import json
@ -7,24 +7,22 @@ cookies = {"appver": "2.0.2"}
async def search_song(song_name: str): async def search_song(song_name: str):
async with aiohttp.ClientSession( """
headers=headers, cookies=cookies 搜索歌曲
) as session: :param song_name: 歌名
async with session.post( """
f"http://music.163.com/api/search/get/", r = await AsyncHttpx.post(
data={"s": song_name, "limit": 1, "type": 1, "offset": 0}, f"http://music.163.com/api/search/get/",
) as r: data={"s": song_name, "limit": 1, "type": 1, "offset": 0},
if r.status != 200: )
return None if r.status_code != 200:
r = await r.text() return None
return json.loads(r) return json.loads(r.text)
async def get_song_id(songName: str) -> int: async def get_song_id(song_name: str) -> int:
""" """ """
根据用户输入的songName 获取候选songId列表 [默认songId数量5] r = await search_song(song_name)
"""
r = await search_song(songName)
return r["result"]["songs"][0]["id"] return r["result"]["songs"][0]["id"]
@ -32,16 +30,9 @@ async def get_song_info(songId: int):
""" """
获取歌曲信息 获取歌曲信息
""" """
async with aiohttp.ClientSession( r = await AsyncHttpx.post(
headers=headers, cookies=cookies f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D",
) as session: )
async with session.post( if r.status_code != 200:
f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D", return None
) as r: return json.loads(r.text)
if r.status != 200:
return None
r = await r.text()
return json.loads(r)

0
plugins/check/__init__.py Normal file → Executable file
View File

33
plugins/check/data_source.py Normal file → Executable file
View File

@ -1,11 +1,7 @@
import psutil import psutil
import aiohttp
import time import time
from datetime import datetime from datetime import datetime
from utils.user_agent import get_user_agent from utils.http_utils import AsyncHttpx
from asyncio.exceptions import TimeoutError
from aiohttp.client_exceptions import ClientConnectorError
from utils.utils import get_local_proxy
from utils.image_utils import CreateImg from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from pathlib import Path from pathlib import Path
@ -34,23 +30,16 @@ class Check:
self.disk = psutil.disk_usage("/").percent self.disk = psutil.disk_usage("/").percent
async def check_network(self): async def check_network(self):
async with aiohttp.ClientSession(headers=get_user_agent()) as session: try:
try: await AsyncHttpx.get("https://www.baidu.com/", timeout=5)
async with session.get( except Exception as e:
"https://www.baidu.com/", proxy=get_local_proxy(), timeout=3 logger.warning(f"访问BaiDu失败... {type(e)}: {e}")
) as response: self.baidu = 404
pass try:
except (TimeoutError, ClientConnectorError) as e: await AsyncHttpx.get("https://www.google.com/", timeout=5)
logger.warning(f"访问BaiDu失败... e: {e}") except Exception as e:
self.baidu = 404 logger.warning(f"访问Google失败... {type(e)}: {e}")
try: self.google = 404
async with session.get(
"https://www.google.com/", proxy=get_local_proxy(), timeout=3
) as response:
pass
except (TimeoutError, ClientConnectorError) as e:
logger.warning(f"访问Google失败... e: {e}")
self.google = 404
def check_user(self): def check_user(self):
rst = "" rst = ""

0
plugins/check_zhenxun_update/__init__.py Normal file → Executable file
View File

43
plugins/check_zhenxun_update/data_source.py Normal file → Executable file
View File

@ -1,18 +1,14 @@
from aiohttp.client_exceptions import ClientConnectorError
from nonebot.adapters.cqhttp import Bot, Message from nonebot.adapters.cqhttp import Bot, Message
from utils.user_agent import get_user_agent
from utils.utils import get_local_proxy
from utils.image_utils import CreateImg from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH from configs.path_config import IMAGE_PATH
from utils.message_builder import image from utils.message_builder import image
from utils.http_utils import AsyncHttpx
from typing import List from typing import List
from services.log import logger from services.log import logger
from pathlib import Path from pathlib import Path
import ujson as json import ujson as json
import nonebot import nonebot
import asyncio import asyncio
import aiofiles
import aiohttp
import platform import platform
import tarfile import tarfile
import shutil import shutil
@ -77,7 +73,7 @@ async def check_update(bot: Bot) -> 'int, str':
message=f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}\n" f"开始更新.....", message=f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}\n" f"开始更新.....",
) )
logger.info(f"开始下载真寻最新版文件....") logger.info(f"开始下载真寻最新版文件....")
if await download_latest_file(tar_gz_url): if await AsyncHttpx.download_file(tar_gz_url, zhenxun_latest_tar_gz):
logger.info("下载真寻最新版文件完成....") logger.info("下载真寻最新版文件完成....")
error = await asyncio.get_event_loop().run_in_executor( error = await asyncio.get_event_loop().run_in_executor(
None, _file_handle, latest_version None, _file_handle, latest_version
@ -194,38 +190,23 @@ def _file_handle(latest_version: str) -> str:
# 获取最新版本号 # 获取最新版本号
async def get_latest_version_data() -> dict: async def get_latest_version_data() -> dict:
async with aiohttp.ClientSession(headers=get_user_agent()) as session: for _ in range(3):
for _ in range(3): try:
try: res = await AsyncHttpx.get(release_url)
async with session.get(release_url, proxy=get_local_proxy()) as res: if res.status_code == 200:
if res.status == 200: return res.json()
return await res.json() except TimeoutError:
except (TimeoutError, ClientConnectorError): pass
pass except Exception as e:
logger.error(f"检查更新真寻获取版本失败 {type(e)}{e}")
return {} return {}
# 下载文件
async def download_latest_file(url_: str) -> bool:
async with aiohttp.ClientSession(headers=get_user_agent()) as session:
for _ in range(3):
try:
async with session.get(url_, proxy=get_local_proxy()) as res:
if res.status == 200:
async with aiofiles.open(zhenxun_latest_tar_gz, "wb") as f:
await f.write(await res.read())
return True
except (TimeoutError, ClientConnectorError):
pass
return False
# 逐行检测 # 逐行检测
def check_old_lines(lines: List[str], line: str) -> str: def check_old_lines(lines: List[str], line: str) -> str:
if "=" not in line: if "=" not in line:
return line return line
for l in lines: for l in lines:
if "=" in l and l.split("=")[0].strip() == line.split("=")[0].strip(): if "=" in l and l.split("=")[0].strip() == line.split("=")[0].strip():
if l.split("=")[1].strip() == 'None': return l
return l
return line return line

46
plugins/coser/__init__.py Normal file → Executable file
View File

@ -1,13 +1,8 @@
from nonebot import on_command from nonebot import on_command
from nonebot.typing import T_State from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent from nonebot.adapters.cqhttp import Bot, MessageEvent
from services.log import logger
from asyncio.exceptions import TimeoutError
from utils.message_builder import image from utils.message_builder import image
from configs.path_config import IMAGE_PATH from services.log import logger
from utils.utils import get_local_proxy
import aiohttp
import aiofiles
__zx_plugin_name__ = "coser" __zx_plugin_name__ = "coser"
__plugin_usage__ = """ __plugin_usage__ = """
@ -32,38 +27,13 @@ coser = on_command(
) )
url = "http://ovooa.com/API/cosplay/api.php" url = "http://iw233.cn/API/cos.php"
@coser.handle() @coser.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
async with aiohttp.ClientSession() as session: try:
try: await coser.send(image(url))
for _ in range(3): except Exception as e:
try: await coser.send("你cos给我看")
async with session.get(url, proxy=get_local_proxy(), timeout=2, verify_ssl=False) as response: logger.error(f"coser 发送了未知错误 {type(e)}{e}")
_url = (await response.json())['text']
async with session.get(
_url, timeout=5, proxy=get_local_proxy(), verify_ssl=False
) as res:
if res.status == 200:
async with aiofiles.open(
f"{IMAGE_PATH}/temp/{event.user_id}_coser.jpg", "wb"
) as f:
await f.write(await res.read())
logger.info(
f"(USER {event.user_id}, "
f"GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送COSER"
)
await coser.send(
image(f"{event.user_id}_coser.jpg", "temp")
)
break
except (TimeoutError, KeyError):
pass
else:
await coser.send("你cos给我看")
except Exception as e:
await coser.send("发生了预料之外的错误..请稍后再试或联系管理员修复...")
logger.error(f"coser 发送了未知错误 {type(e)}{e}")

View File

@ -1,69 +0,0 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent
from utils.utils import get_message_text, is_number
from .data_source import get_csgola_data, get_5e_data
from services.log import logger
__zx_plugin_name__ = "cs国服/平台信息查找"
__plugin_usage__ = """
usage
快速查询csgo战绩和数据
指令
cs国服查询 [steam主页个人id]
5e查询 [5e战绩个人名称]
示例cs国服查询 23848238483
示例5e查询 poster
"""
__plugin_des__ = "什么你也是rush B玩家"
__plugin_cmd__ = ["cs国服查询 [steam主页个人id]", "5e查询 [5e战绩个人名称]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["csgo战绩查询", "cs国服查询", "5e查询"],
}
csgola = on_command("cs国服查询", priority=5, block=True)
csgo5e = on_command("5e查询", priority=5, block=True)
@csgola.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if "http" in msg:
msg = msg[:-1] if msg[-1] == "/" else msg
msg = msg.split("/")[-1]
if not is_number(msg):
await csgola.finish("Id必须为数字", at_sender=True)
await csgola.send("开始查找...")
img, code = await get_csgola_data(int(msg))
if code == 200:
await csgola.send(img, at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查询csgo国服战绩{msg}"
)
else:
await csgola.send(img, at_sender=True)
@csgo5e.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
await csgola.send("开始查找...")
img, code = await get_5e_data(msg)
if code == 200:
await csgo5e.send(img, at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查询csgo国服战绩{msg}"
)
else:
await csgo5e.send(img, at_sender=True)

View File

@ -1,130 +0,0 @@
from configs.path_config import IMAGE_PATH
from utils.image_utils import CreateImg
from utils.message_builder import image
from services.log import logger
from utils.browser import get_browser
from playwright._impl._api_types import TimeoutError
csgola_url = "https://www.csgola.com/player/"
_5e_url = "https://arena.5eplay.com/data/player/"
async def get_csgola_data(uid: int) -> "str, int":
page = None
try:
browser = await get_browser()
if not browser:
return "", 997
page = await browser.new_page()
for _ in range(3):
try:
await page.goto(f"{csgola_url}{uid}", wait_until="networkidle", timeout=10000)
break
except TimeoutError:
pass
else:
return '连接超时...', 995
await page.set_viewport_size({"width": 2560, "height": 1080})
data = await page.query_selector_all(".panel-body")
if not data:
return "未查询到该Id....", 999
await data[0].screenshot(path=f"{IMAGE_PATH}/temp/{uid}_1.png", timeout=100000)
await data[3].screenshot(path=f"{IMAGE_PATH}/temp/{uid}_2.png", timeout=100000)
await data[5].screenshot(path=f"{IMAGE_PATH}/temp/{uid}_3.png", timeout=100000)
await data[7].screenshot(path=f"{IMAGE_PATH}/temp/{uid}_5.png", timeout=100000)
ava = await page.query_selector("div.container:nth-child(4) > div:nth-child(1)")
await ava.screenshot(path=f"{IMAGE_PATH}/temp/{uid}_0.png", timeout=100000)
weapon_data = await page.query_selector(".gun-stats-sec")
await weapon_data.screenshot(
path=f"{IMAGE_PATH}/temp/{uid}_4.png", timeout=100000
)
ava = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_0.png")
statistical_data = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_1.png")
combined_data = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_2.png")
detailed_data = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_3.png")
weapon_data = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_4.png")
map_data = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/{uid}_5.png")
if statistical_data.h > 300:
statistical_data.crop((0, 0, statistical_data.w, 300))
if combined_data.h > 260:
combined_data.crop((0, 0, combined_data.w, 260))
if detailed_data.h > 400:
detailed_data.crop((0, 0, detailed_data.w, 400))
weapon_data.crop((0, 100, weapon_data.w, weapon_data.h))
map_data.crop((0, 310, map_data.w, map_data.h))
height = (
ava.h
+ statistical_data.h
+ combined_data.h
+ detailed_data.h
+ weapon_data.h
+ map_data.h
)
bk = CreateImg(1168, height)
current_h = 0
for img in [
ava,
statistical_data,
combined_data,
detailed_data,
weapon_data,
map_data,
]:
bk.paste(img, (0, current_h))
current_h += img.h
bk.save(f"{IMAGE_PATH}/temp/csgo_{uid}.png")
except Exception as e:
logger.error(f"生成csgola图片错误 {type(e)}{e}")
if page:
await page.close()
return "发生了错误....", 998
if page:
await page.close()
return image(f"csgo_{uid}.png", "temp"), 200
async def get_5e_data(uname: str) -> "str, int":
page = None
try:
browser = await get_browser()
if not browser:
return "", 997
page = await browser.new_page()
await page.goto(f"{_5e_url}{uname}", wait_until="networkidle", timeout=10000)
if "HTTP ERROR 404" in await page.content():
return "未查询到该玩家...", 999
await page.set_viewport_size({"width": 2560, "height": 1080})
body = await page.query_selector("body")
await body.screenshot(
path=f"{IMAGE_PATH}/temp/csgo_{uname}_0.png", timeout=100000
)
await page.click("a.match-tab-item:nth-child(2)")
body = await page.query_selector("body")
await body.screenshot(
path=f"{IMAGE_PATH}/temp/csgo_{uname}_1.png", timeout=100000
)
await page.click("a.match-tab-item:nth-child(1)")
body = await page.query_selector("body")
await body.screenshot(
path=f"{IMAGE_PATH}/temp/csgo_{uname}_2.png", timeout=100000
)
bk = CreateImg(1344 * 3, 2307)
current_w = 0
for i in range(3):
body = CreateImg(0, 0, background=f"{IMAGE_PATH}/temp/csgo_{uname}_{i}.png")
body.crop((600, 90, body.w - 600, body.h - 410))
bk.paste(body, (current_w, 0))
current_w += 1344
bk.save(f"{IMAGE_PATH}/temp/csgo_{uname}.png")
except Exception as e:
logger.error(f"生成5e图片错误 {type(e)}{e}")
if page:
await page.close()
return "发生了错误...", 998
if page:
await page.close()
return image(f"csgo_{uname}.png", "temp"), 200

0
plugins/dialogue/__init__.py Normal file → Executable file
View File

0
plugins/draw_card/__init__.py Normal file → Executable file
View File

59
plugins/draw_card/announcement.py Normal file → Executable file
View File

@ -1,11 +1,11 @@
import aiohttp
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import re
from datetime import datetime, timedelta from datetime import datetime, timedelta
from .config import DRAW_PATH from .config import DRAW_PATH
from pathlib import Path from pathlib import Path
from asyncio.exceptions import TimeoutError from asyncio.exceptions import TimeoutError
from services.log import logger from services.log import logger
from utils.http_utils import AsyncHttpx
import re
try: try:
import ujson as json import ujson as json
except ModuleNotFoundError: except ModuleNotFoundError:
@ -66,16 +66,14 @@ class PrtsAnnouncement:
self.game_name = '明日方舟' self.game_name = '明日方舟'
async def _get_announcement_text(self): async def _get_announcement_text(self):
async with aiohttp.ClientSession(headers=headers) as session: text = (await AsyncHttpx.get(prts_url)).text
async with session.get(prts_url, timeout=7) as res: soup = BeautifulSoup(text, 'lxml')
soup = BeautifulSoup(await res.text(), 'lxml') ol = soup.find('ol', {'class': 'articleList active', 'data-category-key': 'LATEST'})
ol = soup.find('ol', {'class': 'articleList active', 'data-category-key': 'LATEST'}) for li in ol:
for li in ol: itype = li.find('span', {'class': 'articleItemCate'}).text
itype = li.find('span', {'class': 'articleItemCate'}).text if itype == '活动':
if itype == '活动': a = li.find('a')['href']
a = li.find('a')['href'] return (await AsyncHttpx.get(f'https://ak.hypergryph.com{a}')).text
async with session.get(f'https://ak.hypergryph.com{a}', headers=headers, timeout=7) as res:
return await res.text()
async def update_up_char(self): async def update_up_char(self):
prts_up_char.parent.mkdir(parents=True, exist_ok=True) prts_up_char.parent.mkdir(parents=True, exist_ok=True)
@ -147,9 +145,7 @@ class GenshinAnnouncement:
self.game_name = '原神' self.game_name = '原神'
async def _get_announcement_text(self): async def _get_announcement_text(self):
async with aiohttp.ClientSession(headers=headers) as session: return (await AsyncHttpx.get(genshin_url)).text
async with session.get(genshin_url, timeout=7) as res:
return await res.text()
async def update_up_char(self): async def update_up_char(self):
genshin_up_char.parent.mkdir(exist_ok=True, parents=True) genshin_up_char.parent.mkdir(exist_ok=True, parents=True)
@ -218,21 +214,20 @@ class PrettyAnnouncement:
self.game_name = '赛马娘' self.game_name = '赛马娘'
async def _get_announcement_text(self): async def _get_announcement_text(self):
async with aiohttp.ClientSession(headers=headers) as session: text = (await AsyncHttpx.get(pretty_url)).text
async with session.get(pretty_url, timeout=7) as res: soup = BeautifulSoup(text, 'lxml')
soup = BeautifulSoup(await res.text(), 'lxml') divs = soup.find('div', {'id': 'mw-content-text'}).find('div').find_all('div')
divs = soup.find('div', {'id': 'mw-content-text'}).find('div').find_all('div') title = " "
for div in divs: for div in divs:
a = div.find('a') a = div.find('a')
try: try:
title = a['title'] title = a['title']
except (KeyError, TypeError): except (KeyError, TypeError):
continue continue
if title.find('新角色追加') != -1: if title.find('新角色追加') != -1:
url = a['href'] url = a['href']
break break
async with session.get(f'https://wiki.biligame.com/{url}', timeout=7) as res: return (await AsyncHttpx.get(f'https://wiki.biligame.com/{url}')).text, title[:-2]
return await res.text(), title[:-2]
async def update_up_char(self): async def update_up_char(self):
pretty_up_char.parent.mkdir(exist_ok=True, parents=True) pretty_up_char.parent.mkdir(exist_ok=True, parents=True)
@ -343,9 +338,7 @@ class GuardianAnnouncement:
self.game_name = '坎公骑冠剑' self.game_name = '坎公骑冠剑'
async def _get_announcement_text(self): async def _get_announcement_text(self):
async with aiohttp.ClientSession(headers=headers) as session: return (await AsyncHttpx.get(guardian_url)).text
async with session.get(guardian_url, timeout=7) as res:
return await res.text()
async def update_up_char(self): async def update_up_char(self):
data = { data = {

0
plugins/draw_card/async_update_game_info.py Normal file → Executable file
View File

0
plugins/draw_card/azur_handle.py Normal file → Executable file
View File

0
plugins/draw_card/config.py Normal file → Executable file
View File

0
plugins/draw_card/fgo_handle.py Normal file → Executable file
View File

0
plugins/draw_card/genshin_handle.py Normal file → Executable file
View File

0
plugins/draw_card/guardian_handle.py Normal file → Executable file
View File

50
plugins/draw_card/init_card_pool.py Normal file → Executable file
View File

@ -2,6 +2,7 @@ from typing import Any
from .config import DATA_PATH from .config import DATA_PATH
from utils.utils import is_number from utils.utils import is_number
from pathlib import Path from pathlib import Path
from services.log import logger
try: try:
import ujson as json import ujson as json
except ModuleNotFoundError: except ModuleNotFoundError:
@ -27,8 +28,11 @@ def init_game_pool(game: str, data: dict, Operator: Any):
limited = True limited = True
if key.find('阿米娅') != -1: if key.find('阿米娅') != -1:
continue continue
tmp_lst.append(Operator(name=key, star=int(data[key]['星级']), try:
limited=limited, recruit_only=recruit_only, event_only=event_only)) tmp_lst.append(Operator(name=key, star=int(data[key]['星级']),
limited=limited, recruit_only=recruit_only, event_only=event_only))
except Exception as e:
logger.warning(f"明日方舟导入角色 {key} 数据错误:{type(e)}{e}")
if game == 'genshin': if game == 'genshin':
for key in data.keys(): for key in data.keys():
if key.find('旅行者') != -1: if key.find('旅行者') != -1:
@ -36,17 +40,26 @@ def init_game_pool(game: str, data: dict, Operator: Any):
limited = False limited = False
if data[key]['常驻/限定'] == '限定UP': if data[key]['常驻/限定'] == '限定UP':
limited = True limited = True
tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=limited)) try:
tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=limited))
except Exception as e:
logger.warning(f"原神导入角色 {key} 数据错误:{type(e)}{e}")
if game == 'genshin_arms': if game == 'genshin_arms':
for key in data.keys(): for key in data.keys():
if data[key]['获取途径'].find('祈愿') != -1: if data[key]['获取途径'].find('祈愿') != -1:
limited = False limited = False
if data[key]['获取途径'].find('限定祈愿') != -1: if data[key]['获取途径'].find('限定祈愿') != -1:
limited = True limited = True
tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=limited)) try:
tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=limited))
except Exception as e:
logger.warning(f"原神导入武器 {key} 数据错误:{type(e)}{e}")
if game == 'pretty': if game == 'pretty':
for key in data.keys(): for key in data.keys():
tmp_lst.append(Operator(name=key, star=data[key]['初始星级'], limited=False)) try:
tmp_lst.append(Operator(name=key, star=data[key]['初始星级'], limited=False))
except Exception as e:
logger.warning(f"赛马娘导入角色 {key} 数据错误:{type(e)}{e}")
if game == 'pretty_card': if game == 'pretty_card':
for key in data.keys(): for key in data.keys():
limited = False limited = False
@ -54,7 +67,10 @@ def init_game_pool(game: str, data: dict, Operator: Any):
limited = True limited = True
if not data[key]['获取方式']: if not data[key]['获取方式']:
limited = False limited = False
tmp_lst.append(Operator(name=data[key]['中文名'], star=len(data[key]['稀有度']), limited=limited)) try:
tmp_lst.append(Operator(name=data[key]['中文名'], star=len(data[key]['稀有度']), limited=limited))
except Exception as e:
logger.warning(f"赛马娘导入卡片 {key} 数据错误:{type(e)}{e}")
if game in ['guardian', 'guardian_arms']: if game in ['guardian', 'guardian_arms']:
for key in data.keys(): for key in data.keys():
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=False)) tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=False))
@ -63,15 +79,21 @@ def init_game_pool(game: str, data: dict, Operator: Any):
limited = False limited = False
if key.find('') != -1: if key.find('') != -1:
limited = True limited = True
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited)) try:
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited))
except Exception as e:
logger.warning(f"公主连接导入角色 {key} 数据错误:{type(e)}{e}")
if game == 'azur': if game == 'azur':
for key in data.keys(): for key in data.keys():
if is_number(data[key]['星级']): if is_number(data[key]['星级']):
limited = False limited = False
if '可以建造' not in data[key]['获取途径']: if '可以建造' not in data[key]['获取途径']:
limited = True limited = True
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), try:
limited=limited, itype=data[key]['类型'])) tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']),
limited=limited, itype=data[key]['类型']))
except Exception as e:
logger.warning(f"碧蓝航线导入角色 {key} 数据错误:{type(e)}{e}")
if game in ['fgo', 'fgo_card']: if game in ['fgo', 'fgo_card']:
for key in data.keys(): for key in data.keys():
limited = False limited = False
@ -80,14 +102,20 @@ def init_game_pool(game: str, data: dict, Operator: Any):
limited = True limited = True
except KeyError: except KeyError:
pass pass
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited)) try:
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']), limited=limited))
except Exception as e:
logger.warning(f"FGO导入角色 {key} 数据错误:{type(e)}{e}")
if game == 'onmyoji': if game == 'onmyoji':
for key in data.keys(): for key in data.keys():
limited = False limited = False
if key in ['奴良陆生', '卖药郎', '鬼灯', '阿香', '蜜桃&芥子', '犬夜叉', '杀生丸', '桔梗', '朽木露琪亚', '黑崎一护', if key in ['奴良陆生', '卖药郎', '鬼灯', '阿香', '蜜桃&芥子', '犬夜叉', '杀生丸', '桔梗', '朽木露琪亚', '黑崎一护',
'灶门祢豆子', '灶门炭治郎']: '灶门祢豆子', '灶门炭治郎']:
limited = True limited = True
tmp_lst.append(Operator(name=data[key]['名称'], star=data[key]['星级'], limited=limited)) try:
tmp_lst.append(Operator(name=data[key]['名称'], star=data[key]['星级'], limited=limited))
except Exception as e:
logger.warning(f"阴阳师导入角色 {key} 数据错误:{type(e)}{e}")
# print(tmp_lst) # print(tmp_lst)
char_name_lst = [x.name for x in tmp_lst] char_name_lst = [x.name for x in tmp_lst]
up_char_file = Path(f'{DATA_PATH}/draw_card/draw_card_up/{game.split("_")[0]}_up_char.json') up_char_file = Path(f'{DATA_PATH}/draw_card/draw_card_up/{game.split("_")[0]}_up_char.json')

1
plugins/draw_card/onmyoji_handle.py Normal file → Executable file
View File

@ -6,7 +6,6 @@ from .util import generate_img, init_star_rst, BaseData, set_list, get_star, max
from .config import ONMYOJI_SR, ONMYOJI_SSR, ONMYOJI_SP, ONMYOJI_R, DRAW_PATH, ONMYOJI_FLAG from .config import ONMYOJI_SR, ONMYOJI_SSR, ONMYOJI_SP, ONMYOJI_R, DRAW_PATH, ONMYOJI_FLAG
from dataclasses import dataclass from dataclasses import dataclass
from .init_card_pool import init_game_pool from .init_card_pool import init_game_pool
import nonebot
try: try:
import ujson as json import ujson as json
except ModuleNotFoundError: except ModuleNotFoundError:

3
plugins/draw_card/pcr_handle.py Normal file → Executable file
View File

@ -1,7 +1,5 @@
import ujson as json import ujson as json
import os
from nonebot.adapters.cqhttp import MessageSegment from nonebot.adapters.cqhttp import MessageSegment
import nonebot
import random import random
from .update_game_info import update_info from .update_game_info import update_info
from .update_game_simple_info import update_simple_info from .update_game_simple_info import update_simple_info
@ -10,7 +8,6 @@ from .config import PCR_TWO_P, PCR_THREE_P, PCR_ONE_P, DRAW_PATH, PCR_FLAG, PCR_
from dataclasses import dataclass from dataclasses import dataclass
from .init_card_pool import init_game_pool from .init_card_pool import init_game_pool
driver: nonebot.Driver = nonebot.get_driver()
ALL_CHAR = [] ALL_CHAR = []

0
plugins/draw_card/pretty_handle.py Normal file → Executable file
View File

Some files were not shown because too many files have changed in this diff Show More