mirror of
https://github.com/suyiiyii/nonebot-bison.git
synced 2026-06-10 04:39:18 +08:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
c84e3538bc
|
|||
|
202b8df0a6
|
|||
|
d75fe36c3b
|
|||
|
fdfad8a6c7
|
|||
|
2c605fc2dc
|
|||
| 630845fe0d | |||
| d850632f88 | |||
| 3b2feb3ee0 | |||
| 347014f0fb | |||
| cfc1f38ecf | |||
| 23ab1b4323 | |||
| 7e88602baa | |||
| bb928ac4b8 | |||
| 8ab60d65d0 | |||
| d10db08395 | |||
| 5fe9712754 | |||
| 97a0f04808 | |||
| 3bdc79162e | |||
| 8f86f61802 | |||
| ab5154d523 | |||
| 77a4dcd70e | |||
| d4f45571b3 | |||
| a671bd0c61 | |||
| 08ba7b0e3b | |||
| 1a0eca4a38 | |||
| ce1c902905 | |||
| 40f9bc817f | |||
| f416b249f7 | |||
| ec6fe2679d | |||
| 19b25552d0 | |||
| 85b5ab3868 |
@@ -4,7 +4,7 @@
|
||||
"features": {
|
||||
"ghcr.io/devcontainers-contrib/features/poetry:2": {}
|
||||
},
|
||||
"postCreateCommand": "poetry config virtualenvs.in-project true && poetry install -E all && poetry run pre-commit install && yarn 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": {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
name: 问题报告
|
||||
description: 我遇到了问题
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
id: environment
|
||||
attributes:
|
||||
value: |
|
||||
## 环境
|
||||
|
||||
- type: input
|
||||
id: nonebot_bison_version
|
||||
attributes:
|
||||
label: nonebot-bison 版本
|
||||
description: 请填写 nonebot-bison 的版本
|
||||
|
||||
- type: input
|
||||
id: nonebot_version
|
||||
attributes:
|
||||
label: nonebot 版本
|
||||
description: 请填写 nonebot 的版本
|
||||
|
||||
- type: dropdown
|
||||
id: installation_method
|
||||
attributes:
|
||||
label: 安装方式
|
||||
description: 请选择安装方式
|
||||
options:
|
||||
- 通过 nb-cli 安装
|
||||
- 使用 poetry/pdm 等现代包管理器安装
|
||||
- 通过 pip install 安装
|
||||
- 克隆或下载项目直接使用
|
||||
|
||||
- type: input
|
||||
id: operating_system
|
||||
attributes:
|
||||
label: 操作系统
|
||||
description: 请填写操作系统
|
||||
|
||||
- type: textarea
|
||||
id: issue_description
|
||||
attributes:
|
||||
label: 问题
|
||||
description: 请在这里描述你遇到的问题
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: 日志
|
||||
description: 请在这里粘贴你的日志
|
||||
render: shell
|
||||
|
||||
- type: checkboxes
|
||||
id: confirmations
|
||||
attributes:
|
||||
label: 确认
|
||||
options:
|
||||
- label: 我搜索过了 issue,但是并没有发现过与我类似的问题
|
||||
required: true
|
||||
- label: 我确认在日志中去掉了敏感信息
|
||||
required: true
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
name: 问题报告
|
||||
about: 我遇到了问题
|
||||
title: ""
|
||||
labels: ""
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
## 环境
|
||||
|
||||
- nonebot-bison 版本:
|
||||
- nonebot 版本:
|
||||
- 安装方式:(以下方式的一种或者其他方式)
|
||||
1. 通过 nb-cli 安装
|
||||
2. 使用 poetry/pdm 等现代包管理器安装
|
||||
3. 通过 pip install 安装
|
||||
4. 克隆或下载项目直接使用
|
||||
- 操作系统:
|
||||
|
||||
## 问题
|
||||
|
||||
请在这里描述你遇到的问题
|
||||
|
||||
## 日志
|
||||
|
||||
```
|
||||
请在这里粘贴你的日志
|
||||
```
|
||||
|
||||
- [ ] 我搜索过了 issue,但是并没有发现过与我类似的问题
|
||||
- [ ] 我确认在日志中去掉了敏感信息
|
||||
@@ -10,5 +10,5 @@ runs:
|
||||
- name: Build Frontend
|
||||
shell: bash
|
||||
run: |
|
||||
yarn install
|
||||
yarn docs:build
|
||||
pnpm install
|
||||
pnpm docs:build
|
||||
|
||||
@@ -11,5 +11,5 @@ runs:
|
||||
shell: bash
|
||||
working-directory: ./admin-frontend
|
||||
run: |
|
||||
yarn install
|
||||
yarn build
|
||||
pnpm install
|
||||
pnpm build
|
||||
|
||||
@@ -8,11 +8,16 @@ runs:
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Set Up Pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- id: pnpm-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(pnpm store path)"
|
||||
shell: bash
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
path: ${{ steps.pnpm-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
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@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: smoke-test
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
run: poetry run pytest --cov-report xml --cov=./nonebot_bison -k 'not compare' -n auto
|
||||
|
||||
- name: Upload coverage report
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: all-test
|
||||
|
||||
@@ -7,23 +7,23 @@ ci:
|
||||
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.6.3
|
||||
rev: v0.7.2
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix, --exit-non-zero-on-fix]
|
||||
stages: [commit]
|
||||
stages: [pre-commit]
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.13.2
|
||||
hooks:
|
||||
- id: isort
|
||||
stages: [commit]
|
||||
stages: [pre-commit]
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 24.8.0
|
||||
rev: 24.10.0
|
||||
hooks:
|
||||
- id: black
|
||||
stages: [commit]
|
||||
stages: [pre-commit]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v4.0.0-alpha.8
|
||||
@@ -31,10 +31,10 @@ repos:
|
||||
- id: prettier
|
||||
types_or: [javascript, jsx, ts, tsx, markdown, yaml, json]
|
||||
exclude: "admin-frontend/"
|
||||
stages: [commit]
|
||||
stages: [pre-commit]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-eslint
|
||||
rev: v9.9.1
|
||||
rev: v9.14.0
|
||||
hooks:
|
||||
- id: eslint
|
||||
additional_dependencies:
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# Change Log
|
||||
|
||||
## 最近更新
|
||||
|
||||
### 新功能
|
||||
|
||||
- :sparkles: B站获取OPUS格式动态 [@AzideCupric](https://github.com/AzideCupric) ([#651](https://github.com/MountainDash/nonebot-bison/pull/651))
|
||||
- :sparkles: 添加 Cookie 组件 [@suyiiyii](https://github.com/suyiiyii) ([#633](https://github.com/MountainDash/nonebot-bison/pull/633))
|
||||
|
||||
### Bug 修复
|
||||
|
||||
- :bug: 小刻食堂cdn使用https [@phidiaLam](https://github.com/phidiaLam) ([#650](https://github.com/MountainDash/nonebot-bison/pull/650))
|
||||
|
||||
## v0.9.5
|
||||
|
||||
### 新功能
|
||||
|
||||
- :sparkles: 更新默认UA为Windows平台 [@suyiiyii](https://github.com/suyiiyii) ([#643](https://github.com/MountainDash/nonebot-bison/pull/643))
|
||||
|
||||
### Bug 修复
|
||||
|
||||
- 🐛 修复微博更换长内容接口 [@phidiaLam](https://github.com/phidiaLam) ([#645](https://github.com/MountainDash/nonebot-bison/pull/645))
|
||||
- :bug: 修复B站获取匿名Cookie逻辑 [@suyiiyii](https://github.com/suyiiyii) ([#644](https://github.com/MountainDash/nonebot-bison/pull/644))
|
||||
|
||||
### 文档
|
||||
|
||||
- 📝 小刻食堂剪彩文档 [@phidiaLam](https://github.com/phidiaLam) ([#636](https://github.com/MountainDash/nonebot-bison/pull/636))
|
||||
|
||||
## v0.9.4
|
||||
|
||||
### Bug 修复
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"@testing-library/react": "^16.0.1",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/jest": "^29.5.13",
|
||||
"@types/node": "^20.16.5",
|
||||
"@types/node": "^22.9.0",
|
||||
"@types/react": "^18.3.7",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"react": "^18.3.1",
|
||||
@@ -25,7 +25,7 @@
|
||||
"redux": "^5.0.1",
|
||||
"redux-persist": "^6.0.0",
|
||||
"typescript": "^5.6.2",
|
||||
"web-vitals": "^3.5.2"
|
||||
"web-vitals": "^4.2.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
||||
Generated
+70
-69
@@ -36,8 +36,8 @@ importers:
|
||||
specifier: ^29.5.13
|
||||
version: 29.5.13
|
||||
'@types/node':
|
||||
specifier: ^20.16.5
|
||||
version: 20.16.5
|
||||
specifier: ^22.9.0
|
||||
version: 22.9.0
|
||||
'@types/react':
|
||||
specifier: ^18.3.7
|
||||
version: 18.3.7
|
||||
@@ -58,7 +58,7 @@ importers:
|
||||
version: 6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react-scripts:
|
||||
specifier: 5.0.1
|
||||
version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@types/babel__core@7.1.20)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)(react@18.3.1)(type-fest@0.21.3)(typescript@5.6.2)
|
||||
version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@types/babel__core@7.1.20)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)(react@18.3.1)(type-fest@0.21.3)(typescript@5.6.2)
|
||||
redux:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
@@ -69,8 +69,8 @@ importers:
|
||||
specifier: ^5.6.2
|
||||
version: 5.6.2
|
||||
web-vitals:
|
||||
specifier: ^3.5.2
|
||||
version: 3.5.2
|
||||
specifier: ^4.2.4
|
||||
version: 4.2.4
|
||||
devDependencies:
|
||||
'@testing-library/jest-dom':
|
||||
specifier: ^6.5.0
|
||||
@@ -86,10 +86,10 @@ importers:
|
||||
version: 8.57.1
|
||||
eslint-config-airbnb:
|
||||
specifier: ^19.0.4
|
||||
version: 19.0.4(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.1))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.1))(eslint-plugin-react@7.36.1(eslint@8.57.1))(eslint@8.57.1)
|
||||
version: 19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.1))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.1))(eslint-plugin-react@7.36.1(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-config-airbnb-typescript:
|
||||
specifier: ^18.0.0
|
||||
version: 18.0.0(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1)
|
||||
version: 18.0.0(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1)
|
||||
eslint-import-resolver-typescript:
|
||||
specifier: ^3.6.3
|
||||
version: 3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1)
|
||||
@@ -1362,8 +1362,8 @@ packages:
|
||||
'@types/mime@3.0.1':
|
||||
resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
|
||||
|
||||
'@types/node@20.16.5':
|
||||
resolution: {integrity: sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==}
|
||||
'@types/node@22.9.0':
|
||||
resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==}
|
||||
|
||||
'@types/parse-json@4.0.0':
|
||||
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
|
||||
@@ -2827,6 +2827,7 @@ packages:
|
||||
eslint@8.57.1:
|
||||
resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
|
||||
hasBin: true
|
||||
|
||||
espree@9.6.1:
|
||||
@@ -3208,8 +3209,8 @@ packages:
|
||||
resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
http-proxy-middleware@2.0.6:
|
||||
resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==}
|
||||
http-proxy-middleware@2.0.7:
|
||||
resolution: {integrity: sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@types/express': ^4.17.13
|
||||
@@ -5667,8 +5668,8 @@ packages:
|
||||
wbuf@1.7.3:
|
||||
resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==}
|
||||
|
||||
web-vitals@3.5.2:
|
||||
resolution: {integrity: sha512-c0rhqNcHXRkY/ogGDJQxZ9Im9D19hDihbzSQJrsioex+KnFgmMzBiy57Z1EjkhX/+OjyBpclDCzz2ITtjokFmg==}
|
||||
web-vitals@4.2.4:
|
||||
resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
|
||||
|
||||
webidl-conversions@4.0.2:
|
||||
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
|
||||
@@ -6979,7 +6980,7 @@ snapshots:
|
||||
'@jest/console@27.5.1':
|
||||
dependencies:
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
jest-message-util: 27.5.1
|
||||
jest-util: 27.5.1
|
||||
@@ -6988,7 +6989,7 @@ snapshots:
|
||||
'@jest/console@28.1.3':
|
||||
dependencies:
|
||||
'@jest/types': 28.1.3
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
jest-message-util: 28.1.3
|
||||
jest-util: 28.1.3
|
||||
@@ -7001,7 +7002,7 @@ snapshots:
|
||||
'@jest/test-result': 27.5.1
|
||||
'@jest/transform': 27.5.1
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
emittery: 0.8.1
|
||||
@@ -7035,7 +7036,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/fake-timers': 27.5.1
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
jest-mock: 27.5.1
|
||||
|
||||
'@jest/expect-utils@29.7.0':
|
||||
@@ -7046,7 +7047,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/types': 27.5.1
|
||||
'@sinonjs/fake-timers': 8.1.0
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
jest-message-util: 27.5.1
|
||||
jest-mock: 27.5.1
|
||||
jest-util: 27.5.1
|
||||
@@ -7064,7 +7065,7 @@ snapshots:
|
||||
'@jest/test-result': 27.5.1
|
||||
'@jest/transform': 27.5.1
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
collect-v8-coverage: 1.0.1
|
||||
exit: 0.1.2
|
||||
@@ -7148,7 +7149,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
'@types/yargs': 16.0.9
|
||||
chalk: 4.1.2
|
||||
|
||||
@@ -7157,7 +7158,7 @@ snapshots:
|
||||
'@jest/schemas': 28.1.3
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
'@types/yargs': 17.0.32
|
||||
chalk: 4.1.2
|
||||
|
||||
@@ -7166,7 +7167,7 @@ snapshots:
|
||||
'@jest/schemas': 29.6.3
|
||||
'@types/istanbul-lib-coverage': 2.0.6
|
||||
'@types/istanbul-reports': 3.0.4
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
'@types/yargs': 17.0.32
|
||||
chalk: 4.1.2
|
||||
|
||||
@@ -7435,20 +7436,20 @@ snapshots:
|
||||
'@types/body-parser@1.19.2':
|
||||
dependencies:
|
||||
'@types/connect': 3.4.35
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/bonjour@3.5.10':
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/connect-history-api-fallback@1.3.5':
|
||||
dependencies:
|
||||
'@types/express-serve-static-core': 4.17.31
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/connect@3.4.35':
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/eslint@8.4.10':
|
||||
dependencies:
|
||||
@@ -7463,7 +7464,7 @@ snapshots:
|
||||
|
||||
'@types/express-serve-static-core@4.17.31':
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
'@types/qs': 6.9.7
|
||||
'@types/range-parser': 1.2.4
|
||||
|
||||
@@ -7476,13 +7477,13 @@ snapshots:
|
||||
|
||||
'@types/graceful-fs@4.1.5':
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/html-minifier-terser@6.1.0': {}
|
||||
|
||||
'@types/http-proxy@1.17.9':
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/istanbul-lib-coverage@2.0.6': {}
|
||||
|
||||
@@ -7505,7 +7506,7 @@ snapshots:
|
||||
|
||||
'@types/mime@3.0.1': {}
|
||||
|
||||
'@types/node@20.16.5':
|
||||
'@types/node@22.9.0':
|
||||
dependencies:
|
||||
undici-types: 6.19.8
|
||||
|
||||
@@ -7532,7 +7533,7 @@ snapshots:
|
||||
|
||||
'@types/resolve@1.17.1':
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/retry@0.12.0': {}
|
||||
|
||||
@@ -7545,11 +7546,11 @@ snapshots:
|
||||
'@types/serve-static@1.15.0':
|
||||
dependencies:
|
||||
'@types/mime': 3.0.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/sockjs@0.3.33':
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/stack-utils@2.0.3': {}
|
||||
|
||||
@@ -7559,7 +7560,7 @@ snapshots:
|
||||
|
||||
'@types/ws@8.5.3':
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
'@types/yargs-parser@21.0.3': {}
|
||||
|
||||
@@ -9097,7 +9098,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
source-map: 0.6.1
|
||||
|
||||
eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1):
|
||||
eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.1):
|
||||
dependencies:
|
||||
confusing-browser-globals: 1.0.11
|
||||
eslint: 8.57.1
|
||||
@@ -9106,19 +9107,19 @@ snapshots:
|
||||
object.entries: 1.1.7
|
||||
semver: 6.3.1
|
||||
|
||||
eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1):
|
||||
eslint-config-airbnb-typescript@18.0.0(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2)
|
||||
eslint: 8.57.1
|
||||
eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.1)
|
||||
transitivePeerDependencies:
|
||||
- eslint-plugin-import
|
||||
|
||||
eslint-config-airbnb@19.0.4(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.1))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.1))(eslint-plugin-react@7.36.1(eslint@8.57.1))(eslint@8.57.1):
|
||||
eslint-config-airbnb@19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.1))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.1))(eslint-plugin-react@7.36.1(eslint@8.57.1))(eslint@8.57.1):
|
||||
dependencies:
|
||||
eslint: 8.57.1
|
||||
eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.1)
|
||||
eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||
eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1)
|
||||
eslint-plugin-react: 7.36.1(eslint@8.57.1)
|
||||
@@ -9126,7 +9127,7 @@ snapshots:
|
||||
object.assign: 4.1.4
|
||||
object.entries: 1.1.6
|
||||
|
||||
eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)(jest@27.5.1)(typescript@5.6.2):
|
||||
eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)(jest@27.5.1)(typescript@5.6.2):
|
||||
dependencies:
|
||||
'@babel/core': 7.25.2
|
||||
'@babel/eslint-parser': 7.19.1(@babel/core@7.25.2)(eslint@8.57.1)
|
||||
@@ -9137,7 +9138,7 @@ snapshots:
|
||||
confusing-browser-globals: 1.0.11
|
||||
eslint: 8.57.1
|
||||
eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint@8.57.1)
|
||||
eslint-plugin-import: 2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-plugin-import: 2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||
eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(jest@27.5.1)(typescript@5.6.2)
|
||||
eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1)
|
||||
eslint-plugin-react: 7.36.1(eslint@8.57.1)
|
||||
@@ -9167,7 +9168,7 @@ snapshots:
|
||||
debug: 4.3.5
|
||||
enhanced-resolve: 5.17.1
|
||||
eslint: 8.57.1
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||
fast-glob: 3.3.2
|
||||
get-tsconfig: 4.8.0
|
||||
is-bun-module: 1.1.0
|
||||
@@ -9180,7 +9181,7 @@ snapshots:
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
|
||||
eslint-module-utils@2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
@@ -9191,7 +9192,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
|
||||
eslint-module-utils@2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
@@ -9202,7 +9203,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.8.1(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
|
||||
eslint-module-utils@2.8.1(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
@@ -9220,7 +9221,7 @@ snapshots:
|
||||
lodash: 4.17.21
|
||||
string-natural-compare: 3.0.1
|
||||
|
||||
eslint-plugin-import@2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1):
|
||||
eslint-plugin-import@2.30.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
|
||||
dependencies:
|
||||
'@rtsao/scc': 1.1.0
|
||||
array-includes: 3.1.8
|
||||
@@ -9231,7 +9232,7 @@ snapshots:
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.57.1
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-module-utils: 2.11.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.15.1
|
||||
is-glob: 4.0.3
|
||||
@@ -9259,7 +9260,7 @@ snapshots:
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.57.1
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.15.1
|
||||
is-glob: 4.0.3
|
||||
@@ -9861,7 +9862,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
http-proxy-middleware@2.0.6(@types/express@4.17.14):
|
||||
http-proxy-middleware@2.0.7(@types/express@4.17.14):
|
||||
dependencies:
|
||||
'@types/http-proxy': 1.17.9
|
||||
http-proxy: 1.18.1
|
||||
@@ -10145,7 +10146,7 @@ snapshots:
|
||||
'@jest/environment': 27.5.1
|
||||
'@jest/test-result': 27.5.1
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
co: 4.6.0
|
||||
dedent: 0.7.0
|
||||
@@ -10248,7 +10249,7 @@ snapshots:
|
||||
'@jest/environment': 27.5.1
|
||||
'@jest/fake-timers': 27.5.1
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
jest-mock: 27.5.1
|
||||
jest-util: 27.5.1
|
||||
jsdom: 16.7.0
|
||||
@@ -10263,7 +10264,7 @@ snapshots:
|
||||
'@jest/environment': 27.5.1
|
||||
'@jest/fake-timers': 27.5.1
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
jest-mock: 27.5.1
|
||||
jest-util: 27.5.1
|
||||
|
||||
@@ -10275,7 +10276,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/types': 27.5.1
|
||||
'@types/graceful-fs': 4.1.5
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
anymatch: 3.1.2
|
||||
fb-watchman: 2.0.2
|
||||
graceful-fs: 4.2.11
|
||||
@@ -10294,7 +10295,7 @@ snapshots:
|
||||
'@jest/source-map': 27.5.1
|
||||
'@jest/test-result': 27.5.1
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
co: 4.6.0
|
||||
expect: 27.5.1
|
||||
@@ -10368,7 +10369,7 @@ snapshots:
|
||||
jest-mock@27.5.1:
|
||||
dependencies:
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
|
||||
jest-pnp-resolver@1.2.2(jest-resolve@27.5.1):
|
||||
optionalDependencies:
|
||||
@@ -10406,7 +10407,7 @@ snapshots:
|
||||
'@jest/test-result': 27.5.1
|
||||
'@jest/transform': 27.5.1
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
emittery: 0.8.1
|
||||
graceful-fs: 4.2.11
|
||||
@@ -10457,7 +10458,7 @@ snapshots:
|
||||
|
||||
jest-serializer@27.5.1:
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
graceful-fs: 4.2.11
|
||||
|
||||
jest-snapshot@27.5.1:
|
||||
@@ -10490,7 +10491,7 @@ snapshots:
|
||||
jest-util@27.5.1:
|
||||
dependencies:
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
graceful-fs: 4.2.11
|
||||
@@ -10499,7 +10500,7 @@ snapshots:
|
||||
jest-util@28.1.3:
|
||||
dependencies:
|
||||
'@jest/types': 28.1.3
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
graceful-fs: 4.2.11
|
||||
@@ -10508,7 +10509,7 @@ snapshots:
|
||||
jest-util@29.7.0:
|
||||
dependencies:
|
||||
'@jest/types': 29.6.3
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.9.0
|
||||
graceful-fs: 4.2.11
|
||||
@@ -10538,7 +10539,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/test-result': 27.5.1
|
||||
'@jest/types': 27.5.1
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
jest-util: 27.5.1
|
||||
@@ -10548,7 +10549,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@jest/test-result': 28.1.3
|
||||
'@jest/types': 28.1.3
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
ansi-escapes: 4.3.2
|
||||
chalk: 4.1.2
|
||||
emittery: 0.10.2
|
||||
@@ -10557,19 +10558,19 @@ snapshots:
|
||||
|
||||
jest-worker@26.6.2:
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
jest-worker@27.5.1:
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
|
||||
jest-worker@28.1.3:
|
||||
dependencies:
|
||||
'@types/node': 20.16.5
|
||||
'@types/node': 22.9.0
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
|
||||
@@ -11681,7 +11682,7 @@ snapshots:
|
||||
'@remix-run/router': 1.19.2
|
||||
react: 18.3.1
|
||||
|
||||
react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@types/babel__core@7.1.20)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)(react@18.3.1)(type-fest@0.21.3)(typescript@5.6.2):
|
||||
react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(@types/babel__core@7.1.20)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)(react@18.3.1)(type-fest@0.21.3)(typescript@5.6.2):
|
||||
dependencies:
|
||||
'@babel/core': 7.25.2
|
||||
'@pmmmwh/react-refresh-webpack-plugin': 0.5.9(react-refresh@0.11.0)(type-fest@0.21.3)(webpack-dev-server@4.11.1(webpack@5.94.0))(webpack@5.94.0)
|
||||
@@ -11699,7 +11700,7 @@ snapshots:
|
||||
dotenv: 10.0.0
|
||||
dotenv-expand: 5.1.0
|
||||
eslint: 8.57.1
|
||||
eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1)(jest@27.5.1)(typescript@5.6.2)
|
||||
eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.25.2))(@babel/plugin-transform-react-jsx@7.25.2(@babel/core@7.25.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)(jest@27.5.1)(typescript@5.6.2)
|
||||
eslint-webpack-plugin: 3.2.0(eslint@8.57.1)(webpack@5.94.0)
|
||||
file-loader: 6.2.0(webpack@5.94.0)
|
||||
fs-extra: 10.1.0
|
||||
@@ -12703,7 +12704,7 @@ snapshots:
|
||||
dependencies:
|
||||
minimalistic-assert: 1.0.1
|
||||
|
||||
web-vitals@3.5.2: {}
|
||||
web-vitals@4.2.4: {}
|
||||
|
||||
webidl-conversions@4.0.2: {}
|
||||
|
||||
@@ -12739,7 +12740,7 @@ snapshots:
|
||||
express: 4.19.2
|
||||
graceful-fs: 4.2.11
|
||||
html-entities: 2.3.3
|
||||
http-proxy-middleware: 2.0.6(@types/express@4.17.14)
|
||||
http-proxy-middleware: 2.0.7(@types/express@4.17.14)
|
||||
ipaddr.js: 2.0.1
|
||||
open: 8.4.0
|
||||
p-retry: 4.6.2
|
||||
|
||||
@@ -10,7 +10,6 @@ import WeightConfig from './features/weightConfig/WeightManager';
|
||||
import Home from './pages/Home';
|
||||
import Unauthed from './pages/Unauthed';
|
||||
import CookieManager from './features/cookieManager/CookieManager';
|
||||
import CookieTargetManager from './features/cookieTargetManager/CookieTargetManager';
|
||||
|
||||
function App() {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -53,8 +52,8 @@ function App() {
|
||||
element: <CookieManager />,
|
||||
},
|
||||
{
|
||||
path: 'cookie/:cookieId',
|
||||
element: <CookieTargetManager />,
|
||||
path: 'cookie/:siteName',
|
||||
element: <CookieManager />,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Form, Input, Modal } from '@arco-design/web-react';
|
||||
import { useNewCookieMutation } from './cookieConfigSlice';
|
||||
import { useAppDispatch } from '../../app/hooks';
|
||||
import validateCookie from './cookieValidateReq';
|
||||
|
||||
interface CookieAddModalProps {
|
||||
visible: boolean;
|
||||
setVisible: (arg0: boolean) => void;
|
||||
siteName: string;
|
||||
}
|
||||
|
||||
function CookieAddModal({ visible, setVisible, siteName }: CookieAddModalProps) {
|
||||
const FormItem = Form.Item;
|
||||
const [content, setContent] = useState<string>('');
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
const [newCookie] = useNewCookieMutation();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const onSubmit = () => {
|
||||
const postPromise: ReturnType<typeof newCookie> = newCookie({ siteName, content });
|
||||
setConfirmLoading(true);
|
||||
postPromise.then(() => {
|
||||
setConfirmLoading(false);
|
||||
setVisible(false);
|
||||
setContent('');
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="添加 Cookie"
|
||||
visible={visible}
|
||||
onCancel={() => setVisible(false)}
|
||||
confirmLoading={confirmLoading}
|
||||
onOk={onSubmit}
|
||||
style={{ maxWidth: '90vw' }}
|
||||
>
|
||||
|
||||
<Form autoComplete="off">
|
||||
<FormItem label="站点" required>
|
||||
<Input placeholder="Please enter site name" value={siteName} disabled />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="Cookie"
|
||||
required
|
||||
field="content"
|
||||
hasFeedback
|
||||
rules={[
|
||||
{
|
||||
validator: (value, callback) => new Promise<void>((resolve) => {
|
||||
dispatch(validateCookie(siteName, value))
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
callback();
|
||||
} else {
|
||||
callback('Cookie 格式错误');
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}),
|
||||
},
|
||||
]}
|
||||
|
||||
>
|
||||
<Input.TextArea
|
||||
placeholder="请输入 Cookie"
|
||||
value={content}
|
||||
onChange={setContent}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default CookieAddModal;
|
||||
@@ -0,0 +1,128 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Button, Empty, Form, Input, Modal, Space, Table,
|
||||
} from '@arco-design/web-react';
|
||||
import { useDeleteCookieTargetMutation, useGetCookieTargetsQuery } from './cookieConfigSlice';
|
||||
import { Cookie, CookieTarget } from '../../utils/type';
|
||||
import CookieTargetModal from '../cookieTargetManager/CookieTargetModal';
|
||||
|
||||
interface CookieEditModalProps {
|
||||
visible: boolean;
|
||||
setVisible: (arg0: boolean) => void;
|
||||
cookie: Cookie | null
|
||||
}
|
||||
|
||||
function CookieEditModal({ visible, setVisible, cookie }: CookieEditModalProps) {
|
||||
if (!cookie) {
|
||||
return <Empty />;
|
||||
}
|
||||
const FormItem = Form.Item;
|
||||
// const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
const [deleteCookieTarget] = useDeleteCookieTargetMutation();
|
||||
// 获取 Cookie Target
|
||||
const { data: cookieTargets } = useGetCookieTargetsQuery({ cookieId: cookie.id });
|
||||
|
||||
// 添加 Cookie Target
|
||||
const [showAddCookieTargetModal, setShowAddCookieTargetModal] = useState(false);
|
||||
const handleAddCookieTarget = () => () => {
|
||||
setShowAddCookieTargetModal(true);
|
||||
};
|
||||
|
||||
// 删除 Cookie Target
|
||||
const handleDelete = (record: CookieTarget) => () => {
|
||||
deleteCookieTarget({
|
||||
cookieId: record.cookie_id,
|
||||
target: record.target.target,
|
||||
platformName: record.target.platform_name,
|
||||
});
|
||||
};
|
||||
const columns = [
|
||||
{
|
||||
title: '平台名称',
|
||||
dataIndex: 'target.platform_name',
|
||||
},
|
||||
{
|
||||
title: '订阅名称',
|
||||
dataIndex: 'target.target_name',
|
||||
|
||||
},
|
||||
{
|
||||
title: 'Cookie ID',
|
||||
dataIndex: 'cookie_id',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'op',
|
||||
render: (_: null, record: CookieTarget) => (
|
||||
<Space size="small">
|
||||
<Button type="text" status="danger" onClick={handleDelete(record)}>删除</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="编辑 Cookie"
|
||||
visible={visible}
|
||||
onCancel={() => setVisible(false)}
|
||||
// confirmLoading={confirmLoading}
|
||||
onOk={() => setVisible(false)}
|
||||
style={{ maxWidth: '90vw', minWidth: '50vw' }}
|
||||
>
|
||||
<Form autoComplete="off">
|
||||
<FormItem label="Cookie ID">
|
||||
<Input disabled value={cookie.id.toString()} />
|
||||
</FormItem>
|
||||
<FormItem label="Cookie 名称">
|
||||
<Input value={cookie.cookie_name} disabled />
|
||||
</FormItem>
|
||||
<FormItem label="所属站点">
|
||||
<Input value={cookie.site_name} disabled />
|
||||
</FormItem>
|
||||
<FormItem label="内容">
|
||||
<Input.TextArea
|
||||
value={cookie.content}
|
||||
disabled
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="标签">
|
||||
<Input.TextArea
|
||||
value={JSON.stringify(cookie.tags)}
|
||||
disabled
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="最后使用时间">
|
||||
<Input value={cookie.last_usage.toString()} disabled />
|
||||
</FormItem>
|
||||
<FormItem label="状态">
|
||||
<Input value={cookie.status} disabled />
|
||||
</FormItem>
|
||||
<FormItem label="冷却时间(毫秒)">
|
||||
<Input value={cookie.cd_milliseconds.toString()} disabled />
|
||||
</FormItem>
|
||||
|
||||
</Form>
|
||||
|
||||
<Button type="primary" onClick={handleAddCookieTarget()}>关联 Cookie</Button>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={cookieTargets}
|
||||
rowKey={(record: CookieTarget) => `${record.target.platform_name}-${record.target.target}`}
|
||||
scroll={{ x: true }}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<CookieTargetModal
|
||||
cookie={cookie}
|
||||
visible={showAddCookieTargetModal}
|
||||
setVisible={setShowAddCookieTargetModal}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CookieEditModal;
|
||||
@@ -1,120 +1,111 @@
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Card, Descriptions, Grid, List, Popconfirm, Popover, Typography,
|
||||
Table, TableColumnProps, Typography, Space, Popconfirm,
|
||||
} from '@arco-design/web-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { IconDelete, IconEdit } from '@arco-design/web-react/icon';
|
||||
import { selectSiteConf } from '../globalConf/globalConfSlice';
|
||||
import { useAppSelector } from '../../app/hooks';
|
||||
import { Cookie, SiteConfig } from '../../utils/type';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useGetCookiesQuery, useDeleteCookieMutation } from './cookieConfigSlice';
|
||||
import CookieModal from './CookieModal';
|
||||
import './CookieManager.css';
|
||||
|
||||
interface CookieSite {
|
||||
site: SiteConfig;
|
||||
cookies: Cookie[];
|
||||
}
|
||||
import { Cookie } from '../../utils/type';
|
||||
import CookieAddModal from './CookieAddModal';
|
||||
import CookieEditModal from './CookieEditModal';
|
||||
|
||||
export default function CookieManager() {
|
||||
const siteConf = useAppSelector(selectSiteConf);
|
||||
const { siteName } = useParams();
|
||||
const { data: cookieDict } = useGetCookiesQuery();
|
||||
const cookiesList = cookieDict ? Object.values(cookieDict) : [];
|
||||
const cookieSite = Object.values(siteConf).filter((site) => site.enable_cookie);
|
||||
const cookieSiteList: CookieSite[] = cookieSite.map((site) => ({
|
||||
site,
|
||||
cookies: cookiesList.filter((cookie) => cookie.site_name === site.name),
|
||||
}));
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [siteName, setSiteName] = useState('');
|
||||
const [deleteCookie] = useDeleteCookieMutation();
|
||||
|
||||
const handleAddCookie = (newSiteName: string) => () => {
|
||||
console.log(newSiteName);
|
||||
setSiteName(newSiteName);
|
||||
setShowModal(true);
|
||||
// 添加cookie
|
||||
const [showAddModal, setShowAddModal] = React.useState(false);
|
||||
const handleAddCookie = () => () => {
|
||||
setShowAddModal(true);
|
||||
};
|
||||
|
||||
// 删除cookie
|
||||
const [deleteCookie] = useDeleteCookieMutation();
|
||||
const handleDelCookie = (cookieId: string) => () => {
|
||||
console.log(cookieId);
|
||||
deleteCookie({
|
||||
cookieId,
|
||||
});
|
||||
};
|
||||
|
||||
// 编辑cookie
|
||||
const [showEditModal, setShowEditModal] = React.useState(false);
|
||||
const [editCookie, setEditCookie] = React.useState<Cookie | null>(null);
|
||||
const handleEditCookie = (cookie: Cookie) => () => {
|
||||
setEditCookie(cookie);
|
||||
setShowEditModal(true);
|
||||
};
|
||||
|
||||
let data = [];
|
||||
if (siteName) {
|
||||
data = cookiesList.filter((tSite) => tSite.site_name === siteName);
|
||||
}
|
||||
|
||||
const columns: TableColumnProps[] = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
},
|
||||
{
|
||||
title: 'Cookie 名称',
|
||||
dataIndex: 'cookie_name',
|
||||
},
|
||||
{
|
||||
title: '所属站点',
|
||||
dataIndex: 'site_name',
|
||||
},
|
||||
{
|
||||
title: '最后使用时间',
|
||||
dataIndex: 'last_usage',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
},
|
||||
{
|
||||
title: 'CD',
|
||||
dataIndex: 'cd_milliseconds',
|
||||
}, {
|
||||
title: '操作',
|
||||
dataIndex: 'op',
|
||||
render: (_: null, record: Cookie) => (
|
||||
<Space size="small">
|
||||
<Popconfirm
|
||||
title={`确定删除 Cookie ${record.cookie_name} ?`}
|
||||
onOk={handleDelCookie(record.id.toString())}
|
||||
>
|
||||
<span className="list-actions-icon">
|
||||
{/* <IconDelete /> */}
|
||||
<Button type="text" status="danger">删除</Button>
|
||||
</span>
|
||||
</Popconfirm>
|
||||
<Button type="text" onClick={handleEditCookie(record)}>编辑</Button>
|
||||
</Space>
|
||||
),
|
||||
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography.Title heading={4} style={{ margin: '15px' }}>Cookie 管理</Typography.Title>
|
||||
<div>
|
||||
|
||||
<Grid.Row gutter={20}>
|
||||
{cookieSiteList && cookieSiteList.map(({ cookies, site }) => (
|
||||
<Grid.Col xs={24} sm={12} md={8} lg={6} key={site.name} style={{ margin: '1em 0' }}>
|
||||
<Card
|
||||
title={site.name}
|
||||
extra={(
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleAddCookie(site.name)}
|
||||
>
|
||||
添加
|
||||
</Button>
|
||||
)}
|
||||
>
|
||||
<Typography.Title heading={4} style={{ margin: '15px' }}>Cookie 管理</Typography.Title>
|
||||
|
||||
{cookies.map((cookie) => (
|
||||
<List
|
||||
bordered={false}
|
||||
>
|
||||
<Button
|
||||
style={{ width: '90px', margin: '20px 10px' }}
|
||||
type="primary"
|
||||
onClick={handleAddCookie()}
|
||||
>
|
||||
添加
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<List.Item
|
||||
key={cookie.id}
|
||||
style={{ padding: '20px 0', borderBottom: '1px solid var(--color-fill-3)' }}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
|
||||
<Popover
|
||||
key={cookie.id}
|
||||
title={cookie.friendly_name}
|
||||
content={(
|
||||
<Descriptions
|
||||
column={1}
|
||||
title="Cookie 详情"
|
||||
data={Object.entries(cookie).map((entry) => ({
|
||||
label: entry[0].toString(),
|
||||
value: typeof (entry[1]) === 'object' ? JSON.stringify(entry[1]) : entry[1].toString(),
|
||||
}))}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{cookie.friendly_name}
|
||||
|
||||
</Popover>
|
||||
|
||||
<div style={{ display: 'flex' }}>
|
||||
|
||||
<Link to={`/home/cookie/${cookie.id}`}>
|
||||
<span className="list-actions-icon">
|
||||
<IconEdit />
|
||||
</span>
|
||||
</Link>
|
||||
<Popconfirm
|
||||
title={`确定删除 Cookie ${cookie.friendly_name} ?`}
|
||||
onOk={handleDelCookie(cookie.id.toString())}
|
||||
>
|
||||
<span className="list-actions-icon">
|
||||
<IconDelete />
|
||||
</span>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</List.Item>
|
||||
</List>
|
||||
))}
|
||||
</Card>
|
||||
</Grid.Col>
|
||||
))}
|
||||
</Grid.Row>
|
||||
<CookieModal visible={showModal} setVisible={setShowModal} siteName={siteName} />
|
||||
<Table columns={columns} data={data} />
|
||||
<CookieAddModal visible={showAddModal} setVisible={setShowAddModal} siteName={siteName || ''} />
|
||||
<CookieEditModal visible={showEditModal} setVisible={setShowEditModal} cookie={editCookie} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Form, Input, Modal } from '@arco-design/web-react';
|
||||
import { useNewCookieMutation } from './cookieConfigSlice';
|
||||
|
||||
interface CookieModalProps {
|
||||
visible: boolean;
|
||||
setVisible: (arg0: boolean) => void;
|
||||
siteName: string;
|
||||
}
|
||||
|
||||
function CookieModal({ visible, setVisible, siteName }: CookieModalProps) {
|
||||
const FormItem = Form.Item;
|
||||
const [content, setContent] = useState<string>('');
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
const [newCoookie] = useNewCookieMutation();
|
||||
|
||||
const onSubmit = () => {
|
||||
const postPromise: ReturnType<typeof newCoookie> = newCoookie({ siteName, content });
|
||||
setConfirmLoading(true);
|
||||
postPromise.then(() => {
|
||||
setConfirmLoading(false);
|
||||
setVisible(false);
|
||||
setContent('');
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="添加 Cookie"
|
||||
visible={visible}
|
||||
onCancel={() => setVisible(false)}
|
||||
confirmLoading={confirmLoading}
|
||||
onOk={onSubmit}
|
||||
style={{ maxWidth: '90vw' }}
|
||||
>
|
||||
|
||||
<Form autoComplete="off">
|
||||
<FormItem label="Site Name" required>
|
||||
<Input placeholder="Please enter site name" value={siteName} disabled />
|
||||
</FormItem>
|
||||
<FormItem label="Content" required>
|
||||
<Input.TextArea
|
||||
placeholder="Please enter content"
|
||||
value={content}
|
||||
onChange={setContent}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default CookieModal;
|
||||
@@ -40,8 +40,8 @@ export const cookieTargetApi = createApi({
|
||||
baseQuery: baseQueryWithAuth,
|
||||
tagTypes: ['CookieTarget'],
|
||||
endpoints: (builder) => ({
|
||||
getCookieTargets: builder.query<CookieTarget[], {cookieId: number }>({
|
||||
query: (cookieId) => `/cookie_target?cookie_id=${cookieId}`,
|
||||
getCookieTargets: builder.query<CookieTarget[], { cookieId: number }>({
|
||||
query: ({ cookieId }) => `/cookie_target?cookie_id=${cookieId}`,
|
||||
providesTags: ['CookieTarget'],
|
||||
}),
|
||||
newCookieTarget: builder.mutation<StatusResp, NewCookieTargetParam>({
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { AppThunk } from '../../app/store';
|
||||
import { baseUrl } from '../../utils/urls';
|
||||
|
||||
// eslint-disable-next-line
|
||||
export const validCookie =
|
||||
(siteName: string, content: string): AppThunk<Promise<string>> => async (_, getState) => {
|
||||
const url = `${baseUrl}cookie/validate?site_name=${siteName}&content=${content}`;
|
||||
const state = getState();
|
||||
const authToken = state.auth.token;
|
||||
const res = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
method: 'POST',
|
||||
});
|
||||
const resObj = await res.json();
|
||||
return resObj.ok;
|
||||
};
|
||||
|
||||
export default validCookie;
|
||||
@@ -1,74 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import {
|
||||
Button, Space, Table, Typography,
|
||||
} from '@arco-design/web-react';
|
||||
import { useDeleteCookieTargetMutation, useGetCookieTargetsQuery } from '../cookieManager/cookieConfigSlice';
|
||||
import { CookieTarget } from '../../utils/type';
|
||||
import CookieTargetModal from './CookieTargetModal';
|
||||
|
||||
export default function () {
|
||||
const { cookieId } = useParams();
|
||||
const { data: cookieTargets } = useGetCookieTargetsQuery(cookieId);
|
||||
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [deleteCookieTarget] = useDeleteCookieTargetMutation();
|
||||
const handleAdd = () => {
|
||||
console.log('before', showModal);
|
||||
setShowModal(true);
|
||||
console.log('after', showModal);
|
||||
};
|
||||
const handleDelete = (record: CookieTarget) => () => {
|
||||
deleteCookieTarget({
|
||||
cookieId: record.cookieId,
|
||||
target: record.target.target,
|
||||
platformName: record.target.platform_name,
|
||||
});
|
||||
};
|
||||
const columns = [
|
||||
{
|
||||
title: '平台名称',
|
||||
dataIndex: 'target.platform_name',
|
||||
},
|
||||
{
|
||||
title: '订阅名称',
|
||||
dataIndex: 'target.target_name',
|
||||
|
||||
},
|
||||
{
|
||||
title: 'Cookie ID',
|
||||
dataIndex: 'cookie_id',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'op',
|
||||
render: (_: null, record: CookieTarget) => (
|
||||
<Space size="small">
|
||||
<Button type="text" status="danger" onClick={handleDelete(record)}>删除</Button>
|
||||
</Space>
|
||||
),
|
||||
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>
|
||||
<Typography.Title heading={3}>{`Cookie ${cookieId}`}</Typography.Title>
|
||||
</span>
|
||||
<Button style={{ width: '90px', margin: '20px 10px' }} type="primary" onClick={handleAdd}>添加</Button>
|
||||
<Table
|
||||
columns={columns}
|
||||
data={cookieTargets}
|
||||
rowKey={(record: CookieTarget) => `${record.target.platform_name}-${record.target.target}`}
|
||||
scroll={{ x: true }}
|
||||
/>
|
||||
<CookieTargetModal
|
||||
key={cookieId}
|
||||
visible={showModal}
|
||||
setVisible={setShowModal}
|
||||
cookieId={cookieId}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +1,40 @@
|
||||
import React
|
||||
from 'react';
|
||||
import { Modal, Select } from '@arco-design/web-react';
|
||||
import { SubscribeConfig, SubscribeGroupDetail } from '../../utils/type';
|
||||
import {
|
||||
Empty, Form, Modal, Select,
|
||||
} from '@arco-design/web-react';
|
||||
import { Cookie, SubscribeConfig, SubscribeGroupDetail } from '../../utils/type';
|
||||
import { useNewCookieTargetMutation } from '../cookieManager/cookieConfigSlice';
|
||||
import { useGetSubsQuery } from '../subsribeConfigManager/subscribeConfigSlice';
|
||||
import { useAppSelector } from '../../app/hooks';
|
||||
import { selectPlatformConf } from '../globalConf/globalConfSlice';
|
||||
|
||||
interface SubscribeModalProp {
|
||||
cookieId: number;
|
||||
cookie:Cookie| null
|
||||
visible: boolean;
|
||||
setVisible: (arg0: boolean) => void;
|
||||
}
|
||||
|
||||
export default function ({ cookieId, visible, setVisible }: SubscribeModalProp) {
|
||||
export default function CookieTargetModal({
|
||||
cookie, visible, setVisible,
|
||||
}: SubscribeModalProp) {
|
||||
if (!cookie) {
|
||||
return <Empty />;
|
||||
}
|
||||
const [newCookieTarget] = useNewCookieTargetMutation();
|
||||
const FormItem = Form.Item;
|
||||
|
||||
// 筛选出当前Cookie支持的平台
|
||||
const platformConf = useAppSelector(selectPlatformConf);
|
||||
const platformThatSiteSupport = Object.values(platformConf).reduce((p, c) => {
|
||||
if (c.siteName in p) {
|
||||
p[c.siteName].push(c.platformName);
|
||||
} else {
|
||||
p[c.siteName] = [c.platformName];
|
||||
}
|
||||
return p;
|
||||
}, {} as Record<string, string[]>);
|
||||
const supportedPlatform = platformThatSiteSupport[cookie.site_name];
|
||||
|
||||
const { data: subs } = useGetSubsQuery();
|
||||
const pureSubs:SubscribeConfig[] = subs ? Object.values(subs)
|
||||
@@ -20,18 +42,21 @@ export default function ({ cookieId, visible, setVisible }: SubscribeModalProp)
|
||||
pv:Array<SubscribeConfig>,
|
||||
cv:SubscribeGroupDetail,
|
||||
) => pv.concat(cv.subscribes), []) : [];
|
||||
const filteredSubs = pureSubs.filter((sub) => supportedPlatform.includes(sub.platformName));
|
||||
const [index, setIndex] = React.useState(-1);
|
||||
|
||||
const handleSubmit = (idx:number) => {
|
||||
const postPromise: ReturnType<typeof newCookieTarget> = newCookieTarget({
|
||||
cookieId,
|
||||
platformName: pureSubs[idx].platformName,
|
||||
target: pureSubs[idx].target,
|
||||
cookieId: cookie.id,
|
||||
platformName: filteredSubs[idx].platformName,
|
||||
target: filteredSubs[idx].target,
|
||||
});
|
||||
postPromise.then(() => {
|
||||
setVisible(false);
|
||||
});
|
||||
};
|
||||
const { Option } = Select;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="关联 Cookie"
|
||||
@@ -39,22 +64,46 @@ export default function ({ cookieId, visible, setVisible }: SubscribeModalProp)
|
||||
onCancel={() => setVisible(false)}
|
||||
onOk={() => handleSubmit(index)}
|
||||
>
|
||||
<Select
|
||||
placeholder="选择要关联的 target"
|
||||
style={{ width: '100%' }}
|
||||
onChange={setIndex}
|
||||
>
|
||||
{pureSubs.length
|
||||
&& pureSubs.map((sub, idx) => (
|
||||
|
||||
<Form>
|
||||
<FormItem label="平台">
|
||||
|
||||
<Select
|
||||
placeholder="选择要关联的平台"
|
||||
style={{ width: '100%' }}
|
||||
onChange={setIndex}
|
||||
>
|
||||
{supportedPlatform.length
|
||||
&& supportedPlatform.map((sub, idx) => (
|
||||
<Option
|
||||
key={JSON.stringify(sub)}
|
||||
value={idx}
|
||||
>
|
||||
{JSON.stringify(sub)}
|
||||
{sub}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Select>
|
||||
|
||||
</FormItem>
|
||||
<FormItem label="订阅目标" required>
|
||||
<Select
|
||||
placeholder="选择要关联的订阅目标"
|
||||
style={{ width: '100%' }}
|
||||
onChange={setIndex}
|
||||
>
|
||||
{filteredSubs.length
|
||||
&& filteredSubs.map((sub, idx) => (
|
||||
<Option
|
||||
key={JSON.stringify(sub)}
|
||||
value={idx}
|
||||
>
|
||||
{sub.targetName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</FormItem>
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from 'react-router-dom';
|
||||
import { useAppSelector } from '../app/hooks';
|
||||
import { selectIsLogin } from '../features/auth/authSlice';
|
||||
import { selectSiteConf } from '../features/globalConf/globalConfSlice';
|
||||
|
||||
export default function Home() {
|
||||
const location = useLocation();
|
||||
@@ -38,7 +39,7 @@ export default function Home() {
|
||||
} else if (path.startsWith('/home/groups/')) {
|
||||
currentKey = 'subs';
|
||||
} else if (path.startsWith('/home/cookie/')) {
|
||||
currentKey = 'cookie';
|
||||
currentKey = path.substring(6);
|
||||
}
|
||||
|
||||
const [selectedTab, changeSelectTab] = useState(currentKey);
|
||||
@@ -51,6 +52,8 @@ export default function Home() {
|
||||
navigate('/home/weight');
|
||||
} else if (tab === 'cookie') {
|
||||
navigate('/home/cookie');
|
||||
} else if (tab.startsWith('cookie/')) {
|
||||
navigate(`/home/${tab}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -103,6 +106,10 @@ export default function Home() {
|
||||
</Breadcrumb>
|
||||
);
|
||||
}
|
||||
const MenuItem = Menu.Item;
|
||||
const { SubMenu } = Menu;
|
||||
const siteConf = useAppSelector(selectSiteConf);
|
||||
|
||||
return (
|
||||
<Layout className="layout-collapse-demo">
|
||||
<Layout.Header>
|
||||
@@ -125,15 +132,25 @@ export default function Home() {
|
||||
<IconRobot />
|
||||
订阅管理
|
||||
</Menu.Item>
|
||||
<Menu.Item key="cookie">
|
||||
<IconIdcard />
|
||||
Cookie 管理
|
||||
</Menu.Item>
|
||||
<SubMenu
|
||||
key="cookie"
|
||||
title={(
|
||||
<>
|
||||
<IconIdcard />
|
||||
Cookie 管理
|
||||
</>
|
||||
)}
|
||||
>
|
||||
{Object.values(siteConf).filter((site) => site.enable_cookie).map((site) => (
|
||||
<MenuItem key={`cookie/${site.name}`}>
|
||||
{site.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</SubMenu>
|
||||
<Menu.Item key="weight">
|
||||
<IconDashboard />
|
||||
调度权重
|
||||
</Menu.Item>
|
||||
|
||||
</Menu>
|
||||
</Layout.Sider>
|
||||
<Layout.Content style={{ padding: '0 1em' }}>
|
||||
|
||||
@@ -28,12 +28,13 @@ export interface PlatformConfig {
|
||||
categories: CategoryConfig;
|
||||
enabledTag: boolean;
|
||||
platformName: string;
|
||||
siteName: string;
|
||||
hasTarget: boolean;
|
||||
}
|
||||
|
||||
export interface SiteConfig {
|
||||
name: string
|
||||
enable_cookie: string
|
||||
name: string;
|
||||
enable_cookie: string;
|
||||
}
|
||||
|
||||
export interface SubscribeConfig {
|
||||
@@ -90,7 +91,8 @@ export interface Target {
|
||||
export interface Cookie {
|
||||
id: number;
|
||||
site_name: string;
|
||||
friendly_name: string;
|
||||
content: string;
|
||||
cookie_name: string;
|
||||
last_usage: Date;
|
||||
status: string;
|
||||
cd_milliseconds: number;
|
||||
@@ -101,16 +103,16 @@ export interface Cookie {
|
||||
|
||||
export interface CookieTarget {
|
||||
target: Target;
|
||||
cookieId: number;
|
||||
cookie_id: number;
|
||||
}
|
||||
|
||||
export interface NewCookieParam {
|
||||
siteName: string
|
||||
content: string
|
||||
siteName: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface DelCookieParam {
|
||||
cookieId: string
|
||||
cookieId: string;
|
||||
}
|
||||
|
||||
export interface NewCookieTargetParam {
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ BISON_SKIP_BROWSER_CHECK=false
|
||||
BISON_USE_PIC_MERGE=0
|
||||
BISON_RESEND_TIMES=0
|
||||
BISON_PROXY=
|
||||
BISON_UA=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
|
||||
BISON_UA=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0
|
||||
BISON_SHOW_NETWORK_WARNING=true
|
||||
BISON_PLATFORM_THEME='{}'
|
||||
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
FROM node:20.17.0 as frontend
|
||||
FROM node:22.11.0 as frontend
|
||||
ADD . /app
|
||||
WORKDIR /app/admin-frontend
|
||||
RUN yarn && yarn build
|
||||
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
|
||||
|
||||
@@ -23,11 +23,29 @@ export default navbar([
|
||||
link: "",
|
||||
activeMatch: "^/usage/?$",
|
||||
},
|
||||
{
|
||||
text: "Cookie 使用",
|
||||
icon: "cookie",
|
||||
link: "cookie",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "开发",
|
||||
icon: "flask",
|
||||
link: "/dev/",
|
||||
prefix: "/dev/",
|
||||
children: [
|
||||
{
|
||||
text: "基本开发",
|
||||
icon: "tools",
|
||||
link: "",
|
||||
activeMatch: "^/dev/?$",
|
||||
},
|
||||
{
|
||||
text: "Cookie 开发",
|
||||
icon: "cookie",
|
||||
link: "cookie",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
@@ -80,6 +80,7 @@ export default hopeTheme({
|
||||
sup: true,
|
||||
tabs: true,
|
||||
vPre: true,
|
||||
mermaid: true,
|
||||
|
||||
// 在启用之前安装 chart.js
|
||||
// chart: true,
|
||||
@@ -101,9 +102,6 @@ export default hopeTheme({
|
||||
// 在启用之前安装 mathjax-full
|
||||
// mathjax: true,
|
||||
|
||||
// 在启用之前安装 mermaid
|
||||
// mermaid: true,
|
||||
|
||||
// playground: {
|
||||
// presets: ["ts", "vue"],
|
||||
// },
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ highlights:
|
||||
details: 一个由玩家创造的幻想世界
|
||||
link: https://adsrff.web.sdo.com/web1/
|
||||
|
||||
- title: 小刻食堂 (即将支持)
|
||||
- title: 小刻食堂
|
||||
details: 实时获取鹰角发的动态
|
||||
link: https://www.ceobecanteen.top
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
prev: /usage/install
|
||||
next: /dev/cookie
|
||||
---
|
||||
|
||||
# 基本开发须知
|
||||
|
||||
## 语言以及工具
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
prev: /usage/
|
||||
#next: /dev/cookie
|
||||
---
|
||||
|
||||
# Cookie 开发须知
|
||||
|
||||
本项目将大部分 Cookie 相关逻辑提出到了 Site 及 ClientManger 模块中,只需要继承相关类即可获得使用 Cookie 的能力。
|
||||
|
||||
::: tip
|
||||
|
||||
在开发 Cookie 功能之前,你应该对[基本开发](/dev/#基本开发)有一定的了解。
|
||||
|
||||
:::
|
||||
|
||||
## Cookie 相关的基本概念
|
||||
|
||||
- `nonebot_bison.config.db_model.Cookie`: 用于存储 Cookie 的实体类,包含了 Cookie 的名称、内容、状态等信息
|
||||
- `nonebot_bison.config.db_model.CookieTarget`: 用于存储 Cookie 与订阅的关联关系
|
||||
- `nonebot_bison.utils.site.CookieClientManager`: 添加了 Cookie 功能的 ClientManager,是 Cookie 管理功能的核心,调度 Cookie 的功能就在这里实现
|
||||
|
||||
## 快速上手
|
||||
|
||||
例如,现在有一个这样子的 Site 类:
|
||||
|
||||
```python
|
||||
class WeiboSite(Site):
|
||||
name = "weibo.com"
|
||||
schedule_type = "interval"
|
||||
schedule_setting = {"seconds": 3}
|
||||
```
|
||||
|
||||
简而言之,要让站点获得 Cookie 能力,只需要:
|
||||
|
||||
为 Site 类添加一个`client_mgr`字段,值为`CookieClientManager.from_name(name)`,其中`name`为站点名称,这是默认的 Cookie 管理器。
|
||||
|
||||
```python {5}
|
||||
class WeiboSite(Site):
|
||||
name = "weibo.com"
|
||||
schedule_type = "interval"
|
||||
schedule_setting = {"seconds": 3}
|
||||
client_mgr = CookieClientManager.from_name(name)
|
||||
```
|
||||
|
||||
至此,站点就可以使用 Cookie 了!
|
||||
|
||||
## 更好的体验
|
||||
|
||||
为了给用户提供更好的体验,还可以创建自己的 `ClientManager`:继承 `CookieClientManager` 并重写`validate_cookie`和`get_target_name`方法。
|
||||
|
||||
- `async def validate_cookie(cls, content: str) -> bool`该方法将会在 Cookie 添加时被调用,可以在这里验证 Cookie 的有效性
|
||||
- `async def get_cookie_name(cls, content: str) -> str`该方法将会在验证 Cookie 成功后被调用,可以在这里设置 Cookie 的名字并展示给用户
|
||||
|
||||
## 自定义 Cookie 调度策略
|
||||
|
||||
当默认的 Cookie 调度逻辑无法满足需求时,可以重写`CookieClientManager`的`_choose_cookie`方法。
|
||||
|
||||
目前整体的调度逻辑是:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Scheduler
|
||||
participant Platform
|
||||
participant CookieClientManager
|
||||
participant DB
|
||||
participant Internet
|
||||
|
||||
Scheduler->>Platform: exec_fetch
|
||||
Platform->>Platform: do_fetch_new_post(SubUnit)
|
||||
Platform->>Platform: get_sub_list(Target)
|
||||
Platform->>CookieClientManager: get_client(Target)
|
||||
CookieClientManager->>CookieClientManager: _choose_cookie(Target)
|
||||
CookieClientManager->>DB: get_cookies()
|
||||
CookieClientManager->>CookieClientManager: _assemble_client(Target, cookie)
|
||||
CookieClientManager->>Platform: client
|
||||
Platform->>Internet: client.get(Target)
|
||||
Internet->>Platform: response
|
||||
Platform->>CookieClientManager: _response_hook()
|
||||
CookieClientManager->>DB: update_cookie()
|
||||
|
||||
```
|
||||
|
||||
目前 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_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 中的方式
|
||||
|
||||
::: details 大致流程
|
||||
|
||||
1. `Platfrom` 调用 `CookieClientManager.get_client` 方法,传入 `Target` 对象
|
||||
2. `CookieClientManager` 根据 `Target` 选择一个 `Cookie` 对象
|
||||
3. `CookieClientManager` 调用 `CookieClientManager._assemble_client` 方法,将 Cookie 装配到 `Client` 中
|
||||
4. `Platform` 使用 `Client` 进行请求
|
||||
:::
|
||||
|
||||
简单来说:
|
||||
|
||||
- 如果需要修改 Cookie 的默认参数,可以重写`add_user_cookie`方法,这里设置需要的字段
|
||||
- 如果需要修改选择 Cookie 的逻辑,可以重写`_choose_cookie`方法,使用自己的算法选择合适的 Cookie 并返回
|
||||
- 如果需要自定义 Cookie 的格式,可以重写`valid_cookie`方法,自定义验证 Cookie 的逻辑,并重写`_assemble_client`方法,自定义将 Cookie 装配到 Client 中的逻辑
|
||||
- 如果要在请求结束后做一些操作(例如保存此次请求的结果/状态),可以重写`_response_hook`方法,自定义请求结束后的行为
|
||||
- 如果需要跳过一次请求,可以在 `get_client` 方法中抛出 `SkipRequestException` 异常,调度器会捕获该异常并跳过此次请求
|
||||
|
||||
## 实名 Cookie 和匿名 Cookie
|
||||
|
||||
部分站点所有接口都需要携带 Cookie,对于匿名用户(未登录)也会发放一个临时 Cookie,本项目称为匿名 Cookie。
|
||||
|
||||
在此基础上,我们添加了用户上传 Cookie 的功能,这种 Cookie 本项目称为实名 Cookie。
|
||||
|
||||
匿名 Cookie 和实名 Cookie 在同一个框架下统一调度,实名 Cookie 优先级高于匿名 Cookie。为了调度,Cookie 对象有以下字段:
|
||||
|
||||
```python
|
||||
# 最后使用的时刻
|
||||
last_usage: Mapped[datetime.datetime] = mapped_column(DateTime, default=datetime.datetime(1970, 1, 1))
|
||||
# Cookie 当前的状态
|
||||
status: Mapped[str] = mapped_column(String(20), default="")
|
||||
# 使用一次之后,需要的冷却时间
|
||||
cd_milliseconds: Mapped[int] = mapped_column(default=0)
|
||||
# 是否是通用 Cookie(对所有 Target 都有效)
|
||||
is_universal: Mapped[bool] = mapped_column(default=False)
|
||||
# 是否是匿名 Cookie
|
||||
is_anonymous: Mapped[bool] = mapped_column(default=False)
|
||||
# 标签,扩展用
|
||||
tags: Mapped[dict[str, Any]] = mapped_column(JSON().with_variant(JSONB, "postgresql"), default={})
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- **is_universal**:用于标记 Cookie 是否为通用 Cookie,即对所有 Target 都有效。可以理解为是一种特殊的 target,添加 Cookie 和获取 Cookie 时通过传入参数进行设置。
|
||||
|
||||
- **is_anonymous**:用于标记 Cookie 是否为匿名 Cookie,目前的定义是:可以由程序自动生成的,适用于所有 Target 的 Cookie。目前的逻辑是 bison 启动时,生成一个新的匿名 Cookie 并替换掉原有的匿名 Cookie。
|
||||
|
||||
- **无 Target 平台的 Cookie 处理方式**
|
||||
|
||||
对于不存在 Target 的平台,如小刻食堂,可以重写 add_user_cookie 方法,为用户 Cookie 设置 is_universal 字段。这样,在获取 Client 时,由于传入的 Target 为空,就只会选择 is_universal 的 cookie。实现了无 Target 平台的用户 Cookie 调度。
|
||||
|
||||
## 默认的调度策略
|
||||
|
||||
默认的调度策略在 CookieClientManager 的 `_choose_cookie` 方法中实现:
|
||||
|
||||
```python
|
||||
async def _choose_cookie(self, target: Target | None) -> Cookie:
|
||||
"""选择 cookie 的具体算法"""
|
||||
cookies = await config.get_cookie(self._site_name, target)
|
||||
cookies = (cookie for cookie in cookies if cookie.last_usage + cookie.cd < datetime.now())
|
||||
cookie = min(cookies, key=lambda x: x.last_usage)
|
||||
return cookie
|
||||
```
|
||||
|
||||
简而言之,会选择最近使用时间最早的 Cookie,且不在冷却时间内的 Cookie。
|
||||
|
||||
在默认情况下,匿名 Cookie 的冷却时间为 0,实名 Cookie 的冷却时间为 10 秒。也就是说,调度时,如果没有可用的实名 Cookie,则会选择匿名 Cookie。
|
||||
+16
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
prev: /usage/install
|
||||
next: /usage/easy-use
|
||||
next: /usage/cookie
|
||||
---
|
||||
|
||||
# 全方位了解 Bison 的自行车
|
||||
@@ -272,6 +272,21 @@ Bison 在处理每条推送时,会按照以下规则顺序检查推送中的 T
|
||||
3. **需订阅 Tag** 列表为空
|
||||
- **发送**该推送到群中,检查结束
|
||||
|
||||
#### Cookie 功能
|
||||
|
||||
Bison 支持携带 Cookie 进行请求。
|
||||
|
||||
目前支持的平台有:
|
||||
|
||||
- `rss`: RSS
|
||||
- `weibo`: 新浪微博
|
||||
|
||||
::: warning 使用须知
|
||||
Cookie 全局生效,这意味着,通过你的 Cookie 获取到的内容,可能会被发给其他用户。
|
||||
:::
|
||||
|
||||
管理员可以通过**命令**或**管理后台**给 Bison 设置 Cookie。
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
prev: /usage/
|
||||
next: /usage/install
|
||||
---
|
||||
|
||||
# :cookie: Bison 的自行车电助力装置
|
||||
|
||||
Bison 支持 Cookie 啦,你可以将 Cookie 关联到订阅以获得更好的体验。
|
||||
|
||||
但是,盲目使用 Cookie 功能并不能解决问题,反而可能为你的账号带来风险。请阅读完本文档后再决定是否使用 Cookie 功能。
|
||||
|
||||
::: warning 免责声明
|
||||
Bison 具有良好的风控应对机制,我们会尽力保护你的账户,但是无法保证绝对的安全。
|
||||
|
||||
nonebot-bison 开发者及 MountainDash 社区不对因使用 Cookie 导致的任何问题负责。
|
||||
:::
|
||||
|
||||
## :monocle_face: 什么时候需要 Cookie?
|
||||
|
||||
首先,请确认 Cookie 的使用场景,并了解注意事项。
|
||||
|
||||
在绝大多数情况下,Bison 不需要 Cookie 即可正常工作。但是,部分平台只能够获取有限的内容,此时,Cookie 就可以帮助 Bison 获取更多的内容。
|
||||
|
||||
例如,微博平台可以设置微博为“仅粉丝可见”,正常情况下 Bison 无法获取到这些内容。如果你的账号是该博主的粉丝,那么你可以将你的 Cookie 关联到 Bison,这样 Bison 就可以获取到这些受限内容。
|
||||
|
||||
::: warning 使用须知
|
||||
Cookie 全局生效,这意味着,通过你的 Cookie 获取到的内容,可能会被共享给其他用户。
|
||||
|
||||
当然,Bison 不会将你的 Cookie 透露给其他用户。但是,管理员或其他可以接触的数据库的人员可以看到**所有 Cookie**的内容。所以,在上传 Cookie 之前,请确保管理人员可信。
|
||||
:::
|
||||
|
||||
## :wheelchair: 我该怎么使用 Cookie?
|
||||
|
||||
首先,需要明确的是,因为 Cookie 具有隐私性,所有与 Cookie 相关的操作,仅支持**管理员**通过**私聊**或者通过**WebUI**进行管理。
|
||||
|
||||
目前,使用 Cookie 主要有两个步骤:
|
||||
|
||||
- **添加 Cookie**: 将 Cookie 发给 Bison
|
||||
- **关联 Cookie**: 告诉 Bison,你希望在什么时候使用这个 Cookie
|
||||
|
||||
## :nerd_face: 如何获取 Cookie?
|
||||
|
||||
对于大部分平台,Bison 支持 JSON 格式的 Cookie,你可以通过浏览器的开发者工具获取。
|
||||
|
||||
- RSS: 对于各种 RSS 订阅,你需要自行准备需要的 Cookie,以 JSON 格式添加即可
|
||||
- 微博:Bison 兼容 RSSHub 的 Cookie,以下方法引用自[RSSHub 的文档](https://docs.rsshub.app/zh/deploy/config#%E5%BE%AE%E5%8D%9A)
|
||||
> 1. 打开并登录 https://m.weibo.cn(确保打开页面为手机版,如果强制跳转电脑端可尝试使用可更改 UserAgent 的浏览器插件)
|
||||
> 2. 按下 F12 打开控制台,切换至 Network(网络)面板
|
||||
> 3. 在该网页切换至任意关注分组,并在面板打开最先捕获到的请求(该情形下捕获到的请求路径应包含/feed/group)
|
||||
> 4. 查看该请求的 Headers(请求头), 找到 Cookie 字段并复制内容
|
||||
- Bilibili: Bison 兼容 RSSHub 的 Cookie,以下方法引用自[RSSHub 的文档](https://docs.rsshub.app/zh/deploy/config#bilibili)
|
||||
> 1. 打开 https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new?uid=0&type=8
|
||||
> 2. 打开控制台,切换到 Network 面板,刷新
|
||||
> 3. 点击 dynamic_new 请求,找到 Cookie
|
||||
> 4. 视频和专栏,UP 主粉丝及关注只要求 SESSDATA 字段,动态需复制整段 Cookie
|
||||
|
||||
## :sparkles: 给 Bison 添加 Cookie
|
||||
|
||||
打开 Bison 的私聊,发送 `添加cookie` 命令,Bison 会开始添加 Cookie 流程。
|
||||

|
||||
|
||||
然后,依次输入平台名称和 Cookie 内容。
|
||||

|
||||
|
||||
看到 Bison 的回复之后,Cookie 就添加成功啦!
|
||||
|
||||
## :children_crossing: 关联 Cookie 到具体的订阅
|
||||
|
||||
接下来要关联 Cookie 到一个具体的订阅。
|
||||
|
||||
输入 `添加关联cookie` 命令,Bison 就会列出当前所有的订阅。
|
||||
|
||||
我们选择一个订阅,Bison 会列出所有的可以选择的 Cookie。
|
||||
|
||||

|
||||
|
||||
再选择需要关联的 Cookie。
|
||||
|
||||
至此,Bison 便会携带我们的 Cookie 去请求订阅目标啦!
|
||||
|
||||
## :stethoscope: 取消关联 Cookie
|
||||
|
||||
如果你想取消关联某个 Cookie,可以发送 `取消关联cookie` 命令,Bison 会列出所有已被关联的订阅和 Cookie。
|
||||
|
||||
选择需要取消关联的 Cookie,Bison 会取消此 Cookie 对该订阅的关联。
|
||||
|
||||
这是 `添加关联cookie` 的逆向操作。
|
||||
|
||||
## :wastebasket: 删除 Cookie
|
||||
|
||||
如果你想删除某个 Cookie,可以发送 `删除cookie` 命令,Bison 会列出所有已添加的 Cookie。
|
||||
|
||||
选择需要删除的 Cookie,Bison 会删除此 Cookie。
|
||||
|
||||
::: tip
|
||||
只能删除未被关联的 Cookie。
|
||||
|
||||
也就是说,你需要先取消一个 Cookie 的所有关联,才能删除。
|
||||
:::
|
||||
|
||||
这是 `添加cookie` 的逆向操作。
|
||||
|
||||
## :globe_with_meridians: 使用 WebUI 管理 Cookie
|
||||
|
||||
同样的,Bison 提供了一个网页管理 Cookie 的功能,即 WebUI,你可以在网页上查看、添加、删除 Cookie。
|
||||
|
||||
使用方法参见 [使用网页管理订阅](/usage/easy-use#使用网页管理订阅)。
|
||||
|
||||
## :tada: 完成!
|
||||
|
||||
至此,你已经掌握了使用 Cookie 的方法。
|
||||
|
||||
Congratulations! 🎉
|
||||
@@ -61,7 +61,7 @@ def init_fastapi(driver: "Driver"):
|
||||
|
||||
|
||||
def register_get_token_handler():
|
||||
get_token = on_command("后台管理", rule=to_me(), priority=5, aliases={"管理后台"})
|
||||
get_token = on_command("后台管理", rule=to_me(), priority=5, aliases={"管理后台"}, block=True)
|
||||
|
||||
@get_token.handle()
|
||||
async def send_token(bot: "Bot", event: PrivateMessageEvent, state: T_State):
|
||||
|
||||
@@ -12,15 +12,17 @@ from fastapi.security.oauth2 import OAuth2PasswordBearer
|
||||
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 ..platform import site_manager, platform_manager
|
||||
from ..utils.site import CookieClientManager, is_cookie_client_manager
|
||||
from ..utils.site import CookieClientManager, site_manager, is_cookie_client_manager
|
||||
from ..config import NoSuchUserException, NoSuchTargetException, NoSuchSubscribeException, config
|
||||
from .types import (
|
||||
Cookie,
|
||||
Target,
|
||||
TokenResp,
|
||||
GlobalConf,
|
||||
SiteConfig,
|
||||
@@ -66,7 +68,7 @@ async def get_global_conf() -> GlobalConf:
|
||||
platformName=platform_name,
|
||||
categories=platform.categories,
|
||||
enabledTag=platform.enable_tag,
|
||||
site_name=platform.site.name,
|
||||
siteName=platform.site.name,
|
||||
name=platform.name,
|
||||
hasTarget=getattr(platform, "has_target"),
|
||||
)
|
||||
@@ -210,13 +212,13 @@ async def update_weigth_config(platformName: str, target: str, weight_config: We
|
||||
|
||||
|
||||
@router.get("/cookie", dependencies=[Depends(check_is_superuser)])
|
||||
async def get_cookie(site_name: str = None, target: str = None) -> list[Cookie]:
|
||||
# todo: 调用 client_mgr 来添加cookie,以校验和获取cookie_name
|
||||
async def get_cookie(site_name: str | None = None, target: str | None = None) -> list[Cookie]:
|
||||
cookies_in_db = await config.get_cookie(site_name, is_anonymous=False)
|
||||
return [
|
||||
Cookie(
|
||||
id=cookies_in_db[i].id,
|
||||
friendly_name=cookies_in_db[i].cookie_name,
|
||||
content=cookies_in_db[i].content,
|
||||
cookie_name=cookies_in_db[i].cookie_name,
|
||||
site_name=cookies_in_db[i].site_name,
|
||||
last_usage=cookies_in_db[i].last_usage,
|
||||
status=cookies_in_db[i].status,
|
||||
@@ -231,7 +233,7 @@ async def get_cookie(site_name: str = None, target: str = None) -> list[Cookie]:
|
||||
|
||||
@router.post("/cookie", dependencies=[Depends(check_is_superuser)])
|
||||
async def add_cookie(site_name: str, content: str) -> StatusResp:
|
||||
client_mgr = cast(CookieClientManager, site_manager[site_name].client_mgr)
|
||||
client_mgr = cast(CookieClientManager, scheduler_dict[site_manager[site_name]].client_mgr)
|
||||
await client_mgr.add_user_cookie(content)
|
||||
return StatusResp(ok=True, msg="")
|
||||
|
||||
@@ -249,7 +251,12 @@ async def get_cookie_target(
|
||||
cookie_targets = await config.get_cookie_target()
|
||||
# TODO: filter in SQL
|
||||
return [
|
||||
x
|
||||
CookieTarget(
|
||||
target=Target(
|
||||
platform_name=x.target.platform_name, target_name=x.target.target_name, target=x.target.target
|
||||
),
|
||||
cookie_id=x.cookie.id,
|
||||
)
|
||||
for x in cookie_targets
|
||||
if (site_name is None or x.cookie.site_name == site_name)
|
||||
and (target is None or x.target.target == target)
|
||||
@@ -258,12 +265,21 @@ async def get_cookie_target(
|
||||
|
||||
|
||||
@router.post("/cookie_target", dependencies=[Depends(check_is_superuser)])
|
||||
async def add_cookie_target(platform_name: str, target: str, cookie_id: int) -> StatusResp:
|
||||
async def add_cookie_target(platform_name: str, target: T_Target, cookie_id: int) -> StatusResp:
|
||||
await config.add_cookie_target(target, platform_name, cookie_id)
|
||||
return StatusResp(ok=True, msg="")
|
||||
|
||||
|
||||
@router.delete("/cookie_target", dependencies=[Depends(check_is_superuser)])
|
||||
async def del_cookie_target(platform_name: str, target: str, cookie_id: int) -> StatusResp:
|
||||
async def del_cookie_target(platform_name: str, target: T_Target, cookie_id: int) -> StatusResp:
|
||||
await config.delete_cookie_target(target, platform_name, cookie_id)
|
||||
return StatusResp(ok=True, msg="")
|
||||
|
||||
|
||||
@router.post("/cookie/validate", dependencies=[Depends(check_is_superuser)])
|
||||
async def get_cookie_valid(site_name: str, content: str) -> StatusResp:
|
||||
client_mgr = cast(CookieClientManager, scheduler_dict[site_manager[site_name]].client_mgr)
|
||||
if await client_mgr.validate_cookie(content):
|
||||
return StatusResp(ok=True, msg="")
|
||||
else:
|
||||
return StatusResp(ok=False, msg="")
|
||||
|
||||
@@ -6,7 +6,7 @@ class PlatformConfig(BaseModel):
|
||||
categories: dict[int, str]
|
||||
enabledTag: bool
|
||||
platformName: str
|
||||
site_name: str
|
||||
siteName: str
|
||||
hasTarget: bool
|
||||
|
||||
|
||||
@@ -75,7 +75,8 @@ class Target(BaseModel):
|
||||
class Cookie(BaseModel):
|
||||
id: int
|
||||
site_name: str
|
||||
friendly_name: str
|
||||
content: str
|
||||
cookie_name: str
|
||||
last_usage: datetime
|
||||
status: str
|
||||
cd_milliseconds: int
|
||||
|
||||
@@ -285,6 +285,13 @@ class DBConfig:
|
||||
res = [cookie for cookie in res if cookie.id in ids or cookie.is_universal]
|
||||
return res
|
||||
|
||||
async def get_cookie_by_id(self, cookie_id: int) -> Cookie:
|
||||
async with create_session() as sess:
|
||||
cookie = await sess.scalar(select(Cookie).where(Cookie.id == cookie_id))
|
||||
if not cookie:
|
||||
raise NoSuchTargetException(f"cookie {cookie_id} not found")
|
||||
return cookie
|
||||
|
||||
async def add_cookie(self, cookie: Cookie) -> int:
|
||||
async with create_session() as sess:
|
||||
sess.add(cookie)
|
||||
@@ -312,6 +319,8 @@ class DBConfig:
|
||||
.outerjoin(CookieTarget)
|
||||
.options(selectinload(Cookie.targets))
|
||||
)
|
||||
if not cookie:
|
||||
raise NoSuchTargetException(f"cookie {cookie_id} not found")
|
||||
if len(cookie.targets) > 0:
|
||||
raise Exception(f"cookie {cookie.id} in use")
|
||||
await sess.execute(delete(Cookie).where(Cookie.id == cookie_id))
|
||||
@@ -363,5 +372,16 @@ class DBConfig:
|
||||
res.sort(key=lambda x: (x.target.platform_name, x.cookie_id, x.target_id))
|
||||
return res
|
||||
|
||||
async def clear_db(self):
|
||||
"""清空数据库,用于单元测试清理环境"""
|
||||
async with create_session() as sess:
|
||||
await sess.execute(delete(User))
|
||||
await sess.execute(delete(Target))
|
||||
await sess.execute(delete(ScheduleTimeWeight))
|
||||
await sess.execute(delete(Subscribe))
|
||||
await sess.execute(delete(Cookie))
|
||||
await sess.execute(delete(CookieTarget))
|
||||
await sess.commit()
|
||||
|
||||
|
||||
config = DBConfig()
|
||||
|
||||
@@ -8,8 +8,11 @@ from pydantic import BaseModel
|
||||
from nonebot_plugin_saa.registries import AllSupportedPlatformTarget
|
||||
from nonebot.compat import PYDANTIC_V2, ConfigDict, model_dump, type_validate_json, type_validate_python
|
||||
|
||||
from nonebot_bison.types import Tag
|
||||
from nonebot_bison.types import Category
|
||||
from nonebot_bison.types import Target as T_Target
|
||||
|
||||
from ..utils import NBESFParseErr
|
||||
from ....types import Tag, Category
|
||||
from .base import NBESFBase, SubReceipt
|
||||
from ...db_model import Cookie as DBCookie
|
||||
from ...db_config import SubscribeDupException, config
|
||||
@@ -114,7 +117,7 @@ async def magic_cookie_gen(nbesf_data: SubGroup):
|
||||
new_cookie = DBCookie(**model_dump(cookie, exclude={"targets"}))
|
||||
cookie_id = await config.add_cookie(new_cookie)
|
||||
for target in cookie.targets:
|
||||
await config.add_cookie_target(target.target, target.platform_name, cookie_id)
|
||||
await config.add_cookie_target(T_Target(target.target), target.platform_name, cookie_id)
|
||||
except Exception as e:
|
||||
logger.error(f"!添加 Cookie 条目 {repr(cookie)} 失败: {repr(e)}")
|
||||
else:
|
||||
|
||||
@@ -10,6 +10,7 @@ from nonebot.compat import type_validate_python
|
||||
from nonebot_plugin_datastore.db import create_session
|
||||
from sqlalchemy.orm.strategy_options import selectinload
|
||||
|
||||
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
|
||||
@@ -64,13 +65,22 @@ async def subscribes_export(selector: Callable[[Select], Select]) -> v3.SubGroup
|
||||
target_payload = type_validate_python(v3.Target, cookie_target.target)
|
||||
cookie_target_dict[cookie_target.cookie].append(target_payload)
|
||||
|
||||
cookies: list[v3.Cookie] = []
|
||||
for cookie, targets in cookie_target_dict.items():
|
||||
assert isinstance(cookie, Cookie)
|
||||
def cookie_transform(cookie: Cookie, targets: list[v3.Target]) -> v3.Cookie:
|
||||
cookie_dict = row2dict(cookie)
|
||||
cookie_dict["tags"] = cookie.tags
|
||||
cookie_dict["targets"] = targets
|
||||
cookies.append(v3.Cookie(**cookie_dict))
|
||||
return v3.Cookie(**cookie_dict)
|
||||
|
||||
cookies: list[v3.Cookie] = []
|
||||
cookie_set = set()
|
||||
for cookie, targets in cookie_target_dict.items():
|
||||
assert isinstance(cookie, Cookie)
|
||||
cookies.append(cookie_transform(cookie, targets))
|
||||
cookie_set.add(cookie.id)
|
||||
|
||||
# 添加未关联的cookie
|
||||
all_cookies = await config.get_cookie(is_anonymous=False)
|
||||
cookies.extend([cookie_transform(c, []) for c in all_cookies if c.id not in cookie_set])
|
||||
|
||||
sub_group = v3.SubGroup(groups=groups, cookies=cookies)
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
|
||||
class NBESFVerMatchErr(Exception): ...
|
||||
|
||||
|
||||
class NBESFParseErr(Exception): ...
|
||||
|
||||
|
||||
def row2dict(row):
|
||||
def row2dict(row: DeclarativeBase) -> dict[str, Any]:
|
||||
d = {}
|
||||
for column in row.__table__.columns:
|
||||
d[column.name] = str(getattr(row, column.name))
|
||||
|
||||
@@ -3,7 +3,6 @@ from pkgutil import iter_modules
|
||||
from collections import defaultdict
|
||||
from importlib import import_module
|
||||
|
||||
from ..utils import Site
|
||||
from ..plugin_config import plugin_config
|
||||
from .platform import Platform, make_no_target_group
|
||||
|
||||
@@ -36,10 +35,3 @@ def _get_unavailable_platforms() -> dict[str, str]:
|
||||
|
||||
# platform => reason for not available
|
||||
unavailable_paltforms: dict[str, str] = _get_unavailable_platforms()
|
||||
|
||||
|
||||
site_manager: dict[str, type[Site]] = {}
|
||||
for site in Site.registry:
|
||||
if not hasattr(site, "name"):
|
||||
continue
|
||||
site_manager[site.name] = site
|
||||
|
||||
@@ -93,7 +93,7 @@ class Bilibili(NewMessage):
|
||||
@retry_for_352
|
||||
async def get_sub_list(self, target: Target) -> list[DynRawPost]:
|
||||
client = await self.ctx.get_client(target)
|
||||
params = {"host_mid": target, "timezone_offset": -480, "offset": ""}
|
||||
params = {"host_mid": target, "timezone_offset": -480, "offset": "", "features": "itemOpusStyle"}
|
||||
res = await client.get(
|
||||
"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space",
|
||||
params=params,
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import json
|
||||
import random
|
||||
from typing_extensions import override
|
||||
from datetime import datetime, timedelta
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
|
||||
from httpx import AsyncClient
|
||||
from nonebot import logger, require
|
||||
from playwright.async_api import Cookie
|
||||
|
||||
from nonebot_bison.types import Target
|
||||
from nonebot_bison.utils import Site, ClientManager, http_client
|
||||
from nonebot_bison.utils import Site, http_client
|
||||
|
||||
from ...utils.site import CookieClientManager
|
||||
from ...config.db_model import Cookie as CookieModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .platforms import Bilibili
|
||||
@@ -18,45 +22,48 @@ from nonebot_plugin_htmlrender import get_browser
|
||||
B = TypeVar("B", bound="Bilibili")
|
||||
|
||||
|
||||
class BilibiliClientManager(ClientManager):
|
||||
_client: AsyncClient
|
||||
_inited: bool = False
|
||||
class BilibiliClientManager(CookieClientManager):
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._client = http_client()
|
||||
_default_cookie_cd = timedelta(seconds=120)
|
||||
|
||||
async def _get_cookies(self) -> list[Cookie]:
|
||||
browser = await get_browser()
|
||||
async with await browser.new_page() as page:
|
||||
await page.goto(f"https://space.bilibili.com/{random.randint(1, 1000)}/dynamic")
|
||||
await page.wait_for_load_state("load")
|
||||
await page.wait_for_load_state("load") # 等待基本加载完成
|
||||
await page.wait_for_function('document.cookie.includes("bili_ticket")') # 期望保证 GenWebTicket 请求完成
|
||||
await page.wait_for_load_state("networkidle") # 期望保证 ExClimbWuzhi 请求完成
|
||||
cookies = await page.context.cookies()
|
||||
|
||||
return cookies
|
||||
|
||||
async def _reset_client_cookies(self, cookies: list[Cookie]):
|
||||
def _gen_json_cookie(self, cookies: list[Cookie]):
|
||||
cookie_dict = {}
|
||||
for cookie in cookies:
|
||||
self._client.cookies.set(
|
||||
name=cookie.get("name", ""),
|
||||
value=cookie.get("value", ""),
|
||||
domain=cookie.get("domain", ""),
|
||||
path=cookie.get("path", "/"),
|
||||
)
|
||||
cookie_dict[cookie.get("name", "")] = cookie.get("value", "")
|
||||
return cookie_dict
|
||||
|
||||
@override
|
||||
async def _generate_anonymous_cookie(self) -> CookieModel:
|
||||
cookies = await self._get_cookies()
|
||||
cookie = CookieModel(
|
||||
cookie_name=f"{self._site_name} anonymous",
|
||||
site_name=self._site_name,
|
||||
content=json.dumps(self._gen_json_cookie(cookies)),
|
||||
is_universal=True,
|
||||
is_anonymous=True,
|
||||
last_usage=datetime.now(),
|
||||
cd_milliseconds=0,
|
||||
tags="{}",
|
||||
status="",
|
||||
)
|
||||
return cookie
|
||||
|
||||
@override
|
||||
async def refresh_client(self):
|
||||
cookies = await self._get_cookies()
|
||||
await self._reset_client_cookies(cookies)
|
||||
await self._refresh_anonymous_cookie()
|
||||
logger.debug("刷新B站客户端的cookie")
|
||||
|
||||
@override
|
||||
async def get_client(self, target: Target | None) -> AsyncClient:
|
||||
if not self._inited:
|
||||
logger.debug("初始化B站客户端")
|
||||
await self.refresh_client()
|
||||
self._inited = True
|
||||
return self._client
|
||||
|
||||
@override
|
||||
async def get_client_for_static(self) -> AsyncClient:
|
||||
return http_client()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
DATASOURCE_URL = "https://server.ceobecanteen.top/api/v1/canteen/config/datasource/list"
|
||||
COMB_ID_URL = "https://server.ceobecanteen.top/api/v1/canteen/user/getDatasourceComb"
|
||||
COOKIE_ID_URL = "http://cdn.ceobecanteen.top/datasource-comb"
|
||||
COOKIE_ID_URL = "https://cdn.ceobecanteen.top/datasource-comb"
|
||||
COOKIES_URL = "https://server-cdn.ceobecanteen.top/api/v1/cdn/cookie/mainList/cookieList"
|
||||
|
||||
@@ -16,7 +16,7 @@ from nonebot_plugin_saa import PlatformTarget
|
||||
from ..post import Post
|
||||
from ..utils import Site, ProcessContext
|
||||
from ..plugin_config import plugin_config
|
||||
from ..types import Tag, Target, RawPost, SubUnit, Category, RegistryMeta
|
||||
from ..types import Tag, Target, RawPost, SubUnit, Category
|
||||
|
||||
|
||||
class CategoryNotSupport(Exception):
|
||||
@@ -29,6 +29,21 @@ class CategoryNotRecognize(Exception):
|
||||
"""raise in get_category, when you don't know the category of post"""
|
||||
|
||||
|
||||
class RegistryMeta(type):
|
||||
def __new__(cls, name, bases, namespace, **kwargs):
|
||||
return super().__new__(cls, name, bases, namespace)
|
||||
|
||||
def __init__(cls, name, bases, namespace, **kwargs):
|
||||
if kwargs.get("base"):
|
||||
# this is the base class
|
||||
cls.registry = []
|
||||
elif not kwargs.get("abstract"):
|
||||
# this is the subclass
|
||||
cls.registry.append(cls)
|
||||
|
||||
super().__init__(name, bases, namespace, **kwargs)
|
||||
|
||||
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ from ..post import Post
|
||||
from .platform import NewMessage
|
||||
from ..types import Target, RawPost
|
||||
from ..utils import text_similarity
|
||||
from ..utils.site import CookieSite, create_cookie_client_manager
|
||||
from ..utils.site import Site, CookieClientManager
|
||||
|
||||
|
||||
class RssSite(CookieSite):
|
||||
class RssSite(Site):
|
||||
name = "rss"
|
||||
schedule_type = "interval"
|
||||
schedule_setting = {"seconds": 30}
|
||||
client_mgr = create_cookie_client_manager("rss")
|
||||
client_mgr = CookieClientManager.from_name(name)
|
||||
|
||||
|
||||
class RssPost(Post):
|
||||
|
||||
@@ -3,6 +3,7 @@ import json
|
||||
from typing import Any
|
||||
from datetime import datetime
|
||||
from urllib.parse import unquote
|
||||
from typing_extensions import override
|
||||
|
||||
from yarl import URL
|
||||
from lxml.etree import HTML
|
||||
@@ -11,10 +12,10 @@ from nonebot.log import logger
|
||||
from bs4 import BeautifulSoup as bs
|
||||
|
||||
from ..post import Post
|
||||
from ..utils import http_client
|
||||
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
|
||||
from ..utils.site import CookieSite, create_cookie_client_manager
|
||||
|
||||
_HEADER = {
|
||||
"accept": (
|
||||
@@ -36,11 +37,30 @@ _HEADER = {
|
||||
}
|
||||
|
||||
|
||||
class WeiboSite(CookieSite):
|
||||
class WeiboClientManager(CookieClientManager):
|
||||
_site_name = "weibo.com"
|
||||
|
||||
async def _get_current_user_name(self, cookies: dict) -> str:
|
||||
url = "https://m.weibo.cn/setup/nick/detail"
|
||||
async with http_client() as client:
|
||||
r = await client.get(url, headers=_HEADER, cookies=cookies)
|
||||
data = json.loads(r.text)
|
||||
name = data["data"]["user"]["screen_name"]
|
||||
return name
|
||||
|
||||
@override
|
||||
async def get_cookie_name(self, content: str) -> str:
|
||||
"""从cookie内容中获取cookie的友好名字,添加cookie时调用,持久化在数据库中"""
|
||||
name = await self._get_current_user_name(json.loads(content))
|
||||
|
||||
return text_fletten(f"weibo: [{name[:10]}]")
|
||||
|
||||
|
||||
class WeiboSite(Site):
|
||||
name = "weibo.com"
|
||||
schedule_type = "interval"
|
||||
schedule_setting = {"seconds": 3}
|
||||
client_mgr = create_cookie_client_manager(name)
|
||||
client_mgr = WeiboClientManager
|
||||
|
||||
|
||||
class Weibo(NewMessage):
|
||||
@@ -156,7 +176,7 @@ class Weibo(NewMessage):
|
||||
try:
|
||||
client = await self.ctx.get_client()
|
||||
weibo_info = await client.get(
|
||||
"https://m.weibo.cn/statuses/show",
|
||||
"https://m.weibo.cn/statuses/extend",
|
||||
params={"id": weibo_id},
|
||||
headers=_HEADER,
|
||||
)
|
||||
@@ -170,7 +190,7 @@ class Weibo(NewMessage):
|
||||
|
||||
async def _parse_weibo(self, info: dict) -> Post:
|
||||
if info["isLongText"] or info["pic_num"] > 9:
|
||||
info["text"] = (await self._get_long_weibo(info["mid"]))["text"]
|
||||
info["text"] = (await self._get_long_weibo(info["mid"]))["longTextContent"]
|
||||
parsed_text = self._get_text(info["text"])
|
||||
raw_pics_list = info.get("pics", [])
|
||||
pic_urls = [img["large"]["url"] for img in raw_pics_list]
|
||||
|
||||
@@ -36,7 +36,8 @@ class PlugConfig(BaseModel):
|
||||
bison_resend_times: int = 0
|
||||
bison_proxy: str | None = None
|
||||
bison_ua: str = Field(
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)"
|
||||
" Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
|
||||
description="默认UA",
|
||||
)
|
||||
bison_show_network_warning: bool = True
|
||||
|
||||
@@ -33,9 +33,6 @@ async def init_scheduler():
|
||||
else:
|
||||
_schedule_class_platform_dict[site].append(platform_name)
|
||||
for site, target_list in _schedule_class_dict.items():
|
||||
if is_cookie_client_manager(site.client_mgr):
|
||||
client_mgr = cast(CookieClientManager, site.client_mgr)
|
||||
await client_mgr.refresh_anonymous_cookie()
|
||||
if not plugin_config.bison_use_browser and site.require_browser:
|
||||
logger.warning(f"{site.name} requires browser, it will not schedule.")
|
||||
continue
|
||||
@@ -47,6 +44,9 @@ async def init_scheduler():
|
||||
)
|
||||
platform_name_list = _schedule_class_platform_dict[site]
|
||||
scheduler_dict[site] = Scheduler(site, schedulable_args, platform_name_list)
|
||||
if is_cookie_client_manager(site.client_mgr):
|
||||
client_mgr = cast(CookieClientManager, scheduler_dict[site].client_mgr)
|
||||
await client_mgr.refresh_client()
|
||||
config.register_add_target_hook(handle_insert_new_target)
|
||||
config.register_delete_target_hook(handle_delete_target)
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ async def subs_export(path: Path, format: str):
|
||||
export_file = path / f"bison_subscribes_export_{int(time.time())}.{format}"
|
||||
|
||||
logger.info("正在获取订阅信息...")
|
||||
export_data: v2.SubGroup = await subscribes_export(lambda x: x)
|
||||
export_data: v3.SubGroup = await subscribes_export(lambda x: x)
|
||||
|
||||
with export_file.open("w", encoding="utf-8") as f:
|
||||
match format:
|
||||
|
||||
@@ -30,10 +30,12 @@ add_sub_matcher = on_command(
|
||||
add_sub_matcher.handle()(set_target_user_info)
|
||||
do_add_sub(add_sub_matcher)
|
||||
|
||||
|
||||
query_sub_matcher = on_command("查询订阅", rule=configurable_to_me, priority=5, block=True)
|
||||
query_sub_matcher.handle()(set_target_user_info)
|
||||
do_query_sub(query_sub_matcher)
|
||||
|
||||
|
||||
del_sub_matcher = on_command(
|
||||
"删除订阅",
|
||||
rule=configurable_to_me,
|
||||
@@ -151,7 +153,11 @@ async def do_dispatch_command(
|
||||
|
||||
|
||||
no_permission_matcher = on_command(
|
||||
"添加订阅", rule=configurable_to_me, aliases={"删除订阅", "群管理"}, priority=8, block=True
|
||||
"添加订阅",
|
||||
rule=configurable_to_me,
|
||||
aliases={"删除订阅", "群管理", "管理后台", "添加cookie", "删除cookie", "关联cookie", "取消关联cookie"},
|
||||
priority=8,
|
||||
block=True,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
from typing import cast
|
||||
from json import JSONDecodeError
|
||||
|
||||
from nonebot.log import logger
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import Arg, ArgPlainText
|
||||
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 import common_platform, gen_handle_cancel
|
||||
from ..utils.site import CookieClientManager, is_cookie_client_manager
|
||||
from .utils import common_platform, gen_handle_cancel, only_allow_private
|
||||
|
||||
|
||||
def do_add_cookie(add_cookie: type[Matcher]):
|
||||
handle_cancel = gen_handle_cancel(add_cookie, "已中止添加cookie")
|
||||
|
||||
@add_cookie.handle()
|
||||
async def init_promote(state: T_State):
|
||||
async def init_promote(state: T_State, event: MessageEvent):
|
||||
await only_allow_private(event, add_cookie)
|
||||
state["_prompt"] = (
|
||||
"请输入想要添加 Cookie 的平台,目前支持,请输入冒号左边的名称:\n"
|
||||
+ "".join(
|
||||
@@ -42,7 +47,6 @@ def do_add_cookie(add_cookie: type[Matcher]):
|
||||
await add_cookie.finish("已中止添加cookie")
|
||||
elif platform in platform_manager:
|
||||
state["platform"] = platform
|
||||
state["site"] = platform_manager[platform].site
|
||||
else:
|
||||
await add_cookie.reject("平台输入错误")
|
||||
|
||||
@@ -52,18 +56,27 @@ def do_add_cookie(add_cookie: type[Matcher]):
|
||||
|
||||
@add_cookie.got("cookie", MessageTemplate("{_prompt}"), [handle_cancel])
|
||||
async def got_cookie(state: T_State, cookie: Message = Arg()):
|
||||
client_mgr: type[CookieClientManager] = cast(
|
||||
type[CookieClientManager], platform_manager[state["platform"]].site.client_mgr
|
||||
)
|
||||
client_mgr = cast(CookieClientManager, scheduler_dict[platform_manager[state["platform"]].site].client_mgr)
|
||||
cookie_text = cookie.extract_plain_text()
|
||||
if not await client_mgr.validate_cookie(cookie_text):
|
||||
await add_cookie.reject(state["site"].cookie_format_prompt)
|
||||
state["cookie"] = cookie_text
|
||||
await add_cookie.reject(
|
||||
"无效的 Cookie,请检查后重新输入,详情见https://nonebot-bison.netlify.app/usage/cookie.html"
|
||||
)
|
||||
try:
|
||||
cookie_name = await client_mgr.get_cookie_name(cookie_text)
|
||||
state["cookie"] = cookie_text
|
||||
state["cookie_name"] = cookie_name
|
||||
except JSONDecodeError as e:
|
||||
logger.error("获取 Cookie 名称失败" + str(e))
|
||||
await add_cookie.reject(
|
||||
"获取 Cookie 名称失败,请检查后重新输入,详情见https://nonebot-bison.netlify.app/usage/cookie.html"
|
||||
)
|
||||
|
||||
@add_cookie.handle()
|
||||
async def add_cookie_process(state: T_State):
|
||||
client_mgr = cast(CookieClientManager, platform_manager[state["platform"]].site.client_mgr)
|
||||
await client_mgr.add_user_cookie(state["cookie"])
|
||||
client_mgr = cast(CookieClientManager, scheduler_dict[platform_manager[state["platform"]].site].client_mgr)
|
||||
new_cookie = await client_mgr.add_user_cookie(state["cookie"], state["cookie_name"])
|
||||
await add_cookie.finish(
|
||||
f"已添加 Cookie: {state['cookie']} 到平台 {state['platform']}" + "\n请使用“关联cookie”为 Cookie 关联订阅"
|
||||
f"已添加 Cookie: {new_cookie.cookie_name} 到平台 {state['platform']}"
|
||||
+ "\n请使用“关联cookie”为 Cookie 关联订阅"
|
||||
)
|
||||
|
||||
@@ -2,19 +2,21 @@ from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import ArgPlainText
|
||||
from nonebot_plugin_saa import MessageFactory
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
from nonebot.internal.adapter import MessageTemplate
|
||||
|
||||
from ..config import config
|
||||
from ..utils import parse_text
|
||||
from ..platform import platform_manager
|
||||
from .utils import gen_handle_cancel, generate_sub_list_text
|
||||
from .utils import gen_handle_cancel, only_allow_private, generate_sub_list_text
|
||||
|
||||
|
||||
def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
|
||||
handle_cancel = gen_handle_cancel(add_cookie_target_matcher, "已中止关联 cookie")
|
||||
|
||||
@add_cookie_target_matcher.handle()
|
||||
async def init_promote(state: T_State):
|
||||
async def init_promote(state: T_State, event: MessageEvent):
|
||||
await only_allow_private(event, add_cookie_target_matcher)
|
||||
res = await generate_sub_list_text(
|
||||
add_cookie_target_matcher, state, is_index=True, is_show_cookie=True, is_hide_no_cookie_platfrom=True
|
||||
)
|
||||
@@ -24,8 +26,7 @@ def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
|
||||
@add_cookie_target_matcher.got("target_idx", parameterless=[handle_cancel])
|
||||
async def got_target_idx(state: T_State, target_idx: str = ArgPlainText()):
|
||||
try:
|
||||
target_idx = int(target_idx)
|
||||
state["target"] = state["sub_table"][target_idx]
|
||||
state["target"] = state["sub_table"][int(target_idx)]
|
||||
state["site"] = platform_manager[state["target"]["platform_name"]].site
|
||||
except Exception:
|
||||
await add_cookie_target_matcher.reject("序号错误")
|
||||
@@ -55,8 +56,7 @@ def do_add_cookie_target(add_cookie_target_matcher: type[Matcher]):
|
||||
@add_cookie_target_matcher.got("cookie_idx", MessageTemplate("{_prompt}"), [handle_cancel])
|
||||
async def got_cookie_idx(state: T_State, cookie_idx: str = ArgPlainText()):
|
||||
try:
|
||||
cookie_idx = int(cookie_idx)
|
||||
state["cookie"] = state["cookies"][cookie_idx - 1]
|
||||
state["cookie"] = state["cookies"][int(cookie_idx) - 1]
|
||||
except Exception:
|
||||
await add_cookie_target_matcher.reject("序号错误")
|
||||
|
||||
|
||||
@@ -2,17 +2,19 @@ from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import EventPlainText
|
||||
from nonebot_plugin_saa import MessageFactory
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
|
||||
from ..config import config
|
||||
from ..utils import parse_text
|
||||
from .utils import gen_handle_cancel
|
||||
from .utils import gen_handle_cancel, only_allow_private
|
||||
|
||||
|
||||
def do_del_cookie(del_cookie: type[Matcher]):
|
||||
handle_cancel = gen_handle_cancel(del_cookie, "删除中止")
|
||||
|
||||
@del_cookie.handle()
|
||||
async def send_list(state: T_State):
|
||||
async def send_list(state: T_State, event: MessageEvent):
|
||||
await only_allow_private(event, del_cookie)
|
||||
cookies = await config.get_cookie(is_anonymous=False)
|
||||
if not cookies:
|
||||
await del_cookie.finish("暂无已添加的 Cookie\n请使用“添加cookie”命令添加")
|
||||
|
||||
@@ -2,17 +2,19 @@ from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.params import EventPlainText
|
||||
from nonebot_plugin_saa import MessageFactory
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
|
||||
from ..config import config
|
||||
from ..utils import parse_text
|
||||
from .utils import gen_handle_cancel
|
||||
from .utils import gen_handle_cancel, only_allow_private
|
||||
|
||||
|
||||
def do_del_cookie_target(del_cookie_target: type[Matcher]):
|
||||
handle_cancel = gen_handle_cancel(del_cookie_target, "取消关联中止")
|
||||
|
||||
@del_cookie_target.handle()
|
||||
async def send_list(state: T_State):
|
||||
async def send_list(state: T_State, event: MessageEvent):
|
||||
await only_allow_private(event, del_cookie_target)
|
||||
cookie_targets = await config.get_cookie_target()
|
||||
if not cookie_targets:
|
||||
await del_cookie_target.finish("暂无已关联 Cookie\n请使用“添加cookie”命令添加关联")
|
||||
|
||||
@@ -13,6 +13,7 @@ from nonebot_plugin_saa import PlatformTarget, extract_target
|
||||
|
||||
from ..config import config
|
||||
from ..types import Category
|
||||
from ..types import Target as T_Target
|
||||
from ..platform import platform_manager
|
||||
from ..plugin_config import plugin_config
|
||||
from ..utils.site import is_cookie_client_manager
|
||||
@@ -70,7 +71,7 @@ def admin_permission():
|
||||
async def generate_sub_list_text(
|
||||
matcher: type[Matcher],
|
||||
state: T_State,
|
||||
user_info: PlatformTarget = None,
|
||||
user_info: PlatformTarget | None = None,
|
||||
is_index=False,
|
||||
is_show_cookie=False,
|
||||
is_hide_no_cookie_platfrom=False,
|
||||
@@ -88,7 +89,7 @@ async def generate_sub_list_text(
|
||||
sub_list = [
|
||||
sub
|
||||
for sub in sub_list
|
||||
if is_cookie_client_manager(platform_manager.get(sub.target.platform_name).site.client_mgr)
|
||||
if is_cookie_client_manager(platform_manager[sub.target.platform_name].site.client_mgr)
|
||||
]
|
||||
if not sub_list:
|
||||
await matcher.finish("暂无已订阅账号\n请使用“添加订阅”命令添加订阅")
|
||||
@@ -109,7 +110,7 @@ async def generate_sub_list_text(
|
||||
res += " {}".format(", ".join(sub.tags)) + "\n"
|
||||
if is_show_cookie:
|
||||
target_cookies = await config.get_cookie(
|
||||
target=sub.target.target, site_name=platform.site.name, is_anonymous=False
|
||||
target=T_Target(sub.target.target), site_name=platform.site.name, is_anonymous=False
|
||||
)
|
||||
if target_cookies:
|
||||
res += " 关联的 Cookie:\n"
|
||||
@@ -120,3 +121,11 @@ async def generate_sub_list_text(
|
||||
res += f" (平台 {sub.target.platform_name} 已失效,请删除此订阅)"
|
||||
|
||||
return res
|
||||
|
||||
|
||||
async def only_allow_private(
|
||||
event: Event,
|
||||
matcher: type[Matcher],
|
||||
):
|
||||
if not (hasattr(event, "message_type") and getattr(event, "message_type") == "private"):
|
||||
await matcher.finish("请在私聊中使用此命令")
|
||||
|
||||
@@ -58,18 +58,3 @@ class ApiError(Exception):
|
||||
class SubUnit(NamedTuple):
|
||||
sub_target: Target
|
||||
user_sub_infos: list[UserSubInfo]
|
||||
|
||||
|
||||
class RegistryMeta(type):
|
||||
def __new__(cls, name, bases, namespace, **kwargs):
|
||||
return super().__new__(cls, name, bases, namespace)
|
||||
|
||||
def __init__(cls, name, bases, namespace, **kwargs):
|
||||
if kwargs.get("base"):
|
||||
# this is the base class
|
||||
cls.registry = []
|
||||
elif not kwargs.get("abstract"):
|
||||
# this is the subclass
|
||||
cls.registry.append(cls)
|
||||
|
||||
super().__init__(name, bases, namespace, **kwargs)
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import difflib
|
||||
import re
|
||||
import sys
|
||||
import difflib
|
||||
|
||||
import nonebot
|
||||
from nonebot.plugin import require
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from nonebot.log import logger, default_format
|
||||
from nonebot.plugin import require
|
||||
from nonebot_plugin_saa import Text, Image, MessageSegmentFactory
|
||||
|
||||
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 ..plugin_config import plugin_config
|
||||
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):
|
||||
|
||||
+81
-58
@@ -1,17 +1,18 @@
|
||||
import json
|
||||
from typing import Literal
|
||||
from json import JSONDecodeError
|
||||
from typing import Literal, cast
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import httpx
|
||||
from httpx import AsyncClient
|
||||
from nonebot.log import logger
|
||||
|
||||
from ..types import Target
|
||||
from ..config import config
|
||||
from .http import http_client
|
||||
from ..config.db_model import Cookie
|
||||
from ..types import Target, RegistryMeta
|
||||
|
||||
|
||||
class ClientManager(ABC):
|
||||
@@ -42,40 +43,60 @@ class DefaultClientManager(ClientManager):
|
||||
pass
|
||||
|
||||
|
||||
class SkipRequestException(Exception):
|
||||
"""跳过请求异常,如果需要在选择 Cookie 时跳过此次请求,可以抛出此异常"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CookieClientManager(ClientManager):
|
||||
_site_name: str
|
||||
_default_cd: int = timedelta(seconds=10)
|
||||
_default_cookie_cd = timedelta(seconds=15)
|
||||
_site_name: str = ""
|
||||
|
||||
@classmethod
|
||||
async def refresh_anonymous_cookie(cls):
|
||||
"""移除已有的匿名cookie,添加一个新的匿名cookie"""
|
||||
anonymous_cookies = await config.get_cookie(cls._site_name, is_anonymous=True)
|
||||
anonymous_cookie = Cookie(site_name=cls._site_name, content="{}", is_universal=True, is_anonymous=True)
|
||||
for cookie in anonymous_cookies:
|
||||
if not cookie.is_anonymous:
|
||||
continue
|
||||
await config.delete_cookie_by_id(cookie.id)
|
||||
anonymous_cookie.id = cookie.id # 保持原有的id
|
||||
anonymous_cookie.last_usage = datetime.now() # 使得第一次请求优先使用用户 cookie
|
||||
await config.add_cookie(anonymous_cookie)
|
||||
async def _generate_anonymous_cookie(self) -> Cookie:
|
||||
return Cookie(
|
||||
cookie_name=f"{self._site_name} anonymous",
|
||||
site_name=self._site_name,
|
||||
content="{}",
|
||||
is_universal=True,
|
||||
is_anonymous=True,
|
||||
last_usage=datetime.now(),
|
||||
cd_milliseconds=0,
|
||||
tags="{}",
|
||||
status="",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def add_user_cookie(cls, content: str):
|
||||
async def _refresh_anonymous_cookie(self):
|
||||
"""更新已有的匿名cookie,若不存在则添加"""
|
||||
existing_anonymous_cookies = await config.get_cookie(self._site_name, is_anonymous=True)
|
||||
if existing_anonymous_cookies:
|
||||
for cookie in existing_anonymous_cookies:
|
||||
new_anonymous_cookie = await self._generate_anonymous_cookie()
|
||||
new_anonymous_cookie.id = cookie.id # 保持原有的id
|
||||
await config.update_cookie(new_anonymous_cookie)
|
||||
else:
|
||||
new_anonymous_cookie = await self._generate_anonymous_cookie()
|
||||
await config.add_cookie(new_anonymous_cookie)
|
||||
|
||||
async def add_user_cookie(self, content: str, cookie_name: str | None = None) -> Cookie:
|
||||
"""添加用户 cookie"""
|
||||
from ..platform import site_manager
|
||||
|
||||
cookie_site = cast(type[CookieSite], site_manager[cls._site_name])
|
||||
if not await cls.validate_cookie(content):
|
||||
if not await self.validate_cookie(content):
|
||||
raise ValueError()
|
||||
cookie = Cookie(site_name=cls._site_name, content=content)
|
||||
cookie.cookie_name = cookie_site.get_cookie_name(content)
|
||||
cookie.cd = cls._default_cd
|
||||
await config.add_cookie(cookie)
|
||||
cookie = Cookie(site_name=self._site_name, content=content)
|
||||
cookie.cookie_name = cookie_name if cookie_name else await self.get_cookie_name(content)
|
||||
cookie.cd = self._default_cookie_cd
|
||||
cookie_id = await config.add_cookie(cookie)
|
||||
return await config.get_cookie_by_id(cookie_id)
|
||||
|
||||
@classmethod
|
||||
async def validate_cookie(cls, content: str) -> bool:
|
||||
async def get_cookie_name(self, content: str) -> str:
|
||||
"""从cookie内容中获取cookie的友好名字,添加cookie时调用,持久化在数据库中"""
|
||||
from . import text_fletten
|
||||
|
||||
return text_fletten(f"{self._site_name} [{content[:10]}]")
|
||||
|
||||
async def validate_cookie(self, content: str) -> bool:
|
||||
"""验证 cookie 内容是否有效,添加 cookie 时用,可根据平台的具体情况进行重写"""
|
||||
# todo: 考虑移动到 cookie site 中
|
||||
try:
|
||||
data = json.loads(content)
|
||||
if not isinstance(data, dict):
|
||||
@@ -84,7 +105,7 @@ class CookieClientManager(ClientManager):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _generate_hook(self, cookie: Cookie) -> callable:
|
||||
def _generate_hook(self, cookie: Cookie) -> Callable:
|
||||
"""hook 函数生成器,用于回写请求状态到数据库"""
|
||||
|
||||
async def _response_hook(resp: httpx.Response):
|
||||
@@ -92,7 +113,7 @@ class CookieClientManager(ClientManager):
|
||||
logger.trace(f"请求成功: {cookie.id} {resp.request.url}")
|
||||
cookie.status = "success"
|
||||
else:
|
||||
logger.warning(f"请求失败:{cookie.id} {resp.request.url}, 状态码: {resp.status_code}")
|
||||
logger.warning(f"请求失败: {cookie.id} {resp.request.url}, 状态码: {resp.status_code}")
|
||||
cookie.status = "failed"
|
||||
cookie.last_usage = datetime.now()
|
||||
await config.update_cookie(cookie)
|
||||
@@ -102,8 +123,8 @@ class CookieClientManager(ClientManager):
|
||||
async def _choose_cookie(self, target: Target | None) -> Cookie:
|
||||
"""选择 cookie 的具体算法"""
|
||||
cookies = await config.get_cookie(self._site_name, target)
|
||||
cookies = (cookie for cookie in cookies if cookie.last_usage + cookie.cd < datetime.now())
|
||||
cookie = min(cookies, key=lambda x: x.last_usage)
|
||||
avaliable_cookies = (cookie for cookie in cookies if cookie.last_usage + cookie.cd < datetime.now())
|
||||
cookie = min(avaliable_cookies, key=lambda x: x.last_usage)
|
||||
return cookie
|
||||
|
||||
async def get_client(self, target: Target | None) -> AsyncClient:
|
||||
@@ -118,7 +139,7 @@ class CookieClientManager(ClientManager):
|
||||
return await self._assemble_client(client, cookie)
|
||||
|
||||
async def _assemble_client(self, client, cookie) -> AsyncClient:
|
||||
"""组装 client,可以自定义 cookie 对象的 content 装配到 client 中的方式"""
|
||||
"""组装 client,可以自定义 cookie 对象装配到 client 中的方式"""
|
||||
cookies = httpx.Cookies()
|
||||
if cookie:
|
||||
cookies.update(json.loads(cookie.content))
|
||||
@@ -126,6 +147,15 @@ class CookieClientManager(ClientManager):
|
||||
client.event_hooks = {"response": [self._generate_hook(cookie)]}
|
||||
return client
|
||||
|
||||
@classmethod
|
||||
def from_name(cls, site_name: str) -> type["CookieClientManager"]:
|
||||
"""创建一个平台特化的 CookieClientManger"""
|
||||
return type(
|
||||
"CookieClientManager",
|
||||
(CookieClientManager,),
|
||||
{"_site_name": site_name},
|
||||
)
|
||||
|
||||
async def get_client_for_static(self) -> AsyncClient:
|
||||
return http_client()
|
||||
|
||||
@@ -133,23 +163,32 @@ class CookieClientManager(ClientManager):
|
||||
return http_client()
|
||||
|
||||
async def refresh_client(self):
|
||||
pass
|
||||
await self._refresh_anonymous_cookie()
|
||||
|
||||
|
||||
def is_cookie_client_manager(manger: type[ClientManager]) -> bool:
|
||||
return issubclass(manger, CookieClientManager)
|
||||
|
||||
|
||||
def create_cookie_client_manager(site_name: str) -> type[CookieClientManager]:
|
||||
"""创建一个平台特化的 CookieClientManger"""
|
||||
return type(
|
||||
"CookieClientManager",
|
||||
(CookieClientManager,),
|
||||
{"_site_name": site_name},
|
||||
)
|
||||
site_manager: dict[str, type["Site"]] = {}
|
||||
|
||||
|
||||
class Site(metaclass=RegistryMeta, base=True):
|
||||
class SiteMeta(type):
|
||||
def __new__(cls, name, bases, namespace, **kwargs):
|
||||
return super().__new__(cls, name, bases, namespace)
|
||||
|
||||
def __init__(cls, name, bases, namespace, **kwargs):
|
||||
if kwargs.get("base"):
|
||||
# this is the base class
|
||||
cls._key = kwargs.get("key")
|
||||
elif not kwargs.get("abstract"):
|
||||
# this is the subclass
|
||||
if "name" in namespace:
|
||||
site_manager[namespace["name"]] = cls
|
||||
super().__init__(name, bases, namespace, **kwargs)
|
||||
|
||||
|
||||
class Site(metaclass=SiteMeta):
|
||||
schedule_type: Literal["date", "interval", "cron"]
|
||||
schedule_setting: dict
|
||||
name: str
|
||||
@@ -161,18 +200,6 @@ class Site(metaclass=RegistryMeta, base=True):
|
||||
return f"[{self.name}]-{self.name}-{self.schedule_setting}"
|
||||
|
||||
|
||||
class CookieSite(Site):
|
||||
client_mgr: type[CookieClientManager] = CookieClientManager
|
||||
cookie_format_prompt = "无效的 Cookie,请检查后重新输入,详情见<待添加的文档>"
|
||||
|
||||
@classmethod
|
||||
def get_cookie_name(cls, content: str) -> str:
|
||||
"""从cookie内容中获取cookie的友好名字,添加cookie时调用,持久化在数据库中"""
|
||||
from . import text_fletten
|
||||
|
||||
return text_fletten(f"{cls.name} [{content[:10]}]")
|
||||
|
||||
|
||||
def anonymous_site(schedule_type: Literal["date", "interval", "cron"], schedule_setting: dict) -> type[Site]:
|
||||
return type(
|
||||
"AnonymousSite",
|
||||
@@ -183,7 +210,3 @@ def anonymous_site(schedule_type: Literal["date", "interval", "cron"], schedule_
|
||||
"client_mgr": DefaultClientManager,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class SkipRequestException(Exception):
|
||||
pass
|
||||
|
||||
+6
-3
@@ -11,9 +11,12 @@
|
||||
"docs:update-package": "pnpm dlx vp-update"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vuepress/bundler-vite": "2.0.0-rc.15",
|
||||
"@vuepress/bundler-vite": "2.0.0-rc.17",
|
||||
"mermaid": "^11.3.0",
|
||||
"sass-embedded": "^1.79.5",
|
||||
"vue": "^3.5.6",
|
||||
"vuepress": "2.0.0-rc.15",
|
||||
"vuepress-theme-hope": "2.0.0-rc.52"
|
||||
"vuepress": "2.0.0-rc.17",
|
||||
"vuepress-plugin-md-enhance": "2.0.0-rc.57",
|
||||
"vuepress-theme-hope": "2.0.0-rc.58"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+2942
-730
File diff suppressed because it is too large
Load Diff
Generated
+129
-92
@@ -538,6 +538,10 @@ files = [
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"},
|
||||
{file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"},
|
||||
@@ -550,8 +554,14 @@ files = [
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"},
|
||||
{file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"},
|
||||
@@ -562,8 +572,24 @@ files = [
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"},
|
||||
{file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"},
|
||||
{file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"},
|
||||
@@ -573,6 +599,10 @@ files = [
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"},
|
||||
{file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"},
|
||||
@@ -584,6 +614,10 @@ files = [
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"},
|
||||
{file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"},
|
||||
@@ -596,6 +630,10 @@ files = [
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"},
|
||||
{file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"},
|
||||
@@ -608,6 +646,10 @@ files = [
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"},
|
||||
{file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"},
|
||||
{file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"},
|
||||
@@ -2965,95 +3007,90 @@ reference = "offical-source"
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "10.4.0"
|
||||
version = "11.0.0"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"},
|
||||
{file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"},
|
||||
{file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"},
|
||||
{file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"},
|
||||
{file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"},
|
||||
{file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"},
|
||||
{file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"},
|
||||
{file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"},
|
||||
{file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"},
|
||||
{file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"},
|
||||
{file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"},
|
||||
{file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"},
|
||||
{file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"},
|
||||
{file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"},
|
||||
{file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"},
|
||||
{file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"},
|
||||
{file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"},
|
||||
{file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"},
|
||||
{file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"},
|
||||
{file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"},
|
||||
{file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"},
|
||||
{file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"},
|
||||
{file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"},
|
||||
{file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"},
|
||||
{file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"},
|
||||
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"},
|
||||
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"},
|
||||
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"},
|
||||
{file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"},
|
||||
{file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"},
|
||||
{file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"},
|
||||
{file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"},
|
||||
{file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"},
|
||||
{file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"},
|
||||
{file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
|
||||
docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
|
||||
fpx = ["olefile"]
|
||||
mic = ["olefile"]
|
||||
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
|
||||
@@ -3149,13 +3186,13 @@ reference = "offical-source"
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "3.8.0"
|
||||
version = "4.0.1"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"},
|
||||
{file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"},
|
||||
{file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"},
|
||||
{file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -3665,17 +3702,17 @@ reference = "offical-source"
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
version = "5.0.0"
|
||||
version = "6.0.0"
|
||||
description = "Pytest plugin for measuring coverage."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
|
||||
{file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
|
||||
{file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"},
|
||||
{file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
coverage = {version = ">=5.2.1", extras = ["toml"]}
|
||||
coverage = {version = ">=7.5", extras = ["toml"]}
|
||||
pytest = ">=4.6"
|
||||
|
||||
[package.extras]
|
||||
@@ -5129,4 +5166,4 @@ yaml = []
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.10,<4.0.0"
|
||||
content-hash = "02cf0461830f2012ad533f01d152e7b4abb9e8bd94158956a73369f4f91c96b6"
|
||||
content-hash = "3d3bd947b91b8053fc5fed4873b6d0ed4017a5be118611cd93d30ffa265e04fb"
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "nonebot-bison"
|
||||
version = "0.9.4"
|
||||
version = "0.9.5"
|
||||
description = "Subscribe message from social medias"
|
||||
authors = ["felinae98 <felinae225@qq.com>"]
|
||||
license = "MIT"
|
||||
@@ -31,7 +31,7 @@ nonebot-plugin-htmlrender = ">=0.3.5"
|
||||
nonebot-plugin-datastore = ">=1.3.0,<2.0.0"
|
||||
nonebot-plugin-apscheduler = ">=0.5.0"
|
||||
nonebot-plugin-send-anything-anywhere = ">=0.7.1,<0.7.2"
|
||||
pillow = ">=10.4.0,<11.0"
|
||||
pillow = ">=11.0.0,<11.1"
|
||||
pyjwt = "^2.9.0"
|
||||
python-socketio = "^5.11.4"
|
||||
tinydb = "^4.8.0"
|
||||
@@ -49,7 +49,7 @@ ipdb = "^0.13.13"
|
||||
isort = "^5.13.2"
|
||||
nonemoji = "^0.1.4"
|
||||
nb-cli = "^1.4.2"
|
||||
pre-commit = "^3.8.0"
|
||||
pre-commit = "^4.0.1"
|
||||
ruff = "^0.6.5"
|
||||
|
||||
[tool.poetry.group.test.dependencies]
|
||||
@@ -58,7 +58,7 @@ nonebug = "^0.3.7"
|
||||
nonebug-saa = "^0.4.1"
|
||||
pytest = ">=8.3.3,<9.0.0"
|
||||
pytest-asyncio = ">=0.24.0,<0.24.1"
|
||||
pytest-cov = ">=5.0.0,<6"
|
||||
pytest-cov = ">=6.0.0,<7"
|
||||
pytest-mock = "^3.14.0"
|
||||
pytest-xdist = { extras = ["psutil"], version = "^3.6.1" }
|
||||
respx = ">=0.21.1,<0.22"
|
||||
|
||||
@@ -6,14 +6,15 @@ import pytest
|
||||
from nonebug import App
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("_patch_weibo_get_cookie_name")
|
||||
async def test_cookie(app: App, init_scheduler):
|
||||
from nonebot_plugin_saa import TargetQQGroup
|
||||
|
||||
from nonebot_bison.platform import site_manager
|
||||
from nonebot_bison.config.db_config import config
|
||||
from nonebot_bison.scheduler import scheduler_dict
|
||||
from nonebot_bison.types import Target as T_Target
|
||||
from nonebot_bison.utils.site import CookieClientManager
|
||||
from nonebot_bison.config.utils import DuplicateCookieTargetException
|
||||
from nonebot_bison.utils.site import CookieClientManager, site_manager
|
||||
|
||||
target = T_Target("weibo_id")
|
||||
platform_name = "weibo"
|
||||
@@ -26,14 +27,13 @@ async def test_cookie(app: App, init_scheduler):
|
||||
tags=[],
|
||||
)
|
||||
site = site_manager["weibo.com"]
|
||||
client_mgr = cast(CookieClientManager, site.client_mgr)
|
||||
client_mgr = cast(CookieClientManager, scheduler_dict[site].client_mgr)
|
||||
|
||||
# 刷新匿名cookie
|
||||
await client_mgr.refresh_anonymous_cookie()
|
||||
await client_mgr.refresh_client()
|
||||
|
||||
cookies = await config.get_cookie(site_name=site.name)
|
||||
assert len(cookies) == 1
|
||||
|
||||
# 添加用户cookie
|
||||
await client_mgr.add_user_cookie(json.dumps({"test_cookie": "1"}))
|
||||
await client_mgr.add_user_cookie(json.dumps({"test_cookie": "2"}))
|
||||
@@ -67,6 +67,7 @@ async def test_cookie(app: App, init_scheduler):
|
||||
cats=[],
|
||||
tags=[],
|
||||
)
|
||||
|
||||
await client_mgr.add_user_cookie(json.dumps({"test_cookie": "3"}))
|
||||
cookies = await config.get_cookie(site_name=site.name, is_anonymous=False)
|
||||
|
||||
|
||||
@@ -29,6 +29,16 @@ def load_adapters(nonebug_init: None):
|
||||
return driver
|
||||
|
||||
|
||||
def patch_refresh_bilibili_anonymous_cookie(mocker: MockerFixture):
|
||||
# patch 掉bilibili的匿名cookie生成函数,避免真实请求
|
||||
|
||||
from nonebot_bison.platform.bilibili.scheduler import BilibiliClientManager
|
||||
|
||||
mocker.patch.object(
|
||||
BilibiliClientManager, "_get_cookies", return_value=[{"name": "test anonymous", "content": "test"}]
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def app(tmp_path: Path, request: pytest.FixtureRequest, mocker: MockerFixture):
|
||||
sys.path.append(str(Path(__file__).parent.parent / "src" / "plugins"))
|
||||
@@ -51,6 +61,10 @@ async def app(tmp_path: Path, request: pytest.FixtureRequest, mocker: MockerFixt
|
||||
|
||||
param: AppReq = getattr(request, "param", AppReq())
|
||||
|
||||
# 如果在 app 前调用会报错“无法找到调用者”
|
||||
# 而在后面调用又来不及mock,所以只能在中间mock
|
||||
patch_refresh_bilibili_anonymous_cookie(mocker)
|
||||
|
||||
if not param.get("no_init_db"):
|
||||
await init_db()
|
||||
# if not param.get("refresh_bot"):
|
||||
@@ -123,3 +137,22 @@ async def _no_browser(app: App, mocker: MockerFixture):
|
||||
|
||||
mocker.patch.object(plugin_config, "bison_use_browser", False)
|
||||
mocker.patch("nonebot_bison.platform.unavailable_paltforms", _get_unavailable_platforms())
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def _clear_db(app: App):
|
||||
from nonebot_bison.config import config
|
||||
|
||||
await config.clear_db()
|
||||
yield
|
||||
await config.clear_db()
|
||||
return
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def _patch_weibo_get_cookie_name(app: App, mocker: MockerFixture):
|
||||
from nonebot_bison.platform import weibo
|
||||
|
||||
mocker.patch.object(weibo.WeiboClientManager, "_get_current_user_name", return_value="test_name")
|
||||
yield
|
||||
mocker.stopall()
|
||||
|
||||
+4
-11493
File diff suppressed because it is too large
Load Diff
+3
-11498
File diff suppressed because it is too large
Load Diff
+138
@@ -0,0 +1,138 @@
|
||||
{
|
||||
"ok": 1,
|
||||
"data": {
|
||||
"config": {
|
||||
"code": 11000,
|
||||
"text": "<p>非微博会员不可多次修改昵称。自2024年1月1日至今,您已成功修改1次,目前无法继续修改。如需继续改名可开通微博会员。</p>",
|
||||
"guide": {
|
||||
"title": "开通微博会员",
|
||||
"desc": "<p>本年度<span>增加最多8次</span>改名机会</p>",
|
||||
"button_text": "开通会员",
|
||||
"button_url": "https://m.weibo.cn/c/upgrade"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"config": {
|
||||
"title": "修改昵称次数扣除明细"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"id": 114514,
|
||||
"idstr": "114514",
|
||||
"class": 1,
|
||||
"screen_name": "suyiiyii",
|
||||
"name": "suyiiyii",
|
||||
"province": "44",
|
||||
"city": "1",
|
||||
"location": "广东 广州",
|
||||
"description": "",
|
||||
"url": "",
|
||||
"profile_image_url": "https://tvax1.sinaimg.cn/default/images/default_avatar_male_50.gif?KID=imgbed,tva&Expires=1728833531&ssig=jURhYal3%2BR",
|
||||
"light_ring": false,
|
||||
"profile_url": "u/114514",
|
||||
"domain": "",
|
||||
"weihao": "",
|
||||
"gender": "m",
|
||||
"followers_count": 1,
|
||||
"followers_count_str": "1",
|
||||
"friends_count": 6,
|
||||
"pagefriends_count": 0,
|
||||
"statuses_count": 1,
|
||||
"video_status_count": 0,
|
||||
"video_play_count": 0,
|
||||
"super_topic_not_syn_count": 0,
|
||||
"favourites_count": 0,
|
||||
"created_at": "Tue Sep 03 00:07:59 +0800 2024",
|
||||
"following": false,
|
||||
"allow_all_act_msg": false,
|
||||
"geo_enabled": true,
|
||||
"verified": false,
|
||||
"verified_type": -1,
|
||||
"remark": "",
|
||||
"insecurity": {
|
||||
"sexual_content": false
|
||||
},
|
||||
"ptype": 0,
|
||||
"allow_all_comment": true,
|
||||
"avatar_large": "https://tvax1.sinaimg.cn/default/images/default_avatar_male_180.gif?KID=imgbed,tva&Expires=1728833531&ssig=cornnikInk",
|
||||
"avatar_hd": "https://tvax1.sinaimg.cn/default/images/default_avatar_male_180.gif?KID=imgbed,tva&Expires=1728833531&ssig=cornnikInk",
|
||||
"verified_reason": "",
|
||||
"verified_trade": "",
|
||||
"verified_reason_url": "",
|
||||
"verified_source": "",
|
||||
"verified_source_url": "",
|
||||
"follow_me": false,
|
||||
"like": false,
|
||||
"like_me": false,
|
||||
"online_status": 0,
|
||||
"bi_followers_count": 0,
|
||||
"lang": "zh-cn",
|
||||
"star": 0,
|
||||
"mbtype": 0,
|
||||
"mbrank": 0,
|
||||
"svip": 0,
|
||||
"vvip": 0,
|
||||
"mb_expire_time": 0,
|
||||
"block_word": 0,
|
||||
"block_app": 0,
|
||||
"chaohua_ability": 0,
|
||||
"brand_ability": 0,
|
||||
"nft_ability": 0,
|
||||
"vplus_ability": 0,
|
||||
"wenda_ability": 0,
|
||||
"live_ability": 0,
|
||||
"gongyi_ability": 0,
|
||||
"paycolumn_ability": 0,
|
||||
"newbrand_ability": 0,
|
||||
"ecommerce_ability": 0,
|
||||
"hardfan_ability": 0,
|
||||
"wbcolumn_ability": 0,
|
||||
"interaction_user": 0,
|
||||
"audio_ability": 0,
|
||||
"place_ability": 0,
|
||||
"credit_score": 80,
|
||||
"user_ability": 0,
|
||||
"urank": 0,
|
||||
"story_read_state": -1,
|
||||
"vclub_member": 0,
|
||||
"is_teenager": 0,
|
||||
"is_guardian": 0,
|
||||
"is_teenager_list": 0,
|
||||
"pc_new": 0,
|
||||
"special_follow": false,
|
||||
"planet_video": 0,
|
||||
"video_mark": 0,
|
||||
"live_status": 0,
|
||||
"user_ability_extend": 0,
|
||||
"status_total_counter": {
|
||||
"total_cnt": 0,
|
||||
"repost_cnt": 0,
|
||||
"comment_cnt": 0,
|
||||
"like_cnt": 0,
|
||||
"comment_like_cnt": 0
|
||||
},
|
||||
"video_total_counter": {
|
||||
"play_cnt": -1
|
||||
},
|
||||
"brand_account": 0,
|
||||
"hongbaofei": 0,
|
||||
"green_mode": 0,
|
||||
"urisk": 524288,
|
||||
"unfollowing_recom_switch": 1,
|
||||
"block": 0,
|
||||
"block_me": 0,
|
||||
"avatar_type": 0,
|
||||
"is_big": 0,
|
||||
"auth_status": 1,
|
||||
"auth_realname": null,
|
||||
"auth_career": null,
|
||||
"auth_career_name": null,
|
||||
"show_auth": 0,
|
||||
"is_auth": 0,
|
||||
"is_punish": 0,
|
||||
"like_display": 0
|
||||
},
|
||||
"submit": true,
|
||||
"having_count": 0
|
||||
}
|
||||
}
|
||||
@@ -414,7 +414,7 @@ async def test_fetch_new_without_dynamic(bilibili, dummy_user_subinfo, without_d
|
||||
|
||||
target = Target("161775300")
|
||||
post_router = respx.get(
|
||||
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset="
|
||||
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset=&features=itemOpusStyle"
|
||||
)
|
||||
post_router.mock(return_value=Response(200, json=without_dynamic))
|
||||
res = await bilibili.fetch_new_post(SubUnit(target, [dummy_user_subinfo]))
|
||||
@@ -433,7 +433,7 @@ async def test_fetch_new(bilibili, dummy_user_subinfo):
|
||||
target = Target("161775300")
|
||||
|
||||
post_router = respx.get(
|
||||
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset="
|
||||
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset=&features=itemOpusStyle"
|
||||
)
|
||||
post_list = type_validate_python(PostAPI, get_json("bilibili-new.json"))
|
||||
assert post_list.data
|
||||
@@ -477,7 +477,7 @@ async def test_fetch_new_live_rcmd(bilibili: "Bilibili", dummy_user_subinfo):
|
||||
target = Target("13164144")
|
||||
|
||||
post_router = respx.get(
|
||||
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset="
|
||||
f"https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid={target}&timezone_offset=-480&offset=&features=itemOpusStyle"
|
||||
)
|
||||
post_list = type_validate_python(PostAPI, get_json("bilibili-dynamic-live-rcmd.json"))
|
||||
assert post_list.data
|
||||
|
||||
@@ -166,7 +166,7 @@ async def test_batch_fetch_new_with_single(
|
||||
|
||||
targets_router = respx.get("https://server.ceobecanteen.top/api/v1/canteen/config/datasource/list")
|
||||
comb_id_router = respx.post("https://server.ceobecanteen.top/api/v1/canteen/user/getDatasourceComb")
|
||||
cookie_id_router = respx.get("http://cdn.ceobecanteen.top/datasource-comb/2")
|
||||
cookie_id_router = respx.get("https://cdn.ceobecanteen.top/datasource-comb/2")
|
||||
cookies_router = respx.get("https://server-cdn.ceobecanteen.top/api/v1/cdn/cookie/mainList/cookieList")
|
||||
|
||||
targets_router.mock(return_value=Response(200, json=ceobecanteen_targets, headers=mock_respone_headers))
|
||||
|
||||
@@ -45,7 +45,7 @@ async def test_fetch_new(weibo, dummy_user_subinfo):
|
||||
from nonebot_bison.types import Target, SubUnit
|
||||
|
||||
ak_list_router = respx.get("https://m.weibo.cn/api/container/getIndex?containerid=1076036279793937")
|
||||
detail_router = respx.get("https://m.weibo.cn/statuses/show?id=4649031014551911")
|
||||
detail_router = respx.get("https://m.weibo.cn/statuses/extend?id=4649031014551911")
|
||||
ak_list_router.mock(return_value=Response(200, json=get_json("weibo_ak_list_0.json")))
|
||||
detail_router.mock(return_value=Response(200, text=get_file("weibo_detail_4649031014551911")))
|
||||
image_cdn_router.mock(Response(200, content=b""))
|
||||
@@ -77,7 +77,7 @@ async def test_fetch_new(weibo, dummy_user_subinfo):
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_fetch_repost(weibo):
|
||||
repost_detail_router = respx.get("https://m.weibo.cn/statuses/show?id=4645748019299849")
|
||||
repost_detail_router = respx.get("https://m.weibo.cn/statuses/extend?id=4645748019299849")
|
||||
repost_detail_router.mock(return_value=Response(200, text=get_file("weibo_detail_4645748019299849")))
|
||||
image_cdn_router.mock(Response(200, content=b""))
|
||||
raw_post = get_json("weibo_ak_list_1.json")["data"]["cards"][3]
|
||||
@@ -121,7 +121,7 @@ async def test_fetch_repost(weibo):
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_video_cover(weibo):
|
||||
router = respx.get("https://m.weibo.cn/statuses/show?id=4645748019299849")
|
||||
router = respx.get("https://m.weibo.cn/statuses/extend?id=4645748019299849")
|
||||
router.mock(return_value=Response(200, text=get_file("weibo_detail_4645748019299849")))
|
||||
image_cdn_router.mock(Response(200, content=b""))
|
||||
raw_post = get_json("weibo_ak_list_1.json")["data"]["cards"][0]
|
||||
@@ -152,7 +152,7 @@ async def test_classification(weibo):
|
||||
@pytest.mark.asyncio
|
||||
@respx.mock
|
||||
async def test_parse_long(weibo):
|
||||
detail_router = respx.get("https://m.weibo.cn/statuses/show?id=4645748019299849")
|
||||
detail_router = respx.get("https://m.weibo.cn/statuses/extend?id=4645748019299849")
|
||||
detail_router.mock(return_value=Response(200, text=get_file("weibo_detail_4645748019299849")))
|
||||
raw_post = get_json("weibo_ak_list_1.json")["data"]["cards"][0]
|
||||
post = await weibo.parse(raw_post)
|
||||
@@ -217,3 +217,14 @@ async def test_parse_target(weibo: "Weibo"):
|
||||
assert res == "6441489862"
|
||||
with pytest.raises(Platform.ParseTargetException):
|
||||
await weibo.parse_target("https://weibo.com/arknights")
|
||||
|
||||
|
||||
@respx.mock
|
||||
async def test_get_cookie_name(weibo: "Weibo"):
|
||||
from nonebot_bison.platform.weibo import WeiboClientManager
|
||||
|
||||
router = respx.get("https://m.weibo.cn/setup/nick/detail")
|
||||
router.mock(return_value=Response(200, json=get_json("weibo_get-cookie-name.json")))
|
||||
weibo_client_mgr = WeiboClientManager()
|
||||
name = await weibo_client_mgr.get_cookie_name("{}")
|
||||
assert name == "weibo: [suyiiyii]"
|
||||
|
||||
@@ -5,6 +5,8 @@ from unittest.mock import AsyncMock
|
||||
from nonebug import App
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from tests.conftest import patch_refresh_bilibili_anonymous_cookie
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from nonebot_bison.utils import Site
|
||||
|
||||
@@ -199,6 +201,7 @@ async def test_scheduler_skip_browser(mocker: MockerFixture):
|
||||
site = MockSite
|
||||
|
||||
mocker.patch.dict(platform_manager, {"mock_platform": MockPlatform})
|
||||
patch_refresh_bilibili_anonymous_cookie(mocker)
|
||||
|
||||
await init_scheduler()
|
||||
|
||||
@@ -229,6 +232,7 @@ async def test_scheduler_no_skip_not_require_browser(mocker: MockerFixture):
|
||||
site = MockSite
|
||||
|
||||
mocker.patch.dict(platform_manager, {"mock_platform": MockPlatform})
|
||||
patch_refresh_bilibili_anonymous_cookie(mocker)
|
||||
|
||||
await init_scheduler()
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from nonebug.app import App
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
@@ -30,7 +31,8 @@ async def test_add_cookie_rule(app: App, mocker: MockerFixture):
|
||||
ctx.should_pass_permission()
|
||||
|
||||
|
||||
async def test_add_cookie_target_no_cookie(app: App, mocker: MockerFixture):
|
||||
@pytest.mark.usefixtures("_clear_db")
|
||||
async def test_add_cookie_target_no_cookie(app: App):
|
||||
from nonebot.adapters.onebot.v11.bot import Bot
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
|
||||
@@ -80,7 +82,9 @@ async def test_add_cookie_target_no_cookie(app: App, mocker: MockerFixture):
|
||||
)
|
||||
|
||||
|
||||
async def test_add_cookie(app: App, mocker: MockerFixture):
|
||||
@pytest.mark.usefixtures("_clear_db")
|
||||
@pytest.mark.usefixtures("_patch_weibo_get_cookie_name")
|
||||
async def test_add_cookie(app: App):
|
||||
from nonebot.adapters.onebot.v11.bot import Bot
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
|
||||
@@ -90,7 +94,7 @@ async def test_add_cookie(app: App, mocker: MockerFixture):
|
||||
async with app.test_matcher(add_cookie_matcher) as ctx:
|
||||
bot = ctx.create_bot(base=Bot)
|
||||
event_1 = fake_private_message_event(
|
||||
message=Message("添加Cookie"), sender=fake_superuser, to_me=True, user_id=fake_superuser.user_id
|
||||
message=Message("添加cookie"), sender=fake_superuser, to_me=True, user_id=fake_superuser.user_id
|
||||
)
|
||||
ctx.receive_event(bot, event_1)
|
||||
ctx.should_pass_rule()
|
||||
@@ -120,7 +124,11 @@ async def test_add_cookie(app: App, mocker: MockerFixture):
|
||||
message=Message("test"), sender=fake_superuser, to_me=True, user_id=fake_superuser.user_id
|
||||
)
|
||||
ctx.receive_event(bot, event_4_err)
|
||||
ctx.should_call_send(event_4_err, "无效的 Cookie,请检查后重新输入,详情见<待添加的文档>", True)
|
||||
ctx.should_call_send(
|
||||
event_4_err,
|
||||
"无效的 Cookie,请检查后重新输入,详情见https://nonebot-bison.netlify.app/usage/cookie.html",
|
||||
True,
|
||||
)
|
||||
ctx.should_rejected()
|
||||
event_4_ok = fake_private_message_event(
|
||||
message=Message(json.dumps({"cookie": "test"})),
|
||||
@@ -131,7 +139,7 @@ async def test_add_cookie(app: App, mocker: MockerFixture):
|
||||
ctx.receive_event(bot, event_4_ok)
|
||||
ctx.should_pass_rule()
|
||||
ctx.should_call_send(
|
||||
event_4_ok, '已添加 Cookie: {"cookie": "test"} 到平台 weibo\n请使用“关联cookie”为 Cookie 关联订阅', True
|
||||
event_4_ok, "已添加 Cookie: weibo: [test_name] 到平台 weibo\n请使用“关联cookie”为 Cookie 关联订阅", True
|
||||
)
|
||||
|
||||
async with app.test_matcher(add_cookie_target_matcher) as ctx:
|
||||
@@ -176,7 +184,7 @@ async def test_add_cookie(app: App, mocker: MockerFixture):
|
||||
)
|
||||
ctx.receive_event(bot, event_2_ok)
|
||||
ctx.should_pass_rule()
|
||||
ctx.should_call_send(event_2_ok, '请选择一个 Cookie,已关联的 Cookie 不会显示\n1. weibo.com [{"cookie":]', True)
|
||||
ctx.should_call_send(event_2_ok, "请选择一个 Cookie,已关联的 Cookie 不会显示\n1. weibo: [test_name]", True)
|
||||
event_3_err = fake_private_message_event(
|
||||
message=Message("2"), sender=fake_superuser, to_me=True, user_id=fake_superuser.user_id
|
||||
)
|
||||
@@ -188,11 +196,10 @@ async def test_add_cookie(app: App, mocker: MockerFixture):
|
||||
)
|
||||
ctx.receive_event(bot, event_3_ok)
|
||||
ctx.should_pass_rule()
|
||||
ctx.should_call_send(event_3_ok, '已关联 Cookie: weibo.com [{"cookie":] 到订阅 weibo.com weibo_id', True)
|
||||
ctx.should_call_send(event_3_ok, "已关联 Cookie: weibo: [test_name] 到订阅 weibo.com weibo_id", True)
|
||||
|
||||
|
||||
async def test_add_cookie_target_no_target(app: App, mocker: MockerFixture):
|
||||
|
||||
from nonebot.adapters.onebot.v11.bot import Bot
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
|
||||
|
||||
@@ -1,10 +1,73 @@
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from nonebug.app import App
|
||||
|
||||
from ..utils import fake_superuser, fake_private_message_event
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("_clear_db")
|
||||
async def test_del_cookie(app: App):
|
||||
from nonebug_saa import should_send_saa
|
||||
from nonebot.adapters.onebot.v11.bot import Bot
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
from nonebot_plugin_saa import TargetQQGroup, MessageFactory
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.config.db_model import Cookie
|
||||
from nonebot_bison.types import Target as T_Target
|
||||
from nonebot_bison.sub_manager import del_cookie_matcher
|
||||
|
||||
async with app.test_matcher(del_cookie_matcher) as ctx:
|
||||
bot = ctx.create_bot(base=Bot)
|
||||
event = fake_private_message_event(
|
||||
message=Message("删除cookie"), sender=fake_superuser, to_me=True, user_id=fake_superuser.user_id
|
||||
)
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_pass_rule()
|
||||
ctx.should_pass_permission()
|
||||
ctx.should_call_send(event, "暂无已添加的 Cookie\n请使用“添加cookie”命令添加", True)
|
||||
|
||||
async with app.test_matcher(del_cookie_matcher) as ctx:
|
||||
bot = ctx.create_bot(base=Bot)
|
||||
target = T_Target("weibo_id")
|
||||
platform_name = "weibo"
|
||||
await config.add_subscribe(
|
||||
TargetQQGroup(group_id=123),
|
||||
target=target,
|
||||
target_name="weibo_name",
|
||||
platform_name=platform_name,
|
||||
cats=[],
|
||||
tags=[],
|
||||
)
|
||||
await config.add_cookie(Cookie(content=json.dumps({"cookie": "test"}), site_name="weibo.com"))
|
||||
|
||||
event_1 = fake_private_message_event(
|
||||
message=Message("删除cookie"), sender=fake_superuser, to_me=True, user_id=fake_superuser.user_id
|
||||
)
|
||||
ctx.receive_event(bot, event_1)
|
||||
ctx.should_pass_rule()
|
||||
ctx.should_pass_permission()
|
||||
should_send_saa(
|
||||
ctx,
|
||||
MessageFactory(
|
||||
"已添加的 Cookie 为:\n1 weibo.com unnamed cookie"
|
||||
" 0个关联\n请输入要删除的 Cookie 的序号\n输入'取消'中止"
|
||||
),
|
||||
bot,
|
||||
event=event_1,
|
||||
)
|
||||
event_2 = fake_private_message_event(
|
||||
message=Message("1"), sender=fake_superuser, to_me=True, user_id=fake_superuser.user_id
|
||||
)
|
||||
ctx.receive_event(bot, event_2)
|
||||
ctx.should_pass_rule()
|
||||
ctx.should_pass_permission()
|
||||
ctx.should_call_send(event_2, "删除成功", True)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("_clear_db")
|
||||
@pytest.mark.usefixtures("_patch_weibo_get_cookie_name")
|
||||
async def test_del_cookie_err(app: App):
|
||||
from nonebug_saa import should_send_saa
|
||||
from nonebot.adapters.onebot.v11.bot import Bot
|
||||
@@ -51,8 +114,7 @@ async def test_del_cookie_err(app: App):
|
||||
should_send_saa(
|
||||
ctx,
|
||||
MessageFactory(
|
||||
'已添加的 Cookie 为:\n1 weibo.com weibo.com [{"cookie":] '
|
||||
"1个关联\n请输入要删除的 Cookie 的序号\n输入'取消'中止"
|
||||
"已添加的 Cookie 为:\n1 weibo.com unnamed cookie 1个关联\n请输入要删除的 Cookie 的序号\n输入'取消'中止"
|
||||
),
|
||||
bot,
|
||||
event=event_1,
|
||||
@@ -72,62 +134,3 @@ async def test_del_cookie_err(app: App):
|
||||
ctx.should_call_send(event_2, "只能删除未关联的 Cookie,请使用“取消关联cookie”命令取消关联", True)
|
||||
ctx.should_call_send(event_2, "删除错误", True)
|
||||
ctx.should_rejected()
|
||||
|
||||
|
||||
async def test_del_cookie(app: App):
|
||||
from nonebug_saa import should_send_saa
|
||||
from nonebot.adapters.onebot.v11.bot import Bot
|
||||
from nonebot.adapters.onebot.v11.message import Message
|
||||
from nonebot_plugin_saa import TargetQQGroup, MessageFactory
|
||||
|
||||
from nonebot_bison.config import config
|
||||
from nonebot_bison.config.db_model import Cookie
|
||||
from nonebot_bison.types import Target as T_Target
|
||||
from nonebot_bison.sub_manager import del_cookie_matcher
|
||||
|
||||
async with app.test_matcher(del_cookie_matcher) as ctx:
|
||||
bot = ctx.create_bot(base=Bot)
|
||||
event = fake_private_message_event(
|
||||
message=Message("删除cookie"), sender=fake_superuser, to_me=True, user_id=fake_superuser.user_id
|
||||
)
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_pass_rule()
|
||||
ctx.should_pass_permission()
|
||||
ctx.should_call_send(event, "暂无已添加的 Cookie\n请使用“添加cookie”命令添加", True)
|
||||
|
||||
async with app.test_matcher(del_cookie_matcher) as ctx:
|
||||
bot = ctx.create_bot(base=Bot)
|
||||
target = T_Target("weibo_id")
|
||||
platform_name = "weibo"
|
||||
await config.add_subscribe(
|
||||
TargetQQGroup(group_id=123),
|
||||
target=target,
|
||||
target_name="weibo_name",
|
||||
platform_name=platform_name,
|
||||
cats=[],
|
||||
tags=[],
|
||||
)
|
||||
await config.add_cookie(Cookie(content=json.dumps({"cookie": "test"}), site_name="weibo.com"))
|
||||
|
||||
event_1 = fake_private_message_event(
|
||||
message=Message("删除cookie"), sender=fake_superuser, to_me=True, user_id=fake_superuser.user_id
|
||||
)
|
||||
ctx.receive_event(bot, event_1)
|
||||
ctx.should_pass_rule()
|
||||
ctx.should_pass_permission()
|
||||
should_send_saa(
|
||||
ctx,
|
||||
MessageFactory(
|
||||
'已添加的 Cookie 为:\n1 weibo.com weibo.com [{"cookie":]'
|
||||
" 0个关联\n请输入要删除的 Cookie 的序号\n输入'取消'中止"
|
||||
),
|
||||
bot,
|
||||
event=event_1,
|
||||
)
|
||||
event_2 = fake_private_message_event(
|
||||
message=Message("1"), sender=fake_superuser, to_me=True, user_id=fake_superuser.user_id
|
||||
)
|
||||
ctx.receive_event(bot, event_2)
|
||||
ctx.should_pass_rule()
|
||||
ctx.should_pass_permission()
|
||||
ctx.should_call_send(event_2, "删除成功", True)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"version": 3,
|
||||
"groups": [
|
||||
{
|
||||
"user_target": {
|
||||
"platform_type": "QQ Group",
|
||||
"group_id": 1232
|
||||
},
|
||||
"subs": [
|
||||
{
|
||||
"categories": [],
|
||||
"tags": [],
|
||||
"target": {
|
||||
"target_name": "weibo_name",
|
||||
"target": "weibo_id",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"user_target": {
|
||||
"platform_type": "QQ Group",
|
||||
"group_id": 2342
|
||||
},
|
||||
"subs": [
|
||||
{
|
||||
"categories": [],
|
||||
"tags": ["kaltsit", "amiya"],
|
||||
"target": {
|
||||
"target_name": "weibo_name",
|
||||
"target": "weibo_id",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"categories": [1, 2],
|
||||
"tags": [],
|
||||
"target": {
|
||||
"target_name": "bilibili_name",
|
||||
"target": "bilibili_id",
|
||||
"platform_name": "bilibili",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cookies": [
|
||||
{
|
||||
"site_name": "weibo.com",
|
||||
"content": "{\"cookie\": \"test\"}",
|
||||
"cookie_name": "test cookie",
|
||||
"cd_milliseconds": 0,
|
||||
"is_universal": false,
|
||||
"tags": {},
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "weibo_name",
|
||||
"target": "weibo_id",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
version: 3
|
||||
groups:
|
||||
- subs:
|
||||
- categories: []
|
||||
tags: []
|
||||
target:
|
||||
default_schedule_weight: 10
|
||||
platform_name: weibo
|
||||
target: weibo_id
|
||||
target_name: weibo_name
|
||||
user_target:
|
||||
platform_type: QQ Group
|
||||
group_id: 123552
|
||||
- subs:
|
||||
- categories: []
|
||||
tags:
|
||||
- kaltsit
|
||||
- amiya
|
||||
target:
|
||||
default_schedule_weight: 10
|
||||
platform_name: weibo
|
||||
target: weibo_id
|
||||
target_name: weibo_name
|
||||
- categories:
|
||||
- 1
|
||||
- 2
|
||||
tags: []
|
||||
target:
|
||||
default_schedule_weight: 10
|
||||
platform_name: bilibili
|
||||
target: bilibili_id
|
||||
target_name: bilibili_name
|
||||
user_target:
|
||||
platform_type: QQ Group
|
||||
group_id: 234662
|
||||
|
||||
cookies:
|
||||
- site_name: weibo.com
|
||||
content: '{"cookie": "test"}'
|
||||
cookie_name: test cookie
|
||||
cd_milliseconds: 0
|
||||
is_universal: false
|
||||
tags: {}
|
||||
targets:
|
||||
- target_name: weibo_name
|
||||
target: weibo_id
|
||||
platform_name: weibo
|
||||
default_schedule_weight: 10
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"version": 2,
|
||||
"groups": [
|
||||
{
|
||||
"user_target": {
|
||||
"platform_type": "QQ Group",
|
||||
"group_id": 123
|
||||
},
|
||||
"subs": [
|
||||
{
|
||||
"categories": [],
|
||||
"tags": [],
|
||||
"target": {
|
||||
"target_name": "weibo_name",
|
||||
"target": "weibo_id",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"user_target": {
|
||||
"platform_type": "QQ Group",
|
||||
"group_id": 234
|
||||
},
|
||||
"subs": [
|
||||
{
|
||||
"tags": ["kaltsit", "amiya"],
|
||||
"target": {
|
||||
"target_name": "weibo_name",
|
||||
"target": "weibo_id",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"categories": [1, 2],
|
||||
"tags": [],
|
||||
"target": [
|
||||
{
|
||||
"target_name": "bilibili_name",
|
||||
"target": "bilibili_id",
|
||||
"platform_name": "bilibili",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"user_target": {
|
||||
"platform_type": "QQ Group",
|
||||
"group_id": 123
|
||||
},
|
||||
"subs": {
|
||||
"categories": [],
|
||||
"tags": [],
|
||||
"target": {
|
||||
"target_name": "weibo_name2",
|
||||
"target": "weibo_id2",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"user_target": {
|
||||
"platform_type": "QQ Group",
|
||||
"group_id": 123
|
||||
},
|
||||
"subs": [
|
||||
{
|
||||
"categories": [],
|
||||
"tags": [],
|
||||
"target": {
|
||||
"target_name": "weibo_name2",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cookies": [
|
||||
{
|
||||
"site_name": "weibo.com1111",
|
||||
"content": "{\"cookie\": 111}",
|
||||
"cookie_name": "test cookie1",
|
||||
"cd_milliseconds": -1,
|
||||
"is_universal": false,
|
||||
"tags": {},
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "weibo_name",
|
||||
"target": "weibo_id",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"version": 3,
|
||||
"groups": [
|
||||
{
|
||||
"user_target": {
|
||||
"platform_type": "QQ Group",
|
||||
"group_id": 1232
|
||||
},
|
||||
"subs": [
|
||||
{
|
||||
"categories": [],
|
||||
"tags": [],
|
||||
"target": {
|
||||
"target_name": "weibo_name",
|
||||
"target": "weibo_id",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"user_target": {
|
||||
"platform_type": "QQ Group",
|
||||
"group_id": 2342
|
||||
},
|
||||
"subs": [
|
||||
{
|
||||
"categories": [],
|
||||
"tags": ["kaltsit", "amiya"],
|
||||
"target": {
|
||||
"target_name": "weibo_name",
|
||||
"target": "weibo_id",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"categories": [1, 2],
|
||||
"tags": [],
|
||||
"target": {
|
||||
"target_name": "bilibili_name",
|
||||
"target": "bilibili_id",
|
||||
"platform_name": "bilibili",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"user_target": {
|
||||
"platform_type": "QQ Group",
|
||||
"group_id": 1232
|
||||
},
|
||||
"subs": [
|
||||
{
|
||||
"categories": [],
|
||||
"tags": [],
|
||||
"target": {
|
||||
"target_name": "weibo_name",
|
||||
"target": "weibo_id",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"categories": [2, 6],
|
||||
"tags": ["poca"],
|
||||
"target": {
|
||||
"target_name": "weibo_name2",
|
||||
"target": "weibo_id2",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cookies": [
|
||||
{
|
||||
"site_name": "weibo.com",
|
||||
"content": "{\"cookie\": \"test\"}",
|
||||
"cookie_name": "test cookie",
|
||||
"cd_milliseconds": 0,
|
||||
"is_universal": false,
|
||||
"tags": {},
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "weibo_name",
|
||||
"target": "weibo_id",
|
||||
"platform_name": "weibo",
|
||||
"default_schedule_weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -40,6 +40,7 @@ def test_cli_help(app: App):
|
||||
async def test_subs_export(app: App, tmp_path: Path):
|
||||
from nonebot_plugin_saa import TargetQQGroup
|
||||
|
||||
from nonebot_bison.config.db_model import Cookie
|
||||
from nonebot_bison.config.db_config import config
|
||||
from nonebot_bison.types import Target as TTarget
|
||||
from nonebot_bison.script.cli import cli, run_sync
|
||||
@@ -70,6 +71,14 @@ async def test_subs_export(app: App, tmp_path: Path):
|
||||
cats=[1, 2],
|
||||
tags=[],
|
||||
)
|
||||
cookie_id = await config.add_cookie(
|
||||
Cookie(
|
||||
site_name="weibo.com",
|
||||
content='{"cookie": "test"}',
|
||||
cookie_name="test cookie",
|
||||
)
|
||||
)
|
||||
await config.add_cookie_target(TTarget("weibo_id"), "weibo", cookie_id)
|
||||
|
||||
assert len(await config.list_subs_with_all_info()) == 3
|
||||
|
||||
@@ -84,8 +93,9 @@ async def test_subs_export(app: App, tmp_path: Path):
|
||||
assert result.exit_code == 0
|
||||
file_path = Path.cwd() / "bison_subscribes_export_1.json"
|
||||
assert file_path.exists()
|
||||
assert '"version": 2' in file_path.read_text()
|
||||
assert '"version": 3' in file_path.read_text()
|
||||
assert '"group_id": 123' in file_path.read_text()
|
||||
assert '"content": "{\\"cookie\\": \\"test\\"}",\n' in file_path.read_text()
|
||||
|
||||
# 是否导出到指定已存在文件夹
|
||||
data_dir = tmp_path / "data"
|
||||
@@ -94,8 +104,9 @@ async def test_subs_export(app: App, tmp_path: Path):
|
||||
assert result.exit_code == 0
|
||||
file_path2 = data_dir / "bison_subscribes_export_1.json"
|
||||
assert file_path2.exists()
|
||||
assert '"version": 2' in file_path2.read_text()
|
||||
assert '"version": 3' in file_path2.read_text()
|
||||
assert '"group_id": 123' in file_path2.read_text()
|
||||
assert '"content": "{\\"cookie\\": \\"test\\"}",\n' in file_path.read_text()
|
||||
|
||||
# 是否拒绝导出到不存在的文件夹
|
||||
result = await run_sync(runner.invoke)(cli, ["export", "-p", str(tmp_path / "data2")])
|
||||
@@ -106,9 +117,10 @@ async def test_subs_export(app: App, tmp_path: Path):
|
||||
assert result.exit_code == 0
|
||||
file_path3 = tmp_path / "bison_subscribes_export_1.yaml"
|
||||
assert file_path3.exists()
|
||||
assert "version: 2" in file_path3.read_text()
|
||||
assert "version: 3" in file_path3.read_text()
|
||||
assert "group_id: 123" in file_path3.read_text()
|
||||
assert "platform_type: QQ Group" in file_path3.read_text()
|
||||
assert '"content": "{\\"cookie\\": \\"test\\"}",\n' in file_path.read_text()
|
||||
|
||||
# 是否允许以未支持的格式导出
|
||||
result = await run_sync(runner.invoke)(cli, ["export", "-p", str(tmp_path), "--format", "toml"])
|
||||
|
||||
@@ -5,17 +5,18 @@ from nonebot.compat import model_dump
|
||||
from .utils import get_json
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("_clear_db")
|
||||
async def test_subs_export(app: App, init_scheduler):
|
||||
from nonebot_plugin_saa import TargetQQGroup
|
||||
|
||||
from nonebot_bison.config.db_model import User
|
||||
from nonebot_bison.config.db_config import config
|
||||
from nonebot_bison.types import Target as TTarget
|
||||
from nonebot_bison.config.db_model import User, Cookie
|
||||
from nonebot_bison.config.subs_io import subscribes_export
|
||||
|
||||
await config.add_subscribe(
|
||||
TargetQQGroup(group_id=1232),
|
||||
target=TTarget("weibo_id"),
|
||||
target=TTarget(TTarget("weibo_id")),
|
||||
target_name="weibo_name",
|
||||
platform_name="weibo",
|
||||
cats=[],
|
||||
@@ -23,7 +24,7 @@ async def test_subs_export(app: App, init_scheduler):
|
||||
)
|
||||
await config.add_subscribe(
|
||||
TargetQQGroup(group_id=2342),
|
||||
target=TTarget("weibo_id"),
|
||||
target=TTarget(TTarget("weibo_id")),
|
||||
target_name="weibo_name",
|
||||
platform_name="weibo",
|
||||
cats=[],
|
||||
@@ -37,12 +38,20 @@ async def test_subs_export(app: App, init_scheduler):
|
||||
cats=[1, 2],
|
||||
tags=[],
|
||||
)
|
||||
cookie_id = await config.add_cookie(
|
||||
Cookie(
|
||||
site_name="weibo.com",
|
||||
content='{"cookie": "test"}',
|
||||
cookie_name="test cookie",
|
||||
)
|
||||
)
|
||||
await config.add_cookie_target(TTarget("weibo_id"), "weibo", cookie_id)
|
||||
|
||||
data = await config.list_subs_with_all_info()
|
||||
assert len(data) == 3
|
||||
|
||||
nbesf_data = await subscribes_export(lambda x: x)
|
||||
assert model_dump(nbesf_data) == get_json("v2/subs_export.json")
|
||||
assert model_dump(nbesf_data) == get_json("v3/subs_export.json")
|
||||
|
||||
nbesf_data_user_234 = await subscribes_export(
|
||||
lambda stmt: stmt.where(User.user_target == {"platform_type": "QQ Group", "group_id": 2342})
|
||||
@@ -102,16 +111,30 @@ async def test_subs_import_dup_err(app: App, init_scheduler):
|
||||
|
||||
async def test_subs_import_version_disorder(app: App, init_scheduler):
|
||||
from nonebot_bison.config.subs_io import subscribes_import
|
||||
from nonebot_bison.config.subs_io.nbesf_model import v1, v2
|
||||
from nonebot_bison.config.subs_io.utils import NBESFParseErr
|
||||
|
||||
# use v2 parse v1
|
||||
with pytest.raises(NBESFParseErr):
|
||||
v1.nbesf_parser(get_json("v2/subs_export_has_subdup_err.json"))
|
||||
from nonebot_bison.config.subs_io.nbesf_model import v1, v2, v3
|
||||
|
||||
# use v1 parse v2
|
||||
with pytest.raises(NBESFParseErr):
|
||||
v1.nbesf_parser(get_json("v2/subs_export_has_subdup_err.json"))
|
||||
# use v1 parse v3
|
||||
with pytest.raises(NBESFParseErr):
|
||||
v1.nbesf_parser(get_json("v3/subs_export_has_subdup_err.json"))
|
||||
|
||||
# use v2 parse v1
|
||||
with pytest.raises(NBESFParseErr):
|
||||
v2.nbesf_parser(get_json("v1/subs_export_has_subdup_err.json"))
|
||||
# # use v2 parse v3
|
||||
# with pytest.raises(NBESFParseErr):
|
||||
# v2.nbesf_parser(get_json("v3/subs_export_has_subdup_err.json"))
|
||||
|
||||
# use v3 parse v1
|
||||
with pytest.raises(NBESFParseErr):
|
||||
v3.nbesf_parser(get_json("v1/subs_export_has_subdup_err.json"))
|
||||
# # use v3 parse v2
|
||||
# with pytest.raises(NBESFParseErr):
|
||||
# v3.nbesf_parser(get_json("v2/subs_export_has_subdup_err.json"))
|
||||
# TODO: v3 parse v2 不会报错,但是v3 parse v1 会报错,似乎是有问题 (
|
||||
|
||||
with pytest.raises(AssertionError): # noqa: PT012
|
||||
nbesf_data = v2.nbesf_parser(get_json("v2/subs_export_has_subdup_err.json"))
|
||||
@@ -121,7 +144,7 @@ async def test_subs_import_version_disorder(app: App, init_scheduler):
|
||||
|
||||
async def test_subs_import_all_fail(app: App, init_scheduler):
|
||||
"""只要文件格式有任何一个错误, 都不会进行订阅"""
|
||||
from nonebot_bison.config.subs_io.nbesf_model import v1, v2
|
||||
from nonebot_bison.config.subs_io.nbesf_model import v1, v2, v3
|
||||
from nonebot_bison.config.subs_io.nbesf_model.v1 import NBESFParseErr
|
||||
|
||||
with pytest.raises(NBESFParseErr):
|
||||
@@ -129,3 +152,6 @@ async def test_subs_import_all_fail(app: App, init_scheduler):
|
||||
|
||||
with pytest.raises(NBESFParseErr):
|
||||
v2.nbesf_parser(get_json("v2/subs_export_all_illegal.json"))
|
||||
|
||||
with pytest.raises(NBESFParseErr):
|
||||
v3.nbesf_parser(get_json("v3/subs_export_all_illegal.json"))
|
||||
|
||||
@@ -17,7 +17,8 @@ async def test_http_error(app: App):
|
||||
|
||||
assert ctx.gen_req_records() == [
|
||||
"https://example.com Headers({'host': 'example.com', 'accept': '*/*', 'accept-encoding': 'gzip, deflate',"
|
||||
" 'connection': 'keep-alive', 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like"
|
||||
" Gecko) Chrome/51.0.2704.103 Safari/537.36'}) | [403] Headers({'content-length': '15', 'content-type':"
|
||||
" 'connection': 'keep-alive', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||
" (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0'}) | [403] Headers({'content-length': '"
|
||||
"15', 'content-type':"
|
||||
' \'application/json\'}) {"error": "gg"}'
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user