From e14fd5bfd7bf9e17f8fbb928efcba41d79fbcbc4 Mon Sep 17 00:00:00 2001 From: AzideCupric <57004769+AzideCupric@users.noreply.github.com> Date: Wed, 19 Oct 2022 23:49:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=8D=A2=E5=93=94=E5=93=A9=E5=93=94?= =?UTF-8?q?=E5=93=A9=E7=9B=B4=E6=92=AD=E4=BD=BF=E7=94=A8=E7=9A=84api?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=9B=B4=E6=92=AD=E9=97=B4=E6=A0=87?= =?UTF-8?q?=E9=A2=98=E4=BF=AE=E6=94=B9=E6=97=B6=E6=8E=A8=E9=80=81=E7=9B=B4?= =?UTF-8?q?=E6=92=AD=E9=97=B4=E7=9A=84=E5=8A=9F=E8=83=BD=20(#128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(bililive):调整bililive所使用的api与推送消息内容 * feat(bililive):调整功能的实现细节 * test(bililive): 初步完善相关测试 * docs(bililive): 补充文档相关 * change(bililive): 将使用修改配置项以调整订阅的方式调整为使用category * test(bililive): 调整测试,扩大测试范围 * change(bililive):调整Bilibili-live内部的代码实现 * test(bililive):修正测试 * delete(bililive):删除用来测试功能性的临时文件 * change(bililive): 调整代码 --- .../nonebot_bison/platform/bilibili.py | 88 ++++++++--- src/plugins/nonebot_bison/post/__init__.py | 2 +- tests/platforms/static/bili_live_status.json | 135 ++++------------ tests/platforms/test_bilibili_live.py | 146 +++++++++++++++++- 4 files changed, 231 insertions(+), 140 deletions(-) diff --git a/src/plugins/nonebot_bison/platform/bilibili.py b/src/plugins/nonebot_bison/platform/bilibili.py index fe27414..739656c 100644 --- a/src/plugins/nonebot_bison/platform/bilibili.py +++ b/src/plugins/nonebot_bison/platform/bilibili.py @@ -1,13 +1,17 @@ import functools import json import re +from copy import deepcopy +from dataclasses import dataclass, field from datetime import datetime, timedelta from typing import Any, Callable, Optional import httpx from httpx import AsyncClient from nonebot.log import logger +from typing_extensions import Self +from ..plugin_config import plugin_config from ..post import Post from ..types import Category, RawPost, Tag, Target from ..utils import SchedulerConfig @@ -203,7 +207,7 @@ class Bilibililive(StatusChange): # Date : 2022-5-18 8:54 # Description : bilibili开播提醒 # E-mail : 1557157806@qq.com - categories = {} + categories = {1: "开播提醒", 2: "标题更新提醒"} platform_name = "bilibili-live" enable_tag = False enabled = True @@ -212,6 +216,37 @@ class Bilibililive(StatusChange): name = "Bilibili直播" has_target = True + @dataclass + class Info: + uname: str + live_status: int + room_id: str + title: str + cover_from_user: str + keyframe: str + category: Category = field(default=Category(0)) + + def __init__(self, raw_info: dict): + self.__dict__.update(raw_info) + + def is_live_turn_on(self, old_info: Self) -> bool: + # 使用 & 判断直播开始 + # live_status: + # 0:关播 + # 1:直播中 + # 2:轮播中 + if self.live_status == 1 and old_info.live_status != self.live_status: + return True + + return False + + def is_title_update(self, old_info: Self) -> bool: + # 使用 ^ 判断直播时标题改变 + if self.live_status == 1 and old_info.title != self.title: + return True + + return False + @classmethod async def get_target_name( cls, client: AsyncClient, target: Target @@ -224,40 +259,49 @@ class Bilibililive(StatusChange): return None return res_data["data"]["name"] - async def get_status(self, target: Target): - params = {"mid": target} + async def get_status(self, target: Target) -> Info: + params = {"uids[]": target} + # from https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/live/info.md#%E6%89%B9%E9%87%8F%E6%9F%A5%E8%AF%A2%E7%9B%B4%E6%92%AD%E9%97%B4%E7%8A%B6%E6%80%81 res = await self.client.get( - "https://api.bilibili.com/x/space/acc/info", + "http://api.live.bilibili.com/room/v1/Room/get_status_info_by_uids", params=params, timeout=4.0, ) res_dict = json.loads(res.text) if res_dict["code"] == 0: - info = {} - info["uid"] = res_dict["data"]["mid"] - info["uname"] = res_dict["data"]["name"] - info["live_state"] = res_dict["data"]["live_room"]["liveStatus"] - info["room_id"] = res_dict["data"]["live_room"]["roomid"] - info["title"] = res_dict["data"]["live_room"]["title"] - info["cover"] = res_dict["data"]["live_room"]["cover"] + data = res_dict["data"][target] + + info = self.Info(data) + return info else: raise self.FetchError() - def compare_status(self, target: Target, old_status, new_status) -> list[RawPost]: - if ( - new_status["live_state"] != old_status["live_state"] - and new_status["live_state"] == 1 - ): - return [new_status] + def compare_status( + self, target: Target, old_status: Info, new_status: Info + ) -> list[RawPost]: + if new_status.is_live_turn_on(old_status): + # 判断开播 运算符左右有顺序要求 + current_status = deepcopy(new_status) + current_status.category = Category(1) + return [current_status] + elif new_status.is_title_update(old_status): + # 判断直播时直播间标题变更 运算符左右有顺序要求 + current_status = deepcopy(new_status) + current_status.category = Category(2) + return [current_status] else: return [] - async def parse(self, raw_post: RawPost) -> Post: - url = "https://live.bilibili.com/{}".format(raw_post["room_id"]) - pic = [raw_post["cover"]] - target_name = raw_post["uname"] - title = raw_post["title"] + def get_category(self, status: Info) -> Category: + assert status.category != Category(0) + return status.category + + async def parse(self, raw_info: Info) -> Post: + url = "https://live.bilibili.com/{}".format(raw_info.room_id) + pic = [raw_info.keyframe] + target_name = raw_info.uname + title = raw_info.title return Post( self.name, text=title, diff --git a/src/plugins/nonebot_bison/post/__init__.py b/src/plugins/nonebot_bison/post/__init__.py index 3900f47..ff93bec 100644 --- a/src/plugins/nonebot_bison/post/__init__.py +++ b/src/plugins/nonebot_bison/post/__init__.py @@ -1,3 +1,3 @@ from .post import Post -__all__ = ["Post"] +__all__ = ["Post", "CustomPost"] diff --git a/tests/platforms/static/bili_live_status.json b/tests/platforms/static/bili_live_status.json index e907dc9..fb73459 100644 --- a/tests/platforms/static/bili_live_status.json +++ b/tests/platforms/static/bili_live_status.json @@ -1,114 +1,31 @@ { "code": 0, - "message": "0", - "ttl": 1, + "msg": "success", + "message": "success", "data": { - "mid": 13164144, - "name": "魔法Zc目录", - "sex": "男", - "face": "http://i0.hdslb.com/bfs/face/a84fa10f90f7060d0336384954ee1cde7a8e9bc6.jpg", - "face_nft": 0, - "sign": "每日18:00~22:00欢乐直播!请勿在任何乌有相关内容中刷Zc,尊重角色;商务合作qq271374252", - "rank": 10000, - "level": 6, - "jointime": 0, - "moral": 0, - "silence": 0, - "coins": 0, - "fans_badge": true, - "fans_medal": { - "show": false, - "wear": false, - "medal": null - }, - "official": { - "role": 1, - "title": "bilibili 2021百大UP主、知名游戏UP主、直播高能主播", - "desc": "", - "type": 0 - }, - "vip": { - "type": 2, - "status": 1, - "due_date": 1702051200000, - "vip_pay_type": 0, - "theme_type": 0, - "label": { - "path": "", - "text": "年度大会员", - "label_theme": "annual_vip", - "text_color": "#FFFFFF", - "bg_style": 1, - "bg_color": "#FB7299", - "border_color": "" - }, - "avatar_subscript": 1, - "nickname_color": "#FB7299", - "role": 3, - "avatar_subscript_url": "http://i0.hdslb.com/bfs/vip/icon_Certification_big_member_22_3x.png" - }, - "pendant": { - "pid": 3399, - "name": "2233幻星集", - "image": "http://i0.hdslb.com/bfs/garb/item/20c07ded13498a5b12db99660c766ddd92ecfe31.png", - "expire": 0, - "image_enhance": "http://i0.hdslb.com/bfs/garb/item/20c07ded13498a5b12db99660c766ddd92ecfe31.png", - "image_enhance_frame": "" - }, - "nameplate": { - "nid": 1, - "name": "黄金殿堂", - "image": "http://i2.hdslb.com/bfs/face/82896ff40fcb4e7c7259cb98056975830cb55695.png", - "image_small": "http://i0.hdslb.com/bfs/face/627e342851dfda6fe7380c2fa0cbd7fae2e61533.png", - "level": "稀有勋章", - "condition": "单个自制视频总播放数\u003e=100万" - }, - "user_honour_info": { - "mid": 0, - "colour": null, - "tags": [] - }, - "is_followed": true, - "top_photo": "http://i2.hdslb.com/bfs/space/853fea2728651588a2cdef0a1e586bcefff8e3d8.png", - "theme": {}, - "sys_notice": {}, - "live_room": { - "roomStatus": 1, - "liveStatus": 0, - "url": "https://live.bilibili.com/3044248?broadcast_type=0\u0026is_room_feed=1", - "title": "【Zc】早朝危机合约!", - "cover": "http://i0.hdslb.com/bfs/live/new_room_cover/cf7d4d3b2f336c6dba299644c3af952c5db82612.jpg", - "roomid": 3044248, - "roundStatus": 1, - "broadcast_type": 0, - "watched_show": { - "switch": true, - "num": 13753, - "text_small": "1.3万", - "text_large": "1.3万人看过", - "icon": "https://i0.hdslb.com/bfs/live/a725a9e61242ef44d764ac911691a7ce07f36c1d.png", - "icon_location": "", - "icon_web": "https://i0.hdslb.com/bfs/live/8d9d0f33ef8bf6f308742752d13dd0df731df19c.png" - } - }, - "birthday": "07-21", - "school": { - "name": "" - }, - "profession": { - "name": "", - "department": "", - "title": "", - "is_show": 0 - }, - "tags": [ - "评论区UP主", - "目标是星辰大海" - ], - "series": { - "user_upgrade_status": 3, - "show_upgrade_window": false - }, - "is_senior_member": 1 + "13164144": { + "title": "【Zc】从0挑战到15肉鸽!目前10难度", + "room_id": 3044248, + "uid": 13164144, + "online": 270881, + "live_time": 0, + "live_status": 2, + "short_id": 0, + "area": 1, + "area_name": "单机联机", + "area_v2_id": 235, + "area_v2_name": "其他单机", + "area_v2_parent_name": "单机游戏", + "area_v2_parent_id": 6, + "uname": "魔法Zc目录", + "face": "https://i0.hdslb.com/bfs/face/a84fa10f90f7060d0336384954ee1cde7a8e9bc6.jpg", + "tag_name": "以撒,minecraft,饥荒,彩虹六号,东方", + "tags": "明日方舟,搞笑,单机游戏,游戏", + "cover_from_user": "https://i0.hdslb.com/bfs/live/new_room_cover/fd357f0f3cbbb48e9acfbcda616b946c2454c56c.jpg", + "keyframe": "https://i0.hdslb.com/bfs/live-key-frame/keyframe10170435000003044248mwowx0.jpg", + "lock_till": "0000-00-00 00:00:00", + "hidden_till": "0000-00-00 00:00:00", + "broadcast_type": 0 + } } } \ No newline at end of file diff --git a/tests/platforms/test_bilibili_live.py b/tests/platforms/test_bilibili_live.py index e86875e..9b857d0 100644 --- a/tests/platforms/test_bilibili_live.py +++ b/tests/platforms/test_bilibili_live.py @@ -13,13 +13,23 @@ def bili_live(app: App): return platform_manager["bilibili-live"](AsyncClient()) +@pytest.fixture +def dummy_only_status_user_subinfo(app: App): + from nonebot_bison.types import User, UserSubInfo + + user = User(123, "group") + return UserSubInfo(user=user, categories=[1], tags=[]) + + @pytest.mark.asyncio @respx.mock -async def test_fetch_bilibili_live_status(bili_live, dummy_user_subinfo): +async def test_fetch_bililive_only_status_change( + bili_live, dummy_only_status_user_subinfo +): mock_bili_live_status = get_json("bili_live_status.json") bili_live_router = respx.get( - "https://api.bilibili.com/x/space/acc/info?mid=13164144" + "http://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)) @@ -27,18 +37,138 @@ async def test_fetch_bilibili_live_status(bili_live, dummy_user_subinfo): bilibili_main_page_router.mock(return_value=Response(200)) target = "13164144" - res = await bili_live.fetch_new_post(target, [dummy_user_subinfo]) - assert bili_live_router.called + res = await bili_live.fetch_new_post(target, [dummy_only_status_user_subinfo]) + assert bili_live_router.call_count == 1 assert len(res) == 0 - mock_bili_live_status["data"]["live_room"]["liveStatus"] = 1 + # 直播状态更新 + 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_user_subinfo]) + res2 = await bili_live.fetch_new_post(target, [dummy_only_status_user_subinfo]) post = res2[0][1][0] assert post.target_type == "Bilibili直播" - assert post.text == "【Zc】早朝危机合约!" + assert post.text == "【Zc】从0挑战到15肉鸽!目前10难度" assert post.url == "https://live.bilibili.com/3044248" assert post.target_name == "魔法Zc目录" assert post.pics == [ - "http://i0.hdslb.com/bfs/live/new_room_cover/cf7d4d3b2f336c6dba299644c3af952c5db82612.jpg" + "https://i0.hdslb.com/bfs/live-key-frame/keyframe10170435000003044248mwowx0.jpg" ] assert post.compress == True + # 标题变更 + mock_bili_live_status["data"][target]["title"] = "【Zc】从0挑战到15肉鸽!目前11难度" + bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status)) + res3 = await bili_live.fetch_new_post(target, [dummy_only_status_user_subinfo]) + assert bili_live_router.call_count == 3 + assert len(res3[0][1]) == 0 + + +@pytest.fixture +def dummy_only_title_user_subinfo(app: App): + from nonebot_bison.types import User, UserSubInfo + + user = User(123, "group") + return UserSubInfo(user=user, categories=[2], tags=[]) + + +@pytest.mark.asyncio +@respx.mock +async def test_fetch_bililive_only_title_change( + bili_live, dummy_only_title_user_subinfo +): + mock_bili_live_status = get_json("bili_live_status.json") + target = "13164144" + + bili_live_router = respx.get( + "http://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)) + + res = await bili_live.fetch_new_post(target, [dummy_only_title_user_subinfo]) + assert bili_live_router.call_count == 1 + assert len(res) == 0 + # 未开播前标题变更 + mock_bili_live_status["data"][target]["title"] = "【Zc】从0挑战到15肉鸽!目前11难度" + bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status)) + res0 = await bili_live.fetch_new_post(target, [dummy_only_title_user_subinfo]) + assert bili_live_router.call_count == 2 + assert len(res0) == 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_title_user_subinfo]) + assert bili_live_router.call_count == 3 + assert len(res2[0][1]) == 0 + # 标题变更 + mock_bili_live_status["data"][target]["title"] = "【Zc】从0挑战到15肉鸽!目前12难度" + bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status)) + res3 = await bili_live.fetch_new_post(target, [dummy_only_title_user_subinfo]) + post = res3[0][1][0] + assert post.target_type == "Bilibili直播" + assert post.text == "【Zc】从0挑战到15肉鸽!目前12难度" + assert post.url == "https://live.bilibili.com/3044248" + assert post.target_name == "魔法Zc目录" + assert post.pics == [ + "https://i0.hdslb.com/bfs/live-key-frame/keyframe10170435000003044248mwowx0.jpg" + ] + assert post.compress == True + + +@pytest.fixture +def dummy_bililive_user_subinfo(app: App): + from nonebot_bison.types import User, UserSubInfo + + user = User(123, "group") + return UserSubInfo(user=user, categories=[1, 2], tags=[]) + + +@pytest.mark.asyncio +@respx.mock +async def test_fetch_bililive_combo(bili_live, dummy_bililive_user_subinfo): + mock_bili_live_status = get_json("bili_live_status.json") + target = "13164144" + + bili_live_router = respx.get( + "http://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)) + + res = await bili_live.fetch_new_post(target, [dummy_bililive_user_subinfo]) + assert bili_live_router.call_count == 1 + assert len(res) == 0 + # 未开播前标题变更 + mock_bili_live_status["data"][target]["title"] = "【Zc】从0挑战到15肉鸽!目前11难度" + bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status)) + res0 = await bili_live.fetch_new_post(target, [dummy_bililive_user_subinfo]) + assert bili_live_router.call_count == 2 + assert len(res0) == 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_bililive_user_subinfo]) + post2 = res2[0][1][0] + assert post2.target_type == "Bilibili直播" + assert post2.text == "【Zc】从0挑战到15肉鸽!目前11难度" + assert post2.url == "https://live.bilibili.com/3044248" + assert post2.target_name == "魔法Zc目录" + assert post2.pics == [ + "https://i0.hdslb.com/bfs/live-key-frame/keyframe10170435000003044248mwowx0.jpg" + ] + assert post2.compress == True + # 标题变更 + mock_bili_live_status["data"][target]["title"] = "【Zc】从0挑战到15肉鸽!目前12难度" + bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status)) + res3 = await bili_live.fetch_new_post(target, [dummy_bililive_user_subinfo]) + post3 = res3[0][1][0] + assert post3.target_type == "Bilibili直播" + assert post3.text == "【Zc】从0挑战到15肉鸽!目前12难度" + assert post3.url == "https://live.bilibili.com/3044248" + assert post3.target_name == "魔法Zc目录" + assert post3.pics == [ + "https://i0.hdslb.com/bfs/live-key-frame/keyframe10170435000003044248mwowx0.jpg" + ] + assert post3.compress == True