Update data_source.py

This commit is contained in:
HibiKier 2021-09-06 18:03:21 +08:00 committed by GitHub
parent 49567aa958
commit b1b15b63a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,379 +1,200 @@
from bilibili_api.exceptions.ResponseCodeException import ResponseCodeException from nonebot import on_command
from asyncio.exceptions import TimeoutError 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,
)
from models.level_user import LevelUser
from configs.config import GROUP_BILIBILI_SUB_LEVEL
from utils.utils import get_message_text, is_number, scheduler, get_bot
from models.bilibili_sub import BilibiliSub from models.bilibili_sub 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 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 services.log import logger
import aiohttp from nonebot import Driver
import random import nonebot
__plugin_name__ = "B站订阅"
__plugin_usage__ = """B站订阅帮助
添加订阅 [主播/UP/番剧] [id/链接/番名]
删除订阅 [id]
查看订阅"""
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()
bilibili_search_url = "https://api.bilibili.com/x/web-interface/search/all/v2" sub_manager: Optional[SubManager] = None
dynamic_path = Path(IMAGE_PATH) / "bilibili_sub" / "dynamic"
dynamic_path.mkdir(exist_ok=True, parents=True)
async def add_live_sub(live_id: int, sub_user: str) -> str: @driver.on_startup
""" async def _():
添加直播订阅 global sub_manager
:param live_id: 直播房间号 sub_manager = SubManager()
: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: @add_sub.args_parser
""" async def _(bot: Bot, event: MessageEvent, state: T_State):
添加订阅 UP season_data = state["season_data"]
:param uid: UP uid msg = get_message_text(event.json())
:param sub_user: 订阅用户 if not is_number(msg) or int(msg) < 1 or int(msg) > len(season_data):
""" await add_sub.reject("Id必须为数字且在范围内请重新输入...")
try: state["id"] = season_data[int(msg) - 1]["media_id"]
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: @add_sub.handle()
""" async def _(bot: Bot, event: MessageEvent, state: T_State):
添加订阅 UP msg = get_message_text(event.json()).split()
:param media_id: 番剧 media_id if len(msg) < 2:
:param sub_user: 订阅用户 await add_sub.finish("参数不完全,请查看订阅帮助...")
""" sub_type = msg[0]
try: id_ = ""
async with db.transaction(): if isinstance(event, GroupMessageEvent):
try: if not await LevelUser.check_level(
season_info = await bangumi.get_meta(media_id) event.user_id, event.group_id, GROUP_BILIBILI_SUB_LEVEL
except ResponseCodeException: ):
return f"未找到media_id{media_id} 的信息请检查Id是否正确" await add_sub.finish(
season_id = season_info["media"]["season_id"] f"您的权限不足,群内订阅的需要 {GROUP_BILIBILI_SUB_LEVEL} 级权限..", at_sender=True
season_current_episode = season_info["media"]["new_ep"]["index"] )
season_name = season_info["media"]["title"] sub_user = f"{event.user_id}:{event.group_id}"
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: else:
return f"取消订阅:{sub_id} 失败请检查是否订阅过该Id...." 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_)
print(state["season_data"])
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_)
async def get_media_id(keyword: str) -> dict: @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/番剧!")
sub_manager.reload_flag = True
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):
""" """
获取番剧的 media_id 推送信息
:param keyword: 番剧名称 :param rst: 回复
:param sub: BilibiliSub
:param bot: Bot
""" """
params = {"keyword": keyword} if rst:
async with aiohttp.ClientSession() as session: for x in sub.sub_users.split(",")[:-1]:
for _ in range(3):
try: try:
_season_data = {} if ":" in x:
async with session.get( await bot.send_group_msg(
bilibili_search_url, timeout=5, params=params group_id=int(x.split(":")[1]), message=Message(rst)
) as response: )
if response.status == 200: else:
data = await response.json() await bot.send_private_msg(user_id=int(x), message=Message(rst))
if data.get("data"): except Exception as e:
for item in data["data"]["result"]: logger.error(f"B站订阅推送发生错误 sub_id{sub.sub_id} {type(e)}{e}")
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.reload_flag = True
self.live_data = []
self.up_data = []
self.season_data = []
self.sub_list = []
async def reload_sub_data(self):
"""
重载数据
"""
if self.reload_flag or not self.sub_list:
(
self.live_data,
self.up_data,
self.season_data,
) = await BilibiliSub.get_all_sub_data()
for x, i, j in zip(self.live_data, self.up_data, self.season_data):
self.sub_list.append(x)
self.sub_list.append(i)
self.sub_list.append(j)
self.reload_flag = False
def append(self, data: BilibiliSub):
"""
增加新数据
:param data: 数据
"""
self.sub_list.append(data)
async def random_sub_data(self) -> Optional[BilibiliSub]:
"""
随机获取一条数据
:return:
"""
if not self.sub_list:
await self.reload_sub_data()
if self.sub_list:
sub = random.choice(self.sub_list)
self.sub_list.remove(sub)
return sub
return None