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
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
-2
View File
@@ -24,8 +24,6 @@ __plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["Ai", "ai", "AI", "aI"],
}
__plugin_configs__ = {
Regular → Executable
+65 -65
View File
@@ -1,13 +1,12 @@
import os
import random
import re
import aiohttp
from aiohttp.client import ClientSession
from utils.http_utils import AsyncHttpx
from configs.path_config import IMAGE_PATH, DATA_PATH
from services.log import logger
from utils.message_builder import image, face
from configs.config import Config, NICKNAME
from .utils import ai_message_manager
try:
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:
"""
获取 AI 返回值,顺序:图灵 -> 青云客
获取 AI 返回值,顺序: 特殊回复 -> 图灵 -> 青云客
:param text: 问题
:param img_url: 图片链接
:param user_id: 用户id
@@ -34,6 +33,11 @@ async def get_chat_result(text: str, img_url: str, user_id: int, nickname: str)
:return: 回答
"""
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:
index = 0
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:
if text.find(key) != -1:
return random.choice(anime_data[key]).replace("你", nickname)
async with aiohttp.ClientSession() as sess:
rst = await tu_ling(text, img_url, user_id, sess)
if not rst:
rst = await xie_ai(text, sess)
rst = await tu_ling(text, img_url, user_id)
if not rst:
rst = await xie_ai(text)
if not rst:
return no_result()
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:
nickname += "大~人~"
rst = rst.replace("小主人", nickname).replace("小朋友", nickname)
ai_message_manager.add_result(user_id, rst)
print(ai_message_manager)
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 img_url: 图片链接
:param user_id: 用户id
:param sess: AIOHTTP SESSION
:return: 图灵回复
"""
global index
TL_KEY = Config.get_config("ai", "TL_KEY")
req = None
if not TL_KEY:
return ''
return ""
try:
if text:
req = {
@@ -98,62 +103,59 @@ async def tu_ling(text: str, img_url: str, user_id: int, sess: ClientSession) ->
index = 0
return ""
text = ""
async with sess.post(url, json=req) as response:
if response.status != 200:
return no_result()
resp_payload = json.loads(await response.text())
if int(resp_payload["intent"]["code"]) in [4003]:
return ""
if resp_payload["results"]:
for result in resp_payload["results"]:
if result["resultType"] == "text":
text = result["values"]["text"]
if "请求次数超过" in text:
text = ""
response = await AsyncHttpx.post(url, json=req)
if response.status_code != 200:
return no_result()
resp_payload = json.loads(response.text)
if int(resp_payload["intent"]["code"]) in [4003]:
return ""
if resp_payload["results"]:
for result in resp_payload["results"]:
if result["resultType"] == "text":
text = result["values"]["text"]
if "请求次数超过" in text:
text = ""
return text
# 屑 AI
async def xie_ai(text: str, sess: ClientSession) -> str:
async def xie_ai(text: str) -> str:
"""
获取青云客回复
:param text: 问题
:param sess: AIOHTTP SESSION
:return: 青云可回复
"""
async with sess.get(
f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}"
) as res:
content = ""
data = json.loads(await res.text())
if data["result"] == 0:
content = data["content"]
if "菲菲" in content:
content = content.replace("菲菲", NICKNAME)
if "艳儿" in content:
content = content.replace("艳儿", NICKNAME)
if "公众号" in content:
content = ""
if "{br}" in content:
content = content.replace("{br}", "\n")
if "提示" in content:
content = content[: content.find("提示")]
if "淘宝" in content:
return ""
while True:
r = re.search("{face:(.*)}", content)
if r:
id_ = r.group(1)
content = content.replace(
"{" + f"face:{id_}" + "}", str(face(int(id_)))
)
else:
break
return (
content
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
else await check_text(content, sess)
)
res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}")
content = ""
data = json.loads(res.text)
if data["result"] == 0:
content = data["content"]
if "菲菲" in content:
content = content.replace("菲菲", NICKNAME)
if "艳儿" in content:
content = content.replace("艳儿", NICKNAME)
if "公众号" in content:
content = ""
if "{br}" in content:
content = content.replace("{br}", "\n")
if "提示" in content:
content = content[: content.find("提示")]
if "淘宝" in content:
return ""
while True:
r = re.search("{face:(.*)}", content)
if r:
id_ = r.group(1)
content = content.replace(
"{" + f"face:{id_}" + "}", str(face(int(id_)))
)
else:
break
return (
content
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
else await check_text(content)
)
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,检测为恶俗文本改为无回复的回答
:param text: 回复
:param sess: AIOHTTP SESSION
"""
if not Config.get_config("alapi", "ALAPI_TOKEN"):
return text
params = {"token": Config.get_config("alapi", "ALAPI_TOKEN"), "text": text}
try:
async with sess.get(check_url, timeout=2, params=params) as response:
data = await response.json()
if data["code"] == 200:
if data["data"]["conclusion_type"] == 2:
return ""
data = (await AsyncHttpx.get(check_url, timeout=2, params=params)).json()
if data["code"] == 200:
if data["data"]["conclusion_type"] == 2:
return ""
except Exception as e:
logger.error(f"检测违规文本错误...{type(e)}:{e}")
return text
+137
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()
Regular → Executable
View File
Regular → Executable
+46 -46
View File
@@ -1,46 +1,46 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from .data_source import get_data
from services.log import logger
__zx_plugin_name__ = "网易云热评"
__plugin_usage__ = """
usage:
到点了,还是防不了下塔
指令:
网易云热评/到点了/12点了
""".strip()
__plugin_des__ = "生了个人,我很抱歉"
__plugin_cmd__ = ["网易云热评", "到点了", "12点了"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["网易云热评", "网易云评论", "到点了", "12点了"],
}
comments_163 = on_command(
"网易云热评", aliases={"网易云评论", "到点了", "12点了"}, priority=5, block=True
)
comments_163_url = "https://v2.alapi.cn/api/comment"
@comments_163.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
data, code = await get_data(comments_163_url)
if code != 200:
await comments_163.finish(data, at_sender=True)
data = data["data"]
comment = data["comment_content"]
song_name = data["title"]
await comments_163.send(f"{comment}\n\t——《{song_name}》")
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送网易云热评: {comment} \n\t\t————{song_name}"
)
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from .data_source import get_data
from services.log import logger
__zx_plugin_name__ = "网易云热评"
__plugin_usage__ = """
usage:
到点了,还是防不了下塔
指令:
网易云热评/到点了/12点了
""".strip()
__plugin_des__ = "生了个人,我很抱歉"
__plugin_cmd__ = ["网易云热评", "到点了", "12点了"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["网易云热评", "网易云评论", "到点了", "12点了"],
}
comments_163 = on_command(
"网易云热评", aliases={"网易云评论", "到点了", "12点了"}, priority=5, block=True
)
comments_163_url = "https://v2.alapi.cn/api/comment"
@comments_163.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
data, code = await get_data(comments_163_url)
if code != 200:
await comments_163.finish(data, at_sender=True)
data = data["data"]
comment = data["comment_content"]
song_name = data["title"]
await comments_163.send(f"{comment}\n\t——《{song_name}》")
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送网易云热评: {comment} \n\t\t————{song_name}"
)
Regular → Executable
+48 -48
View File
@@ -1,48 +1,48 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.typing import T_State
from utils.message_builder import image
from utils.utils import get_message_text
from .data_source import get_data
from services.log import logger
__zx_plugin_name__ = "b封面"
__plugin_usage__ = """
usage:
b封面 [链接/av/bv/cv/直播id]
示例:b封面 av86863038
""".strip()
__plugin_des__ = "快捷的b站视频封面获取方式"
__plugin_cmd__ = ["b封面/B封面"]
__plugin_type__ = ("一些工具",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["b封面", "B封面"],
}
cover = on_command("b封面", aliases={"B封面"}, priority=5, block=True)
cover_url = "https://v2.alapi.cn/api/bilibili/cover"
@cover.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
params = {"c": msg}
data, code = await get_data(cover_url, params)
if code != 200:
await cover.finish(data, at_sender=True)
data = data["data"]
title = data["title"]
img = data["cover"]
await cover.send(Message(f"title:{title}\n{image(img)}"))
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 获取b站封面: {title} url:{img}"
)
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.typing import T_State
from utils.message_builder import image
from utils.utils import get_message_text
from .data_source import get_data
from services.log import logger
__zx_plugin_name__ = "b封面"
__plugin_usage__ = """
usage:
b封面 [链接/av/bv/cv/直播id]
示例:b封面 av86863038
""".strip()
__plugin_des__ = "快捷的b站视频封面获取方式"
__plugin_cmd__ = ["b封面/B封面"]
__plugin_type__ = ("一些工具",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["b封面", "B封面"],
}
cover = on_command("b封面", aliases={"B封面"}, priority=5, block=True)
cover_url = "https://v2.alapi.cn/api/bilibili/cover"
@cover.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
params = {"c": msg}
data, code = await get_data(cover_url, params)
if code != 200:
await cover.finish(data, at_sender=True)
data = data["data"]
title = data["title"]
img = data["cover"]
await cover.send(Message(f"title:{title}\n{image(img)}"))
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 获取b站封面: {title} url:{img}"
)
Regular → Executable
+49 -51
View File
@@ -1,51 +1,49 @@
from nonebot.adapters.cqhttp import MessageSegment
from utils.image_utils import CreateImg
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from typing import Optional
from configs.config import Config
import aiohttp
async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str], int":
"""
获取ALAPI数据
:param url: 请求链接
:param params: 参数
"""
if not params:
params = {}
params["token"] = Config.get_config("alapi", "ALAPI_TOKEN")
async with aiohttp.ClientSession() as session:
try:
async with session.get(url, timeout=2, params=params) as response:
data = await response.json()
if data["code"] == 200:
if not data["data"]:
return "没有搜索到...", 997
return data, 200
else:
return f'发生了错误...code:{data["code"]}', 999
except TimeoutError:
return "超时了....", 998
def gen_wbtop_pic(data: dict) -> MessageSegment:
"""
生成微博热搜图片
:param data: 微博热搜数据
"""
bk = CreateImg(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = CreateImg(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk.paste(wbtop_bk)
text_bk = CreateImg(700, 32 * 50, 700, 32, color="#797979")
for i, data in enumerate(data):
title = f"{i+1}. {data['hot_word']}"
hot = data["hot_word_num"]
img = CreateImg(700, 30, font_size=20)
w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot)
text_bk.paste(img)
bk.paste(text_bk, (0, 280))
return image(b64=bk.pic2bs4())
from nonebot.adapters.cqhttp import MessageSegment
from utils.image_utils import CreateImg
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from typing import Optional
from configs.config import Config
from utils.http_utils import AsyncHttpx
async def get_data(url: str, params: Optional[dict] = None) -> "Union[dict, str], int":
"""
获取ALAPI数据
:param url: 请求链接
:param params: 参数
"""
if not params:
params = {}
params["token"] = Config.get_config("alapi", "ALAPI_TOKEN")
try:
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
if data["code"] == 200:
if not data["data"]:
return "没有搜索到...", 997
return data, 200
else:
return f'发生了错误...code:{data["code"]}', 999
except TimeoutError:
return "超时了....", 998
def gen_wbtop_pic(data: dict) -> MessageSegment:
"""
生成微博热搜图片
:param data: 微博热搜数据
"""
bk = CreateImg(700, 32 * 50 + 280, 700, 32, color="#797979")
wbtop_bk = CreateImg(700, 280, background=f"{IMAGE_PATH}/other/webtop.png")
bk.paste(wbtop_bk)
text_bk = CreateImg(700, 32 * 50, 700, 32, color="#797979")
for i, data in enumerate(data):
title = f"{i+1}. {data['hot_word']}"
hot = data["hot_word_num"]
img = CreateImg(700, 30, font_size=20)
w, h = img.getsize(title)
img.text((10, int((30 - h) / 2)), title)
img.text((580, int((30 - h) / 2)), hot)
text_bk.paste(img)
bk.paste(text_bk, (0, 280))
return image(b64=bk.pic2bs4())
+48
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}")
Regular → Executable
+42 -42
View File
@@ -1,42 +1,42 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger
from .data_source import get_data
__zx_plugin_name__ = "古诗"
__plugin_usage__ = """usage:
平白无故念首诗
示例:念诗/来首诗/念首诗
"""
__plugin_des__ = "为什么突然文艺起来了!"
__plugin_cmd__ = ["念诗/来首诗/念首诗"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["念诗", "来首诗", "念首诗"],
}
poetry = on_command("念诗", aliases={"来首诗", "念首诗"}, priority=5, block=True)
poetry_url = "https://v2.alapi.cn/api/shici"
@poetry.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
data, code = await get_data(poetry_url)
if code != 200:
await poetry.finish(data, at_sender=True)
data = data["data"]
content = data["content"]
title = data["origin"]
author = data["author"]
await poetry.send(f"{content}\n\t——{author}《{title}》")
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送古诗: f'{content}\n\t--{author}《{title}》'"
)
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger
from .data_source import get_data
__zx_plugin_name__ = "古诗"
__plugin_usage__ = """usage:
平白无故念首诗
示例:念诗/来首诗/念首诗
"""
__plugin_des__ = "为什么突然文艺起来了!"
__plugin_cmd__ = ["念诗/来首诗/念首诗"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["念诗", "来首诗", "念首诗"],
}
poetry = on_command("念诗", aliases={"来首诗", "念首诗"}, priority=5, block=True)
poetry_url = "https://v2.alapi.cn/api/shici"
@poetry.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
data, code = await get_data(poetry_url)
if code != 200:
await poetry.finish(data, at_sender=True)
data = data["data"]
content = data["content"]
title = data["origin"]
author = data["author"]
await poetry.send(f"{content}\n\t——{author}《{title}》")
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送古诗: f'{content}\n\t--{author}《{title}》'"
)
Regular → Executable
+71 -76
View File
@@ -1,76 +1,71 @@
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger
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 configs.path_config import IMAGE_PATH
from utils.message_builder import image
import asyncio
__zx_plugin_name__ = '微博热搜'
__plugin_usage__ = """
usage:
在QQ上吃个瓜
指令:
微博热搜:发送实时热搜
微博热搜 [id]:截图该热搜页面
示例:微博热搜 5
""".strip()
__plugin_des__ = '刚买完瓜,在吃瓜现场'
__plugin_cmd__ = ['微博热搜', '微博热搜 [id]']
__plugin_version__ = 0.1
__plugin_author__ = 'HibiKier'
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ['微博热搜'],
}
wbtop = on_command("wbtop", aliases={'微博热搜'}, priority=5, block=True)
wbtop_url = 'https://v2.alapi.cn/api/new/wbtop'
wbtop_data = []
@wbtop.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
global wbtop_data
msg = get_message_text(event.json())
if not wbtop_data or not msg:
data, code = await get_data(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
wbtop_data = data['data']
if not msg:
img = await asyncio.get_event_loop().run_in_executor(None, gen_wbtop_pic, wbtop_data)
await wbtop.send(img)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查询微博热搜")
if is_number(msg) and 0 < int(msg) <= 50:
url = wbtop_data[int(msg) - 1]['url']
browser = await get_browser()
page = None
try:
if not browser:
logger.warning('获取 browser 失败,请部署至 linux 环境....')
await wbtop.finish('获取 browser 对象失败...')
page = await browser.new_page()
await page.goto(url, wait_until='networkidle', timeout=10000)
await page.set_viewport_size({"width": 2560, "height": 1080})
await asyncio.sleep(5)
div = await page.query_selector("#pl_feedlist_index")
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:
logger.error(f'微博热搜截图出错... {type(e)}: {e}')
if page:
await page.close()
await wbtop.send('发生了一些错误.....')
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger
from .data_source import get_data, gen_wbtop_pic
from utils.utils import get_message_text, is_number
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.http_utils import AsyncPlaywright
import asyncio
__zx_plugin_name__ = "微博热搜"
__plugin_usage__ = """
usage:
在QQ上吃个瓜
指令:
微博热搜:发送实时热搜
微博热搜 [id]:截图该热搜页面
示例:微博热搜 5
""".strip()
__plugin_des__ = "刚买完瓜,在吃瓜现场"
__plugin_cmd__ = ["微博热搜", "微博热搜 [id]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["微博热搜"],
}
wbtop = on_command("wbtop", aliases={"微博热搜"}, priority=5, block=True)
wbtop_url = "https://v2.alapi.cn/api/new/wbtop"
wbtop_data = []
@wbtop.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
global wbtop_data
msg = get_message_text(event.json())
if not wbtop_data or not msg:
data, code = await get_data(wbtop_url)
if code != 200:
await wbtop.finish(data, at_sender=True)
wbtop_data = data["data"]
if not msg:
img = await asyncio.get_event_loop().run_in_executor(
None, gen_wbtop_pic, wbtop_data
)
await wbtop.send(img)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 查询微博热搜"
)
if is_number(msg) and 0 < int(msg) <= 50:
url = wbtop_data[int(msg) - 1]["url"]
try:
await wbtop.send("开始截取数据...")
img = await AsyncPlaywright.screenshot(
url,
f"{IMAGE_PATH}/temp/wbtop_{event.user_id}.png",
"#pl_feedlist_index",
sleep=5
)
await wbtop.send(img)
except Exception as e:
logger.error(f"微博热搜截图出错... {type(e)}: {e}")
await wbtop.send("发生了一些错误.....")
Regular → Executable
+236 -235
View File
@@ -1,235 +1,236 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message
from .data_source import (
add_live_sub,
delete_sub,
add_up_sub,
add_season_sub,
get_media_id,
get_sub_status,
SubManager,
BilibiliSub
)
from models.level_user import LevelUser
from configs.config import Config
from utils.utils import get_message_text, is_number, scheduler, get_bot
from typing import Optional
from services.log import logger
from nonebot import Driver
import nonebot
__zx_plugin_name__ = "B站订阅"
__plugin_usage__ = """
usage:
B站直播,番剧,UP动态开播等提醒
主播订阅相当于 直播间订阅 + UP订阅
指令:[示例Id乱打的,仅做示例]
添加订阅 ['主播'/'UP'/'番剧'] [id/链接/番名]
删除订阅 [id]
查看订阅
示例:添加订阅主播 2345344 <-(直播房间id)
示例:添加订阅UP 2355543 <-(个人主页id)
示例:添加订阅番剧 史莱姆 <-(支持模糊搜索)
示例:添加订阅番剧 125344 <-(番剧id)
示例:删除订阅 2324344 <-(任意id,通过查看订阅获取)
""".strip()
__plugin_des__ = "非常便利的B站订阅通知"
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["B站订阅", "b站订阅", "添加订阅", "删除订阅", "查看订阅"],
}
__plugin_configs__ = {
"GROUP_BILIBILI_SUB_LEVEL": {
"value": 5,
"help": "群内bilibili订阅需要管理的权限",
"default_value": 5,
}
}
add_sub = on_command("添加订阅", priority=5, block=True)
del_sub = on_command("删除订阅", priority=5, block=True)
show_sub_info = on_command("查看订阅", priority=5, block=True)
driver: Driver = nonebot.get_driver()
sub_manager: Optional[SubManager] = None
@driver.on_startup
async def _():
global sub_manager
sub_manager = SubManager()
@add_sub.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
season_data = state["season_data"]
msg = get_message_text(event.json())
if not is_number(msg) or int(msg) < 1 or int(msg) > len(season_data):
await add_sub.reject("Id必须为数字且在范围内!请重新输入...")
state["id"] = season_data[int(msg) - 1]["media_id"]
@add_sub.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()).split()
if len(msg) < 2:
await add_sub.finish("参数不完全,请查看订阅帮助...")
sub_type = msg[0]
id_ = ""
if isinstance(event, GroupMessageEvent):
if not await LevelUser.check_level(
event.user_id,
event.group_id,
Config.get_config("bilibili_sub", "GROUP_BILIBILI_SUB_LEVEL"),
):
await add_sub.finish(
f"您的权限不足,群内订阅的需要 {Config.get_config('bilibili_sub', 'GROUP_BILIBILI_SUB_LEVEL')} 级权限..",
at_sender=True,
)
sub_user = f"{event.user_id}:{event.group_id}"
else:
sub_user = f"{event.user_id}"
state["sub_type"] = sub_type
state["sub_user"] = sub_user
if len(msg) > 1:
if "http" in msg[1]:
msg[1] = msg[1].split("?")[0]
msg[1] = msg[1][:-1] if msg[1][-1] == "/" else msg[1]
msg[1] = msg[1].split("/")[-1]
id_ = msg[1][2:] if msg[1].startswith("md") else msg[1]
if not is_number(id_):
if sub_type in ["season", "动漫", "番剧"]:
rst = "*以为您找到以下番剧,请输入Id选择:*\n"
state["season_data"] = await get_media_id(id_)
if len(state["season_data"]) == 0:
await add_sub.finish(f"未找到番剧:{msg}")
for i, x in enumerate(state["season_data"]):
rst += f'{i + 1}.{state["season_data"][x]["title"]}\n----------\n'
await add_sub.send("\n".join(rst.split("\n")[:-1]))
else:
await add_sub.finish("Id 必须为全数字!")
else:
state["id"] = int(id_)
@add_sub.got("id")
async def _(bot: Bot, event: MessageEvent, state: T_State):
sub_type = state["sub_type"]
sub_user = state["sub_user"]
id_ = state["id"]
if sub_type in ["主播", "直播"]:
await add_sub.send(await add_live_sub(id_, sub_user))
elif sub_type.lower() in ["up", "用户"]:
await add_sub.send(await add_up_sub(id_, sub_user))
elif sub_type in ["season", "动漫", "番剧"]:
await add_sub.send(await add_season_sub(id_, sub_user))
else:
await add_sub.finish("参数错误,第一参数必须为:主播/up/番剧!")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 添加订阅:{sub_type} -> {sub_user} -> {id_}"
)
@del_sub.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if not is_number(msg):
await del_sub.finish("Id必须为数字!", at_sender=True)
id_ = (
f"{event.user_id}:{event.group_id}"
if isinstance(event, GroupMessageEvent)
else f"{event.user_id}"
)
if await BilibiliSub.delete_bilibili_sub(int(msg), id_):
await del_sub.send(f"删除订阅id:{msg} 成功...")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除订阅 {id_}"
)
else:
await del_sub.send(f"删除订阅id:{msg} 失败...")
@show_sub_info.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
id_ = (
f"{event.user_id}:{event.group_id}"
if isinstance(event, GroupMessageEvent)
else f"{event.user_id}"
)
data = await BilibiliSub.get_sub_data(id_)
live_rst = ""
up_rst = ""
season_rst = ""
for x in data:
if x.sub_type == "live":
live_rst += (
f"\t直播间id:{x.sub_id}\n" f"\t名称:{x.uname}\n" f"------------------\n"
)
if x.sub_type == "up":
up_rst += f"\tUP:{x.uname}\n" f"\tuid:{x.uid}\n" f"------------------\n"
if x.sub_type == "season":
season_rst += (
f"\t番名:{x.season_name}\n"
f"\t当前集数:{x.season_current_episode}\n"
f"------------------\n"
)
live_rst = "当前订阅的直播:\n" + live_rst if live_rst else live_rst
up_rst = "当前订阅的UP:\n" + up_rst if up_rst else up_rst
season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst
if not live_rst and not up_rst and not season_rst:
live_rst = "您目前没有任何订阅..."
await show_sub_info.send(live_rst + up_rst + season_rst)
# 推送
@scheduler.scheduled_job(
"interval",
seconds=30,
)
async def _():
bot = get_bot()
sub = None
if bot:
try:
await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data()
if sub:
rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live":
rst = await get_sub_status(sub.sub_id, "up")
await send_sub_msg(rst, sub, bot)
except Exception as e:
logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}")
async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
"""
推送信息
:param rst: 回复
:param sub: BilibiliSub
:param bot: Bot
"""
if rst:
for x in sub.sub_users.split(",")[:-1]:
try:
if ":" in x:
await bot.send_group_msg(
group_id=int(x.split(":")[1]), message=Message(rst)
)
else:
await bot.send_private_msg(user_id=int(x), message=Message(rst))
except Exception as e:
logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id} {type(e)}:{e}")
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, Message
from .data_source import (
add_live_sub,
delete_sub,
add_up_sub,
add_season_sub,
get_media_id,
get_sub_status,
SubManager,
BilibiliSub
)
from models.level_user import LevelUser
from configs.config import Config
from utils.utils import get_message_text, is_number, scheduler, get_bot
from typing import Optional
from services.log import logger
from nonebot import Driver
import nonebot
__zx_plugin_name__ = "B站订阅"
__plugin_usage__ = """
usage:
B站直播,番剧,UP动态开播等提醒
主播订阅相当于 直播间订阅 + UP订阅
指令:[示例Id乱打的,仅做示例]
添加订阅 ['主播'/'UP'/'番剧'] [id/链接/番名]
删除订阅 [id]
查看订阅
示例:添加订阅主播 2345344 <-(直播房间id)
示例:添加订阅UP 2355543 <-(个人主页id)
示例:添加订阅番剧 史莱姆 <-(支持模糊搜索)
示例:添加订阅番剧 125344 <-(番剧id)
示例:删除订阅 2324344 <-(任意id,通过查看订阅获取)
""".strip()
__plugin_des__ = "非常便利的B站订阅通知"
__plugin_cmd__ = ["添加订阅 [主播/UP/番剧] [id/链接/番名]", "删除订阅 [id]", "查看订阅"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["B站订阅", "b站订阅", "添加订阅", "删除订阅", "查看订阅"],
}
__plugin_configs__ = {
"GROUP_BILIBILI_SUB_LEVEL": {
"value": 5,
"help": "群内bilibili订阅需要管理的权限",
"default_value": 5,
}
}
add_sub = on_command("添加订阅", priority=5, block=True)
del_sub = on_command("删除订阅", priority=5, block=True)
show_sub_info = on_command("查看订阅", priority=5, block=True)
driver: Driver = nonebot.get_driver()
sub_manager: Optional[SubManager] = None
@driver.on_startup
async def _():
global sub_manager
sub_manager = SubManager()
@add_sub.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
season_data = state["season_data"]
msg = get_message_text(event.json())
if not is_number(msg) or int(msg) < 1 or int(msg) > len(season_data):
await add_sub.reject("Id必须为数字且在范围内!请重新输入...")
state["id"] = season_data[int(msg) - 1]["media_id"]
@add_sub.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json()).split()
if len(msg) < 2:
await add_sub.finish("参数不完全,请查看订阅帮助...")
sub_type = msg[0]
id_ = ""
if isinstance(event, GroupMessageEvent):
if not await LevelUser.check_level(
event.user_id,
event.group_id,
Config.get_config("bilibili_sub", "GROUP_BILIBILI_SUB_LEVEL"),
):
await add_sub.finish(
f"您的权限不足,群内订阅的需要 {Config.get_config('bilibili_sub', 'GROUP_BILIBILI_SUB_LEVEL')} 级权限..",
at_sender=True,
)
sub_user = f"{event.user_id}:{event.group_id}"
else:
sub_user = f"{event.user_id}"
state["sub_type"] = sub_type
state["sub_user"] = sub_user
if len(msg) > 1:
if "http" in msg[1]:
msg[1] = msg[1].split("?")[0]
msg[1] = msg[1][:-1] if msg[1][-1] == "/" else msg[1]
msg[1] = msg[1].split("/")[-1]
id_ = msg[1][2:] if msg[1].startswith("md") else msg[1]
if not is_number(id_):
if sub_type in ["season", "动漫", "番剧"]:
rst = "*以为您找到以下番剧,请输入Id选择:*\n"
state["season_data"] = await get_media_id(id_)
if len(state["season_data"]) == 0:
await add_sub.finish(f"未找到番剧:{msg}")
for i, x in enumerate(state["season_data"]):
rst += f'{i + 1}.{state["season_data"][x]["title"]}\n----------\n'
await add_sub.send("\n".join(rst.split("\n")[:-1]))
else:
await add_sub.finish("Id 必须为全数字!")
else:
state["id"] = int(id_)
@add_sub.got("id")
async def _(bot: Bot, event: MessageEvent, state: T_State):
sub_type = state["sub_type"]
sub_user = state["sub_user"]
id_ = state["id"]
if sub_type in ["主播", "直播"]:
await add_sub.send(await add_live_sub(id_, sub_user))
elif sub_type.lower() in ["up", "用户"]:
await add_sub.send(await add_up_sub(id_, sub_user))
elif sub_type in ["season", "动漫", "番剧"]:
await add_sub.send(await add_season_sub(id_, sub_user))
else:
await add_sub.finish("参数错误,第一参数必须为:主播/up/番剧!")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 添加订阅:{sub_type} -> {sub_user} -> {id_}"
)
@del_sub.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if not is_number(msg):
await del_sub.finish("Id必须为数字!", at_sender=True)
id_ = (
f"{event.user_id}:{event.group_id}"
if isinstance(event, GroupMessageEvent)
else f"{event.user_id}"
)
if await BilibiliSub.delete_bilibili_sub(int(msg), id_):
await del_sub.send(f"删除订阅id:{msg} 成功...")
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 删除订阅 {id_}"
)
else:
await del_sub.send(f"删除订阅id:{msg} 失败...")
@show_sub_info.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
id_ = (
f"{event.user_id}:{event.group_id}"
if isinstance(event, GroupMessageEvent)
else f"{event.user_id}"
)
data = await BilibiliSub.get_sub_data(id_)
live_rst = ""
up_rst = ""
season_rst = ""
for x in data:
if x.sub_type == "live":
live_rst += (
f"\t直播间id:{x.sub_id}\n" f"\t名称:{x.uname}\n" f"------------------\n"
)
if x.sub_type == "up":
up_rst += f"\tUP:{x.uname}\n" f"\tuid:{x.uid}\n" f"------------------\n"
if x.sub_type == "season":
season_rst += (
f"\t番名:{x.season_name}\n"
f"\t当前集数:{x.season_current_episode}\n"
f"------------------\n"
)
live_rst = "当前订阅的直播:\n" + live_rst if live_rst else live_rst
up_rst = "当前订阅的UP:\n" + up_rst if up_rst else up_rst
season_rst = "当前订阅的番剧:\n" + season_rst if season_rst else season_rst
if not live_rst and not up_rst and not season_rst:
live_rst = "您目前没有任何订阅..."
await show_sub_info.send(live_rst + up_rst + season_rst)
# 推送
@scheduler.scheduled_job(
"interval",
seconds=30,
)
async def _():
bot = get_bot()
sub = None
if bot:
try:
await sub_manager.reload_sub_data()
sub = await sub_manager.random_sub_data()
if sub:
logger.info(f"Bilibili订阅开始检测:{sub.sub_id}")
rst = await get_sub_status(sub.sub_id, sub.sub_type)
await send_sub_msg(rst, sub, bot)
if sub.sub_type == "live":
rst = await get_sub_status(sub.sub_id, "up")
await send_sub_msg(rst, sub, bot)
except Exception as e:
logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id if sub else 0} {type(e)}:{e}")
async def send_sub_msg(rst: str, sub: BilibiliSub, bot: Bot):
"""
推送信息
:param rst: 回复
:param sub: BilibiliSub
:param bot: Bot
"""
if rst:
for x in sub.sub_users.split(",")[:-1]:
try:
if ":" in x:
await bot.send_group_msg(
group_id=int(x.split(":")[1]), message=Message(rst)
)
else:
await bot.send_private_msg(user_id=int(x), message=Message(rst))
except Exception as e:
logger.error(f"B站订阅推送发生错误 sub_id:{sub.sub_id} {type(e)}:{e}")
+391 -394
View File
@@ -1,394 +1,391 @@
from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException
from utils.manager import resources_manager
from asyncio.exceptions import TimeoutError
from .model import BilibiliSub
from bilibili_api.live import LiveRoom
from bilibili_api import bangumi
from utils.message_builder import image
from bilibili_api.user import User
from bilibili_api import user
from typing import Optional
from pathlib import Path
from configs.path_config import IMAGE_PATH
from datetime import datetime
from utils.browser import get_browser
from services.db_context import db
from services.log import logger
import aiohttp
import random
bilibili_search_url = "https://api.bilibili.com/x/web-interface/search/all/v2"
dynamic_path = Path(IMAGE_PATH) / "bilibili_sub" / "dynamic"
dynamic_path.mkdir(exist_ok=True, parents=True)
resources_manager.add_temp_dir(dynamic_path)
async def add_live_sub(live_id: int, sub_user: str) -> str:
"""
添加直播订阅
:param live_id: 直播房间号
:param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group)
:return:
"""
try:
async with db.transaction():
try:
live = LiveRoom(live_id)
live_info = (await live.get_room_info())["room_info"]
except ResponseCodeException:
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
uid = live_info["uid"]
room_id = live_info["room_id"]
short_id = live_info["short_id"]
title = live_info["title"]
live_status = live_info["live_status"]
if await BilibiliSub.add_bilibili_sub(
room_id,
"live",
sub_user,
uid=uid,
live_short_id=short_id,
live_status=live_status,
):
await _get_up_status(live_id)
uname = (await BilibiliSub.get_sub(live_id)).uname
return (
"已成功订阅主播:\n"
f"\ttitle:{title}\n"
f"\tname: {uname}\n"
f"\tlive_id:{live_id}\n"
f"\tuid:{uid}"
)
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅主播live_id:{live_id} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
async def add_up_sub(uid: int, sub_user: str) -> str:
"""
添加订阅 UP
:param uid: UP uid
:param sub_user: 订阅用户
"""
try:
async with db.transaction():
try:
u = user.User(uid)
user_info = await u.get_user_info()
except ResponseCodeException:
return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
uname = user_info["name"]
dynamic_info = await u.get_dynamics(0)
dynamic_upload_time = 0
if dynamic_info.get("cards"):
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
video_info = await u.get_videos()
latest_video_created = 0
if video_info["list"].get("vlist"):
latest_video_created = video_info["list"]["vlist"][0]["created"]
if await BilibiliSub.add_bilibili_sub(
uid,
"up",
sub_user,
uid=uid,
uname=uname,
dynamic_upload_time=dynamic_upload_time,
latest_video_created=latest_video_created,
):
return "已成功订阅UP:\n" f"\tname: {uname}\n" f"\tuid:{uid}"
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅Up uid:{uid} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
async def add_season_sub(media_id: int, sub_user: str) -> str:
"""
添加订阅 UP
:param media_id: 番剧 media_id
:param sub_user: 订阅用户
"""
try:
async with db.transaction():
try:
season_info = await bangumi.get_meta(media_id)
except ResponseCodeException:
return f"未找到media_id:{media_id} 的信息,请检查Id是否正确"
season_id = season_info["media"]["season_id"]
season_current_episode = season_info["media"]["new_ep"]["index"]
season_name = season_info["media"]["title"]
if await BilibiliSub.add_bilibili_sub(
media_id,
"season",
sub_user,
season_name=season_name,
season_id=season_id,
season_current_episode=season_current_episode,
):
return (
"已成功订阅番剧:\n"
f"\ttitle: {season_name}\n"
f"\tcurrent_episode: {season_current_episode}"
)
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅番剧 media_id:{media_id} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
async def delete_sub(sub_id: str, sub_user: str) -> str:
"""
删除订阅
:param sub_id: 订阅 id
:param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group)
"""
if await BilibiliSub.delete_bilibili_sub(sub_id, sub_user):
return f"已成功取消订阅:{sub_id}"
else:
return f"取消订阅:{sub_id} 失败,请检查是否订阅过该Id...."
async def get_media_id(keyword: str) -> dict:
"""
获取番剧的 media_id
:param keyword: 番剧名称
"""
params = {"keyword": keyword}
async with aiohttp.ClientSession() as session:
for _ in range(3):
try:
_season_data = {}
async with session.get(
bilibili_search_url, timeout=5, params=params
) as response:
if response.status == 200:
data = await response.json()
if data.get("data"):
for item in data["data"]["result"]:
if item["result_type"] == "media_bangumi":
idx = 0
for x in item["data"]:
_season_data[idx] = {
"media_id": x["media_id"],
"title": x["title"]
.replace('<em class="keyword">', "")
.replace("</em>", ""),
}
idx += 1
return _season_data
except TimeoutError:
pass
return {}
async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
"""
获取订阅状态
:param id_: 订阅 id
:param sub_type: 订阅类型
:return:
"""
try:
if sub_type == "live":
return await _get_live_status(id_)
elif sub_type == "up":
return await _get_up_status(id_)
elif sub_type == "season":
return await _get_season_status(id_)
except ResponseCodeException:
return "获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
async def _get_live_status(id_: int) -> Optional[str]:
"""
获取直播订阅状态
:param id_: 直播间 id
"""
live = LiveRoom(id_)
live_info = (await live.get_room_info())["room_info"]
title = live_info["title"]
room_id = live_info["room_id"]
live_status = live_info["live_status"]
cover = live_info["cover"]
sub = await BilibiliSub.get_sub(id_)
if sub.live_status != live_status:
await BilibiliSub.update_sub_info(id_, live_status=live_status)
if sub.live_status == 0 and live_status == 1:
return (
f"{image(cover)}\n"
f"{sub.uname} 开播啦!\n"
f"标题:{title}\n"
f"直链:https://live.bilibili.com/{room_id}"
)
return None
async def _get_up_status(id_: int) -> Optional[str]:
"""
获取用户投稿状态
:param id_: 用户 id
:return:
"""
_user = await BilibiliSub.get_sub(id_)
u = user.User(_user.uid)
user_info = await u.get_user_info()
uname = user_info["name"]
video_info = await u.get_videos()
latest_video_created = 0
video = None
if _user.uname != uname:
await BilibiliSub.update_sub_info(id_, uname=uname)
dynamic_img, dynamic_upload_time = await get_user_dynamic(u, _user)
if video_info["list"].get("vlist"):
video = video_info["list"]["vlist"][0]
latest_video_created = video["created"]
rst = ""
if dynamic_img:
await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time)
rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n"
if _user.latest_video_created != latest_video_created and video:
rst = rst + "-------------\n" if rst else rst
await BilibiliSub.update_sub_info(
id_, latest_video_created=latest_video_created
)
rst += (
f'{image(video["pic"])}\n'
f"{uname} 投稿了新视频啦\n"
f'标题:{video["title"]}\n'
f'Bvid:{video["bvid"]}\n'
f'直链:https://www.bilibili.com/video/{video["bvid"]}'
)
rst = None if rst == "-------------\n" else rst
return rst
async def _get_season_status(id_) -> Optional[str]:
"""
获取 番剧 更新状态
:param id_: 番剧 id
"""
season_info = await bangumi.get_meta(id_)
title = season_info["media"]["title"]
_idx = (await BilibiliSub.get_sub(id_)).season_current_episode
new_ep = season_info["media"]["new_ep"]["index"]
if new_ep != _idx:
await BilibiliSub.update_sub_info(
id_, season_current_episode=new_ep, season_update_time=datetime.now()
)
return (
f'{image(season_info["media"]["cover"])}\n'
f"[{title}]更新啦\n"
f"最新集数:{new_ep}"
)
return None
async def get_user_dynamic(
u: User, local_user: BilibiliSub
) -> "Optional[MessageSegment], int":
"""
获取用户动态
:param u: 用户类
:param local_user: 数据库存储的用户数据
:return: 最新动态截图与时间
"""
dynamic_info = await u.get_dynamics(0)
browser = await get_browser()
if dynamic_info.get("cards") and browser:
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
if local_user.dynamic_upload_time != dynamic_upload_time:
page = await browser.new_page()
await page.goto(
f"https://space.bilibili.com/{local_user.uid}/dynamic",
wait_until="networkidle",
timeout=10000,
)
await page.set_viewport_size({"width": 2560, "height": 1080})
# 删除置顶
await page.evaluate(
"""
xs = document.getElementsByClassName('first-card-with-title');
for (x of xs) {
x.remove();
}
"""
)
card = await page.query_selector(".card")
# 截图并保存
await card.screenshot(
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
timeout=100000,
)
await page.close()
return (
image(
f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
"bilibili_sub/dynamic",
),
dynamic_upload_time,
)
return None, None
class SubManager:
def __init__(self):
self.live_data = []
self.up_data = []
self.season_data = []
self.current_index = -1
async def reload_sub_data(self):
"""
重载数据
"""
if not self.live_data or not self.up_data or not self.season_data:
(
_live_data,
_up_data,
_season_data,
) = await BilibiliSub.get_all_sub_data()
if not self.live_data:
self.live_data = _live_data
if not self.up_data:
self.up_data = _up_data
if not self.season_data:
self.season_data = _season_data
async def random_sub_data(self) -> Optional[BilibiliSub]:
"""
随机获取一条数据
:return:
"""
sub = None
if not self.live_data and not self.up_data and not self.season_data:
return sub
self.current_index += 1
if self.current_index == 0:
if self.live_data:
sub = random.choice(self.live_data)
self.live_data.remove(sub)
elif self.current_index == 1:
if self.up_data:
sub = random.choice(self.up_data)
self.up_data.remove(sub)
elif self.current_index == 2:
if self.season_data:
sub = random.choice(self.season_data)
self.season_data.remove(sub)
else:
self.current_index = -1
if sub:
return sub
await self.reload_sub_data()
return await self.random_sub_data()
from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException
from utils.manager import resources_manager
from asyncio.exceptions import TimeoutError
from .model import BilibiliSub
from bilibili_api.live import LiveRoom
from bilibili_api import bangumi
from utils.message_builder import image
from bilibili_api.user import User
from bilibili_api import user
from typing import Optional
from pathlib import Path
from configs.path_config import IMAGE_PATH
from datetime import datetime
from utils.browser import get_browser
from services.db_context import db
from services.log import logger
from utils.http_utils import AsyncHttpx
import random
bilibili_search_url = "https://api.bilibili.com/x/web-interface/search/all/v2"
dynamic_path = Path(IMAGE_PATH) / "bilibili_sub" / "dynamic"
dynamic_path.mkdir(exist_ok=True, parents=True)
resources_manager.add_temp_dir(dynamic_path)
async def add_live_sub(live_id: int, sub_user: str) -> str:
"""
添加直播订阅
:param live_id: 直播房间号
:param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group)
:return:
"""
try:
async with db.transaction():
try:
live = LiveRoom(live_id)
live_info = (await live.get_room_info())["room_info"]
except ResponseCodeException:
return f"未找到房间号Id:{live_id} 的信息,请检查Id是否正确"
uid = live_info["uid"]
room_id = live_info["room_id"]
short_id = live_info["short_id"]
title = live_info["title"]
live_status = live_info["live_status"]
if await BilibiliSub.add_bilibili_sub(
room_id,
"live",
sub_user,
uid=uid,
live_short_id=short_id,
live_status=live_status,
):
await _get_up_status(live_id)
uname = (await BilibiliSub.get_sub(live_id)).uname
return (
"已成功订阅主播:\n"
f"\ttitle:{title}\n"
f"\tname: {uname}\n"
f"\tlive_id:{live_id}\n"
f"\tuid:{uid}"
)
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅主播live_id:{live_id} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
async def add_up_sub(uid: int, sub_user: str) -> str:
"""
添加订阅 UP
:param uid: UP uid
:param sub_user: 订阅用户
"""
try:
async with db.transaction():
try:
u = user.User(uid)
user_info = await u.get_user_info()
except ResponseCodeException:
return f"未找到UpId:{uid} 的信息,请检查Id是否正确"
uname = user_info["name"]
dynamic_info = await u.get_dynamics(0)
dynamic_upload_time = 0
if dynamic_info.get("cards"):
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
video_info = await u.get_videos()
latest_video_created = 0
if video_info["list"].get("vlist"):
latest_video_created = video_info["list"]["vlist"][0]["created"]
if await BilibiliSub.add_bilibili_sub(
uid,
"up",
sub_user,
uid=uid,
uname=uname,
dynamic_upload_time=dynamic_upload_time,
latest_video_created=latest_video_created,
):
return "已成功订阅UP:\n" f"\tname: {uname}\n" f"\tuid:{uid}"
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅Up uid:{uid} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
async def add_season_sub(media_id: int, sub_user: str) -> str:
"""
添加订阅 UP
:param media_id: 番剧 media_id
:param sub_user: 订阅用户
"""
try:
async with db.transaction():
try:
season_info = await bangumi.get_meta(media_id)
except ResponseCodeException:
return f"未找到media_id:{media_id} 的信息,请检查Id是否正确"
season_id = season_info["media"]["season_id"]
season_current_episode = season_info["media"]["new_ep"]["index"]
season_name = season_info["media"]["title"]
if await BilibiliSub.add_bilibili_sub(
media_id,
"season",
sub_user,
season_name=season_name,
season_id=season_id,
season_current_episode=season_current_episode,
):
return (
"已成功订阅番剧:\n"
f"\ttitle: {season_name}\n"
f"\tcurrent_episode: {season_current_episode}"
)
else:
return "添加订阅失败..."
except Exception as e:
logger.error(f"订阅番剧 media_id:{media_id} 发生了错误 {type(e)}:{e}")
return "添加订阅失败..."
async def delete_sub(sub_id: str, sub_user: str) -> str:
"""
删除订阅
:param sub_id: 订阅 id
:param sub_user: 订阅用户 id # 7384933:private or 7384933:2342344(group)
"""
if await BilibiliSub.delete_bilibili_sub(int(sub_id), sub_user):
return f"已成功取消订阅:{sub_id}"
else:
return f"取消订阅:{sub_id} 失败,请检查是否订阅过该Id...."
async def get_media_id(keyword: str) -> dict:
"""
获取番剧的 media_id
:param keyword: 番剧名称
"""
params = {"keyword": keyword}
for _ in range(3):
try:
_season_data = {}
response = await AsyncHttpx.get(bilibili_search_url, params=params, timeout=5)
if response.status_code == 200:
data = response.json()
if data.get("data"):
for item in data["data"]["result"]:
if item["result_type"] == "media_bangumi":
idx = 0
for x in item["data"]:
_season_data[idx] = {
"media_id": x["media_id"],
"title": x["title"]
.replace('<em class="keyword">', "")
.replace("</em>", ""),
}
idx += 1
return _season_data
except TimeoutError:
pass
return {}
async def get_sub_status(id_: int, sub_type: str) -> Optional[str]:
"""
获取订阅状态
:param id_: 订阅 id
:param sub_type: 订阅类型
:return:
"""
try:
if sub_type == "live":
return await _get_live_status(id_)
elif sub_type == "up":
return await _get_up_status(id_)
elif sub_type == "season":
return await _get_season_status(id_)
except ResponseCodeException:
return "获取信息失败...请检查订阅Id是否存在或稍后再试..."
# except Exception as e:
# logger.error(f"获取订阅状态发生预料之外的错误 id_:{id_} {type(e)}:{e}")
# return "发生了预料之外的错误..请稍后再试或联系管理员....."
async def _get_live_status(id_: int) -> Optional[str]:
"""
获取直播订阅状态
:param id_: 直播间 id
"""
live = LiveRoom(id_)
live_info = (await live.get_room_info())["room_info"]
title = live_info["title"]
room_id = live_info["room_id"]
live_status = live_info["live_status"]
cover = live_info["cover"]
sub = await BilibiliSub.get_sub(id_)
if sub.live_status != live_status:
await BilibiliSub.update_sub_info(id_, live_status=live_status)
if sub.live_status == 0 and live_status == 1:
return (
f"{image(cover)}\n"
f"{sub.uname} 开播啦!\n"
f"标题:{title}\n"
f"直链:https://live.bilibili.com/{room_id}"
)
return None
async def _get_up_status(id_: int) -> Optional[str]:
"""
获取用户投稿状态
:param id_: 用户 id
:return:
"""
_user = await BilibiliSub.get_sub(id_)
u = user.User(_user.uid)
user_info = await u.get_user_info()
uname = user_info["name"]
video_info = await u.get_videos()
latest_video_created = 0
video = None
if _user.uname != uname:
await BilibiliSub.update_sub_info(id_, uname=uname)
dynamic_img, dynamic_upload_time = await get_user_dynamic(u, _user)
if video_info["list"].get("vlist"):
video = video_info["list"]["vlist"][0]
latest_video_created = video["created"]
rst = ""
if dynamic_img:
await BilibiliSub.update_sub_info(id_, dynamic_upload_time=dynamic_upload_time)
rst += f"{uname} 发布了动态!\n" f"{dynamic_img}\n"
if _user.latest_video_created != latest_video_created and video:
rst = rst + "-------------\n" if rst else rst
await BilibiliSub.update_sub_info(
id_, latest_video_created=latest_video_created
)
rst += (
f'{image(video["pic"])}\n'
f"{uname} 投稿了新视频啦\n"
f'标题:{video["title"]}\n'
f'Bvid:{video["bvid"]}\n'
f'直链:https://www.bilibili.com/video/{video["bvid"]}'
)
rst = None if rst == "-------------\n" else rst
return rst
async def _get_season_status(id_) -> Optional[str]:
"""
获取 番剧 更新状态
:param id_: 番剧 id
"""
season_info = await bangumi.get_meta(id_)
title = season_info["media"]["title"]
_idx = (await BilibiliSub.get_sub(id_)).season_current_episode
new_ep = season_info["media"]["new_ep"]["index"]
if new_ep != _idx:
await BilibiliSub.update_sub_info(
id_, season_current_episode=new_ep, season_update_time=datetime.now()
)
return (
f'{image(season_info["media"]["cover"])}\n'
f"[{title}]更新啦\n"
f"最新集数:{new_ep}"
)
return None
async def get_user_dynamic(
u: User, local_user: BilibiliSub
) -> "Optional[MessageSegment], int":
"""
获取用户动态
:param u: 用户类
:param local_user: 数据库存储的用户数据
:return: 最新动态截图与时间
"""
dynamic_info = await u.get_dynamics(0)
browser = await get_browser()
if dynamic_info.get("cards") and browser:
dynamic_upload_time = dynamic_info["cards"][0]["desc"]["timestamp"]
if local_user.dynamic_upload_time != dynamic_upload_time:
page = await browser.new_page()
await page.goto(
f"https://space.bilibili.com/{local_user.uid}/dynamic",
wait_until="networkidle",
timeout=10000,
)
await page.set_viewport_size({"width": 2560, "height": 1080})
# 删除置顶
await page.evaluate(
"""
xs = document.getElementsByClassName('first-card-with-title');
for (x of xs) {
x.remove();
}
"""
)
card = await page.query_selector(".card")
# 截图并保存
await card.screenshot(
path=dynamic_path / f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
timeout=100000,
)
await page.close()
return (
image(
f"{local_user.sub_id}_{dynamic_upload_time}.jpg",
"bilibili_sub/dynamic",
),
dynamic_upload_time,
)
return None, None
class SubManager:
def __init__(self):
self.live_data = []
self.up_data = []
self.season_data = []
self.current_index = -1
async def reload_sub_data(self):
"""
重载数据
"""
if not self.live_data or not self.up_data or not self.season_data:
(
_live_data,
_up_data,
_season_data,
) = await BilibiliSub.get_all_sub_data()
if not self.live_data:
self.live_data = _live_data
if not self.up_data:
self.up_data = _up_data
if not self.season_data:
self.season_data = _season_data
async def random_sub_data(self) -> Optional[BilibiliSub]:
"""
随机获取一条数据
:return:
"""
sub = None
if not self.live_data and not self.up_data and not self.season_data:
return sub
self.current_index += 1
if self.current_index == 0:
if self.live_data:
sub = random.choice(self.live_data)
self.live_data.remove(sub)
elif self.current_index == 1:
if self.up_data:
sub = random.choice(self.up_data)
self.up_data.remove(sub)
elif self.current_index == 2:
if self.season_data:
sub = random.choice(self.season_data)
self.season_data.remove(sub)
else:
self.current_index = -1
if sub:
return sub
await self.reload_sub_data()
return await self.random_sub_data()
Regular → Executable
+249 -249
View File
@@ -1,249 +1,249 @@
from services.log import logger
from services.db_context import db
from datetime import datetime
from typing import Optional, List
class BilibiliSub(db.Model):
__tablename__ = "bilibili_sub"
id = db.Column(db.Integer(), primary_key=True)
sub_id = db.Column(db.Integer(), nullable=False)
sub_type = db.Column(db.String(), nullable=False)
# 订阅用户
sub_users = db.Column(db.String(), nullable=False)
# 直播
live_short_id = db.Column(db.Integer())
live_status = db.Column(db.Integer)
# 主播/UP
uid = db.Column(db.BigInteger())
uname = db.Column(db.String())
latest_video_created = db.Column(db.BigInteger()) # 视频上传时间
dynamic_upload_time = db.Column(db.BigInteger(), default=0) # 动态发布时间
# 番剧
season_name = db.Column(db.String())
season_id = db.Column(db.Integer())
season_current_episode = db.Column(db.String())
season_update_time = db.Column(db.DateTime())
_idx1 = db.Index("bilibili_sub_idx1", "sub_id", "sub_type", unique=True)
@classmethod
async def add_bilibili_sub(
cls,
sub_id: int,
sub_type: str,
sub_user: str,
*,
live_short_id: Optional[int] = None,
live_status: Optional[int] = None,
dynamic_upload_time: Optional[int] = None,
uid: Optional[int] = None,
uname: Optional[str] = None,
latest_video_created: Optional[int] = None,
season_name: Optional[str] = None,
season_id: Optional[int] = None,
season_current_episode: Optional[str] = None,
season_update_time: Optional[datetime] = None,
) -> bool:
"""
说明:
添加订阅
参数:
:param sub_id: 订阅名称,房间号,番剧号等
:param sub_type: 订阅类型
:param sub_user: 订阅此条目的用户
:param live_short_id: 直接短 id
:param live_status: 主播开播状态
:param dynamic_upload_time: 主播/UP最新动态时间
:param uid: 主播/UP uid
:param uname: 用户名称
:param latest_video_created: 最新视频上传时间
:param season_name: 番剧名称
:param season_id: 番剧 season_id
:param season_current_episode: 番剧最新集数
:param season_update_time: 番剧更新时间
"""
try:
async with db.transaction():
query = (
await cls.query.where(cls.sub_id == sub_id)
.with_for_update()
.gino.first()
)
sub_user = sub_user if sub_user[-1] == "," else f"{sub_user},"
if query:
if sub_user not in query.sub_users:
sub_users = query.sub_users + sub_user
await query.update(sub_users=sub_users).apply()
else:
sub = await cls.create(
sub_id=sub_id, sub_type=sub_type, sub_users=sub_user
)
await sub.update(
live_short_id=live_short_id
if live_short_id
else sub.live_short_id,
live_status=live_status if live_status else sub.live_status,
dynamic_upload_time=dynamic_upload_time
if dynamic_upload_time
else sub.dynamic_upload_time,
uid=uid if uid else sub.uid,
uname=uname if uname else sub.uname,
latest_video_created=latest_video_created
if latest_video_created
else sub.latest_video_created,
season_update_time=season_update_time
if season_update_time
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode
else sub.season_current_episode,
season_id=season_id if season_id else sub.season_id,
season_name=season_name if season_name else sub.season_name,
).apply()
return True
except Exception as e:
logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}")
return False
@classmethod
async def delete_bilibili_sub(cls, sub_id: int, sub_user: str) -> bool:
"""
说明:
删除订阅
参数:
:param sub_id: 订阅名称
:param sub_user: 删除此条目的用户
"""
try:
async with db.transaction():
query = (
await cls.query.where(
(cls.sub_id == sub_id) & (cls.sub_users.contains(sub_user))
)
.with_for_update()
.gino.first()
)
if not query:
return False
await query.update(
sub_users=query.sub_users.replace(f"{sub_user},", "")
).apply()
if not query.sub_users.strip():
await query.delete()
return True
except Exception as e:
logger.info(f"bilibili_sub 删除订阅错误 {type(e)}: {e}")
return False
@classmethod
async def get_sub(cls, sub_id: int) -> Optional["BilibiliSub"]:
"""
说明:
获取订阅对象
参数:
:param sub_id: 订阅 id
"""
return await cls.query.where(cls.sub_id == sub_id).gino.first()
@classmethod
async def get_sub_data(cls, id_: str) -> List["BilibiliSub"]:
"""
获取 id_ 订阅的所有内容
:param id_: id
"""
query = cls.query.where(cls.sub_users.contains(id_))
return await query.gino.all()
@classmethod
async def update_sub_info(
cls,
sub_id: int,
*,
live_short_id: Optional[int] = None,
live_status: Optional[int] = None,
dynamic_upload_time: Optional[int] = None,
uid: Optional[int] = None,
uname: Optional[str] = None,
latest_video_created: Optional[int] = None,
season_name: Optional[str] = None,
season_id: Optional[int] = None,
season_current_episode: Optional[str] = None,
season_update_time: Optional[datetime] = None,
) -> bool:
"""
说明:
更新订阅信息
参数:
:param sub_id: 订阅名称,房间号,番剧号等
:param live_short_id: 直接短 id
:param live_status: 主播开播状态
:param dynamic_upload_time: 主播/UP最新动态时间
:param uid: 主播/UP uid
:param uname: 用户名称
:param latest_video_created: 最新视频上传时间
:param season_name: 番剧名称
:param season_id: 番剧 season_id
:param season_current_episode: 番剧最新集数
:param season_update_time: 番剧更新时间
"""
try:
async with db.transaction():
sub = (
await cls.query.where(cls.sub_id == sub_id)
.with_for_update()
.gino.first()
)
if sub:
await sub.update(
live_short_id=live_short_id
if live_short_id is not None
else sub.live_short_id,
live_status=live_status
if live_status is not None
else sub.live_status,
dynamic_upload_time=dynamic_upload_time
if dynamic_upload_time is not None
else sub.dynamic_upload_time,
uid=uid if uid is not None else sub.uid,
uname=uname if uname is not None else sub.uname,
latest_video_created=latest_video_created
if latest_video_created is not None
else sub.latest_video_created,
season_update_time=season_update_time
if season_update_time is not None
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode is not None
else sub.season_current_episode,
season_id=season_id if season_id is not None else sub.season_id,
season_name=season_name
if season_name is not None
else sub.season_name,
).apply()
return True
except Exception as e:
logger.info(f"bilibili_sub 更新订阅错误 {type(e)}: {e}")
return False
@classmethod
async def get_all_sub_data(
cls,
) -> "List[BilibiliSub], List[BilibiliSub], List[BilibiliSub]":
"""
说明:
分类获取所有数据
"""
live_data = []
up_data = []
season_data = []
query = await cls.query.gino.all()
for x in query:
if x.sub_type == "live":
live_data.append(x)
if x.sub_type == "up":
up_data.append(x)
if x.sub_type == "season":
season_data.append(x)
return live_data, up_data, season_data
from services.log import logger
from services.db_context import db
from datetime import datetime
from typing import Optional, List
class BilibiliSub(db.Model):
__tablename__ = "bilibili_sub"
id = db.Column(db.Integer(), primary_key=True)
sub_id = db.Column(db.Integer(), nullable=False)
sub_type = db.Column(db.String(), nullable=False)
# 订阅用户
sub_users = db.Column(db.String(), nullable=False)
# 直播
live_short_id = db.Column(db.Integer())
live_status = db.Column(db.Integer)
# 主播/UP
uid = db.Column(db.BigInteger())
uname = db.Column(db.String())
latest_video_created = db.Column(db.BigInteger()) # 视频上传时间
dynamic_upload_time = db.Column(db.BigInteger(), default=0) # 动态发布时间
# 番剧
season_name = db.Column(db.String())
season_id = db.Column(db.Integer())
season_current_episode = db.Column(db.String())
season_update_time = db.Column(db.DateTime())
_idx1 = db.Index("bilibili_sub_idx1", "sub_id", "sub_type", unique=True)
@classmethod
async def add_bilibili_sub(
cls,
sub_id: int,
sub_type: str,
sub_user: str,
*,
live_short_id: Optional[int] = None,
live_status: Optional[int] = None,
dynamic_upload_time: Optional[int] = None,
uid: Optional[int] = None,
uname: Optional[str] = None,
latest_video_created: Optional[int] = None,
season_name: Optional[str] = None,
season_id: Optional[int] = None,
season_current_episode: Optional[str] = None,
season_update_time: Optional[datetime] = None,
) -> bool:
"""
说明:
添加订阅
参数:
:param sub_id: 订阅名称,房间号,番剧号等
:param sub_type: 订阅类型
:param sub_user: 订阅此条目的用户
:param live_short_id: 直接短 id
:param live_status: 主播开播状态
:param dynamic_upload_time: 主播/UP最新动态时间
:param uid: 主播/UP uid
:param uname: 用户名称
:param latest_video_created: 最新视频上传时间
:param season_name: 番剧名称
:param season_id: 番剧 season_id
:param season_current_episode: 番剧最新集数
:param season_update_time: 番剧更新时间
"""
try:
async with db.transaction():
query = (
await cls.query.where(cls.sub_id == sub_id)
.with_for_update()
.gino.first()
)
sub_user = sub_user if sub_user[-1] == "," else f"{sub_user},"
if query:
if sub_user not in query.sub_users:
sub_users = query.sub_users + sub_user
await query.update(sub_users=sub_users).apply()
else:
sub = await cls.create(
sub_id=sub_id, sub_type=sub_type, sub_users=sub_user
)
await sub.update(
live_short_id=live_short_id
if live_short_id
else sub.live_short_id,
live_status=live_status if live_status else sub.live_status,
dynamic_upload_time=dynamic_upload_time
if dynamic_upload_time
else sub.dynamic_upload_time,
uid=uid if uid else sub.uid,
uname=uname if uname else sub.uname,
latest_video_created=latest_video_created
if latest_video_created
else sub.latest_video_created,
season_update_time=season_update_time
if season_update_time
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode
else sub.season_current_episode,
season_id=season_id if season_id else sub.season_id,
season_name=season_name if season_name else sub.season_name,
).apply()
return True
except Exception as e:
logger.info(f"bilibili_sub 添加订阅错误 {type(e)}: {e}")
return False
@classmethod
async def delete_bilibili_sub(cls, sub_id: int, sub_user: str) -> bool:
"""
说明:
删除订阅
参数:
:param sub_id: 订阅名称
:param sub_user: 删除此条目的用户
"""
try:
async with db.transaction():
query = (
await cls.query.where(
(cls.sub_id == sub_id) & (cls.sub_users.contains(sub_user))
)
.with_for_update()
.gino.first()
)
if not query:
return False
await query.update(
sub_users=query.sub_users.replace(f"{sub_user},", "")
).apply()
if not query.sub_users.strip():
await query.delete()
return True
except Exception as e:
logger.info(f"bilibili_sub 删除订阅错误 {type(e)}: {e}")
return False
@classmethod
async def get_sub(cls, sub_id: int) -> Optional["BilibiliSub"]:
"""
说明:
获取订阅对象
参数:
:param sub_id: 订阅 id
"""
return await cls.query.where(cls.sub_id == sub_id).gino.first()
@classmethod
async def get_sub_data(cls, id_: str) -> List["BilibiliSub"]:
"""
获取 id_ 订阅的所有内容
:param id_: id
"""
query = cls.query.where(cls.sub_users.contains(id_))
return await query.gino.all()
@classmethod
async def update_sub_info(
cls,
sub_id: int,
*,
live_short_id: Optional[int] = None,
live_status: Optional[int] = None,
dynamic_upload_time: Optional[int] = None,
uid: Optional[int] = None,
uname: Optional[str] = None,
latest_video_created: Optional[int] = None,
season_name: Optional[str] = None,
season_id: Optional[int] = None,
season_current_episode: Optional[str] = None,
season_update_time: Optional[datetime] = None,
) -> bool:
"""
说明:
更新订阅信息
参数:
:param sub_id: 订阅名称,房间号,番剧号等
:param live_short_id: 直接短 id
:param live_status: 主播开播状态
:param dynamic_upload_time: 主播/UP最新动态时间
:param uid: 主播/UP uid
:param uname: 用户名称
:param latest_video_created: 最新视频上传时间
:param season_name: 番剧名称
:param season_id: 番剧 season_id
:param season_current_episode: 番剧最新集数
:param season_update_time: 番剧更新时间
"""
try:
async with db.transaction():
sub = (
await cls.query.where(cls.sub_id == sub_id)
.with_for_update()
.gino.first()
)
if sub:
await sub.update(
live_short_id=live_short_id
if live_short_id is not None
else sub.live_short_id,
live_status=live_status
if live_status is not None
else sub.live_status,
dynamic_upload_time=dynamic_upload_time
if dynamic_upload_time is not None
else sub.dynamic_upload_time,
uid=uid if uid is not None else sub.uid,
uname=uname if uname is not None else sub.uname,
latest_video_created=latest_video_created
if latest_video_created is not None
else sub.latest_video_created,
season_update_time=season_update_time
if season_update_time is not None
else sub.season_update_time,
season_current_episode=season_current_episode
if season_current_episode is not None
else sub.season_current_episode,
season_id=season_id if season_id is not None else sub.season_id,
season_name=season_name
if season_name is not None
else sub.season_name,
).apply()
return True
except Exception as e:
logger.info(f"bilibili_sub 更新订阅错误 {type(e)}: {e}")
return False
@classmethod
async def get_all_sub_data(
cls,
) -> "List[BilibiliSub], List[BilibiliSub], List[BilibiliSub]":
"""
说明:
分类获取所有数据
"""
live_data = []
up_data = []
season_data = []
query = await cls.query.gino.all()
for x in query:
if x.sub_type == "live":
live_data.append(x)
if x.sub_type == "up":
up_data.append(x)
if x.sub_type == "season":
season_data.append(x)
return live_data, up_data, season_data
Regular → Executable
+71 -72
View File
@@ -1,72 +1,71 @@
from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH
from pathlib import Path
from bilibili_api import user
from io import BytesIO
import aiohttp
BORDER_PATH = Path(IMAGE_PATH) / 'border'
BORDER_PATH.mkdir(parents=True, exist_ok=True)
async def get_pic(url: str) -> bytes:
"""
获取图像
:param url: 图像链接
:return: 图像二进制
"""
async with aiohttp.ClientSession() as session:
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):
"""
生成主播简介图片
:param uid: 主播 uid
:param title: 直播间标题
:param cover: 直播封面
:param tags: 直播标签
:param des: 直播简介
:return:
"""
u = user.User(uid)
user_info = await u.get_user_info()
name = user_info['name']
sex = user_info['sex']
face = user_info['face']
sign = user_info['sign']
ava = CreateImg(100, 100, background=BytesIO(await get_pic(face)))
ava.circle()
cover = CreateImg(470, 265, background=BytesIO(await get_pic(cover)))
print()
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 cover: 直播封面
:param tags: 直播标签
:param des: 直播简介
:param user_name: 主播名称
:param sex: 主播性别
:param sign: 主播签名
:param ava: 主播头像
:return:
"""
border = BORDER_PATH / '0.png'
border_img = None
if border.exists():
border_img = CreateImg(1772, 2657, background=border)
bk = CreateImg(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type='by_width')
from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx
from pathlib import Path
from bilibili_api import user
from io import BytesIO
BORDER_PATH = Path(IMAGE_PATH) / "border"
BORDER_PATH.mkdir(parents=True, exist_ok=True)
async def get_pic(url: str) -> bytes:
"""
获取图像
:param url: 图像链接
:return: 图像二进制
"""
return (await AsyncHttpx.get(url, timeout=10)).content
async def create_live_des_image(uid: int, title: str, cover: str, tags: str, des: str):
"""
生成主播简介图片
:param uid: 主播 uid
:param title: 直播间标题
:param cover: 直播封面
:param tags: 直播标签
:param des: 直播简介
:return:
"""
u = user.User(uid)
user_info = await u.get_user_info()
name = user_info["name"]
sex = user_info["sex"]
face = user_info["face"]
sign = user_info["sign"]
ava = CreateImg(100, 100, background=BytesIO(await get_pic(face)))
ava.circle()
cover = CreateImg(470, 265, background=BytesIO(await get_pic(cover)))
print()
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 cover: 直播封面
:param tags: 直播标签
:param des: 直播简介
:param user_name: 主播名称
:param sex: 主播性别
:param sign: 主播签名
:param ava: 主播头像
:return:
"""
border = BORDER_PATH / "0.png"
border_img = None
if border.exists():
border_img = CreateImg(1772, 2657, background=border)
bk = CreateImg(1772, 2657, font_size=30)
bk.paste(cover, (0, 100), center_type="by_width")
Regular → Executable
+1 -4
View File
@@ -7,7 +7,6 @@ from nonebot.adapters.cqhttp import PrivateMessageEvent
from utils.utils import get_message_text
from nonebot.adapters.cqhttp.permission import PRIVATE
from asyncio.exceptions import TimeoutError
from aiohttp.client_exceptions import ServerDisconnectedError
__zx_plugin_name__ = "磁力搜索"
__plugin_usage__ = """
@@ -89,11 +88,9 @@ async def _(bot: Bot, event: PrivateMessageEvent, state: T_State):
send_flag = True
except TimeoutError:
await bt.finish(f"搜索 {keyword} 超时...")
except ServerDisconnectedError:
await bt.finish(f"搜索 {keyword} 连接失败")
except Exception as e:
await bt.finish(f"bt 其他未知错误..")
logger.error(f"bt 错误 e:{e}")
logger.error(f"bt 错误 {type(e)}:{e}")
if not send_flag:
await bt.send(f"{keyword} 未搜索到...")
logger.info(f"USER {event.user_id} BT搜索 {keyword} 第 {page} 页")
Regular → Executable
+35 -34
View File
@@ -1,8 +1,6 @@
from utils.user_agent import get_user_agent
import aiohttp
from utils.http_utils import AsyncHttpx
from configs.config import Config
from bs4 import BeautifulSoup
from utils.utils import get_local_proxy
import platform
if platform.system() == "Windows":
@@ -15,36 +13,39 @@ url = "http://www.eclzz.world"
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
) as response:
text = await response.text()
if text.find("大约0条结果") != -1:
return
soup = BeautifulSoup(text, "lxml")
item_lst = soup.find_all("div", {"class": "search-item"})
bt_max_num = Config.get_config("bt", "BT_MAX_NUM")
bt_max_num = bt_max_num if bt_max_num < len(item_lst) else len(item_lst)
for item in item_lst[:bt_max_num]:
divs = item.find_all("div")
title = (
str(divs[0].find("a").text)
.replace("<em>", "")
.replace("</em>", "")
.strip()
)
spans = divs[2].find_all("span")
itype = spans[0].text
create_time = spans[1].find("b").text
file_size = spans[2].find("b").text
link = await get_download_link(divs[0].find("a")["href"], session)
yield title, itype, create_time, file_size, link
"""
获取资源信息
:param keyword: 关键词
:param page: 页数
"""
text = (await AsyncHttpx.get(f"{url}/s/{keyword}_rel_{page}.html", timeout=5)).text
if text.find("大约0条结果") != -1:
return
soup = BeautifulSoup(text, "lxml")
item_lst = soup.find_all("div", {"class": "search-item"})
bt_max_num = Config.get_config("bt", "BT_MAX_NUM")
bt_max_num = bt_max_num if bt_max_num < len(item_lst) else len(item_lst)
for item in item_lst[:bt_max_num]:
divs = item.find_all("div")
title = (
str(divs[0].find("a").text)
.replace("<em>", "")
.replace("</em>", "")
.strip()
)
spans = divs[2].find_all("span")
itype = spans[0].text
create_time = spans[1].find("b").text
file_size = spans[2].find("b").text
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 with session.get(
f"{url}{_url}", proxy=get_local_proxy(), timeout=30
) as response:
soup = BeautifulSoup(await response.text(), "lxml")
return soup.find("a", {"id": "down-url"})["href"]
async def get_download_link(_url: str) -> str:
"""
获取资源下载地址
:param _url: 链接
"""
text = (await AsyncHttpx.get(f"{url}{_url}")).text
soup = BeautifulSoup(text, "lxml")
return soup.find("a", {"id": "down-url"})["href"]
Regular → Executable
+57 -57
View File
@@ -1,57 +1,57 @@
from .music_163 import get_song_id, get_song_info
from nonebot.adapters.cqhttp import Bot, Event, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger
from nonebot import on_command
__zx_plugin_name__ = "点歌"
__plugin_usage__ = """
usage:
在线点歌
指令:
点歌 [歌名]
""".strip()
__plugin_des__ = "为你点播了一首曾经的歌"
__plugin_cmd__ = ["点歌 [歌名]"]
__plugin_type__ = ("一些工具",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["点歌"],
}
songpicker = on_command("点歌", priority=5, block=True)
@songpicker.handle()
async def handle_first_receive(bot: Bot, event: Event, state: T_State):
args = str(event.get_message()).strip()
if args:
state["song_name"] = args
@songpicker.got("song_name", prompt="歌名是?")
async def _(bot: Bot, event: Event, state: T_State):
song = state["song_name"]
song_id = await get_song_id(song)
if not song_id:
await songpicker.finish("没有找到这首歌!", at_sender=True)
for _ in range(3):
song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}]
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 点歌 :{song}"
)
await songpicker.finish(song_content)
else:
await songpicker.finish("网易云繁忙...")
from .music_163 import get_song_id, get_song_info
from nonebot.adapters.cqhttp import Bot, Event, GroupMessageEvent
from nonebot.typing import T_State
from services.log import logger
from nonebot import on_command
__zx_plugin_name__ = "点歌"
__plugin_usage__ = """
usage:
在线点歌
指令:
点歌 [歌名]
""".strip()
__plugin_des__ = "为你点播了一首曾经的歌"
__plugin_cmd__ = ["点歌 [歌名]"]
__plugin_type__ = ("一些工具",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["点歌"],
}
music = on_command("点歌", priority=5, block=True)
@music.handle()
async def handle_first_receive(bot: Bot, event: Event, state: T_State):
args = str(event.get_message()).strip()
if args:
state["song_name"] = args
@music.got("song_name", prompt="歌名是?")
async def _(bot: Bot, event: Event, state: T_State):
song = state["song_name"]
song_id = await get_song_id(song)
if not song_id:
await music.finish("没有找到这首歌!", at_sender=True)
for _ in range(3):
song_content = [{"type": "music", "data": {"type": 163, "id": song_id}}]
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 点歌 :{song}"
)
await music.finish(song_content)
else:
await music.finish("网易云繁忙...")
Regular → Executable
+38 -47
View File
@@ -1,47 +1,38 @@
import aiohttp
import json
headers = {"referer": "http://music.163.com"}
cookies = {"appver": "2.0.2"}
async def search_song(song_name: str):
async with aiohttp.ClientSession(
headers=headers, cookies=cookies
) as session:
async with session.post(
f"http://music.163.com/api/search/get/",
data={"s": song_name, "limit": 1, "type": 1, "offset": 0},
) as r:
if r.status != 200:
return None
r = await r.text()
return json.loads(r)
async def get_song_id(songName: str) -> int:
"""
根据用户输入的songName 获取候选songId列表 [默认songId数量:5]
"""
r = await search_song(songName)
return r["result"]["songs"][0]["id"]
async def get_song_info(songId: int):
"""
获取歌曲信息
"""
async with aiohttp.ClientSession(
headers=headers, cookies=cookies
) as session:
async with session.post(
f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D",
) as r:
if r.status != 200:
return None
r = await r.text()
return json.loads(r)
from utils.http_utils import AsyncHttpx
import json
headers = {"referer": "http://music.163.com"}
cookies = {"appver": "2.0.2"}
async def search_song(song_name: str):
"""
搜索歌曲
:param song_name: 歌名
"""
r = await AsyncHttpx.post(
f"http://music.163.com/api/search/get/",
data={"s": song_name, "limit": 1, "type": 1, "offset": 0},
)
if r.status_code != 200:
return None
return json.loads(r.text)
async def get_song_id(song_name: str) -> int:
""" """
r = await search_song(song_name)
return r["result"]["songs"][0]["id"]
async def get_song_info(songId: int):
"""
获取歌曲信息
"""
r = await AsyncHttpx.post(
f"http://music.163.com/api/song/detail/?id={songId}&ids=%5B{songId}%5D",
)
if r.status_code != 200:
return None
return json.loads(r.text)
Regular → Executable
View File
Regular → Executable
+11 -22
View File
@@ -1,11 +1,7 @@
import psutil
import aiohttp
import time
from datetime import datetime
from utils.user_agent import get_user_agent
from asyncio.exceptions import TimeoutError
from aiohttp.client_exceptions import ClientConnectorError
from utils.utils import get_local_proxy
from utils.http_utils import AsyncHttpx
from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH
from pathlib import Path
@@ -34,23 +30,16 @@ class Check:
self.disk = psutil.disk_usage("/").percent
async def check_network(self):
async with aiohttp.ClientSession(headers=get_user_agent()) as session:
try:
async with session.get(
"https://www.baidu.com/", proxy=get_local_proxy(), timeout=3
) as response:
pass
except (TimeoutError, ClientConnectorError) as e:
logger.warning(f"访问BaiDu失败... e: {e}")
self.baidu = 404
try:
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
try:
await AsyncHttpx.get("https://www.baidu.com/", timeout=5)
except Exception as e:
logger.warning(f"访问BaiDu失败... {type(e)}: {e}")
self.baidu = 404
try:
await AsyncHttpx.get("https://www.google.com/", timeout=5)
except Exception as e:
logger.warning(f"访问Google失败... {type(e)}: {e}")
self.google = 404
def check_user(self):
rst = ""
+126 -126
View File
@@ -1,126 +1,126 @@
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.typing import T_State
from nonebot.permission import SUPERUSER
from nonebot import on_command
from .data_source import check_update, get_latest_version_data
from services.log import logger
from utils.utils import scheduler, get_bot
from pathlib import Path
from configs.config import Config
from nonebot.rule import to_me
import platform
import os
__zx_plugin_name__ = "自动更新 [Superuser]"
__plugin_usage__ = """
usage:
检查更新真寻最新版本,包括了自动更新
指令:
检查更新真寻
重启
""".strip()
__plugin_des__ = "就算是真寻也会成长的"
__plugin_cmd__ = ["检查更新真寻", "重启"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"AUTO_UPDATE_ZHENXUN": {
"value": False,
"help": "真寻是否自动检查更新",
"default": False,
}
}
update_zhenxun = on_command("检查更新真寻", permission=SUPERUSER, priority=1, block=True)
restart = on_command(
"重启",
aliases={"restart"},
permission=SUPERUSER,
rule=to_me(),
priority=1,
block=True,
)
@update_zhenxun.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
try:
code, error = await check_update(bot)
if error:
logger.error(f"更新真寻未知错误 {error}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=f"更新真寻未知错误 {error}"
)
except Exception as e:
logger.error(f"更新真寻未知错误 {type(e)}:{e}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"更新真寻未知错误 {type(e)}:{e}",
)
else:
if code == 200:
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=f"更新完毕,请重启真寻...."
)
@restart.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
if str(platform.system()).lower() == "windows":
await restart.finish("暂无windows重启脚本...")
@restart.got("flag", prompt="确定是否重启真寻?(重启失败咱们将失去联系,请谨慎!)")
async def _(bot: Bot, event: MessageEvent, state: T_State):
flag = state["flag"]
if flag.lower() in ["true", "是", "好", "确定", "确定是"]:
await restart.send("开始重启真寻..请稍等...")
open("is_restart", "w")
os.system("./restart.sh")
else:
await restart.send("已取消操作...")
@scheduler.scheduled_job(
"cron",
hour=12,
minute=0,
)
async def _():
if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"):
_version = "v0.0.0"
_version_file = Path() / "__version__"
if _version_file.exists():
_version = (
open(_version_file, "r", encoding="utf8")
.readline()
.split(":")[-1]
.strip()
)
data = await get_latest_version_data()
if data:
latest_version = data["name"]
if _version != latest_version:
bot = get_bot()
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"检测到真寻版本更新\n"
f"当前版本:{_version},最新版本:{latest_version}\n"
f"尝试自动更新...",
)
try:
code = await check_update(bot)
except Exception as e:
logger.error(f"更新真寻未知错误 {type(e)}:{e}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"更新真寻未知错误 {type(e)}:{e}\n",
)
else:
if code == 200:
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"更新完毕,请重启真寻....",
)
from nonebot.adapters.cqhttp import Bot, MessageEvent
from nonebot.typing import T_State
from nonebot.permission import SUPERUSER
from nonebot import on_command
from .data_source import check_update, get_latest_version_data
from services.log import logger
from utils.utils import scheduler, get_bot
from pathlib import Path
from configs.config import Config
from nonebot.rule import to_me
import platform
import os
__zx_plugin_name__ = "自动更新 [Superuser]"
__plugin_usage__ = """
usage:
检查更新真寻最新版本,包括了自动更新
指令:
检查更新真寻
重启
""".strip()
__plugin_des__ = "就算是真寻也会成长的"
__plugin_cmd__ = ["检查更新真寻", "重启"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_configs__ = {
"AUTO_UPDATE_ZHENXUN": {
"value": False,
"help": "真寻是否自动检查更新",
"default": False,
}
}
update_zhenxun = on_command("检查更新真寻", permission=SUPERUSER, priority=1, block=True)
restart = on_command(
"重启",
aliases={"restart"},
permission=SUPERUSER,
rule=to_me(),
priority=1,
block=True,
)
@update_zhenxun.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
try:
code, error = await check_update(bot)
if error:
logger.error(f"更新真寻未知错误 {error}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=f"更新真寻未知错误 {error}"
)
except Exception as e:
logger.error(f"更新真寻未知错误 {type(e)}:{e}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"更新真寻未知错误 {type(e)}:{e}",
)
else:
if code == 200:
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=f"更新完毕,请重启真寻...."
)
@restart.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
if str(platform.system()).lower() == "windows":
await restart.finish("暂无windows重启脚本...")
@restart.got("flag", prompt="确定是否重启真寻?(重启失败咱们将失去联系,请谨慎!)")
async def _(bot: Bot, event: MessageEvent, state: T_State):
flag = state["flag"]
if flag.lower() in ["true", "是", "好", "确定", "确定是"]:
await restart.send("开始重启真寻..请稍等...")
open("is_restart", "w")
os.system("./restart.sh")
else:
await restart.send("已取消操作...")
@scheduler.scheduled_job(
"cron",
hour=12,
minute=0,
)
async def _():
if Config.get_config("check_zhenxun_update", "AUTO_UPDATE_ZHENXUN"):
_version = "v0.0.0"
_version_file = Path() / "__version__"
if _version_file.exists():
_version = (
open(_version_file, "r", encoding="utf8")
.readline()
.split(":")[-1]
.strip()
)
data = await get_latest_version_data()
if data:
latest_version = data["name"]
if _version != latest_version:
bot = get_bot()
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"检测到真寻版本更新\n"
f"当前版本:{_version},最新版本:{latest_version}\n"
f"尝试自动更新...",
)
try:
code = await check_update(bot)
except Exception as e:
logger.error(f"更新真寻未知错误 {type(e)}:{e}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"更新真寻未知错误 {type(e)}:{e}\n",
)
else:
if code == 200:
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"更新完毕,请重启真寻....",
)
+212 -231
View File
@@ -1,231 +1,212 @@
from aiohttp.client_exceptions import ClientConnectorError
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 configs.path_config import IMAGE_PATH
from utils.message_builder import image
from typing import List
from services.log import logger
from pathlib import Path
import ujson as json
import nonebot
import asyncio
import aiofiles
import aiohttp
import platform
import tarfile
import shutil
import os
if str(platform.system()).lower() == "windows":
policy = asyncio.WindowsSelectorEventLoopPolicy()
asyncio.set_event_loop_policy(policy)
driver = nonebot.get_driver()
release_url = "https://api.github.com/repos/HibiKier/zhenxun_bot/releases/latest"
_version_file = Path() / "__version__"
zhenxun_latest_tar_gz = Path() / "zhenxun_latest_file.tar.gz"
temp_dir = Path() / "temp"
backup_dir = Path() / "backup"
@driver.on_bot_connect
async def remind(bot: Bot):
if str(platform.system()).lower() != "windows":
restart = Path() / "restart.sh"
if not restart.exists():
with open(restart, "w", encoding="utf8") as f:
f.write(
f"pid=$(netstat -tunlp | grep "
+ str(bot.config.port)
+ " | awk '{print $7}')\n"
"pid=${pid%/*}\n"
"kill -9 $pid\n"
"sleep 3\n"
"python3 bot.py"
)
os.system("chmod +x ./restart.sh")
logger.info("已自动生成 restart.sh(重启) 文件,请检查脚本是否与本地指令符合...")
is_restart_file = Path() / "is_restart"
if is_restart_file.exists():
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"真寻重启完毕...",
)
is_restart_file.unlink()
async def check_update(bot: Bot) -> 'int, str':
logger.info("开始检查更新真寻酱....")
_version = "v0.0.0"
if _version_file.exists():
_version = (
open(_version_file, "r", encoding="utf8").readline().split(":")[-1].strip()
)
data = await get_latest_version_data()
if data:
latest_version = data["name"]
if _version != latest_version:
tar_gz_url = data["tarball_url"]
logger.info(f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}\n" f"开始更新.....",
)
logger.info(f"开始下载真寻最新版文件....")
if await download_latest_file(tar_gz_url):
logger.info("下载真寻最新版文件完成....")
error = await asyncio.get_event_loop().run_in_executor(
None, _file_handle, latest_version
)
if error:
return 998, error
logger.info("真寻更新完毕,清理文件完成....")
logger.info("开始获取真寻更新日志.....")
update_info = data["body"]
width = 0
height = len(update_info.split('\n')) * 24
A = CreateImg(width, height, font_size=20)
for m in update_info.split('\n'):
w, h = A.getsize(m)
if w > width:
width = w
A = CreateImg(width + 50, height, font_size=20)
A.text((10, 10), update_info)
A.save(f'{IMAGE_PATH}/update_info.png')
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=Message(f"真寻更新完成,版本:{_version} -> {latest_version}\n"
f"更新日期:{data['created_at']}\n"
f"更新日志:\n"
f"{image('update_info.png')}"),
)
return 200, ''
else:
logger.warning(f"下载真寻最新版本失败...版本号:{latest_version}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"下载真寻最新版本失败...版本号:{latest_version}.",
)
else:
logger.info(f"自动获取真寻版本成功:{latest_version},当前版本为最新版,无需更新...")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"自动获取真寻版本成功:{latest_version},当前版本为最新版,无需更新...",
)
else:
logger.warning("自动获取真寻版本失败....")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=f"自动获取真寻版本失败...."
)
return 999, ''
def _file_handle(latest_version: str) -> str:
if not temp_dir.exists():
temp_dir.mkdir(exist_ok=True, parents=True)
if backup_dir.exists():
shutil.rmtree(backup_dir)
tf = None
error = ''
try:
backup_dir.mkdir(exist_ok=True, parents=True)
logger.info("开始解压真寻文件压缩包....")
tf = tarfile.open(zhenxun_latest_tar_gz)
tf.extractall(temp_dir)
logger.info("解压真寻文件压缩包完成....")
zhenxun_latest_file = Path(temp_dir) / os.listdir(temp_dir)[0]
update_info_file = Path(zhenxun_latest_file) / "update_info.json"
update_info = json.load(open(update_info_file, "r", encoding="utf8"))
update_file = update_info["update_file"]
add_file = update_info["add_file"]
delete_file = update_info["delete_file"]
config_file = Path() / "configs" / "config.py"
config_path_file = Path() / "configs" / "config_path.py"
for file in delete_file + update_file:
file = Path() / file
backup_file = Path(backup_dir) / file
if file.exists():
backup_file.parent.mkdir(parents=True, exist_ok=True)
if backup_file.exists():
backup_file.unlink()
if file not in [config_file, config_path_file]:
os.rename(file.absolute(), backup_file.absolute())
else:
with open(file, "r", encoding="utf8") as rf:
data = rf.read()
with open(backup_file, "w", encoding="utf8") as wf:
wf.write(data)
logger.info(f"已备份文件:{file}")
for file in add_file + update_file:
new_file = Path(zhenxun_latest_file) / file
old_file = Path() / file
if old_file not in [config_file, config_path_file]:
if not old_file.exists() and new_file.exists():
os.rename(new_file.absolute(), old_file.absolute())
logger.info(f"已更新文件:{file}")
else:
tmp = ""
new_lines = open(new_file, "r", encoding="utf8").readlines()
old_lines = open(old_file, "r", encoding="utf8").readlines()
for nl in new_lines:
tmp += check_old_lines(old_lines, nl)
with open(file, "w", encoding="utf8") as f:
f.write(tmp)
except Exception as e:
error = f'{type(e)}:{e}'
if tf:
tf.close()
if temp_dir.exists():
shutil.rmtree(temp_dir)
if zhenxun_latest_tar_gz.exists():
zhenxun_latest_tar_gz.unlink()
local_update_info_file = Path() / "update_info.json"
if local_update_info_file.exists():
local_update_info_file.unlink()
with open(_version_file, "w", encoding="utf8") as f:
f.write(f"__version__: {latest_version}")
return error
# 获取最新版本号
async def get_latest_version_data() -> dict:
async with aiohttp.ClientSession(headers=get_user_agent()) as session:
for _ in range(3):
try:
async with session.get(release_url, proxy=get_local_proxy()) as res:
if res.status == 200:
return await res.json()
except (TimeoutError, ClientConnectorError):
pass
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:
if "=" not in line:
return line
for l in lines:
if "=" in l and l.split("=")[0].strip() == line.split("=")[0].strip():
if l.split("=")[1].strip() == 'None':
return l
return line
from nonebot.adapters.cqhttp import Bot, Message
from utils.image_utils import CreateImg
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.http_utils import AsyncHttpx
from typing import List
from services.log import logger
from pathlib import Path
import ujson as json
import nonebot
import asyncio
import platform
import tarfile
import shutil
import os
if str(platform.system()).lower() == "windows":
policy = asyncio.WindowsSelectorEventLoopPolicy()
asyncio.set_event_loop_policy(policy)
driver = nonebot.get_driver()
release_url = "https://api.github.com/repos/HibiKier/zhenxun_bot/releases/latest"
_version_file = Path() / "__version__"
zhenxun_latest_tar_gz = Path() / "zhenxun_latest_file.tar.gz"
temp_dir = Path() / "temp"
backup_dir = Path() / "backup"
@driver.on_bot_connect
async def remind(bot: Bot):
if str(platform.system()).lower() != "windows":
restart = Path() / "restart.sh"
if not restart.exists():
with open(restart, "w", encoding="utf8") as f:
f.write(
f"pid=$(netstat -tunlp | grep "
+ str(bot.config.port)
+ " | awk '{print $7}')\n"
"pid=${pid%/*}\n"
"kill -9 $pid\n"
"sleep 3\n"
"python3 bot.py"
)
os.system("chmod +x ./restart.sh")
logger.info("已自动生成 restart.sh(重启) 文件,请检查脚本是否与本地指令符合...")
is_restart_file = Path() / "is_restart"
if is_restart_file.exists():
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"真寻重启完毕...",
)
is_restart_file.unlink()
async def check_update(bot: Bot) -> 'int, str':
logger.info("开始检查更新真寻酱....")
_version = "v0.0.0"
if _version_file.exists():
_version = (
open(_version_file, "r", encoding="utf8").readline().split(":")[-1].strip()
)
data = await get_latest_version_data()
if data:
latest_version = data["name"]
if _version != latest_version:
tar_gz_url = data["tarball_url"]
logger.info(f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"检测真寻已更新,当前版本:{_version},最新版本:{latest_version}\n" f"开始更新.....",
)
logger.info(f"开始下载真寻最新版文件....")
if await AsyncHttpx.download_file(tar_gz_url, zhenxun_latest_tar_gz):
logger.info("下载真寻最新版文件完成....")
error = await asyncio.get_event_loop().run_in_executor(
None, _file_handle, latest_version
)
if error:
return 998, error
logger.info("真寻更新完毕,清理文件完成....")
logger.info("开始获取真寻更新日志.....")
update_info = data["body"]
width = 0
height = len(update_info.split('\n')) * 24
A = CreateImg(width, height, font_size=20)
for m in update_info.split('\n'):
w, h = A.getsize(m)
if w > width:
width = w
A = CreateImg(width + 50, height, font_size=20)
A.text((10, 10), update_info)
A.save(f'{IMAGE_PATH}/update_info.png')
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=Message(f"真寻更新完成,版本:{_version} -> {latest_version}\n"
f"更新日期:{data['created_at']}\n"
f"更新日志:\n"
f"{image('update_info.png')}"),
)
return 200, ''
else:
logger.warning(f"下载真寻最新版本失败...版本号:{latest_version}")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"下载真寻最新版本失败...版本号:{latest_version}.",
)
else:
logger.info(f"自动获取真寻版本成功:{latest_version},当前版本为最新版,无需更新...")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]),
message=f"自动获取真寻版本成功:{latest_version},当前版本为最新版,无需更新...",
)
else:
logger.warning("自动获取真寻版本失败....")
await bot.send_private_msg(
user_id=int(list(bot.config.superusers)[0]), message=f"自动获取真寻版本失败...."
)
return 999, ''
def _file_handle(latest_version: str) -> str:
if not temp_dir.exists():
temp_dir.mkdir(exist_ok=True, parents=True)
if backup_dir.exists():
shutil.rmtree(backup_dir)
tf = None
error = ''
try:
backup_dir.mkdir(exist_ok=True, parents=True)
logger.info("开始解压真寻文件压缩包....")
tf = tarfile.open(zhenxun_latest_tar_gz)
tf.extractall(temp_dir)
logger.info("解压真寻文件压缩包完成....")
zhenxun_latest_file = Path(temp_dir) / os.listdir(temp_dir)[0]
update_info_file = Path(zhenxun_latest_file) / "update_info.json"
update_info = json.load(open(update_info_file, "r", encoding="utf8"))
update_file = update_info["update_file"]
add_file = update_info["add_file"]
delete_file = update_info["delete_file"]
config_file = Path() / "configs" / "config.py"
config_path_file = Path() / "configs" / "config_path.py"
for file in delete_file + update_file:
file = Path() / file
backup_file = Path(backup_dir) / file
if file.exists():
backup_file.parent.mkdir(parents=True, exist_ok=True)
if backup_file.exists():
backup_file.unlink()
if file not in [config_file, config_path_file]:
os.rename(file.absolute(), backup_file.absolute())
else:
with open(file, "r", encoding="utf8") as rf:
data = rf.read()
with open(backup_file, "w", encoding="utf8") as wf:
wf.write(data)
logger.info(f"已备份文件:{file}")
for file in add_file + update_file:
new_file = Path(zhenxun_latest_file) / file
old_file = Path() / file
if old_file not in [config_file, config_path_file]:
if not old_file.exists() and new_file.exists():
os.rename(new_file.absolute(), old_file.absolute())
logger.info(f"已更新文件:{file}")
else:
tmp = ""
new_lines = open(new_file, "r", encoding="utf8").readlines()
old_lines = open(old_file, "r", encoding="utf8").readlines()
for nl in new_lines:
tmp += check_old_lines(old_lines, nl)
with open(file, "w", encoding="utf8") as f:
f.write(tmp)
except Exception as e:
error = f'{type(e)}:{e}'
if tf:
tf.close()
if temp_dir.exists():
shutil.rmtree(temp_dir)
if zhenxun_latest_tar_gz.exists():
zhenxun_latest_tar_gz.unlink()
local_update_info_file = Path() / "update_info.json"
if local_update_info_file.exists():
local_update_info_file.unlink()
with open(_version_file, "w", encoding="utf8") as f:
f.write(f"__version__: {latest_version}")
return error
# 获取最新版本号
async def get_latest_version_data() -> dict:
for _ in range(3):
try:
res = await AsyncHttpx.get(release_url)
if res.status_code == 200:
return res.json()
except TimeoutError:
pass
except Exception as e:
logger.error(f"检查更新真寻获取版本失败 {type(e)}:{e}")
return {}
# 逐行检测
def check_old_lines(lines: List[str], line: str) -> str:
if "=" not in line:
return line
for l in lines:
if "=" in l and l.split("=")[0].strip() == line.split("=")[0].strip():
return l
return line
Regular → Executable
+8 -38
View File
@@ -1,13 +1,8 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from services.log import logger
from asyncio.exceptions import TimeoutError
from nonebot.adapters.cqhttp import Bot, MessageEvent
from utils.message_builder import image
from configs.path_config import IMAGE_PATH
from utils.utils import get_local_proxy
import aiohttp
import aiofiles
from services.log import logger
__zx_plugin_name__ = "coser"
__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()
async def _(bot: Bot, event: MessageEvent, state: T_State):
async with aiohttp.ClientSession() as session:
try:
for _ in range(3):
try:
async with session.get(url, proxy=get_local_proxy(), timeout=2, verify_ssl=False) as response:
_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}")
try:
await coser.send(image(url))
except Exception as e:
await coser.send("你cos给我看!")
logger.error(f"coser 发送了未知错误 {type(e)}:{e}")
-69
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)
-130
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
Regular → Executable
View File
Regular → Executable
View File
+26 -33
View File
@@ -1,11 +1,11 @@
import aiohttp
from bs4 import BeautifulSoup
import re
from datetime import datetime, timedelta
from .config import DRAW_PATH
from pathlib import Path
from asyncio.exceptions import TimeoutError
from services.log import logger
from utils.http_utils import AsyncHttpx
import re
try:
import ujson as json
except ModuleNotFoundError:
@@ -66,16 +66,14 @@ class PrtsAnnouncement:
self.game_name = '明日方舟'
async def _get_announcement_text(self):
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(prts_url, timeout=7) as res:
soup = BeautifulSoup(await res.text(), 'lxml')
ol = soup.find('ol', {'class': 'articleList active', 'data-category-key': 'LATEST'})
for li in ol:
itype = li.find('span', {'class': 'articleItemCate'}).text
if itype == '活动':
a = li.find('a')['href']
async with session.get(f'https://ak.hypergryph.com{a}', headers=headers, timeout=7) as res:
return await res.text()
text = (await AsyncHttpx.get(prts_url)).text
soup = BeautifulSoup(text, 'lxml')
ol = soup.find('ol', {'class': 'articleList active', 'data-category-key': 'LATEST'})
for li in ol:
itype = li.find('span', {'class': 'articleItemCate'}).text
if itype == '活动':
a = li.find('a')['href']
return (await AsyncHttpx.get(f'https://ak.hypergryph.com{a}')).text
async def update_up_char(self):
prts_up_char.parent.mkdir(parents=True, exist_ok=True)
@@ -147,9 +145,7 @@ class GenshinAnnouncement:
self.game_name = '原神'
async def _get_announcement_text(self):
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(genshin_url, timeout=7) as res:
return await res.text()
return (await AsyncHttpx.get(genshin_url)).text
async def update_up_char(self):
genshin_up_char.parent.mkdir(exist_ok=True, parents=True)
@@ -218,21 +214,20 @@ class PrettyAnnouncement:
self.game_name = '赛马娘'
async def _get_announcement_text(self):
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(pretty_url, timeout=7) as res:
soup = BeautifulSoup(await res.text(), 'lxml')
divs = soup.find('div', {'id': 'mw-content-text'}).find('div').find_all('div')
for div in divs:
a = div.find('a')
try:
title = a['title']
except (KeyError, TypeError):
continue
if title.find('新角色追加') != -1:
url = a['href']
break
async with session.get(f'https://wiki.biligame.com/{url}', timeout=7) as res:
return await res.text(), title[:-2]
text = (await AsyncHttpx.get(pretty_url)).text
soup = BeautifulSoup(text, 'lxml')
divs = soup.find('div', {'id': 'mw-content-text'}).find('div').find_all('div')
title = " "
for div in divs:
a = div.find('a')
try:
title = a['title']
except (KeyError, TypeError):
continue
if title.find('新角色追加') != -1:
url = a['href']
break
return (await AsyncHttpx.get(f'https://wiki.biligame.com/{url}')).text, title[:-2]
async def update_up_char(self):
pretty_up_char.parent.mkdir(exist_ok=True, parents=True)
@@ -343,9 +338,7 @@ class GuardianAnnouncement:
self.game_name = '坎公骑冠剑'
async def _get_announcement_text(self):
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(guardian_url, timeout=7) as res:
return await res.text()
return (await AsyncHttpx.get(guardian_url)).text
async def update_up_char(self):
data = {
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
View File
View File
+39 -11
View File
@@ -2,6 +2,7 @@ from typing import Any
from .config import DATA_PATH
from utils.utils import is_number
from pathlib import Path
from services.log import logger
try:
import ujson as json
except ModuleNotFoundError:
@@ -27,8 +28,11 @@ def init_game_pool(game: str, data: dict, Operator: Any):
limited = True
if key.find('阿米娅') != -1:
continue
tmp_lst.append(Operator(name=key, star=int(data[key]['星级']),
limited=limited, recruit_only=recruit_only, event_only=event_only))
try:
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':
for key in data.keys():
if key.find('旅行者') != -1:
@@ -36,17 +40,26 @@ def init_game_pool(game: str, data: dict, Operator: Any):
limited = False
if data[key]['常驻/限定'] == '限定UP':
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':
for key in data.keys():
if data[key]['获取途径'].find('祈愿') != -1:
limited = False
if data[key]['获取途径'].find('限定祈愿') != -1:
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':
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':
for key in data.keys():
limited = False
@@ -54,7 +67,10 @@ def init_game_pool(game: str, data: dict, Operator: Any):
limited = True
if not data[key]['获取方式']:
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']:
for key in data.keys():
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
if key.find('(') != -1:
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':
for key in data.keys():
if is_number(data[key]['星级']):
limited = False
if '可以建造' not in data[key]['获取途径']:
limited = True
tmp_lst.append(Operator(name=data[key]['名称'], star=int(data[key]['星级']),
limited=limited, itype=data[key]['类型']))
try:
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']:
for key in data.keys():
limited = False
@@ -80,14 +102,20 @@ def init_game_pool(game: str, data: dict, Operator: Any):
limited = True
except KeyError:
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':
for key in data.keys():
limited = False
if key in ['奴良陆生', '卖药郎', '鬼灯', '阿香', '蜜桃&芥子', '犬夜叉', '杀生丸', '桔梗', '朽木露琪亚', '黑崎一护',
'灶门祢豆子', '灶门炭治郎']:
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)
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')
-1
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 dataclasses import dataclass
from .init_card_pool import init_game_pool
import nonebot
try:
import ujson as json
except ModuleNotFoundError:
Regular → Executable
-3
View File
@@ -1,7 +1,5 @@
import ujson as json
import os
from nonebot.adapters.cqhttp import MessageSegment
import nonebot
import random
from .update_game_info import update_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 .init_card_pool import init_game_pool
driver: nonebot.Driver = nonebot.get_driver()
ALL_CHAR = []
View File
Regular → Executable
View File
Regular → Executable
View File
+57 -60
View File
@@ -1,13 +1,11 @@
#coding:utf-8
import aiohttp
from .config import DRAW_PATH
from asyncio.exceptions import TimeoutError
from bs4 import BeautifulSoup
import asyncio
from .util import download_img
from urllib.parse import unquote
from services.log import logger
from .util import remove_prohibited_str
from utils.http_utils import AsyncHttpx
import bs4
import re
try:
@@ -26,40 +24,39 @@ async def update_info(url: str, game_name: str, info_list: list = None) -> 'dict
except (ValueError, FileNotFoundError):
data = {}
try:
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(url, timeout=7) as response:
soup = BeautifulSoup(await response.text(), 'lxml')
_tbody = get_tbody(soup, game_name, url)
trs = _tbody.find_all('tr')
att_dict, start_index, index = init_attr(game_name)
if game_name == 'guardian':
start_index = 1
if game_name == 'azur':
start_index = 0
for th in trs[0].find_all('th')[start_index:]:
text = th.text
if text[-1] == '\n':
text = text[:-1]
att_dict[text] = index
index += 1
for tr in trs[1:]:
member_dict = {}
tds = tr.find_all('td')
if not info_list:
info_list = att_dict.keys()
for key in info_list:
key, attr = parse_key(key, game_name)
td = tds[att_dict[key]]
last_tag = unquote(_find_last_tag(td, attr, game_name), 'utf-8')
member_dict[key] = last_tag
member_dict = intermediate_check(member_dict, key, game_name, td)
avatar_img = await _modify_avatar_url(session, game_name, member_dict["名称"])
member_dict['头像'] = avatar_img if avatar_img else member_dict['头像']
member_dict, name = replace_update_name(member_dict, game_name)
await download_img(member_dict['头像'], game_name, name)
data[name] = member_dict
logger.info(f'{name} is update...')
data = await _last_check(data, game_name, session)
text = (await AsyncHttpx.get(url)).text
soup = BeautifulSoup(text, 'lxml')
_tbody = get_tbody(soup, game_name, url)
trs = _tbody.find_all('tr')
att_dict, start_index, index = init_attr(game_name)
if game_name == 'guardian':
start_index = 1
if game_name == 'azur':
start_index = 0
for th in trs[0].find_all('th')[start_index:]:
text = th.text
if text[-1] == '\n':
text = text[:-1]
att_dict[text] = index
index += 1
for tr in trs[1:]:
member_dict = {}
tds = tr.find_all('td')
if not info_list:
info_list = att_dict.keys()
for key in info_list:
key, attr = parse_key(key, game_name)
td = tds[att_dict[key]]
last_tag = unquote(_find_last_tag(td, attr, game_name), 'utf-8')
member_dict[key] = last_tag
member_dict = intermediate_check(member_dict, key, game_name, td)
avatar_img = await _modify_avatar_url(game_name, member_dict["名称"])
member_dict['头像'] = avatar_img if avatar_img else member_dict['头像']
member_dict, name = replace_update_name(member_dict, game_name)
await download_img(member_dict['头像'], game_name, name)
data[name] = member_dict
logger.info(f'{name} is update...')
data = await _last_check(data, game_name)
except TimeoutError:
logger.warning(f'更新 {game_name} 超时...')
return {}, 999
@@ -97,7 +94,7 @@ def _find_last_tag(element: bs4.element.Tag, attr: str, game_name: str) -> str:
# 获取大图(小图快爬)
async def _modify_avatar_url(session: aiohttp.ClientSession, game_name: str, char_name: str):
async def _modify_avatar_url(game_name: str, char_name: str):
# if game_name == 'prts':
# async with session.get(f'https://wiki.biligame.com/arknights/{char_name}', timeout=7) as res:
# soup = BeautifulSoup(await res.text(), 'lxml')
@@ -109,10 +106,10 @@ async def _modify_avatar_url(session: aiohttp.ClientSession, game_name: str, cha
if game_name == 'genshin':
return None
if game_name == 'pretty_card':
async with session.get(f'https://wiki.biligame.com/umamusume/{char_name}', timeout=7) as res:
soup = BeautifulSoup(await res.text(), 'lxml')
img_url = soup.find('div', {'class': 'support_card-left'}).find('div').find('img').get('src')
return img_url
text = (await AsyncHttpx.get(f'https://wiki.biligame.com/umamusume/{char_name}')).text
soup = BeautifulSoup(text, 'lxml')
img_url = soup.find('div', {'class': 'support_card-left'}).find('div').find('img').get('src')
return img_url
if game_name == 'guardian':
# 未上传图片太多,换成像素图
# async with session.get(f'https://wiki.biligame.com/gt/{char_name}', timeout=7) as res:
@@ -130,7 +127,7 @@ async def _modify_avatar_url(session: aiohttp.ClientSession, game_name: str, cha
# 数据最后处理(是否需要额外数据或处理数据)
async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession):
async def _last_check(data: dict, game_name: str):
# if game_name == 'prts':
# url = 'https://wiki.biligame.com/arknights/'
# tasks = []
@@ -142,23 +139,23 @@ async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession
# data[key]['获取途径'] = x[key]['获取途径']
if game_name == 'genshin':
for key in data.keys():
async with session.get(f'https://wiki.biligame.com/ys/{key}', timeout=7) as res:
soup = BeautifulSoup(await res.text(), 'lxml')
_trs = ''
for table in soup.find_all('table', {'class': 'wikitable'}):
if str(table).find('常驻/限定') != -1:
_trs = table.find('tbody').find_all('tr')
break
for tr in _trs:
data[key]['常驻/限定'] = '未知'
if str(tr).find('限定UP') != -1:
data[key]['常驻/限定'] = '限定UP'
logger.info(f'原神获取额外数据 {key}...{data[key]["常驻/限定"]}')
break
elif str(tr).find('常驻UP') != -1:
data[key]['常驻/限定'] = '常驻UP'
logger.info(f'原神获取额外数据 {key}...{data[key]["常驻/限定"]}')
break
text = (await AsyncHttpx.get(f'https://wiki.biligame.com/ys/{key}')).text
soup = BeautifulSoup(text, 'lxml')
_trs = ''
for table in soup.find_all('table', {'class': 'wikitable'}):
if str(table).find('常驻/限定') != -1:
_trs = table.find('tbody').find_all('tr')
break
for tr in _trs:
data[key]['常驻/限定'] = '未知'
if str(tr).find('限定UP') != -1:
data[key]['常驻/限定'] = '限定UP'
logger.info(f'原神获取额外数据 {key}...{data[key]["常驻/限定"]}')
break
elif str(tr).find('常驻UP') != -1:
data[key]['常驻/限定'] = '常驻UP'
logger.info(f'原神获取额外数据 {key}...{data[key]["常驻/限定"]}')
break
if game_name == 'pretty':
for keys in data.keys():
for key in data[keys].keys():
+98 -93
View File
@@ -1,58 +1,59 @@
import aiohttp
from .config import DRAW_PATH, SEMAPHORE
from asyncio.exceptions import TimeoutError
from .util import download_img
from bs4 import BeautifulSoup
from .util import remove_prohibited_str
from utils.http_utils import AsyncHttpx
from services.log import logger
import asyncio
try:
import ujson as json
except ModuleNotFoundError:
import json
headers = {'User-Agent': '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"'}
headers = {
"User-Agent": '"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)"'
}
async def update_requests_info(game_name: str):
try:
with open(DRAW_PATH + f'{game_name}.json', 'r', encoding='utf8') as f:
with open(DRAW_PATH + f"{game_name}.json", "r", encoding="utf8") as f:
data = json.load(f)
except (ValueError, FileNotFoundError):
data = {}
try:
async with aiohttp.ClientSession(headers=headers) as session:
if game_name in ['fgo', 'fgo_card']:
if game_name == 'fgo':
url = 'http://fgo.vgtime.com/servant/ajax?card=&wd=&ids=&sort=12777&o=desc&pn='
else:
url = 'http://fgo.vgtime.com/equipment/ajax?wd=&ids=&sort=12958&o=desc&pn='
for i in range(9999):
async with session.get(f'{url}{i}', timeout=7) as response:
fgo_data = json.loads(await response.text())
if int(fgo_data['nums']) == 0:
break
for x in fgo_data['data']:
x['name'] = remove_prohibited_str(x['name'])
key = x['name']
data = add_to_data(data, x, game_name)
await download_img(data[key]['头像'], game_name, key)
logger.info(f'{key} is update...')
if game_name == 'onmyoji':
url = 'https://yys.res.netease.com/pc/zt/20161108171335/js/app/all_shishen.json?v74='
async with session.get(f'{url}', timeout=7) as response:
onmyoji_data = await response.json()
for x in onmyoji_data:
x['name'] = remove_prohibited_str(x['name'])
key = x['name']
data = add_to_data(data, x, game_name)
logger.info(f'{key} is update...')
data = await _last_check(data, game_name, session)
if game_name in ["fgo", "fgo_card"]:
if game_name == "fgo":
url = "http://fgo.vgtime.com/servant/ajax?card=&wd=&ids=&sort=12777&o=desc&pn="
else:
url = "http://fgo.vgtime.com/equipment/ajax?wd=&ids=&sort=12958&o=desc&pn="
for i in range(9999):
text = (await AsyncHttpx.get(f"{url}{i}")).text
fgo_data = json.loads(text)
if int(fgo_data["nums"]) == 0:
break
for x in fgo_data["data"]:
x["name"] = remove_prohibited_str(x["name"])
key = x["name"]
data = add_to_data(data, x, game_name)
await download_img(data[key]["头像"], game_name, key)
logger.info(f"{key} is update...")
if game_name == "onmyoji":
url = "https://yys.res.netease.com/pc/zt/20161108171335/js/app/all_shishen.json?v74="
onmyoji_data = (await AsyncHttpx.get(f"{url}")).json()
for x in onmyoji_data:
x["name"] = remove_prohibited_str(x["name"])
key = x["name"]
data = add_to_data(data, x, game_name)
logger.info(f"{key} is update...")
data = await _last_check(data, game_name)
except TimeoutError:
logger.warning(f'更新 {game_name} 超时...')
logger.warning(f"更新 {game_name} 超时...")
return {}, 999
with open(DRAW_PATH + f'{game_name}.json', 'w', encoding='utf8') as wf:
with open(DRAW_PATH + f"{game_name}.json", "w", encoding="utf8") as wf:
json.dump(data, wf, ensure_ascii=False, indent=4)
return data, 200
@@ -60,91 +61,95 @@ async def update_requests_info(game_name: str):
# 添加到字典
def add_to_data(data: dict, x: dict, game_name: str) -> dict:
member_dict = {}
if game_name == 'fgo':
if game_name == "fgo":
member_dict = {
'id': x['id'],
'card_id': x['charid'],
'头像': x['icon'],
'名称': x['name'],
'职阶': x['classes'],
'星级': x['star'],
'hp': x['lvmax4hp'],
'atk': x['lvmax4atk'],
'card_quick': x['cardquick'],
'card_arts': x['cardarts'],
'card_buster': x['cardbuster'],
'宝具': x['tprop'],
"id": x["id"],
"card_id": x["charid"],
"头像": x["icon"],
"名称": x["name"],
"职阶": x["classes"],
"星级": x["star"],
"hp": x["lvmax4hp"],
"atk": x["lvmax4atk"],
"card_quick": x["cardquick"],
"card_arts": x["cardarts"],
"card_buster": x["cardbuster"],
"宝具": x["tprop"],
}
if game_name == 'fgo_card':
if game_name == "fgo_card":
member_dict = {
'id': x['id'],
'card_id': x['equipid'],
'头像': x['icon'],
'名称': x['name'],
'星级': x['star'],
'hp': x['lvmax_hp'],
'atk': x['lvmax_atk'],
'skill_e': x['skill_e'].split('<br />')[: -1],
"id": x["id"],
"card_id": x["equipid"],
"头像": x["icon"],
"名称": x["name"],
"星级": x["star"],
"hp": x["lvmax_hp"],
"atk": x["lvmax_atk"],
"skill_e": x["skill_e"].split("<br />")[:-1],
}
if game_name == 'onmyoji':
if game_name == "onmyoji":
member_dict = {
'id': x['id'],
'名称': x['name'],
'星级': x['level'],
"id": x["id"],
"名称": x["name"],
"星级": x["level"],
}
data[member_dict['名称']] = member_dict
data[member_dict["名称"]] = member_dict
return data
# 获取额外数据
async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession) -> dict:
if game_name == 'fgo':
url = 'http://fgo.vgtime.com/servant/'
async def _last_check(data: dict, game_name: str) -> dict:
if game_name == "fgo":
url = "http://fgo.vgtime.com/servant/"
tasks = []
semaphore = asyncio.Semaphore(SEMAPHORE)
for key in data.keys():
tasks.append(asyncio.ensure_future(_async_update_fgo_extra_info(url, key, data[key]['id'], session, semaphore)))
tasks.append(
asyncio.ensure_future(
_async_update_fgo_extra_info(url, key, data[key]["id"], semaphore)
)
)
asyResult = await asyncio.gather(*tasks)
for x in asyResult:
for key in x.keys():
data[key]['入手方式'] = x[key]['入手方式']
if game_name == 'onmyoji':
url = 'https://yys.163.com/shishen/{}.html'
data[key]["入手方式"] = x[key]["入手方式"]
if game_name == "onmyoji":
url = "https://yys.163.com/shishen/{}.html"
for key in data.keys():
async with session.get(f'{url.format(data[key]["id"])}', timeout=7) as response:
soup = BeautifulSoup(await response.text(), 'lxml')
data[key]['头像'] = "https:" + soup.find('div', {'class': 'pic_wrap'}).find('img')['src']
await download_img(data[key]['头像'], game_name, key)
text = (await AsyncHttpx.get(f'{url.format(data[key]["id"])}')).text
soup = BeautifulSoup(text, "lxml")
data[key]["头像"] = (
"https:" + soup.find("div", {"class": "pic_wrap"}).find("img")["src"]
)
await download_img(data[key]["头像"], game_name, key)
return data
async def _async_update_fgo_extra_info(url: str, key: str, _id: str, session: aiohttp.ClientSession, semaphore):
async def _async_update_fgo_extra_info(url: str, key: str, _id: str, semaphore):
# 防止访问超时
async with semaphore:
for i in range(10):
try:
async with session.get(f'{url}{_id}', timeout=7) as response:
soup = BeautifulSoup(await response.text(), 'lxml')
obtain = soup.find('table', {'class': 'uk-table uk-codex-table'}).find_all('td')[-1].text
if obtain.find('限时活动免费获取 活动结束后无法获得') != -1:
obtain = ['活动获取']
elif obtain.find('非限时UP无法获得') != -1:
obtain = ['限时召唤']
text = (await AsyncHttpx.get(f"{url}{_id}")).text
soup = BeautifulSoup(text, "lxml")
obtain = (
soup.find("table", {"class": "uk-table uk-codex-table"})
.find_all("td")[-1]
.text
)
if obtain.find("限时活动免费获取 活动结束后无法获得") != -1:
obtain = ["活动获取"]
elif obtain.find("非限时UP无法获得") != -1:
obtain = ["限时召唤"]
else:
if obtain.find("&") != -1:
obtain = obtain.strip().split("&")
else:
if obtain.find('&') != -1:
obtain = obtain.strip().split('&')
else:
obtain = obtain.strip().split(' ')
logger.info(f'Fgo获取额外信息 {key}....{obtain}')
x = {key: {}}
x[key]['入手方式'] = obtain
return x
obtain = obtain.strip().split(" ")
logger.info(f"Fgo获取额外信息 {key}....{obtain}")
x = {key: {}}
x[key]["入手方式"] = obtain
return x
except TimeoutError:
logger.warning(f'访问{url}{_id} 第 {i}次 超时...已再次访问')
logger.warning(f"访问{url}{_id} 第 {i}次 超时...已再次访问")
return {}
+39 -36
View File
@@ -1,4 +1,3 @@
import aiohttp
from .config import DRAW_PATH, SEMAPHORE
from asyncio.exceptions import TimeoutError
from bs4 import BeautifulSoup
@@ -6,6 +5,7 @@ from .util import download_img
from .util import remove_prohibited_str
from urllib.parse import unquote
from services.log import logger
from utils.http_utils import AsyncHttpx
import bs4
import asyncio
@@ -24,19 +24,21 @@ async def update_simple_info(url: str, game_name: str) -> 'dict, int':
except (ValueError, FileNotFoundError):
data = {}
try:
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(url, timeout=7) as response:
soup = BeautifulSoup(await response.text(), 'lxml')
divs = get_char_divs(soup, game_name)
for div in divs:
type_lst = get_type_lst(div, game_name)
index = 0
for char_lst in type_lst:
contents = get_char_lst_contents(char_lst, game_name)
for char in contents:
data = await retrieve_char_data(char, game_name, data, index)
index += 1
data = await _last_check(data, game_name, session)
text = (await AsyncHttpx.get(url)).text
soup = BeautifulSoup(text, 'lxml')
divs = get_char_divs(soup, game_name)
for div in divs:
type_lst = get_type_lst(div, game_name)
index = 0
for char_lst in type_lst:
try:
contents = get_char_lst_contents(char_lst, game_name)
except AttributeError:
continue
for char in contents[1:]:
data = await retrieve_char_data(char, game_name, data, index)
index += 1
data = await _last_check(data, game_name)
except TimeoutError:
logger.warning(f'更新 {game_name} 超时...')
return {}, 999
@@ -71,7 +73,7 @@ def get_char_lst_contents(char_lst: bs4.element.Tag, game_name: str):
# 额外数据
async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession) -> dict:
async def _last_check(data: dict, game_name: str) -> dict:
if game_name == 'azur':
idx = 1
for url in [
@@ -91,7 +93,7 @@ async def _last_check(data: dict, game_name: str, session: aiohttp.ClientSession
tasks = []
semaphore = asyncio.Semaphore(SEMAPHORE)
for key in data.keys():
tasks.append(asyncio.ensure_future(_async_update_azur_extra_info(key, session, semaphore)))
tasks.append(asyncio.ensure_future(_async_update_azur_extra_info(key, semaphore)))
asyResult = await asyncio.gather(*tasks)
for x in asyResult:
for key in x.keys():
@@ -125,10 +127,11 @@ async def retrieve_char_data(char: bs4.element.Tag, game_name: str, data: dict,
'名称': remove_prohibited_str(char.find('a')['title']),
'星级': 3 - index}
if game_name == 'azur':
char = char.find('td').find('div')
print(char)
char = char.find('div').find('div').find('div').find('div')
avatar_img = char.find('a').find('img')
try:
member_dict['名称'] = remove_prohibited_str(str(avatar_img['alt'])[: str(avatar_img['alt']).find('头像')])
member_dict['名称'] = remove_prohibited_str(char.find('a')['title'])
except TypeError:
member_dict['名称'] = char.find('a')['title'][:-4]
try:
@@ -138,7 +141,7 @@ async def retrieve_char_data(char: bs4.element.Tag, game_name: str, data: dict,
except TypeError:
member_dict['头像'] = "img link not find..."
logger.warning(f'{member_dict["名称"]} 图片缺失....')
star = char.find('div').find('img')['alt']
star = char.find('img')['alt']
if star == '舰娘头像外框普通.png':
star = 1
elif star == '舰娘头像外框稀有.png':
@@ -161,28 +164,28 @@ async def retrieve_char_data(char: bs4.element.Tag, game_name: str, data: dict,
return data
async def _async_update_azur_extra_info(key: str, session: aiohttp.ClientSession, semaphore):
async def _async_update_azur_extra_info(key: str, semaphore):
if key[-1] == '改':
return {key: {'获取途径': ['无法建造']}}
async with semaphore:
for i in range(20):
try:
async with session.get(f'https://wiki.biligame.com/blhx/{key}', timeout=7) as res:
soup = BeautifulSoup(await res.text(), 'lxml')
try:
construction_time = str(soup.find('table', {'class': 'wikitable sv-general'}).find('tbody'))
x = {key: {'获取途径': []}}
if construction_time.find('无法建造') != -1:
x[key]['获取途径'].append('无法建造')
elif construction_time.find('活动已关闭') != -1:
x[key]['获取途径'].append('活动限定')
else:
x[key]['获取途径'].append('可以建造')
logger.info(f'碧蓝航线获取额外信息 {key}...{x[key]["获取途径"]}')
except AttributeError:
x = {key: {'获取途径': []}}
logger.warning(f'碧蓝航线获取额外信息错误 {key}...{[]}')
return x
text = (await AsyncHttpx.get(f'https://wiki.biligame.com/blhx/{key}')).text
soup = BeautifulSoup(text, 'lxml')
try:
construction_time = str(soup.find('table', {'class': 'wikitable sv-general'}).find('tbody'))
x = {key: {'获取途径': []}}
if construction_time.find('无法建造') != -1:
x[key]['获取途径'].append('无法建造')
elif construction_time.find('活动已关闭') != -1:
x[key]['获取途径'].append('活动限定')
else:
x[key]['获取途径'].append('可以建造')
logger.info(f'碧蓝航线获取额外信息 {key}...{x[key]["获取途径"]}')
except AttributeError:
x = {key: {'获取途径': []}}
logger.warning(f'碧蓝航线获取额外信息错误 {key}...{[]}')
return x
except TimeoutError:
logger.warning(f'访问 https://wiki.biligame.com/blhx/{key} 第 {i}次 超时...已再次访问')
return {}
Regular → Executable
+7 -20
View File
@@ -1,13 +1,9 @@
import aiohttp
import aiofiles
from asyncio.exceptions import TimeoutError
from aiohttp.client_exceptions import InvalidURL
from nonebot.adapters.cqhttp import MessageSegment
from typing import List, Union, Set
from pathlib import Path
from .config import path_dict
from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx
import nonebot
import pypinyin
from utils.image_utils import CreateImg
@@ -54,21 +50,12 @@ async def download_img(url: str, path: str, name: str) -> bool:
if not file.exists():
file.parent.mkdir(exist_ok=True, parents=True)
try:
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(url, timeout=7) as response:
async with aiofiles.open(IMAGE_PATH + f'/draw_card/{path}/{codename}.png', 'wb') as f:
await f.write(await response.read())
logger.info(f'下载 {path_dict[path]} 图片成功,名称:{name},url:{url}')
return True
except TimeoutError:
logger.warning(f'下载 {path_dict[path]} 图片超时,名称:{name},url:{url}')
return False
except InvalidURL:
logger.warning(f'下载 {path_dict[path]} 链接错误,名称:{name},url:{url}')
return False
else:
# logger.info(f'{path_dict[path]} 图片 {name} 已存在')
return False
if await AsyncHttpx.download_file(url, IMAGE_PATH + f'/draw_card/{path}/{codename}.png'):
logger.info(f'下载 {path_dict[path]} 图片成功,名称:{name},url:{url}')
return True
except Exception as e:
logger.warning(f'下载 {path_dict[path]} 链接错误 {type(e)}:{e},名称:{name},url:{url}')
return False
@driver.on_startup
Regular → Executable
+1 -1
View File
@@ -15,7 +15,7 @@ usage:
""".strip()
__plugin_des__ = "可以不玩,不能没有,每日白嫖"
__plugin_cmd__ = ["epic"]
__plugin_version__ = 0.2
__plugin_version__ = 0.1
__plugin_author__ = "AkashiCoin"
__plugin_settings__ = {
"level": 5,
Regular → Executable
+77 -48
View File
@@ -32,11 +32,11 @@ async def get_epic_game():
"withPromotions": True,
},
}
async with AsyncClient(proxies={"all://": None}) as client:
async with AsyncClient(headers=headers) as client:
try:
res = await client.post(epic_url, headers=headers, json=data, timeout=10.0)
resJson = res.json()
games = resJson["data"]["Catalog"]["searchStore"]["elements"]
res = await client.post(epic_url, json=data, timeout=10.0)
res_json = res.json()
games = res_json["data"]["Catalog"]["searchStore"]["elements"]
return games
except Exception as e:
logger.error(str(e))
@@ -53,60 +53,89 @@ async def get_epic_free(bot: Bot, event: Event):
else:
msg_list = []
for game in games:
game_name = game["title"]
game_corp = game["seller"]["name"]
game_price = game["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
# 赋初值以避免 local variable referenced before assignment
game_dev, game_pub, game_thumbnail = (None, None, None)
try:
msg = ""
game_name = game["title"]
game_corp = game["seller"]["name"]
game_price = game["price"]["totalPrice"]["fmtPrice"]["originalPrice"]
game_promotions = game["promotions"]["promotionalOffers"]
upcoming_promotions = game["promotions"]["upcomingPromotionalOffers"]
if not game_promotions and upcoming_promotions:
continue
else:
for image in game["keyImages"]:
game_thumbnail = (
image["url"] if image["type"] == "Thumbnail" else None
)
for pair in game["customAttributes"]:
game_dev = (
pair["value"]
if pair["key"] == "developerName"
else game_corp
)
game_pub = (
pair["value"]
if pair["key"] == "publisherName"
else game_corp
)
game_desp = game["description"]
end_date = ""
if len(game["promotions"]["promotionalOffers"]) != 0:
end_date_iso = game["promotions"]["promotionalOffers"][0][
"promotionalOffers"
][0]["endDate"][:-1]
end_date = datetime.fromisoformat(end_date_iso).strftime(
"%b.%d %H:%M"
)
# API 返回不包含游戏商店 URL,此处自行拼接,可能出现少数游戏 404 请反馈
game_url = f"https://www.epicgames.com/store/zh-CN/p/{game['productSlug'].replace('/home', '')}"
msg = (
f"[CQ:image,file={game_thumbnail}]\n\n"
if game_thumbnail
else ""
# 促销暂未上线,但即将上线
promotion_data = upcoming_promotions[0]["promotionalOffers"][0]
start_date_iso, end_date_iso = (
promotion_data["startDate"][:-1],
promotion_data["endDate"][:-1],
)
msg += f"FREE now :: {game_name} ({game_price})\n\n{game_desp}\n\n"
msg += (
f"游戏由 {game_pub} 发售,"
if game_dev == game_pub
else f"游戏由 {game_dev} 开发、{game_pub} 出版,"
# 删除字符串中最后一个 "Z" 使 Python datetime 可处理此时间
start_date = datetime.fromisoformat(start_date_iso).strftime(
"%b.%d %H:%M"
)
end_date = datetime.fromisoformat(end_date_iso).strftime(
"%b.%d %H:%M"
)
msg += f"将在 UTC 时间 {end_date} 结束免费游玩,戳链接领取吧~\n{game_url}"
_message = msg
if isinstance(event, GroupMessageEvent):
_message = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format(
game_corp, game_name, game_price, start_date, end_date
)
data = {
"type": "node",
"data": {
"name": f"{NICKNAME}",
"name": f"这里是{NICKNAME}酱",
"uin": f"{bot.self_id}",
"content": _message,
},
}
msg_list.append(data)
else:
msg = "\n由 {} 公司发行的游戏 {} ({}) 在 UTC 时间 {} 即将推出免费游玩,预计截至 {}。".format(
game_corp, game_name, game_price, start_date, end_date
)
msg_list.append(msg)
else:
for image in game["keyImages"]:
if image["type"] == "Thumbnail":
game_thumbnail = image["url"]
for pair in game["customAttributes"]:
if pair["key"] == "developerName":
game_dev = pair["value"]
if pair["key"] == "publisherName":
game_pub = pair["value"]
# 如 game['customAttributes'] 未找到则均使用 game_corp 值
game_dev = game_dev if game_dev is not None else game_corp
game_pub = game_pub if game_pub is not None else game_corp
game_desp = game["description"]
end_date_iso = game["promotions"]["promotionalOffers"][0][
"promotionalOffers"
][0]["endDate"][:-1]
end_date = datetime.fromisoformat(end_date_iso).strftime(
"%b.%d %H:%M"
)
# API 返回不包含游戏商店 URL,此处自行拼接,可能出现少数游戏 404 请反馈
game_url_part = (
(game["productSlug"].replace("/home", ""))
if ("/home" in game["productSlug"])
else game["productSlug"]
)
game_url = "https://www.epicgames.com/store/zh-CN/p/{}".format(
game_url_part
)
if isinstance(event, GroupMessageEvent):
_message = "[CQ:image,file={}]\n\nFREE now :: {} ({})\n{}\n此游戏由 {} 开发、{} 发行,将在 UTC 时间 {} 结束免费游玩,戳链接速度加入你的游戏库吧~\n{}\n".format(
game_thumbnail,
game_name,
game_price,
game_desp,
game_dev,
game_pub,
end_date,
game_url,
)
data = {
"type": "node",
"data": {
"name": f"这里是{NICKNAME}酱",
"uin": f"{bot.self_id}",
"content": _message,
},
Regular → Executable
View File
Regular → Executable
+9 -11
View File
@@ -4,12 +4,11 @@ from utils.image_utils import get_img_hash
import random
from utils.message_builder import image
from nonebot import on_message
from utils.utils import get_message_text, get_message_imgs, get_local_proxy
from utils.utils import get_message_text, get_message_imgs
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
import aiohttp
import aiofiles
from configs.config import Config
from utils.http_utils import AsyncHttpx
from utils.manager import group_manager
from services.log import logger
@@ -121,14 +120,13 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
async def get_fudu_img_hash(url, group_id):
try:
async with aiohttp.ClientSession() as session:
async with session.get(url, proxy=get_local_proxy(), timeout=5) as response:
async with aiofiles.open(
IMAGE_PATH + f"temp/compare_{group_id}_img.jpg", "wb"
) as f:
await f.write(await response.read())
img_hash = get_img_hash(IMAGE_PATH + f"temp/compare_{group_id}_img.jpg")
return str(img_hash)
if await AsyncHttpx.download_file(
url, IMAGE_PATH + f"temp/compare_{group_id}_img.jpg"
):
img_hash = get_img_hash(IMAGE_PATH + f"temp/compare_{group_id}_img.jpg")
return str(img_hash)
else:
logger.warning(f"复读下载图片失败...")
except Exception as e:
logger.warning(f"复读读取图片Hash出错 {type(e)}:{e}")
return ""
Regular → Executable
+1
View File
@@ -1,3 +1,4 @@
import nonebot
nonebot.load_plugins("plugins/genshin")
+14 -10
View File
@@ -38,12 +38,15 @@ ALC_PATH.mkdir(parents=True, exist_ok=True)
@almanac.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
alc_img = await get_alc_image(ALC_PATH)
mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub"
await almanac.send(mes)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送查看原神黄历"
)
if alc_img:
mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub"
await almanac.send(mes)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送查看原神黄历"
)
else:
await almanac.send("黄历图片下载失败...")
@scheduler.scheduled_job(
@@ -58,7 +61,8 @@ async def _():
gl = await bot.get_group_list()
gl = [g["group_id"] for g in gl]
alc_img = await get_alc_image(ALC_PATH)
mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub"
for gid in gl:
if await group_manager.check_group_task_status(gid, "genshin_alc"):
await bot.send_group_msg(group_id=int(gid), message=mes)
if alc_img:
mes = alc_img + "\n ※ 黄历数据来源于 genshin.pub"
for gid in gl:
if await group_manager.check_group_task_status(gid, "genshin_alc"):
await bot.send_group_msg(group_id=int(gid), message=mes)
+26 -43
View File
@@ -1,43 +1,26 @@
from utils.browser import get_browser
from utils.message_builder import image
from datetime import datetime
from services.log import logger
from pathlib import Path
import os
url = "https://genshin.pub"
async def get_alc_image(path: Path):
date = datetime.now().date()
for file in os.listdir(path):
if f'{date}.png' != file:
file = path / file
file.unlink()
if f'{date}.png' in os.listdir(path):
return image(f'{date}.png', 'genshin/alc')
page = None
try:
browser = await get_browser()
page = await browser.new_page()
await page.goto(url, wait_until="networkidle", timeout=10000)
await page.set_viewport_size({"width": 2560, "height": 1080})
card = await page.query_selector('.GSAlmanacs_gs_almanacs__3qT_A')
await card.screenshot(path=path / f'{date}.png', timeout=100000)
except Exception as e:
logger.error(f'获取原神黄历发生错误..{type(e)}: {e}')
finally:
if page:
await page.close()
return image(f'{date}.png', 'genshin/alc')
from utils.message_builder import image
from datetime import datetime
from pathlib import Path
from utils.http_utils import AsyncPlaywright
from nonebot.adapters.cqhttp import MessageSegment
from typing import Optional
import os
url = "https://genshin.pub"
async def get_alc_image(path: Path) -> Optional[MessageSegment]:
"""
截取黄历
:param path: 存储路径
"""
date = datetime.now().date()
for file in os.listdir(path):
if f"{date}.png" != file:
file = path / file
file.unlink()
if f"{date}.png" in os.listdir(path):
return image(f"{date}.png", "genshin/alc")
return await AsyncPlaywright.screenshot(
url, path / f"{date}.png", ".GSAlmanacs_gs_almanacs__3qT_A"
)
+2 -2
View File
@@ -1,6 +1,6 @@
from nonebot import on_command, Driver
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from utils.message_builder import image
from utils.image_utils import CreateImg
from utils.browser import get_browser
@@ -59,7 +59,7 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
)
)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})"
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送查看今日素材"
)
@@ -1,46 +0,0 @@
from .qiu_translation import qiu_qiu_word_translation, qiu_qiu_phrase_translation
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
from nonebot import on_command
from utils.utils import get_message_text
from services.log import logger
__zx_plugin_name__ = "丘丘语翻译"
__plugin_usage__ = """
usage:
异世界旅游小助手,仅支持丘丘语翻译至中文
指令:
丘丘语翻译/丘丘一下 [文本]
""".strip()
__plugin_des__ = "其实我听得懂丘丘人讲话"
__plugin_cmd__ = ["丘丘语翻译/丘丘一下 [文本]"]
__plugin_type__ = ("原神相关",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["丘丘语翻译", "丘丘一下"],
}
qiuqiu = on_command("丘丘语翻译", aliases={"丘丘一下", "丘丘翻译"}, priority=5, block=True)
suffix = "\n※ 只能从丘丘语翻译为中文,不能反向翻译\n" "※ 注意空格,不要加入任何标点符号\n" "※ 翻译数据来源于 米游社论坛"
@qiuqiu.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
txt = get_message_text(event.json()).lower()
if txt == "":
return
mes = qiu_qiu_phrase_translation(txt)
if not mes:
mes = qiu_qiu_word_translation(txt)
mes += suffix
# print(mes)
await qiuqiu.send(mes, at_sender=True)
logger.info(
f"(USER {event.user_id}, GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送丘丘翻译:" + txt
)
@@ -1,127 +0,0 @@
{
"word": {
"a": "啊",
"beru": "做",
"biadam": "找死啊",
"biat": "暴揍",
"buka": "肚子",
"celi": "元素",
"dada": "厉害",
"dala": "什么",
"domu": "跳舞",
"eleka": "此时此刻",
"guru": "咕",
"gusha": "草",
"ika": "敌人",
"kundela": "活着",
"kuzi": "强大",
"kucha": "吃草",
"la": "啦~",
"lata": "水元素",
"mani": "给予",
"mi": "我",
"mimi": "我们",
"mita": "肉类",
"mosi": "吃",
"movo": "带来",
"muhe": "胜利",
"ni": "风",
"nini": "风暴",
"nunu": "睡觉",
"nye": "不",
"odomu": "朋友",
"olah": "你好",
"plata": "盾牌",
"pupu": "噗噗",
"sada": "唱歌",
"shato": "伴随",
"si": "什么",
"tomo": "感激",
"todo": "放心",
"tiga": "矿石",
"upa": "聚集",
"unu": "乌努",
"valo": "谢谢你",
"vin": "酒",
"ya": "人",
"yaya": "人们",
"ye": "家伙",
"yeye": "这些家伙",
"yo": "你",
"yoyo": "你们",
"zido": "杀死"
},
"phrase": {
"beru si": "做什么",
"biadam": "可恶啊,找死啊,愤怒语气",
"biat": "打爆,暴揍;可恶的",
"dada": " 极好的,厉害的",
"dala": "表示疑问,不明白的集合,翻译为哪个,什么",
"guru-guru": "拟声词,形容肚子咕咕叫的声音,饥饿",
"guru guru": "拟声词,形容肚子咕咕叫的声音,饥饿",
"gusha": "植物类食物,不怎么好的,难过的",
"kucha pupu": "拟声词,吃草时候的拟声词,咯吱噗噗,形容吃草吃得香,贬义",
"mita": "肉类,好的,好东西,有时代丘丘人,丘丘人部落,好肉族",
"mosi mita": "吃肉,表示开心",
"mosi gusha": "吃草,表示难过",
"movo": "带来,搬运;可引申为收获,丰收",
"muhe": "胜利,战胜;成功的",
"nini": "大量风元素,风暴,狂风",
"nye": "表示否定,没有了,不是",
"pupu":"拟声词,噗噗,表示嘲讽",
"sada": "唱歌,歌颂",
"shato": "乘着,伴随着",
"upa": "凝聚,聚集,集合;聚落,部落",
"unu": "乌努,丘丘人所崇拜的神灵",
"valo": "谢谢你,不客气/再见",
"vin": "酒的,喝酒有关的,酒桶盖子",
"ya": "特指人类,一个人类",
"yaya": "人类的复数 ,大群人类",
"ye": "你这个家伙,蔑称;或用于地位高者对地位低者的称呼",
"yeye": "你们这些家伙,蔑称,ye的复数",
"yo": "你,友善称呼",
"yoyo": "你们,yo的复数,友善称呼ye",
"nye mita da ye mosi zido": "再见,一路平安",
"vin plata dada": "你的酒桶盾牌真不错",
"kucha pupu gucha ye": "你咯吱噗噗的吃草的样子真的好搞笑",
"mani nini biaodomu": "愿风暴给予你死亡",
"celi dada mimi nunu": "赞美元素,我们睡觉吧",
"muhe ye": "你们是不可战胜的",
"ye dada": "你们可真棒",
"ye yika": "你们是敌人",
"nini zido": "愿风暴杀死你"
}
}
@@ -1,73 +0,0 @@
import json
import os
FILE_PATH = os.path.dirname(__file__)
QIU_QIU_WORD = {}
QIU_QIU_PHRASE = {}
with open(
os.path.join(FILE_PATH, "qiu_qiu_dictionary.json"), "r", encoding="UTF-8"
) as f:
data = json.load(f)
QIU_QIU_WORD = data["word"]
QIU_QIU_PHRASE = data["phrase"]
def compare_words(word):
# 比对word库是否有匹配的单词,有的话返回翻译,没有返回原词
if word in QIU_QIU_WORD:
return QIU_QIU_WORD[word]
return word
def compare_phrase(phrase):
# 比对phrase库是否有匹配的单词,有的话返回翻译,没有的话匹配word库,都没有返回原词
if phrase in QIU_QIU_PHRASE:
return QIU_QIU_PHRASE[phrase]
if phrase in QIU_QIU_WORD:
return QIU_QIU_WORD[phrase]
return phrase
def qiu_qiu_word_translation(txt: str):
# 对语句按空格分隔替换单词翻译
txt_list = txt.split(" ")
mes = "你查询的的丘丘语意思为:\n"
for word in txt_list:
tra_word = compare_words(word)
if tra_word == word:
# 如果是原词表示没有翻译,前后加空格接回语句里
if not mes[-1] == " ":
mes += " "
mes += tra_word
mes += " "
else:
mes += tra_word
mes += "\n"
return mes
def qiu_qiu_phrase_translation(phrase):
# 语句翻译,先看phrase库是不是有匹配的语句
# 没有的话把单词拆开返回单词的意思
tra_phrase = compare_phrase(phrase)
if tra_phrase != phrase:
return f"\n翻译丘丘语意思为:\n【{tra_phrase}】\n"
txt_list = phrase.split(" ")
mes = "没有查到这句丘丘语,以下是单词的翻译\n"
for word in txt_list:
if word == " ":
continue
tra_word = compare_phrase(word)
if tra_word == word:
mes += f"{word} : 没有这个词的翻译\n"
else:
mes += f"{word} : {tra_word}\n"
return mes
View File
+266 -266
View File
@@ -1,266 +1,266 @@
from pathlib import Path
from configs.path_config import IMAGE_PATH, TEXT_PATH
from utils.image_utils import CreateImg
from typing import Tuple, List
from math import sqrt, pow
import random
try:
import ujson as json
except ModuleNotFoundError:
import json
icon_path = Path(IMAGE_PATH) / "genshin" / "genshin_icon"
map_path = Path(IMAGE_PATH) / "genshin" / "map" / "map.png"
resource_label_file = Path(TEXT_PATH) / "genshin" / "resource_label_file.json"
resource_point_file = Path(TEXT_PATH) / "genshin" / "resource_point_file.json"
class Map:
"""
原神资源生成类
"""
def __init__(
self,
resource_name: str,
center_point: Tuple[int, int],
deviation: Tuple[int, int] = (25, 51),
padding: int = 100,
planning_route: bool = False,
ratio: float = 1,
):
"""
参数:
:param resource_name: 资源名称
:param center_point: 中心点
:param deviation: 坐标误差
:param padding: 截图外边距
:param planning_route: 是否规划最佳线路
:param ratio: 压缩比率
"""
self.map = CreateImg(0, 0, background=map_path)
self.resource_name = resource_name
self.center_x = center_point[0]
self.center_y = center_point[1]
self.deviation = deviation
self.padding = int(padding * ratio)
self.planning_route = planning_route
self.ratio = ratio
self.deviation = (
int(self.deviation[0] * ratio),
int(self.deviation[1] * ratio),
)
data = json.load(open(resource_label_file, "r", encoding="utf8"))
# 资源 id
self.resource_id = [
data[x]["id"]
for x in data
if x != "CENTER_POINT" and data[x]["name"] == resource_name
][0]
# 传送锚点 id
self.teleport_anchor_id = [
data[x]["id"]
for x in data
if x != "CENTER_POINT" and data[x]["name"] == "传送锚点"
][0]
# 神像 id
self.teleport_god_id = [
data[x]["id"]
for x in data
if x != "CENTER_POINT" and data[x]["name"] == "七天神像"
][0]
# 资源坐标
data = json.load(open(resource_point_file, "r", encoding="utf8"))
self.resource_point = [
Resources(
int((self.center_x + data[x]["x_pos"]) * ratio),
int((self.center_y + data[x]["y_pos"]) * ratio),
)
for x in data
if x != "CENTER_POINT" and data[x]["label_id"] == self.resource_id
]
# 传送锚点坐标
self.teleport_anchor_point = [
Resources(
int((self.center_x + data[x]["x_pos"]) * ratio),
int((self.center_y + data[x]["y_pos"]) * ratio),
)
for x in data
if x != "CENTER_POINT" and data[x]["label_id"] == self.teleport_anchor_id
]
# 神像坐标
self.teleport_god_point = [
Resources(
int((self.center_x + data[x]["x_pos"]) * ratio),
int((self.center_y + data[x]["y_pos"]) * ratio),
)
for x in data
if x != "CENTER_POINT" and data[x]["label_id"] == self.teleport_god_id
]
# 将地图上生成资源图标
def generate_resource_icon_in_map(self) -> int:
x_list = [x.x for x in self.resource_point]
y_list = [x.y for x in self.resource_point]
min_width = min(x_list) - self.padding
max_width = max(x_list) + self.padding
min_height = min(y_list) - self.padding
max_height = max(y_list) + self.padding
self._generate_transfer_icon((min_width, min_height, max_width, max_height))
for res in self.resource_point:
icon = self._get_icon_image(self.resource_id)
self.map.paste(
icon, (res.x - self.deviation[0], res.y - self.deviation[1]), True
)
if self.planning_route:
self._generate_best_route()
self.map.crop((min_width, min_height, max_width, max_height))
rand = random.randint(1, 10000)
self.map.save(f"{IMAGE_PATH}/temp/genshin_map_{rand}.png")
return rand
# 资源数量
def get_resource_count(self) -> int:
return len(self.resource_point)
# 生成传送锚点和神像
def _generate_transfer_icon(self, box: Tuple[int, int, int, int]):
min_width, min_height, max_width, max_height = box
for resources in [self.teleport_anchor_point, self.teleport_god_point]:
id_ = (
self.teleport_anchor_id
if resources == self.teleport_anchor_point
else self.teleport_god_id
)
for res in resources:
if min_width < res.x < max_width and min_height < res.y < max_height:
icon = self._get_icon_image(id_)
self.map.paste(
icon,
(res.x - self.deviation[0], res.y - self.deviation[1]),
True,
)
# 生成最优路线(说是最优其实就是直线最短)
def _generate_best_route(self):
line_points = []
teleport_list = self.teleport_anchor_point + self.teleport_god_point
for teleport in teleport_list:
current_res, res_min_distance = teleport.get_resource_distance(self.resource_point)
current_teleport, teleport_min_distance = current_res.get_resource_distance(teleport_list)
if current_teleport == teleport:
self.map.line(
(current_teleport.x, current_teleport.y, current_res.x, current_res.y), (255, 0, 0), width=1
)
is_used_res_points = []
for res in self.resource_point:
if res in is_used_res_points:
continue
current_teleport, teleport_min_distance = res.get_resource_distance(teleport_list)
current_res, res_min_distance = res.get_resource_distance(self.resource_point)
if teleport_min_distance < res_min_distance:
self.map.line(
(current_teleport.x, current_teleport.y, res.x, res.y), (255, 0, 0), width=1
)
else:
is_used_res_points.append(current_res)
self.map.line(
(current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1
)
res_cp = self.resource_point[:]
res_cp.remove(current_res)
# for _ in res_cp:
current_teleport_, teleport_min_distance = res.get_resource_distance(teleport_list)
current_res, res_min_distance = res.get_resource_distance(res_cp)
if teleport_min_distance < res_min_distance:
self.map.line(
(current_teleport.x, current_teleport.y, res.x, res.y), (255, 0, 0), width=1
)
else:
self.map.line(
(current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1
)
is_used_res_points.append(current_res)
is_used_res_points.append(res)
# resources_route = []
# # 先连上最近的资源路径
# for res in self.resource_point:
# # 拿到最近的资源
# current_res, _ = res.get_resource_distance(
# self.resource_point
# + self.teleport_anchor_point
# + self.teleport_god_point
# )
# self.map.line(
# (current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1
# )
# resources_route.append((current_res, res))
# teleport_list = self.teleport_anchor_point + self.teleport_god_point
# for res1, res2 in resources_route:
# point_list = [x for x in resources_route if res1 in x or res2 in x]
# if not list(set(point_list).intersection(set(teleport_list))):
# if res1 not in teleport_list and res2 not in teleport_list:
# # while True:
# # tmp = [x for x in point_list]
# # break
# teleport1, distance1 = res1.get_resource_distance(teleport_list)
# teleport2, distance2 = res2.get_resource_distance(teleport_list)
# if distance1 > distance2:
# self.map.line(
# (teleport1.x, teleport1.y, res1.x, res1.y),
# (255, 0, 0),
# width=1,
# )
# else:
# self.map.line(
# (teleport2.x, teleport2.y, res2.x, res2.y),
# (255, 0, 0),
# width=1,
# )
# self.map.line(xy, (255, 0, 0), width=3)
# 获取资源图标
def _get_icon_image(self, id_: int) -> "CreateImg":
icon = icon_path / f"{id_}.png"
if icon.exists():
return CreateImg(
int(50 * self.ratio), int(50 * self.ratio), background=icon
)
return CreateImg(
int(50 * self.ratio),
int(50 * self.ratio),
background=f"{icon_path}/box.png",
)
# def _get_shortest_path(self, res: 'Resources', res_2: 'Resources'):
# 资源类
class Resources:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def get_distance(self, x: int, y: int):
return int(sqrt(pow(abs(self.x - x), 2) + pow(abs(self.y - y), 2)))
# 拿到资源在该列表中的最短路径
def get_resource_distance(self, resources: List["Resources"]) -> "Resources, int":
current_res = None
min_distance = 999999
for res in resources:
distance = self.get_distance(res.x, res.y)
if distance < min_distance and res != self:
current_res = res
min_distance = distance
return current_res, min_distance
from pathlib import Path
from configs.path_config import IMAGE_PATH, TEXT_PATH
from utils.image_utils import CreateImg
from typing import Tuple, List
from math import sqrt, pow
import random
try:
import ujson as json
except ModuleNotFoundError:
import json
icon_path = Path(IMAGE_PATH) / "genshin" / "genshin_icon"
map_path = Path(IMAGE_PATH) / "genshin" / "map" / "map.png"
resource_label_file = Path(TEXT_PATH) / "genshin" / "resource_label_file.json"
resource_point_file = Path(TEXT_PATH) / "genshin" / "resource_point_file.json"
class Map:
"""
原神资源生成类
"""
def __init__(
self,
resource_name: str,
center_point: Tuple[int, int],
deviation: Tuple[int, int] = (25, 51),
padding: int = 100,
planning_route: bool = False,
ratio: float = 1,
):
"""
参数:
:param resource_name: 资源名称
:param center_point: 中心点
:param deviation: 坐标误差
:param padding: 截图外边距
:param planning_route: 是否规划最佳线路
:param ratio: 压缩比率
"""
self.map = CreateImg(0, 0, background=map_path)
self.resource_name = resource_name
self.center_x = center_point[0]
self.center_y = center_point[1]
self.deviation = deviation
self.padding = int(padding * ratio)
self.planning_route = planning_route
self.ratio = ratio
self.deviation = (
int(self.deviation[0] * ratio),
int(self.deviation[1] * ratio),
)
data = json.load(open(resource_label_file, "r", encoding="utf8"))
# 资源 id
self.resource_id = [
data[x]["id"]
for x in data
if x != "CENTER_POINT" and data[x]["name"] == resource_name
][0]
# 传送锚点 id
self.teleport_anchor_id = [
data[x]["id"]
for x in data
if x != "CENTER_POINT" and data[x]["name"] == "传送锚点"
][0]
# 神像 id
self.teleport_god_id = [
data[x]["id"]
for x in data
if x != "CENTER_POINT" and data[x]["name"] == "七天神像"
][0]
# 资源坐标
data = json.load(open(resource_point_file, "r", encoding="utf8"))
self.resource_point = [
Resources(
int((self.center_x + data[x]["x_pos"]) * ratio),
int((self.center_y + data[x]["y_pos"]) * ratio),
)
for x in data
if x != "CENTER_POINT" and data[x]["label_id"] == self.resource_id
]
# 传送锚点坐标
self.teleport_anchor_point = [
Resources(
int((self.center_x + data[x]["x_pos"]) * ratio),
int((self.center_y + data[x]["y_pos"]) * ratio),
)
for x in data
if x != "CENTER_POINT" and data[x]["label_id"] == self.teleport_anchor_id
]
# 神像坐标
self.teleport_god_point = [
Resources(
int((self.center_x + data[x]["x_pos"]) * ratio),
int((self.center_y + data[x]["y_pos"]) * ratio),
)
for x in data
if x != "CENTER_POINT" and data[x]["label_id"] == self.teleport_god_id
]
# 将地图上生成资源图标
def generate_resource_icon_in_map(self) -> int:
x_list = [x.x for x in self.resource_point]
y_list = [x.y for x in self.resource_point]
min_width = min(x_list) - self.padding
max_width = max(x_list) + self.padding
min_height = min(y_list) - self.padding
max_height = max(y_list) + self.padding
self._generate_transfer_icon((min_width, min_height, max_width, max_height))
for res in self.resource_point:
icon = self._get_icon_image(self.resource_id)
self.map.paste(
icon, (res.x - self.deviation[0], res.y - self.deviation[1]), True
)
if self.planning_route:
self._generate_best_route()
self.map.crop((min_width, min_height, max_width, max_height))
rand = random.randint(1, 10000)
self.map.save(f"{IMAGE_PATH}/temp/genshin_map_{rand}.png")
return rand
# 资源数量
def get_resource_count(self) -> int:
return len(self.resource_point)
# 生成传送锚点和神像
def _generate_transfer_icon(self, box: Tuple[int, int, int, int]):
min_width, min_height, max_width, max_height = box
for resources in [self.teleport_anchor_point, self.teleport_god_point]:
id_ = (
self.teleport_anchor_id
if resources == self.teleport_anchor_point
else self.teleport_god_id
)
for res in resources:
if min_width < res.x < max_width and min_height < res.y < max_height:
icon = self._get_icon_image(id_)
self.map.paste(
icon,
(res.x - self.deviation[0], res.y - self.deviation[1]),
True,
)
# 生成最优路线(说是最优其实就是直线最短)
def _generate_best_route(self):
line_points = []
teleport_list = self.teleport_anchor_point + self.teleport_god_point
for teleport in teleport_list:
current_res, res_min_distance = teleport.get_resource_distance(self.resource_point)
current_teleport, teleport_min_distance = current_res.get_resource_distance(teleport_list)
if current_teleport == teleport:
self.map.line(
(current_teleport.x, current_teleport.y, current_res.x, current_res.y), (255, 0, 0), width=1
)
is_used_res_points = []
for res in self.resource_point:
if res in is_used_res_points:
continue
current_teleport, teleport_min_distance = res.get_resource_distance(teleport_list)
current_res, res_min_distance = res.get_resource_distance(self.resource_point)
if teleport_min_distance < res_min_distance:
self.map.line(
(current_teleport.x, current_teleport.y, res.x, res.y), (255, 0, 0), width=1
)
else:
is_used_res_points.append(current_res)
self.map.line(
(current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1
)
res_cp = self.resource_point[:]
res_cp.remove(current_res)
# for _ in res_cp:
current_teleport_, teleport_min_distance = res.get_resource_distance(teleport_list)
current_res, res_min_distance = res.get_resource_distance(res_cp)
if teleport_min_distance < res_min_distance:
self.map.line(
(current_teleport.x, current_teleport.y, res.x, res.y), (255, 0, 0), width=1
)
else:
self.map.line(
(current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1
)
is_used_res_points.append(current_res)
is_used_res_points.append(res)
# resources_route = []
# # 先连上最近的资源路径
# for res in self.resource_point:
# # 拿到最近的资源
# current_res, _ = res.get_resource_distance(
# self.resource_point
# + self.teleport_anchor_point
# + self.teleport_god_point
# )
# self.map.line(
# (current_res.x, current_res.y, res.x, res.y), (255, 0, 0), width=1
# )
# resources_route.append((current_res, res))
# teleport_list = self.teleport_anchor_point + self.teleport_god_point
# for res1, res2 in resources_route:
# point_list = [x for x in resources_route if res1 in x or res2 in x]
# if not list(set(point_list).intersection(set(teleport_list))):
# if res1 not in teleport_list and res2 not in teleport_list:
# # while True:
# # tmp = [x for x in point_list]
# # break
# teleport1, distance1 = res1.get_resource_distance(teleport_list)
# teleport2, distance2 = res2.get_resource_distance(teleport_list)
# if distance1 > distance2:
# self.map.line(
# (teleport1.x, teleport1.y, res1.x, res1.y),
# (255, 0, 0),
# width=1,
# )
# else:
# self.map.line(
# (teleport2.x, teleport2.y, res2.x, res2.y),
# (255, 0, 0),
# width=1,
# )
# self.map.line(xy, (255, 0, 0), width=3)
# 获取资源图标
def _get_icon_image(self, id_: int) -> "CreateImg":
icon = icon_path / f"{id_}.png"
if icon.exists():
return CreateImg(
int(50 * self.ratio), int(50 * self.ratio), background=icon
)
return CreateImg(
int(50 * self.ratio),
int(50 * self.ratio),
background=f"{icon_path}/box.png",
)
# def _get_shortest_path(self, res: 'Resources', res_2: 'Resources'):
# 资源类
class Resources:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def get_distance(self, x: int, y: int):
return int(sqrt(pow(abs(self.x - x), 2) + pow(abs(self.y - y), 2)))
# 拿到资源在该列表中的最短路径
def get_resource_distance(self, resources: List["Resources"]) -> "Resources, int":
current_res = None
min_distance = 999999
for res in resources:
distance = self.get_distance(res.x, res.y)
if distance < min_distance and res != self:
current_res = res
min_distance = distance
return current_res, min_distance
+100 -111
View File
@@ -3,18 +3,15 @@ from configs.path_config import IMAGE_PATH, TEXT_PATH
from PIL.Image import UnidentifiedImageError
from utils.message_builder import image
from services.log import logger
from .map import Map
from utils.image_utils import CreateImg
import asyncio
from pathlib import Path
from asyncio.exceptions import TimeoutError
from asyncio import Semaphore
from aiohttp.client import ClientSession
from utils.user_agent import get_user_agent
from utils.image_utils import is_valid
from utils.http_utils import AsyncHttpx
from pathlib import Path
from .map import Map
import asyncio
import nonebot
import aiohttp
import aiofiles
import os
try:
@@ -96,130 +93,126 @@ async def init(flag: bool = False):
global CENTER_POINT, resource_name_list
try:
semaphore = asyncio.Semaphore(10)
async with aiohttp.ClientSession(headers=get_user_agent()) as session:
await download_map_init(session, semaphore, MAP_RATIO, flag)
await download_resource_data(session, semaphore)
await download_resource_type(session)
if not CENTER_POINT:
CENTER_POINT = json.load(open(resource_label_file, "r", encoding="utf8"))[
"CENTER_POINT"
]
with open(resource_type_file, "r", encoding="utf8") as f:
data = json.load(f)
for id_ in data:
for x in data[id_]["children"]:
resource_name_list.append(x["name"])
await download_map_init(semaphore, flag)
await download_resource_data(semaphore)
await download_resource_type()
if not CENTER_POINT:
CENTER_POINT = json.load(open(resource_label_file, "r", encoding="utf8"))[
"CENTER_POINT"
]
with open(resource_type_file, "r", encoding="utf8") as f:
data = json.load(f)
for id_ in data:
for x in data[id_]["children"]:
resource_name_list.append(x["name"])
except TimeoutError:
logger.warning('原神资源查询信息初始化超时....')
pass
# 图标及位置资源
async def download_resource_data(session: ClientSession, semaphore: Semaphore):
async def download_resource_data(semaphore: Semaphore):
icon_path.mkdir(parents=True, exist_ok=True)
resource_label_file.parent.mkdir(parents=True, exist_ok=True)
try:
async with session.get(POINT_LIST_URL, timeout=5) as response:
if response.status == 200:
data = await response.json()
if data["message"] == "OK":
data = data["data"]
for lst in ["label_list", "point_list"]:
resource_data = {"CENTER_POINT": CENTER_POINT}
tasks = []
file = (
resource_label_file
if lst == "label_list"
else resource_point_file
)
for x in data[lst]:
id_ = x["id"]
if lst == "label_list":
img_url = x["icon"]
tasks.append(
asyncio.ensure_future(
download_image(
img_url,
f"{icon_path}/{id_}.png",
session,
semaphore,
True,
)
response = await AsyncHttpx.get(POINT_LIST_URL)
if response.status_code == 200:
data = response.json()
if data["message"] == "OK":
data = data["data"]
for lst in ["label_list", "point_list"]:
resource_data = {"CENTER_POINT": CENTER_POINT}
tasks = []
file = (
resource_label_file
if lst == "label_list"
else resource_point_file
)
for x in data[lst]:
id_ = x["id"]
if lst == "label_list":
img_url = x["icon"]
tasks.append(
asyncio.ensure_future(
download_image(
img_url,
f"{icon_path}/{id_}.png",
semaphore,
True,
)
)
resource_data[id_] = x
await asyncio.gather(*tasks)
with open(file, "w", encoding="utf8") as f:
json.dump(resource_data, f, ensure_ascii=False, indent=4)
else:
logger.warning(f'获取原神资源失败 msg: {data["message"]}')
)
resource_data[id_] = x
await asyncio.gather(*tasks)
with open(file, "w", encoding="utf8") as f:
json.dump(resource_data, f, ensure_ascii=False, indent=4)
else:
logger.warning(f"获取原神资源失败 code:{response.status}")
logger.warning(f'获取原神资源失败 msg: {data["message"]}')
else:
logger.warning(f"获取原神资源失败 code:{response.status_code}")
except TimeoutError:
logger.warning("获取原神资源数据超时...已再次尝试...")
await download_resource_data(session, semaphore)
await download_resource_data(semaphore)
# 下载原神地图并拼图
async def download_map_init(
session: ClientSession, semaphore: Semaphore, ratio: float = 1, flag: bool = False
semaphore: Semaphore, flag: bool = False
):
global CENTER_POINT, MAP_RATIO
map_path.mkdir(exist_ok=True, parents=True)
_map = map_path / "map.png"
if _map.exists() and os.path.getsize(_map) > 1024 * 1024 * 30:
_map.unlink()
async with session.get(MAP_URL, timeout=5) as response:
if response.status == 200:
data = await response.json()
if data["message"] == "OK":
data = json.loads(data["data"]["info"]["detail"])
CENTER_POINT = (data["origin"][0], data["origin"][1])
if not _map.exists():
# padding_w, padding_h = data['padding']
data = data["slices"]
idx = 0
for _map_data in data[0]:
map_url = _map_data['url']
await download_image(
map_url,
f"{map_path}/{idx}.png",
session,
semaphore,
force_flag=flag,
)
idx += 1
_w, h = CreateImg(0, 0, background=f"{map_path}/0.png", ratio=MAP_RATIO).size
w = _w * len(os.listdir(map_path))
map_file = CreateImg(w, h, _w, h, ratio=MAP_RATIO)
for i in range(idx):
map_file.paste(CreateImg(0, 0, background=f"{map_path}/{i}.png", ratio=MAP_RATIO))
map_file.save(f"{map_path}/map.png")
else:
logger.warning(f'获取原神地图失败 msg: {data["message"]}')
response = await AsyncHttpx.get(MAP_URL)
if response.status_code == 200:
data = response.json()
if data["message"] == "OK":
data = json.loads(data["data"]["info"]["detail"])
CENTER_POINT = (data["origin"][0], data["origin"][1])
if not _map.exists():
data = data["slices"]
idx = 0
for _map_data in data[0]:
map_url = _map_data['url']
await download_image(
map_url,
f"{map_path}/{idx}.png",
semaphore,
force_flag=flag,
)
idx += 1
_w, h = CreateImg(0, 0, background=f"{map_path}/0.png", ratio=MAP_RATIO).size
w = _w * len(os.listdir(map_path))
map_file = CreateImg(w, h, _w, h, ratio=MAP_RATIO)
for i in range(idx):
map_file.paste(CreateImg(0, 0, background=f"{map_path}/{i}.png", ratio=MAP_RATIO))
map_file.save(f"{map_path}/map.png")
else:
logger.warning(f"获取原神地图失败 code:{response.status}")
logger.warning(f'获取原神地图失败 msg: {data["message"]}')
else:
logger.warning(f"获取原神地图失败 code:{response.status}")
# 下载资源类型数据
async def download_resource_type(session: ClientSession):
async def download_resource_type():
resource_type_file.parent.mkdir(parents=True, exist_ok=True)
async with session.get(LABEL_URL, timeout=5) as response:
if response.status == 200:
data = await response.json()
if data["message"] == "OK":
data = data["data"]["tree"]
resource_data = {}
for x in data:
id_ = x["id"]
resource_data[id_] = x
with open(resource_type_file, "w", encoding="utf8") as f:
json.dump(resource_data, f, ensure_ascii=False, indent=4)
logger.info(f"更新原神资源类型成功...")
else:
logger.warning(f'获取原神资源类型失败 msg: {data["message"]}')
response = await AsyncHttpx.get(LABEL_URL)
if response.status_code == 200:
data = response.json()
if data["message"] == "OK":
data = data["data"]["tree"]
resource_data = {}
for x in data:
id_ = x["id"]
resource_data[id_] = x
with open(resource_type_file, "w", encoding="utf8") as f:
json.dump(resource_data, f, ensure_ascii=False, indent=4)
logger.info(f"更新原神资源类型成功...")
else:
logger.warning(f"获取原神资源类型失败 code:{response.status}")
logger.warning(f'获取原神资源类型失败 msg: {data["message"]}')
else:
logger.warning(f"获取原神资源类型失败 code:{response.status_code}")
# 初始化资源图标
@@ -239,7 +232,6 @@ def gen_icon(icon: str):
async def download_image(
img_url: str,
path: str,
session: ClientSession,
semaphore: Semaphore,
gen_flag: bool = False,
force_flag: bool = False,
@@ -247,15 +239,12 @@ async def download_image(
async with semaphore:
try:
if not os.path.exists(path) or not is_valid or force_flag:
async with session.get(img_url, timeout=5) as response:
async with aiofiles.open(path, "wb") as f:
await f.write(await response.read())
logger.info(f"下载原神资源图标:{img_url}")
if gen_flag:
gen_icon(path)
except TimeoutError:
logger.warning("下载原神资源图片超时...已再次尝试...")
await download_image(img_url, path, session, semaphore, gen_flag)
if await AsyncHttpx.download_file(img_url, path):
logger.info(f"下载原神资源图标:{img_url}")
if gen_flag:
gen_icon(path)
else:
logger.info(f"下载原神资源图标:{img_url} 失败,等待下次更新...")
except UnidentifiedImageError:
logger.warning(f"原神图片打开错误..已删除,等待下次更新... file: {path}")
if os.path.exists(path):
+3 -1
View File
@@ -25,6 +25,7 @@ from nonebot.rule import to_me
from datetime import datetime, timedelta
from configs.config import NICKNAME
from apscheduler.jobstores.base import JobLookupError
from nonebot.adapters.cqhttp.exception import ActionFailed
import random
import time
@@ -320,7 +321,8 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
b64=await generate_send_redbag_pic(int(bot.self_id), greetings)
),
)
except AttributeError:
except ActionFailed:
logger.warning(f"节日红包 GROUP {g} 发送失败..")
pass
+4 -13
View File
@@ -1,12 +1,11 @@
from models.bag_user import BagUser
from utils.utils import is_number, get_local_proxy
from utils.utils import is_number, get_local_proxy, get_user_avatar
from utils.image_utils import CreateImg
from utils.user_agent import get_user_agent
from configs.path_config import IMAGE_PATH
from ..models.redbag_user import RedbagUser
from .model import RedbagUser
import random
import os
import aiohttp
from io import BytesIO
import asyncio
@@ -47,7 +46,7 @@ async def open_redbag(user_id: int, group_id: int, redbag_data: dict):
async def generate_send_redbag_pic(user_id: int, msg: str = '恭喜发财 大吉大利'):
random_redbag = random.choice(os.listdir(f"{IMAGE_PATH}/prts/redbag_2"))
redbag = CreateImg(0, 0, font_size=38, background=f'{IMAGE_PATH}/prts/redbag_2/{random_redbag}')
ava = CreateImg(65, 65, background=BytesIO(await get_pic(user_id)))
ava = CreateImg(65, 65, background=BytesIO(await get_user_avatar(user_id)))
await asyncio.get_event_loop().run_in_executor(None, ava.circle)
redbag.text((int((redbag.size[0] - redbag.getsize(msg)[0]) / 2), 210), msg, (240, 218, 164))
redbag.paste(ava, (int((redbag.size[0] - ava.size[0])/2), 130), True)
@@ -59,14 +58,6 @@ async def generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount
return await asyncio.create_task(_generate_open_redbag_pic(user_id, send_user_nickname, amount, text))
# 获取QQ头像
async def get_pic(qq):
url = f'http://q1.qlogo.cn/g?b=qq&nk={qq}&s=160'
async with aiohttp.ClientSession(headers=get_user_agent()) as session:
async with session.get(url, proxy=get_local_proxy(), timeout=5) as response:
return await response.read()
# 开红包图片
async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amount: int, text: str):
send_user_nickname += '的红包'
@@ -75,7 +66,7 @@ async def _generate_open_redbag_pic(user_id: int, send_user_nickname: str, amoun
size = CreateImg(0, 0, font_size=50).getsize(send_user_nickname)
# QQ头像
ava_bk = CreateImg(100 + size[0], 66, color='white', font_size=50)
ava = CreateImg(66, 66, background=BytesIO(await get_pic(user_id)))
ava = CreateImg(66, 66, background=BytesIO(await get_user_avatar(user_id)))
ava_bk.paste(ava)
ava_bk.text((100, 7), send_user_nickname)
# ava_bk.show()
View File
+35 -35
View File
@@ -1,35 +1,35 @@
from nonebot import on_message
from nonebot.adapters.cqhttp.permission import GROUP
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
import time
from .data_source import cancel_all_notice, save_data, get_data, set_data_value
from services.log import logger
__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 _(bot: Bot, event: GroupMessageEvent, state: T_State):
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()
from nonebot import on_message
from nonebot.adapters.cqhttp.permission import GROUP
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, 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 _(bot: Bot, event: GroupMessageEvent, state: T_State):
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 -67
View File
@@ -1,67 +1,67 @@
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
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
Regular → Executable
View File
+3 -3
View File
@@ -21,7 +21,7 @@ Config.add_plugin_config(
)
Config.add_plugin_config(
"image_management:delete_img",
"image_management:delete_image",
"DELETE_IMAGE_LEVEL [LEVEL]",
7,
help_="删除图库图片需要的管理员等级",
@@ -29,7 +29,7 @@ Config.add_plugin_config(
)
Config.add_plugin_config(
"image_management:move_img",
"image_management:move_image",
"MOVE_IMAGE_LEVEL [LEVEL]",
7,
help_="移动图库图片需要的管理员等级",
@@ -37,7 +37,7 @@ Config.add_plugin_config(
)
Config.add_plugin_config(
"image_management:upload_img",
"image_management:upload_image",
"UPLOAD_IMAGE_LEVEL [LEVEL]",
6,
help_="上传图库图片需要的管理员等级",
@@ -1,96 +1,96 @@
from configs.path_config import IMAGE_PATH, TEMP_PATH
from utils.message_builder import image
from services.log import logger
from nonebot import on_command
from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import is_number, cn2py, get_message_text
from configs.config import Config
from pathlib import Path
import os
__zx_plugin_name__ = "删除图片 [Admin]"
__plugin_usage__ = """
usage:
删除图库指定图片
指令:
删除图片 [图库] [id]
查看图库
示例:删除图片 美图 666
""".strip()
__plugin_des__ = "不好看的图片删掉删掉!"
__plugin_cmd__ = ["删除图片 [图库] [id]", "查看公开图库"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL")
}
delete_img = on_command("删除图片", priority=5, rule=to_me(), block=True)
@delete_img.args_parser
async def parse(bot: Bot, event: MessageEvent, state: T_State):
if get_message_text(event.json()) in ["取消", "算了"]:
await delete_img.finish("已取消操作..", at_sender=True)
if state["_current_key"] in ["path"]:
if get_message_text(event.json()) not in Config.get_config("image_management", "IMAGE_DIR_LIST"):
await delete_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = get_message_text(event.json())
if state["_current_key"] == "id":
if not is_number(get_message_text(event.json())):
await delete_img.reject("id不正确!请重新输入数字...")
state[state["_current_key"]] = get_message_text(event.json())
@delete_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
raw_arg = get_message_text(event.json()).strip()
if raw_arg:
args = raw_arg.split(" ")
if args[0] in ["帮助"]:
await delete_img.finish(__plugin_usage__)
if len(args) >= 2 and args[0] in Config.get_config("image_management", "IMAGE_DIR_LIST") and is_number(args[1]):
state["path"] = args[0]
state["id"] = args[1]
@delete_img.got("path", prompt="请输入要删除的目标图库?")
@delete_img.got("id", prompt="请输入要删除的图片id?")
async def arg_handle(bot: Bot, event: MessageEvent, state: T_State):
path = cn2py(state["path"])
img_id = state["id"]
# path = IMAGE_PATH + path
path = Path(IMAGE_PATH) / path
temp = Path(IMAGE_PATH) / "temp"
max_id = len(os.listdir(path)) - 1
if int(img_id) > max_id or int(img_id) < 0:
await delete_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True)
try:
if os.path.exists(temp / "delete.jpg"):
os.remove(temp / "delete.jpg")
logger.info("删除图片 delete.jpg 成功")
except Exception as e:
logger.warning(f"删除图片 delete.jpg 失败 e{e}")
try:
os.rename(path / f"{img_id}.jpg", temp / "delete.jpg")
logger.info(f"移动 {path}/{img_id}.jpg 移动成功")
except Exception as e:
logger.warning(f"{path}/{img_id}.jpg --> 移动失败 e:{e}")
if not os.path.exists(path / f"{img_id}.jpg"):
try:
if int(img_id) != max_id:
os.rename(path / f"{max_id}.jpg", path / f"{img_id}.jpg")
except FileExistsError as e:
logger.error(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 失败 e:{e}")
logger.info(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 成功")
logger.info(
f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}"
f" -> id: {img_id} 删除成功"
)
await delete_img.finish(
f"id: {img_id} 删除成功" + image("delete.jpg", TEMP_PATH), at_sender=True
)
await delete_img.finish(f"id: {img_id} 删除失败!")
from configs.path_config import IMAGE_PATH, TEMP_PATH
from utils.message_builder import image
from services.log import logger
from nonebot import on_command
from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import is_number, cn2py, get_message_text
from configs.config import Config
from pathlib import Path
import os
__zx_plugin_name__ = "删除图片 [Admin]"
__plugin_usage__ = """
usage:
删除图库指定图片
指令:
删除图片 [图库] [id]
查看图库
示例:删除图片 美图 666
""".strip()
__plugin_des__ = "不好看的图片删掉删掉!"
__plugin_cmd__ = ["删除图片 [图库] [id]", "查看公开图库"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL")
}
delete_img = on_command("删除图片", priority=5, rule=to_me(), block=True)
@delete_img.args_parser
async def parse(bot: Bot, event: MessageEvent, state: T_State):
if get_message_text(event.json()) in ["取消", "算了"]:
await delete_img.finish("已取消操作..", at_sender=True)
if state["_current_key"] in ["path"]:
if get_message_text(event.json()) not in Config.get_config("image_management", "IMAGE_DIR_LIST"):
await delete_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = get_message_text(event.json())
if state["_current_key"] == "id":
if not is_number(get_message_text(event.json())):
await delete_img.reject("id不正确!请重新输入数字...")
state[state["_current_key"]] = get_message_text(event.json())
@delete_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
raw_arg = get_message_text(event.json()).strip()
if raw_arg:
args = raw_arg.split(" ")
if args[0] in ["帮助"]:
await delete_img.finish(__plugin_usage__)
if len(args) >= 2 and args[0] in Config.get_config("image_management", "IMAGE_DIR_LIST") and is_number(args[1]):
state["path"] = args[0]
state["id"] = args[1]
@delete_img.got("path", prompt="请输入要删除的目标图库?")
@delete_img.got("id", prompt="请输入要删除的图片id?")
async def arg_handle(bot: Bot, event: MessageEvent, state: T_State):
path = cn2py(state["path"])
img_id = state["id"]
# path = IMAGE_PATH + path
path = Path(IMAGE_PATH) / path
temp = Path(IMAGE_PATH) / "temp"
max_id = len(os.listdir(path)) - 1
if int(img_id) > max_id or int(img_id) < 0:
await delete_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True)
try:
if os.path.exists(temp / "delete.jpg"):
os.remove(temp / "delete.jpg")
logger.info("删除图片 delete.jpg 成功")
except Exception as e:
logger.warning(f"删除图片 delete.jpg 失败 e{e}")
try:
os.rename(path / f"{img_id}.jpg", temp / "delete.jpg")
logger.info(f"移动 {path}/{img_id}.jpg 移动成功")
except Exception as e:
logger.warning(f"{path}/{img_id}.jpg --> 移动失败 e:{e}")
if not os.path.exists(path / f"{img_id}.jpg"):
try:
if int(img_id) != max_id:
os.rename(path / f"{max_id}.jpg", path / f"{img_id}.jpg")
except FileExistsError as e:
logger.error(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 失败 e:{e}")
logger.info(f"{path}/{max_id}.jpg 替换 {path}/{img_id}.jpg 成功")
logger.info(
f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'}"
f" -> id: {img_id} 删除成功"
)
await delete_img.finish(
f"id: {img_id} 删除成功" + image("delete.jpg", TEMP_PATH), at_sender=True
)
await delete_img.finish(f"id: {img_id} 删除失败!")
@@ -1,105 +1,105 @@
import os
from services.log import logger
from nonebot import on_command
from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from configs.config import Config
from utils.utils import is_number, cn2py
from configs.path_config import IMAGE_PATH
from pathlib import Path
__zx_plugin_name__ = "移动图片 [Admin]"
__plugin_usage__ = """
usage:
图库间的图片移动操作
指令:
移动图片 [源图库] [目标图库] [id]
查看图库
示例:移动图片 萝莉 美图 234
""".strip()
__plugin_des__ = "图库间的图片移动操作"
__plugin_cmd__ = ["移动图片 [源图库] [目标图库] [id]", "查看公开图库"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("image_management", "MOVE_IMAGE_LEVEL")
}
move_img = on_command("移动图片", priority=5, rule=to_me(), block=True)
@move_img.args_parser
async def parse(bot: Bot, event: MessageEvent, state: T_State):
if str(event.get_message()) in ["取消", "算了"]:
await move_img.finish("已取消操作..", at_sender=True)
if state["_current_key"] in ["source_path", "destination_path"]:
if str(event.get_message()) not in Config.get_config(
"image_management", "IMAGE_DIR_LIST"
):
await move_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = str(event.get_message())
if state["_current_key"] == "id":
if not is_number(str(event.get_message())):
await move_img.reject("id不正确!请重新输入数字...")
state[state["_current_key"]] = str(event.get_message())
@move_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
raw_arg = str(event.get_message()).strip()
if raw_arg:
args = raw_arg.split(" ")
if args[0] in ["帮助"]:
await move_img.finish(__plugin_usage__)
if (
len(args) >= 3
and args[0] in Config.get_config("image_management", "IMAGE_DIR_LIST")
and args[1] in Config.get_config("image_management", "IMAGE_DIR_LIST")
and is_number(args[2])
):
state["source_path"] = args[0]
state["destination_path"] = args[1]
state["id"] = args[2]
else:
await move_img.finish("参数错误,请重试", at_sender=True)
@move_img.got("source_path", prompt="要从哪个图库移出?")
@move_img.got("destination_path", prompt="要移动到哪个图库?")
@move_img.got("id", prompt="要移动的图片id是?")
async def _(bot: Bot, event: MessageEvent, state: T_State):
img_id = state["id"]
source_path = Path(IMAGE_PATH) / cn2py(state["source_path"])
destination_path = Path(IMAGE_PATH) / cn2py(state["destination_path"])
destination_path.mkdir(parents=True, exist_ok=True)
max_id = len(os.listdir(source_path)) - 1
des_max_id = len(os.listdir(destination_path))
if int(img_id) > max_id or int(img_id) < 0:
await move_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True)
try:
os.rename(source_path / f"{img_id}.jpg", destination_path / f"{des_max_id}.jpg")
logger.info(
f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动成功"
)
except Exception as e:
logger.warning(
f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动失败 e:{e}"
)
await move_img.finish(f"移动图片id:{img_id} 失败了...", at_sender=True)
if max_id > 0:
try:
os.rename(source_path / f"{max_id}.jpg", source_path / f"{img_id}.jpg")
logger.info(f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 成功")
except Exception as e:
logger.warning(
f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 失败 e:{e}"
)
await move_img.finish(f"替换图片id:{max_id} -> {img_id} 失败了...", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'} ->"
f" {source_path} --> {destination_path} (id:{img_id}) 移动图片成功"
)
await move_img.finish(f"移动图片 id:{img_id} --> id:{des_max_id}成功", at_sender=True)
import os
from services.log import logger
from nonebot import on_command
from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from configs.config import Config
from utils.utils import is_number, cn2py
from configs.path_config import IMAGE_PATH
from pathlib import Path
__zx_plugin_name__ = "移动图片 [Admin]"
__plugin_usage__ = """
usage:
图库间的图片移动操作
指令:
移动图片 [源图库] [目标图库] [id]
查看图库
示例:移动图片 萝莉 美图 234
""".strip()
__plugin_des__ = "图库间的图片移动操作"
__plugin_cmd__ = ["移动图片 [源图库] [目标图库] [id]", "查看公开图库"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"admin_level": Config.get_config("image_management", "MOVE_IMAGE_LEVEL")
}
move_img = on_command("移动图片", priority=5, rule=to_me(), block=True)
@move_img.args_parser
async def parse(bot: Bot, event: MessageEvent, state: T_State):
if str(event.get_message()) in ["取消", "算了"]:
await move_img.finish("已取消操作..", at_sender=True)
if state["_current_key"] in ["source_path", "destination_path"]:
if str(event.get_message()) not in Config.get_config(
"image_management", "IMAGE_DIR_LIST"
):
await move_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = str(event.get_message())
if state["_current_key"] == "id":
if not is_number(str(event.get_message())):
await move_img.reject("id不正确!请重新输入数字...")
state[state["_current_key"]] = str(event.get_message())
@move_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
raw_arg = str(event.get_message()).strip()
if raw_arg:
args = raw_arg.split(" ")
if args[0] in ["帮助"]:
await move_img.finish(__plugin_usage__)
if (
len(args) >= 3
and args[0] in Config.get_config("image_management", "IMAGE_DIR_LIST")
and args[1] in Config.get_config("image_management", "IMAGE_DIR_LIST")
and is_number(args[2])
):
state["source_path"] = args[0]
state["destination_path"] = args[1]
state["id"] = args[2]
else:
await move_img.finish("参数错误,请重试", at_sender=True)
@move_img.got("source_path", prompt="要从哪个图库移出?")
@move_img.got("destination_path", prompt="要移动到哪个图库?")
@move_img.got("id", prompt="要移动的图片id是?")
async def _(bot: Bot, event: MessageEvent, state: T_State):
img_id = state["id"]
source_path = Path(IMAGE_PATH) / cn2py(state["source_path"])
destination_path = Path(IMAGE_PATH) / cn2py(state["destination_path"])
destination_path.mkdir(parents=True, exist_ok=True)
max_id = len(os.listdir(source_path)) - 1
des_max_id = len(os.listdir(destination_path))
if int(img_id) > max_id or int(img_id) < 0:
await move_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True)
try:
os.rename(source_path / f"{img_id}.jpg", destination_path / f"{des_max_id}.jpg")
logger.info(
f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动成功"
)
except Exception as e:
logger.warning(
f"移动 {source_path}/{img_id}.jpg ---> {destination_path}/{des_max_id} 移动失败 e:{e}"
)
await move_img.finish(f"移动图片id:{img_id} 失败了...", at_sender=True)
if max_id > 0:
try:
os.rename(source_path / f"{max_id}.jpg", source_path / f"{img_id}.jpg")
logger.info(f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 成功")
except Exception as e:
logger.warning(
f"{source_path}/{max_id}.jpg 替换 {source_path}/{img_id}.jpg 失败 e:{e}"
)
await move_img.finish(f"替换图片id:{max_id} -> {img_id} 失败了...", at_sender=True)
logger.info(
f"USER {event.user_id} GROUP {event.group_id if isinstance(event, GroupMessageEvent) else 'private'} ->"
f" {source_path} --> {destination_path} (id:{img_id}) 移动图片成功"
)
await move_img.finish(f"移动图片 id:{img_id} --> id:{des_max_id}成功", at_sender=True)
@@ -1,124 +1,124 @@
from nonebot import on_command, on_keyword, on_regex
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.utils import get_message_text, is_number
from services.log import logger
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import FreqLimiter, cn2py
from configs.config import Config
from utils.manager import group_manager, withdraw_message_manager
import random
import os
try:
import ujson as json
except ModuleNotFoundError:
import json
__zx_plugin_name__ = "发送本地图库图片"
__plugin_usage__ = f"""
usage:
发送指定图库下的随机或指定id图片
指令:
{Config.get_config("image_management", "IMAGE_DIR_LIST")} ?[id]
示例:美图
示例: 萝莉 2
""".strip()
__plugin_des__ = "让看看我的私藏,指[图片]"
__plugin_cmd__ = Config.get_config("image_management", "IMAGE_DIR_LIST")
__plugin_type__ = ("来点好康的",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["发送图片"] + Config.get_config("image_management", "IMAGE_DIR_LIST"),
}
__plugin_task__ = {"pa": "丢人爬"}
__plugin_resources__ = {
"pa": IMAGE_PATH
}
_flmt = FreqLimiter(1)
cmd = set(Config.get_config("image_management", "IMAGE_DIR_LIST"))
# print(cmd)
send_img = on_command("img", aliases=cmd, priority=5, block=True)
pa = on_keyword({"丢人爬", "爪巴"}, priority=5, block=True)
pa_reg = on_regex("^爬$", priority=5, block=True)
search_url = "https://api.fantasyzone.cc/tu/search.php"
@send_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
img_id = get_message_text(event.json())
path = cn2py(state["_prefix"]["raw_command"]) + "/"
if state["_prefix"]["raw_command"] in Config.get_config(
"image_management", "IMAGE_DIR_LIST"
):
if not os.path.exists(f"{IMAGE_PATH}/{path}/"):
os.mkdir(f"{IMAGE_PATH}/{path}/")
length = len(os.listdir(IMAGE_PATH + path))
if length == 0:
logger.warning(f"图库 {path} 为空,调用取消!")
await send_img.finish("该图库中没有图片噢")
index = img_id if img_id else str(random.randint(0, length))
if not is_number(index):
return
if int(index) > length - 1 or int(index) < 0:
await send_img.finish(f"超过当前上下限!({length - 1})")
result = image(f"{index}.jpg", path)
if result:
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送{path}:"
+ result
)
msg_id = await send_img.send(f"id:{index}" + result)
withdraw_message_manager.withdraw_message(
event,
msg_id,
Config.get_config("image_management", "WITHDRAW_IMAGE_MESSAGE"),
)
else:
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送 {path} 失败"
)
await send_img.finish(f"不想给你看Ov|")
@pa.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
if (
isinstance(event, GroupMessageEvent)
and not await group_manager.check_group_task_status(event.group_id, "pa")
or get_message_text(event.json()).startswith("开启")
or get_message_text(event.json()).startswith("关闭")
):
return
msg = get_message_text(event.json())
if not msg or str(event.get_message()[:2]) in ["开启", "关闭"]:
return
if _flmt.check(event.user_id):
_flmt.start_cd(event.user_id)
await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa"))
@pa_reg.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
if (
isinstance(event, GroupMessageEvent)
and not await group_manager.check_group_task_status(event.group_id, "pa")
or get_message_text(event.json()).startswith("开启")
or get_message_text(event.json()).startswith("关闭")
):
return
if _flmt.check(event.user_id):
_flmt.start_cd(event.user_id)
await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa"))
from nonebot import on_command, on_keyword, on_regex
from configs.path_config import IMAGE_PATH
from utils.message_builder import image
from utils.utils import get_message_text, is_number
from services.log import logger
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import FreqLimiter, cn2py
from configs.config import Config
from utils.manager import group_manager, withdraw_message_manager
import random
import os
try:
import ujson as json
except ModuleNotFoundError:
import json
__zx_plugin_name__ = "发送本地图库图片"
__plugin_usage__ = f"""
usage:
发送指定图库下的随机或指定id图片
指令:
{Config.get_config("image_management", "IMAGE_DIR_LIST")} ?[id]
示例:美图
示例: 萝莉 2
""".strip()
__plugin_des__ = "让看看我的私藏,指[图片]"
__plugin_cmd__ = Config.get_config("image_management", "IMAGE_DIR_LIST")
__plugin_type__ = ("来点好康的",)
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["发送图片"] + Config.get_config("image_management", "IMAGE_DIR_LIST"),
}
__plugin_task__ = {"pa": "丢人爬"}
__plugin_resources__ = {
"pa": IMAGE_PATH
}
_flmt = FreqLimiter(1)
cmd = set(Config.get_config("image_management", "IMAGE_DIR_LIST"))
# print(cmd)
send_img = on_command("img", aliases=cmd, priority=5, block=True)
pa = on_keyword({"丢人爬", "爪巴"}, priority=5, block=True)
pa_reg = on_regex("^爬$", priority=5, block=True)
search_url = "https://api.fantasyzone.cc/tu/search.php"
@send_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
img_id = get_message_text(event.json())
path = cn2py(state["_prefix"]["raw_command"]) + "/"
if state["_prefix"]["raw_command"] in Config.get_config(
"image_management", "IMAGE_DIR_LIST"
):
if not os.path.exists(f"{IMAGE_PATH}/{path}/"):
os.mkdir(f"{IMAGE_PATH}/{path}/")
length = len(os.listdir(IMAGE_PATH + path))
if length == 0:
logger.warning(f"图库 {path} 为空,调用取消!")
await send_img.finish("该图库中没有图片噢")
index = img_id if img_id else str(random.randint(0, length))
if not is_number(index):
return
if int(index) > length - 1 or int(index) < 0:
await send_img.finish(f"超过当前上下限!({length - 1})")
result = image(f"{index}.jpg", path)
if result:
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送{path}:"
+ result
)
msg_id = await send_img.send(f"id:{index}" + result)
withdraw_message_manager.withdraw_message(
event,
msg_id,
Config.get_config("image_management", "WITHDRAW_IMAGE_MESSAGE"),
)
else:
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'}) 发送 {path} 失败"
)
await send_img.finish(f"不想给你看Ov|")
@pa.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
if (
isinstance(event, GroupMessageEvent)
and not await group_manager.check_group_task_status(event.group_id, "pa")
or get_message_text(event.json()).startswith("开启")
or get_message_text(event.json()).startswith("关闭")
):
return
msg = get_message_text(event.json())
if not msg or str(event.get_message()[:2]) in ["开启", "关闭"]:
return
if _flmt.check(event.user_id):
_flmt.start_cd(event.user_id)
await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa"))
@pa_reg.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
if (
isinstance(event, GroupMessageEvent)
and not await group_manager.check_group_task_status(event.group_id, "pa")
or get_message_text(event.json()).startswith("开启")
or get_message_text(event.json()).startswith("关闭")
):
return
if _flmt.check(event.user_id):
_flmt.start_cd(event.user_id)
await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), "pa"))
@@ -1,127 +1,126 @@
from nonebot import on_command
from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from configs.config import Config
from utils.utils import get_message_imgs, get_message_text
from .data_source import upload_image_to_local
__zx_plugin_name__ = "上传图片 [Admin]"
__plugin_usage__ = """
usage:
上传图片至指定图库
指令:
查看图库
上传图片 [图库] [图片]
连续上传图片 [图库]
示例:上传图片 美图 [图片]
* 连续上传图片可以通过发送 “stop” 表示停止收集发送的图片,可以开始上传 *
""".strip()
__plugin_des__ = "指定图库图片上传"
__plugin_cmd__ = ["上传图片 [图库] [图片]", "连续上传图片 [图库]", "查看公开图库"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {"admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL")}
upload_img = on_command("上传图片", rule=to_me(), priority=5, block=True)
continuous_upload_img = on_command("连续上传图片", rule=to_me(), priority=5, block=True)
show_gallery = on_command("查看公开图库", priority=1, block=True)
@show_gallery.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
x = '公开图库列表:\n'
for i, e in enumerate(Config.get_config("image_management", "IMAGE_DIR_LIST")):
x += f'\t{i+1}.{e}\n'
await show_gallery.send(x[:-1])
@upload_img.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if msg in ["取消", "算了"]:
await upload_img.finish("已取消操作..", at_sender=True)
if state["_current_key"] in ["path"]:
if msg not in Config.get_config("image_management", "IMAGE_DIR_LIST"):
await upload_img.reject("此目录不正确,请重新输入目录!")
state["path"] = msg
if state["_current_key"] in ["imgs"]:
if not get_message_imgs(event.json()):
await upload_img.reject("图呢图呢图呢图呢!GKD!")
state["imgs"] = get_message_imgs(event.json())
@upload_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
raw_arg = get_message_text(event.json())
img_list = get_message_imgs(event.json())
if raw_arg:
if raw_arg in Config.get_config("image_management", "IMAGE_DIR_LIST"):
state["path"] = raw_arg
if img_list:
state["imgs"] = img_list
@upload_img.got("path", prompt="要将图片上传至什么图库呢?")
async def _(bot: Bot, event: MessageEvent, state: T_State):
pass
@upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!")
async def _(bot: Bot, event: MessageEvent, state: T_State):
path = state["path"]
img_list = state["imgs"]
group_id = 0
if isinstance(event, GroupMessageEvent):
group_id = event.group_id
await upload_img.send(
await upload_image_to_local(img_list, path, event.user_id, group_id)
)
@continuous_upload_img.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
if str(event.get_message()) in ["取消", "算了"]:
await continuous_upload_img.finish("已取消操作..", at_sender=True)
if state["_current_key"] in ["path"]:
if str(event.get_message()) not in Config.get_config("image_management", "IMAGE_DIR_LIST"):
await continuous_upload_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = str(event.get_message())
else:
if get_message_text(event.json()) not in ["stop"]:
img = get_message_imgs(event.json())
if img:
state["tmp"].extend(img)
await continuous_upload_img.reject("图再来!!")
else:
state["imgs"] = state["tmp"]
@continuous_upload_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
path = get_message_imgs(event.json())
if path in Config.get_config("image_management", "IMAGE_DIR_LIST"):
state["path"] = path
await continuous_upload_img.send("图来!!")
state["tmp"] = []
@continuous_upload_img.got("path", prompt="要将图片上传至什么图库呢?")
async def _(bot: Bot, event: MessageEvent, state: T_State):
pass
@continuous_upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!")
async def _(bot: Bot, event: MessageEvent, state: T_State):
path = state["path"]
img_list = state["imgs"]
group_id = 0
if isinstance(event, GroupMessageEvent):
group_id = event.group_id
await continuous_upload_img.send(
await upload_image_to_local(img_list, path, event.user_id, group_id)
)
from nonebot import on_command
from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from configs.config import Config
from utils.utils import get_message_imgs, get_message_text
from .data_source import upload_image_to_local
__zx_plugin_name__ = "上传图片 [Admin]"
__plugin_usage__ = """
usage:
上传图片至指定图库
指令:
查看图库
上传图片 [图库] [图片]
连续上传图片 [图库]
示例:上传图片 美图 [图片]
* 连续上传图片可以通过发送 “stop” 表示停止收集发送的图片,可以开始上传 *
""".strip()
__plugin_des__ = "指定图库图片上传"
__plugin_cmd__ = ["上传图片 [图库] [图片]", "连续上传图片 [图库]", "查看公开图库"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {"admin_level": Config.get_config("image_management", "DELETE_IMAGE_LEVEL")}
upload_img = on_command("上传图片", rule=to_me(), priority=5, block=True)
continuous_upload_img = on_command("连续上传图片", rule=to_me(), priority=5, block=True)
show_gallery = on_command("查看公开图库", priority=1, block=True)
@show_gallery.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
x = '公开图库列表:\n'
for i, e in enumerate(Config.get_config("image_management", "IMAGE_DIR_LIST")):
x += f'\t{i+1}.{e}\n'
await show_gallery.send(x[:-1])
@upload_img.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if msg in ["取消", "算了"]:
await upload_img.finish("已取消操作..", at_sender=True)
if state["_current_key"] in ["path"]:
if msg not in Config.get_config("image_management", "IMAGE_DIR_LIST"):
await upload_img.reject("此目录不正确,请重新输入目录!")
state["path"] = msg
if state["_current_key"] in ["imgs"]:
if not get_message_imgs(event.json()):
await upload_img.reject("图呢图呢图呢图呢!GKD!")
state["imgs"] = get_message_imgs(event.json())
@upload_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
raw_arg = get_message_text(event.json())
img_list = get_message_imgs(event.json())
if raw_arg:
if raw_arg in Config.get_config("image_management", "IMAGE_DIR_LIST"):
state["path"] = raw_arg
if img_list:
state["imgs"] = img_list
@upload_img.got("path", prompt="要将图片上传至什么图库呢?")
async def _(bot: Bot, event: MessageEvent, state: T_State):
pass
@upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!")
async def _(bot: Bot, event: MessageEvent, state: T_State):
path = state["path"]
img_list = state["imgs"]
group_id = 0
if isinstance(event, GroupMessageEvent):
group_id = event.group_id
await upload_img.send(
await upload_image_to_local(img_list, path, event.user_id, group_id)
)
@continuous_upload_img.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
if str(event.get_message()) in ["取消", "算了"]:
await continuous_upload_img.finish("已取消操作..", at_sender=True)
if state["_current_key"] in ["path"]:
if str(event.get_message()) not in Config.get_config("image_management", "IMAGE_DIR_LIST"):
await continuous_upload_img.reject("此目录不正确,请重新输入目录!")
state[state["_current_key"]] = str(event.get_message())
else:
if get_message_text(event.json()) not in ["stop"]:
img = get_message_imgs(event.json())
if img:
state["tmp"].extend(img)
await continuous_upload_img.reject("图再来!!")
else:
state["imgs"] = state["tmp"]
@continuous_upload_img.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
path = get_message_imgs(event.json())
if path in Config.get_config("image_management", "IMAGE_DIR_LIST"):
state["path"] = path
await continuous_upload_img.send("图来!!")
state["tmp"] = []
@continuous_upload_img.got("path", prompt="要将图片上传至什么图库呢?")
async def _(bot: Bot, event: MessageEvent, state: T_State):
pass
@continuous_upload_img.got("imgs", prompt="图呢图呢图呢图呢!GKD!")
async def _(bot: Bot, event: MessageEvent, state: T_State):
path = state["path"]
img_list = state["imgs"]
group_id = 0
if isinstance(event, GroupMessageEvent):
group_id = event.group_id
await continuous_upload_img.send(
await upload_image_to_local(img_list, path, event.user_id, group_id)
)
@@ -1,51 +1,42 @@
from configs.config import NICKNAME
from typing import List
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.utils import cn2py
from pathlib import Path
import aiofiles
import aiohttp
import os
async def upload_image_to_local(
img_list: List[str], path: str, user_id: int, group_id: int = 0
) -> str:
_path = path
path = Path(IMAGE_PATH) / cn2py(path)
path.mkdir(parents=True, exist_ok=True)
img_id = len(os.listdir(path))
failed_list = []
success_id = ""
async with aiohttp.ClientSession() as session:
for img_url in img_list:
try:
async with session.get(img_url, timeout=7) as response:
if response.status == 200:
async with aiofiles.open(path / f"{img_id}.jpg", "wb") as f:
await f.write(await response.read())
success_id += str(img_id) + ","
img_id += 1
else:
failed_list.append(img_url)
logger.warning(f"图片:{img_url} 下载失败....")
except TimeoutError as e:
logger.warning(f"图片:{img_url} 下载超时....e:{e}")
if img_url not in failed_list:
failed_list.append(img_url)
failed_result = ""
for img in failed_list:
failed_result += str(img) + "\n"
logger.info(
f"USER {user_id} GROUP {group_id}"
f" 上传图片至 {_path} 共 {len(img_list)} 张,失败 {len(failed_list)} 张,id={success_id[:-1]}"
)
if failed_list:
return (
f"这次一共为 {_path}库 添加了 {len(img_list) - len(failed_list)} 张图片\n"
f"依次的Id为:{success_id[:-1]}\n上传失败:{failed_result[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW"
)
else:
return f"这次一共为 {_path}库 添加了 {len(img_list)} 张图片\n依次的Id为:" \
f"{success_id[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW"
from configs.config import NICKNAME
from typing import List
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.utils import cn2py
from pathlib import Path
from utils.http_utils import AsyncHttpx
import os
async def upload_image_to_local(
img_list: List[str], path: str, user_id: int, group_id: int = 0
) -> str:
_path = path
path = Path(IMAGE_PATH) / cn2py(path)
path.mkdir(parents=True, exist_ok=True)
img_id = len(os.listdir(path))
failed_list = []
success_id = ""
for img_url in img_list:
if await AsyncHttpx.download_file(img_url, path / f"{img_id}.jpg"):
success_id += str(img_id) + ","
img_id += 1
else:
failed_list.append(img_url)
failed_result = ""
for img in failed_list:
failed_result += str(img) + "\n"
logger.info(
f"USER {user_id} GROUP {group_id}"
f" 上传图片至 {_path} 共 {len(img_list)} 张,失败 {len(failed_list)} 张,id={success_id[:-1]}"
)
if failed_list:
return (
f"这次一共为 {_path}库 添加了 {len(img_list) - len(failed_list)} 张图片\n"
f"依次的Id为:{success_id[:-1]}\n上传失败:{failed_result[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW"
)
else:
return (
f"这次一共为 {_path}库 添加了 {len(img_list)} 张图片\n依次的Id为:"
f"{success_id[:-1]}\n{NICKNAME}感谢您对图库的扩充!WW"
)
-52
View File
@@ -1,52 +0,0 @@
from nonebot import on_command
from utils.user_agent import get_user_agent
from services.log import logger
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
import aiohttp
from asyncio.exceptions import TimeoutError
from configs.config import Config
__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):
params = {"format": "json", "token": f"{Config.get_config('alapi', 'ALAPI_TOKEN')}"}
try:
async with aiohttp.ClientSession(headers=get_user_agent()) as session:
async with session.get(url, timeout=7, params=params) as response:
if response.status == 200:
data = await response.json()
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"]
)
else:
await jitang.send("鸡汤煮坏掉了...")
except TimeoutError:
await jitang.send("鸡汤煮超时了##", at_sender=True)
Regular → Executable
View File
Regular → Executable
+14 -34
View File
@@ -1,17 +1,16 @@
from nonebot import on_message, on_command
from nonebot.adapters.cqhttp import Bot, GroupMessageEvent
from nonebot.adapters.cqhttp.permission import GROUP
from utils.utils import get_message_text, is_number, get_message_imgs, get_local_proxy
from utils.utils import get_message_text, is_number, get_message_imgs
from nonebot.typing import T_State
from asyncio.exceptions import TimeoutError
import time
from nonebot.adapters.cqhttp.exception import ActionFailed
from configs.path_config import DATA_PATH, IMAGE_PATH
from configs.path_config import DATA_PATH, TEMP_PATH
from utils.image_utils import get_img_hash
from services.log import logger
from configs.config import NICKNAME, Config
import aiohttp
import aiofiles
from utils.http_utils import AsyncHttpx
from pathlib import Path
import time
try:
import ujson as json
@@ -36,25 +35,13 @@ __plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {"admin_level": Config.get_config("mute", "MUTE_LEVEL")}
__plugin_configs__ = {
"MUTE_LEVEL [LEVEL]": {
"value": 5,
"help": "更改禁言设置的管理权限",
"default_value": 5
},
"MUTE_DEFAULT_COUNT": {
"value": 10,
"help": "刷屏禁言默认检测次数",
"default_value": 10
},
"MUTE_DEFAULT_TIME": {
"value": 7,
"help": "刷屏检测默认规定时间",
"default_value": 7
},
"MUTE_LEVEL [LEVEL]": {"value": 5, "help": "更改禁言设置的管理权限", "default_value": 5},
"MUTE_DEFAULT_COUNT": {"value": 10, "help": "刷屏禁言默认检测次数", "default_value": 10},
"MUTE_DEFAULT_TIME": {"value": 7, "help": "刷屏检测默认规定时间", "default_value": 7},
"MUTE_DEFAULT_DURATION": {
"value": 10,
"help": "刷屏检测默禁言时长(分钟)",
"default_value": 10
"default_value": 10,
},
}
@@ -85,18 +72,11 @@ def save_data():
async def download_img_and_hash(url, group_id):
try:
async with aiohttp.ClientSession() as session:
async with session.get(
url, proxy=get_local_proxy(), timeout=10
) as response:
async with aiofiles.open(
IMAGE_PATH + f"temp/mute_{group_id}_img.jpg", "wb"
) as f:
await f.write(await response.read())
return str(get_img_hash(IMAGE_PATH + f"temp/mute_{group_id}_img.jpg"))
except TimeoutError:
return ""
if await AsyncHttpx.download_file(
url, Path(TEMP_PATH) / f"mute_{group_id}_img.jpg"
):
return str(get_img_hash(Path(TEMP_PATH) / f"mute_{group_id}_img.jpg"))
return ""
mute_dict = {}
Regular → Executable
View File
Regular → Executable
+63 -66
View File
@@ -1,66 +1,63 @@
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import get_message_text
from services.log import logger
import ujson as json
import aiohttp
__zx_plugin_name__ = "能不能好好说话"
__plugin_usage__ = """
usage:
说人话
指令:
nbnhhsh [文本]
""".strip()
__plugin_des__ = "能不能好好说话,说人话"
__plugin_cmd__ = ["nbnhhsh [文本]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["能不能好好说话", "nbnhhsh"],
}
HHSH_GUESS_URL = "https://lab.magiconch.com/api/nbnhhsh/guess"
nbnhhsh = on_command("nbnhhsh", aliases={"能不能好好说话"}, priority=5, block=True)
@nbnhhsh.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if not msg:
await nbnhhsh.finish('没话说就别说话!')
async with aiohttp.ClientSession(
headers={"content-type": "application/json"}
) as session:
async with session.post(
HHSH_GUESS_URL, data=json.dumps({"text": msg}), timeout=5
) as response:
if response.status == 200:
try:
data = await response.json()
tmp = ""
rst = ""
for x in data:
trans = ""
if x.get("trans"):
trans = x["trans"][0]
elif x.get("inputting"):
trans = ",".join(x["inputting"])
tmp += f'{x["name"]} -> {trans}\n'
rst += trans
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送能不能好好说话: {msg} -> {rst}"
)
await nbnhhsh.send(f"{tmp}={rst}", at_sender=True)
except (IndexError, KeyError):
await nbnhhsh.finish("没有找到对应的翻译....")
else:
await nbnhhsh.finish("网络访问失败了....")
from nonebot import on_command
from nonebot.typing import T_State
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from utils.utils import get_message_text
from utils.http_utils import AsyncHttpx
from services.log import logger
import ujson as json
__zx_plugin_name__ = "能不能好好说话"
__plugin_usage__ = """
usage:
说人话
指令:
nbnhhsh [文本]
""".strip()
__plugin_des__ = "能不能好好说话,说人话"
__plugin_cmd__ = ["nbnhhsh [文本]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["能不能好好说话", "nbnhhsh"],
}
HHSH_GUESS_URL = "https://lab.magiconch.com/api/nbnhhsh/guess"
nbnhhsh = on_command("nbnhhsh", aliases={"能不能好好说话"}, priority=5, block=True)
@nbnhhsh.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
msg = get_message_text(event.json())
if not msg:
await nbnhhsh.finish("没话说就别说话!")
response = await AsyncHttpx.post(
HHSH_GUESS_URL,
data=json.dumps({"text": msg}),
timeout=5,
headers={"content-type": "application/json"},
)
try:
data = response.json()
tmp = ""
rst = ""
for x in data:
trans = ""
if x.get("trans"):
trans = x["trans"][0]
elif x.get("inputting"):
trans = ",".join(x["inputting"])
tmp += f'{x["name"]} -> {trans}\n'
rst += trans
logger.info(
f"(USER {event.user_id}, GROUP "
f"{event.group_id if isinstance(event, GroupMessageEvent) else 'private'})"
f" 发送能不能好好说话: {msg} -> {rst}"
)
await nbnhhsh.send(f"{tmp}={rst}", at_sender=True)
except (IndexError, KeyError):
await nbnhhsh.finish("没有找到对应的翻译....")
+5 -5
View File
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
from typing import Dict
from aiohttp.client_exceptions import ClientError
from nonebot.plugin import on_command, on_message
from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent
from nonebot.typing import T_State
@@ -127,8 +126,8 @@ async def get_setu(bot: Bot, event: MessageEvent, state: T_State):
except IndexError:
# await bot.send(event, traceback.format_exc())
await setu.finish("参数错误")
except ClientError:
await setu.finish("连接失败")
# except ClientError:
# await setu.finish("连接失败")
pic_map: Dict[str, str] = {} # 保存这个群的其阿金一张色图 {"123456":http://xxx"}
@@ -174,7 +173,8 @@ async def handle_previous(bot: Bot, event: GroupMessageEvent, state: T_State):
idx += 1
except IndexError:
await previous.finish("参数错误")
except ClientError:
await previous.finish("连接错误")
except KeyError:
await previous.finish("没有图啊QAQ")
except Exception as e:
logger.error(f"识图未知错误 {type(e)}:{e}")
await previous.finish("未知错误...")
View File
View File
View File
View File
+1 -1
View File
@@ -79,7 +79,7 @@ async def get_pic_from_url(url: str):
data = FormData(boundary="----WebKitFormBoundaryPpuR3EZ1Ap2pXv8W")
data.add_field(name="file", value=content, content_type="image/jpeg",
filename="blob")
async with session.post("https://saucenao.com/search.php", data=data, headers=header) as res:
async with session.post("https://saucenao.com/search.php", proxy=get_local_proxy(), data=data, headers=header) as res:
html = await res.text()
image_data = [each for each in parse_html(html)]
return image_data
View File
View File
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
View File
+2 -2
View File
@@ -1,2 +1,2 @@
from .open_cases_user import *
from .buff_prices import *
from .open_cases_user import *
from .buff_prices import *
View File
View File
View File
Regular → Executable
+179 -213
View File
@@ -1,10 +1,8 @@
from .models.buff_prices import BuffPrice
from services.db_context import db
from datetime import datetime, timedelta
from utils.user_agent import get_user_agent
from configs.path_config import IMAGE_PATH
import aiohttp
import aiofiles
from utils.http_utils import AsyncHttpx
from .models.open_cases_user import OpenCasesUser
import os
from services.log import logger
@@ -43,127 +41,119 @@ async def util_get_buff_price(case_name: str = "狂牙大行动") -> str:
CASE_PINK = eval(case + "_CASE_PINK")
CASE_PURPLE = eval(case + "_CASE_PURPLE")
CASE_BLUE = eval(case + "_CASE_BLUE")
async with aiohttp.ClientSession(
cookies=cookie, headers=get_user_agent()
) as session:
for total_list in [CASE_KNIFE, CASE_RED, CASE_PINK, CASE_PURPLE, CASE_BLUE]:
for skin in total_list:
if skin in [
"蝴蝶刀 | 无涂装",
"求生匕首 | 无涂装",
"流浪者匕首 | 无涂装",
"系绳匕首 | 无涂装",
"骷髅匕首 | 无涂装",
]:
skin = skin.split("|")[0].strip()
async with db.transaction():
name_list = []
price_list = []
parameter = {"game": "csgo", "page_num": "1", "search": skin}
try:
async with session.get(
url,
proxy=Config.get_config("open_cases", "BUFF_PROXY"),
params=parameter,
timeout=20,
) as response:
if response.status == 200:
data = (await response.json())["data"]
total_page = data["total_page"]
data = data["items"]
flag = False
if (
skin.find("|") == -1
): # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']:
for i in range(1, total_page + 1):
name_list = []
price_list = []
parameter = {
"game": "csgo",
"page_num": f"{i}",
"search": skin,
}
async with session.get(
url, params=parameter, timeout=20
) as res:
data = (await response.json())["data"][
"items"
]
for j in range(len(data)):
if data[j]["name"] in [f"{skin}(★)"]:
name = data[j]["name"]
price = data[j][
"sell_reference_price"
]
name_list.append(
name.split("(")[0].strip()
+ " | 无涂装"
)
price_list.append(price)
flag = True
break
if flag:
break
else:
try:
for _ in range(total_page):
for i in range(len(data)):
name = data[i]["name"]
price = data[i]["sell_reference_price"]
name_list.append(name)
price_list.append(price)
except Exception as e:
failed_list.append(skin)
logger.warning(f"{skin}更新失败")
else:
for total_list in [CASE_KNIFE, CASE_RED, CASE_PINK, CASE_PURPLE, CASE_BLUE]:
for skin in total_list:
if skin in [
"蝴蝶刀 | 无涂装",
"求生匕首 | 无涂装",
"流浪者匕首 | 无涂装",
"系绳匕首 | 无涂装",
"骷髅匕首 | 无涂装",
]:
skin = skin.split("|")[0].strip()
async with db.transaction():
name_list = []
price_list = []
parameter = {"game": "csgo", "page_num": "1", "search": skin}
try:
response = await AsyncHttpx.get(url, proxy=Config.get_config("open_cases", "BUFF_PROXY"),
params=parameter,
cookies=cookie,)
if response.status_code == 200:
data = response.json()["data"]
total_page = data["total_page"]
data = data["items"]
flag = False
if (
skin.find("|") == -1
): # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']:
for i in range(1, total_page + 1):
name_list = []
price_list = []
parameter = {
"game": "csgo",
"page_num": f"{i}",
"search": skin,
}
res = await AsyncHttpx.get(url, params=parameter)
data = res.json()["data"][
"items"
]
for j in range(len(data)):
if data[j]["name"] in [f"{skin}(★)"]:
name = data[j]["name"]
price = data[j][
"sell_reference_price"
]
name_list.append(
name.split("(")[0].strip()
+ " | 无涂装"
)
price_list.append(price)
flag = True
break
if flag:
break
else:
try:
for _ in range(total_page):
for i in range(len(data)):
name = data[i]["name"]
price = data[i]["sell_reference_price"]
name_list.append(name)
price_list.append(price)
except Exception as e:
failed_list.append(skin)
logger.warning(f"{skin}更新失败")
except Exception:
else:
failed_list.append(skin)
logger.warning(f"{skin}更新失败")
except Exception:
failed_list.append(skin)
logger.warning(f"{skin}更新失败")
continue
for i in range(len(name_list)):
name = name_list[i].strip()
price = float(price_list[i])
if name.find("(★)") != -1:
name = name[: name.find("(")] + name[name.find(")") + 1 :]
if name.find("消音") != -1 and name.find("(S") != -1:
name = name.split("(")[0][:-4] + "(" + name.split("(")[1]
name = (
name.split("|")[0].strip()
+ " | "
+ name.split("|")[1].strip()
)
elif name.find("消音") != -1:
name = (
name.split("|")[0][:-5].strip()
+ " | "
+ name.split("|")[1].strip()
)
if name.find(" 18 ") != -1 and name.find("(S") != -1:
name = name.split("(")[0][:-5] + "(" + name.split("(")[1]
name = (
name.split("|")[0].strip()
+ " | "
+ name.split("|")[1].strip()
)
elif name.find(" 18 ") != -1:
name = (
name.split("|")[0][:-6].strip()
+ " | "
+ name.split("|")[1].strip()
)
dbskin = await BuffPrice.ensure(name, True)
if (
dbskin.update_date + timedelta(8)
).date() == datetime.now().date():
continue
for i in range(len(name_list)):
name = name_list[i].strip()
price = float(price_list[i])
if name.find("(★)") != -1:
name = name[: name.find("(")] + name[name.find(")") + 1 :]
if name.find("消音") != -1 and name.find("(S") != -1:
name = name.split("(")[0][:-4] + "(" + name.split("(")[1]
name = (
name.split("|")[0].strip()
+ " | "
+ name.split("|")[1].strip()
)
elif name.find("消音") != -1:
name = (
name.split("|")[0][:-5].strip()
+ " | "
+ name.split("|")[1].strip()
)
if name.find(" 18 ") != -1 and name.find("(S") != -1:
name = name.split("(")[0][:-5] + "(" + name.split("(")[1]
name = (
name.split("|")[0].strip()
+ " | "
+ name.split("|")[1].strip()
)
elif name.find(" 18 ") != -1:
name = (
name.split("|")[0][:-6].strip()
+ " | "
+ name.split("|")[1].strip()
)
dbskin = await BuffPrice.ensure(name, True)
if (
dbskin.update_date + timedelta(8)
).date() == datetime.now().date():
continue
await dbskin.update(
case_id=case_id,
skin_price=price,
update_date=datetime.now(),
).apply()
logger.info(f"{name_list[i]}---------->成功更新")
await dbskin.update(
case_id=case_id,
skin_price=price,
update_date=datetime.now(),
).apply()
logger.info(f"{name_list[i]}---------->成功更新")
result = None
if failed_list:
result = ""
@@ -187,112 +177,88 @@ async def util_get_buff_img(case_name: str = "狂牙大行动") -> str:
CASE_PINK = eval(case + "_CASE_PINK")
CASE_PURPLE = eval(case + "_CASE_PURPLE")
CASE_BLUE = eval(case + "_CASE_BLUE")
async with aiohttp.ClientSession(
cookies=cookie, headers=get_user_agent()
) as session:
for total_list in [CASE_KNIFE, CASE_RED, CASE_PINK, CASE_PURPLE, CASE_BLUE]:
for skin in total_list:
parameter = {"game": "csgo", "page_num": "1", "search": skin}
if skin in [
"蝴蝶刀 | 无涂装",
"求生匕首 | 无涂装",
"流浪者匕首 | 无涂装",
"系绳匕首 | 无涂装",
"骷髅匕首 | 无涂装",
]:
skin = skin.split("|")[0].strip()
logger.info(f"开始更新----->{skin}")
skin_name = ""
# try:
async with session.get(
url,
proxy=Config.get_config("open_cases", "BUFF_PROXY"),
params=parameter,
timeout=20,
) as response:
if response.status == 200:
data = (await response.json())["data"]
total_page = data["total_page"]
flag = False
if (
skin.find("|") == -1
): # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']:
for i in range(1, total_page + 1):
async with session.get(
url, params=parameter, timeout=20
) as res:
data = (await response.json())["data"]["items"]
for j in range(len(data)):
if data[j]["name"] in [f"{skin}(★)"]:
img_url = data[j]["goods_info"]["icon_url"]
for k in pypinyin.pinyin(
skin + "无涂装", style=pypinyin.NORMAL
):
skin_name += "".join(k)
async with aiofiles.open(
IMAGE_PATH + path + skin_name + ".png",
"wb",
) as f:
logger.info(f"------->开始写入{skin}")
async with session.get(
img_url, timeout=7
) as res:
await f.write(await res.read())
flag = True
break
if flag:
break
else:
img_url = (await response.json())["data"]["items"][0][
"goods_info"
]["icon_url"]
for i in pypinyin.pinyin(
skin.replace("|", "-").strip(), style=pypinyin.NORMAL
):
skin_name += "".join(i)
async with aiofiles.open(
IMAGE_PATH + path + skin_name + ".png", "wb"
) as f:
logger.info(f"------->开始写入 {skin}")
async with session.get(img_url, timeout=7) as res:
await f.write(await res.read())
for total_list in [CASE_KNIFE, CASE_RED, CASE_PINK, CASE_PURPLE, CASE_BLUE]:
for skin in total_list:
parameter = {"game": "csgo", "page_num": "1", "search": skin}
if skin in [
"蝴蝶刀 | 无涂装",
"求生匕首 | 无涂装",
"流浪者匕首 | 无涂装",
"系绳匕首 | 无涂装",
"骷髅匕首 | 无涂装",
]:
skin = skin.split("|")[0].strip()
logger.info(f"开始更新----->{skin}")
skin_name = ""
# try:
response = await AsyncHttpx.get(url, proxy=Config.get_config("open_cases", "BUFF_PROXY"), params=parameter)
if response.status_code == 200:
data = response.json()["data"]
total_page = data["total_page"]
flag = False
if (
skin.find("|") == -1
): # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']:
for i in range(1, total_page + 1):
res = await AsyncHttpx.get(url, params=parameter)
data = res.json()["data"]["items"]
for j in range(len(data)):
if data[j]["name"] in [f"{skin}(★)"]:
img_url = data[j]["goods_info"]["icon_url"]
for k in pypinyin.pinyin(
skin + "无涂装", style=pypinyin.NORMAL
):
skin_name += "".join(k)
await AsyncHttpx.download_file(img_url, IMAGE_PATH + path + skin_name + ".png")
flag = True
break
if flag:
break
else:
img_url = (await response.json())["data"]["items"][0][
"goods_info"
]["icon_url"]
for i in pypinyin.pinyin(
skin.replace("|", "-").strip(), style=pypinyin.NORMAL
):
skin_name += "".join(i)
if await AsyncHttpx.download_file(img_url, IMAGE_PATH + path + skin_name + ".png"):
logger.info(f"------->写入 {skin} 成功")
else:
logger.info(f"------->写入 {skin} 失败")
result = None
if error_list:
result = ""
for errskin in error_list:
result += errskin + "\n"
for err_skin in error_list:
result += err_skin + "\n"
return result[:-1] if result else "更新图片成功"
async def get_price(dname):
async def get_price(d_name):
cookie = {"session": Config.get_config("open_cases", "COOKIE")}
name_list = []
price_list = []
parameter = {"game": "csgo", "page_num": "1", "search": dname}
parameter = {"game": "csgo", "page_num": "1", "search": d_name}
try:
async with aiohttp.ClientSession(
cookies=cookie, headers=get_user_agent()
) as session:
async with session.get(url, params=parameter, timeout=7) as response:
if response.status == 200:
try:
data = (await response.json())["data"]
total_page = data["total_page"]
data = data["items"]
for _ in range(total_page):
for i in range(len(data)):
name = data[i]["name"]
price = data[i]["sell_reference_price"]
name_list.append(name)
price_list.append(price)
except Exception as e:
return "没有查询到...", 998
else:
return "访问失败!", response.status
response = await AsyncHttpx.get(url, cookies=cookie, params=parameter)
if response.status_code == 200:
try:
data = response.json()["data"]
total_page = data["total_page"]
data = data["items"]
for _ in range(total_page):
for i in range(len(data)):
name = data[i]["name"]
price = data[i]["sell_reference_price"]
name_list.append(name)
price_list.append(price)
except Exception as e:
return "没有查询到...", 998
else:
return "访问失败!", response.status_code
except TimeoutError as e:
return "访问超时! 请重试或稍后再试!", 997
result = f"皮肤: {dname}({len(name_list)})\n"
# result = "皮肤: " + dname + "\n"
result = f"皮肤: {d_name}({len(name_list)})\n"
for i in range(len(name_list)):
result += name_list[i] + ": " + price_list[i] + "\n"
return result[:-1], 999
Regular → Executable
+7 -12
View File
@@ -11,9 +11,9 @@ from nonebot.adapters.cqhttp.exception import ActionFailed
from utils.image_utils import CreateImg
from utils.browser import get_browser
from configs.path_config import IMAGE_PATH
from utils.http_utils import AsyncHttpx
import asyncio
import time
import aiohttp
from bilibili_api import settings
from utils.manager import group_manager
import ujson as json
@@ -51,17 +51,12 @@ async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
if data:
# 转发视频
if data.get("desc") == "哔哩哔哩":
async with aiohttp.ClientSession(
headers=get_user_agent()
) as session:
async with session.get(
data["meta"]["detail_1"]["qqdocurl"],
proxy=get_local_proxy(),
timeout=7,
) as response:
url = str(response.url).split("?")[0]
bvid = url.split("/")[-1]
vd_info = await video.Video(bvid=bvid).get_info()
response = await AsyncHttpx.get(
data["meta"]["detail_1"]["qqdocurl"], timeout=7
)
url = str(response.url).split("?")[0]
bvid = url.split("/")[-1]
vd_info = await video.Video(bvid=bvid).get_info()
# 转发专栏
if (
data.get("meta")
Regular → Executable
+106 -124
View File
@@ -1,124 +1,106 @@
from asyncio.exceptions import TimeoutError
import aiofiles
import aiohttp
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.typing import T_State
from configs.path_config import IMAGE_PATH
from services.log import logger
from utils.message_builder import image
from utils.utils import get_message_text, is_number
from utils.manager import withdraw_message_manager
try:
import ujson as json
except ModuleNotFoundError:
import json
__zx_plugin_name__ = "pid搜索"
__plugin_usage__ = """
usage:
通过 pid 搜索图片
指令:
p搜 [pid]
""".strip()
__plugin_des__ = "通过 pid 搜索图片"
__plugin_cmd__ = ["p搜 [pid]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["p搜"],
}
pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True)
url = "https://api.fantasyzone.cc/tu/search.php"
@pid_search.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
pid = get_message_text(event.json())
if pid:
if pid in ["取消", "算了"]:
await pid_search.finish("已取消操作...")
if not is_number(pid):
await pid_search.reject("笨蛋,重新输入数!字!", at_sender=True)
state["pid"] = pid
@pid_search.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
pid = get_message_text(event.json())
if pid:
state["pid"] = pid
@pid_search.got("pid", prompt="需要查询的图片PID是?")
async def _(bot: Bot, event: MessageEvent, state: T_State):
pid = state["pid"]
params = {
"id": pid,
"p": 1,
}
async with aiohttp.ClientSession() as session:
for _ in range(10):
try:
async with session.get(url, timeout=2, params=params) as response:
data = json.loads(await response.text())
except TimeoutError:
pass
else:
if not data["width"] and not data["height"]:
await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True)
pid = data["id"]
title = data["title"]
author = data["userName"]
author_id = data["userId"]
img_url = data["url"]
for _ in range(5):
try:
await download_pic(img_url, event.user_id)
except TimeoutError:
pass
else:
break
else:
await pid_search.finish("图片下载失败了....", at_sender=True)
tmp = ""
if isinstance(event, GroupMessageEvent):
tmp = "\n【注】将在30后撤回......"
msg_id = await pid_search.send(
Message(
f"title:{title}\n"
f"pid:{pid}\n"
f"author:{author}\n"
f"author_id:{author_id}\n"
f'{image(f"pid_search_{event.user_id}.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)
async def download_pic(img_url: str, user_id: int):
async with aiohttp.ClientSession() as session:
async with session.get(img_url, timeout=2) as res:
async with aiofiles.open(
f"{IMAGE_PATH}/temp/pid_search_{user_id}.png", "wb"
) as f:
await f.write(await res.read())
from asyncio.exceptions import TimeoutError
from nonebot import on_command
from nonebot.adapters.cqhttp import Bot, MessageEvent, Message, GroupMessageEvent
from nonebot.typing import T_State
from configs.path_config import TEMP_PATH
from services.log import logger
from utils.message_builder import image
from utils.utils import get_message_text, is_number
from utils.manager import withdraw_message_manager
from utils.http_utils import AsyncHttpx
from pathlib import Path
try:
import ujson as json
except ModuleNotFoundError:
import json
__zx_plugin_name__ = "pid搜索"
__plugin_usage__ = """
usage:
通过 pid 搜索图片
指令:
p搜 [pid]
""".strip()
__plugin_des__ = "通过 pid 搜索图片"
__plugin_cmd__ = ["p搜 [pid]"]
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
__plugin_settings__ = {
"level": 5,
"default_status": True,
"limit_superuser": False,
"cmd": ["p搜"],
}
pid_search = on_command("p搜", aliases={"pixiv搜", "P搜"}, priority=5, block=True)
url = "https://api.fantasyzone.cc/tu/search.php"
@pid_search.args_parser
async def _(bot: Bot, event: MessageEvent, state: T_State):
pid = get_message_text(event.json())
if pid:
if pid in ["取消", "算了"]:
await pid_search.finish("已取消操作...")
if not is_number(pid):
await pid_search.reject("笨蛋,重新输入数!字!", at_sender=True)
state["pid"] = pid
@pid_search.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
pid = get_message_text(event.json())
if pid:
state["pid"] = pid
@pid_search.got("pid", prompt="需要查询的图片PID是?")
async def _(bot: Bot, event: MessageEvent, state: T_State):
pid = state["pid"]
params = {
"id": pid,
"p": 1,
}
for _ in range(10):
try:
data = (await AsyncHttpx.get(url, params=params, timeout=5)).json()
except TimeoutError:
pass
else:
if not data["width"] and not data["height"]:
await pid_search.finish(f"没有搜索到 PID:{pid} 的图片", at_sender=True)
pid = data["id"]
title = data["title"]
author = data["userName"]
author_id = data["userId"]
img_url = data["url"]
if not await AsyncHttpx.download_file(
img_url, Path(TEMP_PATH) / f"pid_search_{event.user_id}.png"
):
await pid_search.finish("图片下载失败了....", at_sender=True)
tmp = ""
if isinstance(event, GroupMessageEvent):
tmp = "\n【注】将在30后撤回......"
msg_id = await pid_search.send(
Message(
f"title:{title}\n"
f"pid:{pid}\n"
f"author:{author}\n"
f"author_id:{author_id}\n"
f'{image(f"pid_search_{event.user_id}.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)
Regular → Executable
+135 -62
View File
@@ -1,62 +1,135 @@
from configs.config import Config
import nonebot
__zx_plugin_name__ = "更新扩展图库Omega [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
Config.add_plugin_config(
"hibiapi",
"HIBIAPI",
"https://api.obfs.dev",
help_="如果没有自建或其他hibiapi请不要修改",
default_value="https://api.obfs.dev",
)
Config.add_plugin_config(
"pixiv",
"PIXIV_NGINX_URL",
"i.pixiv.re",
help_="Pixiv反向代理"
)
Config.add_plugin_config(
"pix",
"PIX_IMAGE_SIZE",
"master",
name="PIX图库",
help_="PIX图库下载的画质 可能的值:original:原图,master:缩略图(加快发送速度)",
default_value="master"
)
Config.add_plugin_config(
"pix",
"SEARCH_HIBIAPI_BOOKMARKS",
5000,
help_="最低收藏,PIX使用HIBIAPI搜索图片时达到最低收藏才会添加至图库",
default_value=5000
)
Config.add_plugin_config(
"pix",
"WITHDRAW_PIX_MESSAGE",
(0, 1),
help_="自动撤回,参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)",
default_value=(0, 1)
)
Config.add_plugin_config(
"pix",
"PIX_OMEGA_PIXIV_RATIO",
(10, 0),
help_="PIX图库 与 额外图库OmegaPixivIllusts 混合搜索的比例 参1:PIX图库 参2:OmegaPixivIllusts扩展图库(没有此图库请设置为0)",
default_value=(10, 0)
)
Config.add_plugin_config(
"pix",
"TIMEOUT",
10,
help_="下载图片超时限制(秒)",
default_value=10
)
nonebot.load_plugins("plugins/pix_gallery")
from configs.config import Config
from services.log import logger
from .model.omega_pixiv_illusts import OmegaPixivIllusts
from pathlib import Path
from nonebot import Driver
from typing import List
from datetime import datetime
import nonebot
import asyncio
import os
__zx_plugin_name__ = "更新扩展图库Omega [Hidden]"
__plugin_version__ = 0.1
__plugin_author__ = "HibiKier"
Config.add_plugin_config(
"hibiapi",
"HIBIAPI",
"https://api.obfs.dev",
help_="如果没有自建或其他hibiapi请不要修改",
default_value="https://api.obfs.dev",
)
Config.add_plugin_config(
"pixiv",
"PIXIV_NGINX_URL",
"i.pixiv.re",
help_="Pixiv反向代理"
)
Config.add_plugin_config(
"pix",
"PIX_IMAGE_SIZE",
"master",
name="PIX图库",
help_="PIX图库下载的画质 可能的值:original:原图,master:缩略图(加快发送速度)",
default_value="master"
)
Config.add_plugin_config(
"pix",
"SEARCH_HIBIAPI_BOOKMARKS",
5000,
help_="最低收藏,PIX使用HIBIAPI搜索图片时达到最低收藏才会添加至图库",
default_value=5000
)
Config.add_plugin_config(
"pix",
"WITHDRAW_PIX_MESSAGE",
(0, 1),
help_="自动撤回,参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)",
default_value=(0, 1)
)
Config.add_plugin_config(
"pix",
"PIX_OMEGA_PIXIV_RATIO",
(10, 0),
help_="PIX图库 与 额外图库OmegaPixivIllusts 混合搜索的比例 参1:PIX图库 参2:OmegaPixivIllusts扩展图库(没有此图库请设置为0)",
default_value=(10, 0)
)
Config.add_plugin_config(
"pix",
"TIMEOUT",
10,
help_="下载图片超时限制(秒)",
default_value=10
)
nonebot.load_plugins("plugins/pix_gallery")
driver: Driver = nonebot.get_driver()
@driver.on_startup
async def _init_omega_pixiv_illusts():
omega_pixiv_illusts = None
for file in os.listdir("."):
if "omega_pixiv_illusts" in file and ".sql" in file:
omega_pixiv_illusts = Path() / file
if omega_pixiv_illusts:
with open(omega_pixiv_illusts, "r", encoding="utf8") as f:
lines = f.readlines()
tasks = []
length = len([x for x in lines if "INSERT INTO" in x.upper()])
all_pid = await OmegaPixivIllusts.get_all_pid()
index = 0
logger.info("检测到OmegaPixivIllusts数据库,准备开始更新....")
for line in lines:
if "INSERT INTO" in line.upper():
index += 1
tasks.append(
asyncio.ensure_future(_tasks(line, all_pid, length, index))
)
await asyncio.gather(*tasks)
omega_pixiv_illusts.unlink()
async def _tasks(line: str, all_pid: List[int], length: int, index: int):
data = line.split("VALUES", maxsplit=1)[-1].strip()
if data.startswith("("):
data = data[1:]
if data.endswith(");"):
data = data[:-2]
x = data.split(maxsplit=3)
pid = int(x[1][:-1].strip())
if pid in all_pid:
logger.info(f"添加OmegaPixivIllusts图库数据已存在 ---> pid:{pid}")
return
uid = int(x[2][:-1].strip())
x = x[3].split(", '")
title = x[0].strip()[1:-1]
tmp = x[1].split(", ")
author = tmp[0].strip()[:-1]
nsfw_tag = int(tmp[1])
width = int(tmp[2])
height = int(tmp[3])
tags = x[2][:-1]
url = x[3][:-1]
if await OmegaPixivIllusts.add_image_data(
pid,
title,
width,
height,
url,
uid,
author,
nsfw_tag,
tags,
datetime.min,
datetime.min,
):
logger.info(
f"成功添加OmegaPixivIllusts图库数据 pid:{pid} 本次预计存储 {length} 张,已更新第 {index} 张"
)
else:
logger.info(f"添加OmegaPixivIllusts图库数据已存在 ---> pid:{pid}")
+392 -452
View File
@@ -1,452 +1,392 @@
from aiohttp.client_exceptions import (
ClientOSError,
ServerDisconnectedError,
ClientConnectorError,
)
from asyncpg.exceptions import UniqueViolationError
from .model.omega_pixiv_illusts import OmegaPixivIllusts
from asyncio.locks import Semaphore
from aiohttp import ClientPayloadError
from aiohttp.client import ClientSession
from asyncio.exceptions import TimeoutError
from .model.pixiv import Pixiv
from typing import List
from utils.utils import get_local_proxy, change_picture_links
from utils.image_utils import CreateImg
from services.log import logger
from configs.config import Config
from configs.path_config import TEMP_PATH
import platform
import aiohttp
import asyncio
import aiofiles
import math
try:
import ujson as json
except ModuleNotFoundError:
import json
if str(platform.system()).lower() == "windows":
policy = asyncio.WindowsSelectorEventLoopPolicy()
asyncio.set_event_loop_policy(policy)
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;"
" rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
"Referer": "https://www.pixiv.net",
}
HIBIAPI = None
async def start_update_image_url(
current_keyword: List[str], black_pid: List[str]
) -> "int, int":
"""
开始更新图片url
:param current_keyword: 关键词
:param black_pid: 黑名单pid
:return: pid数量和图片数量
"""
global HIBIAPI
pid_count = 0
pic_count = 0
tasks = []
semaphore = asyncio.Semaphore(10)
if not HIBIAPI:
HIBIAPI = Config.get_config("hibiapi", "HIBIAPI")
HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] == "/" else HIBIAPI
async with aiohttp.ClientSession(headers=headers) as session:
for keyword in current_keyword:
for page in range(1, 110):
if keyword.startswith("uid:"):
url = f"{HIBIAPI}/api/pixiv/member_illust"
params = {"id": keyword[4:], "page": page}
if page == 30:
break
elif keyword.startswith("pid:"):
url = f"{HIBIAPI}/api/pixiv/illust"
params = {"id": keyword[4:]}
else:
url = f"{HIBIAPI}/api/pixiv/search"
params = {"word": keyword, "page": page}
tasks.append(
asyncio.ensure_future(
search_image(
url, keyword, params, semaphore, session, page, black_pid
)
)
)
if keyword.startswith("pid:"):
break
result = await asyncio.gather(*tasks)
for x in result:
pid_count += x[0]
pic_count += x[1]
return pid_count, pic_count
async def search_image(
url: str,
keyword: str,
params: dict,
semaphore: Semaphore,
session: ClientSession,
page: int = 1,
black: List[str] = None,
) -> "int, int":
"""
搜索图片
:param url: 搜索url
:param keyword: 关键词
:param params: params参数
:param semaphore: semaphore
:param session: session
:param page: 页面
:param black: pid黑名单
:return: pid数量和图片数量
"""
tmp_pid = []
pic_count = 0
pid_count = 0
async with semaphore:
try:
async with session.get(
url,
params=params,
proxy=get_local_proxy(),
) as response:
data = await response.json()
if (
not data
or data.get("error")
or (not data.get("illusts") and not data.get("illust"))
):
return 0, 0
if url != f"{HIBIAPI}/api/pixiv/illust":
logger.info(f'{keyword}: 获取数据成功...数据总量:{len(data["illusts"])}')
data = data["illusts"]
else:
logger.info(f'获取数据成功...PID:{params.get("id")}')
data = [data["illust"]]
img_data = {}
for x in data:
pid = x["id"]
title = x["title"]
width = x["width"]
height = x["height"]
view = x["total_view"]
bookmarks = x["total_bookmarks"]
uid = x["user"]["id"]
author = x["user"]["name"]
tags = []
for tag in x["tags"]:
for i in tag:
if tag[i]:
tags.append(tag[i])
img_urls = []
if x["page_count"] == 1:
img_urls.append(x["meta_single_page"]["original_image_url"])
else:
for urls in x["meta_pages"]:
img_urls.append(urls["image_urls"]["original"])
if (
(
bookmarks
>= Config.get_config("pix", "SEARCH_HIBIAPI_BOOKMARKS")
or (
url == f"{HIBIAPI}/api/pixiv/member_illust"
and bookmarks >= 1500
)
or (url == f"{HIBIAPI}/api/pixiv/illust")
)
and len(img_urls) < 10
and _check_black(img_urls, black)
):
img_data[pid] = {
"pid": pid,
"title": title,
"width": width,
"height": height,
"view": view,
"bookmarks": bookmarks,
"img_urls": img_urls,
"uid": uid,
"author": author,
"tags": tags,
}
else:
continue
for x in img_data.keys():
data = img_data[x]
for img_url in data["img_urls"]:
img_p = img_url[img_url.rfind("_") + 1 : img_url.rfind(".")]
try:
if await Pixiv.add_image_data(
data["pid"],
data["title"],
data["width"],
data["height"],
data["view"],
data["bookmarks"],
img_url,
img_p,
data["uid"],
data["author"],
",".join(data["tags"]),
):
if data["pid"] not in tmp_pid:
pid_count += 1
tmp_pid.append(data["pid"])
pic_count += 1
logger.info(f'存储图片PID:{data["pid"]} IMG_P:{img_p}')
except UniqueViolationError:
logger.warning(f'{data["pid"]} | {img_url} 已存在...')
except (ServerDisconnectedError, ClientConnectorError, ClientOSError):
logger.warning("搜索图片服务被关闭,再次调用....")
await search_image(url, keyword, params, semaphore, session, page, black)
return pid_count, pic_count
# 下载图片
async def download_image(img_url: str, session: ClientSession, _count: int = 1):
"""
下载图片
:param img_url: 图片url
:param session: session
:param _count: 次数
"""
try:
async with session.get(img_url, proxy=get_local_proxy()) as response:
logger.info(f"下载图片 --> {img_url}")
async with aiofiles.open(f'tmp/{img_url.split("/")[-1]}', "wb") as f:
await f.write(await response.read())
except ServerDisconnectedError:
logger.warning(f"下载图片服务被关闭,第 {_count} 次调用....")
await download_image(img_url, session, _count + 1)
except ClientOSError:
logger.warning(f"远程连接被关闭,第 {_count} 次调用....")
ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL")
if ws_url:
img_url = img_url.replace("i.pximg.net", ws_url)
await download_image(img_url, session, _count + 1)
except TimeoutError:
logger.warning(f"下载或写入超时,第 {_count} 次调用....")
await download_image(img_url, session, _count + 1)
except ClientPayloadError:
pass
async def get_image(img_url: str, user_id: int) -> str:
"""
下载图片
:param img_url:
:param user_id:
:return: 图片名称
"""
global HIBIAPI
if not HIBIAPI:
HIBIAPI = Config.get_config("hibiapi", "HIBIAPI")
HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] == "/" else HIBIAPI
async with aiohttp.ClientSession(headers=headers) as session:
if "https://www.pixiv.net/artworks" in img_url:
pid = img_url.rsplit("/", maxsplit=1)[-1]
params = {"id": pid}
for _ in range(3):
try:
async with session.get(
f"{HIBIAPI}/api/pixiv/illust",
params=params,
proxy=get_local_proxy(),
) as response:
if response.status == 200:
data = await response.json()
if data.get("illust"):
if data["illust"]["page_count"] == 1:
img_url = data["illust"]["meta_single_page"][
"original_image_url"
]
else:
img_url = data["illust"]["meta_pages"][0][
"image_urls"
]["original"]
break
except (ClientConnectorError, TimeoutError):
pass
old_img_url = img_url
img_url = change_picture_links(
img_url, Config.get_config("pix", "PIX_IMAGE_SIZE")
)
ws_url = Config.get_config("pixiv", "PIXIV_NGINX_URL")
if ws_url:
if ws_url.startswith("http"):
ws_url = ws_url.split("//")[-1]
img_url = img_url.replace("i.pximg.net", ws_url).replace("i.pixiv.cat", ws_url)
for _ in range(3):
try:
async with session.get(
img_url,
proxy=get_local_proxy(),
timeout=Config.get_config("pix", "TIMEOUT"),
) as response:
if response.status == 404:
img_url = old_img_url
continue
async with aiofiles.open(
f"{TEMP_PATH}/pix_{user_id}_{img_url[-10:-4]}.jpg", "wb"
) as f:
await f.write(await response.read())
return f"pix_{user_id}_{img_url[-10:-4]}.jpg"
except (ClientConnectorError, TimeoutError):
pass
async def uid_pid_exists(id_: str) -> bool:
"""
检测 pid/uid 是否有效
:param id_: pid/uid
"""
if id_.startswith("uid:"):
url = f"{HIBIAPI}/api/pixiv/member"
elif id_.startswith("pid:"):
url = f"{HIBIAPI}/api/pixiv/illust"
else:
return False
params = {"id": int(id_[4:])}
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(url, params=params, proxy=get_local_proxy()) as response:
data = await response.json()
if data.get("error"):
return False
return True
async def get_keyword_num(keyword: str) -> "int, int, int, int, int":
"""
查看图片相关 tag 数量
:param keyword: 关键词tag
"""
count, r18_count = await Pixiv.get_keyword_num(keyword.split())
count_, setu_count, r18_count_ = await OmegaPixivIllusts.get_keyword_num(
keyword.split()
)
return count, r18_count, count_, setu_count, r18_count_
async def remove_image(pid: int, img_p: str) -> bool:
"""
删除置顶图片
:param pid: pid
:param img_p: 图片 p 如 p0,p1 等
"""
if img_p:
if "p" not in img_p:
img_p = f"p{img_p}"
return await Pixiv.remove_image_data(pid, img_p)
def gen_keyword_pic(
_pass_keyword: List[str], not_pass_keyword: List[str], is_superuser: bool
):
"""
已通过或未通过的所有关键词/uid/pid
:param _pass_keyword: 通过列表
:param not_pass_keyword: 未通过列表
:param is_superuser: 是否超级用户
"""
_keyword = [
x
for x in _pass_keyword
if not x.startswith("uid:")
and not x.startswith("pid:")
and not x.startswith("black:")
]
_uid = [x for x in _pass_keyword if x.startswith("uid:")]
_pid = [x for x in _pass_keyword if x.startswith("pid:")]
_n_keyword = [
x
for x in not_pass_keyword
if not x.startswith("uid:")
and not x.startswith("pid:")
and not x.startswith("black:")
]
_n_uid = [
x
for x in not_pass_keyword
if x.startswith("uid:") and not x.startswith("black:")
]
_n_pid = [
x
for x in not_pass_keyword
if x.startswith("pid:") and not x.startswith("black:")
]
img_width = 0
img_data = {
"_keyword": {"width": 0, "data": _keyword},
"_uid": {"width": 0, "data": _uid},
"_pid": {"width": 0, "data": _pid},
"_n_keyword": {"width": 0, "data": _n_keyword},
"_n_uid": {"width": 0, "data": _n_uid},
"_n_pid": {"width": 0, "data": _n_pid},
}
for x in list(img_data.keys()):
img_data[x]["width"] = math.ceil(len(img_data[x]["data"]) / 40)
img_width += img_data[x]["width"] * 200
if not is_superuser:
img_width = (
img_width
- (
img_data["_n_keyword"]["width"]
+ img_data["_n_uid"]["width"]
+ img_data["_n_pid"]["width"]
)
* 200
)
del img_data["_n_keyword"]
del img_data["_n_pid"]
del img_data["_n_uid"]
current_width = 0
A = CreateImg(img_width, 1100)
for x in list(img_data.keys()):
if img_data[x]["data"]:
img = CreateImg(img_data[x]["width"] * 200, 1100, 200, 1100, font_size=40)
start_index = 0
end_index = 40
total_index = img_data[x]["width"] * 40
for _ in range(img_data[x]["width"]):
tmp = CreateImg(198, 1100, font_size=20)
text_img = CreateImg(198, 100, font_size=50)
key_str = "\n".join(
[key for key in img_data[x]["data"][start_index:end_index]]
)
tmp.text((10, 100), key_str)
if x.find("_n") == -1:
text_img.text((24, 24), "已收录")
else:
text_img.text((24, 24), "待收录")
tmp.paste(text_img, (0, 0))
start_index += 40
end_index = (
end_index + 40 if end_index + 40 <= total_index else total_index
)
background_img = CreateImg(200, 1100, color="#FFE4C4")
background_img.paste(tmp, (1, 1))
img.paste(background_img)
A.paste(img, (current_width, 0))
current_width += img_data[x]["width"] * 200
return A.pic2bs4()
def _check_black(img_urls: List[str], black: List[str]) -> bool:
"""
检测pid是否在黑名单中
:param img_urls: 图片img列表
:param black: 黑名单
:return:
"""
for b in black:
for img_url in img_urls:
if b in img_url:
return False
return True
from asyncpg.exceptions import UniqueViolationError
from .model.omega_pixiv_illusts import OmegaPixivIllusts
from asyncio.locks import Semaphore
from asyncio.exceptions import TimeoutError
from .model.pixiv import Pixiv
from typing import List, Optional
from utils.utils import change_pixiv_image_links
from utils.image_utils import CreateImg
from utils.http_utils import AsyncHttpx
from services.log import logger
from configs.config import Config
from configs.path_config import TEMP_PATH
import aiofiles
import platform
import asyncio
import math
try:
import ujson as json
except ModuleNotFoundError:
import json
if str(platform.system()).lower() == "windows":
policy = asyncio.WindowsSelectorEventLoopPolicy()
asyncio.set_event_loop_policy(policy)
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6;"
" rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
"Referer": "https://www.pixiv.net",
}
HIBIAPI = None
async def start_update_image_url(
current_keyword: List[str], black_pid: List[str]
) -> "int, int":
"""
开始更新图片url
:param current_keyword: 关键词
:param black_pid: 黑名单pid
:return: pid数量和图片数量
"""
global HIBIAPI
pid_count = 0
pic_count = 0
tasks = []
semaphore = asyncio.Semaphore(10)
if not HIBIAPI:
HIBIAPI = Config.get_config("hibiapi", "HIBIAPI")
HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] else HIBIAPI
for keyword in current_keyword:
for page in range(1, 110):
if keyword.startswith("uid:"):
url = f"{HIBIAPI}/api/pixiv/member_illust"
params = {"id": keyword[4:], "page": page}
if page == 30:
break
elif keyword.startswith("pid:"):
url = f"{HIBIAPI}/api/pixiv/illust"
params = {"id": keyword[4:]}
else:
url = f"{HIBIAPI}/api/pixiv/search"
params = {"word": keyword, "page": page}
tasks.append(
asyncio.ensure_future(
search_image(
url, keyword, params, semaphore, page, black_pid
)
)
)
if keyword.startswith("pid:"):
break
result = await asyncio.gather(*tasks)
for x in result:
pid_count += x[0]
pic_count += x[1]
return pid_count, pic_count
async def search_image(
url: str,
keyword: str,
params: dict,
semaphore: Semaphore,
page: int = 1,
black: List[str] = None,
) -> "int, int":
"""
搜索图片
:param url: 搜索url
:param keyword: 关键词
:param params: params参数
:param semaphore: semaphore
:param page: 页面
:param black: pid黑名单
:return: pid数量和图片数量
"""
tmp_pid = []
pic_count = 0
pid_count = 0
async with semaphore:
try:
data = (await AsyncHttpx.get(url, params=params)).json()
if (
not data
or data.get("error")
or (not data.get("illusts") and not data.get("illust"))
):
return 0, 0
if url != f"{HIBIAPI}/api/pixiv/illust":
logger.info(f'{keyword}: 获取数据成功...数据总量:{len(data["illusts"])}')
data = data["illusts"]
else:
logger.info(f'获取数据成功...PID:{params.get("id")}')
data = [data["illust"]]
img_data = {}
for x in data:
pid = x["id"]
title = x["title"]
width = x["width"]
height = x["height"]
view = x["total_view"]
bookmarks = x["total_bookmarks"]
uid = x["user"]["id"]
author = x["user"]["name"]
tags = []
for tag in x["tags"]:
for i in tag:
if tag[i]:
tags.append(tag[i])
img_urls = []
if x["page_count"] == 1:
img_urls.append(x["meta_single_page"]["original_image_url"])
else:
for urls in x["meta_pages"]:
img_urls.append(urls["image_urls"]["original"])
if (
(
bookmarks
>= Config.get_config("pix", "SEARCH_HIBIAPI_BOOKMARKS")
or (
url == f"{HIBIAPI}/api/pixiv/member_illust"
and bookmarks >= 1500
)
or (url == f"{HIBIAPI}/api/pixiv/illust")
)
and len(img_urls) < 10
and _check_black(img_urls, black)
):
img_data[pid] = {
"pid": pid,
"title": title,
"width": width,
"height": height,
"view": view,
"bookmarks": bookmarks,
"img_urls": img_urls,
"uid": uid,
"author": author,
"tags": tags,
}
else:
continue
for x in img_data.keys():
data = img_data[x]
for img_url in data["img_urls"]:
img_p = img_url[img_url.rfind("_") + 1 : img_url.rfind(".")]
try:
if await Pixiv.add_image_data(
data["pid"],
data["title"],
data["width"],
data["height"],
data["view"],
data["bookmarks"],
img_url,
img_p,
data["uid"],
data["author"],
",".join(data["tags"]),
):
if data["pid"] not in tmp_pid:
pid_count += 1
tmp_pid.append(data["pid"])
pic_count += 1
logger.info(f'存储图片PID:{data["pid"]} IMG_P:{img_p}')
except UniqueViolationError:
logger.warning(f'{data["pid"]} | {img_url} 已存在...')
except Exception as e:
logger.warning(f"PIX在线搜索图片错误,已再次调用 {type(e)}:{e}")
await search_image(url, keyword, params, semaphore, page, black)
return pid_count, pic_count
async def get_image(img_url: str, user_id: int) -> Optional[str]:
"""
下载图片
:param img_url:
:param user_id:
:return: 图片名称
"""
if "https://www.pixiv.net/artworks" in img_url:
pid = img_url.rsplit("/", maxsplit=1)[-1]
params = {"id": pid}
for _ in range(3):
try:
response = await AsyncHttpx.get(f"{HIBIAPI}/api/pixiv/illust", params=params)
if response.status_code == 200:
data = response.json()
if data.get("illust"):
if data["illust"]["page_count"] == 1:
img_url = data["illust"]["meta_single_page"][
"original_image_url"
]
else:
img_url = data["illust"]["meta_pages"][0][
"image_urls"
]["original"]
break
except TimeoutError:
pass
old_img_url = img_url
img_url = change_pixiv_image_links(
img_url, Config.get_config("pix", "PIX_IMAGE_SIZE"), Config.get_config("pixiv", "PIXIV_NGINX_URL")
)
old_img_url = change_pixiv_image_links(
old_img_url, None, Config.get_config("pixiv", "PIXIV_NGINX_URL")
)
for _ in range(3):
try:
response = await AsyncHttpx.get(img_url, timeout=Config.get_config("pix", "TIMEOUT"),)
if response.status_code == 404:
img_url = old_img_url
continue
async with aiofiles.open(
f"{TEMP_PATH}/pix_{user_id}_{img_url[-10:-4]}.jpg", "wb"
) as f:
await f.write(response.content)
return f"pix_{user_id}_{img_url[-10:-4]}.jpg"
except TimeoutError:
pass
return None
async def uid_pid_exists(id_: str) -> bool:
"""
检测 pid/uid 是否有效
:param id_: pid/uid
"""
if id_.startswith("uid:"):
url = f"{HIBIAPI}/api/pixiv/member"
elif id_.startswith("pid:"):
url = f"{HIBIAPI}/api/pixiv/illust"
else:
return False
params = {"id": int(id_[4:])}
data = (await AsyncHttpx.get(url, params=params)).json()
if data.get("error"):
return False
return True
async def get_keyword_num(keyword: str) -> "int, int, int, int, int":
"""
查看图片相关 tag 数量
:param keyword: 关键词tag
"""
count, r18_count = await Pixiv.get_keyword_num(keyword.split())
count_, setu_count, r18_count_ = await OmegaPixivIllusts.get_keyword_num(
keyword.split()
)
return count, r18_count, count_, setu_count, r18_count_
async def remove_image(pid: int, img_p: str) -> bool:
"""
删除置顶图片
:param pid: pid
:param img_p: 图片 p 如 p0,p1 等
"""
if img_p:
if "p" not in img_p:
img_p = f"p{img_p}"
return await Pixiv.remove_image_data(pid, img_p)
def gen_keyword_pic(
_pass_keyword: List[str], not_pass_keyword: List[str], is_superuser: bool
):
"""
已通过或未通过的所有关键词/uid/pid
:param _pass_keyword: 通过列表
:param not_pass_keyword: 未通过列表
:param is_superuser: 是否超级用户
"""
_keyword = [
x
for x in _pass_keyword
if not x.startswith("uid:")
and not x.startswith("pid:")
and not x.startswith("black:")
]
_uid = [x for x in _pass_keyword if x.startswith("uid:")]
_pid = [x for x in _pass_keyword if x.startswith("pid:")]
_n_keyword = [
x
for x in not_pass_keyword
if not x.startswith("uid:")
and not x.startswith("pid:")
and not x.startswith("black:")
]
_n_uid = [
x
for x in not_pass_keyword
if x.startswith("uid:") and not x.startswith("black:")
]
_n_pid = [
x
for x in not_pass_keyword
if x.startswith("pid:") and not x.startswith("black:")
]
img_width = 0
img_data = {
"_keyword": {"width": 0, "data": _keyword},
"_uid": {"width": 0, "data": _uid},
"_pid": {"width": 0, "data": _pid},
"_n_keyword": {"width": 0, "data": _n_keyword},
"_n_uid": {"width": 0, "data": _n_uid},
"_n_pid": {"width": 0, "data": _n_pid},
}
for x in list(img_data.keys()):
img_data[x]["width"] = math.ceil(len(img_data[x]["data"]) / 40)
img_width += img_data[x]["width"] * 200
if not is_superuser:
img_width = (
img_width
- (
img_data["_n_keyword"]["width"]
+ img_data["_n_uid"]["width"]
+ img_data["_n_pid"]["width"]
)
* 200
)
del img_data["_n_keyword"]
del img_data["_n_pid"]
del img_data["_n_uid"]
current_width = 0
A = CreateImg(img_width, 1100)
for x in list(img_data.keys()):
if img_data[x]["data"]:
img = CreateImg(img_data[x]["width"] * 200, 1100, 200, 1100, font_size=40)
start_index = 0
end_index = 40
total_index = img_data[x]["width"] * 40
for _ in range(img_data[x]["width"]):
tmp = CreateImg(198, 1100, font_size=20)
text_img = CreateImg(198, 100, font_size=50)
key_str = "\n".join(
[key for key in img_data[x]["data"][start_index:end_index]]
)
tmp.text((10, 100), key_str)
if x.find("_n") == -1:
text_img.text((24, 24), "已收录")
else:
text_img.text((24, 24), "待收录")
tmp.paste(text_img, (0, 0))
start_index += 40
end_index = (
end_index + 40 if end_index + 40 <= total_index else total_index
)
background_img = CreateImg(200, 1100, color="#FFE4C4")
background_img.paste(tmp, (1, 1))
img.paste(background_img)
A.paste(img, (current_width, 0))
current_width += img_data[x]["width"] * 200
return A.pic2bs4()
def _check_black(img_urls: List[str], black: List[str]) -> bool:
"""
检测pid是否在黑名单中
:param img_urls: 图片img列表
:param black: 黑名单
:return:
"""
for b in black:
for img_url in img_urls:
if b in img_url:
return False
return True
+3 -3
View File
@@ -1,3 +1,3 @@
from .pixiv_keyword_user import *
from .omega_pixiv_illusts import *
from .pixiv import *
from .pixiv_keyword_user import *
from .omega_pixiv_illusts import *
from .pixiv import *
+141 -141
View File
@@ -1,141 +1,141 @@
from typing import Optional, List
from datetime import datetime
from services.db_context import db
class OmegaPixivIllusts(db.Model):
__tablename__ = "omega_pixiv_illusts"
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer(), primary_key=True)
pid = db.Column(db.BigInteger(), nullable=False)
uid = db.Column(db.BigInteger(), nullable=False)
title = db.Column(db.String(), nullable=False)
uname = db.Column(db.String(), nullable=False)
nsfw_tag = db.Column(db.Integer(), nullable=False)
width = db.Column(db.Integer(), nullable=False)
height = db.Column(db.Integer(), nullable=False)
tags = db.Column(db.String(), nullable=False)
url = db.Column(db.String(), nullable=False)
created_at = db.Column(db.DateTime(timezone=True))
updated_at = db.Column(db.DateTime(timezone=True))
_idx1 = db.Index("omega_pixiv_illusts_idx1", "pid", "url", unique=True)
@classmethod
async def add_image_data(
cls,
pid: int,
title: str,
width: int,
height: int,
url: str,
uid: int,
uname: str,
nsfw_tag: int,
tags: str,
created_at: datetime,
updated_at: datetime,
):
"""
说明:
添加图片信息
参数:
:param pid: pid
:param title: 标题
:param width: 宽度
:param height: 长度
:param url: url链接
:param uid: 作者uid
:param uname: 作者名称
:param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18
:param tags: 相关tag
:param created_at: 创建日期
:param updated_at: 更新日期
"""
if not await cls.check_exists(pid):
await cls.create(
pid=pid,
title=title,
width=width,
height=height,
url=url,
uid=uid,
uname=uname,
nsfw_tag=nsfw_tag,
tags=tags,
)
return True
return False
@classmethod
async def query_images(
cls,
keywords: Optional[List[str]] = None,
uid: Optional[int] = None,
pid: Optional[int] = None,
nsfw_tag: Optional[int] = 0,
num: int = 100
) -> List[Optional["OmegaPixivIllusts"]]:
"""
说明:
查找符合条件的图片
参数:
:param keywords: 关键词
:param uid: 画师uid
:param pid: 图片pid
:param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18
:param num: 获取图片数量
"""
if nsfw_tag is not None:
query = cls.query.where(cls.nsfw_tag == nsfw_tag)
else:
query = cls.query
if keywords:
for keyword in keywords:
query = query.where(cls.tags.contains(keyword))
elif uid:
query = query.where(cls.uid == uid)
elif pid:
query = query.where(cls.uid == pid)
query = query.order_by(db.func.random()).limit(num)
return await query.gino.all()
@classmethod
async def check_exists(cls, pid: int) -> bool:
"""
说明:
检测pid是否已存在
参数:
:param pid: 图片PID
"""
query = await cls.query.where(cls.pid == pid).gino.all()
return bool(query)
@classmethod
async def get_keyword_num(cls, tags: List[str] = None) -> "int, int, int":
"""
说明:
获取相关关键词(keyword, tag)在图库中的数量
参数:
:param tags: 关键词/Tag
"""
setattr(OmegaPixivIllusts, 'count', db.func.count(cls.pid).label('count'))
query = cls.select('count')
if tags:
for tag in tags:
query = query.where(cls.tags.contains(tag))
count = await query.where(cls.nsfw_tag == 0).gino.first()
setu_count = await query.where(cls.nsfw_tag == 1).gino.first()
r18_count = await query.where(cls.nsfw_tag == 2).gino.first()
return count[0], setu_count[0], r18_count[0]
@classmethod
async def get_all_pid(cls) -> List[int]:
"""
说明:
获取所有图片PID
"""
data = await cls.select('pid').gino.all()
return [x[0] for x in data]
from typing import Optional, List
from datetime import datetime
from services.db_context import db
class OmegaPixivIllusts(db.Model):
__tablename__ = "omega_pixiv_illusts"
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer(), primary_key=True)
pid = db.Column(db.BigInteger(), nullable=False)
uid = db.Column(db.BigInteger(), nullable=False)
title = db.Column(db.String(), nullable=False)
uname = db.Column(db.String(), nullable=False)
nsfw_tag = db.Column(db.Integer(), nullable=False)
width = db.Column(db.Integer(), nullable=False)
height = db.Column(db.Integer(), nullable=False)
tags = db.Column(db.String(), nullable=False)
url = db.Column(db.String(), nullable=False)
created_at = db.Column(db.DateTime(timezone=True))
updated_at = db.Column(db.DateTime(timezone=True))
_idx1 = db.Index("omega_pixiv_illusts_idx1", "pid", "url", unique=True)
@classmethod
async def add_image_data(
cls,
pid: int,
title: str,
width: int,
height: int,
url: str,
uid: int,
uname: str,
nsfw_tag: int,
tags: str,
created_at: datetime,
updated_at: datetime,
):
"""
说明:
添加图片信息
参数:
:param pid: pid
:param title: 标题
:param width: 宽度
:param height: 长度
:param url: url链接
:param uid: 作者uid
:param uname: 作者名称
:param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18
:param tags: 相关tag
:param created_at: 创建日期
:param updated_at: 更新日期
"""
if not await cls.check_exists(pid):
await cls.create(
pid=pid,
title=title,
width=width,
height=height,
url=url,
uid=uid,
uname=uname,
nsfw_tag=nsfw_tag,
tags=tags,
)
return True
return False
@classmethod
async def query_images(
cls,
keywords: Optional[List[str]] = None,
uid: Optional[int] = None,
pid: Optional[int] = None,
nsfw_tag: Optional[int] = 0,
num: int = 100
) -> List[Optional["OmegaPixivIllusts"]]:
"""
说明:
查找符合条件的图片
参数:
:param keywords: 关键词
:param uid: 画师uid
:param pid: 图片pid
:param nsfw_tag: nsfw标签, 0=safe, 1=setu. 2=r18
:param num: 获取图片数量
"""
if nsfw_tag is not None:
query = cls.query.where(cls.nsfw_tag == nsfw_tag)
else:
query = cls.query
if keywords:
for keyword in keywords:
query = query.where(cls.tags.contains(keyword))
elif uid:
query = query.where(cls.uid == uid)
elif pid:
query = query.where(cls.uid == pid)
query = query.order_by(db.func.random()).limit(num)
return await query.gino.all()
@classmethod
async def check_exists(cls, pid: int) -> bool:
"""
说明:
检测pid是否已存在
参数:
:param pid: 图片PID
"""
query = await cls.query.where(cls.pid == pid).gino.all()
return bool(query)
@classmethod
async def get_keyword_num(cls, tags: List[str] = None) -> "int, int, int":
"""
说明:
获取相关关键词(keyword, tag)在图库中的数量
参数:
:param tags: 关键词/Tag
"""
setattr(OmegaPixivIllusts, 'count', db.func.count(cls.pid).label('count'))
query = cls.select('count')
if tags:
for tag in tags:
query = query.where(cls.tags.contains(tag))
count = await query.where(cls.nsfw_tag == 0).gino.first()
setu_count = await query.where(cls.nsfw_tag == 1).gino.first()
r18_count = await query.where(cls.nsfw_tag == 2).gino.first()
return count[0], setu_count[0], r18_count[0]
@classmethod
async def get_all_pid(cls) -> List[int]:
"""
说明:
获取所有图片PID
"""
data = await cls.select('pid').gino.all()
return [x[0] for x in data]

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