🔀 merge main

This commit is contained in:
suyiiyii 2024-09-03 09:48:22 +08:00
commit 012df0593d
14 changed files with 7638 additions and 5550 deletions

View File

@ -4,6 +4,10 @@
### Bug 修复 ### Bug 修复
- 无权限用户尝试添加订阅时返回提示信息 [@suyiiyii](https://github.com/suyiiyii) ([#617](https://github.com/MountainDash/nonebot-bison/pull/617))
- B站请求策略阶段行为优化 [@AzideCupric](https://github.com/AzideCupric) ([#610](https://github.com/MountainDash/nonebot-bison/pull/610))
- Rss 不再删除格式化字符 [@suyiiyii](https://github.com/suyiiyii) ([#615](https://github.com/MountainDash/nonebot-bison/pull/615))
- forbid adding platform that needs browser in no-browser env [@felinae98](https://github.com/felinae98) ([#609](https://github.com/MountainDash/nonebot-bison/pull/609))
- 修正项目的代码警告 [@AzideCupric](https://github.com/AzideCupric) ([#614](https://github.com/MountainDash/nonebot-bison/pull/614)) - 修正项目的代码警告 [@AzideCupric](https://github.com/AzideCupric) ([#614](https://github.com/MountainDash/nonebot-bison/pull/614))
- 修复 anonymous_site() 无法正确工作的问题 [@felinae98](https://github.com/felinae98) ([#606](https://github.com/MountainDash/nonebot-bison/pull/606)) - 修复 anonymous_site() 无法正确工作的问题 [@felinae98](https://github.com/felinae98) ([#606](https://github.com/MountainDash/nonebot-bison/pull/606))

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@ from pkgutil import iter_modules
from collections import defaultdict from collections import defaultdict
from importlib import import_module from importlib import import_module
from ..plugin_config import plugin_config
from .platform import Platform, make_no_target_group from .platform import Platform, make_no_target_group
_package_dir = str(Path(__file__).resolve().parent) _package_dir = str(Path(__file__).resolve().parent)
@ -22,3 +23,15 @@ for name, platform_list in _platform_list.items():
platform_manager[name] = platform_list[0] platform_manager[name] = platform_list[0]
else: else:
platform_manager[name] = make_no_target_group(platform_list) platform_manager[name] = make_no_target_group(platform_list)
def _get_unavailable_platforms() -> dict[str, str]:
res = {}
for name, platform in platform_manager.items():
if platform.site.require_browser and not plugin_config.bison_use_browser:
res[name] = "需要启用 bison_use_browser"
return res
# platform => reason for not available
unavailable_paltforms: dict[str, str] = _get_unavailable_platforms()

View File

@ -236,15 +236,19 @@ def retry_for_352(api_func: Callable[[TBilibili, Target], Awaitable[list[DynRawP
case RetryState.NROMAL | RetryState.REFRESH | RetryState.RAISE: case RetryState.NROMAL | RetryState.REFRESH | RetryState.RAISE:
try: try:
res = await api_func(bls, *args, **kwargs) res = await api_func(bls, *args, **kwargs)
except ApiCode352Error: except ApiCode352Error as e:
logger.error("API 352 错误") logger.warning("本次 Bilibili API 请求返回 352 错误码")
await _retry_fsm.emit(RetryEvent.REQUEST_AND_RAISE) await _retry_fsm.emit(RetryEvent.REQUEST_AND_RAISE)
if _retry_fsm.current_state == RetryState.RAISE:
raise e
return [] return []
else: else:
await _retry_fsm.emit(RetryEvent.REQUEST_AND_SUCCESS) await _retry_fsm.emit(RetryEvent.REQUEST_AND_SUCCESS)
return res return res
case RetryState.BACKOFF: case RetryState.BACKOFF:
logger.warning("回避中,不请求") logger.warning("本次 Bilibili 请求回避中,不请求")
await _retry_fsm.emit(RetryEvent.IN_BACKOFF_TIME) await _retry_fsm.emit(RetryEvent.IN_BACKOFF_TIME)
return [] return []
case _: case _:

View File

@ -68,7 +68,7 @@ class BilibiliClientManager(ClientManager):
class BilibiliSite(Site): class BilibiliSite(Site):
name = "bilibili.com" name = "bilibili.com"
schedule_setting = {"seconds": 50} schedule_setting = {"seconds": 60}
schedule_type = "interval" schedule_type = "interval"
client_mgr = BilibiliClientManager client_mgr = BilibiliClientManager
require_browser = True require_browser = True

View File

@ -9,8 +9,8 @@ from bs4 import BeautifulSoup as bs
from ..post import Post from ..post import Post
from .platform import NewMessage from .platform import NewMessage
from ..types import Target, RawPost from ..types import Target, RawPost
from ..utils import Site, text_similarity
from ..utils.site import create_cookie_client_manager from ..utils.site import create_cookie_client_manager
from ..utils import Site, text_fletten, text_similarity
class RssSite(Site): class RssSite(Site):
@ -34,7 +34,7 @@ class RssPost(Post):
for p in soup.find_all("p"): for p in soup.find_all("p"):
p.insert_after("\n") p.insert_after("\n")
return text_fletten(soup.get_text()) return soup.get_text()
class Rss(NewMessage): class Rss(NewMessage):
@ -84,7 +84,7 @@ class Rss(NewMessage):
async def parse(self, raw_post: RawPost) -> Post: async def parse(self, raw_post: RawPost) -> Post:
title = raw_post.get("title", "") title = raw_post.get("title", "")
soup = bs(raw_post.description, "html.parser") soup = bs(raw_post.description, "html.parser")
desc = soup.text.strip() desc = raw_post.description
title, desc = self._text_process(title, desc) title, desc = self._text_process(title, desc)
pics = [x.attrs["src"] for x in soup("img")] pics = [x.attrs["src"] for x in soup("img")]
if raw_post.get("media_content"): if raw_post.get("media_content"):

View File

@ -128,10 +128,21 @@ async def do_dispatch_command(
asyncio.create_task(new_matcher_ins.run(bot, event, state)) asyncio.create_task(new_matcher_ins.run(bot, event, state))
no_permission_matcher = on_command(
"添加订阅", rule=configurable_to_me, aliases={"删除订阅", "群管理"}, priority=8, block=True
)
@no_permission_matcher.handle()
async def send_no_permission():
await no_permission_matcher.finish("您没有权限进行此操作,请联系 Bot 管理员")
__all__ = [ __all__ = [
"common_platform", "common_platform",
"add_sub_matcher", "add_sub_matcher",
"query_sub_matcher", "query_sub_matcher",
"del_sub_matcher", "del_sub_matcher",
"group_manage_matcher", "group_manage_matcher",
"no_permission_matcher",
] ]

View File

@ -9,9 +9,9 @@ from nonebot_plugin_saa import Text, PlatformTarget, SupportedAdapters
from ..types import Target from ..types import Target
from ..config import config from ..config import config
from ..apis import check_sub_target from ..apis import check_sub_target
from ..platform import Platform, platform_manager
from ..config.db_config import SubscribeDupException from ..config.db_config import SubscribeDupException
from .utils import common_platform, ensure_user_info, gen_handle_cancel from .utils import common_platform, ensure_user_info, gen_handle_cancel
from ..platform import Platform, platform_manager, unavailable_paltforms
def do_add_sub(add_sub: type[Matcher]): def do_add_sub(add_sub: type[Matcher]):
@ -39,6 +39,8 @@ def do_add_sub(add_sub: type[Matcher]):
elif platform == "取消": elif platform == "取消":
await add_sub.finish("已中止订阅") await add_sub.finish("已中止订阅")
elif platform in platform_manager: elif platform in platform_manager:
if platform in unavailable_paltforms:
await add_sub.finish(f"无法订阅 {platform}{unavailable_paltforms[platform]}")
state["platform"] = platform state["platform"] = platform
else: else:
await add_sub.reject("平台输入错误") await add_sub.reject("平台输入错误")

View File

@ -18,6 +18,7 @@ def pytest_configure(config: pytest.Config) -> None:
"superusers": {"10001"}, "superusers": {"10001"},
"command_start": {""}, "command_start": {""},
"log_level": "TRACE", "log_level": "TRACE",
"bison_use_browser": True,
} }
@ -113,3 +114,12 @@ async def use_legacy_config(app: App):
# 清除单例的缓存 # 清除单例的缓存
Singleton._instances.clear() Singleton._instances.clear()
@pytest.fixture
async def _no_browser(app: App, mocker: MockerFixture):
from nonebot_bison.plugin_config import plugin_config
from nonebot_bison.platform import _get_unavailable_platforms
mocker.patch.object(plugin_config, "bison_use_browser", False)
mocker.patch("nonebot_bison.platform.unavailable_paltforms", _get_unavailable_platforms())

View File

@ -183,7 +183,7 @@ async def test_retry_for_352(app: App, mocker: MockerFixture):
fakebili.set_raise352(True) fakebili.set_raise352(True)
for state in test_state_list: for state in test_state_list[:-3]:
logger.info(f"\n\nnow state should be {state}") logger.info(f"\n\nnow state should be {state}")
assert _retry_fsm.current_state == state assert _retry_fsm.current_state == state
@ -194,6 +194,13 @@ async def test_retry_for_352(app: App, mocker: MockerFixture):
if state == RetryState.BACKOFF: if state == RetryState.BACKOFF:
freeze_start += timedelta_length * (_retry_fsm.addon.backoff_count + 1) ** 2 freeze_start += timedelta_length * (_retry_fsm.addon.backoff_count + 1) ** 2
for state in test_state_list[-3:]:
logger.info(f"\n\nnow state should be {state}")
assert _retry_fsm.current_state == state
with pytest.raises(ApiCode352Error):
await fakebili.get_sub_list(Target("t1")) # type: ignore
assert client_mgr.refresh_client_call_count == 4 * 3 + 3 # refresh + raise assert client_mgr.refresh_client_call_count == 4 * 3 + 3 # refresh + raise
assert client_mgr.get_client_call_count == 2 + 4 * 3 + 3 # previous + refresh + raise assert client_mgr.get_client_call_count == 2 + 4 * 3 + 3 # previous + refresh + raise

View File

@ -88,9 +88,21 @@ async def test_fetch_new_1(
assert post1.title is None assert post1.title is None
assert ( assert (
post1.content post1.content
== "【#統合戦略】 引き続き新テーマ「ミヅキと紺碧の樹」の新要素及びシステムの変更点を一部ご紹介します!" == "【#統合戦略】 <br />引き続き新テーマ「ミヅキと紺碧の樹」の新要素及びシステムの変更点を一部ご紹介します! "
" 今回は「灯火」、「ダイス」、「記号認識」、「鍵」についてです。詳細は添付の画像をご確認ください。" "<br /><br />"
"#アークナイツ https://t.co/ARmptV0Zvu" "今回は「灯火」、「ダイス」、「記号認識」、「鍵」についてです。<br />詳細は添付の画像をご確認ください。"
"<br /><br />"
"#アークナイツ https://t.co/ARmptV0Zvu<br />"
'<img src="https://pbs.twimg.com/media/FwZG9YAacAIXDw2?format=jpg&amp;name=orig" />'
)
plain_content = await post1.get_plain_content()
assert (
plain_content == "【#統合戦略】 \n"
"引き続き新テーマ「ミヅキと紺碧の樹」の新要素及びシステムの変更点を一部ご紹介します! \n\n"
"今回は「灯火」、「ダイス」、「記号認識」、「鍵」についてです。\n"
"詳細は添付の画像をご確認ください。\n\n"
"#アークナイツ https://t.co/ARmptV0Zvu\n"
"[图片]"
) )
@ -174,7 +186,9 @@ async def test_fetch_new_4(
assert len(res2[0][1]) == 1 assert len(res2[0][1]) == 1
post1 = res2[0][1][0] post1 = res2[0][1][0]
assert post1.url == "https://wallhaven.cc/w/85rjej" assert post1.url == "https://wallhaven.cc/w/85rjej"
assert post1.content == "85rjej.jpg" assert post1.content == '<img alt="loading" class="lazyload" src="https://th.wallhaven.cc/small/85/85rjej.jpg" />'
plain_content = await post1.get_plain_content()
assert plain_content == "[图片]"
def test_similar_text_process(): def test_similar_text_process():

View File

@ -615,3 +615,48 @@ async def test_add_with_bilibili_bangumi_target_parser(app: App, init_scheduler)
assert sub.tags == [] assert sub.tags == []
assert sub.target.platform_name == "bilibili-bangumi" assert sub.target.platform_name == "bilibili-bangumi"
assert sub.target.target_name == "汉化日记 第三季" assert sub.target.target_name == "汉化日记 第三季"
@pytest.mark.asyncio
async def test_subscribe_platform_requires_browser(app: App, mocker: MockerFixture):
from nonebot.adapters.onebot.v11.event import Sender
from nonebot.adapters.onebot.v11.message import Message
from nonebot_bison.plugin_config import plugin_config
from nonebot_bison.sub_manager import add_sub_matcher, common_platform
from nonebot_bison.platform import platform_manager, unavailable_paltforms
mocker.patch.object(plugin_config, "bison_use_browser", False)
mocker.patch.dict(unavailable_paltforms, {"bilibili": "需要启用 bison_use_browser"})
async with app.test_matcher(add_sub_matcher) as ctx:
bot = ctx.create_bot()
event_1 = fake_group_message_event(
message=Message("添加订阅"),
sender=Sender(card="", nickname="test", role="admin"),
to_me=True,
)
ctx.receive_event(bot, event_1)
ctx.should_pass_rule()
ctx.should_call_send(
event_1,
BotReply.add_reply_on_platform(platform_manager=platform_manager, common_platform=common_platform),
True,
)
event_2 = fake_group_message_event(
message=Message("全部"), sender=Sender(card="", nickname="test", role="admin")
)
ctx.receive_event(bot, event_2)
ctx.should_rejected()
ctx.should_call_send(
event_2,
BotReply.add_reply_on_platform_input_allplatform(platform_manager),
True,
)
event_3 = fake_group_message_event(message=Message("bilibili"), sender=fake_admin_user)
ctx.receive_event(bot, event_3)
ctx.should_call_send(
event_3,
BotReply.add_reply_platform_unavailable("bilibili", "需要启用 bison_use_browser"),
True,
)

View File

@ -0,0 +1,45 @@
import pytest
from nonebug import App
from ..utils import BotReply, fake_admin_user, fake_group_message_event
@pytest.mark.asyncio
async def test_with_permission(app: App):
from nonebot.adapters.onebot.v11.bot import Bot
from nonebot.adapters.onebot.v11.message import Message
from nonebot_bison.platform import platform_manager
from nonebot_bison.sub_manager import add_sub_matcher, common_platform, no_permission_matcher
async with app.test_matcher([add_sub_matcher, no_permission_matcher]) as ctx:
bot = ctx.create_bot(base=Bot)
event = fake_group_message_event(message=Message("添加订阅"), sender=fake_admin_user, to_me=True)
ctx.receive_event(bot, event)
ctx.should_call_send(
event,
BotReply.add_reply_on_platform(platform_manager, common_platform),
True,
)
ctx.should_pass_rule()
ctx.should_pass_permission()
@pytest.mark.asyncio
async def test_without_permission(app: App):
from nonebot.adapters.onebot.v11.bot import Bot
from nonebot.adapters.onebot.v11.message import Message
from nonebot_bison.sub_manager import add_sub_matcher, no_permission_matcher
async with app.test_matcher([add_sub_matcher, no_permission_matcher]) as ctx:
bot = ctx.create_bot(base=Bot)
event = fake_group_message_event(message=Message("添加订阅"), to_me=True)
ctx.receive_event(bot, event)
ctx.should_call_send(
event,
BotReply.no_permission,
True,
)
ctx.should_pass_rule()
ctx.should_pass_permission()

View File

@ -146,6 +146,10 @@ class BotReply:
extra_text = ("1." + target_promot + "\n2.") if target_promot else "" extra_text = ("1." + target_promot + "\n2.") if target_promot else ""
return extra_text + base_text return extra_text + base_text
@staticmethod
def add_reply_platform_unavailable(platform: str, reason: str) -> str:
return f"无法订阅 {platform}{reason}"
add_reply_on_id_input_error = "id输入错误" add_reply_on_id_input_error = "id输入错误"
add_reply_on_target_parse_input_error = "不能从你的输入中提取出id请检查你输入的内容是否符合预期" add_reply_on_target_parse_input_error = "不能从你的输入中提取出id请检查你输入的内容是否符合预期"
add_reply_on_platform_input_error = "平台输入错误" add_reply_on_platform_input_error = "平台输入错误"
@ -154,3 +158,4 @@ class BotReply:
) )
add_reply_on_tags_need_more_info = "订阅标签直接输入标签内容\n屏蔽标签请在标签名称前添加~号\n详见https://nonebot-bison.netlify.app/usage/#%E5%B9%B3%E5%8F%B0%E8%AE%A2%E9%98%85%E6%A0%87%E7%AD%BE-tag" add_reply_on_tags_need_more_info = "订阅标签直接输入标签内容\n屏蔽标签请在标签名称前添加~号\n详见https://nonebot-bison.netlify.app/usage/#%E5%B9%B3%E5%8F%B0%E8%AE%A2%E9%98%85%E6%A0%87%E7%AD%BE-tag"
add_reply_abort = "已中止订阅" add_reply_abort = "已中止订阅"
no_permission = "您没有权限进行此操作,请联系 Bot 管理员"