mirror of
https://github.com/suyiiyii/nonebot-bison.git
synced 2025-06-04 02:26:11 +08:00
Add status chage and test
This commit is contained in:
parent
d0b29abed7
commit
9e6a9228f6
@ -20,7 +20,7 @@ class MonsterSiren(NewMessage, NoTargetMixin):
|
||||
async def get_target_name(_) -> str:
|
||||
return '塞壬唱片新闻'
|
||||
|
||||
async def get_sub_list(self) -> list[RawPost]:
|
||||
async def get_sub_list(self, _) -> list[RawPost]:
|
||||
async with httpx.AsyncClient() as client:
|
||||
raw_data = await client.get('https://monster-siren.hypergryph.com/api/news')
|
||||
return raw_data.json()['data']['list']
|
||||
|
@ -84,7 +84,14 @@ class CategoryMixin(metaclass=RegistryABCMeta, abstract=True):
|
||||
"Return category of given Rawpost"
|
||||
raise NotImplementedError()
|
||||
|
||||
class MessageProcessMixin(PlaformNameMixin, CategoryMixin, abstract=True):
|
||||
class ParsePostMixin(metaclass=RegistryABCMeta, abstract=True):
|
||||
|
||||
@abstractmethod
|
||||
async def parse(self, raw_post: RawPost) -> Post:
|
||||
"parse RawPost into post"
|
||||
...
|
||||
|
||||
class MessageProcessMixin(PlaformNameMixin, CategoryMixin, ParsePostMixin, abstract=True):
|
||||
"General message process fetch, parse, filter progress"
|
||||
|
||||
def __init__(self):
|
||||
@ -95,10 +102,6 @@ class MessageProcessMixin(PlaformNameMixin, CategoryMixin, abstract=True):
|
||||
def get_id(self, post: RawPost) -> Any:
|
||||
"Get post id of given RawPost"
|
||||
|
||||
@abstractmethod
|
||||
async def parse(self, raw_post: RawPost) -> Post:
|
||||
"parse RawPost into post"
|
||||
...
|
||||
|
||||
async def _parse_with_cache(self, raw_post: RawPost) -> Post:
|
||||
post_id = self.get_id(raw_post)
|
||||
@ -168,7 +171,7 @@ class NewMessageProcessMixin(StorageMixinProto, MessageProcessMixin, abstract=Tr
|
||||
self.set_stored_data(target, store)
|
||||
return res
|
||||
|
||||
class UserCustomFilterMixin(CategoryMixin, abstract=True):
|
||||
class UserCustomFilterMixin(CategoryMixin, ParsePostMixin, abstract=True):
|
||||
|
||||
categories: dict[Category, str]
|
||||
enable_tag: bool
|
||||
@ -196,6 +199,21 @@ class UserCustomFilterMixin(CategoryMixin, abstract=True):
|
||||
res.append(raw_post)
|
||||
return res
|
||||
|
||||
async def dispatch_user_post(self, target: Target, new_posts: list[RawPost], users: list[UserSubInfo]) -> list[tuple[User, list[Post]]]:
|
||||
res: list[tuple[User, list[Post]]] = []
|
||||
for user, category_getter, tag_getter in users:
|
||||
required_tags = tag_getter(target) if self.enable_tag else []
|
||||
cats = category_getter(target)
|
||||
user_raw_post = await self.filter_user_custom(new_posts, cats, required_tags)
|
||||
user_post: list[Post] = []
|
||||
for raw_post in user_raw_post:
|
||||
if isinstance(self, MessageProcessMixin):
|
||||
user_post.append(await self._parse_with_cache(raw_post))
|
||||
else:
|
||||
user_post.append(await self.parse(raw_post))
|
||||
res.append((user, user_post))
|
||||
return res
|
||||
|
||||
class Platform(metaclass=RegistryABCMeta, base=True):
|
||||
|
||||
# schedule_interval: int
|
||||
@ -220,12 +238,12 @@ class NewMessage(
|
||||
UserCustomFilterMixin,
|
||||
abstract=True
|
||||
):
|
||||
"Fetch a list of messages, filter the new messages, dispatch it to different users"
|
||||
|
||||
async def fetch_new_post(self, target: Target, users: list[UserSubInfo]) -> list[tuple[User, list[Post]]]:
|
||||
try:
|
||||
post_list = await self.get_sub_list(target)
|
||||
new_posts = await self.filter_common_with_diff(target, post_list)
|
||||
res: list[tuple[User, list[Post]]] = []
|
||||
if not new_posts:
|
||||
return []
|
||||
else:
|
||||
@ -234,17 +252,44 @@ class NewMessage(
|
||||
self.platform_name,
|
||||
target if self.has_target else '-',
|
||||
self.get_id(post)))
|
||||
for user, category_getter, tag_getter in users:
|
||||
required_tags = tag_getter(target) if self.enable_tag else []
|
||||
cats = category_getter(target)
|
||||
user_raw_post = await self.filter_user_custom(new_posts, cats, required_tags)
|
||||
user_post: list[Post] = []
|
||||
for raw_post in user_raw_post:
|
||||
user_post.append(await self._parse_with_cache(raw_post))
|
||||
res.append((user, user_post))
|
||||
res = await self.dispatch_user_post(target, new_posts, users)
|
||||
self.parse_cache = {}
|
||||
return res
|
||||
except httpx.RequestError as err:
|
||||
logger.warning("network connection error: {}, url: {}".format(type(err), err.request.url))
|
||||
return []
|
||||
|
||||
class StatusChange(
|
||||
Platform,
|
||||
StorageMixinProto,
|
||||
PlaformNameMixin,
|
||||
UserCustomFilterMixin,
|
||||
abstract=True
|
||||
):
|
||||
"Watch a status, and fire a post when status changes"
|
||||
|
||||
@abstractmethod
|
||||
async def get_status(self, target: Target) -> Any:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def compare_status(self, target: Target, old_status, new_status) -> Optional[RawPost]:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
async def parse(self, raw_post: RawPost) -> Post:
|
||||
...
|
||||
|
||||
async def fetch_new_post(self, target: Target, users: list[UserSubInfo]) -> list[tuple[User, list[Post]]]:
|
||||
try:
|
||||
new_status = await self.get_status(target)
|
||||
res = []
|
||||
if old_status := self.get_stored_data(target):
|
||||
diff = self.compare_status(target, old_status, new_status)
|
||||
if diff:
|
||||
res = await self.dispatch_user_post(target, [diff], users)
|
||||
self.set_stored_data(target, new_status)
|
||||
return res
|
||||
except httpx.RequestError as err:
|
||||
logger.warning("network connection error: {}, url: {}".format(type(err), err.request.url))
|
||||
return []
|
||||
|
63
tests/platforms/monster-siren_list_0.json
Normal file
63
tests/platforms/monster-siren_list_0.json
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"list": [
|
||||
{
|
||||
"cid": "114091",
|
||||
"title": "#AUS小屋",
|
||||
"cate": 8,
|
||||
"date": "2021-06-23"
|
||||
},
|
||||
{
|
||||
"cid": "027726",
|
||||
"title": "「音律联觉原声EP」正式上架",
|
||||
"cate": 1,
|
||||
"date": "2021-06-12"
|
||||
},
|
||||
{
|
||||
"cid": "750459",
|
||||
"title": "「ManiFesto:」MV正式公开",
|
||||
"cate": 1,
|
||||
"date": "2021-06-08"
|
||||
},
|
||||
{
|
||||
"cid": "241304",
|
||||
"title": "「Real Me」正式上架",
|
||||
"cate": 1,
|
||||
"date": "2021-06-01"
|
||||
},
|
||||
{
|
||||
"cid": "578835",
|
||||
"title": "#D.D.D.PHOTO",
|
||||
"cate": 8,
|
||||
"date": "2021-05-24"
|
||||
},
|
||||
{
|
||||
"cid": "489188",
|
||||
"title": "#AUS小屋",
|
||||
"cate": 8,
|
||||
"date": "2021-05-19"
|
||||
},
|
||||
{
|
||||
"cid": "992677",
|
||||
"title": "「Immutable」正式上架",
|
||||
"cate": 1,
|
||||
"date": "2021-05-02"
|
||||
},
|
||||
{
|
||||
"cid": "605962",
|
||||
"title": "「Voices」正式上架",
|
||||
"cate": 1,
|
||||
"date": "2021-05-01"
|
||||
},
|
||||
{
|
||||
"cid": "336213",
|
||||
"title": "#D.D.D.PHOTO",
|
||||
"cate": 8,
|
||||
"date": "2021-04-28"
|
||||
}
|
||||
],
|
||||
"end": false
|
||||
}
|
||||
}
|
1
tests/platforms/monster-siren_list_1.json
Normal file
1
tests/platforms/monster-siren_list_1.json
Normal file
@ -0,0 +1 @@
|
||||
{"code":0,"msg":"","data":{"list":[{"cid":"241303","title":"#D.D.D.PHOTO","cate":8,"date":"2021-06-29"},{"cid":"114091","title":"#AUS小屋","cate":8,"date":"2021-06-23"},{"cid":"027726","title":"「音律联觉原声EP」正式上架","cate":1,"date":"2021-06-12"},{"cid":"750459","title":"「ManiFesto:」MV正式公开","cate":1,"date":"2021-06-08"},{"cid":"241304","title":"「Real Me」正式上架","cate":1,"date":"2021-06-01"},{"cid":"578835","title":"#D.D.D.PHOTO","cate":8,"date":"2021-05-24"},{"cid":"489188","title":"#AUS小屋","cate":8,"date":"2021-05-19"},{"cid":"992677","title":"「Immutable」正式上架","cate":1,"date":"2021-05-02"},{"cid":"605962","title":"「Voices」正式上架","cate":1,"date":"2021-05-01"},{"cid":"336213","title":"#D.D.D.PHOTO","cate":8,"date":"2021-04-28"}],"end":false}}
|
44
tests/platforms/test_monster-siren.py
Normal file
44
tests/platforms/test_monster-siren.py
Normal file
@ -0,0 +1,44 @@
|
||||
import pytest
|
||||
import typing
|
||||
import respx
|
||||
from httpx import Response
|
||||
import feedparser
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
import sys
|
||||
sys.path.append('./src/plugins')
|
||||
import nonebot_hk_reporter
|
||||
|
||||
from .utils import get_json, get_file
|
||||
|
||||
@pytest.fixture
|
||||
def monster_siren(plugin_module: 'nonebot_hk_reporter'):
|
||||
return plugin_module.platform.platform_manager['monster-siren']
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def monster_siren_list_0():
|
||||
return get_json('monster-siren_list_0.json')
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def monster_siren_list_1():
|
||||
return get_json('monster-siren_list_1.json')
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_fetch_new(monster_siren, dummy_user_subinfo, monster_siren_list_0, monster_siren_list_1):
|
||||
ak_list_router = respx.get("https://monster-siren.hypergryph.com/api/news")
|
||||
ak_list_router.mock(return_value=Response(200, json=monster_siren_list_0))
|
||||
target = ''
|
||||
res = await monster_siren.fetch_new_post(target, [dummy_user_subinfo])
|
||||
assert(ak_list_router.called)
|
||||
assert(len(res) == 0)
|
||||
mock_data = monster_siren_list_1
|
||||
ak_list_router.mock(return_value=Response(200, json=mock_data))
|
||||
res3 = await monster_siren.fetch_new_post(target, [dummy_user_subinfo])
|
||||
assert(len(res3[0][1]) == 1)
|
||||
post = res3[0][1][0]
|
||||
assert(post.target_type == 'monster-siren')
|
||||
assert(post.text == '#D.D.D.PHOTO')
|
||||
assert(post.url == 'https://monster-siren.hypergryph.com/info/241303')
|
||||
assert(post.target_name == '塞壬唱片新闻')
|
||||
assert(len(post.pics) == 0)
|
@ -1,6 +1,6 @@
|
||||
import sys
|
||||
import typing
|
||||
from typing import Any
|
||||
from typing import Any, Optional
|
||||
|
||||
import pytest
|
||||
|
||||
@ -172,6 +172,52 @@ def mock_platform_no_target(plugin_module: 'nonebot_hk_reporter'):
|
||||
|
||||
return MockPlatform()
|
||||
|
||||
@pytest.fixture
|
||||
def mock_status_change(plugin_module: 'nonebot_hk_reporter'):
|
||||
class MockPlatform(plugin_module.platform.platform.StatusChange,
|
||||
plugin_module.platform.platform.NoTargetMixin):
|
||||
|
||||
platform_name = 'mock_platform'
|
||||
name = 'Mock Platform'
|
||||
enabled = True
|
||||
is_common = True
|
||||
enable_tag = False
|
||||
schedule_type = 'interval'
|
||||
schedule_kw = {'seconds': 10}
|
||||
categories = {
|
||||
1: '转发',
|
||||
2: '视频',
|
||||
}
|
||||
def __init__(self):
|
||||
self.sub_index = 0
|
||||
super().__init__()
|
||||
|
||||
async def get_status(self, _: 'Target'):
|
||||
if self.sub_index == 0:
|
||||
self.sub_index += 1
|
||||
return {'s': False}
|
||||
elif self.sub_index == 1:
|
||||
self.sub_index += 1
|
||||
return {'s': True}
|
||||
else:
|
||||
return {'s': False}
|
||||
|
||||
def compare_status(self, target, old_status, new_status) -> Optional['RawPost']:
|
||||
if old_status['s'] == False and new_status['s'] == True:
|
||||
return {'text': 'on', 'cat': 1}
|
||||
elif old_status['s'] == True and new_status['s'] == False:
|
||||
return {'text': 'off', 'cat': 2}
|
||||
return None
|
||||
|
||||
async def parse(self, raw_post) -> 'Post':
|
||||
return plugin_module.post.Post('mock_status', raw_post['text'], '')
|
||||
|
||||
def get_category(self, raw_post):
|
||||
return raw_post['cat']
|
||||
|
||||
return MockPlatform()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_new_message_target_without_cats_tags(mock_platform_without_cats_tags, user_info_factory):
|
||||
res1 = await mock_platform_without_cats_tags.fetch_new_post('dummy', [user_info_factory(lambda _: [1,2], lambda _: [])])
|
||||
@ -230,3 +276,25 @@ async def test_new_message_no_target(mock_platform_no_target, user_info_factory)
|
||||
assert('p2' in id_set_1 and 'p3' in id_set_1)
|
||||
assert('p2' in id_set_2)
|
||||
assert('p2' in id_set_3)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_status_change(mock_status_change, user_info_factory):
|
||||
res1 = await mock_status_change.fetch_new_post('dummy', [user_info_factory(lambda _: [1,2], lambda _: [])])
|
||||
assert(len(res1) == 0)
|
||||
res2 = await mock_status_change.fetch_new_post('dummy', [
|
||||
user_info_factory(lambda _: [1,2], lambda _:[])
|
||||
])
|
||||
assert(len(res2) == 1)
|
||||
posts = res2[0][1]
|
||||
assert(len(posts) == 1)
|
||||
assert(posts[0].text == 'on')
|
||||
res3 = await mock_status_change.fetch_new_post('dummy', [
|
||||
user_info_factory(lambda _: [1,2], lambda _: []),
|
||||
user_info_factory(lambda _: [1], lambda _: []),
|
||||
])
|
||||
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.fetch_new_post('dummy', [user_info_factory(lambda _: [1,2], lambda _: [])])
|
||||
assert(len(res4) == 0)
|
||||
|
Loading…
x
Reference in New Issue
Block a user