nonebot-plugin-bililive 2.1.6__tar.gz → 2.1.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/PKG-INFO +3 -2
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/README.md +2 -1
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/config.py +10 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/database/db.py +13 -1
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/pusher/dynamic_pusher.py +1 -3
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/utils/__init__.py +13 -7
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/utils/browser.py +90 -19
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/version.py +1 -1
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/pyproject.toml +1 -1
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/tests/test_maintenance.py +139 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/LICENSE +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/__main__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/bilibili_api.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/cli/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/cli/bot.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/cli/utils.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/database/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/database/models.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/libs/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/libs/dynamic/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/libs/dynamic/card.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/libs/dynamic/desc.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/libs/dynamic/display.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/libs/dynamic/user_profile.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/libs/dynamic/web.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/at/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/at/at_off.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/at/at_on.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/auto_agree.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/auto_delete.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/dynamic/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/dynamic/dynamic_off.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/dynamic/dynamic_on.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/help.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/live/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/live/live_now.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/live/live_off.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/live/live_on.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/permission/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/permission/permission_off.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/permission/permission_on.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/pusher/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/pusher/live_pusher.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/sub/__init__.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/sub/add_sub.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/sub/delete_sub.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/plugins/sub/sub_list.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/utils/captcha_solver.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/utils/fonts_provider.py +0 -0
- {nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/utils/mobile.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nonebot-plugin-bililive
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.7
|
|
4
4
|
Summary: Push bilibili dynamics and live notifications to QQ with NoneBot2.
|
|
5
5
|
Keywords: nonebot,nonebot2,nonebot-plugin,bilibili,onebot,onebot-v11
|
|
6
6
|
Author-Email: Akiyy_Lab <2806578374@qq.com>
|
|
@@ -116,8 +116,9 @@ BiliLive 是一个基于 NoneBot2 的 B 站推送插件,支持将 UP 主的直
|
|
|
116
116
|
| BILILIVE_DYNAMIC_FONT | 否 | Noto Sans CJK SC | 截图字体 |
|
|
117
117
|
| BILILIVE_DYNAMIC_BIG_IMAGE | 否 | false | 是否优先展示大图 |
|
|
118
118
|
| BILILIVE_COMMAND_PREFIX | 否 | 空字符串 | 命令额外前缀 |
|
|
119
|
+
| BILILIVE_CHROMIUM_ENDPOINT | 否 | 无 | 外部 Chromium CDP 地址,如 `http://127.0.0.1:9222` |
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
动态抓取优先使用 `BILILIVE_CHROMIUM_ENDPOINT` 连接的外部 Chromium;未配置时使用 Playwright 持久化浏览器中的 cookies 请求网页动态接口。若某些 UID 持续抓取失败,建议在外部 Chromium 或插件浏览器数据目录中登录常用 B 站账号,以降低风控概率。
|
|
121
122
|
|
|
122
123
|
## 🎉 使用
|
|
123
124
|
|
|
@@ -82,8 +82,9 @@ BiliLive 是一个基于 NoneBot2 的 B 站推送插件,支持将 UP 主的直
|
|
|
82
82
|
| BILILIVE_DYNAMIC_FONT | 否 | Noto Sans CJK SC | 截图字体 |
|
|
83
83
|
| BILILIVE_DYNAMIC_BIG_IMAGE | 否 | false | 是否优先展示大图 |
|
|
84
84
|
| BILILIVE_COMMAND_PREFIX | 否 | 空字符串 | 命令额外前缀 |
|
|
85
|
+
| BILILIVE_CHROMIUM_ENDPOINT | 否 | 无 | 外部 Chromium CDP 地址,如 `http://127.0.0.1:9222` |
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
动态抓取优先使用 `BILILIVE_CHROMIUM_ENDPOINT` 连接的外部 Chromium;未配置时使用 Playwright 持久化浏览器中的 cookies 请求网页动态接口。若某些 UID 持续抓取失败,建议在外部 Chromium 或插件浏览器数据目录中登录常用 B 站账号,以降低风控概率。
|
|
87
88
|
|
|
88
89
|
## 🎉 使用
|
|
89
90
|
|
{nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/config.py
RENAMED
|
@@ -21,6 +21,7 @@ class Config(BaseModel):
|
|
|
21
21
|
bililive_captcha_address: str = "https://captcha-cd.ngworks.cn"
|
|
22
22
|
bililive_captcha_token: str = "bililive"
|
|
23
23
|
bililive_browser_ua: str | None = None
|
|
24
|
+
bililive_chromium_endpoint: str | None = None
|
|
24
25
|
bililive_dynamic_timeout: int = 30
|
|
25
26
|
bililive_dynamic_font_source: str = "system"
|
|
26
27
|
bililive_dynamic_font: str | None = "Noto Sans CJK SC"
|
|
@@ -46,6 +47,7 @@ class Config(BaseModel):
|
|
|
46
47
|
"haruka_captcha_address": "bililive_captcha_address",
|
|
47
48
|
"haruka_captcha_token": "bililive_captcha_token",
|
|
48
49
|
"haruka_browser_ua": "bililive_browser_ua",
|
|
50
|
+
"haruka_chromium_endpoint": "bililive_chromium_endpoint",
|
|
49
51
|
"haruka_dynamic_timeout": "bililive_dynamic_timeout",
|
|
50
52
|
"haruka_dynamic_font_source": "bililive_dynamic_font_source",
|
|
51
53
|
"haruka_dynamic_font": "bililive_dynamic_font",
|
|
@@ -72,6 +74,14 @@ class Config(BaseModel):
|
|
|
72
74
|
def dynamic_interval_non_negative(cls, value: int):
|
|
73
75
|
return 0 if value < 1 else value
|
|
74
76
|
|
|
77
|
+
@field_validator("bililive_chromium_endpoint")
|
|
78
|
+
@classmethod
|
|
79
|
+
def chromium_endpoint(cls, value: str | None):
|
|
80
|
+
if value is None:
|
|
81
|
+
return None
|
|
82
|
+
value = value.strip()
|
|
83
|
+
return value or None
|
|
84
|
+
|
|
75
85
|
@field_validator("bililive_screenshot_style")
|
|
76
86
|
@classmethod
|
|
77
87
|
def screenshot_style(cls, value: str):
|
|
@@ -304,7 +304,19 @@ class DB:
|
|
|
304
304
|
@classmethod
|
|
305
305
|
async def set_sub(cls, conf, switch, **kwargs):
|
|
306
306
|
"""开关订阅设置"""
|
|
307
|
-
|
|
307
|
+
sub = await cls.get_sub(**kwargs)
|
|
308
|
+
if not sub:
|
|
309
|
+
return False
|
|
310
|
+
previous = getattr(sub, conf, None)
|
|
311
|
+
updated = await Sub.update(kwargs, **{conf: switch})
|
|
312
|
+
if not updated:
|
|
313
|
+
return False
|
|
314
|
+
if conf == "dynamic":
|
|
315
|
+
if switch and not previous:
|
|
316
|
+
dynamic_offset[int(kwargs["uid"])] = -1
|
|
317
|
+
await cls.save_dynamic_offsets()
|
|
318
|
+
await cls.update_uid_list()
|
|
319
|
+
return True
|
|
308
320
|
|
|
309
321
|
@classmethod
|
|
310
322
|
async def get_version(cls):
|
|
@@ -135,9 +135,7 @@ async def process_dynamic_uid(uid: int):
|
|
|
135
135
|
|
|
136
136
|
dynamic_risk_control_until.pop(uid, None)
|
|
137
137
|
|
|
138
|
-
if not dynamics:
|
|
139
|
-
if uid in offset and offset[uid] == -1: # 不记录会导致第一次发动态不推送
|
|
140
|
-
await db.set_dynamic_offset(uid, 0)
|
|
138
|
+
if not dynamics:
|
|
141
139
|
return
|
|
142
140
|
name = get_dynamic_author_name(dynamics[0], use_web_fallback)
|
|
143
141
|
|
|
@@ -298,14 +298,20 @@ def on_startup():
|
|
|
298
298
|
from .browser import check_playwright_env, install
|
|
299
299
|
|
|
300
300
|
check_proxy()
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
logger.warning(
|
|
306
|
-
"Playwright 运行环境不完整,已跳过启动时强校验;"
|
|
307
|
-
f"涉及截图/浏览器能力时可能不可用。错误:{err}"
|
|
301
|
+
if plugin_config.bililive_chromium_endpoint:
|
|
302
|
+
logger.info(
|
|
303
|
+
"已配置 BILILIVE_CHROMIUM_ENDPOINT,"
|
|
304
|
+
"将优先连接外部 Chromium,跳过内置 Chromium 安装"
|
|
308
305
|
)
|
|
306
|
+
else:
|
|
307
|
+
install()
|
|
308
|
+
try:
|
|
309
|
+
asyncio.get_event_loop().run_until_complete(check_playwright_env())
|
|
310
|
+
except ImportError as err:
|
|
311
|
+
logger.warning(
|
|
312
|
+
"Playwright 运行环境不完整,已跳过启动时强校验;"
|
|
313
|
+
f"涉及截图/浏览器能力时可能不可用。错误:{err}"
|
|
314
|
+
)
|
|
309
315
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
310
316
|
|
|
311
317
|
|
|
@@ -7,7 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
|
|
8
8
|
from nonebot import logger
|
|
9
9
|
from playwright.__main__ import main
|
|
10
|
-
from playwright.async_api import BrowserContext, Page, async_playwright
|
|
10
|
+
from playwright.async_api import BrowserContext, Page, Playwright, async_playwright
|
|
11
11
|
|
|
12
12
|
from ..config import plugin_config
|
|
13
13
|
from ..utils import get_path
|
|
@@ -15,29 +15,81 @@ from .captcha_solver import CaptchaInfer
|
|
|
15
15
|
from .fonts_provider import fill_font
|
|
16
16
|
|
|
17
17
|
_browser: BrowserContext | None = None
|
|
18
|
+
_playwright: Playwright | None = None
|
|
18
19
|
mobile_js = Path(__file__).parent.joinpath("mobile.js")
|
|
19
20
|
WEB_DYNAMIC_URL = "https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space"
|
|
21
|
+
DEFAULT_MOBILE_USER_AGENT = (
|
|
22
|
+
"Mozilla/5.0 (Linux; Android 10; RMX1911) AppleWebKit/537.36 "
|
|
23
|
+
"(KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36"
|
|
24
|
+
)
|
|
25
|
+
DEFAULT_DESKTOP_USER_AGENT = (
|
|
26
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
|
27
|
+
"(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_user_agent() -> str:
|
|
32
|
+
if plugin_config.bililive_browser_ua:
|
|
33
|
+
return plugin_config.bililive_browser_ua
|
|
34
|
+
if plugin_config.bililive_screenshot_style.lower() == "mobile":
|
|
35
|
+
return DEFAULT_MOBILE_USER_AGENT
|
|
36
|
+
return DEFAULT_DESKTOP_USER_AGENT
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_dynamic_api_headers(uid: int) -> dict[str, str]:
|
|
40
|
+
return {
|
|
41
|
+
"accept": "application/json, text/plain, */*",
|
|
42
|
+
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
|
|
43
|
+
"referer": f"https://space.bilibili.com/{uid}/dynamic",
|
|
44
|
+
"origin": "https://space.bilibili.com",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def _ensure_playwright() -> Playwright:
|
|
49
|
+
global _playwright
|
|
50
|
+
if _playwright is None:
|
|
51
|
+
_playwright = await async_playwright().start()
|
|
52
|
+
return _playwright
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def _apply_browser_headers(context: BrowserContext) -> None:
|
|
56
|
+
await context.set_extra_http_headers(
|
|
57
|
+
{
|
|
58
|
+
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
|
59
|
+
}
|
|
60
|
+
)
|
|
20
61
|
|
|
21
62
|
|
|
22
|
-
async def
|
|
23
|
-
logger.info("
|
|
63
|
+
async def init_browser_cdp(endpoint: str) -> BrowserContext:
|
|
64
|
+
logger.info(f"连接外部 Chromium:{endpoint}")
|
|
65
|
+
global _browser
|
|
66
|
+
playwright = await _ensure_playwright()
|
|
67
|
+
browser = await playwright.chromium.connect_over_cdp(endpoint)
|
|
68
|
+
if browser.contexts:
|
|
69
|
+
browser_context = browser.contexts[0]
|
|
70
|
+
else:
|
|
71
|
+
browser_context = await browser.new_context(
|
|
72
|
+
user_agent=get_user_agent(),
|
|
73
|
+
device_scale_factor=2,
|
|
74
|
+
)
|
|
75
|
+
await _apply_browser_headers(browser_context)
|
|
76
|
+
_browser = browser_context
|
|
77
|
+
return _browser
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
async def init_browser_playwright(
|
|
81
|
+
proxy=plugin_config.bililive_proxy, **kwargs
|
|
82
|
+
) -> BrowserContext:
|
|
83
|
+
logger.info("初始化 Playwright 内置浏览器")
|
|
24
84
|
if proxy:
|
|
25
85
|
kwargs["proxy"] = {"server": proxy}
|
|
26
86
|
global _browser
|
|
27
|
-
|
|
87
|
+
playwright = await _ensure_playwright()
|
|
28
88
|
browser_data = Path(get_path("browser"))
|
|
29
89
|
browser_data.mkdir(parents=True, exist_ok=True)
|
|
30
|
-
browser_context = await
|
|
90
|
+
browser_context = await playwright.chromium.launch_persistent_context(
|
|
31
91
|
browser_data,
|
|
32
|
-
user_agent=
|
|
33
|
-
or (
|
|
34
|
-
(
|
|
35
|
-
"Mozilla/5.0 (Linux; Android 10; RMX1911) AppleWebKit/537.36 "
|
|
36
|
-
"(KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36"
|
|
37
|
-
)
|
|
38
|
-
if plugin_config.bililive_screenshot_style.lower() == "mobile"
|
|
39
|
-
else None
|
|
40
|
-
),
|
|
92
|
+
user_agent=get_user_agent(),
|
|
41
93
|
device_scale_factor=2,
|
|
42
94
|
timeout=plugin_config.bililive_dynamic_timeout * 1000,
|
|
43
95
|
**kwargs,
|
|
@@ -53,10 +105,24 @@ async def init_browser(proxy=plugin_config.bililive_proxy, **kwargs) -> BrowserC
|
|
|
53
105
|
}
|
|
54
106
|
]
|
|
55
107
|
)
|
|
108
|
+
await _apply_browser_headers(browser_context)
|
|
56
109
|
_browser = browser_context
|
|
57
110
|
return _browser
|
|
58
111
|
|
|
59
112
|
|
|
113
|
+
async def init_browser(proxy=plugin_config.bililive_proxy, **kwargs) -> BrowserContext:
|
|
114
|
+
endpoint = plugin_config.bililive_chromium_endpoint
|
|
115
|
+
if endpoint:
|
|
116
|
+
try:
|
|
117
|
+
return await init_browser_cdp(endpoint)
|
|
118
|
+
except Exception as err:
|
|
119
|
+
logger.warning(
|
|
120
|
+
f"连接外部 Chromium 失败({endpoint}),"
|
|
121
|
+
f"将回退到 Playwright 内置浏览器:{err}"
|
|
122
|
+
)
|
|
123
|
+
return await init_browser_playwright(proxy=proxy, **kwargs)
|
|
124
|
+
|
|
125
|
+
|
|
60
126
|
async def get_browser() -> BrowserContext:
|
|
61
127
|
global _browser
|
|
62
128
|
if not _browser:
|
|
@@ -76,23 +142,28 @@ async def get_bilibili_cookies() -> dict[str, str]:
|
|
|
76
142
|
async def get_user_dynamics_payload_in_browser(uid: int) -> dict:
|
|
77
143
|
browser = await get_browser()
|
|
78
144
|
page = await browser.new_page()
|
|
145
|
+
api_headers = get_dynamic_api_headers(uid)
|
|
79
146
|
try:
|
|
147
|
+
await page.set_extra_http_headers(
|
|
148
|
+
{
|
|
149
|
+
**api_headers,
|
|
150
|
+
"User-Agent": get_user_agent(),
|
|
151
|
+
}
|
|
152
|
+
)
|
|
80
153
|
await page.goto(
|
|
81
154
|
"https://www.bilibili.com/",
|
|
82
155
|
wait_until="domcontentloaded",
|
|
83
156
|
timeout=plugin_config.bililive_dynamic_timeout * 1000,
|
|
84
157
|
)
|
|
85
158
|
return await page.evaluate(
|
|
86
|
-
"""async ({ url, uid }) => {
|
|
159
|
+
"""async ({ url, uid, headers }) => {
|
|
87
160
|
const response = await fetch(`${url}?host_mid=${uid}`, {
|
|
88
161
|
credentials: 'include',
|
|
89
|
-
headers
|
|
90
|
-
accept: 'application/json, text/plain, */*',
|
|
91
|
-
},
|
|
162
|
+
headers,
|
|
92
163
|
});
|
|
93
164
|
return await response.json();
|
|
94
165
|
}""",
|
|
95
|
-
{"url": WEB_DYNAMIC_URL, "uid": str(uid)},
|
|
166
|
+
{"url": WEB_DYNAMIC_URL, "uid": str(uid), "headers": api_headers},
|
|
96
167
|
)
|
|
97
168
|
finally:
|
|
98
169
|
with contextlib.suppress(Exception):
|
|
@@ -65,10 +65,12 @@ with patch("nonebot.get_driver", return_value=DummyDriver()), patch(
|
|
|
65
65
|
core_version = import_module("nonebot_plugin_bililive.version")
|
|
66
66
|
db_module = import_module("nonebot_plugin_bililive.database.db")
|
|
67
67
|
web_dynamic = import_module("nonebot_plugin_bililive.libs.dynamic.web")
|
|
68
|
+
browser_module = import_module("nonebot_plugin_bililive.utils.browser")
|
|
68
69
|
plugin_entry = import_module("nonebot_plugin_bililive")
|
|
69
70
|
DB = db_module.DB
|
|
70
71
|
models = import_module("nonebot_plugin_bililive.database.models")
|
|
71
72
|
Group = models.Group
|
|
73
|
+
Sub = models.Sub
|
|
72
74
|
get_path = import_module("nonebot_plugin_bililive.utils").get_path
|
|
73
75
|
|
|
74
76
|
|
|
@@ -102,6 +104,16 @@ class ConfigTests(unittest.TestCase):
|
|
|
102
104
|
self.assertEqual(config.bililive_live_interval, 12)
|
|
103
105
|
self.assertEqual(config.bililive_command_prefix, "hb")
|
|
104
106
|
|
|
107
|
+
def test_chromium_endpoint_is_trimmed(self):
|
|
108
|
+
config = Config(bililive_chromium_endpoint=" http://127.0.0.1:9222 ")
|
|
109
|
+
|
|
110
|
+
self.assertEqual(config.bililive_chromium_endpoint, "http://127.0.0.1:9222")
|
|
111
|
+
|
|
112
|
+
def test_legacy_haruka_chromium_endpoint_is_migrated(self):
|
|
113
|
+
config = Config(**{"haruka_chromium_endpoint": "http://127.0.0.1:9333"})
|
|
114
|
+
|
|
115
|
+
self.assertEqual(config.bililive_chromium_endpoint, "http://127.0.0.1:9333")
|
|
116
|
+
|
|
105
117
|
|
|
106
118
|
class PluginEntryTests(unittest.TestCase):
|
|
107
119
|
def test_plugin_entry_exposes_plugin_metadata(self):
|
|
@@ -131,6 +143,67 @@ class PluginEntryTests(unittest.TestCase):
|
|
|
131
143
|
self.assertNotIn('bilireq>=', pyproject)
|
|
132
144
|
|
|
133
145
|
|
|
146
|
+
class BrowserHelperTests(unittest.IsolatedAsyncioTestCase):
|
|
147
|
+
def test_get_dynamic_api_headers_include_space_referer(self):
|
|
148
|
+
headers = browser_module.get_dynamic_api_headers(477332594)
|
|
149
|
+
|
|
150
|
+
self.assertEqual(
|
|
151
|
+
headers["referer"],
|
|
152
|
+
"https://space.bilibili.com/477332594/dynamic",
|
|
153
|
+
)
|
|
154
|
+
self.assertEqual(headers["origin"], "https://space.bilibili.com")
|
|
155
|
+
self.assertIn("application/json", headers["accept"])
|
|
156
|
+
|
|
157
|
+
async def test_init_browser_prefers_external_chromium_when_configured(self):
|
|
158
|
+
cdp_context = object()
|
|
159
|
+
with (
|
|
160
|
+
patch.object(
|
|
161
|
+
browser_module.plugin_config,
|
|
162
|
+
"bililive_chromium_endpoint",
|
|
163
|
+
"http://127.0.0.1:9222",
|
|
164
|
+
),
|
|
165
|
+
patch.object(
|
|
166
|
+
browser_module,
|
|
167
|
+
"init_browser_cdp",
|
|
168
|
+
new=AsyncMock(return_value=cdp_context),
|
|
169
|
+
) as init_browser_cdp,
|
|
170
|
+
patch.object(
|
|
171
|
+
browser_module,
|
|
172
|
+
"init_browser_playwright",
|
|
173
|
+
new=AsyncMock(),
|
|
174
|
+
) as init_browser_playwright,
|
|
175
|
+
):
|
|
176
|
+
context = await browser_module.init_browser()
|
|
177
|
+
|
|
178
|
+
self.assertIs(context, cdp_context)
|
|
179
|
+
init_browser_cdp.assert_awaited_once_with("http://127.0.0.1:9222")
|
|
180
|
+
init_browser_playwright.assert_not_awaited()
|
|
181
|
+
|
|
182
|
+
async def test_init_browser_falls_back_to_playwright_when_cdp_fails(self):
|
|
183
|
+
playwright_context = object()
|
|
184
|
+
with (
|
|
185
|
+
patch.object(
|
|
186
|
+
browser_module.plugin_config,
|
|
187
|
+
"bililive_chromium_endpoint",
|
|
188
|
+
"http://127.0.0.1:9222",
|
|
189
|
+
),
|
|
190
|
+
patch.object(
|
|
191
|
+
browser_module,
|
|
192
|
+
"init_browser_cdp",
|
|
193
|
+
new=AsyncMock(side_effect=RuntimeError("connect failed")),
|
|
194
|
+
),
|
|
195
|
+
patch.object(
|
|
196
|
+
browser_module,
|
|
197
|
+
"init_browser_playwright",
|
|
198
|
+
new=AsyncMock(return_value=playwright_context),
|
|
199
|
+
) as init_browser_playwright,
|
|
200
|
+
):
|
|
201
|
+
context = await browser_module.init_browser()
|
|
202
|
+
|
|
203
|
+
self.assertIs(context, playwright_context)
|
|
204
|
+
init_browser_playwright.assert_awaited_once()
|
|
205
|
+
|
|
206
|
+
|
|
134
207
|
class WebDynamicTests(unittest.TestCase):
|
|
135
208
|
def test_parse_web_dynamic_items_extracts_required_fields(self):
|
|
136
209
|
payload = {
|
|
@@ -268,6 +341,72 @@ class DBPermissionTests(unittest.IsolatedAsyncioTestCase):
|
|
|
268
341
|
self.assertFalse(changed)
|
|
269
342
|
update.assert_not_awaited()
|
|
270
343
|
|
|
344
|
+
async def test_set_sub_enabling_dynamic_resets_offset_and_updates_uid_list(self):
|
|
345
|
+
sub = SimpleNamespace(dynamic=False)
|
|
346
|
+
db_module.dynamic_offset.clear()
|
|
347
|
+
db_module.dynamic_offset[123] = 456
|
|
348
|
+
with (
|
|
349
|
+
patch.object(DB, "get_sub", new=AsyncMock(return_value=sub)),
|
|
350
|
+
patch.object(Sub, "update", new=AsyncMock(return_value=True)),
|
|
351
|
+
patch.object(DB, "update_uid_list", new=AsyncMock()) as update_uid_list,
|
|
352
|
+
patch.object(DB, "save_dynamic_offsets", new=AsyncMock()) as save_offsets,
|
|
353
|
+
):
|
|
354
|
+
updated = await DB.set_sub(
|
|
355
|
+
"dynamic",
|
|
356
|
+
True,
|
|
357
|
+
uid=123,
|
|
358
|
+
type="group",
|
|
359
|
+
type_id=456,
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
self.assertTrue(updated)
|
|
363
|
+
self.assertEqual(db_module.dynamic_offset[123], -1)
|
|
364
|
+
save_offsets.assert_awaited_once()
|
|
365
|
+
update_uid_list.assert_awaited_once()
|
|
366
|
+
|
|
367
|
+
async def test_set_sub_enabling_dynamic_skips_reset_when_already_enabled(self):
|
|
368
|
+
sub = SimpleNamespace(dynamic=True)
|
|
369
|
+
db_module.dynamic_offset.clear()
|
|
370
|
+
db_module.dynamic_offset[123] = 456
|
|
371
|
+
with (
|
|
372
|
+
patch.object(DB, "get_sub", new=AsyncMock(return_value=sub)),
|
|
373
|
+
patch.object(Sub, "update", new=AsyncMock(return_value=True)),
|
|
374
|
+
patch.object(DB, "update_uid_list", new=AsyncMock()) as update_uid_list,
|
|
375
|
+
patch.object(DB, "save_dynamic_offsets", new=AsyncMock()) as save_offsets,
|
|
376
|
+
):
|
|
377
|
+
updated = await DB.set_sub(
|
|
378
|
+
"dynamic",
|
|
379
|
+
True,
|
|
380
|
+
uid=123,
|
|
381
|
+
type="group",
|
|
382
|
+
type_id=456,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
self.assertTrue(updated)
|
|
386
|
+
self.assertEqual(db_module.dynamic_offset[123], 456)
|
|
387
|
+
save_offsets.assert_not_awaited()
|
|
388
|
+
update_uid_list.assert_awaited_once()
|
|
389
|
+
|
|
390
|
+
async def test_set_sub_disabling_dynamic_updates_uid_list(self):
|
|
391
|
+
sub = SimpleNamespace(dynamic=True)
|
|
392
|
+
with (
|
|
393
|
+
patch.object(DB, "get_sub", new=AsyncMock(return_value=sub)),
|
|
394
|
+
patch.object(Sub, "update", new=AsyncMock(return_value=True)),
|
|
395
|
+
patch.object(DB, "update_uid_list", new=AsyncMock()) as update_uid_list,
|
|
396
|
+
patch.object(DB, "save_dynamic_offsets", new=AsyncMock()) as save_offsets,
|
|
397
|
+
):
|
|
398
|
+
updated = await DB.set_sub(
|
|
399
|
+
"dynamic",
|
|
400
|
+
False,
|
|
401
|
+
uid=123,
|
|
402
|
+
type="group",
|
|
403
|
+
type_id=456,
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
self.assertTrue(updated)
|
|
407
|
+
save_offsets.assert_not_awaited()
|
|
408
|
+
update_uid_list.assert_awaited_once()
|
|
409
|
+
|
|
271
410
|
|
|
272
411
|
if __name__ == "__main__":
|
|
273
412
|
unittest.main()
|
|
File without changes
|
{nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/__init__.py
RENAMED
|
File without changes
|
{nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/cli/bot.py
RENAMED
|
File without changes
|
{nonebot_plugin_bililive-2.1.6 → nonebot_plugin_bililive-2.1.7}/nonebot_plugin_bililive/cli/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|