提供批量 api 接口 (#290)

* 🚧 add batch api

* 🚧 support batch in scheduler

*  use batch api in bilibili-live

 patch platform_manager directly

 use batch api in bilibili-live

 patch platform_manager directly

* ♻️ refactor var name

* 🐛 fix test

* 🐛 fix scheduler

* 🐛 fix test
This commit is contained in:
felinae98
2023-08-29 21:12:42 +08:00
committed by GitHub
parent 219e3ba5c6
commit e7dcfdee50
16 changed files with 519 additions and 166 deletions
+11 -7
View File
@@ -49,6 +49,8 @@ async def test_fetch_new(
monster_siren_list_0,
monster_siren_list_1,
):
from nonebot_bison.types import Target, SubUnit
ak_list_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletinList?target=IOS")
detail_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/5716")
version_router = respx.get("https://ak-conf.hypergryph.com/config/prod/official/IOS/version")
@@ -63,14 +65,14 @@ async def test_fetch_new(
preannouncement_router.mock(return_value=Response(200, json=get_json("arknights-pre-0.json")))
monster_siren_router.mock(return_value=Response(200, json=monster_siren_list_0))
terra_list.mock(return_value=Response(200, json=get_json("terra-hist-0.json")))
target = ""
res = await arknights.fetch_new_post(target, [dummy_user_subinfo])
target = Target("")
res = await arknights.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert ak_list_router.called
assert len(res) == 0
assert not detail_router.called
mock_data = arknights_list_0
ak_list_router.mock(return_value=Response(200, json=mock_data))
res3 = await arknights.fetch_new_post(target, [dummy_user_subinfo])
res3 = await arknights.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert len(res3[0][1]) == 1
assert detail_router.called
post = res3[0][1][0]
@@ -82,7 +84,7 @@ async def test_fetch_new(
# assert(post.pics == ['https://ak-fs.hypergryph.com/announce/images/20210623/e6f49aeb9547a2278678368a43b95b07.jpg'])
await post.generate_messages()
terra_list.mock(return_value=Response(200, json=get_json("terra-hist-1.json")))
res = await arknights.fetch_new_post(target, [dummy_user_subinfo])
res = await arknights.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert len(res) == 1
post = res[0][1][0]
assert post.target_type == "terra-historicus"
@@ -101,6 +103,8 @@ async def test_send_with_render(
monster_siren_list_0,
monster_siren_list_1,
):
from nonebot_bison.types import Target, SubUnit
ak_list_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletinList?target=IOS")
detail_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/8397")
version_router = respx.get("https://ak-conf.hypergryph.com/config/prod/official/IOS/version")
@@ -115,14 +119,14 @@ async def test_send_with_render(
preannouncement_router.mock(return_value=Response(200, json=get_json("arknights-pre-0.json")))
monster_siren_router.mock(return_value=Response(200, json=monster_siren_list_0))
terra_list.mock(return_value=Response(200, json=get_json("terra-hist-0.json")))
target = ""
res = await arknights.fetch_new_post(target, [dummy_user_subinfo])
target = Target("")
res = await arknights.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert ak_list_router.called
assert len(res) == 0
assert not detail_router.called
mock_data = arknights_list_1
ak_list_router.mock(return_value=Response(200, json=mock_data))
res3 = await arknights.fetch_new_post(target, [dummy_user_subinfo])
res3 = await arknights.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert len(res3[0][1]) == 1
assert detail_router.called
post = res3[0][1][0]
+9 -5
View File
@@ -106,14 +106,16 @@ async def test_dynamic_forward(bilibili, bing_dy_list):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_new_without_dynamic(bilibili, dummy_user_subinfo, without_dynamic):
from nonebot_bison.types import Target, SubUnit
post_router = respx.get(
"https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?host_uid=161775300&offset=0&need_top=0"
)
post_router.mock(return_value=Response(200, json=without_dynamic))
bilibili_main_page_router = respx.get("https://www.bilibili.com/")
bilibili_main_page_router.mock(return_value=Response(200))
target = "161775300"
res = await bilibili.fetch_new_post(target, [dummy_user_subinfo])
target = Target("161775300")
res = await bilibili.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert post_router.called
assert len(res) == 0
@@ -121,21 +123,23 @@ async def test_fetch_new_without_dynamic(bilibili, dummy_user_subinfo, without_d
@pytest.mark.asyncio
@respx.mock
async def test_fetch_new(bilibili, dummy_user_subinfo):
from nonebot_bison.types import Target, SubUnit
post_router = respx.get(
"https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?host_uid=161775300&offset=0&need_top=0"
)
post_router.mock(return_value=Response(200, json=get_json("bilibili_strange_post-0.json")))
bilibili_main_page_router = respx.get("https://www.bilibili.com/")
bilibili_main_page_router.mock(return_value=Response(200))
target = "161775300"
res = await bilibili.fetch_new_post(target, [dummy_user_subinfo])
target = Target("161775300")
res = await bilibili.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert post_router.called
assert len(res) == 0
mock_data = get_json("bilibili_strange_post.json")
mock_data["data"]["cards"][0]["desc"]["timestamp"] = int(datetime.now().timestamp())
post_router.mock(return_value=Response(200, json=mock_data))
res2 = await bilibili.fetch_new_post(target, [dummy_user_subinfo])
res2 = await bilibili.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert len(res2[0][1]) == 1
post = res2[0][1][0]
assert (
+4 -4
View File
@@ -35,7 +35,7 @@ async def test_parse_target(bili_bangumi: "BilibiliBangumi"):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_bilibili_bangumi_status(bili_bangumi: "BilibiliBangumi", dummy_user_subinfo):
from nonebot_bison.types import Target
from nonebot_bison.types import Target, SubUnit
bili_bangumi_router = respx.get("https://api.bilibili.com/pgc/review/user?media_id=28235413")
bili_bangumi_detail_router = respx.get("https://api.bilibili.com/pgc/view/web/season?season_id=39719")
@@ -43,15 +43,15 @@ async def test_fetch_bilibili_bangumi_status(bili_bangumi: "BilibiliBangumi", du
bilibili_main_page_router = respx.get("https://www.bilibili.com/")
bilibili_main_page_router.mock(return_value=Response(200))
target = Target("28235413")
res = await bili_bangumi.fetch_new_post(target, [dummy_user_subinfo])
res = await bili_bangumi.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert len(res) == 0
res = await bili_bangumi.fetch_new_post(target, [dummy_user_subinfo])
res = await bili_bangumi.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert len(res) == 0
bili_bangumi_router.mock(return_value=Response(200, json=get_json("bilibili-gangumi-hanhua1.json")))
bili_bangumi_detail_router.mock(return_value=Response(200, json=get_json("bilibili-gangumi-hanhua1-detail.json")))
res2 = await bili_bangumi.fetch_new_post(target, [dummy_user_subinfo])
res2 = await bili_bangumi.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
post = res2[0][1][0]
assert post.target_type == "Bilibili剧集"
+42 -30
View File
@@ -29,16 +29,18 @@ def dummy_only_open_user_subinfo(app: App):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_bililive_no_room(bili_live, dummy_only_open_user_subinfo):
from nonebot_bison.types import Target, SubUnit
mock_bili_live_status = get_json("bili_live_status.json")
mock_bili_live_status["data"] = []
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])
target = Target("13164144")
res = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_open_user_subinfo]))])
assert bili_live_router.call_count == 1
assert len(res) == 0
@@ -46,23 +48,25 @@ async def test_fetch_bililive_no_room(bili_live, dummy_only_open_user_subinfo):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_first_live(bili_live, dummy_only_open_user_subinfo):
from nonebot_bison.types import Target, SubUnit
mock_bili_live_status = get_json("bili_live_status.json")
empty_bili_live_status = deepcopy(mock_bili_live_status)
empty_bili_live_status["data"] = []
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])
target = Target("13164144")
res = await bili_live.batch_fetch_new_post([(SubUnit(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])
res2 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_open_user_subinfo]))])
assert bili_live_router.call_count == 2
assert len(res2) == 1
post = res2[0][1][0]
@@ -77,6 +81,8 @@ async def test_fetch_first_live(bili_live, dummy_only_open_user_subinfo):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_bililive_only_live_open(bili_live, dummy_only_open_user_subinfo):
from nonebot_bison.types import Target, SubUnit
mock_bili_live_status = get_json("bili_live_status.json")
bili_live_router = respx.get("https://api.live.bilibili.com/room/v1/Room/get_status_info_by_uids?uids[]=13164144")
@@ -85,14 +91,14 @@ async def test_fetch_bililive_only_live_open(bili_live, dummy_only_open_user_sub
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])
target = Target("13164144")
res = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_open_user_subinfo]))])
assert bili_live_router.call_count == 1
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))
res2 = await bili_live.fetch_new_post(target, [dummy_only_open_user_subinfo])
res2 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_open_user_subinfo]))])
post = res2[0][1][0]
assert post.target_type == "Bilibili直播"
assert post.text == "[开播] 【Zc】从0挑战到15肉鸽!目前10难度"
@@ -103,13 +109,13 @@ async def test_fetch_bililive_only_live_open(bili_live, dummy_only_open_user_sub
# 标题变更
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_open_user_subinfo])
res3 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_open_user_subinfo]))])
assert bili_live_router.call_count == 3
assert len(res3[0][1]) == 0
# 直播状态更新-下播
mock_bili_live_status["data"][target]["live_status"] = 0
bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status))
res4 = await bili_live.fetch_new_post(target, [dummy_only_open_user_subinfo])
res4 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_open_user_subinfo]))])
assert bili_live_router.call_count == 4
assert len(res4[0][1]) == 0
@@ -127,8 +133,10 @@ def dummy_only_title_user_subinfo(app: App):
@pytest.mark.asyncio()
@respx.mock
async def test_fetch_bililive_only_title_change(bili_live, dummy_only_title_user_subinfo):
from nonebot_bison.types import Target, SubUnit
mock_bili_live_status = get_json("bili_live_status.json")
target = "13164144"
target = Target("13164144")
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))
@@ -136,25 +144,25 @@ async def test_fetch_bililive_only_title_change(bili_live, dummy_only_title_user
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])
res = await bili_live.batch_fetch_new_post([(SubUnit(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])
res0 = await bili_live.batch_fetch_new_post([(SubUnit(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])
res2 = await bili_live.batch_fetch_new_post([(SubUnit(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])
res3 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_title_user_subinfo]))])
post = res3[0][1][0]
assert post.target_type == "Bilibili直播"
assert post.text == "[标题更新] 【Zc】从0挑战到15肉鸽!目前12难度"
@@ -165,7 +173,7 @@ async def test_fetch_bililive_only_title_change(bili_live, dummy_only_title_user
# 直播状态更新-下播
mock_bili_live_status["data"][target]["live_status"] = 0
bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status))
res4 = await bili_live.fetch_new_post(target, [dummy_only_title_user_subinfo])
res4 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_title_user_subinfo]))])
assert bili_live_router.call_count == 5
assert len(res4[0][1]) == 0
@@ -183,8 +191,10 @@ def dummy_only_close_user_subinfo(app: App):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_bililive_only_close(bili_live, dummy_only_close_user_subinfo):
from nonebot_bison.types import Target, SubUnit
mock_bili_live_status = get_json("bili_live_status.json")
target = "13164144"
target = Target("13164144")
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))
@@ -192,31 +202,31 @@ async def test_fetch_bililive_only_close(bili_live, dummy_only_close_user_subinf
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_close_user_subinfo])
res = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_close_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_close_user_subinfo])
res0 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_close_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_close_user_subinfo])
res2 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_close_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_close_user_subinfo])
res3 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_close_user_subinfo]))])
assert bili_live_router.call_count == 4
assert len(res3[0][1]) == 0
# 直播状态更新-下播
mock_bili_live_status["data"][target]["live_status"] = 0
bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status))
res4 = await bili_live.fetch_new_post(target, [dummy_only_close_user_subinfo])
res4 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_only_close_user_subinfo]))])
assert bili_live_router.call_count == 5
post = res4[0][1][0]
assert post.target_type == "Bilibili直播"
@@ -240,8 +250,10 @@ def dummy_bililive_user_subinfo(app: App):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_bililive_combo(bili_live, dummy_bililive_user_subinfo):
from nonebot_bison.types import Target, SubUnit
mock_bili_live_status = get_json("bili_live_status.json")
target = "13164144"
target = Target("13164144")
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))
@@ -249,19 +261,19 @@ async def test_fetch_bililive_combo(bili_live, dummy_bililive_user_subinfo):
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])
res = await bili_live.batch_fetch_new_post([(SubUnit(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])
res0 = await bili_live.batch_fetch_new_post([(SubUnit(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])
res2 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_bililive_user_subinfo]))])
post2 = res2[0][1][0]
assert post2.target_type == "Bilibili直播"
assert post2.text == "[开播] 【Zc】从0挑战到15肉鸽!目前11难度"
@@ -272,7 +284,7 @@ async def test_fetch_bililive_combo(bili_live, dummy_bililive_user_subinfo):
# 标题变更
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])
res3 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_bililive_user_subinfo]))])
post3 = res3[0][1][0]
assert post3.target_type == "Bilibili直播"
assert post3.text == "[标题更新] 【Zc】从0挑战到15肉鸽!目前12难度"
@@ -283,7 +295,7 @@ async def test_fetch_bililive_combo(bili_live, dummy_bililive_user_subinfo):
# 直播状态更新-下播
mock_bili_live_status["data"][target]["live_status"] = 0
bili_live_router.mock(return_value=Response(200, json=mock_bili_live_status))
res4 = await bili_live.fetch_new_post(target, [dummy_bililive_user_subinfo])
res4 = await bili_live.batch_fetch_new_post([(SubUnit(target, [dummy_bililive_user_subinfo]))])
post4 = res4[0][1][0]
assert post4.target_type == "Bilibili直播"
assert post4.text == "[下播] 【Zc】从0挑战到15肉鸽!目前12难度"
+5 -3
View File
@@ -27,16 +27,18 @@ def ff14_newdata_json_1():
@pytest.mark.asyncio
@respx.mock
async def test_fetch_new(ff14, dummy_user_subinfo, ff14_newdata_json_0, ff14_newdata_json_1):
from nonebot_bison.types import Target, SubUnit
newdata = respx.get(
"https://cqnews.web.sdo.com/api/news/newsList?gameCode=ff&CategoryCode=5309,5310,5311,5312,5313&pageIndex=0&pageSize=5"
)
newdata.mock(return_value=Response(200, json=ff14_newdata_json_0))
target = ""
res = await ff14.fetch_new_post(target, [dummy_user_subinfo])
target = Target("")
res = await ff14.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert newdata.called
assert len(res) == 0
newdata.mock(return_value=Response(200, json=ff14_newdata_json_1))
res = await ff14.fetch_new_post(target, [dummy_user_subinfo])
res = await ff14.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert newdata.called
post = res[0][1][0]
assert post.target_type == "ff14"
+5 -3
View File
@@ -41,14 +41,16 @@ def ncm_artist_1(ncm_artist_raw: dict):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_new(ncm_artist, ncm_artist_0, ncm_artist_1, dummy_user_subinfo):
from nonebot_bison.types import Target, SubUnit
ncm_router = respx.get("https://music.163.com/api/artist/albums/32540734")
ncm_router.mock(return_value=Response(200, json=ncm_artist_0))
target = "32540734"
res = await ncm_artist.fetch_new_post(target, [dummy_user_subinfo])
target = Target("32540734")
res = await ncm_artist.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert ncm_router.called
assert len(res) == 0
ncm_router.mock(return_value=Response(200, json=ncm_artist_1))
res2 = await ncm_artist.fetch_new_post(target, [dummy_user_subinfo])
res2 = await ncm_artist.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
post = res2[0][1][0]
assert post.target_type == "ncm-artist"
assert post.text == "新专辑发布:Y1K"
+5 -3
View File
@@ -41,14 +41,16 @@ def ncm_radio_1(ncm_radio_raw: dict):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_new(ncm_radio, ncm_radio_0, ncm_radio_1, dummy_user_subinfo):
from nonebot_bison.types import Target, SubUnit
ncm_router = respx.post("http://music.163.com/api/dj/program/byradio")
ncm_router.mock(return_value=Response(200, json=ncm_radio_0))
target = "793745436"
res = await ncm_radio.fetch_new_post(target, [dummy_user_subinfo])
target = Target("793745436")
res = await ncm_radio.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert ncm_router.called
assert len(res) == 0
ncm_router.mock(return_value=Response(200, json=ncm_radio_1))
res2 = await ncm_radio.fetch_new_post(target, [dummy_user_subinfo])
res2 = await ncm_radio.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
post = res2[0][1][0]
assert post.target_type == "ncm-radio"
assert post.text == "网易云电台更新:「松烟行动」灰齐山麓"
+203 -32
View File
@@ -325,16 +325,14 @@ def mock_status_change(app: App):
@pytest.mark.asyncio
async def test_new_message_target_without_cats_tags(mock_platform_without_cats_tags, user_info_factory):
from nonebot_bison.utils import ProcessContext
from nonebot_bison.types import Target, SubUnit
res1 = await mock_platform_without_cats_tags(ProcessContext(), AsyncClient()).fetch_new_post(
"dummy", [user_info_factory([1, 2], [])]
SubUnit(Target("dummy"), [user_info_factory([1, 2], [])])
)
assert len(res1) == 0
res2 = await mock_platform_without_cats_tags(ProcessContext(), AsyncClient()).fetch_new_post(
"dummy",
[
user_info_factory([], []),
],
SubUnit(Target("dummy"), [user_info_factory([], [])]),
)
assert len(res2) == 1
posts_1 = res2[0][1]
@@ -348,16 +346,21 @@ async def test_new_message_target_without_cats_tags(mock_platform_without_cats_t
@pytest.mark.asyncio
async def test_new_message_target(mock_platform, user_info_factory):
from nonebot_bison.utils import ProcessContext
from nonebot_bison.types import Target, SubUnit
res1 = await mock_platform(ProcessContext(), AsyncClient()).fetch_new_post("dummy", [user_info_factory([1, 2], [])])
res1 = await mock_platform(ProcessContext(), AsyncClient()).fetch_new_post(
SubUnit(Target("dummy"), [user_info_factory([1, 2], [])])
)
assert len(res1) == 0
res2 = await mock_platform(ProcessContext(), AsyncClient()).fetch_new_post(
"dummy",
[
user_info_factory([1, 2], []),
user_info_factory([1], []),
user_info_factory([1, 2], ["tag1"]),
],
SubUnit(
Target("dummy"),
[
user_info_factory([1, 2], []),
user_info_factory([1], []),
user_info_factory([1, 2], ["tag1"]),
],
),
)
assert len(res2) == 3
posts_1 = res2[0][1]
@@ -378,18 +381,21 @@ async def test_new_message_target(mock_platform, user_info_factory):
@pytest.mark.asyncio
async def test_new_message_no_target(mock_platform_no_target, user_info_factory):
from nonebot_bison.utils import ProcessContext
from nonebot_bison.types import Target, SubUnit
res1 = await mock_platform_no_target(ProcessContext(), AsyncClient()).fetch_new_post(
"dummy", [user_info_factory([1, 2], [])]
SubUnit(Target("dummy"), [user_info_factory([1, 2], [])])
)
assert len(res1) == 0
res2 = await mock_platform_no_target(ProcessContext(), AsyncClient()).fetch_new_post(
"dummy",
[
user_info_factory([1, 2], []),
user_info_factory([1], []),
user_info_factory([1, 2], ["tag1"]),
],
SubUnit(
Target("dummy"),
[
user_info_factory([1, 2], []),
user_info_factory([1], []),
user_info_factory([1, 2], ["tag1"]),
],
),
)
assert len(res2) == 3
posts_1 = res2[0][1]
@@ -406,7 +412,7 @@ async def test_new_message_no_target(mock_platform_no_target, user_info_factory)
assert "p2" in id_set_2
assert "p2" in id_set_3
res3 = await mock_platform_no_target(ProcessContext(), AsyncClient()).fetch_new_post(
"dummy", [user_info_factory([1, 2], [])]
SubUnit(Target("dummy"), [user_info_factory([1, 2], [])])
)
assert len(res3) == 0
@@ -414,31 +420,34 @@ async def test_new_message_no_target(mock_platform_no_target, user_info_factory)
@pytest.mark.asyncio
async def test_status_change(mock_status_change, user_info_factory):
from nonebot_bison.utils import ProcessContext
from nonebot_bison.types import Target, SubUnit
res1 = await mock_status_change(ProcessContext(), AsyncClient()).fetch_new_post(
"dummy", [user_info_factory([1, 2], [])]
SubUnit(Target("dummy"), [user_info_factory([1, 2], [])])
)
assert len(res1) == 0
res2 = await mock_status_change(ProcessContext(), AsyncClient()).fetch_new_post(
"dummy", [user_info_factory([1, 2], [])]
SubUnit(Target("dummy"), [user_info_factory([1, 2], [])])
)
assert len(res2) == 1
posts = res2[0][1]
assert len(posts) == 1
assert posts[0].text == "on"
res3 = await mock_status_change(ProcessContext(), AsyncClient()).fetch_new_post(
"dummy",
[
user_info_factory([1, 2], []),
user_info_factory([1], []),
],
SubUnit(
Target("dummy"),
[
user_info_factory([1, 2], []),
user_info_factory([1], []),
],
),
)
assert len(res3) == 2
assert len(res3[0][1]) == 1
assert res3[0][1][0].text == "off"
assert len(res3[1][1]) == 0
res4 = await mock_status_change(ProcessContext(), AsyncClient()).fetch_new_post(
"dummy", [user_info_factory([1, 2], [])]
SubUnit(Target("dummy"), [user_info_factory([1, 2], [])])
)
assert len(res4) == 0
@@ -450,7 +459,7 @@ async def test_group(
mock_platform_no_target_2,
user_info_factory,
):
from nonebot_bison.types import Target
from nonebot_bison.types import Target, SubUnit
from nonebot_bison.utils import ProcessContext, http_client
from nonebot_bison.platform.platform import make_no_target_group
@@ -458,14 +467,176 @@ async def test_group(
group_platform_class = make_no_target_group([mock_platform_no_target, mock_platform_no_target_2])
group_platform = group_platform_class(ProcessContext(), http_client())
res1 = await group_platform.fetch_new_post(dummy, [user_info_factory([1, 4], [])])
res1 = await group_platform.fetch_new_post(SubUnit(dummy, [user_info_factory([1, 4], [])]))
assert len(res1) == 0
res2 = await group_platform.fetch_new_post(dummy, [user_info_factory([1, 4], [])])
res2 = await group_platform.fetch_new_post(SubUnit(dummy, [user_info_factory([1, 4], [])]))
assert len(res2) == 1
posts = res2[0][1]
assert len(posts) == 2
id_set_2 = {x.text for x in posts}
assert "p2" in id_set_2
assert "p6" in id_set_2
res3 = await group_platform.fetch_new_post(dummy, [user_info_factory([1, 4], [])])
res3 = await group_platform.fetch_new_post(SubUnit(dummy, [user_info_factory([1, 4], [])]))
assert len(res3) == 0
async def test_batch_fetch_new_message(app: App):
from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.post import Post
from nonebot_bison.platform.platform import NewMessage
from nonebot_bison.utils.context import ProcessContext
from nonebot_bison.types import Target, RawPost, SubUnit, UserSubInfo
class BatchNewMessage(NewMessage):
platform_name = "mock_platform"
name = "Mock Platform"
enabled = True
is_common = True
schedule_interval = 10
enable_tag = False
categories = {}
has_target = True
sub_index = 0
@classmethod
async def get_target_name(cls, client, _: "Target"):
return "MockPlatform"
def get_id(self, post: "RawPost") -> Any:
return post["id"]
def get_date(self, raw_post: "RawPost") -> float:
return raw_post["date"]
async def parse(self, raw_post: "RawPost") -> "Post":
return Post(
"mock_platform",
raw_post["text"],
"http://t.tt/" + str(self.get_id(raw_post)),
target_name="Mock",
)
@classmethod
async def batch_get_sub_list(cls, targets: list[Target]) -> list[list[RawPost]]:
assert len(targets) > 1
if cls.sub_index == 0:
cls.sub_index += 1
res = [
[raw_post_list_2[0]],
[raw_post_list_2[1]],
]
else:
res = [
[raw_post_list_2[0], raw_post_list_2[2]],
[raw_post_list_2[1], raw_post_list_2[3]],
]
res += [[]] * (len(targets) - 2)
return res
user1 = UserSubInfo(TargetQQGroup(group_id=123), [1, 2, 3], [])
user2 = UserSubInfo(TargetQQGroup(group_id=234), [1, 2, 3], [])
platform_obj = BatchNewMessage(ProcessContext(), None) # type:ignore
res1 = await platform_obj.batch_fetch_new_post(
[
SubUnit(Target("target1"), [user1]),
SubUnit(Target("target2"), [user1, user2]),
SubUnit(Target("target3"), [user2]),
]
)
assert len(res1) == 0
res2 = await platform_obj.batch_fetch_new_post(
[
SubUnit(Target("target1"), [user1]),
SubUnit(Target("target2"), [user1, user2]),
SubUnit(Target("target3"), [user2]),
]
)
assert len(res2) == 3
send_set = set()
for platform_target, posts in res2:
for post in posts:
send_set.add((platform_target, post.text))
assert (TargetQQGroup(group_id=123), "p3") in send_set
assert (TargetQQGroup(group_id=123), "p4") in send_set
assert (TargetQQGroup(group_id=234), "p4") in send_set
async def test_batch_fetch_compare_status(app: App):
from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.post import Post
from nonebot_bison.utils.context import ProcessContext
from nonebot_bison.platform.platform import StatusChange
from nonebot_bison.types import Target, RawPost, SubUnit, Category, UserSubInfo
class BatchStatusChange(StatusChange):
platform_name = "mock_platform"
name = "Mock Platform"
enabled = True
is_common = True
enable_tag = False
schedule_type = "interval"
schedule_kw = {"seconds": 10}
has_target = False
categories = {
Category(1): "转发",
Category(2): "视频",
}
sub_index = 0
@classmethod
async def batch_get_status(cls, targets: "list[Target]"):
assert len(targets) > 0
res = [{"s": cls.sub_index == 1} for _ in targets]
res[0]["s"] = not res[0]["s"]
if cls.sub_index == 0:
cls.sub_index += 1
return res
def compare_status(self, target, old_status, new_status) -> list["RawPost"]:
if old_status["s"] is False and new_status["s"] is True:
return [{"text": "on", "cat": 1}]
elif old_status["s"] is True and new_status["s"] is False:
return [{"text": "off", "cat": 2}]
return []
async def parse(self, raw_post) -> "Post":
return Post("mock_status", raw_post["text"], "")
def get_category(self, raw_post):
return raw_post["cat"]
batch_status_change = BatchStatusChange(ProcessContext(), None) # type: ignore
user1 = UserSubInfo(TargetQQGroup(group_id=123), [1, 2, 3], [])
user2 = UserSubInfo(TargetQQGroup(group_id=234), [1, 2, 3], [])
res1 = await batch_status_change.batch_fetch_new_post(
[
SubUnit(Target("target1"), [user1]),
SubUnit(Target("target2"), [user1, user2]),
]
)
assert len(res1) == 0
res2 = await batch_status_change.batch_fetch_new_post(
[
SubUnit(Target("target1"), [user1]),
SubUnit(Target("target2"), [user1, user2]),
]
)
send_set = set()
for platform_target, posts in res2:
for post in posts:
send_set.add((platform_target, post.text))
assert len(send_set) == 3
assert (TargetQQGroup(group_id=123), "off") in send_set
assert (TargetQQGroup(group_id=123), "on") in send_set
assert (TargetQQGroup(group_id=234), "on") in send_set
+20 -12
View File
@@ -68,15 +68,17 @@ async def test_fetch_new_1(
user_info_factory,
update_time_feed_1,
):
from nonebot_bison.types import Target, SubUnit
## 标题重复的情况
rss_router = respx.get("https://rsshub.app/twitter/user/ArknightsStaff")
rss_router.mock(return_value=Response(200, text=get_file("rss-twitter-ArknightsStaff-0.xml")))
target = "https://rsshub.app/twitter/user/ArknightsStaff"
res1 = await rss.fetch_new_post(target, [user_info_factory([], [])])
target = Target("https://rsshub.app/twitter/user/ArknightsStaff")
res1 = await rss.fetch_new_post(SubUnit(target, [user_info_factory([], [])]))
assert len(res1) == 0
rss_router.mock(return_value=Response(200, text=update_time_feed_1))
res2 = await rss.fetch_new_post(target, [user_info_factory([], [])])
res2 = await rss.fetch_new_post(SubUnit(target, [user_info_factory([], [])]))
assert len(res2[0][1]) == 1
post1 = res2[0][1][0]
assert post1.url == "https://twitter.com/ArknightsStaff/status/1659091539023282178"
@@ -95,15 +97,17 @@ async def test_fetch_new_2(
user_info_factory,
update_time_feed_2,
):
from nonebot_bison.types import Target, SubUnit
## 标题与正文不重复的情况
rss_router = respx.get("https://www.ruanyifeng.com/blog/atom.xml")
rss_router.mock(return_value=Response(200, text=get_file("rss-ruanyifeng-0.xml")))
target = "https://www.ruanyifeng.com/blog/atom.xml"
res1 = await rss.fetch_new_post(target, [user_info_factory([], [])])
target = Target("https://www.ruanyifeng.com/blog/atom.xml")
res1 = await rss.fetch_new_post(SubUnit(target, [user_info_factory([], [])]))
assert len(res1) == 0
rss_router.mock(return_value=Response(200, text=update_time_feed_2))
res2 = await rss.fetch_new_post(target, [user_info_factory([], [])])
res2 = await rss.fetch_new_post(SubUnit(target, [user_info_factory([], [])]))
assert len(res2[0][1]) == 1
post1 = res2[0][1][0]
assert post1.url == "http://www.ruanyifeng.com/blog/2023/05/weekly-issue-255.html"
@@ -129,15 +133,17 @@ async def test_fetch_new_3(
user_info_factory,
update_time_feed_3,
):
from nonebot_bison.types import Target, SubUnit
## 只有<updated>没有<published>
rss_router = respx.get("https://github.com/R3nzTheCodeGOD/R3nzSkin/releases.atom")
rss_router.mock(return_value=Response(200, text=get_file("rss-github-atom-0.xml")))
target = "https://github.com/R3nzTheCodeGOD/R3nzSkin/releases.atom"
res1 = await rss.fetch_new_post(target, [user_info_factory([], [])])
target = Target("https://github.com/R3nzTheCodeGOD/R3nzSkin/releases.atom")
res1 = await rss.fetch_new_post(SubUnit(target, [user_info_factory([], [])]))
assert len(res1) == 0
rss_router.mock(return_value=Response(200, text=update_time_feed_3))
res2 = await rss.fetch_new_post(target, [user_info_factory([], [])])
res2 = await rss.fetch_new_post(SubUnit(target, [user_info_factory([], [])]))
assert len(res2[0][1]) == 1
post1 = res2[0][1][0]
assert post1.url == "https://github.com/R3nzTheCodeGOD/R3nzSkin/releases/tag/v3.0.9"
@@ -150,15 +156,17 @@ async def test_fetch_new_4(
rss,
user_info_factory,
):
from nonebot_bison.types import Target, SubUnit
## 没有日期信息的情况
rss_router = respx.get("https://rsshub.app/wallhaven/hot?limit=5")
rss_router.mock(return_value=Response(200, text=get_file("rss-top5-old.xml")))
target = "https://rsshub.app/wallhaven/hot?limit=5"
res1 = await rss.fetch_new_post(target, [user_info_factory([], [])])
target = Target("https://rsshub.app/wallhaven/hot?limit=5")
res1 = await rss.fetch_new_post(SubUnit(target, [user_info_factory([], [])]))
assert len(res1) == 0
rss_router.mock(return_value=Response(200, text=get_file("rss-top5-new.xml")))
res2 = await rss.fetch_new_post(target, [user_info_factory([], [])])
res2 = await rss.fetch_new_post(SubUnit(target, [user_info_factory([], [])]))
assert len(res2[0][1]) == 1
post1 = res2[0][1][0]
assert post1.url == "https://wallhaven.cc/w/85rjej"
+9 -5
View File
@@ -41,25 +41,27 @@ async def test_get_name(weibo):
@pytest.mark.asyncio
@respx.mock
async def test_fetch_new(weibo, dummy_user_subinfo):
from nonebot_bison.types import Target, SubUnit
ak_list_router = respx.get("https://m.weibo.cn/api/container/getIndex?containerid=1076036279793937")
detail_router = respx.get("https://m.weibo.cn/detail/4649031014551911")
ak_list_router.mock(return_value=Response(200, json=get_json("weibo_ak_list_0.json")))
detail_router.mock(return_value=Response(200, text=get_file("weibo_detail_4649031014551911")))
image_cdn_router.mock(Response(200, content=b""))
target = "6279793937"
res = await weibo.fetch_new_post(target, [dummy_user_subinfo])
target = Target("6279793937")
res = await weibo.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert ak_list_router.called
assert len(res) == 0
assert not detail_router.called
mock_data = get_json("weibo_ak_list_1.json")
ak_list_router.mock(return_value=Response(200, json=mock_data))
res2 = await weibo.fetch_new_post(target, [dummy_user_subinfo])
res2 = await weibo.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert len(res2) == 0
mock_data["data"]["cards"][1]["mblog"]["created_at"] = datetime.now(timezone("Asia/Shanghai")).strftime(
"%a %b %d %H:%M:%S %z %Y"
)
ak_list_router.mock(return_value=Response(200, json=mock_data))
res3 = await weibo.fetch_new_post(target, [dummy_user_subinfo])
res3 = await weibo.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
assert len(res3[0][1]) == 1
assert not detail_router.called
post = res3[0][1][0]
@@ -103,7 +105,9 @@ def test_tag(weibo, weibo_ak_list_1):
@pytest.mark.asyncio
@pytest.mark.compare
async def test_rsshub_compare(weibo):
target = "6279793937"
from nonebot_bison.types import Target
target = Target("6279793937")
raw_posts = filter(weibo.filter_platform_custom, await weibo.get_sub_list(target))
posts = []
for raw_post in raw_posts:
+41
View File
@@ -1,7 +1,9 @@
import typing
from datetime import time
from unittest.mock import AsyncMock
from nonebug import App
from httpx import AsyncClient
from pytest_mock import MockerFixture
if typing.TYPE_CHECKING:
@@ -50,6 +52,45 @@ async def test_scheduler_without_time(init_scheduler):
assert static_res["bilibili-live-t2"] == 3
async def test_scheduler_batch_api(init_scheduler, mocker: MockerFixture):
from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.config import config
from nonebot_bison.types import UserSubInfo
from nonebot_bison.scheduler import scheduler_dict
from nonebot_bison.types import Target as T_Target
from nonebot_bison.scheduler.manager import init_scheduler
from nonebot_bison.platform.bilibili import BilibiliSchedConf
await config.add_subscribe(TargetQQGroup(group_id=123), T_Target("t1"), "target1", "bilibili-live", [], [])
await config.add_subscribe(TargetQQGroup(group_id=123), T_Target("t2"), "target2", "bilibili-live", [], [])
mocker.patch.object(BilibiliSchedConf, "get_client", return_value=AsyncClient())
await init_scheduler()
batch_fetch_mock = AsyncMock()
class FakePlatform:
def __init__(self) -> None:
self.do_batch_fetch_new_post = batch_fetch_mock
fake_platform_obj = FakePlatform()
mocker.patch.dict(
"nonebot_bison.scheduler.scheduler.platform_manager",
{"bilibili-live": mocker.Mock(return_value=fake_platform_obj)},
)
await scheduler_dict[BilibiliSchedConf].exec_fetch()
batch_fetch_mock.assert_called_once_with(
[
(T_Target("t1"), [UserSubInfo(user=TargetQQGroup(group_id=123), categories=[], tags=[])]),
(T_Target("t2"), [UserSubInfo(user=TargetQQGroup(group_id=123), categories=[], tags=[])]),
]
)
async def test_scheduler_with_time(app: App, init_scheduler, mocker: MockerFixture):
from nonebot_plugin_saa import TargetQQGroup