Merge pull request #101 from AzideCupric/upstream-follow

feat (issue #67 ):添加屏蔽特定tag的功能
This commit is contained in:
felinae98 2022-09-12 13:18:38 +08:00 committed by GitHub
commit 2272b9372f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 2603 additions and 15 deletions

View File

@ -215,3 +215,29 @@ RSS 链接即为 uid
在网易云网页上电台的链接一般为`https://music.163.com/#/djradio?id=793745436``id=`
后面的数字即为 uid
### 平台订阅标签Tag
社交平台中的 Tag 一般指使用井号(`#`)作为前缀,将关键词进行标记,方便用户进行搜索的功能。
例子:`#明日方舟# #每日打卡#weibo、bilibili #baracampTwitter`
在 Bison 中,用户在添加平台账号订阅时(如果该平台提供有 hashtag 功能),
会进行`输入需要订阅/屏蔽tag`的步骤。
如果需要订阅某些 tag需要直接向 bison 发送需要订阅的一系列 tag并使用空格进行分隔。
例:`A1行动预备组 罗德厨房——回甘`
如果需要屏蔽某些 tag需要在需要屏蔽的 tag 前加上前缀`~`,对于复数的 tag使用空格进行分隔。
例:`~123罗德岛 ~莱茵生命漫画`
可以综合运用以上规则进行同时订阅/屏蔽。
例:`A1行动预备组 ~123罗德岛 罗德厨房——回甘 ~莱茵生命漫画`
#### Tag 的推送规则
每当 Bison 抓取到一条推送,推送中的 Tag 会经过一下检查:
- Bison 会对**需屏蔽 Tag**进行最优先检查,只要检测到本次推送中存在**任一**已记录的**需屏蔽 tag**Bison 便会将该推送丢弃。
- 上一个检查通过后Bison 会对**需订阅 tag**进行检查,如果本次推送中存在**任一**已记录的**需订阅 tag**Bison 便会将该推送发送到群中。
- 当已记录的**需订阅 tag**为空时,只要通过了*第一条规则*的检查Bison 就会将该推送发送到群中。
- 当已记录的**需订阅 tag**不为空时,即使通过了*第一条规则*的检查,若本次推送不含**任何**已记录的**需订阅 tag**Bison 也会将该推送丢弃。

View File

@ -138,7 +138,7 @@ def do_add_sub(add_sub: Type[Matcher]):
state["id"] = target
state["name"] = name
except (LookupError):
url = "https://nonebot-bison.vercel.app/usage/#%E6%89%80%E6%94%AF%E6%8C%81%E5%B9%B3%E5%8F%B0%E7%9A%84-uid"
url = "https://nonebot-bison.netlify.app/usage/#%E6%89%80%E6%94%AF%E6%8C%81%E5%B9%B3%E5%8F%B0%E7%9A%84-uid"
title = "Bison所支持的平台UID"
content = "查询相关平台的uid格式或获取方式"
image = "https://s3.bmp.ovh/imgs/2022/03/ab3cc45d83bd3dd3.jpg"
@ -183,13 +183,17 @@ def do_add_sub(add_sub: Type[Matcher]):
if not platform_manager[state["platform"]].enable_tag:
state["tags"] = []
return
state["_prompt"] = '请输入要订阅的tag订阅所有tag输入"全部标签"'
state["_prompt"] = '请输入要订阅/屏蔽的tag(不含#号)\n多个tag请使用空格隔开\n具体规则回复"详情"'
async def parser_tags(event: MessageEvent, state: T_State):
if not isinstance(state["tags"], Message):
return
if str(event.get_message()).strip() == "取消": # 一般不会有叫 取消 的tag吧
await add_sub.finish("已中止订阅")
if str(event.get_message()).strip() == "详情":
await add_sub.reject(
'订阅tag直接输入tag内容\n订阅所有tag输入"全部标签"\n屏蔽tag请在tag名称前添加~号\n详见https://nonebot-bison.netlify.app/usage/#平台订阅标签-tag'
)
if str(event.get_message()).strip() == "全部标签":
state["tags"] = []
else:

View File

@ -111,6 +111,43 @@ class Platform(metaclass=RegistryABCMeta, base=True):
def set_stored_data(self, target: Target, data: Any):
self.store[target] = data
def tag_separator(self, stored_tags: list[Tag]) -> tuple[list[Tag], list[Tag]]:
"""返回分离好的正反tag元组"""
subscribed_tags = []
banned_tags = []
for tag in stored_tags:
if tag.startswith("~"):
banned_tags.append(tag.lstrip("~"))
else:
subscribed_tags.append(tag)
return subscribed_tags, banned_tags
def is_banned_post(
self,
post_tags: Collection[Tag],
subscribed_tags: list[Tag],
banned_tags: list[Tag],
) -> bool:
"""只要存在任意屏蔽tag则返回真此行为优先级最高。
存在任意被订阅tag则返回假此行为优先级次之
若被订阅tag为空则返回假
"""
# 存在任意需要屏蔽的tag则为真
if banned_tags:
for tag in post_tags or []:
if tag in banned_tags:
return True
# 检测屏蔽tag后再检测订阅tag
# 存在任意需要订阅的tag则为假
if subscribed_tags:
ban_it = True
for tag in post_tags or []:
if tag in subscribed_tags:
ban_it = False
return ban_it
else:
return False
async def filter_user_custom(
self, raw_post_list: list[RawPost], cats: list[Category], tags: list[Tag]
) -> list[RawPost]:
@ -121,13 +158,9 @@ class Platform(metaclass=RegistryABCMeta, base=True):
if cats and cat not in cats:
continue
if self.enable_tag and tags:
flag = False
post_tags = self.get_tags(raw_post)
for tag in post_tags or []:
if tag in tags:
flag = True
break
if not flag:
if self.is_banned_post(
self.get_tags(raw_post), *self.tag_separator(tags)
):
continue
res.append(raw_post)
return res

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,246 @@
{
"data": {
"cards": [
{
"desc": {
"type": 2
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": ""
}
]
}
}
},
{
"desc": {
"type": 1
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": ""
}
]
}
}
},
{
"desc": {
"type": 8
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": "明日方舟"
},
{
"topic_name": "风笛"
},
{
"topic_name": "琴柳"
},
{
"topic_name": "风暴瞭望"
},
{
"topic_name": "轮换池"
},
{
"topic_name": "打卡挑战"
}
]
}
}
},
{
"desc": {
"type": 2
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": ""
}
]
}
}
},
{
"desc": {
"type": 1
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": "明日方舟"
},
{
"topic_name": "饼学大厦"
},
{
"topic_name": "可露希尔的秘密档案"
}
]
}
}
},
{
"desc": {
"type": 1
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": "罗德岛相簿"
},
{
"topic_name": "可露希尔的秘密档案"
},
{
"topic_name": "罗德岛闲逛部"
}
]
}
}
},
{
"desc": {
"type": 8
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": "明日方舟"
},
{
"topic_name": "轮换学"
},
{
"topic_name": "常驻标准寻访"
},
{
"topic_name": "轮换池"
},
{
"topic_name": "打卡挑战"
},
{
"topic_name": "舟游"
}
]
}
}
},
{
"desc": {
"type": 2
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": "明日方舟"
},
{
"topic_name": "饼学大厦"
}
]
}
}
},
{
"desc": {
"type": 4
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": ""
}
]
}
}
},
{
"desc": {
"type": 1
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": "明日方舟"
},
{
"topic_name": "饼学大厦"
},
{
"topic_name": "罗德岛相簿"
},
{
"topic_name": "可露希尔的秘密档案"
},
{
"topic_name": "罗德岛闲逛部"
}
]
}
}
},
{
"desc": {
"type": 1
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": "明日方舟"
},
{
"topic_name": "饼学大厦"
}
]
}
}
},
{
"desc": {
"type": 1
},
"display": {
"topic_info": {
"topic_details": [
{
"topic_name": "明日方舟"
},
{
"topic_name": "饼学大厦"
},
{
"topic_name": "罗德岛相簿"
},
{
"topic_name": "可露希尔的秘密档案"
},
{
"topic_name": "罗德岛闲逛部"
}
]
}
}
}
]
}
}

129
tests/platforms/static/tag_cases.json vendored Normal file
View File

@ -0,0 +1,129 @@
[
{
"case": {
"post_tags": [
"111",
"222",
"333",
"444"
],
"subscribed_tags": [
"222"
],
"banned_tags": [
"555"
]
},
"result": false
},
{
"case": {
"post_tags": [
"111",
"222",
"333",
"444"
],
"subscribed_tags": [],
"banned_tags": [
"555"
]
},
"result": false
},
{
"case": {
"post_tags": [
"111",
"222",
"333",
"444"
],
"subscribed_tags": [],
"banned_tags": [
"444"
]
},
"result": true
},
{
"case": {
"post_tags": [
"111",
"222",
"333",
"444"
],
"subscribed_tags": [
"222"
],
"banned_tags": []
},
"result": false
},
{
"case": {
"post_tags": [
"111",
"222",
"333",
"444"
],
"subscribed_tags": [],
"banned_tags": []
},
"result": false
},
{
"case": {
"post_tags": [
"111",
"222",
"333",
"444"
],
"subscribed_tags": ["111","555","666"],
"banned_tags": []
},
"result": false
},
{
"case": {
"post_tags": [
"111",
"222",
"333",
"444"
],
"subscribed_tags": ["111","555"],
"banned_tags": ["333"]
},
"result": true
},
{
"case": {
"post_tags": [
"111",
"222",
"333",
"444"
],
"subscribed_tags": ["111","333"],
"banned_tags": ["111"]
},
"result": true
},
{
"case": {
"post_tags": [
"111",
"222",
"333",
"444"
],
"subscribed_tags": ["222"],
"banned_tags": ["555","333"]
},
"result": true
}
]

View File

@ -69,3 +69,33 @@ async def test_parse_target(bilibili: "Bilibili"):
await bilibili.parse_target(
"https://www.bilibili.com/video/BV1qP4y1g738?spm_id_from=333.999.0.0"
)
@pytest.fixture(scope="module")
def post_list():
return get_json("bilibili_fake_dy_list.json")["data"]["cards"]
# 测试新tag机制的平台推送情况
@pytest.mark.asyncio
async def test_filter_user_custom(bilibili, post_list):
only_banned_tags = ["~可露希尔的秘密档案"]
res0 = await bilibili.filter_user_custom(post_list, [], only_banned_tags)
assert len(res0) == 8
only_subscribed_tags = ["可露希尔的秘密档案"]
res1 = await bilibili.filter_user_custom(post_list, [], only_subscribed_tags)
assert len(res1) == 4
multi_subs_tags_1 = ["可露希尔的秘密档案", "罗德岛相簿"]
res2 = await bilibili.filter_user_custom(post_list, [], multi_subs_tags_1)
assert len(res2) == 4
multi_subs_tags_2 = ["罗德岛相簿", "风暴瞭望"]
res3 = await bilibili.filter_user_custom(post_list, [], multi_subs_tags_2)
assert len(res3) == 4
multi_subs_tags_3 = ["明日方舟", "~饼学大厦"]
res4 = await bilibili.filter_user_custom(post_list, [], multi_subs_tags_3)
assert len(res4) == 2

View File

@ -0,0 +1,32 @@
import pytest
from nonebug.app import App
from .utils import get_json
@pytest.fixture(scope="module")
def test_cases():
return get_json("tag_cases.json")
# 测试正反tag的判断情况
@pytest.mark.asyncio
async def test_filter_user_custom_tag(app: App, test_cases):
from nonebot_bison.platform import platform_manager
bilibili = platform_manager["bilibili"]
for case in test_cases:
res = bilibili.is_banned_post(**case["case"])
assert res == case["result"]
# 测试正反tag的分离情况
@pytest.mark.asyncio
async def test_tag_separator(app: App):
from nonebot_bison.platform import platform_manager
bilibili = platform_manager["bilibili"]
tags = ["~111", "222", "333", "~444", "555"]
res = bilibili.tag_separator(tags)
assert res[0] == ["222", "333", "555"]
assert res[1] == ["111", "444"]

View File

@ -1,3 +1,5 @@
from email import message
import pytest
import respx
from httpx import Response
@ -154,12 +156,20 @@ async def test_add_with_target(app: App):
)
ctx.receive_event(bot, event_5_ok)
ctx.should_call_send(event_5_ok, Message(BotReply.add_reply_on_tags), True)
event_6 = fake_group_message_event(
event_6_more_info = fake_group_message_event(
message=Message("详情"), sender=fake_admin_user
)
ctx.receive_event(bot, event_6_more_info)
ctx.should_call_send(
event_6_more_info, BotReply.add_reply_on_tags_need_more_info, True
)
ctx.should_rejected()
event_6_ok = fake_group_message_event(
message=Message("全部标签"), sender=fake_admin_user
)
ctx.receive_event(bot, event_6)
ctx.receive_event(bot, event_6_ok)
ctx.should_call_send(
event_6, BotReply.add_reply_subscribe_success("明日方舟Arknights"), True
event_6_ok, BotReply.add_reply_subscribe_success("明日方舟Arknights"), True
)
ctx.should_finished()
subs = config.list_subscribe(10000, "group")

View File

@ -99,7 +99,7 @@ class BotReply:
@staticmethod
def add_reply_on_id_input_search():
search_url = "https://nonebot-bison.vercel.app/usage/#%E6%89%80%E6%94%AF%E6%8C%81%E5%B9%B3%E5%8F%B0%E7%9A%84-uid"
search_url = "https://nonebot-bison.netlify.app/usage/#%E6%89%80%E6%94%AF%E6%8C%81%E5%B9%B3%E5%8F%B0%E7%9A%84-uid"
search_title = "Bison所支持的平台UID"
search_content = "查询相关平台的uid格式或获取方式"
search_image = "https://s3.bmp.ovh/imgs/2022/03/ab3cc45d83bd3dd3.jpg"
@ -144,5 +144,6 @@ class BotReply:
add_reply_on_id_input_error = "id输入错误"
add_reply_on_target_parse_input_error = "不能从你的输入中提取出id请检查你输入的内容是否符合预期"
add_reply_on_platform_input_error = "平台输入错误"
add_reply_on_tags = '请输入要订阅的tag订阅所有tag输入"全部标签"'
add_reply_on_tags = '请输入要订阅/屏蔽的tag(不含#号)\n多个tag请使用空格隔开\n具体规则回复"详情"'
add_reply_on_tags_need_more_info = '订阅tag直接输入tag内容\n订阅所有tag输入"全部标签"\n屏蔽tag请在tag名称前添加~号\n详见https://nonebot-bison.netlify.app/usage/#平台订阅标签-tag'
add_reply_abort = "已中止订阅"