zhenxun_bot/plugins/bilibili_sub/data_source.py
2022-04-04 20:33:37 +08:00

401 lines
14 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 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 = 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: 订阅类型
"""
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 f"Id{id_} 获取信息失败...请检查订阅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 (
latest_video_created
and _user.latest_video_created
and video
and _user.latest_video_created < latest_video_created
):
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()
try:
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,
)
except Exception as e:
logger.error(f"B站订阅获取用户动态 发送错误 {type(e)}{e}")
finally:
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()