diff --git a/nonebot_bison/platform/bilibili.py b/nonebot_bison/platform/bilibili.py index 9637c8f..baa7a06 100644 --- a/nonebot_bison/platform/bilibili.py +++ b/nonebot_bison/platform/bilibili.py @@ -297,6 +297,21 @@ class Bilibililive(StatusChange): return None return res_data["data"]["card"]["name"] + def gen_empty_info(self, uid: int) -> Info: + """返回一个空的Info,用于该用户没有直播间的情况""" + return Bilibililive.Info( + title="", + room_id=0, + uid=uid, + live_time=0, + live_status=Bilibililive.LiveStatus.OFF, + area_v2_name="", + uname="", + face="", + cover_from_user="", + keyframe="", + ) + async def get_status(self, target: Target) -> Info: params = {"uids[]": target} # https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/live/info.md#批量查询直播间状态 @@ -307,41 +322,40 @@ class Bilibililive(StatusChange): ) res_dict = res.json() - if res_dict["code"] == 0: - data = res_dict["data"][target] - self.Info.update_forward_refs() - info = self.Info.parse_obj(data) - - return info - else: + if res_dict["code"] != 0: raise self.FetchError() + data = res_dict.get("data") + if not data: + return self.gen_empty_info(uid=int(target)) + room_data = data[target] + return self.Info.parse_obj(room_data) + def compare_status( self, _: Target, old_status: Info, new_status: Info ) -> list[RawPost]: action = Bilibililive.LiveAction match new_status.get_live_action(old_status): case action.TURN_ON: - current_status = deepcopy(new_status) - current_status.category = Category(1) - return [current_status] + return self._gen_current_status(new_status, 1) case action.TITLE_UPDATE: - current_status = deepcopy(new_status) - current_status.category = Category(2) - return [current_status] + return self._gen_current_status(new_status, 2) case action.TURN_OFF: - current_status = deepcopy(new_status) - current_status.category = Category(3) - return [current_status] + return self._gen_current_status(new_status, 3) case _: return [] + def _gen_current_status(self, new_status: Info, category: Category): + current_status = deepcopy(new_status) + current_status.category = Category(category) + return [current_status] + def get_category(self, status: Info) -> Category: assert status.category != Category(0) return status.category async def parse(self, raw_post: Info) -> Post: - url = "https://live.bilibili.com/{}".format(raw_post.room_id) + url = f"https://live.bilibili.com/{raw_post.room_id}" pic = ( [raw_post.cover] if raw_post.category == Category(1) @@ -387,11 +401,11 @@ class BilibiliBangumi(StatusChange): if re.match(r"\d+", target_string): return Target(target_string) elif m := re.match(r"md(\d+)", target_string): - return Target(m.group(1)) + return Target(m[1]) elif m := re.match( r"(?:https?://)?www\.bilibili\.com/bangumi/media/md(\d+)", target_string ): - return Target(m.group(1)) + return Target(m[1]) raise cls.ParseTargetException() async def get_status(self, target: Target): @@ -441,3 +455,6 @@ class BilibiliBangumi(StatusChange): target_name=target_name, compress=True, ) + + +Bilibililive.Info.update_forward_refs() diff --git a/tests/platforms/test_bilibili_live.py b/tests/platforms/test_bilibili_live.py index ed202d0..3c8b885 100644 --- a/tests/platforms/test_bilibili_live.py +++ b/tests/platforms/test_bilibili_live.py @@ -1,3 +1,5 @@ +from copy import deepcopy + import pytest import respx from httpx import AsyncClient, Response @@ -24,6 +26,60 @@ def dummy_only_open_user_subinfo(app: App): return UserSubInfo(user=user, categories=[1], tags=[]) +@pytest.mark.asyncio +@respx.mock +async def test_fetch_bililive_no_room(bili_live, dummy_only_open_user_subinfo): + mock_bili_live_status = get_json("bili_live_status.json") + mock_bili_live_status["data"] = [] + bili_live_router = respx.get( + "https://api.live.bilibili.com/room/v1/Room/get_status_info_by_uids?uids[]=13164144" + ) + bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status)) + + bilibili_main_page_router = respx.get("https://www.bilibili.com/") + bilibili_main_page_router.mock(return_value=Response(200)) + + target = "13164144" + res = await bili_live.fetch_new_post(target, [dummy_only_open_user_subinfo]) + assert bili_live_router.call_count == 1 + assert len(res) == 0 + + +@pytest.mark.asyncio +@respx.mock +async def test_fetch_first_live(bili_live, dummy_only_open_user_subinfo): + mock_bili_live_status = get_json("bili_live_status.json") + empty_bili_live_status = deepcopy(mock_bili_live_status) + empty_bili_live_status["data"] = [] + bili_live_router = respx.get( + "https://api.live.bilibili.com/room/v1/Room/get_status_info_by_uids?uids[]=13164144" + ) + bili_live_router.mock(return_value=Response(200, json=empty_bili_live_status)) + + bilibili_main_page_router = respx.get("https://www.bilibili.com/") + bilibili_main_page_router.mock(return_value=Response(200)) + + target = "13164144" + res = await bili_live.fetch_new_post(target, [dummy_only_open_user_subinfo]) + assert bili_live_router.call_count == 1 + assert len(res) == 0 + + mock_bili_live_status["data"][target]["live_status"] = 1 + bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status)) + res2 = await bili_live.fetch_new_post(target, [dummy_only_open_user_subinfo]) + assert bili_live_router.call_count == 2 + assert len(res2) == 1 + post = res2[0][1][0] + assert post.target_type == "Bilibili直播" + assert post.text == "[开播] 【Zc】从0挑战到15肉鸽!目前10难度" + assert post.url == "https://live.bilibili.com/3044248" + assert post.target_name == "魔法Zc目录 其他单机" + assert post.pics == [ + "https://i0.hdslb.com/bfs/live/new_room_cover/fd357f0f3cbbb48e9acfbcda616b946c2454c56c.jpg" + ] + assert post.compress == True + + @pytest.mark.asyncio @respx.mock async def test_fetch_bililive_only_live_open(bili_live, dummy_only_open_user_subinfo): @@ -40,7 +96,7 @@ async def test_fetch_bililive_only_live_open(bili_live, dummy_only_open_user_sub target = "13164144" res = await bili_live.fetch_new_post(target, [dummy_only_open_user_subinfo]) assert bili_live_router.call_count == 1 - assert len(res) == 0 + assert len(res[0][1]) == 0 # 直播状态更新-上播 mock_bili_live_status["data"][target]["live_status"] = 1 bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status))