mirror of
https://github.com/suyiiyii/nonebot-bison.git
synced 2026-05-10 18:57:56 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
c84e3538bc
|
|||
|
202b8df0a6
|
|||
|
d75fe36c3b
|
|||
|
fdfad8a6c7
|
|||
|
2c605fc2dc
|
@@ -2,9 +2,9 @@
|
||||
"name": "Default Linux Universal",
|
||||
"image": "mcr.microsoft.com/devcontainers/universal:2-linux",
|
||||
"features": {
|
||||
"ghcr.io/va-h/devcontainers-features/uv:1": {}
|
||||
"ghcr.io/devcontainers-contrib/features/poetry:2": {}
|
||||
},
|
||||
"postCreateCommand": "uv venv && uv sync --all-groups --all-extras && uv run pre-commit install && npm install -g pnpm && pnpm install",
|
||||
"postCreateCommand": "poetry config virtualenvs.in-project true && poetry install -E all && poetry run pre-commit install && npm install -g pnpm && pnpm install",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
@@ -12,7 +12,7 @@
|
||||
"python.analysis.typeCheckingMode": "basic",
|
||||
"ruff.organizeImports": false,
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "charliermarsh.ruff",
|
||||
"editor.defaultFormatter": "ms-python.black-formatter",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.ruff": true,
|
||||
"source.organizeImports": true
|
||||
@@ -44,6 +44,8 @@
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"ms-python.isort",
|
||||
"ms-python.black-formatter",
|
||||
"charliermarsh.ruff",
|
||||
"EditorConfig.EditorConfig",
|
||||
"esbenp.prettier-vscode",
|
||||
|
||||
+6
-11
@@ -1,24 +1,19 @@
|
||||
*.ini
|
||||
*.yaml
|
||||
*.yml
|
||||
*.md
|
||||
*.json
|
||||
*.toml
|
||||
*.xml
|
||||
tests
|
||||
node_modules
|
||||
admin-frontend
|
||||
data*
|
||||
htmlcov
|
||||
docker
|
||||
dist
|
||||
docs
|
||||
venv
|
||||
.venv
|
||||
/admin-frontend/node_modules
|
||||
/LICENSE
|
||||
/.editorconfig
|
||||
/.gitattributes
|
||||
/.prettierignore
|
||||
/.prettierrc
|
||||
/.devcontainer/
|
||||
/.github/
|
||||
.*
|
||||
|
||||
# self install 时需要
|
||||
!pyproject.toml
|
||||
!README.md
|
||||
|
||||
@@ -27,7 +27,7 @@ body:
|
||||
description: 请选择安装方式
|
||||
options:
|
||||
- 通过 nb-cli 安装
|
||||
- 使用 uv/poetry/pdm 等现代包管理器安装
|
||||
- 使用 poetry/pdm 等现代包管理器安装
|
||||
- 通过 pip install 安装
|
||||
- 克隆或下载项目直接使用
|
||||
|
||||
|
||||
@@ -6,14 +6,6 @@ inputs:
|
||||
description: Python version
|
||||
required: false
|
||||
default: "3.10"
|
||||
install-deps:
|
||||
description: Install dependencies
|
||||
required: false
|
||||
default: "false"
|
||||
install-playwright:
|
||||
description: Install Playwright
|
||||
required: false
|
||||
default: "false"
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
@@ -22,19 +14,7 @@ runs:
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ inputs.python-version }}
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v4
|
||||
- name: Setup Poetry
|
||||
uses: Gr1N/setup-poetry@v9
|
||||
with:
|
||||
version: "latest"
|
||||
enable-cache: true
|
||||
|
||||
- name: Install dependencies
|
||||
if: ${{ inputs.install-deps == 'true' }}
|
||||
run: uv sync --frozen
|
||||
shell: bash
|
||||
|
||||
- name: Install Playwright
|
||||
if: ${{ inputs.install-playwright == 'true' }}
|
||||
run: uv run playwright install
|
||||
shell: bash
|
||||
poetry-version: "1.7.1"
|
||||
|
||||
+28
-14
@@ -11,7 +11,7 @@ on:
|
||||
- nonebot_bison/**
|
||||
- tests/**
|
||||
- pyproject.toml
|
||||
- uv.lock
|
||||
- poetry.lock
|
||||
- docker.env.prod
|
||||
- .github/**
|
||||
pull_request:
|
||||
@@ -21,7 +21,7 @@ on:
|
||||
- nonebot_bison/**
|
||||
- tests/**
|
||||
- pyproject.toml
|
||||
- uv.lock
|
||||
- poetry.lock
|
||||
- docker.env.prod
|
||||
- .github/**
|
||||
types:
|
||||
@@ -72,11 +72,12 @@ jobs:
|
||||
uses: ./.github/actions/setup-python
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
install-deps: "true"
|
||||
install-playwright: "true"
|
||||
|
||||
- name: Install prerequisites
|
||||
run: poetry install
|
||||
|
||||
- name: Run Pytest
|
||||
run: uv run pytest --cov-report xml --cov=./nonebot_bison -k 'not compare and not render' -n auto
|
||||
run: poetry run pytest --cov-report xml --cov=./nonebot_bison -k 'not compare and not render' -n auto
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: codecov/codecov-action@v5
|
||||
@@ -105,11 +106,12 @@ jobs:
|
||||
uses: ./.github/actions/setup-python
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
install-deps: "true"
|
||||
install-playwright: "true"
|
||||
|
||||
- name: Install prerequisites
|
||||
run: poetry install
|
||||
|
||||
- name: Run Pytest
|
||||
run: uv run pytest --cov-report xml --cov=./nonebot_bison -k 'not compare' -n auto
|
||||
run: poetry run pytest --cov-report xml --cov=./nonebot_bison -k 'not compare' -n auto
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: codecov/codecov-action@v5
|
||||
@@ -125,6 +127,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download frontend files
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend
|
||||
path: ./nonebot_bison/admin_page/dist
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
@@ -136,7 +144,7 @@ jobs:
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile
|
||||
file: ./docker/Dockerfile_with_frontend
|
||||
push: false
|
||||
load: true
|
||||
tags: felinae98/nonebot-bison:dummy
|
||||
@@ -166,20 +174,26 @@ jobs:
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile
|
||||
file: ./docker/Dockerfile_with_frontend
|
||||
push: true
|
||||
tags: felinae98/nonebot-bison:${{ env.GIT_BRANCH_NAME }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
docker-main-nobrowser:
|
||||
name: Docker main without browser
|
||||
docker-main-sentry:
|
||||
name: Docker main sentry
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-frontend, test]
|
||||
if: github.event_name != 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download frontend files
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend
|
||||
path: ./nonebot_bison/admin_page/dist
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
@@ -201,8 +215,8 @@ jobs:
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile_without_browser
|
||||
file: ./docker/Dockerfile_with_frontend_sentry
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: felinae98/nonebot-bison:${{ env.GIT_BRANCH_NAME }}-nobrowser
|
||||
tags: felinae98/nonebot-bison:${{ env.GIT_BRANCH_NAME }}-sentry
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
@@ -9,12 +9,35 @@ on:
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build-frontend:
|
||||
name: Build Frontend
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build Frontend
|
||||
uses: ./.github/actions/build-frontend
|
||||
|
||||
- name: Upload dist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frontend
|
||||
path: ./admin-frontend/build/
|
||||
|
||||
docker-main:
|
||||
name: Docker main
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-frontend]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download frontend files
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend
|
||||
path: ./src/plugins/nonebot_bison/admin_page/dist
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
@@ -31,18 +54,25 @@ jobs:
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile
|
||||
file: ./docker/Dockerfile_with_frontend
|
||||
push: true
|
||||
tags: felinae98/nonebot-bison:${{ inputs.dockerTag }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
docker-main-nobrowser:
|
||||
name: Docker main without browser
|
||||
docker-main-sentry:
|
||||
name: Docker main sentry
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-frontend]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download frontend files
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend
|
||||
path: ./src/plugins/nonebot_bison/admin_page/dist
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
@@ -59,7 +89,7 @@ jobs:
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile_without_browser
|
||||
tags: felinae98/nonebot-bison:${{ inputs.dockerTag }}-nobrowser
|
||||
file: ./docker/Dockerfile_with_frontend_sentry
|
||||
tags: felinae98/nonebot-bison:${{ inputs.dockerTag }}-sentry
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
- nonebot_bison/**
|
||||
- tests/**
|
||||
- pyproject.toml
|
||||
- uv.lock
|
||||
- poetry.lock
|
||||
- docker.env.prod
|
||||
- .github/**
|
||||
pull_request:
|
||||
@@ -21,7 +21,7 @@ on:
|
||||
- nonebot_bison/**
|
||||
- tests/**
|
||||
- pyproject.toml
|
||||
- uv.lock
|
||||
- poetry.lock
|
||||
- docker.env.prod
|
||||
- .github/**
|
||||
types:
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install prerequisites
|
||||
run: uv add pydantic==1.10 && uv sync --frozen && uv run playwright install
|
||||
run: poetry add pydantic@^1.10 && poetry install
|
||||
|
||||
- name: Run Pytest
|
||||
run: uv run pytest -k 'not compare and not render' -n auto
|
||||
run: poetry run pytest -k 'not compare and not render' -n auto
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Setup Python environment
|
||||
uses: ./.github/actions/setup-python
|
||||
|
||||
- run: echo "TAG_NAME=v$(uvx --from=toml-cli toml get --toml-path=pyproject.toml project.version)" >> $GITHUB_ENV
|
||||
- run: echo "TAG_NAME=v$(poetry version -s)" >> $GITHUB_ENV
|
||||
|
||||
- name: Archive Changelog
|
||||
uses: docker://ghcr.io/nonebot/auto-changelog:master
|
||||
@@ -30,6 +30,6 @@ jobs:
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email github-actions[bot]@users.noreply.github.com
|
||||
git add .
|
||||
git commit -m ":bookmark: Release ${{ env.TAG_NAME }}"
|
||||
git commit -m ":bookmark: Release $(poetry version -s)"
|
||||
git tag ${{ env.TAG_NAME }}
|
||||
git push && git push --tags
|
||||
|
||||
@@ -54,17 +54,24 @@ jobs:
|
||||
|
||||
- name: Publish PyPI and Github
|
||||
run: |
|
||||
uv publish --build -u ${{ secrets.PYPI_USERNAME }} -p ${{ secrets.PYPI_PASSWORD }} || echo "Already pushed to pypi"
|
||||
poetry publish --build -u ${{ secrets.PYPI_USERNAME }} -p ${{ secrets.PYPI_PASSWORD }} || echo "Already pushed to pypi"
|
||||
gh release upload --clobber ${{ env.TAG_NAME }} dist/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
publish-docker:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-frontend
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download frontend files
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend
|
||||
path: ./nonebot_bison/admin_page/dist
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
@@ -85,7 +92,7 @@ jobs:
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile
|
||||
file: ./docker/Dockerfile_with_frontend
|
||||
push: true
|
||||
tags: |
|
||||
felinae98/nonebot-bison:latest
|
||||
@@ -97,10 +104,10 @@ jobs:
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile_without_browser
|
||||
file: ./docker/Dockerfile_with_frontend_sentry
|
||||
push: true
|
||||
tags: |
|
||||
felinae98/nonebot-bison:${{ env.TAG_NAME }}-nobrowser
|
||||
felinae98/nonebot-bison:nobrowser
|
||||
felinae98/nonebot-bison:${{ env.TAG_NAME }}-sentry
|
||||
felinae98/nonebot-bison:sentry
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
@@ -19,9 +19,4 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run Ruff Lint
|
||||
uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
src: >-
|
||||
nonebot_bison/
|
||||
extra_plugins/
|
||||
tests/
|
||||
uses: chartboost/ruff-action@v1
|
||||
|
||||
+14
-4
@@ -7,12 +7,22 @@ ci:
|
||||
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.8.2
|
||||
rev: v0.7.2
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix]
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
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]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
@@ -24,7 +34,7 @@ repos:
|
||||
stages: [pre-commit]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-eslint
|
||||
rev: v9.16.0
|
||||
rev: v9.14.0
|
||||
hooks:
|
||||
- id: eslint
|
||||
additional_dependencies:
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
|
||||
### Bug 修复
|
||||
|
||||
- :bug: 修复新版 httpx 中 proxy 不起效的问题 [@suyiiyii](https://github.com/suyiiyii) ([#669](https://github.com/MountainDash/nonebot-bison/pull/669))
|
||||
- :bug: 修复 cookie 模块 type hint [@suyiiyii](https://github.com/suyiiyii) ([#658](https://github.com/MountainDash/nonebot-bison/pull/658))
|
||||
- :bug: B站转发动态补充 DeletedItem 类型解析 [@AzideCupric](https://github.com/AzideCupric) ([#659](https://github.com/MountainDash/nonebot-bison/pull/659))
|
||||
- :bug: 小刻食堂cdn使用https [@phidiaLam](https://github.com/phidiaLam) ([#650](https://github.com/MountainDash/nonebot-bison/pull/650))
|
||||
|
||||
## v0.9.5
|
||||
|
||||
@@ -17,6 +17,9 @@ _✨ 通用订阅推送插件 ✨_
|
||||
<img src="https://img.shields.io/pypi/v/nonebot-bison?logo=python&logoColor=edb641" alt="pypi">
|
||||
</a>
|
||||
<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">
|
||||
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json" alt="ruff">
|
||||
</a>
|
||||
|
||||
+14
-205
@@ -1,207 +1,16 @@
|
||||
# syntax=docker/dockerfile:1.10
|
||||
FROM python:3.12-slim AS metadata-stage
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
TZ=Asia/Shanghai
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
python3-pip \
|
||||
git \
|
||||
&& pip install toml \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY ./pyproject.toml ./
|
||||
|
||||
RUN echo "📋 Collecting metadata..." && \
|
||||
export CREATED_DATE="$(date -u '+%Y-%m-%d %H:%M:%S UTC')" && \
|
||||
export PYPROJECT_NAME=$(python3 -c 'import toml; print(toml.load("pyproject.toml")["project"]["name"])') && \
|
||||
export DESCRIPTION=$(python3 -c 'import toml; print(toml.load("pyproject.toml")["project"]["description"])') && \
|
||||
export AUTHOR=$(python3 -c 'import toml; print(toml.load("pyproject.toml")["project"]["authors"][0]["name"])') && \
|
||||
echo "⏰ Created Date: $CREATED_DATE" && \
|
||||
echo "📝 Project Name: $PYPROJECT_NAME" && \
|
||||
echo "📄 Description: $DESCRIPTION" && \
|
||||
echo "👤 Author: $AUTHOR" && \
|
||||
echo "$CREATED_DATE" > /tmp/CREATED_DATE && \
|
||||
echo "$PYPROJECT_NAME" > /tmp/PYPROJECT_NAME && \
|
||||
echo "$DESCRIPTION" > /tmp/DESCRIPTION && \
|
||||
echo "$AUTHOR" > /tmp/AUTHOR
|
||||
|
||||
|
||||
RUN --mount=type=bind,source=./.git/,target=/tmp/.git/ \
|
||||
echo "🏷️ Checking version information..." && \
|
||||
VERSION=$(git describe --tags --exact-match 2>/dev/null || git rev-parse --short HEAD || echo "unknown") && \
|
||||
echo "📌 Version: $VERSION" && \
|
||||
echo "$VERSION" > /tmp/VERSION
|
||||
|
||||
LABEL org.opencontainers.image.created="$(cat /tmp/CREATED_DATE)" \
|
||||
org.opencontainers.image.authors="$(cat /tmp/AUTHOR)" \
|
||||
org.opencontainers.image.description="$(cat /tmp/DESCRIPTION)" \
|
||||
org.opencontainers.image.name="$(cat /tmp/PYPROJECT_NAME)" \
|
||||
org.opencontainers.image.version="$(cat /tmp/VERSION)"
|
||||
|
||||
FROM node:20-slim AS frontend-stage
|
||||
|
||||
WORKDIR /tmp/admin-frontend
|
||||
|
||||
ENV TZ=Asia/Shanghai \
|
||||
PNPM_HOME="/pnpm" \
|
||||
PATH="$PNPM_HOME:$PATH"
|
||||
|
||||
COPY ./admin-frontend/package.json ./admin-frontend/pnpm-lock.yaml ./
|
||||
|
||||
RUN corepack enable && \
|
||||
corepack prepare pnpm@latest --activate && \
|
||||
echo "🔔 Verifying Node.js installation..." && \
|
||||
if NODE_VERSION=$(node --version 2>&1); then \
|
||||
echo "✅ Node.js version ${NODE_VERSION} installed successfully"; \
|
||||
else \
|
||||
echo "❌ Node.js installation failed" && exit 1; \
|
||||
fi && \
|
||||
echo "🔔 Verifying pnpm installation..." && \
|
||||
if PNPM_VERSION=$(pnpm --version 2>&1); then \
|
||||
echo "✅ pnpm version ${PNPM_VERSION} installed successfully"; \
|
||||
else \
|
||||
echo "❌ pnpm installation failed" && exit 1; \
|
||||
fi
|
||||
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
COPY ./admin-frontend .
|
||||
RUN mkdir -p ../nonebot_bison/admin_page/dist
|
||||
|
||||
RUN echo "🏗️ Starting frontend build..." && \
|
||||
pnpm run build && \
|
||||
echo "✅ Frontend build completed"
|
||||
|
||||
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS playwright-stage
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
TZ=Asia/Shanghai
|
||||
|
||||
RUN echo "📦 Installing playwright..." && \
|
||||
uvx playwright install --with-deps chromium && \
|
||||
echo "✅ Playwright installed successfully"
|
||||
|
||||
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS uv-stage
|
||||
FROM node:22.11.0 as frontend
|
||||
ADD . /app
|
||||
WORKDIR /app/admin-frontend
|
||||
RUN npm install -g pnpm
|
||||
RUN pnpm install && pnpm build
|
||||
|
||||
FROM python:3.11
|
||||
RUN python3 -m pip install poetry && poetry config virtualenvs.create false
|
||||
WORKDIR /app
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
UV_COMPILE_BYTECODE=1 \
|
||||
UV_FROZEN=1 \
|
||||
UV_LINK_MODE=copy \
|
||||
TZ=Asia/Shanghai
|
||||
|
||||
RUN echo "🔔 Verifying uv installation..." && \
|
||||
if UV_VERSION=$(uv --version 2>&1); then \
|
||||
echo "✅ uv version ${UV_VERSION} installed successfully"; \
|
||||
else \
|
||||
echo "❌ uv installation failed" && exit 1; \
|
||||
fi
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||
echo "📦 Installing project dependencies..." && \
|
||||
uv sync --no-install-project --no-dev --extra docker && \
|
||||
echo "✅ Dependencies installed successfully"
|
||||
|
||||
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
UV_COMPILE_BYTECODE=1 \
|
||||
UV_FROZEN=1 \
|
||||
UV_LINK_MODE=copy \
|
||||
TZ=Asia/Shanghai \
|
||||
SHELL="/bin/bash"
|
||||
|
||||
RUN echo "📦 Installing system dependencies..." && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
xvfb \
|
||||
fonts-noto-color-emoji \
|
||||
fonts-unifont \
|
||||
libfontconfig1 \
|
||||
libfreetype6 \
|
||||
xfonts-scalable \
|
||||
fonts-liberation \
|
||||
fonts-ipafont-gothic \
|
||||
fonts-wqy-zenhei \
|
||||
fonts-tlwg-loma-otf \
|
||||
at-spi2-common \
|
||||
fonts-freefont-ttf \
|
||||
libasound2 \
|
||||
libasound2-data \
|
||||
libatk-bridge2.0-0 \
|
||||
libatk1.0-0 \
|
||||
libatspi2.0-0 \
|
||||
libavahi-client3 \
|
||||
libavahi-common-data \
|
||||
libavahi-common3 \
|
||||
libcairo2 \
|
||||
libcups2 \
|
||||
libdatrie1 \
|
||||
libdbus-1-3 \
|
||||
libfribidi0 \
|
||||
libgbm1 \
|
||||
libglib2.0-0 \
|
||||
libgraphite2-3 \
|
||||
libharfbuzz0b \
|
||||
libnspr4 \
|
||||
libnss3 \
|
||||
libpango-1.0-0 \
|
||||
libthai-data \
|
||||
libthai0 \
|
||||
libwayland-server0 \
|
||||
libxcb-render0 \
|
||||
libxcomposite1 \
|
||||
libxdamage1 \
|
||||
libxi6 \
|
||||
libxkbcommon0 && \
|
||||
fc-cache -fv && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
echo "✅ System dependencies installed successfully"
|
||||
|
||||
# 少变动的放上面,频繁变动的放下面
|
||||
COPY ./docker.env.prod ./.env
|
||||
COPY --from=playwright-stage /root/.cache/ms-playwright /root/.cache/ms-playwright
|
||||
COPY extra_plugins/ /app/extra_plugins/
|
||||
COPY --from=frontend-stage /tmp/nonebot_bison/admin_page/dist ./nonebot_bison/admin_page/dist
|
||||
COPY --from=uv-stage /app /app
|
||||
COPY ./pyproject.toml ./uv.lock ./README.md ./
|
||||
COPY nonebot_bison/ /app/nonebot_bison/
|
||||
COPY --from=metadata-stage /tmp/VERSION /app/VERSION
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
echo "📦 Installing bison project..." && \
|
||||
uv sync --no-dev --extra docker && \
|
||||
uv run nb adapter install nonebot-adapter-satori && \
|
||||
uv run nb adapter install nonebot-adapter-qq && \
|
||||
uv run nb plugin install nonebot-plugin-sentry && \
|
||||
echo "✅ Bison installed successfully"
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
RUN echo '#!/bin/bash' > /app/start.sh && \
|
||||
echo 'echo "📌 Current bison Version: $(cat /app/VERSION)"' >> /app/start.sh && \
|
||||
echo 'echo "🚀 Starting service..."' >> /app/start.sh && \
|
||||
echo 'source /app/.venv/bin/activate' >> /app/start.sh && \
|
||||
echo 'nb run' >> /app/start.sh && \
|
||||
chmod +x /app/start.sh
|
||||
|
||||
CMD ["/app/start.sh"]
|
||||
COPY ./README.md ./pyproject.toml ./poetry.lock* /app/
|
||||
RUN poetry install --only=main,docker
|
||||
ADD src /app/src
|
||||
ADD bot.py /app/
|
||||
COPY --from=frontend /app/nonebot_bison/admin_page/dist /app/nonebot_bison/admin_page/dist
|
||||
ENV HOST=0.0.0.0
|
||||
CMD ["nb", "run"]
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM python:3.11
|
||||
RUN apt-get update && apt-get install -y fonts-wqy-microhei chromium nano
|
||||
RUN python3 -m pip config set global.index-url https://mirrors.aliyun.com/pypi/simple
|
||||
RUN python3 -m pip install poetry && poetry config virtualenvs.create false
|
||||
WORKDIR /app
|
||||
COPY ./README.md ./pyproject.toml ./poetry.lock* /app/
|
||||
RUN poetry install --only=main,docker
|
||||
ENV BISON_BROWSER=local:/usr/bin/chromium
|
||||
ADD src /app/src
|
||||
ENV HOST=0.0.0.0
|
||||
@@ -0,0 +1,9 @@
|
||||
FROM python:3.11
|
||||
RUN python3 -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
RUN python3 -m pip install poetry && poetry config virtualenvs.create false
|
||||
WORKDIR /app
|
||||
COPY ./README.md ./pyproject.toml ./poetry.lock* /app/
|
||||
RUN poetry install --only=main,docker
|
||||
ADD src /app/src
|
||||
ENV HOST=0.0.0.0
|
||||
CMD ["nb", "run"]
|
||||
@@ -0,0 +1,50 @@
|
||||
# syntax=docker/dockerfile:1.10
|
||||
FROM python:3.11-slim-bullseye as base
|
||||
|
||||
FROM base as builder
|
||||
|
||||
ENV PYTHONFAULTHANDLER=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONHASHSEED=random \
|
||||
PIP_NO_CACHE_DIR=off \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
||||
PIP_DEFAULT_TIMEOUT=100 \
|
||||
POETRY_NO_INTERACTION=1 \
|
||||
POETRY_VIRTUALENVS_CREATE=false \
|
||||
PATH="$PATH:/runtime/bin" \
|
||||
PYTHONPATH="$PYTHONPATH:/runtime/lib/python3.10/site-packages" \
|
||||
# Versions:
|
||||
POETRY_VERSION=1.7.1
|
||||
RUN apt-get update && apt-get install -y build-essential unzip wget python-dev git
|
||||
RUN pip install "poetry==$POETRY_VERSION"
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY README.md pyproject.toml poetry.lock /src/
|
||||
|
||||
RUN poetry export --only=main,docker --without-hashes --no-interaction --no-ansi -f requirements.txt -o requirements.txt
|
||||
RUN pip install --prefix=/runtime --force-reinstall -r requirements.txt
|
||||
|
||||
FROM base as runtime
|
||||
|
||||
WORKDIR /app
|
||||
RUN --mount=type=cache,target=/var/cache/apt \
|
||||
--mount=type=cache,target=/var/lib/apt \
|
||||
apt-get update && apt-get install -y xvfb fonts-noto-color-emoji ttf-unifont \
|
||||
libfontconfig1 libfreetype6 xfonts-cyrillic xfonts-scalable fonts-liberation \
|
||||
fonts-ipafont-gothic fonts-wqy-zenhei fonts-tlwg-loma-otf \
|
||||
fonts-liberation libasound2 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 \
|
||||
libcairo2 libcups2 libdbus-1-3 libdrm2 libegl1 libgbm1 libglib2.0-0 libgtk-3-0 \
|
||||
libnspr4 libnss3 libpango-1.0-0 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \
|
||||
libxdamage1 libxext6 libxfixes3 libxrandr2 libxshmfence1 nano
|
||||
|
||||
COPY --from=builder /runtime /usr/local
|
||||
ADD . /app/
|
||||
RUN pip install -e . && playwright install chromium
|
||||
RUN mv docker.env.prod .env.prod && \
|
||||
nb adapter install nonebot-adapter-red && \
|
||||
nb adapter install nonebot-adapter-qq
|
||||
ENV HOST=0.0.0.0
|
||||
CMD ["nb", "run"]
|
||||
|
||||
# vim: ft=dockerfile
|
||||
@@ -0,0 +1,51 @@
|
||||
# syntax=docker/dockerfile:1.10
|
||||
FROM python:3.11-slim-bullseye as base
|
||||
|
||||
FROM base as builder
|
||||
|
||||
ENV PYTHONFAULTHANDLER=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PYTHONHASHSEED=random \
|
||||
PIP_NO_CACHE_DIR=off \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
||||
PIP_DEFAULT_TIMEOUT=100 \
|
||||
POETRY_NO_INTERACTION=1 \
|
||||
POETRY_VIRTUALENVS_CREATE=false \
|
||||
PATH="$PATH:/runtime/bin" \
|
||||
PYTHONPATH="$PYTHONPATH:/runtime/lib/python3.10/site-packages" \
|
||||
# Versions:
|
||||
POETRY_VERSION=1.7.1
|
||||
RUN apt-get update && apt-get install -y build-essential unzip wget python3-dev git
|
||||
RUN pip install "poetry==$POETRY_VERSION"
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY pyproject.toml poetry.lock /src/
|
||||
|
||||
RUN poetry export --only=main,docker --without-hashes --no-interaction --no-ansi -f requirements.txt -o requirements.txt
|
||||
RUN pip install --prefix=/runtime --force-reinstall -r requirements.txt
|
||||
|
||||
FROM base as runtime
|
||||
|
||||
WORKDIR /app
|
||||
RUN --mount=type=cache,target=/var/cache/apt \
|
||||
--mount=type=cache,target=/var/lib/apt \
|
||||
apt-get update && apt-get install -y xvfb fonts-noto-color-emoji ttf-unifont \
|
||||
libfontconfig1 libfreetype6 xfonts-cyrillic xfonts-scalable fonts-liberation \
|
||||
fonts-ipafont-gothic fonts-wqy-zenhei fonts-tlwg-loma-otf \
|
||||
fonts-liberation libasound2 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 \
|
||||
libcairo2 libcups2 libdbus-1-3 libdrm2 libegl1 libgbm1 libglib2.0-0 libgtk-3-0 \
|
||||
libnspr4 libnss3 libpango-1.0-0 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \
|
||||
libxdamage1 libxext6 libxfixes3 libxrandr2 libxshmfence1 nano
|
||||
|
||||
COPY --from=builder /runtime /usr/local
|
||||
ADD . /app/
|
||||
RUN pip install -e . && playwright install chromium
|
||||
RUN mv docker.env.prod .env.prod && \
|
||||
nb adapter install nonebot-adapter-red && \
|
||||
nb adapter install nonebot-adapter-qq && \
|
||||
nb plugin install nonebot-plugin-sentry
|
||||
ENV HOST=0.0.0.0
|
||||
CMD ["nb", "run"]
|
||||
|
||||
# vim: ft=dockerfile
|
||||
@@ -1,147 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.10
|
||||
FROM python:3.12-slim AS metadata-stage
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
TZ=Asia/Shanghai
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
python3-pip \
|
||||
git \
|
||||
&& pip install toml \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY ./pyproject.toml ./
|
||||
|
||||
RUN echo "📋 Collecting metadata..." && \
|
||||
export CREATED_DATE="$(date -u '+%Y-%m-%d %H:%M:%S UTC')" && \
|
||||
export PYPROJECT_NAME=$(python3 -c 'import toml; print(toml.load("pyproject.toml")["project"]["name"])') && \
|
||||
export DESCRIPTION=$(python3 -c 'import toml; print(toml.load("pyproject.toml")["project"]["description"])') && \
|
||||
export AUTHOR=$(python3 -c 'import toml; print(toml.load("pyproject.toml")["project"]["authors"][0]["name"])') && \
|
||||
echo "⏰ Created Date: $CREATED_DATE" && \
|
||||
echo "📝 Project Name: $PYPROJECT_NAME" && \
|
||||
echo "📄 Description: $DESCRIPTION" && \
|
||||
echo "👤 Author: $AUTHOR" && \
|
||||
echo "$CREATED_DATE" > /tmp/CREATED_DATE && \
|
||||
echo "$PYPROJECT_NAME" > /tmp/PYPROJECT_NAME && \
|
||||
echo "$DESCRIPTION" > /tmp/DESCRIPTION && \
|
||||
echo "$AUTHOR" > /tmp/AUTHOR
|
||||
|
||||
RUN --mount=type=bind,source=./.git/,target=/tmp/.git/ \
|
||||
echo "🏷️ Checking version information..." && \
|
||||
VERSION=$(git describe --tags --exact-match 2>/dev/null || git rev-parse --short HEAD || echo "unknown") && \
|
||||
echo "📌 Version: $VERSION" && \
|
||||
echo "$VERSION" > /tmp/VERSION
|
||||
|
||||
LABEL org.opencontainers.image.created="$(cat /tmp/CREATED_DATE)" \
|
||||
org.opencontainers.image.authors="$(cat /tmp/AUTHOR)" \
|
||||
org.opencontainers.image.description="$(cat /tmp/DESCRIPTION)" \
|
||||
org.opencontainers.image.name="$(cat /tmp/PYPROJECT_NAME)" \
|
||||
org.opencontainers.image.version="$(cat /tmp/VERSION)"
|
||||
|
||||
FROM node:20-slim AS frontend-stage
|
||||
|
||||
WORKDIR /tmp/admin-frontend
|
||||
|
||||
ENV TZ=Asia/Shanghai \
|
||||
PNPM_HOME="/pnpm" \
|
||||
PATH="$PNPM_HOME:$PATH"
|
||||
|
||||
COPY ./admin-frontend/package.json ./admin-frontend/pnpm-lock.yaml ./
|
||||
|
||||
RUN corepack enable && \
|
||||
corepack prepare pnpm@latest --activate && \
|
||||
echo "🔔 Verifying Node.js installation..." && \
|
||||
if NODE_VERSION=$(node --version 2>&1); then \
|
||||
echo "✅ Node.js version ${NODE_VERSION} installed successfully"; \
|
||||
else \
|
||||
echo "❌ Node.js installation failed" && exit 1; \
|
||||
fi && \
|
||||
echo "🔔 Verifying pnpm installation..." && \
|
||||
if PNPM_VERSION=$(pnpm --version 2>&1); then \
|
||||
echo "✅ pnpm version ${PNPM_VERSION} installed successfully"; \
|
||||
else \
|
||||
echo "❌ pnpm installation failed" && exit 1; \
|
||||
fi
|
||||
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
COPY ./admin-frontend .
|
||||
RUN mkdir -p ../nonebot_bison/admin_page/dist
|
||||
|
||||
RUN echo "🏗️ Starting frontend build..." && \
|
||||
pnpm run build && \
|
||||
echo "✅ Frontend build completed"
|
||||
|
||||
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS uv-stage
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
UV_COMPILE_BYTECODE=1 \
|
||||
UV_FROZEN=1 \
|
||||
UV_LINK_MODE=copy \
|
||||
TZ=Asia/Shanghai
|
||||
|
||||
RUN echo "🔔 Verifying uv installation..." && \
|
||||
if UV_VERSION=$(uv --version 2>&1); then \
|
||||
echo "✅ uv version ${UV_VERSION} installed successfully"; \
|
||||
else \
|
||||
echo "❌ uv installation failed" && exit 1; \
|
||||
fi
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||
echo "📦 Installing project dependencies..." && \
|
||||
uv sync --no-install-project --no-dev --extra docker && \
|
||||
echo "✅ Dependencies installed successfully"
|
||||
|
||||
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
UV_COMPILE_BYTECODE=1 \
|
||||
UV_FROZEN=1 \
|
||||
UV_LINK_MODE=copy \
|
||||
TZ=Asia/Shanghai \
|
||||
SHELL="/bin/bash"
|
||||
|
||||
# 少变动的放上面,频繁变动的放下面
|
||||
COPY ./docker.env.prod ./.env
|
||||
COPY extra_plugins/ /app/extra_plugins/
|
||||
COPY --from=frontend-stage /tmp/nonebot_bison/admin_page/dist ./nonebot_bison/admin_page/dist
|
||||
COPY --from=uv-stage /app /app
|
||||
COPY ./pyproject.toml ./uv.lock ./README.md ./
|
||||
COPY nonebot_bison/ /app/nonebot_bison/
|
||||
COPY --from=metadata-stage /tmp/VERSION /app/VERSION
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
echo "📦 Installing bison project..." && \
|
||||
uv sync --no-dev --extra docker && \
|
||||
uv run nb adapter install nonebot-adapter-satori && \
|
||||
uv run nb adapter install nonebot-adapter-qq && \
|
||||
uv run nb plugin install nonebot-plugin-sentry && \
|
||||
echo "✅ Bison installed successfully"
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENV BISON_SKIP_BROWSER_CHECK=True \
|
||||
BISON_USE_BROWSER=False
|
||||
|
||||
RUN echo '#!/bin/bash' > /app/start.sh && \
|
||||
echo 'echo "📌 Current bison Version: $(cat /app/VERSION)"' >> /app/start.sh && \
|
||||
echo 'echo "🚀 Starting service..."' >> /app/start.sh && \
|
||||
echo 'source /app/.venv/bin/activate' >> /app/start.sh && \
|
||||
echo 'nb run' >> /app/start.sh && \
|
||||
chmod +x /app/start.sh
|
||||
|
||||
CMD ["/app/start.sh"]
|
||||
+3
-3
@@ -8,7 +8,7 @@ next: /dev/cookie
|
||||
## 语言以及工具
|
||||
|
||||
1. 本项目使用了`python3.10`的特性进行开发,所以请确保你的 Python 版本>=3.10
|
||||
2. 本项目使用 uv 进行依赖管理,请确保开发之前已经进行过`uv sync`,运行时在`uv venv`的环境中进行运行
|
||||
2. 本项目使用 poetry 进行依赖管理,请确保开发之前已经进行过`poetry install`,运行时在`poetry shell`的环境中进行运行
|
||||
3. 本项目使用的 node 项目管理工具是 pnpm
|
||||
|
||||
::: tip 参考
|
||||
@@ -30,7 +30,7 @@ next: /dev/cookie
|
||||
2. 运行 bot
|
||||
|
||||
```bash
|
||||
uv run nb run
|
||||
poetry run nb run
|
||||
```
|
||||
|
||||
3. 运行前端:
|
||||
@@ -50,7 +50,7 @@ next: /dev/cookie
|
||||
|
||||
## 代码格式
|
||||
|
||||
本项目使用了 pre-commit 来进行代码美化和格式化。在`uv venv`状态下执行`pre-commit install`来安装 git hook,可自动在 commit 时
|
||||
本项目使用了 pre-commit 来进行代码美化和格式化。在`poetry shell`状态下执行`pre-commit install`来安装 git hook,可自动在 commit 时
|
||||
格式化代码。
|
||||
|
||||
## 适配新网站
|
||||
|
||||
+5
-3
@@ -83,8 +83,10 @@ sequenceDiagram
|
||||
目前 CookieClientManager 具有以下方法
|
||||
|
||||
- `refresh_anonymous_cookie(cls)` 移除已有的匿名 cookie,添加一个新的匿名 cookie,应该在 CCM 初始化时调用
|
||||
- `add_user_cookie(cls, content: str)` 添加用户 cookie,在这里对 Cookie 进行检查并获取 cookie_name,写入数据库
|
||||
- `_generate_hook(self, cookie: Cookie) -> Callable` hook 函数生成器,用于回写请求状态到数据库
|
||||
- `_choose_cookie(self, target: Target) -> Cookie` 选择 cookie 的具体算法
|
||||
- `add_identified_cookie(cls, content: str, cookie_name: str | None = None) -> Cookie` 对外的接口,添加实名 cookie,内部会调用 Site 的方法进行检查
|
||||
- `add_user_cookie(cls, content: str, cookie_name: str | None = None) -> Cookie` 对外的接口,添加用户 cookie,内部会调用 Site 的方法进行检查
|
||||
- `get_client(self, target: Target | None) -> AsyncClient` 对外的接口,获取 client,根据 target 选择 cookie
|
||||
- `_assemble_client(self, client, cookie) -> AsyncClient` 组装 client,可以自定义 cookie 对象的 content 装配到 client 中的方式
|
||||
|
||||
@@ -98,7 +100,7 @@ sequenceDiagram
|
||||
|
||||
简单来说:
|
||||
|
||||
- 如果需要修改 Cookie 的默认参数,可以重写`add_identified_cookie`方法,这里设置需要的字段
|
||||
- 如果需要修改 Cookie 的默认参数,可以重写`add_user_cookie`方法,这里设置需要的字段
|
||||
- 如果需要修改选择 Cookie 的逻辑,可以重写`_choose_cookie`方法,使用自己的算法选择合适的 Cookie 并返回
|
||||
- 如果需要自定义 Cookie 的格式,可以重写`valid_cookie`方法,自定义验证 Cookie 的逻辑,并重写`_assemble_client`方法,自定义将 Cookie 装配到 Client 中的逻辑
|
||||
- 如果要在请求结束后做一些操作(例如保存此次请求的结果/状态),可以重写`_response_hook`方法,自定义请求结束后的行为
|
||||
@@ -135,7 +137,7 @@ sequenceDiagram
|
||||
|
||||
- **无 Target 平台的 Cookie 处理方式**
|
||||
|
||||
对于不存在 Target 的平台,如小刻食堂,可以重写 add_identified_cookie 方法,为实名 cookie 设置 is_universal 字段。这样,在获取 Client 时,由于传入的 Target 为空,就只会选择 is_universal 的 cookie。实现了无 Target 平台的实名 cookie 调度。
|
||||
对于不存在 Target 的平台,如小刻食堂,可以重写 add_user_cookie 方法,为用户 Cookie 设置 is_universal 字段。这样,在获取 Client 时,由于传入的 Target 为空,就只会选择 is_universal 的 cookie。实现了无 Target 平台的用户 Cookie 调度。
|
||||
|
||||
## 默认的调度策略
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ next: /usage/easy-use
|
||||
参考视频教程 [保姆级新手教学 - Well404](https://www.bilibili.com/video/BV1984y1b7JY)
|
||||
:::
|
||||
::: warning 防止环境冲突!
|
||||
建议所有操作都在虚拟环境下进行,推荐使用[`uv`](https://docs.astral.sh/uv/)或者 python 自带的[`venv`](https://docs.python.org/zh-cn/3/library/venv.html)
|
||||
建议所有操作都在虚拟环境下进行,推荐使用[`poetry`](https://python-poetry.org/)或者 python 自带的[`venv`](https://docs.python.org/zh-cn/3/library/venv.html)
|
||||
:::
|
||||
|
||||
### 使用 nb-cli 安装 <Badge type="tip" text="推荐" vertical="top" />
|
||||
@@ -43,17 +43,17 @@ next: /usage/easy-use
|
||||
3. 在项目中添加依赖
|
||||
|
||||
```bash
|
||||
uv add nonebot-bison
|
||||
poetry add nonebot-bison
|
||||
```
|
||||
|
||||
### 手动安装
|
||||
|
||||
1. 安装 pip 包`nonebot-bison`
|
||||
::: code-tabs
|
||||
@tab uv
|
||||
@tab poetry
|
||||
|
||||
```bash
|
||||
uv add nonebot-bison
|
||||
poetry add nonebot-bison
|
||||
```
|
||||
|
||||
@tab pip
|
||||
@@ -169,13 +169,13 @@ Bison 的 WebUI 是需要编译后才能使用的,直接克隆源代码需要
|
||||
本项目中使用了 Python 3.10 的语法,如果出现问题,请检查 Python 版本
|
||||
:::
|
||||
|
||||
1. 首先安装 uv:[安装方法](https://docs.astral.sh/uv/getting-started/installation/)
|
||||
2. clone 本项目,在项目中使用`uv sync`安装依赖
|
||||
1. 首先安装 poetry:[安装方法](https://python-poetry.org/docs/#installation)
|
||||
2. clone 本项目,在项目中`poetry install`安装依赖
|
||||
|
||||
```bash
|
||||
git clone https://github.com/felinae98/nonebot-bison.git
|
||||
cd nonebot-bison
|
||||
uv sync
|
||||
poetry install
|
||||
```
|
||||
|
||||
#### WebUI 安装
|
||||
@@ -235,5 +235,5 @@ Bison 的 WebUI 是需要编译后才能使用的,直接克隆源代码需要
|
||||
2. 启动 Bot
|
||||
|
||||
```bash
|
||||
uv run nb run
|
||||
poetry run nb run
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from nonebot import on_request
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters.onebot.v11.event import FriendRequestEvent, GroupRequestEvent
|
||||
from nonebot.log import logger
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters.onebot.v11.event import GroupRequestEvent, FriendRequestEvent
|
||||
|
||||
friend_req = on_request(priority=5)
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ require("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 . import post, send, theme, types, utils, config, platform, bootstrap, scheduler, admin_page, sub_manager
|
||||
|
||||
__help__version__ = "0.8.2"
|
||||
nonebot_plugin_saa.enable_auto_select_bot()
|
||||
@@ -36,12 +36,12 @@ __all__ = [
|
||||
"admin_page",
|
||||
"bootstrap",
|
||||
"config",
|
||||
"platform",
|
||||
"sub_manager",
|
||||
"post",
|
||||
"scheduler",
|
||||
"send",
|
||||
"sub_manager",
|
||||
"theme",
|
||||
"platform",
|
||||
"types",
|
||||
"utils",
|
||||
"theme",
|
||||
]
|
||||
|
||||
@@ -2,16 +2,15 @@ import os
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from nonebot import get_driver, on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
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 nonebot import get_driver, on_command
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from nonebot.adapters.onebot.v11.event import PrivateMessageEvent
|
||||
|
||||
from .api import router as api_router
|
||||
from ..plugin_config import plugin_config
|
||||
from .token_manager import token_manager as tm
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -22,9 +21,9 @@ STATIC_PATH = (Path(__file__).parent / "dist").resolve()
|
||||
|
||||
|
||||
def init_fastapi(driver: "Driver"):
|
||||
import socketio
|
||||
from fastapi.applications import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
import socketio
|
||||
|
||||
sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
|
||||
socket_app = socketio.ASGIApp(sio, socketio_path="socket")
|
||||
|
||||
@@ -1,39 +1,38 @@
|
||||
from typing import cast
|
||||
|
||||
from fastapi import status
|
||||
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 fastapi import status
|
||||
from fastapi.routing import APIRouter
|
||||
from fastapi.param_functions import Depends
|
||||
from fastapi.exceptions import HTTPException
|
||||
from nonebot_plugin_saa import TargetQQGroup
|
||||
from nonebot_plugin_saa.auto_select_bot import get_bot
|
||||
from fastapi.security.oauth2 import OAuth2PasswordBearer
|
||||
|
||||
from nonebot_bison.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 ..types import WeightConfig
|
||||
from ..apis import check_sub_target
|
||||
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 ..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 (
|
||||
AddSubscribeReq,
|
||||
Cookie,
|
||||
CookieTarget,
|
||||
GlobalConf,
|
||||
PlatformConfig,
|
||||
SiteConfig,
|
||||
StatusResp,
|
||||
SubscribeConfig,
|
||||
SubscribeGroupDetail,
|
||||
SubscribeResp,
|
||||
Target,
|
||||
TokenResp,
|
||||
GlobalConf,
|
||||
SiteConfig,
|
||||
StatusResp,
|
||||
CookieTarget,
|
||||
SubscribeResp,
|
||||
PlatformConfig,
|
||||
AddSubscribeReq,
|
||||
SubscribeConfig,
|
||||
SubscribeGroupDetail,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["api"])
|
||||
@@ -235,7 +234,7 @@ async def get_cookie(site_name: str | None = None, target: str | None = None) ->
|
||||
@router.post("/cookie", dependencies=[Depends(check_is_superuser)])
|
||||
async def add_cookie(site_name: str, content: str) -> StatusResp:
|
||||
client_mgr = cast(CookieClientManager, scheduler_dict[site_manager[site_name]].client_mgr)
|
||||
await client_mgr.add_identified_cookie(content)
|
||||
await client_mgr.add_user_cookie(content)
|
||||
return StatusResp(ok=True, msg="")
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
import random
|
||||
import string
|
||||
import datetime
|
||||
|
||||
import jwt
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from datetime import timedelta
|
||||
import random
|
||||
import string
|
||||
from datetime import timedelta
|
||||
|
||||
from expiringdictx import ExpiringDict
|
||||
|
||||
|
||||
@@ -60,8 +60,8 @@ class StatusResp(BaseModel):
|
||||
msg: str
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from .platform import platform_manager
|
||||
from .scheduler import scheduler_dict
|
||||
from .types import Target
|
||||
from .scheduler import scheduler_dict
|
||||
from .platform import platform_manager
|
||||
|
||||
|
||||
async def check_sub_target(platform_name: str, target: Target):
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from nonebot.log import logger
|
||||
from nonebot_plugin_datastore.db import get_engine, post_db_init, pre_db_init
|
||||
from sqlalchemy import inspect, text
|
||||
from sqlalchemy import text, inspect
|
||||
from nonebot_plugin_datastore.db import get_engine, pre_db_init, post_db_init
|
||||
|
||||
from .config.config_legacy import start_up as legacy_db_startup
|
||||
from .config.db_migration import data_migrate
|
||||
from .scheduler.manager import init_scheduler
|
||||
from .config.config_legacy import start_up as legacy_db_startup
|
||||
|
||||
|
||||
@pre_db_init
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from typing import Literal, overload
|
||||
|
||||
from nonebot.compat import PYDANTIC_V2
|
||||
from pydantic import BaseModel
|
||||
from nonebot.compat import PYDANTIC_V2
|
||||
|
||||
__all__ = ("model_rebuild", "model_validator")
|
||||
__all__ = ("model_validator", "model_rebuild")
|
||||
|
||||
|
||||
if PYDANTIC_V2:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from .db_config import config as config
|
||||
from .utils import NoSuchSubscribeException as NoSuchSubscribeException
|
||||
from .utils import NoSuchTargetException as NoSuchTargetException
|
||||
from .utils import NoSuchUserException as NoSuchUserException
|
||||
from .utils import NoSuchTargetException as NoSuchTargetException
|
||||
from .utils import NoSuchSubscribeException as NoSuchSubscribeException
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
import json
|
||||
import os
|
||||
import json
|
||||
from os import path
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
from typing import Literal, TypedDict
|
||||
|
||||
from nonebot.log import logger
|
||||
from tinydb import Query, TinyDB
|
||||
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
from nonebot_bison.types import Target, User
|
||||
from nonebot_bison.utils import Singleton
|
||||
|
||||
from .utils import NoSuchSubscribeException, NoSuchUserException
|
||||
from ..utils import Singleton
|
||||
from ..types import User, Target
|
||||
from ..platform import platform_manager
|
||||
from ..plugin_config import plugin_config
|
||||
from .utils import NoSuchUserException, NoSuchSubscribeException
|
||||
|
||||
supported_target_type = platform_manager.keys()
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
from collections.abc import Awaitable, Callable, Sequence
|
||||
from datetime import datetime, time
|
||||
from datetime import time, datetime
|
||||
from collections.abc import Callable, Sequence, Awaitable
|
||||
|
||||
from nonebot.compat import model_dump
|
||||
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 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_bison.types import Category, PlatformWeightConfigResp, Tag, TimeWeightConfig, UserSubInfo, WeightConfig
|
||||
from nonebot_bison.types import Target as T_Target
|
||||
|
||||
from .db_model import Cookie, CookieTarget, ScheduleTimeWeight, Subscribe, Target, User
|
||||
from .utils import DuplicateCookieTargetException, NoSuchTargetException
|
||||
from ..types import Tag
|
||||
from ..types import Target as T_Target
|
||||
from .utils import NoSuchTargetException, DuplicateCookieTargetException
|
||||
from .db_model import User, Cookie, Target, Subscribe, CookieTarget, ScheduleTimeWeight
|
||||
from ..types import Category, UserSubInfo, WeightConfig, TimeWeightConfig, PlatformWeightConfigResp
|
||||
|
||||
|
||||
def _get_time():
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from nonebot.compat import model_dump
|
||||
from nonebot.log import logger
|
||||
from nonebot.compat import model_dump
|
||||
from nonebot_plugin_datastore.db import get_engine
|
||||
from nonebot_plugin_saa import TargetQQGroup, TargetQQPrivate
|
||||
from sqlalchemy.ext.asyncio.session import AsyncSession
|
||||
from nonebot_plugin_saa import TargetQQGroup, TargetQQPrivate
|
||||
|
||||
from .db_model import User, Target, Subscribe
|
||||
from .config_legacy import Config, ConfigContent, drop
|
||||
from .db_model import Subscribe, Target, User
|
||||
|
||||
|
||||
async def data_migrate():
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from pathlib import Path
|
||||
|
||||
from nonebot_plugin_saa import PlatformTarget
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict
|
||||
from nonebot_plugin_datastore import get_plugin_data
|
||||
from nonebot_plugin_saa import PlatformTarget
|
||||
from sqlalchemy import JSON, DateTime, ForeignKey, String, UniqueConstraint
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from sqlalchemy.orm import Mapped, relationship, mapped_column
|
||||
from sqlalchemy import JSON, String, DateTime, ForeignKey, UniqueConstraint
|
||||
|
||||
from nonebot_bison.types import Category, Tag
|
||||
from ..types import Tag, Category
|
||||
|
||||
Model = get_plugin_data().Model
|
||||
get_plugin_data().set_migration_dir(Path(__file__).parent / "migrations")
|
||||
|
||||
@@ -6,8 +6,8 @@ Create Date: 2022-03-21 19:18:13.762626
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "0571870f5222"
|
||||
|
||||
@@ -6,8 +6,8 @@ Create Date: 2022-03-27 21:50:10.911649
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "4a46ba54a3f3"
|
||||
|
||||
@@ -6,8 +6,8 @@ Create Date: 2022-05-31 22:05:13.235981
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "5f3370328e44"
|
||||
|
||||
@@ -6,8 +6,8 @@ Create Date: 2023-03-20 00:39:30.199915
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@@ -6,8 +6,8 @@ Create Date: 2023-03-20 11:08:42.883556
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
@@ -6,8 +6,8 @@ Create Date: 2023-03-20 15:38:20.220599
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "8d3863e9d74b"
|
||||
|
||||
@@ -6,8 +6,8 @@ Create Date: 2022-03-29 21:01:38.213153
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "a333d6224193"
|
||||
|
||||
@@ -6,10 +6,10 @@ Create Date: 2023-03-20 01:14:42.623789
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
from alembic import op
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "a5466912fad0"
|
||||
|
||||
@@ -6,8 +6,8 @@ Create Date: 2023-03-09 19:10:42.168133
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "aceef470d69c"
|
||||
|
||||
@@ -6,11 +6,11 @@ Create Date: 2023-03-02 14:04:16.492133
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "bd92923c218f"
|
||||
|
||||
@@ -6,8 +6,8 @@ Create Date: 2024-09-23 10:03:30.593263
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy import Text
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ Create Date: 2023-08-25 00:20:51.511329
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
from alembic import op
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "f9baef347cc8"
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
from . import v1, v2, v3
|
||||
from .base import NBESFBase
|
||||
|
||||
__all__ = ["NBESFBase", "v1", "v2", "v3"]
|
||||
__all__ = ["v1", "v2", "v3", "NBESFBase"]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from abc import ABC
|
||||
|
||||
from pydantic import BaseModel
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict
|
||||
from nonebot_plugin_saa.registries import AllSupportedPlatformTarget as UserInfo
|
||||
from pydantic import BaseModel
|
||||
|
||||
from nonebot_bison.types import Category, Tag
|
||||
from ....types import Tag, Category
|
||||
|
||||
|
||||
class NBESFBase(BaseModel, ABC):
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
"""nbesf is Nonebot Bison Enchangable Subscribes File! ver.1"""
|
||||
|
||||
from functools import partial
|
||||
from typing import Any
|
||||
from functools import partial
|
||||
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
|
||||
from nonebot.log import logger
|
||||
from pydantic import BaseModel
|
||||
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 nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
|
||||
|
||||
from ..utils import NBESFParseErr
|
||||
from ....types import Tag, Category
|
||||
from .base import NBESFBase, SubReceipt
|
||||
from ...db_config import SubscribeDupException, config
|
||||
|
||||
# ===== nbesf 定义格式 ====== #
|
||||
NBESF_VERSION = 1
|
||||
@@ -80,7 +79,7 @@ class SubGroup(
|
||||
"""
|
||||
|
||||
version: int = NBESF_VERSION
|
||||
groups: list[SubPack] = Field(default_factory=list)
|
||||
groups: list[SubPack] = []
|
||||
|
||||
|
||||
# ======================= #
|
||||
@@ -109,11 +108,11 @@ async def subs_receipt_gen(nbesf_data: SubGroup):
|
||||
try:
|
||||
await config.add_subscribe(receipt.user, **model_dump(receipt, exclude={"user"}))
|
||||
except SubscribeDupException:
|
||||
logger.warning(f"!添加订阅条目 {receipt!r} 失败: 相同的订阅已存在")
|
||||
logger.warning(f"!添加订阅条目 {repr(receipt)} 失败: 相同的订阅已存在")
|
||||
except Exception as e:
|
||||
logger.error(f"!添加订阅条目 {receipt!r} 失败: {e!r}")
|
||||
logger.error(f"!添加订阅条目 {repr(receipt)} 失败: {repr(e)}")
|
||||
else:
|
||||
logger.success(f"添加订阅条目 {receipt!r} 成功!")
|
||||
logger.success(f"添加订阅条目 {repr(receipt)} 成功!")
|
||||
|
||||
|
||||
def nbesf_parser(raw_data: Any) -> SubGroup:
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
"""nbesf is Nonebot Bison Enchangable Subscribes File! ver.2"""
|
||||
|
||||
from functools import partial
|
||||
from typing import Any
|
||||
from functools import partial
|
||||
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
|
||||
from nonebot.log import logger
|
||||
from pydantic import BaseModel
|
||||
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 nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
|
||||
|
||||
from ..utils import NBESFParseErr
|
||||
from ....types import Tag, Category
|
||||
from .base import NBESFBase, SubReceipt
|
||||
from ...db_config import SubscribeDupException, config
|
||||
|
||||
# ===== nbesf 定义格式 ====== #
|
||||
NBESF_VERSION = 2
|
||||
@@ -65,7 +64,7 @@ class SubGroup(NBESFBase):
|
||||
"""
|
||||
|
||||
version: int = NBESF_VERSION
|
||||
groups: list[SubPack] = Field(default_factory=list)
|
||||
groups: list[SubPack] = []
|
||||
|
||||
|
||||
# ======================= #
|
||||
@@ -86,11 +85,11 @@ async def subs_receipt_gen(nbesf_data: SubGroup):
|
||||
try:
|
||||
await config.add_subscribe(receipt.user, **model_dump(receipt, exclude={"user"}))
|
||||
except SubscribeDupException:
|
||||
logger.warning(f"!添加订阅条目 {receipt!r} 失败: 相同的订阅已存在")
|
||||
logger.warning(f"!添加订阅条目 {repr(receipt)} 失败: 相同的订阅已存在")
|
||||
except Exception as e:
|
||||
logger.error(f"!添加订阅条目 {receipt!r} 失败: {e!r}")
|
||||
logger.error(f"!添加订阅条目 {repr(receipt)} 失败: {repr(e)}")
|
||||
else:
|
||||
logger.success(f"添加订阅条目 {receipt!r} 成功!")
|
||||
logger.success(f"添加订阅条目 {repr(receipt)} 成功!")
|
||||
|
||||
|
||||
def nbesf_parser(raw_data: Any) -> SubGroup:
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
"""nbesf is Nonebot Bison Enchangable Subscribes File! ver.2"""
|
||||
|
||||
from functools import partial
|
||||
from typing import Any
|
||||
from functools import partial
|
||||
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
|
||||
from nonebot.log import logger
|
||||
from pydantic import BaseModel
|
||||
from nonebot_plugin_saa.registries import AllSupportedPlatformTarget
|
||||
from pydantic import BaseModel, Field
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
|
||||
|
||||
from nonebot_bison.config.db_config import SubscribeDupException, config
|
||||
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 Tag
|
||||
from nonebot_bison.types import Category
|
||||
from nonebot_bison.types import Target as T_Target
|
||||
|
||||
from ..utils import NBESFParseErr
|
||||
from .base import NBESFBase, SubReceipt
|
||||
from ...db_model import Cookie as DBCookie
|
||||
from ...db_config import SubscribeDupException, config
|
||||
|
||||
# ===== nbesf 定义格式 ====== #
|
||||
NBESF_VERSION = 3
|
||||
@@ -79,8 +80,8 @@ class SubGroup(NBESFBase):
|
||||
"""
|
||||
|
||||
version: int = NBESF_VERSION
|
||||
groups: list[SubPack] = Field(default_factory=list)
|
||||
cookies: list[Cookie] = Field(default_factory=list)
|
||||
groups: list[SubPack] = []
|
||||
cookies: list[Cookie] = []
|
||||
|
||||
|
||||
# ======================= #
|
||||
@@ -102,11 +103,11 @@ async def subs_receipt_gen(nbesf_data: SubGroup):
|
||||
try:
|
||||
await config.add_subscribe(receipt.user, **model_dump(receipt, exclude={"user"}))
|
||||
except SubscribeDupException:
|
||||
logger.warning(f"!添加订阅条目 {receipt!r} 失败: 相同的订阅已存在")
|
||||
logger.warning(f"!添加订阅条目 {repr(receipt)} 失败: 相同的订阅已存在")
|
||||
except Exception as e:
|
||||
logger.error(f"!添加订阅条目 {receipt!r} 失败: {e!r}")
|
||||
logger.error(f"!添加订阅条目 {repr(receipt)} 失败: {repr(e)}")
|
||||
else:
|
||||
logger.success(f"添加订阅条目 {receipt!r} 成功!")
|
||||
logger.success(f"添加订阅条目 {repr(receipt)} 成功!")
|
||||
|
||||
|
||||
async def magic_cookie_gen(nbesf_data: SubGroup):
|
||||
@@ -118,9 +119,9 @@ async def magic_cookie_gen(nbesf_data: SubGroup):
|
||||
for target in cookie.targets:
|
||||
await config.add_cookie_target(T_Target(target.target), target.platform_name, cookie_id)
|
||||
except Exception as e:
|
||||
logger.error(f"!添加 Cookie 条目 {cookie!r} 失败: {e!r}")
|
||||
logger.error(f"!添加 Cookie 条目 {repr(cookie)} 失败: {repr(e)}")
|
||||
else:
|
||||
logger.success(f"添加 Cookie 条目 {cookie!r} 成功!")
|
||||
logger.success(f"添加 Cookie 条目 {repr(cookie)} 成功!")
|
||||
|
||||
|
||||
def nbesf_parser(raw_data: Any) -> SubGroup:
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
from typing import cast
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable
|
||||
from typing import cast
|
||||
|
||||
from nonebot.compat import type_validate_python
|
||||
from nonebot.log import logger
|
||||
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 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_plugin_datastore.db import create_session
|
||||
from sqlalchemy.orm.strategy_options import selectinload
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.config.db_model import Cookie, CookieTarget, Subscribe, Target, User
|
||||
|
||||
from .nbesf_model import NBESFBase, v1, v2, v3
|
||||
from .. import config
|
||||
from .utils import NBESFVerMatchErr, row2dict
|
||||
from .nbesf_model import NBESFBase, v1, v2, v3
|
||||
from ..db_model import User, Cookie, Target, Subscribe, CookieTarget
|
||||
|
||||
|
||||
async def subscribes_export(selector: Callable[[Select], Select]) -> v3.SubGroup:
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from collections import defaultdict
|
||||
from importlib import import_module
|
||||
from pathlib import Path
|
||||
from pkgutil import iter_modules
|
||||
from collections import defaultdict
|
||||
from importlib import import_module
|
||||
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
|
||||
from ..plugin_config import plugin_config
|
||||
from .platform import Platform, make_no_target_group
|
||||
|
||||
_package_dir = str(Path(__file__).resolve().parent)
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
from functools import partial
|
||||
import html
|
||||
import re
|
||||
from typing import Any, ClassVar
|
||||
import html
|
||||
from typing import Any
|
||||
from functools import partial
|
||||
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from httpx import AsyncClient
|
||||
from nonebot.compat import type_validate_python
|
||||
from pydantic import BaseModel, Field
|
||||
from yarl import URL
|
||||
from httpx import AsyncClient
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from pydantic import Field, BaseModel
|
||||
from nonebot.compat import type_validate_python
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -59,7 +58,7 @@ class ArkBulletinResponse(ArkResponseBase):
|
||||
class ArknightsSite(Site):
|
||||
name = "arknights"
|
||||
schedule_type = "interval"
|
||||
schedule_setting: ClassVar[dict] = {"seconds": 30}
|
||||
schedule_setting = {"seconds": 30}
|
||||
|
||||
|
||||
class ArknightsPost(Post, HTMLContentSupport):
|
||||
@@ -96,7 +95,7 @@ class ArknightsPost(Post, HTMLContentSupport):
|
||||
|
||||
|
||||
class Arknights(NewMessage):
|
||||
categories: ClassVar[dict[Category, str]] = {1: "游戏公告"}
|
||||
categories = {1: "游戏公告"}
|
||||
platform_name = "arknights"
|
||||
name = "明日方舟游戏信息"
|
||||
enable_tag = False
|
||||
@@ -158,7 +157,7 @@ class Arknights(NewMessage):
|
||||
|
||||
|
||||
class AkVersion(StatusChange):
|
||||
categories: ClassVar[dict[Category, str]] = {2: "更新信息"}
|
||||
categories = {2: "更新信息"}
|
||||
platform_name = "arknights"
|
||||
name = "明日方舟游戏信息"
|
||||
enable_tag = False
|
||||
@@ -203,7 +202,7 @@ class AkVersion(StatusChange):
|
||||
|
||||
|
||||
class MonsterSiren(NewMessage):
|
||||
categories: ClassVar[dict[Category, str]] = {3: "塞壬唱片新闻"}
|
||||
categories = {3: "塞壬唱片新闻"}
|
||||
platform_name = "arknights"
|
||||
name = "明日方舟游戏信息"
|
||||
enable_tag = False
|
||||
@@ -251,7 +250,7 @@ class MonsterSiren(NewMessage):
|
||||
|
||||
|
||||
class TerraHistoricusComic(NewMessage):
|
||||
categories: ClassVar[dict[Category, str]] = {4: "泰拉记事社漫画"}
|
||||
categories = {4: "泰拉记事社漫画"}
|
||||
platform_name = "arknights"
|
||||
name = "明日方舟游戏信息"
|
||||
enable_tag = False
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from .platforms import Bilibili as Bilibili
|
||||
from .platforms import BilibiliBangumi as BilibiliBangumi
|
||||
from .platforms import Bilibililive as Bilibililive
|
||||
from .scheduler import BiliBangumiSite as BiliBangumiSite
|
||||
from .scheduler import BilibiliClientManager as BilibiliClientManager
|
||||
from .scheduler import BilibiliSite as BilibiliSite
|
||||
from .scheduler import BililiveSite as BililiveSite
|
||||
from .platforms import BilibiliBangumi as BilibiliBangumi
|
||||
from .scheduler import BiliBangumiSite as BiliBangumiSite
|
||||
from .scheduler import BilibiliClientManager as BilibiliClientManager
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import sys
|
||||
import asyncio
|
||||
from collections.abc import AsyncGenerator, Awaitable, Callable, Sequence
|
||||
from collections.abc import Set as AbstractSet
|
||||
from dataclasses import dataclass
|
||||
import inspect
|
||||
from enum import Enum
|
||||
from functools import wraps
|
||||
import inspect
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from collections.abc import Set as AbstractSet
|
||||
from collections.abc import Callable, Sequence, Awaitable, AsyncGenerator
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Concatenate,
|
||||
Generic,
|
||||
NamedTuple,
|
||||
ParamSpec,
|
||||
TypeVar,
|
||||
Protocol,
|
||||
ParamSpec,
|
||||
TypeAlias,
|
||||
TypedDict,
|
||||
TypeVar,
|
||||
NamedTuple,
|
||||
Concatenate,
|
||||
overload,
|
||||
runtime_checkable,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Any, Literal, TypeAlias, TypeVar
|
||||
from typing import Any, Literal, TypeVar, TypeAlias
|
||||
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict
|
||||
from pydantic import BaseModel
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict
|
||||
|
||||
from nonebot_bison.compat import model_rebuild
|
||||
|
||||
@@ -13,7 +13,7 @@ TBaseModel = TypeVar("TBaseModel", bound=type[BaseModel])
|
||||
def model_rebuild_recurse(cls: TBaseModel) -> TBaseModel:
|
||||
"""Recursively rebuild all BaseModel subclasses in the class."""
|
||||
if not PYDANTIC_V2:
|
||||
from inspect import getmembers, isclass
|
||||
from inspect import isclass, getmembers
|
||||
|
||||
for _, sub_cls in getmembers(cls, lambda x: isclass(x) and issubclass(x, BaseModel)):
|
||||
model_rebuild_recurse(sub_cls)
|
||||
@@ -131,7 +131,7 @@ class PostAPI(APIBase):
|
||||
basic: "PostAPI.Basic"
|
||||
id_str: str
|
||||
modules: "PostAPI.Modules"
|
||||
orig: "PostAPI.Item | PostAPI.DeletedItem | None" = None
|
||||
orig: "PostAPI.Item | None" = None
|
||||
topic: "PostAPI.Topic | None" = None
|
||||
type: DynamicType
|
||||
|
||||
@@ -141,14 +141,6 @@ class PostAPI(APIBase):
|
||||
modules: "PostAPI.Modules"
|
||||
type: Literal["DYNAMIC_TYPE_NONE"]
|
||||
|
||||
def to_item(self) -> "PostAPI.Item":
|
||||
return PostAPI.Item(
|
||||
basic=self.basic,
|
||||
id_str="",
|
||||
modules=self.modules,
|
||||
type=self.type,
|
||||
)
|
||||
|
||||
class Data(Base):
|
||||
items: "list[PostAPI.Item | PostAPI.DeletedItem] | None" = None
|
||||
|
||||
|
||||
@@ -1,41 +1,40 @@
|
||||
import re
|
||||
import json
|
||||
from copy import deepcopy
|
||||
from enum import Enum, unique
|
||||
import json
|
||||
import re
|
||||
from typing import ClassVar, NamedTuple
|
||||
from typing import NamedTuple
|
||||
from typing_extensions import Self
|
||||
|
||||
from httpx import AsyncClient
|
||||
from nonebot import logger
|
||||
from nonebot.compat import type_validate_json, type_validate_python
|
||||
from pydantic import BaseModel, Field, ValidationError
|
||||
from yarl import URL
|
||||
from nonebot import logger
|
||||
from httpx import AsyncClient
|
||||
from pydantic import Field, BaseModel, ValidationError
|
||||
from nonebot.compat import type_validate_json, type_validate_python
|
||||
|
||||
from nonebot_bison.compat import model_rebuild
|
||||
from nonebot_bison.platform.platform import CategoryNotRecognize, CategoryNotSupport, NewMessage, StatusChange
|
||||
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 nonebot_bison.compat import model_rebuild
|
||||
from nonebot_bison.utils import text_similarity, decode_unicode_escapes
|
||||
from nonebot_bison.types import Tag, Target, RawPost, ApiError, Category
|
||||
|
||||
from .models import (
|
||||
ArticleMajor,
|
||||
CommonMajor,
|
||||
CoursesMajor,
|
||||
DeletedMajor,
|
||||
DrawMajor,
|
||||
DynamicType,
|
||||
DynRawPost,
|
||||
LiveMajor,
|
||||
LiveRecommendMajor,
|
||||
OPUSMajor,
|
||||
PGCMajor,
|
||||
PostAPI,
|
||||
UnknownMajor,
|
||||
UserAPI,
|
||||
VideoMajor,
|
||||
)
|
||||
from .retry import ApiCode352Error, retry_for_352
|
||||
from .scheduler import BiliBangumiSite, BilibiliSite, BililiveSite
|
||||
from .scheduler import BilibiliSite, BililiveSite, BiliBangumiSite
|
||||
from ..platform import NewMessage, StatusChange, CategoryNotSupport, CategoryNotRecognize
|
||||
from .models import (
|
||||
PostAPI,
|
||||
UserAPI,
|
||||
PGCMajor,
|
||||
DrawMajor,
|
||||
LiveMajor,
|
||||
OPUSMajor,
|
||||
DynRawPost,
|
||||
VideoMajor,
|
||||
CommonMajor,
|
||||
DynamicType,
|
||||
ArticleMajor,
|
||||
CoursesMajor,
|
||||
UnknownMajor,
|
||||
LiveRecommendMajor,
|
||||
)
|
||||
|
||||
|
||||
class _ProcessedText(NamedTuple):
|
||||
@@ -51,7 +50,7 @@ class _ParsedMojarPost(NamedTuple):
|
||||
|
||||
|
||||
class Bilibili(NewMessage):
|
||||
categories: ClassVar[dict[Category, str]] = {
|
||||
categories = {
|
||||
1: "一般动态",
|
||||
2: "专栏文章",
|
||||
3: "视频",
|
||||
@@ -162,6 +161,7 @@ class Bilibili(NewMessage):
|
||||
return tags
|
||||
|
||||
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)])
|
||||
if title_similarity > 0.9:
|
||||
@@ -243,13 +243,6 @@ class Bilibili(NewMessage):
|
||||
pics=[courses.cover],
|
||||
url=URL(courses.jump_url).with_scheme("https").human_repr(),
|
||||
)
|
||||
case DeletedMajor(none=none):
|
||||
return _ParsedMojarPost(
|
||||
title="",
|
||||
content=none.tips,
|
||||
pics=[],
|
||||
url=None,
|
||||
)
|
||||
case UnknownMajor(type=unknown_type):
|
||||
raise CategoryNotSupport(unknown_type)
|
||||
case None: # 没有major的情况
|
||||
@@ -266,13 +259,10 @@ class Bilibili(NewMessage):
|
||||
parsed_raw_post = self.pre_parse_by_mojar(raw_post)
|
||||
parsed_raw_repost = None
|
||||
if self._do_get_category(raw_post.type) == Category(5):
|
||||
match raw_post.orig:
|
||||
case PostAPI.Item() as orig:
|
||||
parsed_raw_repost = self.pre_parse_by_mojar(orig)
|
||||
case PostAPI.DeletedItem() as orig:
|
||||
parsed_raw_repost = self.pre_parse_by_mojar(orig.to_item())
|
||||
case None:
|
||||
logger.warning(f"转发动态{raw_post.id_str}没有原动态")
|
||||
if raw_post.orig:
|
||||
parsed_raw_repost = self.pre_parse_by_mojar(raw_post.orig)
|
||||
else:
|
||||
logger.warning(f"转发动态{raw_post.id_str}没有原动态")
|
||||
|
||||
post = Post(
|
||||
self,
|
||||
@@ -285,14 +275,8 @@ class Bilibili(NewMessage):
|
||||
nickname=raw_post.modules.module_author.name,
|
||||
)
|
||||
if parsed_raw_repost:
|
||||
match raw_post.orig:
|
||||
case PostAPI.Item() as orig:
|
||||
orig = orig
|
||||
case PostAPI.DeletedItem() as orig:
|
||||
orig = orig.to_item()
|
||||
case None:
|
||||
raise ValueError("转发动态没有原动态")
|
||||
|
||||
orig = raw_post.orig
|
||||
assert orig
|
||||
post.repost = Post(
|
||||
self,
|
||||
content=decode_unicode_escapes(parsed_raw_repost.content),
|
||||
@@ -307,7 +291,7 @@ class Bilibili(NewMessage):
|
||||
|
||||
|
||||
class Bilibililive(StatusChange):
|
||||
categories: ClassVar[dict[Category, str]] = {1: "开播提醒", 2: "标题更新提醒", 3: "下播提醒"}
|
||||
categories = {1: "开播提醒", 2: "标题更新提醒", 3: "下播提醒"}
|
||||
platform_name = "bilibili-live"
|
||||
enable_tag = False
|
||||
enabled = True
|
||||
@@ -457,7 +441,7 @@ class Bilibililive(StatusChange):
|
||||
|
||||
|
||||
class BilibiliBangumi(StatusChange):
|
||||
categories: ClassVar[dict[Category, str]] = {}
|
||||
categories = {}
|
||||
platform_name = "bilibili-bangumi"
|
||||
enable_tag = False
|
||||
enabled = True
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
import random
|
||||
from enum import Enum
|
||||
from functools import wraps
|
||||
import random
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
from collections.abc import Callable, Awaitable
|
||||
from typing_extensions import override, assert_never
|
||||
from typing import TYPE_CHECKING, Generic, Literal, TypeVar
|
||||
from typing_extensions import assert_never, override
|
||||
|
||||
from httpx import URL as HttpxURL
|
||||
from nonebot.log import logger
|
||||
from strenum import StrEnum
|
||||
from nonebot.log import logger
|
||||
from httpx import URL as HttpxURL
|
||||
|
||||
from nonebot_bison.types import Target
|
||||
|
||||
from .fsm import FSM, ActionReturn, Condition, StateGraph, Transition, reset_on_exception
|
||||
from .models import DynRawPost
|
||||
from .fsm import FSM, Condition, StateGraph, Transition, ActionReturn, reset_on_exception
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .platforms import Bilibili
|
||||
@@ -115,7 +115,8 @@ class RetryAddon(Generic[TBilibili]):
|
||||
|
||||
def record_backoff_finish_time(self):
|
||||
self.backoff_finish_time = (
|
||||
datetime.now() + self.backoff_timedelta * self.backoff_count**2
|
||||
datetime.now()
|
||||
+ self.backoff_timedelta * self.backoff_count**2
|
||||
# + timedelta(seconds=random.randint(1, 60)) # jitter
|
||||
)
|
||||
logger.trace(f"set backoff finish time: {self.backoff_finish_time}")
|
||||
|
||||
@@ -1,35 +1,30 @@
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
import random
|
||||
from typing import TYPE_CHECKING, ClassVar, TypeVar
|
||||
from typing_extensions import override
|
||||
from datetime import datetime, timedelta
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
|
||||
from httpx import AsyncClient, Response
|
||||
from httpx import AsyncClient
|
||||
from nonebot import logger, require
|
||||
from playwright.async_api import Cookie
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.config.db_model import Cookie as CookieModel
|
||||
from nonebot_bison.config.db_model import Target
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
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:
|
||||
from .platforms import Bilibili
|
||||
|
||||
if plugin_config.bison_use_browser:
|
||||
require("nonebot_plugin_htmlrender")
|
||||
from nonebot_plugin_htmlrender import get_browser
|
||||
require("nonebot_plugin_htmlrender")
|
||||
from nonebot_plugin_htmlrender import get_browser
|
||||
|
||||
B = TypeVar("B", bound="Bilibili")
|
||||
|
||||
|
||||
class BilibiliClientManager(CookieClientManager):
|
||||
|
||||
_default_cookie_cd = timedelta(seconds=120)
|
||||
current_identified_cookie: CookieModel | None = None
|
||||
_site_name = "bilibili.com"
|
||||
|
||||
async def _get_cookies(self) -> list[Cookie]:
|
||||
browser = await get_browser()
|
||||
@@ -64,43 +59,6 @@ class BilibiliClientManager(CookieClientManager):
|
||||
)
|
||||
return cookie
|
||||
|
||||
def _generate_hook(self, cookie: CookieModel) -> Callable:
|
||||
"""hook 函数生成器,用于回写请求状态到数据库"""
|
||||
|
||||
async def _response_hook(resp: Response):
|
||||
await resp.aread()
|
||||
if resp.status_code == 200 and "-352" not in resp.text:
|
||||
logger.trace(f"请求成功: {cookie.id} {resp.request.url}")
|
||||
cookie.status = "success"
|
||||
else:
|
||||
logger.warning(f"请求失败: {cookie.id} {resp.request.url}, 状态码: {resp.status_code}")
|
||||
cookie.status = "failed"
|
||||
self.current_identified_cookie = None
|
||||
cookie.last_usage = datetime.now()
|
||||
await config.update_cookie(cookie)
|
||||
|
||||
return _response_hook
|
||||
|
||||
async def _get_next_identified_cookie(self) -> CookieModel | None:
|
||||
"""选择下一个实名 cookie"""
|
||||
cookies = await config.get_cookie(self._site_name, is_anonymous=False)
|
||||
available_cookies = [cookie for cookie in cookies if cookie.last_usage + cookie.cd < datetime.now()]
|
||||
if not available_cookies:
|
||||
return None
|
||||
cookie = min(available_cookies, key=lambda x: x.last_usage)
|
||||
return cookie
|
||||
|
||||
async def _choose_cookie(self, target: Target | None) -> CookieModel:
|
||||
"""选择 cookie 的具体算法"""
|
||||
if self.current_identified_cookie is None:
|
||||
# 若当前没有选定实名 cookie 则尝试获取
|
||||
self.current_identified_cookie = await self._get_next_identified_cookie()
|
||||
if self.current_identified_cookie:
|
||||
# 如果当前有选定的实名 cookie 则直接返回
|
||||
return self.current_identified_cookie
|
||||
# 否则返回匿名 cookie
|
||||
return (await config.get_cookie(self._site_name, is_anonymous=True))[0]
|
||||
|
||||
@override
|
||||
async def refresh_client(self):
|
||||
await self._refresh_anonymous_cookie()
|
||||
@@ -117,7 +75,7 @@ class BilibiliClientManager(CookieClientManager):
|
||||
|
||||
class BilibiliSite(Site):
|
||||
name = "bilibili.com"
|
||||
schedule_setting: ClassVar[dict] = {"seconds": 60}
|
||||
schedule_setting = {"seconds": 60}
|
||||
schedule_type = "interval"
|
||||
client_mgr = BilibiliClientManager
|
||||
require_browser = True
|
||||
@@ -125,11 +83,11 @@ class BilibiliSite(Site):
|
||||
|
||||
class BililiveSite(Site):
|
||||
name = "live.bilibili.com"
|
||||
schedule_setting: ClassVar[dict] = {"seconds": 5}
|
||||
schedule_setting = {"seconds": 5}
|
||||
schedule_type = "interval"
|
||||
|
||||
|
||||
class BiliBangumiSite(Site):
|
||||
name = "bilibili.com/bangumi"
|
||||
schedule_setting: ClassVar[dict] = {"seconds": 30}
|
||||
schedule_setting = {"seconds": 30}
|
||||
schedule_type = "interval"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
from collections.abc import Callable
|
||||
from datetime import timedelta
|
||||
from functools import partial
|
||||
from types import MappingProxyType
|
||||
from typing import TypeAlias
|
||||
from functools import partial
|
||||
from datetime import timedelta
|
||||
from types import MappingProxyType
|
||||
from collections.abc import Callable
|
||||
|
||||
from expiringdictx import ExpiringDict, SimpleCache
|
||||
from hishel import AsyncCacheTransport, AsyncInMemoryStorage, Controller
|
||||
from httpx import AsyncClient, AsyncHTTPTransport
|
||||
from expiringdictx import SimpleCache, ExpiringDict
|
||||
from hishel import Controller, AsyncCacheTransport, AsyncInMemoryStorage
|
||||
|
||||
from .const import DATASOURCE_URL
|
||||
from .models import CeobeSource, CeobeTarget, DataSourceResponse
|
||||
from .utils import process_response
|
||||
from .models import CeobeSource, CeobeTarget, DataSourceResponse
|
||||
|
||||
cache_transport = AsyncCacheTransport(
|
||||
AsyncHTTPTransport(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Literal, NamedTuple, TypeVar
|
||||
from typing import Literal, TypeVar, NamedTuple
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
from collections import defaultdict
|
||||
from datetime import timedelta
|
||||
from typing import ParamSpec
|
||||
from functools import partial
|
||||
from typing import ClassVar, ParamSpec
|
||||
from datetime import timedelta
|
||||
from collections import defaultdict
|
||||
|
||||
from httpx import AsyncClient
|
||||
from nonebot import logger, require
|
||||
from rapidfuzz import fuzz, process
|
||||
|
||||
from nonebot_bison.platform.platform import NewMessage
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
from nonebot_bison.post import Post
|
||||
from nonebot_bison.types import Category, RawPost, Target
|
||||
from nonebot_bison.utils import ClientManager, Site, capture_html
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
from nonebot_bison.types import Target, RawPost, Category
|
||||
from nonebot_bison.utils import Site, ClientManager, capture_html
|
||||
|
||||
from .cache import CeobeCache, CeobeClient, CeobeDataSourceCache
|
||||
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 ..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 .models import CeobeImage, CeobeCookie, CeobeTextPic, CombIdResponse, CookiesResponse, CookieIdResponse
|
||||
|
||||
P = ParamSpec("P")
|
||||
|
||||
@@ -49,7 +49,7 @@ class CeobeCanteenSite(Site):
|
||||
name = "ceobe_canteen"
|
||||
schedule_type = "interval"
|
||||
# lwt の 推荐间隔
|
||||
schedule_setting: ClassVar[dict] = {"seconds": 15}
|
||||
schedule_setting = {"seconds": 15}
|
||||
client_mgr = CeobeCanteenClientManager
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class CeobeCanteen(NewMessage):
|
||||
use_batch: bool = True
|
||||
default_theme: str = "ceobecanteen"
|
||||
|
||||
categories: ClassVar[dict[Category, str]] = {1: "普通", 2: "转发"}
|
||||
categories: dict[Category, str] = {1: "普通", 2: "转发"}
|
||||
|
||||
data_source_cache = CeobeDataSourceCache()
|
||||
|
||||
@@ -213,9 +213,7 @@ class CeobeCanteen(NewMessage):
|
||||
logger.debug(f"snapshot official website url: {url}")
|
||||
|
||||
# /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"
|
||||
)
|
||||
snapshot_selector = "html > body > div:nth-child(1) > div:nth-child(1) > div > div:nth-child(1) > div:nth-child(1) > div" # noqa: E501
|
||||
# /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
|
||||
viewport = {"width": 1024, "height": 19990}
|
||||
|
||||
@@ -3,7 +3,7 @@ from nonebot import logger
|
||||
from nonebot.compat import type_validate_python
|
||||
|
||||
from .exception import CeobeResponseError
|
||||
from .models import CookieIdResponse, ResponseModel
|
||||
from .models import ResponseModel, CookieIdResponse
|
||||
|
||||
|
||||
def process_response(response: Response, parse_model: type[ResponseModel]) -> ResponseModel:
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
from typing import Any, ClassVar
|
||||
from typing import Any
|
||||
|
||||
from httpx import AsyncClient
|
||||
|
||||
from nonebot_bison.post import Post
|
||||
from nonebot_bison.types import RawPost, Target
|
||||
from nonebot_bison.utils import anonymous_site
|
||||
|
||||
from ..post import Post
|
||||
from .platform import NewMessage
|
||||
from ..utils import anonymous_site
|
||||
from ..types import Target, RawPost
|
||||
|
||||
|
||||
class FF14(NewMessage):
|
||||
categories: ClassVar[dict] = {}
|
||||
categories = {}
|
||||
platform_name = "ff14"
|
||||
name = "最终幻想XIV官方公告"
|
||||
enable_tag = False
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import re
|
||||
from typing import Any, ClassVar
|
||||
from typing import Any
|
||||
|
||||
from httpx import AsyncClient
|
||||
|
||||
from nonebot_bison.post import Post
|
||||
from nonebot_bison.types import ApiError, Category, RawPost, Target
|
||||
from nonebot_bison.utils import Site
|
||||
|
||||
from ..post import Post
|
||||
from ..utils import Site
|
||||
from .platform import NewMessage
|
||||
from ..types import Target, RawPost, ApiError
|
||||
|
||||
|
||||
class NcmSite(Site):
|
||||
name = "music.163.com"
|
||||
schedule_type = "interval"
|
||||
schedule_setting: ClassVar[dict] = {"minutes": 1}
|
||||
schedule_setting = {"minutes": 1}
|
||||
|
||||
|
||||
class NcmArtist(NewMessage):
|
||||
categories: ClassVar[dict[Category, str]] = {}
|
||||
categories = {}
|
||||
platform_name = "ncm-artist"
|
||||
enable_tag = False
|
||||
enabled = True
|
||||
@@ -74,7 +73,7 @@ class NcmArtist(NewMessage):
|
||||
|
||||
|
||||
class NcmRadio(NewMessage):
|
||||
categories: ClassVar[dict[Category, str]] = {}
|
||||
categories = {}
|
||||
platform_name = "ncm-radio"
|
||||
enable_tag = False
|
||||
enabled = True
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict
|
||||
from collections.abc import Awaitable, Callable, Collection
|
||||
from dataclasses import dataclass
|
||||
import json
|
||||
import ssl
|
||||
import json
|
||||
import time
|
||||
import typing
|
||||
from typing import Any, ParamSpec, TypeVar
|
||||
from dataclasses import dataclass
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict
|
||||
from typing import Any, TypeVar, ParamSpec
|
||||
from collections.abc import Callable, Awaitable, Collection
|
||||
|
||||
import httpx
|
||||
from httpx import AsyncClient
|
||||
from nonebot.log import logger
|
||||
from nonebot_plugin_saa import PlatformTarget
|
||||
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
from nonebot_bison.post import Post
|
||||
from nonebot_bison.types import Category, RawPost, SubUnit, Tag, Target
|
||||
from nonebot_bison.utils import ProcessContext, Site
|
||||
from ..post import Post
|
||||
from ..utils import Site, ProcessContext
|
||||
from ..plugin_config import plugin_config
|
||||
from ..types import Tag, Target, RawPost, SubUnit, Category
|
||||
|
||||
|
||||
class CategoryNotSupport(Exception):
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import calendar
|
||||
import time
|
||||
from typing import Any, ClassVar
|
||||
import calendar
|
||||
from typing import Any
|
||||
|
||||
from bs4 import BeautifulSoup as bs
|
||||
import feedparser
|
||||
from httpx import AsyncClient
|
||||
from bs4 import BeautifulSoup as bs
|
||||
|
||||
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 ..post import Post
|
||||
from .platform import NewMessage
|
||||
from ..types import Target, RawPost
|
||||
from ..utils import text_similarity
|
||||
from ..utils.site import Site, CookieClientManager
|
||||
|
||||
|
||||
class RssSite(Site):
|
||||
name = "rss"
|
||||
schedule_type = "interval"
|
||||
schedule_setting: ClassVar[dict] = {"seconds": 30}
|
||||
schedule_setting = {"seconds": 30}
|
||||
client_mgr = CookieClientManager.from_name(name)
|
||||
|
||||
|
||||
class RssPost(Post):
|
||||
|
||||
async def get_plain_content(self) -> str:
|
||||
soup = bs(self.content, "html.parser")
|
||||
|
||||
@@ -38,7 +38,7 @@ class RssPost(Post):
|
||||
|
||||
|
||||
class Rss(NewMessage):
|
||||
categories: ClassVar[dict[Category, str]] = {}
|
||||
categories = {}
|
||||
enable_tag = False
|
||||
platform_name = "rss"
|
||||
name = "Rss"
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
from datetime import datetime
|
||||
import json
|
||||
import re
|
||||
from typing import Any, ClassVar
|
||||
from typing_extensions import override
|
||||
import json
|
||||
from typing import Any
|
||||
from datetime import datetime
|
||||
from urllib.parse import unquote
|
||||
from typing_extensions import override
|
||||
|
||||
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 lxml.etree import HTML
|
||||
from httpx import AsyncClient
|
||||
from nonebot.log import logger
|
||||
from bs4 import BeautifulSoup as bs
|
||||
|
||||
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 ..utils import http_client, text_fletten
|
||||
from ..utils.site import Site, CookieClientManager
|
||||
from ..types import Tag, Target, RawPost, ApiError, Category
|
||||
|
||||
_HEADER = {
|
||||
"accept": (
|
||||
@@ -60,12 +59,12 @@ class WeiboClientManager(CookieClientManager):
|
||||
class WeiboSite(Site):
|
||||
name = "weibo.com"
|
||||
schedule_type = "interval"
|
||||
schedule_setting: ClassVar[dict] = {"seconds": 3}
|
||||
schedule_setting = {"seconds": 3}
|
||||
client_mgr = WeiboClientManager
|
||||
|
||||
|
||||
class Weibo(NewMessage):
|
||||
categories: ClassVar[dict[Category, str]] = {
|
||||
categories = {
|
||||
1: "转发",
|
||||
2: "视频",
|
||||
3: "图文",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import nonebot
|
||||
from nonebot import get_plugin_config
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict
|
||||
from pydantic import BaseModel, Field
|
||||
from yarl import URL
|
||||
from nonebot import get_plugin_config
|
||||
from pydantic import Field, BaseModel
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict
|
||||
|
||||
global_config = nonebot.get_driver().config
|
||||
PlatformName = str
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from nonebot_plugin_saa import MessageFactory, MessageSegmentFactory, Text
|
||||
from nonebot_plugin_saa import Text, MessageFactory, MessageSegmentFactory
|
||||
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
from nonebot_bison.utils import text_to_image
|
||||
from ..utils import text_to_image
|
||||
from ..plugin_config import plugin_config
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass, fields
|
||||
import reprlib
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
import reprlib
|
||||
from typing import TYPE_CHECKING
|
||||
from dataclasses import fields, dataclass
|
||||
|
||||
from nonebot.log import logger
|
||||
from nonebot_plugin_saa import MessageSegmentFactory
|
||||
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
from nonebot_bison.theme import theme_manager
|
||||
from nonebot_bison.theme.types import ThemeRenderError, ThemeRenderUnsupportError
|
||||
|
||||
from ..theme import theme_manager
|
||||
from .abstract_post import AbstractPost
|
||||
from ..plugin_config import plugin_config
|
||||
from .protocol import PlainContentSupport
|
||||
from ..theme.types import ThemeRenderError, ThemeRenderUnsupportError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot_bison.platform import Platform
|
||||
from ..platform import Platform
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -32,7 +30,7 @@ class Post(AbstractPost, PlainContentSupport):
|
||||
"""文本内容"""
|
||||
title: str | None = None
|
||||
"""标题"""
|
||||
images: Sequence[str | bytes | Path | BytesIO] | None = None
|
||||
images: list[str | bytes | Path | BytesIO] | None = None
|
||||
"""图片列表"""
|
||||
timestamp: float | None = None
|
||||
"""发布/获取时间戳, 秒"""
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
from .manager import handle_delete_target, handle_insert_new_target, init_scheduler, scheduler_dict
|
||||
from .manager import init_scheduler, scheduler_dict, handle_delete_target, handle_insert_new_target
|
||||
|
||||
__all__ = ["handle_delete_target", "handle_insert_new_target", "init_scheduler", "scheduler_dict"]
|
||||
__all__ = ["init_scheduler", "handle_delete_target", "handle_insert_new_target", "scheduler_dict"]
|
||||
|
||||
@@ -2,15 +2,14 @@ from typing import cast
|
||||
|
||||
from nonebot.log import logger
|
||||
|
||||
from nonebot_bison.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 ..utils import Site
|
||||
from ..config import config
|
||||
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] = {}
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from collections import defaultdict
|
||||
|
||||
from nonebot.log import logger
|
||||
from nonebot_plugin_apscheduler import scheduler
|
||||
from nonebot_plugin_saa.utils.exceptions import NoBotFound
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.send import send_msgs
|
||||
from nonebot_bison.types import SubUnit, Target
|
||||
from nonebot_bison.utils import ClientManager, ProcessContext, Site
|
||||
from nonebot_bison.utils.site import SkipRequestException
|
||||
from nonebot_bison.utils import ClientManager
|
||||
|
||||
from ..config import config
|
||||
from ..send import send_msgs
|
||||
from ..types import Target, SubUnit
|
||||
from ..platform import platform_manager
|
||||
from ..utils import Site, ProcessContext
|
||||
from ..utils.site import SkipRequestException
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
+12
-13
@@ -1,26 +1,25 @@
|
||||
from collections.abc import Callable, Coroutine
|
||||
from functools import partial, wraps
|
||||
import importlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import time
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
from typing import Any, TypeVar
|
||||
from functools import wraps, partial
|
||||
from collections.abc import Callable, Coroutine
|
||||
|
||||
from anyio import open_file
|
||||
from nonebot.compat import model_dump
|
||||
from nonebot.log import logger
|
||||
from nonebot.compat import model_dump
|
||||
|
||||
from nonebot_bison.config.subs_io import subscribes_export, subscribes_import
|
||||
from nonebot_bison.config.subs_io.nbesf_model import v1, v2, v3
|
||||
from nonebot_bison.scheduler.manager import init_scheduler
|
||||
from ..scheduler.manager import init_scheduler
|
||||
from ..config.subs_io.nbesf_model import v1, v2, v3
|
||||
from ..config.subs_io import subscribes_export, subscribes_import
|
||||
|
||||
try:
|
||||
from typing_extensions import ParamSpec
|
||||
|
||||
import anyio
|
||||
from anyio import from_thread, to_thread
|
||||
import click
|
||||
from anyio import to_thread, from_thread
|
||||
except ImportError as e: # pragma: no cover
|
||||
raise ImportError("请使用 `pip install nonebot-bison[cli]` 安装所需依赖") from e
|
||||
|
||||
@@ -128,18 +127,18 @@ async def subs_import(path: str, format: str):
|
||||
import_file_path = Path(path)
|
||||
assert import_file_path.is_file(), "该路径不是文件!"
|
||||
|
||||
async with await open_file(import_file_path, "r", encoding="utf-8") as f:
|
||||
with import_file_path.open("r", encoding="utf-8") as f:
|
||||
match format:
|
||||
case "yaml" | "yml":
|
||||
logger.info("正在从yaml导入...")
|
||||
|
||||
pyyaml = import_yaml_module()
|
||||
import_items = pyyaml.safe_load(await f.read())
|
||||
import_items = pyyaml.safe_load(f)
|
||||
|
||||
case "json":
|
||||
logger.info("正在从json导入...")
|
||||
|
||||
import_items = json.loads(await f.read())
|
||||
import_items = json.load(f)
|
||||
|
||||
case _:
|
||||
raise click.BadParameter(message=f"不支持的导入格式: {format}")
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import asyncio
|
||||
from collections import deque
|
||||
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot.log import logger
|
||||
from nonebot_plugin_saa import AggregatedMessageFactory, MessageFactory, PlatformTarget
|
||||
from nonebot_plugin_saa.auto_select_bot import refresh_bots
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from nonebot_plugin_saa import MessageFactory, PlatformTarget, AggregatedMessageFactory
|
||||
|
||||
from .plugin_config import plugin_config
|
||||
|
||||
@@ -14,8 +14,6 @@ QUEUE: deque[tuple[PlatformTarget, Sendable, int]] = deque()
|
||||
|
||||
MESSGE_SEND_INTERVAL = 1.5
|
||||
|
||||
_MESSAGE_DISPATCH_TASKS: set[asyncio.Task] = set()
|
||||
|
||||
|
||||
async def _do_send(send_target: PlatformTarget, msg: Sendable):
|
||||
try:
|
||||
@@ -61,9 +59,7 @@ async def _send_msgs_dispatch(send_target: PlatformTarget, msg: Sendable):
|
||||
QUEUE.append((send_target, msg, plugin_config.bison_resend_times))
|
||||
# len(QUEUE) before append was 0
|
||||
if len(QUEUE) == 1:
|
||||
task = asyncio.create_task(do_send_msgs())
|
||||
_MESSAGE_DISPATCH_TASKS.add(task)
|
||||
task.add_done_callback(_MESSAGE_DISPATCH_TASKS.discard)
|
||||
asyncio.create_task(do_send_msgs())
|
||||
else:
|
||||
await _do_send(send_target, msg)
|
||||
|
||||
|
||||
@@ -2,25 +2,23 @@ import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
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.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 .del_cookie import do_del_cookie
|
||||
from .del_cookie_target import do_del_cookie_target
|
||||
from .del_sub import do_del_sub
|
||||
from .query_sub import do_query_sub
|
||||
from .utils import admin_permission, common_platform, configurable_to_me, gen_handle_cancel, set_target_user_info
|
||||
|
||||
_COMMAND_DISPATCH_TASKS: set[asyncio.Task] = set()
|
||||
from .add_cookie import do_add_cookie
|
||||
from .del_cookie import do_del_cookie
|
||||
from .add_cookie_target import do_add_cookie_target
|
||||
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(
|
||||
"添加订阅",
|
||||
@@ -151,10 +149,7 @@ async def do_dispatch_command(
|
||||
else:
|
||||
do_del_sub(new_matcher)
|
||||
new_matcher_ins = new_matcher()
|
||||
|
||||
task = asyncio.create_task(new_matcher_ins.run(bot, event, state))
|
||||
_COMMAND_DISPATCH_TASKS.add(task)
|
||||
task.add_done_callback(_COMMAND_DISPATCH_TASKS.discard)
|
||||
asyncio.create_task(new_matcher_ins.run(bot, event, state))
|
||||
|
||||
|
||||
no_permission_matcher = on_command(
|
||||
@@ -172,14 +167,14 @@ async def send_no_permission():
|
||||
|
||||
|
||||
__all__ = [
|
||||
"add_cookie_matcher",
|
||||
"add_cookie_target_matcher",
|
||||
"add_sub_matcher",
|
||||
"common_platform",
|
||||
"del_cookie_matcher",
|
||||
"del_cookie_target_matcher",
|
||||
"add_sub_matcher",
|
||||
"query_sub_matcher",
|
||||
"del_sub_matcher",
|
||||
"group_manage_matcher",
|
||||
"no_permission_matcher",
|
||||
"query_sub_matcher",
|
||||
"add_cookie_matcher",
|
||||
"add_cookie_target_matcher",
|
||||
"del_cookie_target_matcher",
|
||||
"del_cookie_matcher",
|
||||
]
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
from json import JSONDecodeError
|
||||
from typing import cast
|
||||
from json import JSONDecodeError
|
||||
|
||||
from nonebot.adapters import Message, MessageTemplate
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
from nonebot.log import logger
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import Arg, ArgPlainText
|
||||
from nonebot.typing import T_State
|
||||
|
||||
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 nonebot.adapters.onebot.v11 import MessageEvent
|
||||
from nonebot.adapters import Message, MessageTemplate
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -76,7 +75,7 @@ def do_add_cookie(add_cookie: type[Matcher]):
|
||||
@add_cookie.handle()
|
||||
async def add_cookie_process(state: T_State):
|
||||
client_mgr = cast(CookieClientManager, scheduler_dict[platform_manager[state["platform"]].site].client_mgr)
|
||||
new_cookie = await client_mgr.add_identified_cookie(state["cookie"], state["cookie_name"])
|
||||
new_cookie = await client_mgr.add_user_cookie(state["cookie"], state["cookie_name"])
|
||||
await add_cookie.finish(
|
||||
f"已添加 Cookie: {new_cookie.cookie_name} 到平台 {state['platform']}"
|
||||
+ "\n请使用“关联cookie”为 Cookie 关联订阅"
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
from nonebot.internal.adapter import MessageTemplate
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import ArgPlainText
|
||||
from nonebot.typing import T_State
|
||||
from nonebot_plugin_saa import MessageFactory
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
from nonebot.internal.adapter import MessageTemplate
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.utils import parse_text
|
||||
|
||||
from .utils import gen_handle_cancel, generate_sub_list_text, only_allow_private
|
||||
from ..config import config
|
||||
from ..utils import parse_text
|
||||
from ..platform import platform_manager
|
||||
from .utils import gen_handle_cancel, only_allow_private, generate_sub_list_text
|
||||
|
||||
|
||||
def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
|
||||
@@ -34,7 +33,8 @@ def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
|
||||
|
||||
@add_cookie_target_matcher.handle()
|
||||
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)
|
||||
associated_cookies = await config.get_cookie(
|
||||
target=state["target"]["target"],
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import contextlib
|
||||
|
||||
from nonebot.adapters import Message, MessageTemplate
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import Arg, ArgPlainText
|
||||
from nonebot.typing import T_State
|
||||
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 nonebot.adapters import Message, MessageTemplate
|
||||
from nonebot_plugin_saa import Text, PlatformTarget, SupportedAdapters
|
||||
|
||||
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 ..platform import Platform, platform_manager, unavailable_paltforms
|
||||
|
||||
|
||||
def do_add_sub(add_sub: type[Matcher]):
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import EventPlainText
|
||||
from nonebot.typing import T_State
|
||||
from nonebot_plugin_saa import MessageFactory
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.utils import parse_text
|
||||
|
||||
from ..config import config
|
||||
from ..utils import parse_text
|
||||
from .utils import gen_handle_cancel, only_allow_private
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import EventPlainText
|
||||
from nonebot.typing import T_State
|
||||
from nonebot_plugin_saa import MessageFactory
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.utils import parse_text
|
||||
|
||||
from ..config import config
|
||||
from ..utils import parse_text
|
||||
from .utils import gen_handle_cancel, only_allow_private
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import Arg, EventPlainText
|
||||
from nonebot.typing import T_State
|
||||
from nonebot_plugin_saa import MessageFactory, PlatformTarget
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.types import Category
|
||||
from nonebot_bison.utils import parse_text
|
||||
|
||||
from ..config import config
|
||||
from ..types import Category
|
||||
from ..utils import parse_text
|
||||
from ..platform import platform_manager
|
||||
from .utils import ensure_user_info, gen_handle_cancel
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import Arg
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot_plugin_saa import MessageFactory, PlatformTarget
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.types import Category
|
||||
from nonebot_bison.utils import parse_text
|
||||
|
||||
from ..config import config
|
||||
from ..types import Category
|
||||
from ..utils import parse_text
|
||||
from .utils import ensure_user_info
|
||||
from ..platform import platform_manager
|
||||
|
||||
|
||||
def do_query_sub(query_sub: type[Matcher]):
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import contextlib
|
||||
from typing import Annotated
|
||||
from itertools import groupby
|
||||
from operator import attrgetter
|
||||
from typing import Annotated
|
||||
|
||||
from nonebot.adapters import Event
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import Depends, EventPlainText, EventToMe
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.rule import Rule
|
||||
from nonebot.adapters import Event
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.params import Depends, EventToMe, EventPlainText
|
||||
from nonebot_plugin_saa import PlatformTarget, extract_target
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.platform import platform_manager
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
from nonebot_bison.types import Category
|
||||
from nonebot_bison.types import Target as T_Target
|
||||
from nonebot_bison.utils.site import is_cookie_client_manager
|
||||
from ..config import config
|
||||
from ..types import Category
|
||||
from ..types import Target as T_Target
|
||||
from ..platform import platform_manager
|
||||
from ..plugin_config import plugin_config
|
||||
from ..utils.site import is_cookie_client_manager
|
||||
|
||||
|
||||
def _configurable_to_me(to_me: bool = EventToMe()):
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from importlib import import_module
|
||||
from pathlib import Path
|
||||
from pkgutil import iter_modules
|
||||
from importlib import import_module
|
||||
|
||||
from .types import Theme
|
||||
from .registry import theme_manager
|
||||
from .types import Theme, ThemeRegistrationError
|
||||
from .types import ThemeRegistrationError
|
||||
from .types import ThemeRenderError as ThemeRenderError
|
||||
from .types import ThemeRenderUnsupportError as ThemeRenderUnsupportError
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
from typing import ClassVar
|
||||
|
||||
from nonebot import logger
|
||||
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
|
||||
from ..plugin_config import plugin_config
|
||||
from .types import Theme, ThemeRegistrationError
|
||||
|
||||
|
||||
class ThemeManager:
|
||||
__themes: ClassVar[dict[str, Theme]] = {}
|
||||
__themes: dict[str, Theme] = {}
|
||||
|
||||
def register(self, theme: Theme):
|
||||
logger.trace(f"Registering theme: {theme}")
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from dataclasses import dataclass
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
|
||||
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory
|
||||
|
||||
from nonebot_bison.theme import Theme, ThemeRenderError, ThemeRenderUnsupportError
|
||||
from nonebot_bison.theme.utils import web_embed_image
|
||||
from nonebot_bison.utils import text_fletten
|
||||
from nonebot_bison.theme.utils import web_embed_image
|
||||
from nonebot_bison.theme import Theme, ThemeRenderError, ThemeRenderUnsupportError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot_bison.platform.arknights import ArknightsPost
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from collections.abc import Sequence
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
|
||||
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory
|
||||
|
||||
from nonebot_bison.theme import Theme
|
||||
from nonebot_bison.utils import is_pics_mergable, pic_merge
|
||||
from nonebot_bison.utils import pic_merge, is_pics_mergable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot_bison.post import Post
|
||||
@@ -51,7 +50,7 @@ class BasicTheme(Theme):
|
||||
client = await post.platform.ctx.get_client_for_static()
|
||||
msgs: list[MessageSegmentFactory] = [Text(text)]
|
||||
|
||||
pics_group: list[Sequence[str | bytes | Path | BytesIO]] = []
|
||||
pics_group: list[list[str | bytes | Path | BytesIO]] = []
|
||||
if post.images:
|
||||
pics_group.append(post.images)
|
||||
if rp and rp.images:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
|
||||
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory
|
||||
|
||||
from nonebot_bison.utils import pic_merge, is_pics_mergable
|
||||
from nonebot_bison.theme import Theme, ThemeRenderUnsupportError
|
||||
from nonebot_bison.utils import is_pics_mergable, pic_merge
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot_bison.post import Post
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from httpx import AsyncClient
|
||||
import jinja2
|
||||
from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
|
||||
from PIL import Image as PILImage
|
||||
from pydantic import BaseModel
|
||||
from yarl import URL
|
||||
from httpx import AsyncClient
|
||||
from pydantic import BaseModel
|
||||
from PIL import Image as PILImage
|
||||
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory
|
||||
|
||||
from nonebot_bison.compat import model_validator
|
||||
from nonebot_bison.theme import Theme, ThemeRenderError, ThemeRenderUnsupportError
|
||||
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.utils import is_pics_mergable, pic_merge
|
||||
from nonebot_bison.theme import Theme, ThemeRenderError, ThemeRenderUnsupportError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot_bison.post import Post
|
||||
@@ -123,7 +122,7 @@ class CeobeCanteenTheme(Theme):
|
||||
|
||||
@staticmethod
|
||||
async def merge_pics(
|
||||
images: Sequence[str | bytes | Path | BytesIO],
|
||||
images: list[str | bytes | Path | BytesIO],
|
||||
client: AsyncClient,
|
||||
) -> list[str | bytes | Path | BytesIO]:
|
||||
if is_pics_mergable(images):
|
||||
@@ -225,7 +224,7 @@ class CeobeCanteenTheme(Theme):
|
||||
text += f"详情: {post.url}"
|
||||
msgs.append(Text(text))
|
||||
|
||||
pics_group: list[Sequence[str | bytes | Path | BytesIO]] = []
|
||||
pics_group: list[list[str | bytes | Path | BytesIO]] = []
|
||||
if post.images:
|
||||
pics_group.append(post.images)
|
||||
if post.repost and post.repost.images:
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from collections.abc import Sequence
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
|
||||
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory
|
||||
|
||||
from nonebot_bison.post.protocol import HTMLContentSupport
|
||||
from nonebot_bison.theme import Theme, ThemeRenderError
|
||||
from nonebot_bison.utils import is_pics_mergable, pic_merge
|
||||
from nonebot_bison.post.protocol import HTMLContentSupport
|
||||
from nonebot_bison.utils import pic_merge, is_pics_mergable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot_bison.post import Post
|
||||
@@ -32,6 +31,7 @@ class Ht2iTheme(Theme):
|
||||
raise ThemeRenderError(f"渲染文本失败: {e}")
|
||||
|
||||
async def render(self, post: "Post"):
|
||||
|
||||
md_text = ""
|
||||
|
||||
md_text += f"## {post.title}\n\n" if post.title else ""
|
||||
@@ -50,7 +50,9 @@ class Ht2iTheme(Theme):
|
||||
else:
|
||||
rp_content = await rp.get_content()
|
||||
|
||||
md_text += "> \n> " + rp_content if len(rp_content) < 500 else f"{rp_content[:500]}..." + " \n"
|
||||
md_text += (
|
||||
"> \n> " + rp_content if len(rp_content) < 500 else f"{rp_content[:500]}..." + " \n" # noqa: E501
|
||||
) # noqa: E501
|
||||
md_text += "\n\n"
|
||||
|
||||
md_text += f"###### 来源: {post.platform.name} {post.nickname or ''}\n"
|
||||
@@ -66,7 +68,7 @@ class Ht2iTheme(Theme):
|
||||
if urls:
|
||||
msgs.append(Text("\n".join(urls)))
|
||||
|
||||
pics_group: list[Sequence[str | bytes | Path | BytesIO]] = []
|
||||
pics_group: list[list[str | bytes | Path | BytesIO]] = []
|
||||
if post.images:
|
||||
pics_group.append(post.images)
|
||||
if rp and rp.images:
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from nonebot import logger, require
|
||||
from nonebot_plugin_saa import MessageSegmentFactory
|
||||
from pydantic import BaseModel, PrivateAttr
|
||||
from nonebot_plugin_saa import MessageSegmentFactory
|
||||
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
from ..plugin_config import plugin_config
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot_bison.post.abstract_post import AbstractPost
|
||||
from ..post.abstract_post import AbstractPost
|
||||
|
||||
|
||||
class Theme(ABC, BaseModel):
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from base64 import b64encode
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from base64 import b64encode
|
||||
|
||||
from qrcode import constants
|
||||
from qrcode.image.pil import PilImage
|
||||
from qrcode.main import QRCode
|
||||
from qrcode.image.pil import PilImage
|
||||
|
||||
|
||||
def convert_to_qr(data: str, **kwarg) -> bytes:
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import time
|
||||
from typing import Any, Literal, NamedTuple, NewType
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Literal, NewType, NamedTuple
|
||||
|
||||
from httpx import URL
|
||||
from nonebot_plugin_saa import PlatformTarget as SendTarget
|
||||
from pydantic import BaseModel
|
||||
from nonebot_plugin_saa import PlatformTarget as SendTarget
|
||||
|
||||
RawPost = Any
|
||||
Target = NewType("Target", str)
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
import difflib
|
||||
import re
|
||||
import sys
|
||||
from typing import Any, ClassVar
|
||||
import difflib
|
||||
|
||||
from bs4 import BeautifulSoup as bs
|
||||
import nonebot
|
||||
from nonebot.log import default_format, logger
|
||||
from nonebot.plugin import require
|
||||
from nonebot_plugin_saa import Image, MessageSegmentFactory, Text
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from nonebot.log import logger, default_format
|
||||
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory
|
||||
|
||||
from nonebot_bison.plugin_config import plugin_config
|
||||
|
||||
from .context import ProcessContext as ProcessContext
|
||||
from .site import Site as Site
|
||||
from ..plugin_config import plugin_config
|
||||
from .image import pic_merge as pic_merge
|
||||
from .http import http_client as http_client
|
||||
from .image import capture_html as capture_html
|
||||
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 text_to_image as text_to_image
|
||||
from .site import ClientManager as ClientManager
|
||||
from .site import DefaultClientManager as DefaultClientManager
|
||||
from .site import Site as Site
|
||||
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 pic_url_to_image as pic_url_to_image
|
||||
from .site import DefaultClientManager as DefaultClientManager
|
||||
|
||||
|
||||
class Singleton(type):
|
||||
_instances: ClassVar[dict[Any, Any]] = {}
|
||||
_instances = {}
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user