🧑‍💻 使用 Ruff 包办所有格式化和检查 (#663)

This commit is contained in:
Azide 2024-12-17 10:52:21 +08:00 committed by GitHub
parent af1609730c
commit 08ad5c288c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
126 changed files with 875 additions and 933 deletions

View File

@ -19,4 +19,9 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Run Ruff Lint - name: Run Ruff Lint
uses: chartboost/ruff-action@v1 uses: astral-sh/ruff-action@v2
with:
src: >-
nonebot_bison/
extra_plugins/
tests/

View File

@ -7,22 +7,12 @@ ci:
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks" autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
repos: repos:
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.1 rev: v0.8.2
hooks: hooks:
- id: ruff - id: ruff
args: [--fix, --exit-non-zero-on-fix] args: [--fix]
stages: [pre-commit] stages: [pre-commit]
- id: ruff-format
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
stages: [pre-commit]
- repo: https://github.com/psf/black
rev: 24.10.0
hooks:
- id: black
stages: [pre-commit] stages: [pre-commit]
- repo: https://github.com/pre-commit/mirrors-prettier - repo: https://github.com/pre-commit/mirrors-prettier

View File

@ -17,9 +17,6 @@ _✨ 通用订阅推送插件 ✨_
<img src="https://img.shields.io/pypi/v/nonebot-bison?logo=python&logoColor=edb641" alt="pypi"> <img src="https://img.shields.io/pypi/v/nonebot-bison?logo=python&logoColor=edb641" alt="pypi">
</a> </a>
<img src="https://img.shields.io/badge/python-3.10+-blue?logo=python&logoColor=edb641" alt="python"> <img src="https://img.shields.io/badge/python-3.10+-blue?logo=python&logoColor=edb641" alt="python">
<a href="https://github.com/psf/black">
<img src="https://img.shields.io/badge/code%20style-black-000000.svg?logo=python&logoColor=edb641" alt="black">
</a>
<a href="https://github.com/astral-sh/ruff"> <a href="https://github.com/astral-sh/ruff">
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json" alt="ruff"> <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json" alt="ruff">
</a> </a>

View File

@ -1,7 +1,7 @@
from nonebot import on_request from nonebot import on_request
from nonebot.log import logger
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.onebot.v11 import Bot
from nonebot.adapters.onebot.v11.event import GroupRequestEvent, FriendRequestEvent from nonebot.adapters.onebot.v11.event import FriendRequestEvent, GroupRequestEvent
from nonebot.log import logger
friend_req = on_request(priority=5) friend_req = on_request(priority=5)

View File

@ -6,8 +6,8 @@ require("nonebot_plugin_saa")
import nonebot_plugin_saa import nonebot_plugin_saa
from . import admin_page, bootstrap, config, platform, post, scheduler, send, sub_manager, theme, types, utils
from .plugin_config import PlugConfig, plugin_config from .plugin_config import PlugConfig, plugin_config
from . import post, send, theme, types, utils, config, platform, bootstrap, scheduler, admin_page, sub_manager
__help__version__ = "0.8.2" __help__version__ = "0.8.2"
nonebot_plugin_saa.enable_auto_select_bot() nonebot_plugin_saa.enable_auto_select_bot()
@ -36,12 +36,12 @@ __all__ = [
"admin_page", "admin_page",
"bootstrap", "bootstrap",
"config", "config",
"sub_manager", "platform",
"post", "post",
"scheduler", "scheduler",
"send", "send",
"platform", "sub_manager",
"theme",
"types", "types",
"utils", "utils",
"theme",
] ]

View File

@ -2,15 +2,16 @@ import os
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from nonebot.log import logger
from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot import get_driver, on_command from nonebot import get_driver, on_command
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.onebot.v11 import Bot
from nonebot.adapters.onebot.v11.event import PrivateMessageEvent from nonebot.adapters.onebot.v11.event import PrivateMessageEvent
from nonebot.log import logger
from nonebot.rule import to_me
from nonebot.typing import T_State
from nonebot_bison.plugin_config import plugin_config
from .api import router as api_router from .api import router as api_router
from ..plugin_config import plugin_config
from .token_manager import token_manager as tm from .token_manager import token_manager as tm
if TYPE_CHECKING: if TYPE_CHECKING:
@ -21,9 +22,9 @@ STATIC_PATH = (Path(__file__).parent / "dist").resolve()
def init_fastapi(driver: "Driver"): def init_fastapi(driver: "Driver"):
import socketio
from fastapi.applications import FastAPI from fastapi.applications import FastAPI
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
import socketio
sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*") sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
socket_app = socketio.ASGIApp(sio, socketio_path="socket") socket_app = socketio.ASGIApp(sio, socketio_path="socket")

View File

@ -1,38 +1,39 @@
from typing import cast from typing import cast
import nonebot
from fastapi import status from fastapi import status
from fastapi.routing import APIRouter
from fastapi.param_functions import Depends
from fastapi.exceptions import HTTPException from fastapi.exceptions import HTTPException
from fastapi.param_functions import Depends
from fastapi.routing import APIRouter
from fastapi.security.oauth2 import OAuth2PasswordBearer
import nonebot
from nonebot_plugin_saa import TargetQQGroup from nonebot_plugin_saa import TargetQQGroup
from nonebot_plugin_saa.auto_select_bot import get_bot from nonebot_plugin_saa.auto_select_bot import get_bot
from fastapi.security.oauth2 import OAuth2PasswordBearer
from ..types import WeightConfig from nonebot_bison.apis import check_sub_target
from ..apis import check_sub_target from nonebot_bison.config import NoSuchSubscribeException, NoSuchTargetException, NoSuchUserException, config
from nonebot_bison.config.db_config import SubscribeDupException
from nonebot_bison.platform import platform_manager
from nonebot_bison.scheduler import scheduler_dict
from nonebot_bison.types import Target as T_Target
from nonebot_bison.types import WeightConfig
from nonebot_bison.utils.get_bot import get_groups
from nonebot_bison.utils.site import CookieClientManager, is_cookie_client_manager, site_manager
from .jwt import load_jwt, pack_jwt from .jwt import load_jwt, pack_jwt
from ..scheduler import scheduler_dict
from ..types import Target as T_Target
from ..utils.get_bot import get_groups
from ..platform import platform_manager
from .token_manager import token_manager from .token_manager import token_manager
from ..config.db_config import SubscribeDupException
from ..utils.site import CookieClientManager, site_manager, is_cookie_client_manager
from ..config import NoSuchUserException, NoSuchTargetException, NoSuchSubscribeException, config
from .types import ( from .types import (
AddSubscribeReq,
Cookie, Cookie,
Target, CookieTarget,
TokenResp,
GlobalConf, GlobalConf,
PlatformConfig,
SiteConfig, SiteConfig,
StatusResp, StatusResp,
CookieTarget,
SubscribeResp,
PlatformConfig,
AddSubscribeReq,
SubscribeConfig, SubscribeConfig,
SubscribeGroupDetail, SubscribeGroupDetail,
SubscribeResp,
Target,
TokenResp,
) )
router = APIRouter(prefix="/api", tags=["api"]) router = APIRouter(prefix="/api", tags=["api"])

View File

@ -1,6 +1,6 @@
import datetime
import random import random
import string import string
import datetime
import jwt import jwt

View File

@ -1,6 +1,6 @@
from datetime import timedelta
import random import random
import string import string
from datetime import timedelta
from expiringdictx import ExpiringDict from expiringdictx import ExpiringDict

View File

@ -60,8 +60,8 @@ class StatusResp(BaseModel):
msg: str msg: str
from typing import Any
from datetime import datetime from datetime import datetime
from typing import Any
from pydantic import BaseModel from pydantic import BaseModel

View File

@ -1,6 +1,6 @@
from .types import Target
from .scheduler import scheduler_dict
from .platform import platform_manager from .platform import platform_manager
from .scheduler import scheduler_dict
from .types import Target
async def check_sub_target(platform_name: str, target: Target): async def check_sub_target(platform_name: str, target: Target):

View File

@ -1,10 +1,10 @@
from nonebot.log import logger from nonebot.log import logger
from sqlalchemy import text, inspect from nonebot_plugin_datastore.db import get_engine, post_db_init, pre_db_init
from nonebot_plugin_datastore.db import get_engine, pre_db_init, post_db_init from sqlalchemy import inspect, text
from .config.config_legacy import start_up as legacy_db_startup
from .config.db_migration import data_migrate from .config.db_migration import data_migrate
from .scheduler.manager import init_scheduler from .scheduler.manager import init_scheduler
from .config.config_legacy import start_up as legacy_db_startup
@pre_db_init @pre_db_init

View File

@ -1,9 +1,9 @@
from typing import Literal, overload from typing import Literal, overload
from pydantic import BaseModel
from nonebot.compat import PYDANTIC_V2 from nonebot.compat import PYDANTIC_V2
from pydantic import BaseModel
__all__ = ("model_validator", "model_rebuild") __all__ = ("model_rebuild", "model_validator")
if PYDANTIC_V2: if PYDANTIC_V2:

View File

@ -1,4 +1,4 @@
from .db_config import config as config from .db_config import config as config
from .utils import NoSuchUserException as NoSuchUserException
from .utils import NoSuchTargetException as NoSuchTargetException
from .utils import NoSuchSubscribeException as NoSuchSubscribeException from .utils import NoSuchSubscribeException as NoSuchSubscribeException
from .utils import NoSuchTargetException as NoSuchTargetException
from .utils import NoSuchUserException as NoSuchUserException

View File

@ -1,19 +1,20 @@
import os from collections import defaultdict
from datetime import datetime
import json import json
import os
from os import path from os import path
from pathlib import Path from pathlib import Path
from datetime import datetime
from collections import defaultdict
from typing import Literal, TypedDict from typing import Literal, TypedDict
from nonebot.log import logger from nonebot.log import logger
from tinydb import Query, TinyDB from tinydb import Query, TinyDB
from ..utils import Singleton from nonebot_bison.platform import platform_manager
from ..types import User, Target from nonebot_bison.plugin_config import plugin_config
from ..platform import platform_manager from nonebot_bison.types import Target, User
from ..plugin_config import plugin_config from nonebot_bison.utils import Singleton
from .utils import NoSuchUserException, NoSuchSubscribeException
from .utils import NoSuchSubscribeException, NoSuchUserException
supported_target_type = platform_manager.keys() supported_target_type = platform_manager.keys()

View File

@ -1,20 +1,20 @@
import asyncio import asyncio
from collections import defaultdict from collections import defaultdict
from datetime import time, datetime from collections.abc import Awaitable, Callable, Sequence
from collections.abc import Callable, Sequence, Awaitable from datetime import datetime, time
from nonebot.compat import model_dump from nonebot.compat import model_dump
from sqlalchemy.orm import selectinload
from sqlalchemy.exc import IntegrityError
from sqlalchemy import func, delete, select
from nonebot_plugin_saa import PlatformTarget
from nonebot_plugin_datastore import create_session from nonebot_plugin_datastore import create_session
from nonebot_plugin_saa import PlatformTarget
from sqlalchemy import delete, func, select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import selectinload
from ..types import Tag from nonebot_bison.types import Category, PlatformWeightConfigResp, Tag, TimeWeightConfig, UserSubInfo, WeightConfig
from ..types import Target as T_Target from nonebot_bison.types import Target as T_Target
from .utils import NoSuchTargetException, DuplicateCookieTargetException
from .db_model import User, Cookie, Target, Subscribe, CookieTarget, ScheduleTimeWeight from .db_model import Cookie, CookieTarget, ScheduleTimeWeight, Subscribe, Target, User
from ..types import Category, UserSubInfo, WeightConfig, TimeWeightConfig, PlatformWeightConfigResp from .utils import DuplicateCookieTargetException, NoSuchTargetException
def _get_time(): def _get_time():

View File

@ -1,11 +1,11 @@
from nonebot.log import logger
from nonebot.compat import model_dump from nonebot.compat import model_dump
from nonebot.log import logger
from nonebot_plugin_datastore.db import get_engine from nonebot_plugin_datastore.db import get_engine
from sqlalchemy.ext.asyncio.session import AsyncSession
from nonebot_plugin_saa import TargetQQGroup, TargetQQPrivate from nonebot_plugin_saa import TargetQQGroup, TargetQQPrivate
from sqlalchemy.ext.asyncio.session import AsyncSession
from .db_model import User, Target, Subscribe
from .config_legacy import Config, ConfigContent, drop from .config_legacy import Config, ConfigContent, drop
from .db_model import Subscribe, Target, User
async def data_migrate(): async def data_migrate():

View File

@ -1,15 +1,15 @@
import datetime import datetime
from typing import Any
from pathlib import Path from pathlib import Path
from typing import Any
from nonebot_plugin_saa import PlatformTarget
from sqlalchemy.dialects.postgresql import JSONB
from nonebot.compat import PYDANTIC_V2, ConfigDict from nonebot.compat import PYDANTIC_V2, ConfigDict
from nonebot_plugin_datastore import get_plugin_data from nonebot_plugin_datastore import get_plugin_data
from sqlalchemy.orm import Mapped, relationship, mapped_column from nonebot_plugin_saa import PlatformTarget
from sqlalchemy import JSON, String, DateTime, ForeignKey, UniqueConstraint from sqlalchemy import JSON, DateTime, ForeignKey, String, UniqueConstraint
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column, relationship
from ..types import Tag, Category from nonebot_bison.types import Category, Tag
Model = get_plugin_data().Model Model = get_plugin_data().Model
get_plugin_data().set_migration_dir(Path(__file__).parent / "migrations") get_plugin_data().set_migration_dir(Path(__file__).parent / "migrations")

View File

@ -6,8 +6,8 @@ Create Date: 2022-03-21 19:18:13.762626
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "0571870f5222" revision = "0571870f5222"

View File

@ -6,8 +6,8 @@ Create Date: 2022-03-27 21:50:10.911649
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "4a46ba54a3f3" revision = "4a46ba54a3f3"

View File

@ -6,8 +6,8 @@ Create Date: 2022-05-31 22:05:13.235981
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "5f3370328e44" revision = "5f3370328e44"

View File

@ -6,8 +6,8 @@ Create Date: 2023-03-20 00:39:30.199915
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.postgresql import JSONB
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -6,8 +6,8 @@ Create Date: 2023-03-20 11:08:42.883556
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.postgresql import JSONB
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.

View File

@ -6,8 +6,8 @@ Create Date: 2023-03-20 15:38:20.220599
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "8d3863e9d74b" revision = "8d3863e9d74b"

View File

@ -6,8 +6,8 @@ Create Date: 2022-03-29 21:01:38.213153
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "a333d6224193" revision = "a333d6224193"

View File

@ -6,10 +6,10 @@ Create Date: 2023-03-20 01:14:42.623789
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.orm import Session import sqlalchemy as sa
from sqlalchemy.ext.automap import automap_base from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "a5466912fad0" revision = "a5466912fad0"

View File

@ -6,8 +6,8 @@ Create Date: 2023-03-09 19:10:42.168133
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "aceef470d69c" revision = "aceef470d69c"

View File

@ -6,11 +6,11 @@ Create Date: 2023-03-02 14:04:16.492133
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
import sqlalchemy as sa
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import Session
from sqlalchemy.ext.automap import automap_base from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "bd92923c218f" revision = "bd92923c218f"

View File

@ -6,8 +6,8 @@ Create Date: 2024-09-23 10:03:30.593263
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
import sqlalchemy as sa
from sqlalchemy import Text from sqlalchemy import Text
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql

View File

@ -6,10 +6,10 @@ Create Date: 2023-08-25 00:20:51.511329
""" """
import sqlalchemy as sa
from alembic import op from alembic import op
from sqlalchemy.orm import Session import sqlalchemy as sa
from sqlalchemy.ext.automap import automap_base from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "f9baef347cc8" revision = "f9baef347cc8"

View File

@ -3,4 +3,4 @@
from . import v1, v2, v3 from . import v1, v2, v3
from .base import NBESFBase from .base import NBESFBase
__all__ = ["v1", "v2", "v3", "NBESFBase"] __all__ = ["NBESFBase", "v1", "v2", "v3"]

View File

@ -1,10 +1,10 @@
from abc import ABC from abc import ABC
from pydantic import BaseModel
from nonebot.compat import PYDANTIC_V2, ConfigDict from nonebot.compat import PYDANTIC_V2, ConfigDict
from nonebot_plugin_saa.registries import AllSupportedPlatformTarget as UserInfo from nonebot_plugin_saa.registries import AllSupportedPlatformTarget as UserInfo
from pydantic import BaseModel
from ....types import Tag, Category from nonebot_bison.types import Category, Tag
class NBESFBase(BaseModel, ABC): class NBESFBase(BaseModel, ABC):

View File

@ -1,17 +1,18 @@
"""nbesf is Nonebot Bison Enchangable Subscribes File! ver.1""" """nbesf is Nonebot Bison Enchangable Subscribes File! ver.1"""
from typing import Any
from functools import partial from functools import partial
from typing import Any
from nonebot.log import logger
from pydantic import BaseModel
from nonebot_plugin_saa import TargetQQGroup, TargetQQPrivate
from nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python from nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
from nonebot.log import logger
from nonebot_plugin_saa import TargetQQGroup, TargetQQPrivate
from pydantic import BaseModel, Field
from nonebot_bison.config.db_config import SubscribeDupException, config
from nonebot_bison.config.subs_io.utils import NBESFParseErr
from nonebot_bison.types import Category, Tag
from ..utils import NBESFParseErr
from ....types import Tag, Category
from .base import NBESFBase, SubReceipt from .base import NBESFBase, SubReceipt
from ...db_config import SubscribeDupException, config
# ===== nbesf 定义格式 ====== # # ===== nbesf 定义格式 ====== #
NBESF_VERSION = 1 NBESF_VERSION = 1
@ -79,7 +80,7 @@ class SubGroup(
""" """
version: int = NBESF_VERSION version: int = NBESF_VERSION
groups: list[SubPack] = [] groups: list[SubPack] = Field(default_factory=list)
# ======================= # # ======================= #
@ -108,11 +109,11 @@ async def subs_receipt_gen(nbesf_data: SubGroup):
try: try:
await config.add_subscribe(receipt.user, **model_dump(receipt, exclude={"user"})) await config.add_subscribe(receipt.user, **model_dump(receipt, exclude={"user"}))
except SubscribeDupException: except SubscribeDupException:
logger.warning(f"!添加订阅条目 {repr(receipt)} 失败: 相同的订阅已存在") logger.warning(f"!添加订阅条目 {receipt!r} 失败: 相同的订阅已存在")
except Exception as e: except Exception as e:
logger.error(f"!添加订阅条目 {repr(receipt)} 失败: {repr(e)}") logger.error(f"!添加订阅条目 {receipt!r} 失败: {e!r}")
else: else:
logger.success(f"添加订阅条目 {repr(receipt)} 成功!") logger.success(f"添加订阅条目 {receipt!r} 成功!")
def nbesf_parser(raw_data: Any) -> SubGroup: def nbesf_parser(raw_data: Any) -> SubGroup:

View File

@ -1,17 +1,18 @@
"""nbesf is Nonebot Bison Enchangable Subscribes File! ver.2""" """nbesf is Nonebot Bison Enchangable Subscribes File! ver.2"""
from typing import Any
from functools import partial from functools import partial
from typing import Any
from nonebot.log import logger
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.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
from nonebot.log import logger
from nonebot_plugin_saa.registries import AllSupportedPlatformTarget
from pydantic import BaseModel, Field
from nonebot_bison.config.db_config import SubscribeDupException, config
from nonebot_bison.config.subs_io.utils import NBESFParseErr
from nonebot_bison.types import Category, Tag
from ..utils import NBESFParseErr
from ....types import Tag, Category
from .base import NBESFBase, SubReceipt from .base import NBESFBase, SubReceipt
from ...db_config import SubscribeDupException, config
# ===== nbesf 定义格式 ====== # # ===== nbesf 定义格式 ====== #
NBESF_VERSION = 2 NBESF_VERSION = 2
@ -64,7 +65,7 @@ class SubGroup(NBESFBase):
""" """
version: int = NBESF_VERSION version: int = NBESF_VERSION
groups: list[SubPack] = [] groups: list[SubPack] = Field(default_factory=list)
# ======================= # # ======================= #
@ -85,11 +86,11 @@ async def subs_receipt_gen(nbesf_data: SubGroup):
try: try:
await config.add_subscribe(receipt.user, **model_dump(receipt, exclude={"user"})) await config.add_subscribe(receipt.user, **model_dump(receipt, exclude={"user"}))
except SubscribeDupException: except SubscribeDupException:
logger.warning(f"!添加订阅条目 {repr(receipt)} 失败: 相同的订阅已存在") logger.warning(f"!添加订阅条目 {receipt!r} 失败: 相同的订阅已存在")
except Exception as e: except Exception as e:
logger.error(f"!添加订阅条目 {repr(receipt)} 失败: {repr(e)}") logger.error(f"!添加订阅条目 {receipt!r} 失败: {e!r}")
else: else:
logger.success(f"添加订阅条目 {repr(receipt)} 成功!") logger.success(f"添加订阅条目 {receipt!r} 成功!")
def nbesf_parser(raw_data: Any) -> SubGroup: def nbesf_parser(raw_data: Any) -> SubGroup:

View File

@ -1,21 +1,20 @@
"""nbesf is Nonebot Bison Enchangable Subscribes File! ver.2""" """nbesf is Nonebot Bison Enchangable Subscribes File! ver.2"""
from typing import Any
from functools import partial from functools import partial
from typing import Any
from nonebot.log import logger
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.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
from nonebot.log import logger
from nonebot_plugin_saa.registries import AllSupportedPlatformTarget
from pydantic import BaseModel, Field
from nonebot_bison.types import Tag from nonebot_bison.config.db_config import SubscribeDupException, config
from nonebot_bison.types import Category from nonebot_bison.config.db_model import Cookie as DBCookie
from nonebot_bison.config.subs_io.utils import NBESFParseErr
from nonebot_bison.types import Category, Tag
from nonebot_bison.types import Target as T_Target from nonebot_bison.types import Target as T_Target
from ..utils import NBESFParseErr
from .base import NBESFBase, SubReceipt from .base import NBESFBase, SubReceipt
from ...db_model import Cookie as DBCookie
from ...db_config import SubscribeDupException, config
# ===== nbesf 定义格式 ====== # # ===== nbesf 定义格式 ====== #
NBESF_VERSION = 3 NBESF_VERSION = 3
@ -80,8 +79,8 @@ class SubGroup(NBESFBase):
""" """
version: int = NBESF_VERSION version: int = NBESF_VERSION
groups: list[SubPack] = [] groups: list[SubPack] = Field(default_factory=list)
cookies: list[Cookie] = [] cookies: list[Cookie] = Field(default_factory=list)
# ======================= # # ======================= #
@ -103,11 +102,11 @@ async def subs_receipt_gen(nbesf_data: SubGroup):
try: try:
await config.add_subscribe(receipt.user, **model_dump(receipt, exclude={"user"})) await config.add_subscribe(receipt.user, **model_dump(receipt, exclude={"user"}))
except SubscribeDupException: except SubscribeDupException:
logger.warning(f"!添加订阅条目 {repr(receipt)} 失败: 相同的订阅已存在") logger.warning(f"!添加订阅条目 {receipt!r} 失败: 相同的订阅已存在")
except Exception as e: except Exception as e:
logger.error(f"!添加订阅条目 {repr(receipt)} 失败: {repr(e)}") logger.error(f"!添加订阅条目 {receipt!r} 失败: {e!r}")
else: else:
logger.success(f"添加订阅条目 {repr(receipt)} 成功!") logger.success(f"添加订阅条目 {receipt!r} 成功!")
async def magic_cookie_gen(nbesf_data: SubGroup): async def magic_cookie_gen(nbesf_data: SubGroup):
@ -119,9 +118,9 @@ async def magic_cookie_gen(nbesf_data: SubGroup):
for target in cookie.targets: for target in cookie.targets:
await config.add_cookie_target(T_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: except Exception as e:
logger.error(f"!添加 Cookie 条目 {repr(cookie)} 失败: {repr(e)}") logger.error(f"!添加 Cookie 条目 {cookie!r} 失败: {e!r}")
else: else:
logger.success(f"添加 Cookie 条目 {repr(cookie)} 成功!") logger.success(f"添加 Cookie 条目 {cookie!r} 成功!")
def nbesf_parser(raw_data: Any) -> SubGroup: def nbesf_parser(raw_data: Any) -> SubGroup:

View File

@ -1,19 +1,20 @@
from typing import cast
from collections import defaultdict from collections import defaultdict
from collections.abc import Callable from collections.abc import Callable
from typing import cast
from sqlalchemy import select
from nonebot.log import logger
from sqlalchemy.sql.selectable import Select
from nonebot_plugin_saa import PlatformTarget
from nonebot.compat import type_validate_python from nonebot.compat import type_validate_python
from nonebot.log import logger
from nonebot_plugin_datastore.db import create_session from nonebot_plugin_datastore.db import create_session
from nonebot_plugin_saa import PlatformTarget
from sqlalchemy import select
from sqlalchemy.orm.strategy_options import selectinload from sqlalchemy.orm.strategy_options import selectinload
from sqlalchemy.sql.selectable import Select
from nonebot_bison.config import config
from nonebot_bison.config.db_model import Cookie, CookieTarget, Subscribe, Target, User
from .. import config
from .utils import NBESFVerMatchErr, row2dict
from .nbesf_model import NBESFBase, v1, v2, v3 from .nbesf_model import NBESFBase, v1, v2, v3
from ..db_model import User, Cookie, Target, Subscribe, CookieTarget from .utils import NBESFVerMatchErr, row2dict
async def subscribes_export(selector: Callable[[Select], Select]) -> v3.SubGroup: async def subscribes_export(selector: Callable[[Select], Select]) -> v3.SubGroup:

View File

@ -1,9 +1,10 @@
from pathlib import Path
from pkgutil import iter_modules
from collections import defaultdict from collections import defaultdict
from importlib import import_module from importlib import import_module
from pathlib import Path
from pkgutil import iter_modules
from nonebot_bison.plugin_config import plugin_config
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)

View File

@ -1,18 +1,19 @@
import re
import html
from typing import Any
from functools import partial from functools import partial
import html
import re
from typing import Any, ClassVar
from yarl import URL
from httpx import AsyncClient
from bs4 import BeautifulSoup as bs from bs4 import BeautifulSoup as bs
from pydantic import Field, BaseModel from httpx import AsyncClient
from nonebot.compat import type_validate_python from nonebot.compat import type_validate_python
from pydantic import BaseModel, Field
from yarl import URL
from nonebot_bison.post import Post
from nonebot_bison.post.protocol import HTMLContentSupport
from nonebot_bison.types import Category, RawPost, Target
from nonebot_bison.utils import Site
from ..post import Post
from ..utils import Site
from ..types import Target, RawPost, Category
from ..post.protocol import HTMLContentSupport
from .platform import NewMessage, StatusChange from .platform import NewMessage, StatusChange
@ -58,7 +59,7 @@ class ArkBulletinResponse(ArkResponseBase):
class ArknightsSite(Site): class ArknightsSite(Site):
name = "arknights" name = "arknights"
schedule_type = "interval" schedule_type = "interval"
schedule_setting = {"seconds": 30} schedule_setting: ClassVar[dict] = {"seconds": 30}
class ArknightsPost(Post, HTMLContentSupport): class ArknightsPost(Post, HTMLContentSupport):
@ -95,7 +96,7 @@ class ArknightsPost(Post, HTMLContentSupport):
class Arknights(NewMessage): class Arknights(NewMessage):
categories = {1: "游戏公告"} categories: ClassVar[dict[Category, str]] = {1: "游戏公告"}
platform_name = "arknights" platform_name = "arknights"
name = "明日方舟游戏信息" name = "明日方舟游戏信息"
enable_tag = False enable_tag = False
@ -157,7 +158,7 @@ class Arknights(NewMessage):
class AkVersion(StatusChange): class AkVersion(StatusChange):
categories = {2: "更新信息"} categories: ClassVar[dict[Category, str]] = {2: "更新信息"}
platform_name = "arknights" platform_name = "arknights"
name = "明日方舟游戏信息" name = "明日方舟游戏信息"
enable_tag = False enable_tag = False
@ -202,7 +203,7 @@ class AkVersion(StatusChange):
class MonsterSiren(NewMessage): class MonsterSiren(NewMessage):
categories = {3: "塞壬唱片新闻"} categories: ClassVar[dict[Category, str]] = {3: "塞壬唱片新闻"}
platform_name = "arknights" platform_name = "arknights"
name = "明日方舟游戏信息" name = "明日方舟游戏信息"
enable_tag = False enable_tag = False
@ -250,7 +251,7 @@ class MonsterSiren(NewMessage):
class TerraHistoricusComic(NewMessage): class TerraHistoricusComic(NewMessage):
categories = {4: "泰拉记事社漫画"} categories: ClassVar[dict[Category, str]] = {4: "泰拉记事社漫画"}
platform_name = "arknights" platform_name = "arknights"
name = "明日方舟游戏信息" name = "明日方舟游戏信息"
enable_tag = False enable_tag = False

View File

@ -1,7 +1,7 @@
from .platforms import Bilibili as Bilibili from .platforms import Bilibili as Bilibili
from .platforms import Bilibililive as Bilibililive
from .scheduler import BilibiliSite as BilibiliSite
from .scheduler import BililiveSite as BililiveSite
from .platforms import BilibiliBangumi as BilibiliBangumi from .platforms import BilibiliBangumi as BilibiliBangumi
from .platforms import Bilibililive as Bilibililive
from .scheduler import BiliBangumiSite as BiliBangumiSite from .scheduler import BiliBangumiSite as BiliBangumiSite
from .scheduler import BilibiliClientManager as BilibiliClientManager from .scheduler import BilibiliClientManager as BilibiliClientManager
from .scheduler import BilibiliSite as BilibiliSite
from .scheduler import BililiveSite as BililiveSite

View File

@ -1,22 +1,22 @@
import sys
import asyncio import asyncio
import inspect from collections.abc import AsyncGenerator, Awaitable, Callable, Sequence
from collections.abc import Set as AbstractSet
from dataclasses import dataclass
from enum import Enum from enum import Enum
from functools import wraps from functools import wraps
from dataclasses import dataclass import inspect
from collections.abc import Set as AbstractSet import sys
from collections.abc import Callable, Sequence, Awaitable, AsyncGenerator
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any, Any,
Concatenate,
Generic, Generic,
TypeVar, NamedTuple,
Protocol,
ParamSpec, ParamSpec,
Protocol,
TypeAlias, TypeAlias,
TypedDict, TypedDict,
NamedTuple, TypeVar,
Concatenate,
overload, overload,
runtime_checkable, runtime_checkable,
) )

View File

@ -1,7 +1,7 @@
from typing import Any, Literal, TypeVar, TypeAlias from typing import Any, Literal, TypeAlias, TypeVar
from pydantic import BaseModel
from nonebot.compat import PYDANTIC_V2, ConfigDict from nonebot.compat import PYDANTIC_V2, ConfigDict
from pydantic import BaseModel
from nonebot_bison.compat import model_rebuild from nonebot_bison.compat import model_rebuild
@ -13,7 +13,7 @@ TBaseModel = TypeVar("TBaseModel", bound=type[BaseModel])
def model_rebuild_recurse(cls: TBaseModel) -> TBaseModel: def model_rebuild_recurse(cls: TBaseModel) -> TBaseModel:
"""Recursively rebuild all BaseModel subclasses in the class.""" """Recursively rebuild all BaseModel subclasses in the class."""
if not PYDANTIC_V2: if not PYDANTIC_V2:
from inspect import isclass, getmembers from inspect import getmembers, isclass
for _, sub_cls in getmembers(cls, lambda x: isclass(x) and issubclass(x, BaseModel)): for _, sub_cls in getmembers(cls, lambda x: isclass(x) and issubclass(x, BaseModel)):
model_rebuild_recurse(sub_cls) model_rebuild_recurse(sub_cls)

View File

@ -1,41 +1,41 @@
import re
import json
from copy import deepcopy from copy import deepcopy
from enum import Enum, unique from enum import Enum, unique
from typing import NamedTuple import json
import re
from typing import ClassVar, NamedTuple
from typing_extensions import Self from typing_extensions import Self
from yarl import URL
from nonebot import logger
from httpx import AsyncClient from httpx import AsyncClient
from pydantic import Field, BaseModel, ValidationError from nonebot import logger
from nonebot.compat import type_validate_json, type_validate_python from nonebot.compat import type_validate_json, type_validate_python
from pydantic import BaseModel, Field, ValidationError
from yarl import URL
from nonebot_bison.post.post import Post
from nonebot_bison.compat import model_rebuild from nonebot_bison.compat import model_rebuild
from nonebot_bison.utils import text_similarity, decode_unicode_escapes from nonebot_bison.platform.platform import CategoryNotRecognize, CategoryNotSupport, NewMessage, StatusChange
from nonebot_bison.types import Tag, Target, RawPost, ApiError, Category from nonebot_bison.post.post import Post
from nonebot_bison.types import ApiError, Category, RawPost, Tag, Target
from nonebot_bison.utils import decode_unicode_escapes, text_similarity
from .retry import ApiCode352Error, retry_for_352
from .scheduler import BilibiliSite, BililiveSite, BiliBangumiSite
from ..platform import NewMessage, StatusChange, CategoryNotSupport, CategoryNotRecognize
from .models import ( from .models import (
PostAPI,
UserAPI,
PGCMajor,
DrawMajor,
LiveMajor,
OPUSMajor,
DynRawPost,
VideoMajor,
CommonMajor,
DynamicType,
ArticleMajor, ArticleMajor,
CommonMajor,
CoursesMajor, CoursesMajor,
DeletedMajor, DeletedMajor,
UnknownMajor, DrawMajor,
DynamicType,
DynRawPost,
LiveMajor,
LiveRecommendMajor, LiveRecommendMajor,
OPUSMajor,
PGCMajor,
PostAPI,
UnknownMajor,
UserAPI,
VideoMajor,
) )
from .retry import ApiCode352Error, retry_for_352
from .scheduler import BiliBangumiSite, BilibiliSite, BililiveSite
class _ProcessedText(NamedTuple): class _ProcessedText(NamedTuple):
@ -51,7 +51,7 @@ class _ParsedMojarPost(NamedTuple):
class Bilibili(NewMessage): class Bilibili(NewMessage):
categories = { categories: ClassVar[dict[Category, str]] = {
1: "一般动态", 1: "一般动态",
2: "专栏文章", 2: "专栏文章",
3: "视频", 3: "视频",
@ -162,7 +162,6 @@ class Bilibili(NewMessage):
return tags return tags
def _text_process(self, dynamic: str, desc: str, title: str) -> _ProcessedText: def _text_process(self, dynamic: str, desc: str, title: str) -> _ProcessedText:
# 计算视频标题和视频描述相似度 # 计算视频标题和视频描述相似度
title_similarity = 0.0 if len(title) == 0 or len(desc) == 0 else text_similarity(title, desc[: len(title)]) title_similarity = 0.0 if len(title) == 0 or len(desc) == 0 else text_similarity(title, desc[: len(title)])
if title_similarity > 0.9: if title_similarity > 0.9:
@ -308,7 +307,7 @@ class Bilibili(NewMessage):
class Bilibililive(StatusChange): class Bilibililive(StatusChange):
categories = {1: "开播提醒", 2: "标题更新提醒", 3: "下播提醒"} categories: ClassVar[dict[Category, str]] = {1: "开播提醒", 2: "标题更新提醒", 3: "下播提醒"}
platform_name = "bilibili-live" platform_name = "bilibili-live"
enable_tag = False enable_tag = False
enabled = True enabled = True
@ -458,7 +457,7 @@ class Bilibililive(StatusChange):
class BilibiliBangumi(StatusChange): class BilibiliBangumi(StatusChange):
categories = {} categories: ClassVar[dict[Category, str]] = {}
platform_name = "bilibili-bangumi" platform_name = "bilibili-bangumi"
enable_tag = False enable_tag = False
enabled = True enabled = True

View File

@ -1,20 +1,20 @@
import random from collections.abc import Awaitable, Callable
from enum import Enum
from functools import wraps
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timedelta from datetime import datetime, timedelta
from collections.abc import Callable, Awaitable from enum import Enum
from typing_extensions import override, assert_never from functools import wraps
import random
from typing import TYPE_CHECKING, Generic, Literal, TypeVar from typing import TYPE_CHECKING, Generic, Literal, TypeVar
from typing_extensions import assert_never, override
from strenum import StrEnum
from nonebot.log import logger
from httpx import URL as HttpxURL from httpx import URL as HttpxURL
from nonebot.log import logger
from strenum import StrEnum
from nonebot_bison.types import Target from nonebot_bison.types import Target
from .fsm import FSM, ActionReturn, Condition, StateGraph, Transition, reset_on_exception
from .models import DynRawPost from .models import DynRawPost
from .fsm import FSM, Condition, StateGraph, Transition, ActionReturn, reset_on_exception
if TYPE_CHECKING: if TYPE_CHECKING:
from .platforms import Bilibili from .platforms import Bilibili
@ -115,8 +115,7 @@ class RetryAddon(Generic[TBilibili]):
def record_backoff_finish_time(self): def record_backoff_finish_time(self):
self.backoff_finish_time = ( self.backoff_finish_time = (
datetime.now() datetime.now() + self.backoff_timedelta * self.backoff_count**2
+ self.backoff_timedelta * self.backoff_count**2
# + timedelta(seconds=random.randint(1, 60)) # jitter # + timedelta(seconds=random.randint(1, 60)) # jitter
) )
logger.trace(f"set backoff finish time: {self.backoff_finish_time}") logger.trace(f"set backoff finish time: {self.backoff_finish_time}")

View File

@ -1,17 +1,16 @@
from datetime import datetime, timedelta
import json import json
import random import random
from typing import TYPE_CHECKING, ClassVar, TypeVar
from typing_extensions import override from typing_extensions import override
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, TypeVar
from httpx import AsyncClient from httpx import AsyncClient
from nonebot import logger, require from nonebot import logger, require
from playwright.async_api import Cookie from playwright.async_api import Cookie
from nonebot_bison.config.db_model import Cookie as CookieModel
from nonebot_bison.utils import Site, http_client from nonebot_bison.utils import Site, http_client
from nonebot_bison.utils.site import CookieClientManager
from ...utils.site import CookieClientManager
from ...config.db_model import Cookie as CookieModel
if TYPE_CHECKING: if TYPE_CHECKING:
from .platforms import Bilibili from .platforms import Bilibili
@ -23,7 +22,6 @@ B = TypeVar("B", bound="Bilibili")
class BilibiliClientManager(CookieClientManager): class BilibiliClientManager(CookieClientManager):
_default_cookie_cd = timedelta(seconds=120) _default_cookie_cd = timedelta(seconds=120)
async def _get_cookies(self) -> list[Cookie]: async def _get_cookies(self) -> list[Cookie]:
@ -75,7 +73,7 @@ class BilibiliClientManager(CookieClientManager):
class BilibiliSite(Site): class BilibiliSite(Site):
name = "bilibili.com" name = "bilibili.com"
schedule_setting = {"seconds": 60} schedule_setting: ClassVar[dict] = {"seconds": 60}
schedule_type = "interval" schedule_type = "interval"
client_mgr = BilibiliClientManager client_mgr = BilibiliClientManager
require_browser = True require_browser = True
@ -83,11 +81,11 @@ class BilibiliSite(Site):
class BililiveSite(Site): class BililiveSite(Site):
name = "live.bilibili.com" name = "live.bilibili.com"
schedule_setting = {"seconds": 5} schedule_setting: ClassVar[dict] = {"seconds": 5}
schedule_type = "interval" schedule_type = "interval"
class BiliBangumiSite(Site): class BiliBangumiSite(Site):
name = "bilibili.com/bangumi" name = "bilibili.com/bangumi"
schedule_setting = {"seconds": 30} schedule_setting: ClassVar[dict] = {"seconds": 30}
schedule_type = "interval" schedule_type = "interval"

View File

@ -1,16 +1,16 @@
from typing import TypeAlias
from functools import partial
from datetime import timedelta
from types import MappingProxyType
from collections.abc import Callable from collections.abc import Callable
from datetime import timedelta
from functools import partial
from types import MappingProxyType
from typing import TypeAlias
from expiringdictx import ExpiringDict, SimpleCache
from hishel import AsyncCacheTransport, AsyncInMemoryStorage, Controller
from httpx import AsyncClient, AsyncHTTPTransport from httpx import AsyncClient, AsyncHTTPTransport
from expiringdictx import SimpleCache, ExpiringDict
from hishel import Controller, AsyncCacheTransport, AsyncInMemoryStorage
from .const import DATASOURCE_URL from .const import DATASOURCE_URL
from .utils import process_response
from .models import CeobeSource, CeobeTarget, DataSourceResponse from .models import CeobeSource, CeobeTarget, DataSourceResponse
from .utils import process_response
cache_transport = AsyncCacheTransport( cache_transport = AsyncCacheTransport(
AsyncHTTPTransport(), AsyncHTTPTransport(),

View File

@ -1,4 +1,4 @@
from typing import Literal, TypeVar, NamedTuple from typing import Literal, NamedTuple, TypeVar
from pydantic import BaseModel from pydantic import BaseModel

View File

@ -1,23 +1,23 @@
from typing import ParamSpec
from functools import partial
from datetime import timedelta
from collections import defaultdict from collections import defaultdict
from datetime import timedelta
from functools import partial
from typing import ClassVar, ParamSpec
from httpx import AsyncClient from httpx import AsyncClient
from nonebot import logger, require from nonebot import logger, require
from rapidfuzz import fuzz, process from rapidfuzz import fuzz, process
from nonebot_bison.post import Post from nonebot_bison.platform.platform import NewMessage
from nonebot_bison.plugin_config import plugin_config from nonebot_bison.plugin_config import plugin_config
from nonebot_bison.types import Target, RawPost, Category from nonebot_bison.post import Post
from nonebot_bison.utils import Site, ClientManager, capture_html from nonebot_bison.types import Category, RawPost, Target
from nonebot_bison.utils import ClientManager, Site, capture_html
from ..platform import NewMessage
from .utils import process_response
from .const import COMB_ID_URL, COOKIES_URL, COOKIE_ID_URL
from .exception import CeobeSnapshotSkip, CeobeSnapshotFailed
from .cache import CeobeCache, CeobeClient, CeobeDataSourceCache from .cache import CeobeCache, CeobeClient, CeobeDataSourceCache
from .models import CeobeImage, CeobeCookie, CeobeTextPic, CombIdResponse, CookiesResponse, CookieIdResponse from .const import COMB_ID_URL, COOKIE_ID_URL, COOKIES_URL
from .exception import CeobeSnapshotFailed, CeobeSnapshotSkip
from .models import CeobeCookie, CeobeImage, CeobeTextPic, CombIdResponse, CookieIdResponse, CookiesResponse
from .utils import process_response
P = ParamSpec("P") P = ParamSpec("P")
@ -49,7 +49,7 @@ class CeobeCanteenSite(Site):
name = "ceobe_canteen" name = "ceobe_canteen"
schedule_type = "interval" schedule_type = "interval"
# lwt の 推荐间隔 # lwt の 推荐间隔
schedule_setting = {"seconds": 15} schedule_setting: ClassVar[dict] = {"seconds": 15}
client_mgr = CeobeCanteenClientManager client_mgr = CeobeCanteenClientManager
@ -64,7 +64,7 @@ class CeobeCanteen(NewMessage):
use_batch: bool = True use_batch: bool = True
default_theme: str = "ceobecanteen" default_theme: str = "ceobecanteen"
categories: dict[Category, str] = {1: "普通", 2: "转发"} categories: ClassVar[dict[Category, str]] = {1: "普通", 2: "转发"}
data_source_cache = CeobeDataSourceCache() data_source_cache = CeobeDataSourceCache()
@ -213,7 +213,9 @@ class CeobeCanteen(NewMessage):
logger.debug(f"snapshot official website url: {url}") logger.debug(f"snapshot official website url: {url}")
# /html/body/div[1]/div[1]/div/div[1]/div[1]/div # /html/body/div[1]/div[1]/div/div[1]/div[1]/div
snapshot_selector = "html > body > div:nth-child(1) > div:nth-child(1) > div > div:nth-child(1) > div:nth-child(1) > div" # noqa: E501 snapshot_selector = (
"html > body > div:nth-child(1) > div:nth-child(1) > div > div:nth-child(1) > div:nth-child(1) > div"
)
# /html/body/div[1]/div[1]/div/div[1]/div[1]/div/div[4]/div/div/div # /html/body/div[1]/div[1]/div/div[1]/div[1]/div/div[4]/div/div/div
calculate_selector = "html > body > div:nth-child(1) > div:nth-child(1) > div > div:nth-child(1) > div:nth-child(1) > div > div:nth-child(4) > div > div > div" # noqa: E501 calculate_selector = "html > body > div:nth-child(1) > div:nth-child(1) > div > div:nth-child(1) > div:nth-child(1) > div > div:nth-child(4) > div > div > div" # noqa: E501
viewport = {"width": 1024, "height": 19990} viewport = {"width": 1024, "height": 19990}

View File

@ -3,7 +3,7 @@ from nonebot import logger
from nonebot.compat import type_validate_python from nonebot.compat import type_validate_python
from .exception import CeobeResponseError from .exception import CeobeResponseError
from .models import ResponseModel, CookieIdResponse from .models import CookieIdResponse, ResponseModel
def process_response(response: Response, parse_model: type[ResponseModel]) -> ResponseModel: def process_response(response: Response, parse_model: type[ResponseModel]) -> ResponseModel:

View File

@ -1,15 +1,16 @@
from typing import Any from typing import Any, ClassVar
from httpx import AsyncClient from httpx import AsyncClient
from ..post import Post from nonebot_bison.post import Post
from nonebot_bison.types import RawPost, Target
from nonebot_bison.utils import anonymous_site
from .platform import NewMessage from .platform import NewMessage
from ..utils import anonymous_site
from ..types import Target, RawPost
class FF14(NewMessage): class FF14(NewMessage):
categories = {} categories: ClassVar[dict] = {}
platform_name = "ff14" platform_name = "ff14"
name = "最终幻想XIV官方公告" name = "最终幻想XIV官方公告"
enable_tag = False enable_tag = False

View File

@ -1,22 +1,23 @@
import re import re
from typing import Any from typing import Any, ClassVar
from httpx import AsyncClient from httpx import AsyncClient
from ..post import Post from nonebot_bison.post import Post
from ..utils import Site from nonebot_bison.types import ApiError, Category, RawPost, Target
from nonebot_bison.utils import Site
from .platform import NewMessage from .platform import NewMessage
from ..types import Target, RawPost, ApiError
class NcmSite(Site): class NcmSite(Site):
name = "music.163.com" name = "music.163.com"
schedule_type = "interval" schedule_type = "interval"
schedule_setting = {"minutes": 1} schedule_setting: ClassVar[dict] = {"minutes": 1}
class NcmArtist(NewMessage): class NcmArtist(NewMessage):
categories = {} categories: ClassVar[dict[Category, str]] = {}
platform_name = "ncm-artist" platform_name = "ncm-artist"
enable_tag = False enable_tag = False
enabled = True enabled = True
@ -73,7 +74,7 @@ class NcmArtist(NewMessage):
class NcmRadio(NewMessage): class NcmRadio(NewMessage):
categories = {} categories: ClassVar[dict[Category, str]] = {}
platform_name = "ncm-radio" platform_name = "ncm-radio"
enable_tag = False enable_tag = False
enabled = True enabled = True

View File

@ -1,22 +1,22 @@
import ssl
import json
import time
import typing
from dataclasses import dataclass
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections import defaultdict from collections import defaultdict
from typing import Any, TypeVar, ParamSpec from collections.abc import Awaitable, Callable, Collection
from collections.abc import Callable, Awaitable, Collection from dataclasses import dataclass
import json
import ssl
import time
import typing
from typing import Any, ParamSpec, TypeVar
import httpx import httpx
from httpx import AsyncClient from httpx import AsyncClient
from nonebot.log import logger from nonebot.log import logger
from nonebot_plugin_saa import PlatformTarget from nonebot_plugin_saa import PlatformTarget
from ..post import Post from nonebot_bison.plugin_config import plugin_config
from ..utils import Site, ProcessContext from nonebot_bison.post import Post
from ..plugin_config import plugin_config from nonebot_bison.types import Category, RawPost, SubUnit, Tag, Target
from ..types import Tag, Target, RawPost, SubUnit, Category from nonebot_bison.utils import ProcessContext, Site
class CategoryNotSupport(Exception): class CategoryNotSupport(Exception):

View File

@ -1,27 +1,27 @@
import time
import calendar import calendar
from typing import Any import time
from typing import Any, ClassVar
from bs4 import BeautifulSoup as bs
import feedparser import feedparser
from httpx import AsyncClient from httpx import AsyncClient
from bs4 import BeautifulSoup as bs
from ..post import Post from nonebot_bison.post import Post
from nonebot_bison.types import Category, RawPost, Target
from nonebot_bison.utils import text_similarity
from nonebot_bison.utils.site import CookieClientManager, Site
from .platform import NewMessage from .platform import NewMessage
from ..types import Target, RawPost
from ..utils import text_similarity
from ..utils.site import Site, CookieClientManager
class RssSite(Site): class RssSite(Site):
name = "rss" name = "rss"
schedule_type = "interval" schedule_type = "interval"
schedule_setting = {"seconds": 30} schedule_setting: ClassVar[dict] = {"seconds": 30}
client_mgr = CookieClientManager.from_name(name) client_mgr = CookieClientManager.from_name(name)
class RssPost(Post): class RssPost(Post):
async def get_plain_content(self) -> str: async def get_plain_content(self) -> str:
soup = bs(self.content, "html.parser") soup = bs(self.content, "html.parser")
@ -38,7 +38,7 @@ class RssPost(Post):
class Rss(NewMessage): class Rss(NewMessage):
categories = {} categories: ClassVar[dict[Category, str]] = {}
enable_tag = False enable_tag = False
platform_name = "rss" platform_name = "rss"
name = "Rss" name = "Rss"

View File

@ -1,21 +1,22 @@
import re
import json
from typing import Any
from datetime import datetime from datetime import datetime
from urllib.parse import unquote import json
import re
from typing import Any, ClassVar
from typing_extensions import override from typing_extensions import override
from urllib.parse import unquote
from yarl import URL
from lxml.etree import HTML
from httpx import AsyncClient
from nonebot.log import logger
from bs4 import BeautifulSoup as bs from bs4 import BeautifulSoup as bs
from httpx import AsyncClient
from lxml.etree import HTML
from nonebot.log import logger
from yarl import URL
from nonebot_bison.post import Post
from nonebot_bison.types import ApiError, Category, RawPost, Tag, Target
from nonebot_bison.utils import http_client, text_fletten
from nonebot_bison.utils.site import CookieClientManager, Site
from ..post import Post
from .platform import NewMessage from .platform import NewMessage
from ..utils import http_client, text_fletten
from ..utils.site import Site, CookieClientManager
from ..types import Tag, Target, RawPost, ApiError, Category
_HEADER = { _HEADER = {
"accept": ( "accept": (
@ -59,12 +60,12 @@ class WeiboClientManager(CookieClientManager):
class WeiboSite(Site): class WeiboSite(Site):
name = "weibo.com" name = "weibo.com"
schedule_type = "interval" schedule_type = "interval"
schedule_setting = {"seconds": 3} schedule_setting: ClassVar[dict] = {"seconds": 3}
client_mgr = WeiboClientManager client_mgr = WeiboClientManager
class Weibo(NewMessage): class Weibo(NewMessage):
categories = { categories: ClassVar[dict[Category, str]] = {
1: "转发", 1: "转发",
2: "视频", 2: "视频",
3: "图文", 3: "图文",

View File

@ -1,8 +1,8 @@
import nonebot import nonebot
from yarl import URL
from nonebot import get_plugin_config from nonebot import get_plugin_config
from pydantic import Field, BaseModel
from nonebot.compat import PYDANTIC_V2, ConfigDict from nonebot.compat import PYDANTIC_V2, ConfigDict
from pydantic import BaseModel, Field
from yarl import URL
global_config = nonebot.get_driver().config global_config = nonebot.get_driver().config
PlatformName = str PlatformName = str

View File

@ -1,10 +1,10 @@
from dataclasses import dataclass
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass
from nonebot_plugin_saa import Text, MessageFactory, MessageSegmentFactory from nonebot_plugin_saa import MessageFactory, MessageSegmentFactory, Text
from ..utils import text_to_image from nonebot_bison.plugin_config import plugin_config
from ..plugin_config import plugin_config from nonebot_bison.utils import text_to_image
@dataclass(kw_only=True) @dataclass(kw_only=True)

View File

@ -1,21 +1,22 @@
import reprlib from collections.abc import Sequence
from dataclasses import dataclass, fields
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
import reprlib
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from collections.abc import Sequence
from dataclasses import fields, dataclass
from nonebot.log import logger from nonebot.log import logger
from nonebot_plugin_saa import MessageSegmentFactory from nonebot_plugin_saa import MessageSegmentFactory
from ..theme import theme_manager from nonebot_bison.plugin_config import plugin_config
from nonebot_bison.theme import theme_manager
from nonebot_bison.theme.types import ThemeRenderError, ThemeRenderUnsupportError
from .abstract_post import AbstractPost from .abstract_post import AbstractPost
from ..plugin_config import plugin_config
from .protocol import PlainContentSupport from .protocol import PlainContentSupport
from ..theme.types import ThemeRenderError, ThemeRenderUnsupportError
if TYPE_CHECKING: if TYPE_CHECKING:
from ..platform import Platform from nonebot_bison.platform import Platform
@dataclass @dataclass

View File

@ -1,3 +1,3 @@
from .manager import init_scheduler, scheduler_dict, handle_delete_target, handle_insert_new_target from .manager import handle_delete_target, handle_insert_new_target, init_scheduler, scheduler_dict
__all__ = ["init_scheduler", "handle_delete_target", "handle_insert_new_target", "scheduler_dict"] __all__ = ["handle_delete_target", "handle_insert_new_target", "init_scheduler", "scheduler_dict"]

View File

@ -2,14 +2,15 @@ from typing import cast
from nonebot.log import logger from nonebot.log import logger
from ..utils import Site from nonebot_bison.config import config
from ..config import config from nonebot_bison.config.db_model import Target
from nonebot_bison.platform import platform_manager
from nonebot_bison.plugin_config import plugin_config
from nonebot_bison.types import Target as T_Target
from nonebot_bison.utils import Site
from nonebot_bison.utils.site import CookieClientManager, is_cookie_client_manager
from .scheduler import Scheduler from .scheduler import Scheduler
from ..config.db_model import Target
from ..types import Target as T_Target
from ..platform import platform_manager
from ..plugin_config import plugin_config
from ..utils.site import CookieClientManager, is_cookie_client_manager
scheduler_dict: dict[type[Site], Scheduler] = {} scheduler_dict: dict[type[Site], Scheduler] = {}

View File

@ -1,18 +1,16 @@
from dataclasses import dataclass
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass
from nonebot.log import logger from nonebot.log import logger
from nonebot_plugin_apscheduler import scheduler from nonebot_plugin_apscheduler import scheduler
from nonebot_plugin_saa.utils.exceptions import NoBotFound from nonebot_plugin_saa.utils.exceptions import NoBotFound
from nonebot_bison.utils import ClientManager from nonebot_bison.config import config
from nonebot_bison.platform import platform_manager
from ..config import config from nonebot_bison.send import send_msgs
from ..send import send_msgs from nonebot_bison.types import SubUnit, Target
from ..types import Target, SubUnit from nonebot_bison.utils import ClientManager, ProcessContext, Site
from ..platform import platform_manager from nonebot_bison.utils.site import SkipRequestException
from ..utils import Site, ProcessContext
from ..utils.site import SkipRequestException
@dataclass @dataclass

View File

@ -1,25 +1,26 @@
import json from collections.abc import Callable, Coroutine
import time from functools import partial, wraps
import importlib import importlib
import json
from pathlib import Path from pathlib import Path
import time
from types import ModuleType from types import ModuleType
from typing import Any, TypeVar from typing import Any, TypeVar
from functools import wraps, partial
from collections.abc import Callable, Coroutine
from nonebot.log import logger from anyio import open_file
from nonebot.compat import model_dump from nonebot.compat import model_dump
from nonebot.log import logger
from ..scheduler.manager import init_scheduler from nonebot_bison.config.subs_io import subscribes_export, subscribes_import
from ..config.subs_io.nbesf_model import v1, v2, v3 from nonebot_bison.config.subs_io.nbesf_model import v1, v2, v3
from ..config.subs_io import subscribes_export, subscribes_import from nonebot_bison.scheduler.manager import init_scheduler
try: try:
from typing_extensions import ParamSpec from typing_extensions import ParamSpec
import anyio import anyio
from anyio import from_thread, to_thread
import click import click
from anyio import to_thread, from_thread
except ImportError as e: # pragma: no cover except ImportError as e: # pragma: no cover
raise ImportError("请使用 `pip install nonebot-bison[cli]` 安装所需依赖") from e raise ImportError("请使用 `pip install nonebot-bison[cli]` 安装所需依赖") from e
@ -127,18 +128,18 @@ async def subs_import(path: str, format: str):
import_file_path = Path(path) import_file_path = Path(path)
assert import_file_path.is_file(), "该路径不是文件!" assert import_file_path.is_file(), "该路径不是文件!"
with import_file_path.open("r", encoding="utf-8") as f: async with await open_file(import_file_path, "r", encoding="utf-8") as f:
match format: match format:
case "yaml" | "yml": case "yaml" | "yml":
logger.info("正在从yaml导入...") logger.info("正在从yaml导入...")
pyyaml = import_yaml_module() pyyaml = import_yaml_module()
import_items = pyyaml.safe_load(f) import_items = pyyaml.safe_load(await f.read())
case "json": case "json":
logger.info("正在从json导入...") logger.info("正在从json导入...")
import_items = json.load(f) import_items = json.loads(await f.read())
case _: case _:
raise click.BadParameter(message=f"不支持的导入格式: {format}") raise click.BadParameter(message=f"不支持的导入格式: {format}")

View File

@ -1,10 +1,10 @@
import asyncio import asyncio
from collections import deque from collections import deque
from nonebot.log import logger
from nonebot_plugin_saa.auto_select_bot import refresh_bots
from nonebot.adapters.onebot.v11.exception import ActionFailed from nonebot.adapters.onebot.v11.exception import ActionFailed
from nonebot_plugin_saa import MessageFactory, PlatformTarget, AggregatedMessageFactory from nonebot.log import logger
from nonebot_plugin_saa import AggregatedMessageFactory, MessageFactory, PlatformTarget
from nonebot_plugin_saa.auto_select_bot import refresh_bots
from .plugin_config import plugin_config from .plugin_config import plugin_config
@ -14,6 +14,8 @@ QUEUE: deque[tuple[PlatformTarget, Sendable, int]] = deque()
MESSGE_SEND_INTERVAL = 1.5 MESSGE_SEND_INTERVAL = 1.5
_MESSAGE_DISPATCH_TASKS: set[asyncio.Task] = set()
async def _do_send(send_target: PlatformTarget, msg: Sendable): async def _do_send(send_target: PlatformTarget, msg: Sendable):
try: try:
@ -59,7 +61,9 @@ async def _send_msgs_dispatch(send_target: PlatformTarget, msg: Sendable):
QUEUE.append((send_target, msg, plugin_config.bison_resend_times)) QUEUE.append((send_target, msg, plugin_config.bison_resend_times))
# len(QUEUE) before append was 0 # len(QUEUE) before append was 0
if len(QUEUE) == 1: if len(QUEUE) == 1:
asyncio.create_task(do_send_msgs()) task = asyncio.create_task(do_send_msgs())
_MESSAGE_DISPATCH_TASKS.add(task)
task.add_done_callback(_MESSAGE_DISPATCH_TASKS.discard)
else: else:
await _do_send(send_target, msg) await _do_send(send_target, msg)

View File

@ -2,23 +2,25 @@ import asyncio
from datetime import datetime from datetime import datetime
from nonebot import on_command from nonebot import on_command
from nonebot.typing import T_State
from nonebot.matcher import Matcher
from nonebot.rule import Rule, to_me
from nonebot.permission import SUPERUSER
from nonebot_plugin_saa import TargetQQGroup
from nonebot.params import ArgStr, ArgPlainText
from nonebot.adapters import Bot, MessageTemplate from nonebot.adapters import Bot, MessageTemplate
from nonebot.adapters.onebot.v11.event import PrivateMessageEvent from nonebot.adapters.onebot.v11.event import PrivateMessageEvent
from nonebot.matcher import Matcher
from nonebot.params import ArgPlainText, ArgStr
from nonebot.permission import SUPERUSER
from nonebot.rule import Rule, to_me
from nonebot.typing import T_State
from nonebot_plugin_saa import TargetQQGroup
from .add_cookie import do_add_cookie
from .add_cookie_target import do_add_cookie_target
from .add_sub import do_add_sub from .add_sub import do_add_sub
from .del_cookie import do_del_cookie
from .del_cookie_target import do_del_cookie_target
from .del_sub import do_del_sub from .del_sub import do_del_sub
from .query_sub import do_query_sub from .query_sub import do_query_sub
from .add_cookie import do_add_cookie from .utils import admin_permission, common_platform, configurable_to_me, gen_handle_cancel, set_target_user_info
from .del_cookie import do_del_cookie
from .add_cookie_target import do_add_cookie_target _COMMAND_DISPATCH_TASKS: set[asyncio.Task] = set()
from .del_cookie_target import do_del_cookie_target
from .utils import common_platform, admin_permission, gen_handle_cancel, configurable_to_me, set_target_user_info
add_sub_matcher = on_command( add_sub_matcher = on_command(
"添加订阅", "添加订阅",
@ -149,7 +151,10 @@ async def do_dispatch_command(
else: else:
do_del_sub(new_matcher) do_del_sub(new_matcher)
new_matcher_ins = new_matcher() new_matcher_ins = new_matcher()
asyncio.create_task(new_matcher_ins.run(bot, event, state))
task = asyncio.create_task(new_matcher_ins.run(bot, event, state))
_COMMAND_DISPATCH_TASKS.add(task)
task.add_done_callback(_COMMAND_DISPATCH_TASKS.discard)
no_permission_matcher = on_command( no_permission_matcher = on_command(
@ -167,14 +172,14 @@ async def send_no_permission():
__all__ = [ __all__ = [
"common_platform", "add_cookie_matcher",
"add_cookie_target_matcher",
"add_sub_matcher", "add_sub_matcher",
"query_sub_matcher", "common_platform",
"del_cookie_matcher",
"del_cookie_target_matcher",
"del_sub_matcher", "del_sub_matcher",
"group_manage_matcher", "group_manage_matcher",
"no_permission_matcher", "no_permission_matcher",
"add_cookie_matcher", "query_sub_matcher",
"add_cookie_target_matcher",
"del_cookie_target_matcher",
"del_cookie_matcher",
] ]

View File

@ -1,16 +1,17 @@
from typing import cast
from json import JSONDecodeError from json import JSONDecodeError
from typing import cast
from nonebot.adapters import Message, MessageTemplate
from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.log import logger from nonebot.log import logger
from nonebot.typing import T_State
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.params import Arg, ArgPlainText from nonebot.params import Arg, ArgPlainText
from nonebot.adapters.onebot.v11 import MessageEvent from nonebot.typing import T_State
from nonebot.adapters import Message, MessageTemplate
from nonebot_bison.platform import platform_manager
from nonebot_bison.scheduler import scheduler_dict
from nonebot_bison.utils.site import CookieClientManager, is_cookie_client_manager
from ..scheduler import scheduler_dict
from ..platform import platform_manager
from ..utils.site import CookieClientManager, is_cookie_client_manager
from .utils import common_platform, gen_handle_cancel, only_allow_private from .utils import common_platform, gen_handle_cancel, only_allow_private

View File

@ -1,14 +1,15 @@
from nonebot.typing import T_State
from nonebot.matcher import Matcher
from nonebot.params import ArgPlainText
from nonebot_plugin_saa import MessageFactory
from nonebot.adapters.onebot.v11 import MessageEvent from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.internal.adapter import MessageTemplate from nonebot.internal.adapter import MessageTemplate
from nonebot.matcher import Matcher
from nonebot.params import ArgPlainText
from nonebot.typing import T_State
from nonebot_plugin_saa import MessageFactory
from ..config import config from nonebot_bison.config import config
from ..utils import parse_text from nonebot_bison.platform import platform_manager
from ..platform import platform_manager from nonebot_bison.utils import parse_text
from .utils import gen_handle_cancel, only_allow_private, generate_sub_list_text
from .utils import gen_handle_cancel, generate_sub_list_text, only_allow_private
def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]): def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
@ -33,7 +34,6 @@ def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
@add_cookie_target_matcher.handle() @add_cookie_target_matcher.handle()
async def init_promote_cookie(state: T_State): async def init_promote_cookie(state: T_State):
# 获取 site 的所有用户 cookie再排除掉已经关联的 cookie剩下的就是可以关联的 cookie # 获取 site 的所有用户 cookie再排除掉已经关联的 cookie剩下的就是可以关联的 cookie
cookies = await config.get_cookie(site_name=state["site"].name, is_anonymous=False) cookies = await config.get_cookie(site_name=state["site"].name, is_anonymous=False)
associated_cookies = await config.get_cookie( associated_cookies = await config.get_cookie(

View File

@ -1,17 +1,18 @@
import contextlib import contextlib
from nonebot.typing import T_State from nonebot.adapters import Message, MessageTemplate
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.params import Arg, ArgPlainText from nonebot.params import Arg, ArgPlainText
from nonebot.adapters import Message, MessageTemplate from nonebot.typing import T_State
from nonebot_plugin_saa import Text, PlatformTarget, SupportedAdapters from nonebot_plugin_saa import PlatformTarget, SupportedAdapters, Text
from nonebot_bison.apis import check_sub_target
from nonebot_bison.config import config
from nonebot_bison.config.db_config import SubscribeDupException
from nonebot_bison.platform import Platform, platform_manager, unavailable_paltforms
from nonebot_bison.types import Target
from ..types import Target
from ..config import config
from ..apis import check_sub_target
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]):

View File

@ -1,11 +1,12 @@
from nonebot.typing import T_State from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.params import EventPlainText from nonebot.params import EventPlainText
from nonebot.typing import T_State
from nonebot_plugin_saa import MessageFactory from nonebot_plugin_saa import MessageFactory
from nonebot.adapters.onebot.v11 import MessageEvent
from ..config import config from nonebot_bison.config import config
from ..utils import parse_text from nonebot_bison.utils import parse_text
from .utils import gen_handle_cancel, only_allow_private from .utils import gen_handle_cancel, only_allow_private

View File

@ -1,11 +1,12 @@
from nonebot.typing import T_State from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.params import EventPlainText from nonebot.params import EventPlainText
from nonebot.typing import T_State
from nonebot_plugin_saa import MessageFactory from nonebot_plugin_saa import MessageFactory
from nonebot.adapters.onebot.v11 import MessageEvent
from ..config import config from nonebot_bison.config import config
from ..utils import parse_text from nonebot_bison.utils import parse_text
from .utils import gen_handle_cancel, only_allow_private from .utils import gen_handle_cancel, only_allow_private

View File

@ -1,12 +1,13 @@
from nonebot.typing import T_State
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.params import Arg, EventPlainText from nonebot.params import Arg, EventPlainText
from nonebot.typing import T_State
from nonebot_plugin_saa import MessageFactory, PlatformTarget from nonebot_plugin_saa import MessageFactory, PlatformTarget
from ..config import config from nonebot_bison.config import config
from ..types import Category from nonebot_bison.platform import platform_manager
from ..utils import parse_text from nonebot_bison.types import Category
from ..platform import platform_manager from nonebot_bison.utils import parse_text
from .utils import ensure_user_info, gen_handle_cancel from .utils import ensure_user_info, gen_handle_cancel

View File

@ -1,12 +1,13 @@
from nonebot.params import Arg
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.params import Arg
from nonebot_plugin_saa import MessageFactory, PlatformTarget from nonebot_plugin_saa import MessageFactory, PlatformTarget
from ..config import config from nonebot_bison.config import config
from ..types import Category from nonebot_bison.platform import platform_manager
from ..utils import parse_text from nonebot_bison.types import Category
from nonebot_bison.utils import parse_text
from .utils import ensure_user_info from .utils import ensure_user_info
from ..platform import platform_manager
def do_query_sub(query_sub: type[Matcher]): def do_query_sub(query_sub: type[Matcher]):

View File

@ -1,22 +1,22 @@
import contextlib import contextlib
from typing import Annotated
from itertools import groupby from itertools import groupby
from operator import attrgetter from operator import attrgetter
from typing import Annotated
from nonebot.rule import Rule
from nonebot.adapters import Event from nonebot.adapters import Event
from nonebot.typing import T_State
from nonebot.matcher import Matcher from nonebot.matcher import Matcher
from nonebot.params import Depends, EventPlainText, EventToMe
from nonebot.permission import SUPERUSER from nonebot.permission import SUPERUSER
from nonebot.params import Depends, EventToMe, EventPlainText from nonebot.rule import Rule
from nonebot.typing import T_State
from nonebot_plugin_saa import PlatformTarget, extract_target from nonebot_plugin_saa import PlatformTarget, extract_target
from ..config import config from nonebot_bison.config import config
from ..types import Category from nonebot_bison.platform import platform_manager
from ..types import Target as T_Target from nonebot_bison.plugin_config import plugin_config
from ..platform import platform_manager from nonebot_bison.types import Category
from ..plugin_config import plugin_config from nonebot_bison.types import Target as T_Target
from ..utils.site import is_cookie_client_manager from nonebot_bison.utils.site import is_cookie_client_manager
def _configurable_to_me(to_me: bool = EventToMe()): def _configurable_to_me(to_me: bool = EventToMe()):

View File

@ -1,10 +1,9 @@
from importlib import import_module
from pathlib import Path from pathlib import Path
from pkgutil import iter_modules from pkgutil import iter_modules
from importlib import import_module
from .types import Theme
from .registry import theme_manager from .registry import theme_manager
from .types import ThemeRegistrationError from .types import Theme, ThemeRegistrationError
from .types import ThemeRenderError as ThemeRenderError from .types import ThemeRenderError as ThemeRenderError
from .types import ThemeRenderUnsupportError as ThemeRenderUnsupportError from .types import ThemeRenderUnsupportError as ThemeRenderUnsupportError

View File

@ -1,11 +1,14 @@
from typing import ClassVar
from nonebot import logger from nonebot import logger
from ..plugin_config import plugin_config from nonebot_bison.plugin_config import plugin_config
from .types import Theme, ThemeRegistrationError from .types import Theme, ThemeRegistrationError
class ThemeManager: class ThemeManager:
__themes: dict[str, Theme] = {} __themes: ClassVar[dict[str, Theme]] = {}
def register(self, theme: Theme): def register(self, theme: Theme):
logger.trace(f"Registering theme: {theme}") logger.trace(f"Registering theme: {theme}")

View File

@ -1,13 +1,13 @@
from dataclasses import dataclass
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from dataclasses import dataclass
from typing import TYPE_CHECKING, Literal from typing import TYPE_CHECKING, Literal
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
from nonebot_bison.utils import text_fletten
from nonebot_bison.theme.utils import web_embed_image
from nonebot_bison.theme import Theme, ThemeRenderError, ThemeRenderUnsupportError from nonebot_bison.theme import Theme, ThemeRenderError, ThemeRenderUnsupportError
from nonebot_bison.theme.utils import web_embed_image
from nonebot_bison.utils import text_fletten
if TYPE_CHECKING: if TYPE_CHECKING:
from nonebot_bison.platform.arknights import ArknightsPost from nonebot_bison.platform.arknights import ArknightsPost

View File

@ -1,11 +1,12 @@
from collections.abc import Sequence
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Literal from typing import TYPE_CHECKING, Literal
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
from nonebot_bison.theme import Theme from nonebot_bison.theme import Theme
from nonebot_bison.utils import pic_merge, is_pics_mergable from nonebot_bison.utils import is_pics_mergable, pic_merge
if TYPE_CHECKING: if TYPE_CHECKING:
from nonebot_bison.post import Post from nonebot_bison.post import Post
@ -50,7 +51,7 @@ class BasicTheme(Theme):
client = await post.platform.ctx.get_client_for_static() client = await post.platform.ctx.get_client_for_static()
msgs: list[MessageSegmentFactory] = [Text(text)] msgs: list[MessageSegmentFactory] = [Text(text)]
pics_group: list[list[str | bytes | Path | BytesIO]] = [] pics_group: list[Sequence[str | bytes | Path | BytesIO]] = []
if post.images: if post.images:
pics_group.append(post.images) pics_group.append(post.images)
if rp and rp.images: if rp and rp.images:

View File

@ -1,9 +1,9 @@
from typing import TYPE_CHECKING, Literal from typing import TYPE_CHECKING, Literal
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
from nonebot_bison.utils import pic_merge, is_pics_mergable
from nonebot_bison.theme import Theme, ThemeRenderUnsupportError from nonebot_bison.theme import Theme, ThemeRenderUnsupportError
from nonebot_bison.utils import is_pics_mergable, pic_merge
if TYPE_CHECKING: if TYPE_CHECKING:
from nonebot_bison.post import Post from nonebot_bison.post import Post

View File

@ -1,19 +1,20 @@
from collections.abc import Sequence
from datetime import datetime
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from datetime import datetime
from typing import TYPE_CHECKING, Literal from typing import TYPE_CHECKING, Literal
import jinja2
from yarl import URL
from httpx import AsyncClient from httpx import AsyncClient
from pydantic import BaseModel import jinja2
from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
from PIL import Image as PILImage from PIL import Image as PILImage
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory from pydantic import BaseModel
from yarl import URL
from nonebot_bison.compat import model_validator from nonebot_bison.compat import model_validator
from nonebot_bison.utils import pic_merge, is_pics_mergable
from nonebot_bison.theme.utils import convert_to_qr, web_embed_image
from nonebot_bison.theme import Theme, ThemeRenderError, ThemeRenderUnsupportError from nonebot_bison.theme import Theme, ThemeRenderError, ThemeRenderUnsupportError
from nonebot_bison.theme.utils import convert_to_qr, web_embed_image
from nonebot_bison.utils import is_pics_mergable, pic_merge
if TYPE_CHECKING: if TYPE_CHECKING:
from nonebot_bison.post import Post from nonebot_bison.post import Post
@ -122,7 +123,7 @@ class CeobeCanteenTheme(Theme):
@staticmethod @staticmethod
async def merge_pics( async def merge_pics(
images: list[str | bytes | Path | BytesIO], images: Sequence[str | bytes | Path | BytesIO],
client: AsyncClient, client: AsyncClient,
) -> list[str | bytes | Path | BytesIO]: ) -> list[str | bytes | Path | BytesIO]:
if is_pics_mergable(images): if is_pics_mergable(images):
@ -224,7 +225,7 @@ class CeobeCanteenTheme(Theme):
text += f"详情: {post.url}" text += f"详情: {post.url}"
msgs.append(Text(text)) msgs.append(Text(text))
pics_group: list[list[str | bytes | Path | BytesIO]] = [] pics_group: list[Sequence[str | bytes | Path | BytesIO]] = []
if post.images: if post.images:
pics_group.append(post.images) pics_group.append(post.images)
if post.repost and post.repost.images: if post.repost and post.repost.images:

View File

@ -1,12 +1,13 @@
from collections.abc import Sequence
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Literal from typing import TYPE_CHECKING, Literal
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
from nonebot_bison.theme import Theme, ThemeRenderError
from nonebot_bison.post.protocol import HTMLContentSupport from nonebot_bison.post.protocol import HTMLContentSupport
from nonebot_bison.utils import pic_merge, is_pics_mergable from nonebot_bison.theme import Theme, ThemeRenderError
from nonebot_bison.utils import is_pics_mergable, pic_merge
if TYPE_CHECKING: if TYPE_CHECKING:
from nonebot_bison.post import Post from nonebot_bison.post import Post
@ -31,7 +32,6 @@ class Ht2iTheme(Theme):
raise ThemeRenderError(f"渲染文本失败: {e}") raise ThemeRenderError(f"渲染文本失败: {e}")
async def render(self, post: "Post"): async def render(self, post: "Post"):
md_text = "" md_text = ""
md_text += f"## {post.title}\n\n" if post.title else "" md_text += f"## {post.title}\n\n" if post.title else ""
@ -50,9 +50,7 @@ class Ht2iTheme(Theme):
else: else:
rp_content = await rp.get_content() rp_content = await rp.get_content()
md_text += ( md_text += "> \n> " + rp_content if len(rp_content) < 500 else f"{rp_content[:500]}..." + " \n"
"> \n> " + rp_content if len(rp_content) < 500 else f"{rp_content[:500]}..." + " \n" # noqa: E501
) # noqa: E501
md_text += "\n\n" md_text += "\n\n"
md_text += f"###### 来源: {post.platform.name} {post.nickname or ''}\n" md_text += f"###### 来源: {post.platform.name} {post.nickname or ''}\n"
@ -68,7 +66,7 @@ class Ht2iTheme(Theme):
if urls: if urls:
msgs.append(Text("\n".join(urls))) msgs.append(Text("\n".join(urls)))
pics_group: list[list[str | bytes | Path | BytesIO]] = [] pics_group: list[Sequence[str | bytes | Path | BytesIO]] = []
if post.images: if post.images:
pics_group.append(post.images) pics_group.append(post.images)
if rp and rp.images: if rp and rp.images:

View File

@ -1,14 +1,14 @@
from typing import TYPE_CHECKING
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
from nonebot import logger, require from nonebot import logger, require
from pydantic import BaseModel, PrivateAttr
from nonebot_plugin_saa import MessageSegmentFactory from nonebot_plugin_saa import MessageSegmentFactory
from pydantic import BaseModel, PrivateAttr
from ..plugin_config import plugin_config from nonebot_bison.plugin_config import plugin_config
if TYPE_CHECKING: if TYPE_CHECKING:
from ..post.abstract_post import AbstractPost from nonebot_bison.post.abstract_post import AbstractPost
class Theme(ABC, BaseModel): class Theme(ABC, BaseModel):

View File

@ -1,10 +1,10 @@
from base64 import b64encode
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from base64 import b64encode
from qrcode import constants from qrcode import constants
from qrcode.main import QRCode
from qrcode.image.pil import PilImage from qrcode.image.pil import PilImage
from qrcode.main import QRCode
def convert_to_qr(data: str, **kwarg) -> bytes: def convert_to_qr(data: str, **kwarg) -> bytes:

View File

@ -1,10 +1,10 @@
from datetime import time
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Literal, NewType, NamedTuple from datetime import time
from typing import Any, Literal, NamedTuple, NewType
from httpx import URL from httpx import URL
from pydantic import BaseModel
from nonebot_plugin_saa import PlatformTarget as SendTarget from nonebot_plugin_saa import PlatformTarget as SendTarget
from pydantic import BaseModel
RawPost = Any RawPost = Any
Target = NewType("Target", str) Target = NewType("Target", str)

View File

@ -1,29 +1,31 @@
import difflib
import re import re
import sys import sys
import difflib from typing import Any, ClassVar
import nonebot
from nonebot.plugin import require
from bs4 import BeautifulSoup as bs from bs4 import BeautifulSoup as bs
from nonebot.log import logger, default_format import nonebot
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory from nonebot.log import default_format, logger
from nonebot.plugin import require
from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
from .site import Site as Site from nonebot_bison.plugin_config import plugin_config
from ..plugin_config import plugin_config
from .image import pic_merge as pic_merge from .context import ProcessContext as ProcessContext
from .http import http_client as http_client from .http import http_client as http_client
from .image import capture_html as capture_html from .image import capture_html as capture_html
from .site import ClientManager as ClientManager
from .image import text_to_image as text_to_image
from .site import anonymous_site as anonymous_site
from .context import ProcessContext as ProcessContext
from .image import is_pics_mergable as is_pics_mergable from .image import is_pics_mergable as is_pics_mergable
from .image import pic_merge as pic_merge
from .image import pic_url_to_image as pic_url_to_image from .image import pic_url_to_image as pic_url_to_image
from .image import text_to_image as text_to_image
from .site import ClientManager as ClientManager
from .site import DefaultClientManager as DefaultClientManager from .site import DefaultClientManager as DefaultClientManager
from .site import Site as Site
from .site import anonymous_site as anonymous_site
class Singleton(type): class Singleton(type):
_instances = {} _instances: ClassVar[dict[Any, Any]] = {}
def __call__(cls, *args, **kwargs): def __call__(cls, *args, **kwargs):
if cls not in cls._instances: if cls not in cls._instances:

View File

@ -1,6 +1,6 @@
from base64 import b64encode from base64 import b64encode
from httpx import Response, AsyncClient from httpx import AsyncClient, Response
from nonebot_bison.types import Target from nonebot_bison.types import Target

View File

@ -1,12 +1,12 @@
"""提供获取 Bot 的方法""" """提供获取 Bot 的方法"""
from typing import Any
from collections import defaultdict from collections import defaultdict
from typing import Any
import nonebot import nonebot
from nonebot.adapters import Bot from nonebot.adapters import Bot
from nonebot_plugin_saa import PlatformTarget
from nonebot.adapters.onebot.v11 import Bot as Ob11Bot from nonebot.adapters.onebot.v11 import Bot as Ob11Bot
from nonebot_plugin_saa import PlatformTarget
GROUP: dict[int, list[Bot]] = {} GROUP: dict[int, list[Bot]] = {}
USER: dict[int, list[Bot]] = {} USER: dict[int, list[Bot]] = {}

View File

@ -1,6 +1,6 @@
import httpx import httpx
from ..plugin_config import plugin_config from nonebot_bison.plugin_config import plugin_config
http_args = { http_args = {
"proxies": plugin_config.bison_proxy or None, "proxies": plugin_config.bison_proxy or None,

View File

@ -1,16 +1,17 @@
from io import BytesIO from collections.abc import Sequence
from functools import partial from functools import partial
from io import BytesIO
from typing import Literal, TypeGuard from typing import Literal, TypeGuard
from yarl import URL
from PIL import Image
from httpx import AsyncClient from httpx import AsyncClient
from nonebot import logger, require from nonebot import logger, require
from PIL.Image import Image as PILImage
from nonebot_plugin_saa import Text as SaaText
from nonebot_plugin_saa import Image as SaaImage from nonebot_plugin_saa import Image as SaaImage
from nonebot_plugin_saa import Text as SaaText
from PIL import Image
from PIL.Image import Image as PILImage
from yarl import URL
from ..plugin_config import plugin_config from nonebot_bison.plugin_config import plugin_config
async def pic_url_to_image(data: str | bytes, http_client: AsyncClient) -> PILImage: async def pic_url_to_image(data: str | bytes, http_client: AsyncClient) -> PILImage:
@ -96,7 +97,7 @@ async def pic_merge(pics: list[str | bytes], http_client: AsyncClient) -> list[s
return pics return pics
def is_pics_mergable(imgs: list) -> TypeGuard[list[str | bytes]]: def is_pics_mergable(imgs: Sequence) -> TypeGuard[list[str | bytes]]:
if any(not isinstance(img, str | bytes) for img in imgs): if any(not isinstance(img, str | bytes) for img in imgs):
return False return False

View File

@ -1,18 +1,19 @@
import json
from typing import Literal
from json import JSONDecodeError
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections.abc import Callable from collections.abc import Callable
from datetime import datetime, timedelta from datetime import datetime, timedelta
import json
from json import JSONDecodeError
from typing import Literal
import httpx import httpx
from httpx import AsyncClient from httpx import AsyncClient
from nonebot.log import logger from nonebot.log import logger
from ..types import Target from nonebot_bison.config import config
from ..config import config from nonebot_bison.config.db_model import Cookie
from nonebot_bison.types import Target
from .http import http_client from .http import http_client
from ..config.db_model import Cookie
class ClientManager(ABC): class ClientManager(ABC):

144
poetry.lock generated
View File

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. # This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
[[package]] [[package]]
name = "aiodns" name = "aiodns"
@ -470,57 +470,6 @@ type = "legacy"
url = "https://pypi.org/simple" url = "https://pypi.org/simple"
reference = "offical-source" reference = "offical-source"
[[package]]
name = "black"
version = "24.8.0"
description = "The uncompromising code formatter."
optional = false
python-versions = ">=3.8"
files = [
{file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"},
{file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"},
{file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"},
{file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"},
{file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"},
{file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"},
{file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"},
{file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"},
{file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"},
{file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"},
{file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"},
{file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"},
{file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"},
{file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"},
{file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"},
{file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"},
{file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"},
{file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"},
{file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"},
{file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"},
{file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"},
{file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"},
]
[package.dependencies]
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
packaging = ">=22.0"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "offical-source"
[[package]] [[package]]
name = "brotli" name = "brotli"
version = "1.1.0" version = "1.1.0"
@ -1864,25 +1813,6 @@ type = "legacy"
url = "https://pypi.org/simple" url = "https://pypi.org/simple"
reference = "offical-source" reference = "offical-source"
[[package]]
name = "isort"
version = "5.13.2"
description = "A Python utility / library to sort Python imports."
optional = false
python-versions = ">=3.8.0"
files = [
{file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
{file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
]
[package.extras]
colors = ["colorama (>=0.4.6)"]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "offical-source"
[[package]] [[package]]
name = "jedi" name = "jedi"
version = "0.19.1" version = "0.19.1"
@ -2565,22 +2495,6 @@ type = "legacy"
url = "https://pypi.org/simple" url = "https://pypi.org/simple"
reference = "offical-source" reference = "offical-source"
[[package]]
name = "mypy-extensions"
version = "1.0.0"
description = "Type system extensions for programs checked with the mypy type checker."
optional = false
python-versions = ">=3.5"
files = [
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "offical-source"
[[package]] [[package]]
name = "nb-cli" name = "nb-cli"
version = "1.4.2" version = "1.4.2"
@ -2970,22 +2884,6 @@ type = "legacy"
url = "https://pypi.org/simple" url = "https://pypi.org/simple"
reference = "offical-source" reference = "offical-source"
[[package]]
name = "pathspec"
version = "0.12.1"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
python-versions = ">=3.8"
files = [
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
]
[package.source]
type = "legacy"
url = "https://pypi.org/simple"
reference = "offical-source"
[[package]] [[package]]
name = "pexpect" name = "pexpect"
version = "4.9.0" version = "4.9.0"
@ -4222,29 +4120,29 @@ reference = "offical-source"
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.6.5" version = "0.8.2"
description = "An extremely fast Python linter and code formatter, written in Rust." description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "ruff-0.6.5-py3-none-linux_armv6l.whl", hash = "sha256:7e4e308f16e07c95fc7753fc1aaac690a323b2bb9f4ec5e844a97bb7fbebd748"}, {file = "ruff-0.8.2-py3-none-linux_armv6l.whl", hash = "sha256:c49ab4da37e7c457105aadfd2725e24305ff9bc908487a9bf8d548c6dad8bb3d"},
{file = "ruff-0.6.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:932cd69eefe4daf8c7d92bd6689f7e8182571cb934ea720af218929da7bd7d69"}, {file = "ruff-0.8.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ec016beb69ac16be416c435828be702ee694c0d722505f9c1f35e1b9c0cc1bf5"},
{file = "ruff-0.6.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3a8d42d11fff8d3143ff4da41742a98f8f233bf8890e9fe23077826818f8d680"}, {file = "ruff-0.8.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f05cdf8d050b30e2ba55c9b09330b51f9f97d36d4673213679b965d25a785f3c"},
{file = "ruff-0.6.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a50af6e828ee692fb10ff2dfe53f05caecf077f4210fae9677e06a808275754f"}, {file = "ruff-0.8.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60f578c11feb1d3d257b2fb043ddb47501ab4816e7e221fbb0077f0d5d4e7b6f"},
{file = "ruff-0.6.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:794ada3400a0d0b89e3015f1a7e01f4c97320ac665b7bc3ade24b50b54cb2972"}, {file = "ruff-0.8.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbd5cf9b0ae8f30eebc7b360171bd50f59ab29d39f06a670b3e4501a36ba5897"},
{file = "ruff-0.6.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:381413ec47f71ce1d1c614f7779d88886f406f1fd53d289c77e4e533dc6ea200"}, {file = "ruff-0.8.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b402ddee3d777683de60ff76da801fa7e5e8a71038f57ee53e903afbcefdaa58"},
{file = "ruff-0.6.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:52e75a82bbc9b42e63c08d22ad0ac525117e72aee9729a069d7c4f235fc4d276"}, {file = "ruff-0.8.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:705832cd7d85605cb7858d8a13d75993c8f3ef1397b0831289109e953d833d29"},
{file = "ruff-0.6.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09c72a833fd3551135ceddcba5ebdb68ff89225d30758027280968c9acdc7810"}, {file = "ruff-0.8.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32096b41aaf7a5cc095fa45b4167b890e4c8d3fd217603f3634c92a541de7248"},
{file = "ruff-0.6.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:800c50371bdcb99b3c1551d5691e14d16d6f07063a518770254227f7f6e8c178"}, {file = "ruff-0.8.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e769083da9439508833cfc7c23e351e1809e67f47c50248250ce1ac52c21fb93"},
{file = "ruff-0.6.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e25ddd9cd63ba1f3bd51c1f09903904a6adf8429df34f17d728a8fa11174253"}, {file = "ruff-0.8.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fe716592ae8a376c2673fdfc1f5c0c193a6d0411f90a496863c99cd9e2ae25d"},
{file = "ruff-0.6.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7291e64d7129f24d1b0c947ec3ec4c0076e958d1475c61202497c6aced35dd19"}, {file = "ruff-0.8.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:81c148825277e737493242b44c5388a300584d73d5774defa9245aaef55448b0"},
{file = "ruff-0.6.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9ad7dfbd138d09d9a7e6931e6a7e797651ce29becd688be8a0d4d5f8177b4b0c"}, {file = "ruff-0.8.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d261d7850c8367704874847d95febc698a950bf061c9475d4a8b7689adc4f7fa"},
{file = "ruff-0.6.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:005256d977021790cc52aa23d78f06bb5090dc0bfbd42de46d49c201533982ae"}, {file = "ruff-0.8.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1ca4e3a87496dc07d2427b7dd7ffa88a1e597c28dad65ae6433ecb9f2e4f022f"},
{file = "ruff-0.6.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:482c1e6bfeb615eafc5899127b805d28e387bd87db38b2c0c41d271f5e58d8cc"}, {file = "ruff-0.8.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:729850feed82ef2440aa27946ab39c18cb4a8889c1128a6d589ffa028ddcfc22"},
{file = "ruff-0.6.5-py3-none-win32.whl", hash = "sha256:cf4d3fa53644137f6a4a27a2b397381d16454a1566ae5335855c187fbf67e4f5"}, {file = "ruff-0.8.2-py3-none-win32.whl", hash = "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1"},
{file = "ruff-0.6.5-py3-none-win_amd64.whl", hash = "sha256:3e42a57b58e3612051a636bc1ac4e6b838679530235520e8f095f7c44f706ff9"}, {file = "ruff-0.8.2-py3-none-win_amd64.whl", hash = "sha256:2aae99ec70abf43372612a838d97bfe77d45146254568d94926e8ed5bbb409ea"},
{file = "ruff-0.6.5-py3-none-win_arm64.whl", hash = "sha256:51935067740773afdf97493ba9b8231279e9beef0f2a8079188c4776c25688e0"}, {file = "ruff-0.8.2-py3-none-win_arm64.whl", hash = "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8"},
{file = "ruff-0.6.5.tar.gz", hash = "sha256:4d32d87fab433c0cf285c3683dd4dae63be05fd7a1d65b3f5bf7cdd05a6b96fb"}, {file = "ruff-0.8.2.tar.gz", hash = "sha256:b84f4f414dda8ac7f75075c1fa0b905ac0ff25361f42e6d5da681a465e0f78e5"},
] ]
[package.source] [package.source]
@ -5166,4 +5064,4 @@ yaml = []
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.10,<4.0.0" python-versions = ">=3.10,<4.0.0"
content-hash = "3d3bd947b91b8053fc5fed4873b6d0ed4017a5be118611cd93d30ffa265e04fb" content-hash = "c66f1511368c8de33a7706df2a20ef24d4191769fa29bf69bd2d5985381218d4"

View File

@ -44,13 +44,11 @@ expiringdictx = "^1.1.0"
rapidfuzz = "^3.9.7" rapidfuzz = "^3.9.7"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
black = ">=24.8.0,<25.0"
ipdb = "^0.13.13" ipdb = "^0.13.13"
isort = "^5.13.2"
nonemoji = "^0.1.4" nonemoji = "^0.1.4"
nb-cli = "^1.4.2" nb-cli = "^1.4.2"
pre-commit = "^4.0.1" pre-commit = "^4.0.1"
ruff = "^0.6.5" ruff = "^0.8.2"
[tool.poetry.group.test.dependencies] [tool.poetry.group.test.dependencies]
flaky = "^3.8.1" flaky = "^3.8.1"
@ -99,25 +97,44 @@ line-length = 120
target-version = "py310" target-version = "py310"
[tool.ruff.lint] [tool.ruff.lint]
select = ["E", "W", "F", "UP", "C", "T", "PYI", "PT", "Q"] select = [
ignore = ["E402", "C901", "PT023"] "F", # Pyflakes
"W", # pycodestyle warnings
"E", # pycodestyle errors
"I", # isort
"UP", # pyupgrade
"ASYNC", # flake8-async
"C4", # flake8-comprehensions
"T10", # flake8-debugger
"T20", # flake8-print
"PYI", # flake8-pyi
"PT", # flake8-pytest-style
"Q", # flake8-quotes
"TID", # flake8-tidy-imports
"RUF", # Ruff-specific rules
]
ignore = [
"E402", # module-import-not-at-top-of-file
"UP037", # quoted-annotation
"RUF001", # ambiguous-unicode-character-string
"RUF002", # ambiguous-unicode-character-docstring
"RUF003", # ambiguous-unicode-character-comment
]
[tool.black] [tool.ruff.format]
line-length = 120 line-ending = "lf"
preview = true
target-version = ["py310", "py311", "py312"]
include = '\.pyi?$'
extend-exclude = '''
'''
[tool.isort] [tool.ruff.lint.isort]
profile = "black" force-sort-within-sections = true
line_length = 120 known-first-party = ["nonebot_bison", "tests/*"]
skip_gitignore = true extra-standard-library = ["typing_extensions"]
length_sort = true
force_sort_within_sections = true [tool.ruff.lint.flake8-pytest-style]
src_paths = ["nonebot_bison", "tests"] fixture-parentheses = false
extra_standard_library = ["typing_extensions"] mark-parentheses = false
[tool.ruff.lint.pyupgrade]
keep-runtime-typing = true
[tool.nonebot] [tool.nonebot]
adapters = [ adapters = [
@ -130,6 +147,7 @@ builtin_plugins = ["echo"]
[tool.pyright] [tool.pyright]
typeCheckingMode = "basic" typeCheckingMode = "basic"
reportShadowedImports = false reportShadowedImports = false
disableBytesTypePromotions = true
pythonVersion = "3.10" pythonVersion = "3.10"
pythonPlatform = "All" pythonPlatform = "All"
executionEnvironments = [ executionEnvironments = [

View File

@ -1,7 +1,7 @@
import typing import typing
import pytest
from nonebug.app import App from nonebug.app import App
import pytest
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
import sys import sys

View File

@ -1,16 +1,16 @@
import pytest
from nonebug.app import App from nonebug.app import App
import pytest
async def test_add_subscribe(app: App, init_scheduler): async def test_add_subscribe(app: App, init_scheduler):
from nonebot_plugin_saa import TargetQQGroup
from sqlalchemy.sql.expression import select
from nonebot_plugin_datastore.db import get_engine from nonebot_plugin_datastore.db import get_engine
from nonebot_plugin_saa import TargetQQGroup
from sqlalchemy.ext.asyncio.session import AsyncSession from sqlalchemy.ext.asyncio.session import AsyncSession
from sqlalchemy.sql.expression import select
from nonebot_bison.config.db_config import config from nonebot_bison.config.db_config import config
from nonebot_bison.config.db_model import Subscribe, Target, User
from nonebot_bison.types import Target as TTarget from nonebot_bison.types import Target as TTarget
from nonebot_bison.config.db_model import User, Target, Subscribe
await config.add_subscribe( await config.add_subscribe(
TargetQQGroup(group_id=123), TargetQQGroup(group_id=123),
@ -69,8 +69,8 @@ async def test_add_subscribe(app: App, init_scheduler):
async def test_add_dup_sub(init_scheduler): async def test_add_dup_sub(init_scheduler):
from nonebot_plugin_saa import TargetQQGroup from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.types import Target as TTarget
from nonebot_bison.config.db_config import SubscribeDupException, config from nonebot_bison.config.db_config import SubscribeDupException, config
from nonebot_bison.types import Target as TTarget
await config.add_subscribe( await config.add_subscribe(
TargetQQGroup(group_id=123), TargetQQGroup(group_id=123),
@ -93,15 +93,15 @@ async def test_add_dup_sub(init_scheduler):
async def test_del_subsribe(init_scheduler): async def test_del_subsribe(init_scheduler):
from sqlalchemy.sql.functions import func
from nonebot_plugin_saa import TargetQQGroup
from sqlalchemy.sql.expression import select
from nonebot_plugin_datastore.db import get_engine from nonebot_plugin_datastore.db import get_engine
from nonebot_plugin_saa import TargetQQGroup
from sqlalchemy.ext.asyncio.session import AsyncSession from sqlalchemy.ext.asyncio.session import AsyncSession
from sqlalchemy.sql.expression import select
from sqlalchemy.sql.functions import func
from nonebot_bison.config.db_config import config from nonebot_bison.config.db_config import config
from nonebot_bison.config.db_model import Subscribe, Target
from nonebot_bison.types import Target as TTarget from nonebot_bison.types import Target as TTarget
from nonebot_bison.config.db_model import Target, Subscribe
await config.add_subscribe( await config.add_subscribe(
TargetQQGroup(group_id=123), TargetQQGroup(group_id=123),

View File

@ -1,9 +1,9 @@
from datetime import datetime
import json import json
from typing import cast from typing import cast
from datetime import datetime
import pytest
from nonebug import App from nonebug import App
import pytest
@pytest.mark.usefixtures("_patch_weibo_get_cookie_name") @pytest.mark.usefixtures("_patch_weibo_get_cookie_name")
@ -11,9 +11,9 @@ async def test_cookie(app: App, init_scheduler):
from nonebot_plugin_saa import TargetQQGroup from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.config.db_config import config from nonebot_bison.config.db_config import config
from nonebot_bison.config.utils import DuplicateCookieTargetException
from nonebot_bison.scheduler import scheduler_dict from nonebot_bison.scheduler import scheduler_dict
from nonebot_bison.types import Target as T_Target from nonebot_bison.types import Target as T_Target
from nonebot_bison.config.utils import DuplicateCookieTargetException
from nonebot_bison.utils.site import CookieClientManager, site_manager from nonebot_bison.utils.site import CookieClientManager, site_manager
target = T_Target("weibo_id") target = T_Target("weibo_id")

View File

@ -1,9 +1,9 @@
async def test_migration(use_legacy_config): async def test_migration(use_legacy_config):
from nonebot_plugin_saa import TargetQQGroup
from nonebot_plugin_datastore.db import init_db from nonebot_plugin_datastore.db import init_db
from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.config.db_config import config
from nonebot_bison.config.config_legacy import Config from nonebot_bison.config.config_legacy import Config
from nonebot_bison.config.db_config import config
config_legacy = Config() config_legacy = Config()
config_legacy.add_subscribe( config_legacy.add_subscribe(
@ -57,11 +57,11 @@ async def test_migration(use_legacy_config):
async def test_migrate_dup(use_legacy_config): async def test_migrate_dup(use_legacy_config):
from nonebot_plugin_saa import TargetQQGroup
from nonebot_plugin_datastore.db import init_db from nonebot_plugin_datastore.db import init_db
from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.config.db_config import config
from nonebot_bison.config.config_legacy import Config from nonebot_bison.config.config_legacy import Config
from nonebot_bison.config.db_config import config
config_legacy = Config() config_legacy = Config()
config_legacy.add_subscribe( config_legacy.add_subscribe(

View File

@ -7,8 +7,8 @@ from pytest_mock import MockerFixture
async def test_create_config(init_scheduler): async def test_create_config(init_scheduler):
from nonebot_plugin_saa import TargetQQGroup from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.config.db_config import TimeWeightConfig, WeightConfig, config
from nonebot_bison.types import Target as T_Target from nonebot_bison.types import Target as T_Target
from nonebot_bison.config.db_config import WeightConfig, TimeWeightConfig, config
await config.add_subscribe( await config.add_subscribe(
TargetQQGroup(group_id=123), TargetQQGroup(group_id=123),
@ -49,8 +49,8 @@ async def test_get_current_weight(init_scheduler, mocker: MockerFixture):
from nonebot_plugin_saa import TargetQQGroup from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.config import db_config from nonebot_bison.config import db_config
from nonebot_bison.config.db_config import TimeWeightConfig, WeightConfig, config
from nonebot_bison.types import Target as T_Target from nonebot_bison.types import Target as T_Target
from nonebot_bison.config.db_config import WeightConfig, TimeWeightConfig, config
await config.add_subscribe( await config.add_subscribe(
TargetQQGroup(group_id=123), TargetQQGroup(group_id=123),
@ -108,13 +108,13 @@ async def test_get_current_weight(init_scheduler, mocker: MockerFixture):
async def test_get_platform_target(app: App, init_scheduler): async def test_get_platform_target(app: App, init_scheduler):
from nonebot_plugin_saa import TargetQQGroup
from sqlalchemy.sql.expression import select
from nonebot_plugin_datastore.db import get_engine from nonebot_plugin_datastore.db import get_engine
from nonebot_plugin_saa import TargetQQGroup
from sqlalchemy.ext.asyncio.session import AsyncSession from sqlalchemy.ext.asyncio.session import AsyncSession
from sqlalchemy.sql.expression import select
from nonebot_bison.config.db_model import Target
from nonebot_bison.config.db_config import config from nonebot_bison.config.db_config import config
from nonebot_bison.config.db_model import Target
from nonebot_bison.types import Target as T_Target from nonebot_bison.types import Target as T_Target
await config.add_subscribe( await config.add_subscribe(
@ -158,9 +158,9 @@ async def test_get_platform_target(app: App, init_scheduler):
async def test_get_platform_target_subscribers(app: App, init_scheduler): async def test_get_platform_target_subscribers(app: App, init_scheduler):
from nonebot_plugin_saa import TargetQQGroup from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.types import UserSubInfo
from nonebot_bison.config.db_config import config from nonebot_bison.config.db_config import config
from nonebot_bison.types import Target as T_Target from nonebot_bison.types import Target as T_Target
from nonebot_bison.types import UserSubInfo
await config.add_subscribe( await config.add_subscribe(
TargetQQGroup(group_id=123), TargetQQGroup(group_id=123),

View File

@ -1,13 +1,13 @@
import sys
from pathlib import Path from pathlib import Path
from shutil import rmtree from shutil import rmtree
import sys
import pytest
import nonebot import nonebot
from sqlalchemy import delete
from nonebug import NONEBOT_INIT_KWARGS, App
from pytest_mock.plugin import MockerFixture
from nonebot.adapters.onebot.v11 import Adapter as OnebotV11Adapter from nonebot.adapters.onebot.v11 import Adapter as OnebotV11Adapter
from nonebug import NONEBOT_INIT_KWARGS, App
import pytest
from pytest_mock.plugin import MockerFixture
from sqlalchemy import delete
from .utils import AppReq from .utils import AppReq
@ -44,12 +44,12 @@ async def app(tmp_path: Path, request: pytest.FixtureRequest, mocker: MockerFixt
sys.path.append(str(Path(__file__).parent.parent / "src" / "plugins")) sys.path.append(str(Path(__file__).parent.parent / "src" / "plugins"))
nonebot.require("nonebot_bison") nonebot.require("nonebot_bison")
from nonebot_plugin_htmlrender.browser import shutdown_browser
from nonebot_plugin_datastore.db import init_db, create_session
from nonebot_plugin_datastore.config import plugin_config as datastore_config from nonebot_plugin_datastore.config import plugin_config as datastore_config
from nonebot_plugin_datastore.db import create_session, init_db
from nonebot_plugin_htmlrender.browser import shutdown_browser
from nonebot_bison import plugin_config from nonebot_bison import plugin_config
from nonebot_bison.config.db_model import User, Target, Subscribe, ScheduleTimeWeight from nonebot_bison.config.db_model import ScheduleTimeWeight, Subscribe, Target, User
plugin_config.bison_config_path = str(tmp_path / "legacy_config") plugin_config.bison_config_path = str(tmp_path / "legacy_config")
plugin_config.bison_filter_log = False plugin_config.bison_filter_log = False
@ -111,8 +111,8 @@ async def init_scheduler(app: App):
async def use_legacy_config(app: App): async def use_legacy_config(app: App):
import aiofiles import aiofiles
from nonebot_bison.utils import Singleton
from nonebot_bison.config.config_legacy import Config, get_config_path from nonebot_bison.config.config_legacy import Config, get_config_path
from nonebot_bison.utils import Singleton
# 默认不创建配置所在的文件夹 # 默认不创建配置所在的文件夹
# 如果需要测试需要手动创建相关文件夹 # 如果需要测试需要手动创建相关文件夹
@ -132,8 +132,8 @@ async def use_legacy_config(app: App):
@pytest.fixture @pytest.fixture
async def _no_browser(app: App, mocker: MockerFixture): async def _no_browser(app: App, mocker: MockerFixture):
from nonebot_bison.plugin_config import plugin_config
from nonebot_bison.platform import _get_unavailable_platforms from nonebot_bison.platform import _get_unavailable_platforms
from nonebot_bison.plugin_config import plugin_config
mocker.patch.object(plugin_config, "bison_use_browser", False) mocker.patch.object(plugin_config, "bison_use_browser", False)
mocker.patch("nonebot_bison.platform.unavailable_paltforms", _get_unavailable_platforms()) mocker.patch("nonebot_bison.platform.unavailable_paltforms", _get_unavailable_platforms())

View File

@ -1,10 +1,10 @@
from time import time from time import time
import respx
import pytest
from httpx import Response from httpx import Response
from nonebug.app import App
from nonebot.compat import model_dump, type_validate_python from nonebot.compat import model_dump, type_validate_python
from nonebug.app import App
import pytest
import respx
from .utils import get_file, get_json from .utils import get_file, get_json
@ -12,7 +12,7 @@ from .utils import get_file, get_json
@pytest.fixture @pytest.fixture
def arknights(app: App): def arknights(app: App):
from nonebot_bison.platform import platform_manager from nonebot_bison.platform import platform_manager
from nonebot_bison.utils import ProcessContext, DefaultClientManager from nonebot_bison.utils import DefaultClientManager, ProcessContext
return platform_manager["arknights"](ProcessContext(DefaultClientManager())) return platform_manager["arknights"](ProcessContext(DefaultClientManager()))
@ -44,8 +44,8 @@ def monster_siren_list_1():
@respx.mock @respx.mock
async def test_url_parse(app: App): async def test_url_parse(app: App):
from nonebot_bison.utils import ProcessContext, DefaultClientManager from nonebot_bison.platform.arknights import ArkBulletinResponse, Arknights, BulletinData, BulletinListItem
from nonebot_bison.platform.arknights import Arknights, BulletinData, BulletinListItem, ArkBulletinResponse from nonebot_bison.utils import DefaultClientManager, ProcessContext
cid_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/1") cid_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/1")
@ -110,10 +110,10 @@ async def test_url_parse(app: App):
assert p4.url == "http://www.baidu.com/" assert p4.url == "http://www.baidu.com/"
@pytest.mark.asyncio() @pytest.mark.asyncio
async def test_get_date_in_bulletin(app: App): async def test_get_date_in_bulletin(app: App):
from nonebot_bison.utils import ProcessContext, DefaultClientManager
from nonebot_bison.platform.arknights import Arknights, BulletinListItem from nonebot_bison.platform.arknights import Arknights, BulletinListItem
from nonebot_bison.utils import DefaultClientManager, ProcessContext
arknights = Arknights(ProcessContext(DefaultClientManager())) arknights = Arknights(ProcessContext(DefaultClientManager()))
assert ( assert (
@ -131,11 +131,11 @@ async def test_get_date_in_bulletin(app: App):
) )
@pytest.mark.asyncio() @pytest.mark.asyncio
@respx.mock @respx.mock
async def test_parse_with_breakline(app: App): async def test_parse_with_breakline(app: App):
from nonebot_bison.utils import ProcessContext, DefaultClientManager
from nonebot_bison.platform.arknights import Arknights, BulletinListItem from nonebot_bison.platform.arknights import Arknights, BulletinListItem
from nonebot_bison.utils import DefaultClientManager, ProcessContext
detail = get_json("arknights-detail-805") detail = get_json("arknights-detail-805")
detail["data"]["header"] = "" detail["data"]["header"] = ""
@ -158,7 +158,7 @@ async def test_parse_with_breakline(app: App):
assert post.title == "【公开招募】 - 标签刷新通知" assert post.title == "【公开招募】 - 标签刷新通知"
@pytest.mark.asyncio() @pytest.mark.asyncio
@respx.mock @respx.mock
async def test_fetch_new( async def test_fetch_new(
arknights, arknights,
@ -168,9 +168,9 @@ async def test_fetch_new(
monster_siren_list_0, monster_siren_list_0,
monster_siren_list_1, monster_siren_list_1,
): ):
from nonebot_bison.post import Post
from nonebot_bison.types import Target, SubUnit
from nonebot_bison.platform.arknights import ArknightsPost from nonebot_bison.platform.arknights import ArknightsPost
from nonebot_bison.post import Post
from nonebot_bison.types import SubUnit, Target
ak_list_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletinList?target=IOS") ak_list_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletinList?target=IOS")
detail_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/5716") detail_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/5716")
@ -229,7 +229,7 @@ async def test_fetch_new(
assert "brief" == post3.get_priority_themes()[0] assert "brief" == post3.get_priority_themes()[0]
@pytest.mark.render() @pytest.mark.render
@respx.mock @respx.mock
async def test_send_with_render( async def test_send_with_render(
arknights, arknights,
@ -239,8 +239,8 @@ async def test_send_with_render(
monster_siren_list_0, monster_siren_list_0,
monster_siren_list_1, monster_siren_list_1,
): ):
from nonebot_bison.types import Target, SubUnit
from nonebot_bison.platform.arknights import ArknightsPost from nonebot_bison.platform.arknights import ArknightsPost
from nonebot_bison.types import SubUnit, Target
ak_list_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletinList?target=IOS") ak_list_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletinList?target=IOS")
detail_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/8397") detail_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/8397")
@ -284,13 +284,13 @@ async def test_send_with_render(
assert r assert r
@pytest.mark.render() @pytest.mark.render
@respx.mock @respx.mock
async def test_parse_title( async def test_parse_title(
app: App, app: App,
): ):
from nonebot_bison.utils import ProcessContext, DefaultClientManager
from nonebot_bison.platform.arknights import Arknights, BulletinListItem from nonebot_bison.platform.arknights import Arknights, BulletinListItem
from nonebot_bison.utils import DefaultClientManager, ProcessContext
detail_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/8397") detail_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/8397")

View File

@ -1,16 +1,16 @@
from datetime import datetime
import random import random
from time import time from time import time
from datetime import datetime from typing import TYPE_CHECKING, Any, ClassVar
from typing import TYPE_CHECKING, Any
import respx
import pytest
from loguru import logger
from nonebug.app import App
from httpx import URL, Response
from freezegun import freeze_time from freezegun import freeze_time
from pytest_mock import MockerFixture from httpx import URL, Response
from loguru import logger
from nonebot.compat import model_dump, type_validate_python from nonebot.compat import model_dump, type_validate_python
from nonebug.app import App
import pytest
from pytest_mock import MockerFixture
import respx
from .utils import get_json from .utils import get_json
@ -28,9 +28,9 @@ if TYPE_CHECKING:
@pytest.fixture @pytest.fixture
def bilibili(app: App) -> "Bilibili": def bilibili(app: App) -> "Bilibili":
from nonebot_bison.utils import ProcessContext
from nonebot_bison.platform import platform_manager from nonebot_bison.platform import platform_manager
from nonebot_bison.platform.bilibili import BilibiliClientManager from nonebot_bison.platform.bilibili import BilibiliClientManager
from nonebot_bison.utils import ProcessContext
return platform_manager["bilibili"](ProcessContext(BilibiliClientManager())) # type: ignore return platform_manager["bilibili"](ProcessContext(BilibiliClientManager())) # type: ignore
@ -62,7 +62,7 @@ def without_dynamic(app: App):
async def test_reset_on_exception(app: App): async def test_reset_on_exception(app: App):
from strenum import StrEnum from strenum import StrEnum
from nonebot_bison.platform.bilibili.fsm import FSM, StateGraph, Transition, ActionReturn, reset_on_exception from nonebot_bison.platform.bilibili.fsm import FSM, ActionReturn, StateGraph, Transition, reset_on_exception
class State(StrEnum): class State(StrEnum):
A = "A" A = "A"
@ -146,12 +146,12 @@ async def test_reset_on_exception(app: App):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_retry_for_352(app: App, mocker: MockerFixture): async def test_retry_for_352(app: App, mocker: MockerFixture):
from nonebot_bison.post import Post
from nonebot_bison.types import Target, RawPost
from nonebot_bison.platform.platform import NewMessage
from nonebot_bison.platform.bilibili.platforms import ApiCode352Error from nonebot_bison.platform.bilibili.platforms import ApiCode352Error
from nonebot_bison.utils import ClientManager, ProcessContext, http_client
from nonebot_bison.platform.bilibili.retry import RetryAddon, RetryState, _retry_fsm, retry_for_352 from nonebot_bison.platform.bilibili.retry import RetryAddon, RetryState, _retry_fsm, retry_for_352
from nonebot_bison.platform.platform import NewMessage
from nonebot_bison.post import Post
from nonebot_bison.types import RawPost, Target
from nonebot_bison.utils import ClientManager, ProcessContext, http_client
mocker.patch.object(random, "random", return_value=0.0) # 稳定触发RAISE阶段的随缘刷新 mocker.patch.object(random, "random", return_value=0.0) # 稳定触发RAISE阶段的随缘刷新
@ -166,7 +166,7 @@ async def test_retry_for_352(app: App, mocker: MockerFixture):
is_common = True is_common = True
schedule_interval = 10 schedule_interval = 10
enable_tag = False enable_tag = False
categories = {} categories: ClassVar[dict] = {}
has_target = True has_target = True
raise352 = False raise352 = False
@ -422,7 +422,7 @@ async def test_dynamic_forword_deleted(bilibili: "Bilibili", bing_dy_list: list)
@pytest.mark.asyncio @pytest.mark.asyncio
@respx.mock @respx.mock
async def test_fetch_new_without_dynamic(bilibili, dummy_user_subinfo, without_dynamic): async def test_fetch_new_without_dynamic(bilibili, dummy_user_subinfo, without_dynamic):
from nonebot_bison.types import Target, SubUnit from nonebot_bison.types import SubUnit, Target
target = Target("161775300") target = Target("161775300")
post_router = respx.get( post_router = respx.get(
@ -439,8 +439,8 @@ async def test_fetch_new_without_dynamic(bilibili, dummy_user_subinfo, without_d
async def test_fetch_new(bilibili, dummy_user_subinfo): async def test_fetch_new(bilibili, dummy_user_subinfo):
from nonebot.compat import model_dump, type_validate_python from nonebot.compat import model_dump, type_validate_python
from nonebot_bison.types import Target, SubUnit
from nonebot_bison.platform.bilibili.models import PostAPI from nonebot_bison.platform.bilibili.models import PostAPI
from nonebot_bison.types import SubUnit, Target
target = Target("161775300") target = Target("161775300")
@ -483,8 +483,8 @@ async def test_fetch_new(bilibili, dummy_user_subinfo):
async def test_fetch_new_live_rcmd(bilibili: "Bilibili", dummy_user_subinfo): async def test_fetch_new_live_rcmd(bilibili: "Bilibili", dummy_user_subinfo):
from nonebot.compat import model_dump, type_validate_python from nonebot.compat import model_dump, type_validate_python
from nonebot_bison.types import Target, SubUnit
from nonebot_bison.platform.bilibili.models import PostAPI from nonebot_bison.platform.bilibili.models import PostAPI
from nonebot_bison.types import SubUnit, Target
target = Target("13164144") target = Target("13164144")

View File

@ -1,9 +1,9 @@
import typing import typing
import respx
import pytest
from httpx import Response from httpx import Response
from nonebug.app import App from nonebug.app import App
import pytest
import respx
from .utils import get_json from .utils import get_json
@ -14,7 +14,7 @@ if typing.TYPE_CHECKING:
@pytest.fixture @pytest.fixture
def bili_bangumi(app: App): def bili_bangumi(app: App):
from nonebot_bison.platform import platform_manager from nonebot_bison.platform import platform_manager
from nonebot_bison.utils import ProcessContext, DefaultClientManager from nonebot_bison.utils import DefaultClientManager, ProcessContext
return platform_manager["bilibili-bangumi"](ProcessContext(DefaultClientManager())) return platform_manager["bilibili-bangumi"](ProcessContext(DefaultClientManager()))
@ -38,7 +38,7 @@ async def test_parse_target(bili_bangumi: "BilibiliBangumi"):
@pytest.mark.asyncio @pytest.mark.asyncio
@respx.mock @respx.mock
async def test_fetch_bilibili_bangumi_status(bili_bangumi: "BilibiliBangumi", dummy_user_subinfo): async def test_fetch_bilibili_bangumi_status(bili_bangumi: "BilibiliBangumi", dummy_user_subinfo):
from nonebot_bison.types import Target, SubUnit from nonebot_bison.types import SubUnit, Target
bili_bangumi_router = respx.get("https://api.bilibili.com/pgc/review/user?media_id=28235413") bili_bangumi_router = respx.get("https://api.bilibili.com/pgc/review/user?media_id=28235413")
bili_bangumi_detail_router = respx.get("https://api.bilibili.com/pgc/view/web/season?season_id=39719") bili_bangumi_detail_router = respx.get("https://api.bilibili.com/pgc/view/web/season?season_id=39719")

View File

@ -1,10 +1,10 @@
from copy import deepcopy from copy import deepcopy
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import respx
import pytest
from httpx import Response from httpx import Response
from nonebug.app import App from nonebug.app import App
import pytest
import respx
from .utils import get_json from .utils import get_json
@ -15,7 +15,7 @@ if TYPE_CHECKING:
@pytest.fixture @pytest.fixture
def bili_live(app: App): def bili_live(app: App):
from nonebot_bison.platform import platform_manager from nonebot_bison.platform import platform_manager
from nonebot_bison.utils import ProcessContext, DefaultClientManager from nonebot_bison.utils import DefaultClientManager, ProcessContext
return platform_manager["bilibili-live"](ProcessContext(DefaultClientManager())) return platform_manager["bilibili-live"](ProcessContext(DefaultClientManager()))
@ -33,7 +33,7 @@ def dummy_only_open_user_subinfo(app: App):
@pytest.mark.asyncio @pytest.mark.asyncio
@respx.mock @respx.mock
async def test_fetch_bililive_no_room(bili_live, dummy_only_open_user_subinfo): async def test_fetch_bililive_no_room(bili_live, dummy_only_open_user_subinfo):
from nonebot_bison.types import Target, SubUnit from nonebot_bison.types import SubUnit, Target
mock_bili_live_status = get_json("bili_live_status.json") mock_bili_live_status = get_json("bili_live_status.json")
mock_bili_live_status["data"] = {} mock_bili_live_status["data"] = {}
@ -53,7 +53,7 @@ async def test_fetch_bililive_no_room(bili_live, dummy_only_open_user_subinfo):
@respx.mock @respx.mock
async def test_fetch_first_live(bili_live, dummy_only_open_user_subinfo): async def test_fetch_first_live(bili_live, dummy_only_open_user_subinfo):
from nonebot_bison.post import Post from nonebot_bison.post import Post
from nonebot_bison.types import Target, SubUnit from nonebot_bison.types import SubUnit, Target
mock_bili_live_status = get_json("bili_live_status.json") mock_bili_live_status = get_json("bili_live_status.json")
empty_bili_live_status = deepcopy(mock_bili_live_status) empty_bili_live_status = deepcopy(mock_bili_live_status)
@ -90,7 +90,7 @@ async def test_fetch_first_live(bili_live, dummy_only_open_user_subinfo):
@respx.mock @respx.mock
async def test_fetch_bililive_only_live_open(bili_live: "Bilibililive", dummy_only_open_user_subinfo): async def test_fetch_bililive_only_live_open(bili_live: "Bilibililive", dummy_only_open_user_subinfo):
from nonebot_bison.post import Post from nonebot_bison.post import Post
from nonebot_bison.types import Target, SubUnit from nonebot_bison.types import SubUnit, Target
mock_bili_live_status = get_json("bili_live_status.json") mock_bili_live_status = get_json("bili_live_status.json")
@ -141,11 +141,11 @@ def dummy_only_title_user_subinfo(app: App):
return UserSubInfo(user=user, categories=[2], tags=[]) return UserSubInfo(user=user, categories=[2], tags=[])
@pytest.mark.asyncio() @pytest.mark.asyncio
@respx.mock @respx.mock
async def test_fetch_bililive_only_title_change(bili_live, dummy_only_title_user_subinfo): async def test_fetch_bililive_only_title_change(bili_live, dummy_only_title_user_subinfo):
from nonebot_bison.post import Post from nonebot_bison.post import Post
from nonebot_bison.types import Target, SubUnit from nonebot_bison.types import SubUnit, Target
mock_bili_live_status = get_json("bili_live_status.json") mock_bili_live_status = get_json("bili_live_status.json")
target = Target("13164144") target = Target("13164144")
@ -204,7 +204,7 @@ def dummy_only_close_user_subinfo(app: App):
@respx.mock @respx.mock
async def test_fetch_bililive_only_close(bili_live, dummy_only_close_user_subinfo): async def test_fetch_bililive_only_close(bili_live, dummy_only_close_user_subinfo):
from nonebot_bison.post import Post from nonebot_bison.post import Post
from nonebot_bison.types import Target, SubUnit from nonebot_bison.types import SubUnit, Target
mock_bili_live_status = get_json("bili_live_status.json") mock_bili_live_status = get_json("bili_live_status.json")
target = Target("13164144") target = Target("13164144")
@ -264,7 +264,7 @@ def dummy_bililive_user_subinfo(app: App):
@respx.mock @respx.mock
async def test_fetch_bililive_combo(bili_live, dummy_bililive_user_subinfo): async def test_fetch_bililive_combo(bili_live, dummy_bililive_user_subinfo):
from nonebot_bison.post import Post from nonebot_bison.post import Post
from nonebot_bison.types import Target, SubUnit from nonebot_bison.types import SubUnit, Target
mock_bili_live_status = get_json("bili_live_status.json") mock_bili_live_status = get_json("bili_live_status.json")
target = Target("13164144") target = Target("13164144")

View File

@ -1,10 +1,10 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import respx
import pytest
from httpx import Response from httpx import Response
from nonebug.app import App
from nonebot.compat import type_validate_python from nonebot.compat import type_validate_python
from nonebug.app import App
import pytest
import respx
from .utils import get_json from .utils import get_json
@ -24,9 +24,9 @@ def dummy_only_open_user_subinfo(app: App):
@pytest.fixture @pytest.fixture
def ceobecanteen(app: App): def ceobecanteen(app: App):
from nonebot_bison.utils import ProcessContext
from nonebot_bison.platform import platform_manager from nonebot_bison.platform import platform_manager
from nonebot_bison.platform.ceobecanteen.platform import CeobeCanteenClientManager from nonebot_bison.platform.ceobecanteen.platform import CeobeCanteenClientManager
from nonebot_bison.utils import ProcessContext
return platform_manager["ceobecanteen"](ProcessContext(CeobeCanteenClientManager())) return platform_manager["ceobecanteen"](ProcessContext(CeobeCanteenClientManager()))
@ -71,7 +71,7 @@ def ceobecanteen_cookies_1() -> dict:
return get_json("ceobecanteen_cookies_1.json") return get_json("ceobecanteen_cookies_1.json")
@pytest.mark.asyncio() @pytest.mark.asyncio
async def test_parse_retweet(app: App): async def test_parse_retweet(app: App):
from nonebot_bison.platform.ceobecanteen.models import CookiesResponse from nonebot_bison.platform.ceobecanteen.models import CookiesResponse
@ -79,7 +79,7 @@ async def test_parse_retweet(app: App):
assert cookie_with_retweet.data.cookies[0].item.retweeted assert cookie_with_retweet.data.cookies[0].item.retweeted
@pytest.mark.render() @pytest.mark.render
async def test_ceobe_snapshot(app: App, ceobecanteen: "CeobeCanteen"): async def test_ceobe_snapshot(app: App, ceobecanteen: "CeobeCanteen"):
from nonebot_bison.platform.ceobecanteen.models import CeobeCookie from nonebot_bison.platform.ceobecanteen.models import CeobeCookie
@ -113,7 +113,7 @@ async def test_ceobe_snapshot(app: App, ceobecanteen: "CeobeCanteen"):
@pytest.mark.skip("极限测试, 不在CI中运行") @pytest.mark.skip("极限测试, 不在CI中运行")
@pytest.mark.asyncio() @pytest.mark.asyncio
async def test_parse_crazy(app: App, ceobecanteen): async def test_parse_crazy(app: App, ceobecanteen):
from nonebot_plugin_saa import Image from nonebot_plugin_saa import Image
@ -139,7 +139,7 @@ async def test_parse_crazy(app: App, ceobecanteen):
show(ext((await post4.generate_messages())[0][0])) # type: ignore show(ext((await post4.generate_messages())[0][0])) # type: ignore
@pytest.mark.asyncio() @pytest.mark.asyncio
@respx.mock @respx.mock
async def test_batch_fetch_new_with_single( async def test_batch_fetch_new_with_single(
app: App, app: App,
@ -220,7 +220,7 @@ async def test_batch_fetch_new_with_single(
await post3.generate_messages() await post3.generate_messages()
@pytest.mark.asyncio() @pytest.mark.asyncio
@respx.mock @respx.mock
async def test_parse_target_fuzzy(app: App, ceobecanteen: "CeobeCanteen", dummy_target, ceobecanteen_targets): async def test_parse_target_fuzzy(app: App, ceobecanteen: "CeobeCanteen", dummy_target, ceobecanteen_targets):
from nonebot_bison.platform.ceobecanteen import CeobeCanteen from nonebot_bison.platform.ceobecanteen import CeobeCanteen

View File

@ -1,7 +1,7 @@
import respx
import pytest
from httpx import Response from httpx import Response
from nonebug.app import App from nonebug.app import App
import pytest
import respx
from .utils import get_json from .utils import get_json
@ -9,7 +9,7 @@ from .utils import get_json
@pytest.fixture @pytest.fixture
def ff14(app: App): def ff14(app: App):
from nonebot_bison.platform import platform_manager from nonebot_bison.platform import platform_manager
from nonebot_bison.utils import ProcessContext, DefaultClientManager from nonebot_bison.utils import DefaultClientManager, ProcessContext
return platform_manager["ff14"](ProcessContext(DefaultClientManager())) return platform_manager["ff14"](ProcessContext(DefaultClientManager()))
@ -28,7 +28,7 @@ def ff14_newdata_json_1():
@respx.mock @respx.mock
async def test_fetch_new(ff14, dummy_user_subinfo, ff14_newdata_json_0, ff14_newdata_json_1): async def test_fetch_new(ff14, dummy_user_subinfo, ff14_newdata_json_0, ff14_newdata_json_1):
from nonebot_bison.post import Post from nonebot_bison.post import Post
from nonebot_bison.types import Target, SubUnit from nonebot_bison.types import SubUnit, Target
newdata = respx.get( newdata = respx.get(
"https://cqnews.web.sdo.com/api/news/newsList?gameCode=ff&CategoryCode=5309,5310,5311,5312,5313&pageIndex=0&pageSize=5" "https://cqnews.web.sdo.com/api/news/newsList?gameCode=ff&CategoryCode=5309,5310,5311,5312,5313&pageIndex=0&pageSize=5"

Some files were not shown because too many files have changed in this diff Show More