16 Commits

Author SHA1 Message Date
suyiiyii 6e28ad3502 🔧 更新默认UA为Windows平台 2024-10-23 18:24:40 +08:00
洛初 40f9bc817f feat(ISSUE_TEMPLATE): 修改问题报告模板 (#630)
test-build / Build Frontend (push) Has been cancelled
test-build / Smoke-test Coverage (macos-latest, 3.10) (push) Has been cancelled
test-build / Smoke-test Coverage (macos-latest, 3.11) (push) Has been cancelled
test-build / Smoke-test Coverage (macos-latest, 3.12) (push) Has been cancelled
test-build / Smoke-test Coverage (ubuntu-latest, 3.10) (push) Has been cancelled
test-build / Smoke-test Coverage (ubuntu-latest, 3.11) (push) Has been cancelled
test-build / Smoke-test Coverage (ubuntu-latest, 3.12) (push) Has been cancelled
test-build / Smoke-test Coverage (windows-latest, 3.10) (push) Has been cancelled
test-build / Smoke-test Coverage (windows-latest, 3.11) (push) Has been cancelled
test-build / Smoke-test Coverage (windows-latest, 3.12) (push) Has been cancelled
test-build / All-test Coverage (macos-latest, 3.10) (push) Has been cancelled
test-build / All-test Coverage (macos-latest, 3.11) (push) Has been cancelled
test-build / All-test Coverage (macos-latest, 3.12) (push) Has been cancelled
test-build / All-test Coverage (ubuntu-latest, 3.10) (push) Has been cancelled
test-build / All-test Coverage (ubuntu-latest, 3.11) (push) Has been cancelled
test-build / All-test Coverage (ubuntu-latest, 3.12) (push) Has been cancelled
test-build / All-test Coverage (windows-latest, 3.10) (push) Has been cancelled
test-build / All-test Coverage (windows-latest, 3.11) (push) Has been cancelled
test-build / All-test Coverage (windows-latest, 3.12) (push) Has been cancelled
pydantic1-compat-test / pydantic1 test (ubuntu-latest, 3.11) (push) Has been cancelled
Ruff Lint / Ruff Lint (push) Has been cancelled
test-build / Docker main (push) Has been cancelled
test-build / Docker main sentry (push) Has been cancelled
*  feat(ISSUE_TEMPLATE): 修改问题报告模板 

  feat(ISSUE_TEMPLATE): 修改问题报告模板

* 💄 auto fix by pre-commit hooks

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-10-03 10:31:47 +08:00
github-actions[bot] f416b249f7 📝 Update changelog 2024-10-03 02:31:09 +00:00
suyiiyii ec6fe2679d 👷 将自动化工具中的 yarn 替换为 pnpm (#634)
* 👷 将自动化工具中的 yarn 替换为 pnpm

* 🐛 写串台了艹

* 🐛 最讨厌调试 action 了 (
2024-10-03 10:29:57 +08:00
github-actions[bot] 19b25552d0 📝 Update changelog 2024-10-01 07:38:45 +00:00
洛梧藤 85b5ab3868 📝 小刻食堂剪彩文档 (#636) 2024-10-01 15:38:16 +08:00
felinae98 cf6b7fcd6d 📌 pin eslint to 8
test-build / Build Frontend (push) Has been cancelled
test-build / Smoke-test Coverage (macos-latest, 3.10) (push) Has been cancelled
test-build / Smoke-test Coverage (macos-latest, 3.11) (push) Has been cancelled
test-build / Smoke-test Coverage (macos-latest, 3.12) (push) Has been cancelled
test-build / Smoke-test Coverage (ubuntu-latest, 3.10) (push) Has been cancelled
test-build / Smoke-test Coverage (ubuntu-latest, 3.11) (push) Has been cancelled
test-build / Smoke-test Coverage (ubuntu-latest, 3.12) (push) Has been cancelled
test-build / Smoke-test Coverage (windows-latest, 3.10) (push) Has been cancelled
test-build / Smoke-test Coverage (windows-latest, 3.11) (push) Has been cancelled
test-build / Smoke-test Coverage (windows-latest, 3.12) (push) Has been cancelled
test-build / All-test Coverage (macos-latest, 3.10) (push) Has been cancelled
test-build / All-test Coverage (macos-latest, 3.11) (push) Has been cancelled
test-build / All-test Coverage (macos-latest, 3.12) (push) Has been cancelled
test-build / All-test Coverage (ubuntu-latest, 3.10) (push) Has been cancelled
test-build / All-test Coverage (ubuntu-latest, 3.11) (push) Has been cancelled
test-build / All-test Coverage (ubuntu-latest, 3.12) (push) Has been cancelled
test-build / All-test Coverage (windows-latest, 3.10) (push) Has been cancelled
test-build / All-test Coverage (windows-latest, 3.11) (push) Has been cancelled
test-build / All-test Coverage (windows-latest, 3.12) (push) Has been cancelled
pydantic1-compat-test / pydantic1 test (ubuntu-latest, 3.11) (push) Has been cancelled
Ruff Lint / Ruff Lint (push) Has been cancelled
test-build / Docker main (push) Has been cancelled
test-build / Docker main sentry (push) Has been cancelled
2024-09-21 12:19:54 +08:00
renovate[bot] 2c70d3c44e ⬆️ Update all non-major dependencies (#624)
* ⬆️ Update all non-major dependencies

* 💄 auto fix by pre-commit hooks

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-19 13:51:41 +08:00
dependabot[bot] d5c31394df ⬆️ Bump vite from 5.4.2 to 5.4.6 (#625)
* ⬆️ Bump vite from 5.4.2 to 5.4.6

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.2 to 5.4.6.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* 💄 auto fix by pre-commit hooks

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-19 11:19:54 +08:00
github-actions[bot] 908de2c5ca 🔖 Release 0.9.4 2024-09-17 15:10:51 +00:00
felinae98 76be8454f3 🔖 release 0.9.4 2024-09-17 23:09:39 +08:00
github-actions[bot] 7845ef8c74 📝 Update changelog 2024-09-17 14:59:38 +00:00
Azide 088e7a439f 新增可以在 fsm 抛出错误后重置 fsm 的装饰器工具 2024-09-17 22:59:09 +08:00
renovate[bot] ab5236ee37 ⬆️ Update all non-major dependencies (#621)
* ⬆️ Update all non-major dependencies

* 💄 auto fix by pre-commit hooks

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-12 13:28:17 +08:00
pre-commit-ci[bot] ded3e34259 ⬆️ auto update by pre-commit hooks (#620)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.6.0 → v0.6.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.0...v0.6.3)
- [github.com/pre-commit/mirrors-eslint: v9.8.0 → v9.9.1](https://github.com/pre-commit/mirrors-eslint/compare/v9.8.0...v9.9.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-03 13:35:36 +08:00
renovate[bot] 68f7e3b72a ⬆️ Update all non-major dependencies (#595)
* ⬆️ Update all non-major dependencies

* 💄 auto fix by pre-commit hooks

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-09-03 13:34:49 +08:00
37 changed files with 4009 additions and 3945 deletions
+1 -1
View File
@@ -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": {
+61
View File
@@ -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
-31
View File
@@ -1,31 +0,0 @@
---
name: 问题报告
about: 我遇到了问题
title: ""
labels: ""
assignees: ""
---
## 环境
- nonebot-bison 版本:
- nonebot 版本:
- 安装方式:(以下方式的一种或者其他方式)
1. 通过 nb-cli 安装
2. 使用 poetry/pdm 等现代包管理器安装
3. 通过 pip install 安装
4. 克隆或下载项目直接使用
- 操作系统:
## 问题
请在这里描述你遇到的问题
## 日志
```
请在这里粘贴你的日志
```
- [ ] 我搜索过了 issue,但是并没有发现过与我类似的问题
- [ ] 我确认在日志中去掉了敏感信息
+2 -2
View File
@@ -10,5 +10,5 @@ runs:
- name: Build Frontend
shell: bash
run: |
yarn install
yarn docs:build
pnpm install
pnpm docs:build
+2 -2
View File
@@ -11,5 +11,5 @@ runs:
shell: bash
working-directory: ./admin-frontend
run: |
yarn install
yarn build
pnpm install
pnpm build
+9 -4
View File
@@ -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') }}
+2 -2
View File
@@ -7,7 +7,7 @@ ci:
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.0
rev: v0.6.3
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
@@ -34,7 +34,7 @@ repos:
stages: [commit]
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.8.0
rev: v9.9.1
hooks:
- id: eslint
additional_dependencies:
+7
View File
@@ -2,8 +2,15 @@
## 最近更新
### 文档
- 📝 小刻食堂剪彩文档 [@phidiaLam](https://github.com/phidiaLam) ([#636](https://github.com/MountainDash/nonebot-bison/pull/636))
## v0.9.4
### Bug 修复
- FSM 内部执行外部函数出现异常时不应崩溃 [@AzideCupric](https://github.com/AzideCupric) ([#616](https://github.com/MountainDash/nonebot-bison/pull/616))
- 无权限用户尝试添加订阅时返回提示信息 [@suyiiyii](https://github.com/suyiiyii) ([#617](https://github.com/MountainDash/nonebot-bison/pull/617))
- B站请求策略阶段行为优化 [@AzideCupric](https://github.com/AzideCupric) ([#610](https://github.com/MountainDash/nonebot-bison/pull/610))
- Rss 不再删除格式化字符 [@suyiiyii](https://github.com/suyiiyii) ([#615](https://github.com/MountainDash/nonebot-bison/pull/615))
+18 -18
View File
@@ -5,26 +5,26 @@
"homepage": "bison",
"proxy": "http://127.0.0.1:8080",
"dependencies": {
"@arco-design/web-react": "^2.63.1",
"@babel/core": "^7.24.7",
"@arco-design/web-react": "^2.64.0",
"@babel/core": "^7.25.2",
"@babel/plugin-syntax-flow": "^7.24.7",
"@babel/plugin-transform-react-jsx": "^7.24.7",
"@babel/plugin-transform-react-jsx": "^7.25.2",
"@reduxjs/toolkit": "^1.9.7",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.0.0",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.12",
"@types/node": "^20.14.10",
"@types/react": "^18.3.3",
"@types/jest": "^29.5.13",
"@types/node": "^20.16.5",
"@types/react": "^18.3.7",
"@types/react-dom": "^18.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.24.1",
"react-router-dom": "^6.26.2",
"react-scripts": "5.0.1",
"redux": "^5.0.1",
"redux-persist": "^6.0.0",
"typescript": "^5.5.4",
"typescript": "^5.6.2",
"web-vitals": "^3.5.2"
},
"scripts": {
@@ -53,17 +53,17 @@
]
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.6",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"eslint": "^9.6.0",
"@testing-library/jest-dom": "^6.5.0",
"@typescript-eslint/eslint-plugin": "^8.6.0",
"@typescript-eslint/parser": "^8.6.0",
"eslint": "^8.57.1",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^18.0.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.9.0",
"eslint-plugin-react": "^7.34.3",
"eslint-import-resolver-typescript": "^3.6.3",
"eslint-plugin-import": "^2.30.0",
"eslint-plugin-jsx-a11y": "^6.10.0",
"eslint-plugin-react": "^7.36.1",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-redux": "^4.1.0"
"eslint-plugin-react-redux": "^4.2.0"
}
}
+1045 -1033
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -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
View File
@@ -1,7 +1,8 @@
FROM node:20.15.1 as frontend
FROM node:20.17.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
+1 -1
View File
@@ -1,4 +1,4 @@
# syntax=docker/dockerfile:1.8
# syntax=docker/dockerfile:1.10
FROM python:3.11-slim-bullseye as base
FROM base as builder
+1 -1
View File
@@ -1,4 +1,4 @@
# syntax=docker/dockerfile:1.8
# syntax=docker/dockerfile:1.10
FROM python:3.11-slim-bullseye as base
FROM base as builder
+1 -1
View File
@@ -70,7 +70,7 @@ highlights:
details: 一个由玩家创造的幻想世界
link: https://adsrff.web.sdo.com/web1/
- title: 小刻食堂 (即将支持)
- title: 小刻食堂
details: 实时获取鹰角发的动态
link: https://www.ceobecanteen.top
-11
View File
@@ -1,5 +1,4 @@
from .types import Target
from .config.db_model import Cookie
from .scheduler import scheduler_dict
from .platform import platform_manager
@@ -11,13 +10,3 @@ async def check_sub_target(platform_name: str, target: Target):
client = await scheduler.client_mgr.get_query_name_client()
return await platform_manager[platform_name].get_target_name(client, target)
async def check_sub_target_cookie(platform_name: str, target: Target, cookie: str):
# TODO
return "check pass"
async def get_cookie_friendly_name(cookie: Cookie):
# TODO
return f"{cookie.platform_name} [{cookie.content[:10]}]"
+2 -76
View File
@@ -12,8 +12,8 @@ from nonebot_plugin_datastore import create_session
from ..types import Tag
from ..types import Target as T_Target
from .utils import NoSuchTargetException, DuplicateCookieTargetException
from .db_model import User, Cookie, Target, Subscribe, CookieTarget, ScheduleTimeWeight
from .utils import NoSuchTargetException
from .db_model import User, Target, Subscribe, ScheduleTimeWeight
from ..types import Category, UserSubInfo, WeightConfig, TimeWeightConfig, PlatformWeightConfigResp
@@ -259,79 +259,5 @@ class DBConfig:
)
return res
async def get_cookie(self, platform_name: str = None, target: T_Target = None) -> list[Cookie]:
async with create_session() as sess:
query = select(Cookie).distinct()
if platform_name:
query = query.where(Cookie.platform_name == platform_name)
query = query.outerjoin(CookieTarget).options(selectinload(Cookie.targets))
res = (await sess.scalars(query)).all()
if target:
query = select(CookieTarget.cookie_id).join(Target).where(Target.target == target)
ids = set((await sess.scalars(query)).all())
res = [cookie for cookie in res if cookie.id in ids]
return res
async def add_cookie(self, platform_name: str, content: str) -> int:
async with create_session() as sess:
cookie = Cookie(platform_name=platform_name, content=content)
sess.add(cookie)
await sess.commit()
await sess.refresh(cookie)
return cookie.id
async def update_cookie(self, cookie: Cookie):
async with create_session() as sess:
cookie_in_db: Cookie | None = await sess.scalar(select(Cookie).where(Cookie.id == cookie.id))
if not cookie_in_db:
return
cookie_in_db.content = cookie.content
cookie_in_db.last_usage = cookie.last_usage
cookie_in_db.status = cookie.status
cookie_in_db.tags = cookie.tags
await sess.commit()
async def delete_cookie(self, cookie_id: int):
async with create_session() as sess:
await sess.execute(delete(Cookie).where(Cookie.id == cookie_id))
await sess.commit()
async def get_cookie_by_target(self, target: T_Target, platform_name: str) -> list[Cookie]:
async with create_session() as sess:
query = (
select(Cookie)
.join(CookieTarget)
.join(Target)
.where(Target.platform_name == platform_name, Target.target == target)
)
return list((await sess.scalars(query)).all())
async def add_cookie_target(self, target: T_Target, platform_name: str, cookie_id: int):
async with create_session() as sess:
target_obj = await sess.scalar(
select(Target).where(Target.platform_name == platform_name, Target.target == target)
)
# check if relation exists
cookie_target = await sess.scalar(
select(CookieTarget).where(CookieTarget.target == target_obj, CookieTarget.cookie_id == cookie_id)
)
if cookie_target:
raise DuplicateCookieTargetException()
cookie_obj = await sess.scalar(select(Cookie).where(Cookie.id == cookie_id))
cookie_target = CookieTarget(target=target_obj, cookie=cookie_obj)
sess.add(cookie_target)
await sess.commit()
async def delete_cookie_target(self, target: T_Target, platform_name: str, cookie_id: int):
async with create_session() as sess:
target_obj = await sess.scalar(
select(Target).where(Target.platform_name == platform_name, Target.target == target)
)
cookie_obj = await sess.scalar(select(Cookie).where(Cookie.id == cookie_id))
await sess.execute(
delete(CookieTarget).where(CookieTarget.target == target_obj, CookieTarget.cookie == cookie_obj)
)
await sess.commit()
config = DBConfig()
+1 -23
View File
@@ -1,5 +1,4 @@
import datetime
from typing import Any
from pathlib import Path
from nonebot_plugin_saa import PlatformTarget
@@ -7,7 +6,7 @@ from sqlalchemy.dialects.postgresql import JSONB
from nonebot.compat import PYDANTIC_V2, ConfigDict
from nonebot_plugin_datastore import get_plugin_data
from sqlalchemy.orm import Mapped, relationship, mapped_column
from sqlalchemy import JSON, String, DateTime, ForeignKey, UniqueConstraint
from sqlalchemy import JSON, String, ForeignKey, UniqueConstraint
from ..types import Tag, Category
@@ -37,7 +36,6 @@ class Target(Model):
subscribes: Mapped[list["Subscribe"]] = relationship(back_populates="target")
time_weight: Mapped[list["ScheduleTimeWeight"]] = relationship(back_populates="target")
cookies: Mapped[list["CookieTarget"]] = relationship(back_populates="target")
class ScheduleTimeWeight(Model):
@@ -68,23 +66,3 @@ class Subscribe(Model):
target: Mapped[Target] = relationship(back_populates="subscribes")
user: Mapped[User] = relationship(back_populates="subscribes")
class Cookie(Model):
id: Mapped[int] = mapped_column(primary_key=True)
platform_name: Mapped[str] = mapped_column(String(20))
content: Mapped[str] = mapped_column(String(1024))
last_usage: Mapped[datetime.datetime] = mapped_column(DateTime, default=datetime.datetime(1970, 1, 1))
status: Mapped[str] = mapped_column(String(20), default="")
tags: Mapped[dict[str, Any]] = mapped_column(JSON().with_variant(JSONB, "postgresql"), default={})
targets: Mapped[list["CookieTarget"]] = relationship(back_populates="cookie")
class CookieTarget(Model):
id: Mapped[int] = mapped_column(primary_key=True)
target_id: Mapped[int] = mapped_column(ForeignKey("nonebot_bison_target.id", ondelete="CASCADE"))
cookie_id: Mapped[int] = mapped_column(ForeignKey("nonebot_bison_cookie.id", ondelete="CASCADE"))
target: Mapped[Target] = relationship(back_populates="cookies")
cookie: Mapped[Cookie] = relationship(back_populates="targets")
@@ -1,59 +0,0 @@
"""empty message
Revision ID: 590dc2911ea7
Revises: f9baef347cc8
Create Date: 2024-08-31 23:06:02.123932
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy import Text
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "590dc2911ea7"
down_revision = "f9baef347cc8"
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"nonebot_bison_cookie",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("platform_name", sa.String(length=20), nullable=False),
sa.Column("content", sa.String(length=1024), nullable=False),
sa.Column("last_usage", sa.DateTime(), nullable=False),
sa.Column("status", sa.String(length=20), nullable=False),
sa.Column("tags", sa.JSON().with_variant(postgresql.JSONB(astext_type=Text()), "postgresql"), nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_nonebot_bison_cookie")),
)
op.create_table(
"nonebot_bison_cookietarget",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("target_id", sa.Integer(), nullable=False),
sa.Column("cookie_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["cookie_id"],
["nonebot_bison_cookie.id"],
name=op.f("fk_nonebot_bison_cookietarget_cookie_id_nonebot_bison_cookie"),
ondelete="CASCADE",
),
sa.ForeignKeyConstraint(
["target_id"],
["nonebot_bison_target.id"],
name=op.f("fk_nonebot_bison_cookietarget_target_id_nonebot_bison_target"),
ondelete="CASCADE",
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_nonebot_bison_cookietarget")),
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("nonebot_bison_cookietarget")
op.drop_table("nonebot_bison_cookie")
# ### end Alembic commands ###
-4
View File
@@ -8,7 +8,3 @@ class NoSuchSubscribeException(Exception):
class NoSuchTargetException(Exception):
pass
class DuplicateCookieTargetException(Exception):
pass
+62 -1
View File
@@ -2,10 +2,24 @@ import sys
import asyncio
import inspect
from enum import Enum
from functools import wraps
from dataclasses import dataclass
from collections.abc import Set as AbstractSet
from collections.abc import Callable, Sequence, Awaitable, AsyncGenerator
from typing import TYPE_CHECKING, Any, Generic, TypeVar, Protocol, TypeAlias, TypedDict, NamedTuple, runtime_checkable
from typing import (
TYPE_CHECKING,
Any,
Generic,
TypeVar,
Protocol,
ParamSpec,
TypeAlias,
TypedDict,
NamedTuple,
Concatenate,
overload,
runtime_checkable,
)
from nonebot import logger
@@ -17,6 +31,7 @@ TAddon = TypeVar("TAddon", contravariant=True)
TState = TypeVar("TState", contravariant=True)
TEvent = TypeVar("TEvent", contravariant=True)
TFSM = TypeVar("TFSM", bound="FSM", contravariant=True)
P = ParamSpec("P")
class StateError(Exception): ...
@@ -163,6 +178,52 @@ class FSM(Generic[TState, TEvent, TAddon]):
self.started = False
del self.machine
self.current_state = self.graph["initial"]
self.machine = self._core()
logger.trace("FSM closed")
@overload
def reset_on_exception(
func: Callable[Concatenate[TFSM, P], Awaitable[ActionReturn]],
) -> Callable[Concatenate[TFSM, P], Awaitable[ActionReturn]]:
"""自动在发生异常后重置 FSM"""
@overload
def reset_on_exception(
auto_start: bool = False,
) -> Callable[
[Callable[Concatenate[TFSM, P], Awaitable[ActionReturn]]], Callable[Concatenate[TFSM, P], Awaitable[ActionReturn]]
]:
"""自动在异常后重置 FSM,当 auto_start 为 True 时,自动启动 FSM"""
# 参考自 dataclasses.dataclass 的实现
def reset_on_exception(func=None, /, *, auto_start=False): # pyright: ignore[reportInconsistentOverload]
def warp(func: Callable[Concatenate[TFSM, P], Awaitable[ActionReturn]]):
return __reset_clear_up(func, auto_start)
# 判断调用的是 @reset_on_exception 还是 @reset_on_exception(...)
if func is None:
# 调用的是带括号的
return warp
# 调用的是不带括号的
return warp(func)
def __reset_clear_up(func: Callable[Concatenate[TFSM, P], Awaitable[ActionReturn]], auto_start: bool):
@wraps(func)
async def wrapper(fsm_self: TFSM, *args: P.args, **kwargs: P.kwargs) -> ActionReturn:
try:
return await func(fsm_self, *args, **kwargs)
except Exception as e:
logger.error(f"Exception in {func.__name__}: {e}")
await fsm_self.reset()
if auto_start and not fsm_self.started:
await fsm_self.start()
raise e
return wrapper
+6 -1
View File
@@ -14,7 +14,7 @@ from httpx import URL as HttpxURL
from nonebot_bison.types import Target
from .models import DynRawPost
from .fsm import FSM, Condition, StateGraph, Transition, ActionReturn
from .fsm import FSM, Condition, StateGraph, Transition, ActionReturn, reset_on_exception
if TYPE_CHECKING:
from .platforms import Bilibili
@@ -218,6 +218,11 @@ class RetryFSM(FSM[RetryState, RetryEvent, RetryAddon[TBilibili]]):
self.addon.reset_all()
await super().reset()
@override
@reset_on_exception
async def emit(self, event: RetryEvent):
await super().emit(event)
# FIXME: 拿出来是方便测试了,但全局单例会导致所有被装饰的函数共享状态,有待改进
_retry_fsm = RetryFSM(RETRY_GRAPH, RetryAddon["Bilibili"]())
+1 -3
View File
@@ -10,14 +10,12 @@ from ..post import Post
from .platform import NewMessage
from ..types import Target, RawPost
from ..utils import Site, text_similarity
from ..utils.site import create_cookie_client_manager
class RssSite(Site):
name = "rss"
schedule_type = "interval"
schedule_setting = {"seconds": 30}
client_mgr = create_cookie_client_manager("rss")
class RssPost(Post):
@@ -65,7 +63,7 @@ class Rss(NewMessage):
return post.id
async def get_sub_list(self, target: Target) -> list[RawPost]:
client = await self.ctx.get_client(target)
client = await self.ctx.get_client()
res = await client.get(target, timeout=10.0)
feed = feedparser.parse(res)
entries = feed.entries
+2 -1
View File
@@ -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
+2 -22
View File
@@ -14,8 +14,6 @@ from nonebot.adapters.onebot.v11.event import PrivateMessageEvent
from .add_sub import do_add_sub
from .del_sub import do_del_sub
from .query_sub import do_query_sub
from .add_cookie import do_add_cookie
from .add_cookie_target import do_add_cookie_target
from .utils import common_platform, admin_permission, gen_handle_cancel, configurable_to_me, set_target_user_info
add_sub_matcher = on_command(
@@ -28,10 +26,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,
@@ -42,26 +42,6 @@ del_sub_matcher = on_command(
del_sub_matcher.handle()(set_target_user_info)
do_del_sub(del_sub_matcher)
add_cookie_matcher = on_command(
"添加cookie",
rule=configurable_to_me,
permission=admin_permission(),
priority=5,
block=True,
)
add_cookie_matcher.handle()(set_target_user_info)
do_add_cookie(add_cookie_matcher)
add_cookie_target_matcher = on_command(
"关联cookie",
rule=configurable_to_me,
permission=admin_permission(),
priority=5,
block=True,
)
add_cookie_target_matcher.handle()(set_target_user_info)
do_add_cookie_target(add_cookie_target_matcher)
group_manage_matcher = on_command("群管理", rule=to_me(), permission=SUPERUSER, priority=4, block=True)
group_handle_cancel = gen_handle_cancel(group_manage_matcher, "已取消")
-60
View File
@@ -1,60 +0,0 @@
from nonebot.typing import T_State
from nonebot.matcher import Matcher
from nonebot.params import Arg, ArgPlainText
from nonebot.adapters import Message, MessageTemplate
from ..types import Target
from ..config import config
from ..platform import platform_manager
from ..apis import check_sub_target_cookie
from .utils import common_platform, gen_handle_cancel
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):
state["_prompt"] = (
"请输入想要添加 Cookie 的平台,目前支持,请输入冒号左边的名称:\n"
+ "".join(
[f"{platform_name}: {platform_manager[platform_name].name}\n" for platform_name in common_platform]
)
+ "要查看全部平台请输入:“全部”\n中止添加cookie过程请输入:“取消”"
)
@add_cookie.got("platform", MessageTemplate("{_prompt}"), [handle_cancel])
async def parse_platform(state: T_State, platform: str = ArgPlainText()) -> None:
if platform == "全部":
message = "全部平台\n" + "\n".join(
[f"{platform_name}: {platform.name}" for platform_name, platform in platform_manager.items()]
)
await add_cookie.reject(message)
elif platform == "取消":
await add_cookie.finish("已中止添加cookie")
elif platform in platform_manager:
state["platform"] = platform
else:
await add_cookie.reject("平台输入错误")
@add_cookie.handle()
async def prepare_get_id(matcher: Matcher, state: T_State):
cur_platform = platform_manager[state["platform"]]
if cur_platform.has_target:
state["_prompt"] = "请输入 Cookie"
else:
matcher.set_arg("cookie", None) # type: ignore
state["id"] = "default"
@add_cookie.got("cookie", MessageTemplate("{_prompt}"), [handle_cancel])
async def got_cookie(state: T_State, cookie: Message = Arg()):
cookie_text = cookie.extract_plain_text()
state["cookie"] = cookie_text
state["name"] = await check_sub_target_cookie(state["platform"], Target(""), cookie_text)
@add_cookie.handle()
async def add_cookie_process(state: T_State):
await config.add_cookie(state["platform"], state["cookie"])
await add_cookie.finish(
f"已添加 Cookie: {state['cookie']} 到平台 {state['platform']}" + "\n请使用“关联cookie”为 Cookie 关联订阅"
)
@@ -1,63 +0,0 @@
from nonebot.typing import T_State
from nonebot.matcher import Matcher
from nonebot.params import ArgPlainText
from nonebot_plugin_saa import MessageFactory
from nonebot.internal.adapter import MessageTemplate
from ..config import config
from ..utils import parse_text
from ..apis import get_cookie_friendly_name
from .utils import gen_handle_cancel, 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):
res = await generate_sub_list_text(add_cookie_target_matcher, state, is_index=True, is_show_cookie=True)
res += "请输入要关联 cookie 的订阅的序号\n输入'取消'中止"
await MessageFactory(await parse_text(res)).send()
@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]
except Exception:
await add_cookie_target_matcher.reject("序号错误")
@add_cookie_target_matcher.handle()
async def init_promote_cookie(state: T_State):
cookies = await config.get_cookie(platform_name=state["target"]["platform_name"])
associated_cookies = await config.get_cookie(
target=state["target"]["target"],
platform_name=state["target"]["platform_name"],
)
associated_cookie_ids = {cookie.id for cookie in associated_cookies}
cookies = [cookie for cookie in cookies if cookie.id not in associated_cookie_ids]
if not cookies:
await add_cookie_target_matcher.finish(
"当前平台暂无可关联的 Cookie,请使用“添加cookie”命令添加或检查已关联的 Cookie"
)
state["cookies"] = cookies
state["_prompt"] = "请选择一个 Cookie,已关联的 Cookie 不会显示\n" + "\n".join(
[f"{idx}. {await get_cookie_friendly_name(cookie)}" for idx, cookie in enumerate(cookies, 1)]
)
@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]
except Exception:
await add_cookie_target_matcher.reject("序号错误")
@add_cookie_target_matcher.handle()
async def add_cookie_target_process(state: T_State):
await config.add_cookie_target(state["target"]["target"], state["target"]["platform_name"], state["cookie"].id)
await add_cookie_target_matcher.finish(
f"已关联 Cookie: {await get_cookie_friendly_name(state['cookie'])} "
f"到订阅 {state['target']['platform_name']} {state['target']['target']}"
)
+1 -49
View File
@@ -1,21 +1,16 @@
import contextlib
from typing import Annotated
from itertools import groupby
from operator import attrgetter
from nonebot.rule import Rule
from nonebot.adapters import Event
from nonebot.typing import T_State
from nonebot.matcher import Matcher
from nonebot.permission import SUPERUSER
from nonebot_plugin_saa import extract_target
from nonebot.params import Depends, EventToMe, EventPlainText
from nonebot_plugin_saa import PlatformTarget, extract_target
from ..config import config
from ..types import Category
from ..platform import platform_manager
from ..plugin_config import plugin_config
from ..apis import get_cookie_friendly_name
def _configurable_to_me(to_me: bool = EventToMe()):
@@ -65,46 +60,3 @@ def admin_permission():
permission = permission | GROUP_ADMIN | GROUP_OWNER
return permission
async def generate_sub_list_text(
matcher: type[Matcher], state: T_State, user_info: PlatformTarget = None, is_index=False, is_show_cookie=False
):
if user_info:
sub_list = await config.list_subscribe(user_info)
else:
sub_list = await config.list_subs_with_all_info()
sub_list = [
next(group)
for key, group in groupby(sorted(sub_list, key=attrgetter("target_id")), key=attrgetter("target_id"))
]
if not sub_list:
await matcher.finish("暂无已订阅账号\n请使用“添加订阅”命令添加订阅")
res = "订阅的帐号为:\n"
state["sub_table"] = {}
for index, sub in enumerate(sub_list, 1):
state["sub_table"][index] = {
"platform_name": sub.target.platform_name,
"target": sub.target.target,
}
res += f"{index} " if is_index else ""
res += f"{sub.target.platform_name} {sub.target.target_name} {sub.target.target}\n"
if platform := platform_manager.get(sub.target.platform_name):
if platform.categories:
res += " [{}]".format(", ".join(platform.categories[Category(x)] for x in sub.categories)) + "\n"
if platform.enable_tag:
if sub.tags:
res += " {}".format(", ".join(sub.tags)) + "\n"
if is_show_cookie:
target_cookies = await config.get_cookie(
target=sub.target.target, platform_name=sub.target.platform_name
)
if target_cookies:
res += " 关联的 Cookie\n"
for cookie in target_cookies:
res += f" \t{await get_cookie_friendly_name(cookie)}\n"
else:
res += f" (平台 {sub.target.platform_name} 已失效,请删除此订阅)"
return res
-42
View File
@@ -1,12 +1,9 @@
import json
from typing import Literal
from abc import ABC, abstractmethod
import httpx
from httpx import AsyncClient
from ..types import Target
from ..config import config
from .http import http_client
@@ -38,45 +35,6 @@ class DefaultClientManager(ClientManager):
pass
class CookieClientManager(ClientManager):
_platform_name: str
async def _choose_cookie(self, target: Target) -> dict[str, str]:
if not target:
return {}
cookies = await config.get_cookie_by_target(target, self._platform_name)
if not cookies:
return {}
cookie = sorted(cookies, key=lambda x: x.last_usage, reverse=True)[0]
return json.loads(cookie.content)
async def get_client(self, target: Target | None) -> AsyncClient:
client = http_client()
cookie = await self._choose_cookie(target)
cookies = httpx.Cookies()
if cookie:
cookies.update(cookie)
client.cookies = cookies
return client
async def get_client_for_static(self) -> AsyncClient:
pass
async def get_query_name_client(self) -> AsyncClient:
pass
async def refresh_client(self):
pass
def create_cookie_client_manager(platform_name: str) -> type[CookieClientManager]:
return type(
"CookieClientManager",
(CookieClientManager,),
{"_platform_name": platform_name},
)
class Site:
schedule_type: Literal["date", "interval", "cron"]
schedule_setting: dict
+4 -4
View File
@@ -11,9 +11,9 @@
"docs:update-package": "pnpm dlx vp-update"
},
"devDependencies": {
"@vuepress/bundler-vite": "2.0.0-rc.14",
"vue": "^3.4.31",
"vuepress": "2.0.0-rc.14",
"vuepress-theme-hope": "2.0.0-rc.50"
"@vuepress/bundler-vite": "2.0.0-rc.15",
"vue": "^3.5.6",
"vuepress": "2.0.0-rc.15",
"vuepress-theme-hope": "2.0.0-rc.52"
}
}
+2120 -1770
View File
File diff suppressed because it is too large Load Diff
Generated
+536 -519
View File
File diff suppressed because it is too large Load Diff
+24 -24
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "nonebot-bison"
version = "0.9.3"
version = "0.9.4"
description = "Subscribe message from social medias"
authors = ["felinae98 <felinae225@qq.com>"]
license = "MIT"
@@ -24,40 +24,40 @@ classifiers = [
python = ">=3.10,<4.0.0"
beautifulsoup4 = ">=4.12.3"
feedparser = "^6.0.11"
httpx = ">=0.27.0"
nonebot2 = { extras = ["fastapi"], version = "^2.3.2" }
nonebot-adapter-onebot = "^2.4.4"
nonebot-plugin-htmlrender = ">=0.3.3"
httpx = ">=0.27.2"
nonebot2 = { extras = ["fastapi"], version = "^2.3.3" }
nonebot-adapter-onebot = "^2.4.5"
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.6.1,<0.7.0"
pillow = ">=8.4.0,<11.0"
pyjwt = "^2.8.0"
python-socketio = "^5.11.3"
nonebot-plugin-send-anything-anywhere = ">=0.7.1,<0.7.2"
pillow = ">=10.4.0,<11.0"
pyjwt = "^2.9.0"
python-socketio = "^5.11.4"
tinydb = "^4.8.0"
qrcode = "^7.4.2"
pydantic = ">=1.10.17,<3.0.0,!=2.5.0,!=2.5.1"
lxml = ">=5.2.2"
yarl = ">=1.9.4"
hishel = "^0.0.20"
expiringdictx = "^1.0.1"
rapidfuzz = "^3.9.3"
pydantic = ">=2.9.2,<3.0.0,!=2.5.0,!=2.5.1"
lxml = ">=5.3.0"
yarl = ">=1.11.1"
hishel = "^0.0.30"
expiringdictx = "^1.1.0"
rapidfuzz = "^3.9.7"
[tool.poetry.group.dev.dependencies]
black = ">=23.12.1,<25.0"
black = ">=24.8.0,<25.0"
ipdb = "^0.13.13"
isort = "^5.13.2"
nonemoji = "^0.1.4"
nb-cli = "^1.4.1"
pre-commit = "^3.7.1"
ruff = "^0.6.0"
nb-cli = "^1.4.2"
pre-commit = "^3.8.0"
ruff = "^0.6.5"
[tool.poetry.group.test.dependencies]
flaky = "^3.8.1"
nonebug = "^0.3.7"
nonebug-saa = "^0.4.1"
pytest = ">=7.4.4,<9.0.0"
pytest-asyncio = ">=0.23.7,<0.24.0"
pytest = ">=8.3.3,<9.0.0"
pytest-asyncio = ">=0.24.0,<0.24.1"
pytest-cov = ">=5.0.0,<6"
pytest-mock = "^3.14.0"
pytest-xdist = { extras = ["psutil"], version = "^3.6.1" }
@@ -68,10 +68,10 @@ freezegun = "^1.5.1"
optional = true
[tool.poetry.group.docker.dependencies]
nb-cli = "^1.4.1"
nonebot2 = { extras = ["fastapi", "aiohttp"], version = "^2.3.2" }
nb-cli = "^1.4.2"
nonebot2 = { extras = ["fastapi", "aiohttp"], version = "^2.3.3" }
nonebot-adapter-red = "^0.9.0"
nonebot-adapter-qq = "^1.4.4"
nonebot-adapter-qq = "^1.5.1"
poetry-core = "^1.9.0"
[tool.poetry.extras]
View File
-107
View File
@@ -1,107 +0,0 @@
import datetime
from nonebug import App
async def test_get_platform_target(app: App, init_scheduler):
from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.config.db_config import config
from nonebot_bison.types import Target as T_Target
await config.add_subscribe(
TargetQQGroup(group_id=123),
target=T_Target("weibo_id"),
target_name="weibo_name",
platform_name="weibo",
cats=[],
tags=[],
)
# await config.add_cookie(TargetQQGroup(group_id=123), "weibo", "cookie")
# cookies = await config.get_cookie_by_user(TargetQQGroup(group_id=123))
#
# res = await config.get_platform_target("weibo")
# assert len(res) == 2
# await config.del_subscribe(TargetQQGroup(group_id=123), T_Target("weibo_id1"), "weibo")
# res = await config.get_platform_target("weibo")
# assert len(res) == 2
# await config.del_subscribe(TargetQQGroup(group_id=123), T_Target("weibo_id"), "weibo")
# res = await config.get_platform_target("weibo")
# assert len(res) == 1
#
# async with AsyncSession(get_engine()) as sess:
# res = await sess.scalars(select(Target).where(Target.platform_name == "weibo"))
# assert len(res.all()) == 2
# await config.get_cookie_by_user(TargetQQGroup(group_id=123))
async def test_cookie_by_user(app: App, init_scheduler):
from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.config.db_config import config
from nonebot_bison.types import Target as T_Target
await config.add_subscribe(
TargetQQGroup(group_id=123),
target=T_Target("weibo_id"),
target_name="weibo_name",
platform_name="weibo",
cats=[],
tags=[],
)
await config.add_cookie(TargetQQGroup(group_id=123), "weibo", "cookie")
cookies = await config.get_cookie(TargetQQGroup(group_id=123))
cookie = cookies[0]
assert len(cookies) == 1
assert cookie.content == "cookie"
assert cookie.platform_name == "weibo"
cookie.last_usage = 0
assert cookie.status == ""
assert cookie.tags == {}
cookie.content = "cookie1"
cookie.last_usage = datetime.datetime(2024, 8, 22, 0, 0, 0)
cookie.status = "status1"
cookie.tags = {"tag1": "value1"}
await config.update_cookie(cookie)
cookies = await config.get_cookie(TargetQQGroup(group_id=123))
assert len(cookies) == 1
assert cookies[0].content == cookie.content
assert cookies[0].last_usage == cookie.last_usage
assert cookies[0].status == cookie.status
assert cookies[0].tags == cookie.tags
await config.delete_cookie(cookies[0].id)
cookies = await config.get_cookie(TargetQQGroup(group_id=123))
assert len(cookies) == 0
async def test_cookie_target_by_target(app: App, init_scheduler):
from nonebot_plugin_saa import TargetQQGroup
from nonebot_bison.config.db_config import config
from nonebot_bison.types import Target as T_Target
await config.add_subscribe(
TargetQQGroup(group_id=123),
target=T_Target("weibo_id"),
target_name="weibo_name",
platform_name="weibo",
cats=[],
tags=[],
)
id = await config.add_cookie(TargetQQGroup(group_id=123), "weibo", "cookie")
await config.add_cookie_target(T_Target("weibo_id"), "weibo", id)
cookies = await config.get_cookie_by_target(T_Target("weibo_id"), "weibo")
assert len(cookies) == 1
assert cookies[0].content == "cookie"
assert cookies[0].platform_name == "weibo"
await config.delete_cookie_target(T_Target("weibo_id"), "weibo", id)
cookies = await config.get_cookie_by_target(T_Target("weibo_id"), "weibo")
assert len(cookies) == 0
+86
View File
@@ -58,6 +58,92 @@ def without_dynamic(app: App):
)
@pytest.mark.asyncio
async def test_reset_on_exception(app: App):
from strenum import StrEnum
from nonebot_bison.platform.bilibili.fsm import FSM, StateGraph, Transition, ActionReturn, reset_on_exception
class State(StrEnum):
A = "A"
B = "B"
C = "C"
class Event(StrEnum):
A = "A"
B = "B"
C = "C"
class Addon:
pass
async def raction(from_: State, event: Event, to: State, addon: Addon) -> ActionReturn:
logger.info(f"action: {from_} -> {to}")
raise RuntimeError("test")
async def action(from_: State, event: Event, to: State, addon: Addon) -> ActionReturn:
logger.info(f"action: {from_} -> {to}")
graph: StateGraph[State, Event, Addon] = {
"transitions": {
State.A: {
Event.A: Transition(raction, State.B),
Event.B: Transition(action, State.C),
},
State.B: {
Event.B: Transition(action, State.C),
},
State.C: {
Event.C: Transition(action, State.A),
},
},
"initial": State.A,
}
addon = Addon()
class AFSM(FSM[State, Event, Addon]):
@reset_on_exception(auto_start=True)
async def emit(self, event: Event):
return await super().emit(event)
fsm = AFSM(graph, addon)
await fsm.start()
with pytest.raises(RuntimeError):
await fsm.emit(Event.A)
assert fsm.started is True
await fsm.emit(Event.B)
await fsm.emit(Event.C)
class BFSM(FSM[State, Event, Addon]):
@reset_on_exception
async def emit(self, event: Event):
return await super().emit(event)
fsm = BFSM(graph, addon)
await fsm.start()
with pytest.raises(RuntimeError):
await fsm.emit(Event.A)
assert fsm.started is False
with pytest.raises(TypeError, match="can't send non-None value to a just-started async generator"):
await fsm.emit(Event.B)
class CFSM(FSM[State, Event, Addon]): ...
fsm = CFSM(graph, addon)
await fsm.start()
with pytest.raises(RuntimeError):
await fsm.emit(Event.A)
assert fsm.started is True
with pytest.raises(StopAsyncIteration):
await fsm.emit(Event.B)
@pytest.mark.asyncio
async def test_retry_for_352(app: App, mocker: MockerFixture):
from nonebot_bison.post import Post
+3 -2
View File
@@ -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"}'
]