mirror of
				https://github.com/suyiiyii/nonebot-bison.git
				synced 2025-11-04 05:24:53 +08:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			7d65873d06
			...
			b7ba0ac661
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b7ba0ac661 | |||
| 
						 | 
					a83cf3ca79 | ||
| 
						 | 
					2137cb17eb | ||
| e496bf82e6 | |||
| 
						 | 
					23d945f8c7 | ||
| 
						 | 
					74a3a0fab0 | 
@ -7,7 +7,7 @@ ci:
 | 
			
		||||
  autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
 | 
			
		||||
repos:
 | 
			
		||||
  - repo: https://github.com/astral-sh/ruff-pre-commit
 | 
			
		||||
    rev: v0.7.2
 | 
			
		||||
    rev: v0.8.1
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: ruff
 | 
			
		||||
        args: [--fix, --exit-non-zero-on-fix]
 | 
			
		||||
@ -34,7 +34,7 @@ repos:
 | 
			
		||||
        stages: [pre-commit]
 | 
			
		||||
 | 
			
		||||
  - repo: https://github.com/pre-commit/mirrors-eslint
 | 
			
		||||
    rev: v9.14.0
 | 
			
		||||
    rev: v9.16.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: eslint
 | 
			
		||||
        additional_dependencies:
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,8 @@
 | 
			
		||||
 | 
			
		||||
### Bug 修复
 | 
			
		||||
 | 
			
		||||
- :bug: 修复 cookie 模块 type hint [@suyiiyii](https://github.com/suyiiyii) ([#658](https://github.com/MountainDash/nonebot-bison/pull/658))
 | 
			
		||||
- :bug: B站转发动态补充 DeletedItem 类型解析 [@AzideCupric](https://github.com/AzideCupric) ([#659](https://github.com/MountainDash/nonebot-bison/pull/659))
 | 
			
		||||
- :bug: 小刻食堂cdn使用https [@phidiaLam](https://github.com/phidiaLam) ([#650](https://github.com/MountainDash/nonebot-bison/pull/650))
 | 
			
		||||
 | 
			
		||||
## v0.9.5
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ from nonebot.adapters.onebot.v11.event import PrivateMessageEvent
 | 
			
		||||
from .api import router as api_router
 | 
			
		||||
from ..plugin_config import plugin_config
 | 
			
		||||
from .token_manager import token_manager as tm
 | 
			
		||||
from .api import metrics_router as metrics_router
 | 
			
		||||
from ..metrics import metrics_router as metrics_router
 | 
			
		||||
 | 
			
		||||
if TYPE_CHECKING:
 | 
			
		||||
    from nonebot.drivers.fastapi import Driver
 | 
			
		||||
 | 
			
		||||
@ -3,13 +3,11 @@ from typing import cast
 | 
			
		||||
import nonebot
 | 
			
		||||
from fastapi import status
 | 
			
		||||
from fastapi.routing import APIRouter
 | 
			
		||||
from starlette.responses import Response
 | 
			
		||||
from fastapi.param_functions import Depends
 | 
			
		||||
from fastapi.exceptions import HTTPException
 | 
			
		||||
from nonebot_plugin_saa import TargetQQGroup
 | 
			
		||||
from nonebot_plugin_saa.auto_select_bot import get_bot
 | 
			
		||||
from fastapi.security.oauth2 import OAuth2PasswordBearer
 | 
			
		||||
from prometheus_client import CONTENT_TYPE_LATEST, generate_latest
 | 
			
		||||
 | 
			
		||||
from ..types import WeightConfig
 | 
			
		||||
from ..apis import check_sub_target
 | 
			
		||||
@ -24,6 +22,7 @@ from ..utils.site import CookieClientManager, site_manager, is_cookie_client_man
 | 
			
		||||
from ..config import NoSuchUserException, NoSuchTargetException, NoSuchSubscribeException, config
 | 
			
		||||
from .types import (
 | 
			
		||||
    Cookie,
 | 
			
		||||
    Target,
 | 
			
		||||
    TokenResp,
 | 
			
		||||
    GlobalConf,
 | 
			
		||||
    SiteConfig,
 | 
			
		||||
@ -213,7 +212,7 @@ async def update_weigth_config(platformName: str, target: str, weight_config: We
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@router.get("/cookie", dependencies=[Depends(check_is_superuser)])
 | 
			
		||||
async def get_cookie(site_name: str = None, target: str = None) -> list[Cookie]:
 | 
			
		||||
async def get_cookie(site_name: str | None = None, target: str | None = None) -> list[Cookie]:
 | 
			
		||||
    cookies_in_db = await config.get_cookie(site_name, is_anonymous=False)
 | 
			
		||||
    return [
 | 
			
		||||
        Cookie(
 | 
			
		||||
@ -252,7 +251,12 @@ async def get_cookie_target(
 | 
			
		||||
    cookie_targets = await config.get_cookie_target()
 | 
			
		||||
    # TODO: filter in SQL
 | 
			
		||||
    return [
 | 
			
		||||
        x
 | 
			
		||||
        CookieTarget(
 | 
			
		||||
            target=Target(
 | 
			
		||||
                platform_name=x.target.platform_name, target_name=x.target.target_name, target=x.target.target
 | 
			
		||||
            ),
 | 
			
		||||
            cookie_id=x.cookie.id,
 | 
			
		||||
        )
 | 
			
		||||
        for x in cookie_targets
 | 
			
		||||
        if (site_name is None or x.cookie.site_name == site_name)
 | 
			
		||||
        and (target is None or x.target.target == target)
 | 
			
		||||
@ -261,13 +265,13 @@ async def get_cookie_target(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@router.post("/cookie_target", dependencies=[Depends(check_is_superuser)])
 | 
			
		||||
async def add_cookie_target(platform_name: str, target: str, cookie_id: int) -> StatusResp:
 | 
			
		||||
async def add_cookie_target(platform_name: str, target: T_Target, cookie_id: int) -> StatusResp:
 | 
			
		||||
    await config.add_cookie_target(target, platform_name, cookie_id)
 | 
			
		||||
    return StatusResp(ok=True, msg="")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@router.delete("/cookie_target", dependencies=[Depends(check_is_superuser)])
 | 
			
		||||
async def del_cookie_target(platform_name: str, target: str, cookie_id: int) -> StatusResp:
 | 
			
		||||
async def del_cookie_target(platform_name: str, target: T_Target, cookie_id: int) -> StatusResp:
 | 
			
		||||
    await config.delete_cookie_target(target, platform_name, cookie_id)
 | 
			
		||||
    return StatusResp(ok=True, msg="")
 | 
			
		||||
 | 
			
		||||
@ -279,11 +283,3 @@ async def get_cookie_valid(site_name: str, content: str) -> StatusResp:
 | 
			
		||||
        return StatusResp(ok=True, msg="")
 | 
			
		||||
    else:
 | 
			
		||||
        return StatusResp(ok=False, msg="")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
metrics_router = APIRouter(prefix="/api/metrics", tags=["metrics"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@metrics_router.get("")
 | 
			
		||||
async def metrics():
 | 
			
		||||
    return Response(media_type=CONTENT_TYPE_LATEST, content=generate_latest())
 | 
			
		||||
 | 
			
		||||
@ -288,6 +288,8 @@ class DBConfig:
 | 
			
		||||
    async def get_cookie_by_id(self, cookie_id: int) -> Cookie:
 | 
			
		||||
        async with create_session() as sess:
 | 
			
		||||
            cookie = await sess.scalar(select(Cookie).where(Cookie.id == cookie_id))
 | 
			
		||||
            if not cookie:
 | 
			
		||||
                raise NoSuchTargetException(f"cookie {cookie_id} not found")
 | 
			
		||||
            return cookie
 | 
			
		||||
 | 
			
		||||
    async def add_cookie(self, cookie: Cookie) -> int:
 | 
			
		||||
@ -317,6 +319,8 @@ class DBConfig:
 | 
			
		||||
                .outerjoin(CookieTarget)
 | 
			
		||||
                .options(selectinload(Cookie.targets))
 | 
			
		||||
            )
 | 
			
		||||
            if not cookie:
 | 
			
		||||
                raise NoSuchTargetException(f"cookie {cookie_id} not found")
 | 
			
		||||
            if len(cookie.targets) > 0:
 | 
			
		||||
                raise Exception(f"cookie {cookie.id} in use")
 | 
			
		||||
            await sess.execute(delete(Cookie).where(Cookie.id == cookie_id))
 | 
			
		||||
 | 
			
		||||
@ -8,8 +8,11 @@ from pydantic import BaseModel
 | 
			
		||||
from nonebot_plugin_saa.registries import AllSupportedPlatformTarget
 | 
			
		||||
from nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
 | 
			
		||||
 | 
			
		||||
from nonebot_bison.types import Tag
 | 
			
		||||
from nonebot_bison.types import Category
 | 
			
		||||
from nonebot_bison.types import Target as T_Target
 | 
			
		||||
 | 
			
		||||
from ..utils import NBESFParseErr
 | 
			
		||||
from ....types import Tag, Category
 | 
			
		||||
from .base import NBESFBase, SubReceipt
 | 
			
		||||
from ...db_model import Cookie as DBCookie
 | 
			
		||||
from ...db_config import SubscribeDupException, config
 | 
			
		||||
@ -114,7 +117,7 @@ async def magic_cookie_gen(nbesf_data: SubGroup):
 | 
			
		||||
            new_cookie = DBCookie(**model_dump(cookie, exclude={"targets"}))
 | 
			
		||||
            cookie_id = await config.add_cookie(new_cookie)
 | 
			
		||||
            for target in cookie.targets:
 | 
			
		||||
                await config.add_cookie_target(target.target, target.platform_name, cookie_id)
 | 
			
		||||
                await config.add_cookie_target(T_Target(target.target), target.platform_name, cookie_id)
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            logger.error(f"!添加 Cookie 条目 {repr(cookie)} 失败: {repr(e)}")
 | 
			
		||||
        else:
 | 
			
		||||
 | 
			
		||||
@ -65,7 +65,7 @@ async def subscribes_export(selector: Callable[[Select], Select]) -> v3.SubGroup
 | 
			
		||||
        target_payload = type_validate_python(v3.Target, cookie_target.target)
 | 
			
		||||
        cookie_target_dict[cookie_target.cookie].append(target_payload)
 | 
			
		||||
 | 
			
		||||
    def cookie_transform(cookie: Cookie, targets: [Target]) -> v3.Cookie:
 | 
			
		||||
    def cookie_transform(cookie: Cookie, targets: list[v3.Target]) -> v3.Cookie:
 | 
			
		||||
        cookie_dict = row2dict(cookie)
 | 
			
		||||
        cookie_dict["tags"] = cookie.tags
 | 
			
		||||
        cookie_dict["targets"] = targets
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,6 @@
 | 
			
		||||
from ..db_model import Model
 | 
			
		||||
from typing import Any
 | 
			
		||||
 | 
			
		||||
from sqlalchemy.orm import DeclarativeBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NBESFVerMatchErr(Exception): ...
 | 
			
		||||
@ -7,7 +9,7 @@ class NBESFVerMatchErr(Exception): ...
 | 
			
		||||
class NBESFParseErr(Exception): ...
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def row2dict(row: Model) -> dict:
 | 
			
		||||
def row2dict(row: DeclarativeBase) -> dict[str, Any]:
 | 
			
		||||
    d = {}
 | 
			
		||||
    for column in row.__table__.columns:
 | 
			
		||||
        d[column.name] = str(getattr(row, column.name))
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,6 @@
 | 
			
		||||
from prometheus_client import Counter
 | 
			
		||||
from fastapi import APIRouter
 | 
			
		||||
from starlette.responses import Response
 | 
			
		||||
from prometheus_client import CONTENT_TYPE_LATEST, Counter, generate_latest
 | 
			
		||||
 | 
			
		||||
# Request counter
 | 
			
		||||
request_counter = Counter("bison_request_counter", "The number of requests")
 | 
			
		||||
@ -7,3 +9,9 @@ success_counter = Counter("bison_success_counter", "The number of successful req
 | 
			
		||||
 | 
			
		||||
# Sent counter
 | 
			
		||||
sent_counter = Counter("bison_sent_counter", "The number of sent messages")
 | 
			
		||||
metrics_router = APIRouter(prefix="/api/metrics", tags=["metrics"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@metrics_router.get("")
 | 
			
		||||
async def metrics():
 | 
			
		||||
    return Response(media_type=CONTENT_TYPE_LATEST, content=generate_latest())
 | 
			
		||||
 | 
			
		||||
@ -131,7 +131,7 @@ class PostAPI(APIBase):
 | 
			
		||||
        basic: "PostAPI.Basic"
 | 
			
		||||
        id_str: str
 | 
			
		||||
        modules: "PostAPI.Modules"
 | 
			
		||||
        orig: "PostAPI.Item | None" = None
 | 
			
		||||
        orig: "PostAPI.Item | PostAPI.DeletedItem | None" = None
 | 
			
		||||
        topic: "PostAPI.Topic | None" = None
 | 
			
		||||
        type: DynamicType
 | 
			
		||||
 | 
			
		||||
@ -141,6 +141,14 @@ class PostAPI(APIBase):
 | 
			
		||||
        modules: "PostAPI.Modules"
 | 
			
		||||
        type: Literal["DYNAMIC_TYPE_NONE"]
 | 
			
		||||
 | 
			
		||||
        def to_item(self) -> "PostAPI.Item":
 | 
			
		||||
            return PostAPI.Item(
 | 
			
		||||
                basic=self.basic,
 | 
			
		||||
                id_str="",
 | 
			
		||||
                modules=self.modules,
 | 
			
		||||
                type=self.type,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    class Data(Base):
 | 
			
		||||
        items: "list[PostAPI.Item | PostAPI.DeletedItem] | None" = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@ from .models import (
 | 
			
		||||
    DynamicType,
 | 
			
		||||
    ArticleMajor,
 | 
			
		||||
    CoursesMajor,
 | 
			
		||||
    DeletedMajor,
 | 
			
		||||
    UnknownMajor,
 | 
			
		||||
    LiveRecommendMajor,
 | 
			
		||||
)
 | 
			
		||||
@ -243,6 +244,13 @@ class Bilibili(NewMessage):
 | 
			
		||||
                    pics=[courses.cover],
 | 
			
		||||
                    url=URL(courses.jump_url).with_scheme("https").human_repr(),
 | 
			
		||||
                )
 | 
			
		||||
            case DeletedMajor(none=none):
 | 
			
		||||
                return _ParsedMojarPost(
 | 
			
		||||
                    title="",
 | 
			
		||||
                    content=none.tips,
 | 
			
		||||
                    pics=[],
 | 
			
		||||
                    url=None,
 | 
			
		||||
                )
 | 
			
		||||
            case UnknownMajor(type=unknown_type):
 | 
			
		||||
                raise CategoryNotSupport(unknown_type)
 | 
			
		||||
            case None:  # 没有major的情况
 | 
			
		||||
@ -259,10 +267,13 @@ class Bilibili(NewMessage):
 | 
			
		||||
        parsed_raw_post = self.pre_parse_by_mojar(raw_post)
 | 
			
		||||
        parsed_raw_repost = None
 | 
			
		||||
        if self._do_get_category(raw_post.type) == Category(5):
 | 
			
		||||
            if raw_post.orig:
 | 
			
		||||
                parsed_raw_repost = self.pre_parse_by_mojar(raw_post.orig)
 | 
			
		||||
            else:
 | 
			
		||||
                logger.warning(f"转发动态{raw_post.id_str}没有原动态")
 | 
			
		||||
            match raw_post.orig:
 | 
			
		||||
                case PostAPI.Item() as orig:
 | 
			
		||||
                    parsed_raw_repost = self.pre_parse_by_mojar(orig)
 | 
			
		||||
                case PostAPI.DeletedItem() as orig:
 | 
			
		||||
                    parsed_raw_repost = self.pre_parse_by_mojar(orig.to_item())
 | 
			
		||||
                case None:
 | 
			
		||||
                    logger.warning(f"转发动态{raw_post.id_str}没有原动态")
 | 
			
		||||
 | 
			
		||||
        post = Post(
 | 
			
		||||
            self,
 | 
			
		||||
@ -275,8 +286,14 @@ class Bilibili(NewMessage):
 | 
			
		||||
            nickname=raw_post.modules.module_author.name,
 | 
			
		||||
        )
 | 
			
		||||
        if parsed_raw_repost:
 | 
			
		||||
            orig = raw_post.orig
 | 
			
		||||
            assert orig
 | 
			
		||||
            match raw_post.orig:
 | 
			
		||||
                case PostAPI.Item() as orig:
 | 
			
		||||
                    orig = orig
 | 
			
		||||
                case PostAPI.DeletedItem() as orig:
 | 
			
		||||
                    orig = orig.to_item()
 | 
			
		||||
                case None:
 | 
			
		||||
                    raise ValueError("转发动态没有原动态")
 | 
			
		||||
 | 
			
		||||
            post.repost = Post(
 | 
			
		||||
                self,
 | 
			
		||||
                content=decode_unicode_escapes(parsed_raw_repost.content),
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import reprlib
 | 
			
		||||
from io import BytesIO
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import TYPE_CHECKING
 | 
			
		||||
from collections.abc import Sequence
 | 
			
		||||
from dataclasses import fields, dataclass
 | 
			
		||||
 | 
			
		||||
from nonebot.log import logger
 | 
			
		||||
@ -30,7 +31,7 @@ class Post(AbstractPost, PlainContentSupport):
 | 
			
		||||
    """文本内容"""
 | 
			
		||||
    title: str | None = None
 | 
			
		||||
    """标题"""
 | 
			
		||||
    images: list[str | bytes | Path | BytesIO] | None = None
 | 
			
		||||
    images: Sequence[str | bytes | Path | BytesIO] | None = None
 | 
			
		||||
    """图片列表"""
 | 
			
		||||
    timestamp: float | None = None
 | 
			
		||||
    """发布/获取时间戳, 秒"""
 | 
			
		||||
 | 
			
		||||
@ -83,7 +83,7 @@ async def subs_export(path: Path, format: str):
 | 
			
		||||
    export_file = path / f"bison_subscribes_export_{int(time.time())}.{format}"
 | 
			
		||||
 | 
			
		||||
    logger.info("正在获取订阅信息...")
 | 
			
		||||
    export_data: v2.SubGroup = await subscribes_export(lambda x: x)
 | 
			
		||||
    export_data: v3.SubGroup = await subscribes_export(lambda x: x)
 | 
			
		||||
 | 
			
		||||
    with export_file.open("w", encoding="utf-8") as f:
 | 
			
		||||
        match format:
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,7 @@ def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
 | 
			
		||||
    @add_cookie_target_matcher.got("target_idx", parameterless=[handle_cancel])
 | 
			
		||||
    async def got_target_idx(state: T_State, target_idx: str = ArgPlainText()):
 | 
			
		||||
        try:
 | 
			
		||||
            target_idx = int(target_idx)
 | 
			
		||||
            state["target"] = state["sub_table"][target_idx]
 | 
			
		||||
            state["target"] = state["sub_table"][int(target_idx)]
 | 
			
		||||
            state["site"] = platform_manager[state["target"]["platform_name"]].site
 | 
			
		||||
        except Exception:
 | 
			
		||||
            await add_cookie_target_matcher.reject("序号错误")
 | 
			
		||||
@ -57,8 +56,7 @@ def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
 | 
			
		||||
    @add_cookie_target_matcher.got("cookie_idx", MessageTemplate("{_prompt}"), [handle_cancel])
 | 
			
		||||
    async def got_cookie_idx(state: T_State, cookie_idx: str = ArgPlainText()):
 | 
			
		||||
        try:
 | 
			
		||||
            cookie_idx = int(cookie_idx)
 | 
			
		||||
            state["cookie"] = state["cookies"][cookie_idx - 1]
 | 
			
		||||
            state["cookie"] = state["cookies"][int(cookie_idx) - 1]
 | 
			
		||||
        except Exception:
 | 
			
		||||
            await add_cookie_target_matcher.reject("序号错误")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ from nonebot_plugin_saa import PlatformTarget, extract_target
 | 
			
		||||
 | 
			
		||||
from ..config import config
 | 
			
		||||
from ..types import Category
 | 
			
		||||
from ..types import Target as T_Target
 | 
			
		||||
from ..platform import platform_manager
 | 
			
		||||
from ..plugin_config import plugin_config
 | 
			
		||||
from ..utils.site import is_cookie_client_manager
 | 
			
		||||
@ -88,7 +89,7 @@ async def generate_sub_list_text(
 | 
			
		||||
        sub_list = [
 | 
			
		||||
            sub
 | 
			
		||||
            for sub in sub_list
 | 
			
		||||
            if is_cookie_client_manager(platform_manager.get(sub.target.platform_name).site.client_mgr)
 | 
			
		||||
            if is_cookie_client_manager(platform_manager[sub.target.platform_name].site.client_mgr)
 | 
			
		||||
        ]
 | 
			
		||||
    if not sub_list:
 | 
			
		||||
        await matcher.finish("暂无已订阅账号\n请使用“添加订阅”命令添加订阅")
 | 
			
		||||
@ -109,7 +110,7 @@ async def generate_sub_list_text(
 | 
			
		||||
                    res += " {}".format(", ".join(sub.tags)) + "\n"
 | 
			
		||||
            if is_show_cookie:
 | 
			
		||||
                target_cookies = await config.get_cookie(
 | 
			
		||||
                    target=sub.target.target, site_name=platform.site.name, is_anonymous=False
 | 
			
		||||
                    target=T_Target(sub.target.target), site_name=platform.site.name, is_anonymous=False
 | 
			
		||||
                )
 | 
			
		||||
                if target_cookies:
 | 
			
		||||
                    res += "  关联的 Cookie:\n"
 | 
			
		||||
@ -126,6 +127,5 @@ async def only_allow_private(
 | 
			
		||||
    event: Event,
 | 
			
		||||
    matcher: type[Matcher],
 | 
			
		||||
):
 | 
			
		||||
    # if not issubclass(PrivateMessageEvent, event.__class__):
 | 
			
		||||
    if event.message_type != "private":
 | 
			
		||||
    if not (hasattr(event, "message_type") and getattr(event, "message_type") == "private"):
 | 
			
		||||
        await matcher.finish("请在私聊中使用此命令")
 | 
			
		||||
 | 
			
		||||
@ -123,8 +123,8 @@ class CookieClientManager(ClientManager):
 | 
			
		||||
    async def _choose_cookie(self, target: Target | None) -> Cookie:
 | 
			
		||||
        """选择 cookie 的具体算法"""
 | 
			
		||||
        cookies = await config.get_cookie(self._site_name, target)
 | 
			
		||||
        cookies = (cookie for cookie in cookies if cookie.last_usage + cookie.cd < datetime.now())
 | 
			
		||||
        cookie = min(cookies, key=lambda x: x.last_usage)
 | 
			
		||||
        avaliable_cookies = (cookie for cookie in cookies if cookie.last_usage + cookie.cd < datetime.now())
 | 
			
		||||
        cookie = min(avaliable_cookies, key=lambda x: x.last_usage)
 | 
			
		||||
        return cookie
 | 
			
		||||
 | 
			
		||||
    async def get_client(self, target: Target | None) -> AsyncClient:
 | 
			
		||||
@ -183,8 +183,8 @@ class SiteMeta(type):
 | 
			
		||||
            cls._key = kwargs.get("key")
 | 
			
		||||
        elif not kwargs.get("abstract"):
 | 
			
		||||
            # this is the subclass
 | 
			
		||||
            if hasattr(cls, "name"):
 | 
			
		||||
                site_manager[cls.name] = cls
 | 
			
		||||
            if "name" in namespace:
 | 
			
		||||
                site_manager[namespace["name"]] = cls
 | 
			
		||||
        super().__init__(name, bases, namespace, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										204
									
								
								tests/platforms/static/bilibili-new.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										204
									
								
								tests/platforms/static/bilibili-new.json
									
									
									
									
										vendored
									
									
								
							@ -4053,6 +4053,210 @@
 | 
			
		||||
        },
 | 
			
		||||
        "type": "DYNAMIC_TYPE_DRAW",
 | 
			
		||||
        "visible": true
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "basic": {
 | 
			
		||||
          "comment_id_str": "965806534205374473",
 | 
			
		||||
          "comment_type": 17,
 | 
			
		||||
          "like_icon": {
 | 
			
		||||
            "action_url": "https://i0.hdslb.com/bfs/garb/item/8860c7c01179f9984f88fb61bc55cab9dc1d28cb.bin",
 | 
			
		||||
            "end_url": "",
 | 
			
		||||
            "id": 33772,
 | 
			
		||||
            "start_url": ""
 | 
			
		||||
          },
 | 
			
		||||
          "rid_str": "965806534205374473"
 | 
			
		||||
        },
 | 
			
		||||
        "id_str": "965806534205374473",
 | 
			
		||||
        "modules": {
 | 
			
		||||
          "module_author": {
 | 
			
		||||
            "avatar": {
 | 
			
		||||
              "container_size": {
 | 
			
		||||
                "height": 1.35,
 | 
			
		||||
                "width": 1.35
 | 
			
		||||
              },
 | 
			
		||||
              "fallback_layers": {
 | 
			
		||||
                "is_critical_group": true,
 | 
			
		||||
                "layers": [
 | 
			
		||||
                  {
 | 
			
		||||
                    "general_spec": {
 | 
			
		||||
                      "pos_spec": {
 | 
			
		||||
                        "axis_x": 0.675,
 | 
			
		||||
                        "axis_y": 0.675,
 | 
			
		||||
                        "coordinate_pos": 2
 | 
			
		||||
                      },
 | 
			
		||||
                      "render_spec": {
 | 
			
		||||
                        "opacity": 1
 | 
			
		||||
                      },
 | 
			
		||||
                      "size_spec": {
 | 
			
		||||
                        "height": 1,
 | 
			
		||||
                        "width": 1
 | 
			
		||||
                      }
 | 
			
		||||
                    },
 | 
			
		||||
                    "layer_config": {
 | 
			
		||||
                      "is_critical": true,
 | 
			
		||||
                      "tags": {
 | 
			
		||||
                        "AVATAR_LAYER": {},
 | 
			
		||||
                        "GENERAL_CFG": {
 | 
			
		||||
                          "config_type": 1,
 | 
			
		||||
                          "general_config": {
 | 
			
		||||
                            "web_css_style": {
 | 
			
		||||
                              "borderRadius": "50%"
 | 
			
		||||
                            }
 | 
			
		||||
                          }
 | 
			
		||||
                        }
 | 
			
		||||
                      }
 | 
			
		||||
                    },
 | 
			
		||||
                    "resource": {
 | 
			
		||||
                      "res_image": {
 | 
			
		||||
                        "image_src": {
 | 
			
		||||
                          "placeholder": 6,
 | 
			
		||||
                          "remote": {
 | 
			
		||||
                            "bfs_style": "widget-layer-avatar",
 | 
			
		||||
                            "url": "https://i0.hdslb.com/bfs/face/5d255a47deae6c5214f93cdbbf3b01f23cac4a5e.jpg"
 | 
			
		||||
                          },
 | 
			
		||||
                          "src_type": 1
 | 
			
		||||
                        }
 | 
			
		||||
                      },
 | 
			
		||||
                      "res_type": 3
 | 
			
		||||
                    },
 | 
			
		||||
                    "visible": true
 | 
			
		||||
                  }
 | 
			
		||||
                ]
 | 
			
		||||
              },
 | 
			
		||||
              "mid": "6050499"
 | 
			
		||||
            },
 | 
			
		||||
            "face": "https://i0.hdslb.com/bfs/face/5d255a47deae6c5214f93cdbbf3b01f23cac4a5e.jpg",
 | 
			
		||||
            "face_nft": false,
 | 
			
		||||
            "following": null,
 | 
			
		||||
            "jump_url": "//space.bilibili.com/6050499/dynamic",
 | 
			
		||||
            "label": "",
 | 
			
		||||
            "mid": 6050499,
 | 
			
		||||
            "name": "血毒嘿咻",
 | 
			
		||||
            "official_verify": {
 | 
			
		||||
              "desc": "",
 | 
			
		||||
              "type": -1
 | 
			
		||||
            },
 | 
			
		||||
            "pendant": {
 | 
			
		||||
              "expire": 0,
 | 
			
		||||
              "image": "",
 | 
			
		||||
              "image_enhance": "",
 | 
			
		||||
              "image_enhance_frame": "",
 | 
			
		||||
              "n_pid": 0,
 | 
			
		||||
              "name": "",
 | 
			
		||||
              "pid": 0
 | 
			
		||||
            },
 | 
			
		||||
            "pub_action": "",
 | 
			
		||||
            "pub_location_text": "",
 | 
			
		||||
            "pub_time": "08月15日",
 | 
			
		||||
            "pub_ts": 1723707757,
 | 
			
		||||
            "type": "AUTHOR_TYPE_NORMAL",
 | 
			
		||||
            "vip": {
 | 
			
		||||
              "avatar_subscript": 0,
 | 
			
		||||
              "avatar_subscript_url": "",
 | 
			
		||||
              "due_date": 1648224000000,
 | 
			
		||||
              "label": {
 | 
			
		||||
                "bg_color": "",
 | 
			
		||||
                "bg_style": 0,
 | 
			
		||||
                "border_color": "",
 | 
			
		||||
                "img_label_uri_hans": "",
 | 
			
		||||
                "img_label_uri_hans_static": "https://i0.hdslb.com/bfs/vip/d7b702ef65a976b20ed854cbd04cb9e27341bb79.png",
 | 
			
		||||
                "img_label_uri_hant": "",
 | 
			
		||||
                "img_label_uri_hant_static": "https://i0.hdslb.com/bfs/activity-plat/static/20220614/e369244d0b14644f5e1a06431e22a4d5/KJunwh19T5.png",
 | 
			
		||||
                "label_theme": "",
 | 
			
		||||
                "path": "",
 | 
			
		||||
                "text": "",
 | 
			
		||||
                "text_color": "",
 | 
			
		||||
                "use_img_label": true
 | 
			
		||||
              },
 | 
			
		||||
              "nickname_color": "",
 | 
			
		||||
              "status": 0,
 | 
			
		||||
              "theme_type": 0,
 | 
			
		||||
              "type": 1
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "module_dynamic": {
 | 
			
		||||
            "additional": null,
 | 
			
		||||
            "desc": {
 | 
			
		||||
              "rich_text_nodes": [
 | 
			
		||||
                {
 | 
			
		||||
                  "orig_text": "转发动态",
 | 
			
		||||
                  "text": "转发动态",
 | 
			
		||||
                  "type": "RICH_TEXT_NODE_TYPE_TEXT"
 | 
			
		||||
                }
 | 
			
		||||
              ],
 | 
			
		||||
              "text": "转发动态"
 | 
			
		||||
            },
 | 
			
		||||
            "major": null,
 | 
			
		||||
            "topic": null
 | 
			
		||||
          },
 | 
			
		||||
          "module_more": {
 | 
			
		||||
            "three_point_items": [
 | 
			
		||||
              {
 | 
			
		||||
                "label": "举报",
 | 
			
		||||
                "type": "THREE_POINT_REPORT"
 | 
			
		||||
              }
 | 
			
		||||
            ]
 | 
			
		||||
          },
 | 
			
		||||
          "module_stat": {
 | 
			
		||||
            "comment": {
 | 
			
		||||
              "count": 0,
 | 
			
		||||
              "forbidden": false
 | 
			
		||||
            },
 | 
			
		||||
            "forward": {
 | 
			
		||||
              "count": 0,
 | 
			
		||||
              "forbidden": false
 | 
			
		||||
            },
 | 
			
		||||
            "like": {
 | 
			
		||||
              "count": 0,
 | 
			
		||||
              "forbidden": false,
 | 
			
		||||
              "status": false
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "orig": {
 | 
			
		||||
          "basic": {
 | 
			
		||||
            "comment_id_str": "",
 | 
			
		||||
            "comment_type": 0,
 | 
			
		||||
            "like_icon": {
 | 
			
		||||
              "action_url": "",
 | 
			
		||||
              "end_url": "",
 | 
			
		||||
              "id": 0,
 | 
			
		||||
              "start_url": ""
 | 
			
		||||
            },
 | 
			
		||||
            "rid_str": ""
 | 
			
		||||
          },
 | 
			
		||||
          "id_str": null,
 | 
			
		||||
          "modules": {
 | 
			
		||||
            "module_author": {
 | 
			
		||||
              "face": "",
 | 
			
		||||
              "face_nft": false,
 | 
			
		||||
              "following": false,
 | 
			
		||||
              "jump_url": "",
 | 
			
		||||
              "label": "",
 | 
			
		||||
              "mid": 0,
 | 
			
		||||
              "name": "",
 | 
			
		||||
              "pub_action": "",
 | 
			
		||||
              "pub_time": "",
 | 
			
		||||
              "pub_ts": 0,
 | 
			
		||||
              "type": "AUTHOR_TYPE_NORMAL"
 | 
			
		||||
            },
 | 
			
		||||
            "module_dynamic": {
 | 
			
		||||
              "additional": null,
 | 
			
		||||
              "desc": null,
 | 
			
		||||
              "major": {
 | 
			
		||||
                "none": {
 | 
			
		||||
                  "tips": "源动态已被作者删除"
 | 
			
		||||
                },
 | 
			
		||||
                "type": "MAJOR_TYPE_NONE"
 | 
			
		||||
              },
 | 
			
		||||
              "topic": null
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "type": "DYNAMIC_TYPE_NONE",
 | 
			
		||||
          "visible": true
 | 
			
		||||
        },
 | 
			
		||||
        "type": "DYNAMIC_TYPE_FORWARD",
 | 
			
		||||
        "visible": true
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "offset": "915793667264872453",
 | 
			
		||||
 | 
			
		||||
@ -407,6 +407,18 @@ async def test_dynamic_forward(bilibili: "Bilibili", bing_dy_list: list):
 | 
			
		||||
    assert rp.url == "https://t.bilibili.com/915793667264872453"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def test_dynamic_forword_deleted(bilibili: "Bilibili", bing_dy_list: list):
 | 
			
		||||
    from nonebot_bison.post import Post
 | 
			
		||||
 | 
			
		||||
    post: Post = await bilibili.parse(bing_dy_list[12])
 | 
			
		||||
    assert post.content == "转发动态"
 | 
			
		||||
    assert post.url == "https://t.bilibili.com/965806534205374473"
 | 
			
		||||
    assert (repost := post.repost)
 | 
			
		||||
    assert repost.url is None
 | 
			
		||||
    assert not repost.title
 | 
			
		||||
    assert repost.content == "源动态已被作者删除"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.asyncio
 | 
			
		||||
@respx.mock
 | 
			
		||||
async def test_fetch_new_without_dynamic(bilibili, dummy_user_subinfo, without_dynamic):
 | 
			
		||||
 | 
			
		||||
@ -78,7 +78,7 @@ async def test_subs_export(app: App, tmp_path: Path):
 | 
			
		||||
            cookie_name="test cookie",
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    await config.add_cookie_target("weibo_id", "weibo", cookie_id)
 | 
			
		||||
    await config.add_cookie_target(TTarget("weibo_id"), "weibo", cookie_id)
 | 
			
		||||
 | 
			
		||||
    assert len(await config.list_subs_with_all_info()) == 3
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ async def test_subs_export(app: App, init_scheduler):
 | 
			
		||||
 | 
			
		||||
    await config.add_subscribe(
 | 
			
		||||
        TargetQQGroup(group_id=1232),
 | 
			
		||||
        target=TTarget("weibo_id"),
 | 
			
		||||
        target=TTarget(TTarget("weibo_id")),
 | 
			
		||||
        target_name="weibo_name",
 | 
			
		||||
        platform_name="weibo",
 | 
			
		||||
        cats=[],
 | 
			
		||||
@ -24,7 +24,7 @@ async def test_subs_export(app: App, init_scheduler):
 | 
			
		||||
    )
 | 
			
		||||
    await config.add_subscribe(
 | 
			
		||||
        TargetQQGroup(group_id=2342),
 | 
			
		||||
        target=TTarget("weibo_id"),
 | 
			
		||||
        target=TTarget(TTarget("weibo_id")),
 | 
			
		||||
        target_name="weibo_name",
 | 
			
		||||
        platform_name="weibo",
 | 
			
		||||
        cats=[],
 | 
			
		||||
@ -45,7 +45,7 @@ async def test_subs_export(app: App, init_scheduler):
 | 
			
		||||
            cookie_name="test cookie",
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    await config.add_cookie_target("weibo_id", "weibo", cookie_id)
 | 
			
		||||
    await config.add_cookie_target(TTarget("weibo_id"), "weibo", cookie_id)
 | 
			
		||||
 | 
			
		||||
    data = await config.list_subs_with_all_info()
 | 
			
		||||
    assert len(data) == 3
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user