Merge branch 'feature/new-use' of https://github.com/HibiKier/zhenxun_bot into feature/new-use

This commit is contained in:
HibiKier 2025-04-12 20:41:12 +08:00
commit 6e7b6888b1
25 changed files with 11219 additions and 918 deletions

40
.github/actions/setup-python/action.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Setup Python
description: Setup Python
inputs:
python-version:
description: Python version
required: false
default: "3.10"
env-dir:
description: Environment directory
required: false
default: "."
no-root:
description: Do not install package in the environment
required: false
default: "false"
runs:
using: "composite"
steps:
- name: Install poetry
run: pipx install poetry
shell: bash
- uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
cache: "poetry"
cache-dependency-path: |
./poetry.lock
${{ inputs.env-dir }}/poetry.lock
- run: |
cd ${{ inputs.env-dir }}
if [ "${{ inputs.no-root }}" = "true" ]; then
poetry install --all-extras --no-root
else
poetry install --all-extras
fi
shell: bash

11
.github/workflows/linting.yml vendored Normal file
View File

@ -0,0 +1,11 @@
name: Sequential Lint and Type Check
on: [push, pull_request]
jobs:
ruff-call:
uses: ./.github/workflows/ruff.yml
pyright-call:
needs: ruff-call
uses: ./.github/workflows/pyright.yml

55
.github/workflows/pyright.yml vendored Normal file
View File

@ -0,0 +1,55 @@
name: Pyright Lint
on:
workflow_call:
workflow_dispatch:
inputs:
python-version:
description: "Python version"
required: false
type: choice
options:
- "all"
- "3.10"
- "3.11"
- "3.12"
default: "all"
debug-mode:
description: "enable debug mode"
required: false
type: boolean
default: false
jobs:
pyright:
name: Pyright Lint
runs-on: ubuntu-latest
concurrency:
group: pyright-${{ github.ref }}-${{ matrix.env }}
cancel-in-progress: true
strategy:
matrix:
env: [pydantic-v1, pydantic-v2]
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Setup Python environment
uses: ./.github/actions/setup-python
with:
env-dir: ./envs/${{ matrix.env }}
no-root: true
- run: |
(cd ./envs/${{ matrix.env }} && echo "$(poetry env info --path)/bin" >> $GITHUB_PATH)
if [ "${{ matrix.env }}" = "pydantic-v1" ]; then
sed -i 's/PYDANTIC_V2 = true/PYDANTIC_V2 = false/g' ./pyproject.toml
fi
shell: bash
- name: Run Pyright Check
uses: jakebailey/pyright-action@v2
with:
pylance-version: latest-release

20
.github/workflows/ruff.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: Ruff Lint
on:
workflow_call:
jobs:
ruff:
name: Ruff Lint
runs-on: ubuntu-latest
concurrency:
group: ruff-${{ github.ref }}
cancel-in-progress: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Ruff
uses: astral-sh/ruff-action@v3
- name: Run Ruff Check
run: ruff check

View File

@ -2,8 +2,6 @@
"recommendations": [ "recommendations": [
"charliermarsh.ruff", "charliermarsh.ruff",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"ms-python.black-formatter",
"ms-python.isort",
"ms-python.python", "ms-python.python",
"ms-python.vscode-pylance" "ms-python.vscode-pylance"
] ]

View File

@ -16,6 +16,7 @@
"jsdelivr", "jsdelivr",
"kaiheila", "kaiheila",
"lolicon", "lolicon",
"Mahiro",
"nonebot", "nonebot",
"onebot", "onebot",
"pixiv", "pixiv",
@ -24,19 +25,19 @@
"tobytes", "tobytes",
"ujson", "ujson",
"unban", "unban",
"Uninfo",
"userinfo", "userinfo",
"zhenxun", "zhenxun"
"jsdelivr"
], ],
"python.analysis.autoImportCompletions": true, "python.analysis.autoImportCompletions": true,
"python.testing.pytestArgs": ["tests"], "python.testing.pytestArgs": ["tests"],
"python.testing.unittestEnabled": false, "python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true, "python.testing.pytestEnabled": true,
"[python]": { "[python]": {
"editor.defaultFormatter": "charliermarsh.ruff", // 使 Ruff "editor.defaultFormatter": "charliermarsh.ruff",
"editor.wordBasedSuggestions": "allDocuments", "editor.wordBasedSuggestions": "allDocuments",
"editor.formatOnType": true, "editor.formatOnType": true,
"editor.formatOnSave": true, // "editor.formatOnSave": true,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit", "source.fixAll.ruff": "explicit",
"source.organizeImports": "explicit" "source.organizeImports": "explicit"

View File

@ -44,7 +44,7 @@
<div align=center> <div align=center>
[文档](https://hibikier.github.io/zhenxun_bot/) [文档](https://zhenxun-org.github.io/zhenxun_bot/)
</div> </div>
@ -124,7 +124,7 @@ AccessToken: PUBLIC_ZHENXUN_TEST
- 通过 Config 配置项将所有插件配置统计保存至 config.yaml利于统一用户修改 - 通过 Config 配置项将所有插件配置统计保存至 config.yaml利于统一用户修改
- 方便增删插件,原生 nonebot2 matcher不需要额外修改仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息` - 方便增删插件,原生 nonebot2 matcher不需要额外修改仅仅通过简单的配置属性就可以生成`帮助图片`和`帮助信息`
- 提供了 cd阻塞每日次数等限制仅仅通过简单的属性就可以生成一个限制例如`PluginCdBlock` 等 - 提供了 cd阻塞每日次数等限制仅仅通过简单的属性就可以生成一个限制例如`PluginCdBlock` 等
- **更多详细请通过 [传送门](https://hibikier.github.io/zhenxun_bot/) 查看文档!** - **更多详细请通过 [传送门](https://zhenxun-org.github.io/zhenxun_bot/) 查看文档!**
## 🛠️ 简单部署 ## 🛠️ 简单部署

67
docker-compose-dev.yml Normal file
View File

@ -0,0 +1,67 @@
services:
db:
image: postgres:15
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: zhenxun
volumes:
- pgdata:/var/lib/postgresql/data
labels:
- "prometheus.io/scrape=true"
- "prometheus.io/port=9187"
postgres-exporter:
image: prometheuscommunity/postgres-exporter
environment:
DATA_SOURCE_NAME: "postgresql://postgres:password@db:5432/zhenxun?sslmode=disable"
ports:
- "9187:9187"
depends_on:
- db
redis:
image: redis:7
ports:
- "6379:6379"
labels:
- "prometheus.io/scrape=true"
- "prometheus.io/port=9121"
redis-exporter:
image: oliver006/redis_exporter
environment:
REDIS_ADDR: redis://redis:6379
ports:
- "9121:9121"
depends_on:
- redis
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
grafana:
image: grafana/grafana
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
depends_on:
- prometheus
volumes:
pgdata:
prometheus_data:
grafana_data:

4811
envs/pydantic-v1/poetry.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,140 @@
[tool.poetry]
name = "zhenxun_bot"
version = "0.2.4"
description = "基于 Nonebot2 和 go-cqhttp 开发,以 postgresql 作为数据库非常可爱的绪山真寻bot"
authors = ["HibiKier <775757368@qq.com>"]
license = "AGPL"
package-mode = false
[[tool.poetry.source]]
name = "aliyun"
url = "https://mirrors.aliyun.com/pypi/simple/"
priority = "primary"
[tool.poetry.dependencies]
python = "^3.10"
playwright = "^1.41.1"
nonebot-adapter-onebot = "^2.3.1"
nonebot-plugin-apscheduler = "^0.5"
tortoise-orm = { extras = ["asyncpg"], version = "^0.20.0" }
cattrs = "^23.2.3"
ruamel-yaml = "^0.18.5"
strenum = "^0.4.15"
nonebot-plugin-session = "^0.2.3"
ujson = "^5.9.0"
nb-cli = "^1.3.0"
nonebot2 = { extras = ["fastapi"], version = "^2.3.3" }
pillow = "^10.0.0"
retrying = "^1.3.4"
aiofiles = "^23.2.1"
nonebot-plugin-htmlrender = ">=0.6.0,<1.0.0"
pypinyin = "^0.51.0"
beautifulsoup4 = "^4.12.3"
lxml = "^5.1.0"
psutil = "^5.9.8"
feedparser = "^6.0.11"
imagehash = "^4.3.1"
cn2an = "^0.5.22"
dateparser = "^1.2.0"
bilireq = "0.2.3post0"
python-jose = { extras = ["cryptography"], version = "^3.3.0" }
python-multipart = "^0.0.9"
aiocache = "^0.12.2"
py-cpuinfo = "^9.0.0"
nonebot-plugin-alconna = "^0.54.0"
tenacity = "^9.0.0"
nonebot-plugin-uninfo = ">0.4.1"
pydantic = "1.10.18"
[tool.poetry.group.dev.dependencies]
nonebug = "^0.4"
pytest-cov = "^5.0.0"
pytest-mock = "^3.6.1"
pytest-asyncio = "^0.25"
pytest-xdist = "^3.3.1"
respx = "^0.21.1"
ruff = "^0.8.0"
pre-commit = "^4.0.0"
[tool.nonebot]
plugins = [
"nonebot_plugin_apscheduler",
"nonebot_plugin_session",
"nonebot_plugin_htmlrender",
"nonebot_plugin_alconna",
]
plugin_dirs = ["zhenxun/services", "zhenxun/builtin_plugins", "zhenxun/plugins"]
adapters = [
{ name = "OneBot V11", module_name = "nonebot.adapters.onebot.v11" },
# { name = "DoDo", module_name = "nonebot.adapters.dodo" },
# { name = "开黑啦", module_name = "nonebot.adapters.kaiheila" },
]
[tool.ruff]
line-length = 88
target-version = "py310"
[tool.ruff.format]
line-ending = "lf"
[tool.ruff.lint]
select = [
"F", # Pyflakes
"W", # pycodestyle warnings
"E", # pycodestyle errors
"I", # isort
"UP", # pyupgrade
"ASYNC", # flake8-async
"C4", # flake8-comprehensions
"T10", # flake8-debugger
"T20", # flake8-print
"PYI", # flake8-pyi
"PT", # flake8-pytest-style
"Q", # flake8-quotes
"TID", # flake8-tidy-imports
"RUF", # Ruff-specific rules
]
ignore = [
"E402", # module-import-not-at-top-of-file
"UP037", # quoted-annotation
"RUF001", # ambiguous-unicode-character-string
"RUF002", # ambiguous-unicode-character-docstring
"RUF003", # ambiguous-unicode-character-comment
"TID252", # relative-imports
]
[tool.ruff.lint.isort]
force-sort-within-sections = true
known-first-party = ["zhenxun", "tests/*"]
extra-standard-library = ["typing_extensions"]
[tool.ruff.lint.flake8-pytest-style]
fixture-parentheses = false
mark-parentheses = false
[tool.ruff.lint.pyupgrade]
keep-runtime-typing = true
[tool.pyright]
pythonVersion = "3.10"
pythonPlatform = "All"
defineConstant = { PYDANTIC_V2 = true }
executionEnvironments = [
{ root = "./tests", extraPaths = [
"./",
] },
{ root = "./" },
]
typeCheckingMode = "standard"
reportShadowedImports = false
disableBytesTypePromotions = true
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

4905
envs/pydantic-v2/poetry.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,140 @@
[tool.poetry]
name = "zhenxun_bot"
version = "0.2.4"
description = "基于 Nonebot2 和 go-cqhttp 开发,以 postgresql 作为数据库非常可爱的绪山真寻bot"
authors = ["HibiKier <775757368@qq.com>"]
license = "AGPL"
package-mode = false
[[tool.poetry.source]]
name = "aliyun"
url = "https://mirrors.aliyun.com/pypi/simple/"
priority = "primary"
[tool.poetry.dependencies]
python = "^3.10"
playwright = "^1.41.1"
nonebot-adapter-onebot = "^2.3.1"
nonebot-plugin-apscheduler = "^0.5"
tortoise-orm = { extras = ["asyncpg"], version = "^0.20.0" }
cattrs = "^23.2.3"
ruamel-yaml = "^0.18.5"
strenum = "^0.4.15"
nonebot-plugin-session = "^0.2.3"
ujson = "^5.9.0"
nb-cli = "^1.3.0"
nonebot2 = { extras = ["fastapi"], version = "^2.3.3" }
pillow = "^10.0.0"
retrying = "^1.3.4"
aiofiles = "^23.2.1"
nonebot-plugin-htmlrender = ">=0.6.0,<1.0.0"
pypinyin = "^0.51.0"
beautifulsoup4 = "^4.12.3"
lxml = "^5.1.0"
psutil = "^5.9.8"
feedparser = "^6.0.11"
imagehash = "^4.3.1"
cn2an = "^0.5.22"
dateparser = "^1.2.0"
bilireq = "0.2.3post0"
python-jose = { extras = ["cryptography"], version = "^3.3.0" }
python-multipart = "^0.0.9"
aiocache = "^0.12.2"
py-cpuinfo = "^9.0.0"
nonebot-plugin-alconna = "^0.54.0"
tenacity = "^9.0.0"
nonebot-plugin-uninfo = ">0.4.1"
pydantic = "2.10.6"
[tool.poetry.group.dev.dependencies]
nonebug = "^0.4"
pytest-cov = "^5.0.0"
pytest-mock = "^3.6.1"
pytest-asyncio = "^0.25"
pytest-xdist = "^3.3.1"
respx = "^0.21.1"
ruff = "^0.8.0"
pre-commit = "^4.0.0"
[tool.nonebot]
plugins = [
"nonebot_plugin_apscheduler",
"nonebot_plugin_session",
"nonebot_plugin_htmlrender",
"nonebot_plugin_alconna",
]
plugin_dirs = ["zhenxun/services", "zhenxun/builtin_plugins", "zhenxun/plugins"]
adapters = [
{ name = "OneBot V11", module_name = "nonebot.adapters.onebot.v11" },
# { name = "DoDo", module_name = "nonebot.adapters.dodo" },
# { name = "开黑啦", module_name = "nonebot.adapters.kaiheila" },
]
[tool.ruff]
line-length = 88
target-version = "py310"
[tool.ruff.format]
line-ending = "lf"
[tool.ruff.lint]
select = [
"F", # Pyflakes
"W", # pycodestyle warnings
"E", # pycodestyle errors
"I", # isort
"UP", # pyupgrade
"ASYNC", # flake8-async
"C4", # flake8-comprehensions
"T10", # flake8-debugger
"T20", # flake8-print
"PYI", # flake8-pyi
"PT", # flake8-pytest-style
"Q", # flake8-quotes
"TID", # flake8-tidy-imports
"RUF", # Ruff-specific rules
]
ignore = [
"E402", # module-import-not-at-top-of-file
"UP037", # quoted-annotation
"RUF001", # ambiguous-unicode-character-string
"RUF002", # ambiguous-unicode-character-docstring
"RUF003", # ambiguous-unicode-character-comment
"TID252", # relative-imports
]
[tool.ruff.lint.isort]
force-sort-within-sections = true
known-first-party = ["zhenxun", "tests/*"]
extra-standard-library = ["typing_extensions"]
[tool.ruff.lint.flake8-pytest-style]
fixture-parentheses = false
mark-parentheses = false
[tool.ruff.lint.pyupgrade]
keep-runtime-typing = true
[tool.pyright]
pythonVersion = "3.10"
pythonPlatform = "All"
defineConstant = { PYDANTIC_V2 = true }
executionEnvironments = [
{ root = "./tests", extraPaths = [
"./",
] },
{ root = "./" },
]
typeCheckingMode = "standard"
reportShadowedImports = false
disableBytesTypePromotions = true
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

1541
poetry.lock generated

File diff suppressed because it is too large Load Diff

12
prometheus.yml Normal file
View File

@ -0,0 +1,12 @@
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'postgresql'
static_configs:
- targets: [ 'postgres-exporter:9187' ]
- job_name: 'redis'
static_configs:
- targets: [ 'redis-exporter:9121' ]

View File

@ -45,6 +45,7 @@ nonebot-plugin-alconna = "^0.54.0"
tenacity = "^9.0.0" tenacity = "^9.0.0"
nonebot-plugin-uninfo = ">0.4.1" nonebot-plugin-uninfo = ">0.4.1"
nonebot-plugin-waiter = "^0.8.1" nonebot-plugin-waiter = "^0.8.1"
multidict = ">=6.0.0,!=6.3.2"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
nonebug = "^0.4" nonebug = "^0.4"

View File

@ -7,7 +7,7 @@ apscheduler==3.11.0 ; python_version >= "3.10" and python_version < "4.0"
arclet-alconna-tools==0.7.10 ; python_version >= "3.10" and python_version < "4.0" arclet-alconna-tools==0.7.10 ; python_version >= "3.10" and python_version < "4.0"
arclet-alconna==1.8.35 ; python_version >= "3.10" and python_version < "4.0" arclet-alconna==1.8.35 ; python_version >= "3.10" and python_version < "4.0"
arrow==1.3.0 ; python_version >= "3.10" and python_version < "4.0" arrow==1.3.0 ; python_version >= "3.10" and python_version < "4.0"
async-timeout==5.0.1 ; python_version >= "3.10" and python_version < "3.11.0" async-timeout==5.0.1 ; python_version == "3.10"
asyncpg==0.30.0 ; python_version >= "3.10" and python_version < "4.0" asyncpg==0.30.0 ; python_version >= "3.10" and python_version < "4.0"
attrs==25.1.0 ; python_version >= "3.10" and python_version < "4.0" attrs==25.1.0 ; python_version >= "3.10" and python_version < "4.0"
beautifulsoup4==4.13.3 ; python_version >= "3.10" and python_version < "4.0" beautifulsoup4==4.13.3 ; python_version >= "3.10" and python_version < "4.0"
@ -21,7 +21,7 @@ chardet==5.2.0 ; python_version >= "3.10" and python_version < "4.0"
charset-normalizer==3.4.1 ; python_version >= "3.10" and python_version < "4.0" charset-normalizer==3.4.1 ; python_version >= "3.10" and python_version < "4.0"
click==8.1.8 ; python_version >= "3.10" and python_version < "4.0" click==8.1.8 ; python_version >= "3.10" and python_version < "4.0"
cn2an==0.5.23 ; python_version >= "3.10" and python_version < "4.0" cn2an==0.5.23 ; python_version >= "3.10" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.10" and python_version < "4.0" and platform_system == "Windows" colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" and (platform_system == "Windows" or sys_platform == "win32")
cookiecutter==2.6.0 ; python_version >= "3.10" and python_version < "4.0" cookiecutter==2.6.0 ; python_version >= "3.10" and python_version < "4.0"
cryptography==44.0.1 ; python_version >= "3.10" and python_version < "4.0" cryptography==44.0.1 ; python_version >= "3.10" and python_version < "4.0"
dateparser==1.2.1 ; python_version >= "3.10" and python_version < "4.0" dateparser==1.2.1 ; python_version >= "3.10" and python_version < "4.0"
@ -60,6 +60,7 @@ nonebot-plugin-session==0.2.3 ; python_version >= "3.10" and python_version < "4
nonebot-plugin-uninfo==0.6.8 ; python_version >= "3.10" and python_version < "4.0" nonebot-plugin-uninfo==0.6.8 ; python_version >= "3.10" and python_version < "4.0"
nonebot-plugin-waiter==0.8.1 ; python_version >= "3.10" and python_version < "4.0" nonebot-plugin-waiter==0.8.1 ; python_version >= "3.10" and python_version < "4.0"
nonebot2==2.4.1 ; python_version >= "3.10" and python_version < "4.0" nonebot2==2.4.1 ; python_version >= "3.10" and python_version < "4.0"
nonebot2[fastapi]==2.4.1 ; python_version >= "3.10" and python_version < "4.0"
noneprompt==0.1.9 ; python_version >= "3.10" and python_version < "4.0" noneprompt==0.1.9 ; python_version >= "3.10" and python_version < "4.0"
numpy==2.2.2 ; python_version >= "3.10" and python_version < "4.0" numpy==2.2.2 ; python_version >= "3.10" and python_version < "4.0"
pillow==10.4.0 ; python_version >= "3.10" and python_version < "4.0" pillow==10.4.0 ; python_version >= "3.10" and python_version < "4.0"
@ -81,10 +82,10 @@ pygments==2.19.1 ; python_version >= "3.10" and python_version < "4.0"
pygtrie==2.5.0 ; python_version >= "3.10" and python_version < "4.0" pygtrie==2.5.0 ; python_version >= "3.10" and python_version < "4.0"
pymdown-extensions==10.14.3 ; python_version >= "3.10" and python_version < "4.0" pymdown-extensions==10.14.3 ; python_version >= "3.10" and python_version < "4.0"
pypika-tortoise==0.1.6 ; python_version >= "3.10" and python_version < "4.0" pypika-tortoise==0.1.6 ; python_version >= "3.10" and python_version < "4.0"
pypinyin==0.51.0 ; python_version >= "3.10" and python_version < "4.0" pypinyin==0.51.0 ; python_version >= "3.10" and python_version < "4"
python-dateutil==2.9.0.post0 ; python_version >= "3.10" and python_version < "4.0" python-dateutil==2.9.0.post0 ; python_version >= "3.10" and python_version < "4.0"
python-dotenv==1.0.1 ; python_version >= "3.10" and python_version < "4.0" python-dotenv==1.0.1 ; python_version >= "3.10" and python_version < "4.0"
python-jose==3.3.0 ; python_version >= "3.10" and python_version < "4.0" python-jose[cryptography]==3.3.0 ; python_version >= "3.10" and python_version < "4.0"
python-markdown-math==0.8 ; python_version >= "3.10" and python_version < "4.0" python-markdown-math==0.8 ; python_version >= "3.10" and python_version < "4.0"
python-multipart==0.0.9 ; python_version >= "3.10" and python_version < "4.0" python-multipart==0.0.9 ; python_version >= "3.10" and python_version < "4.0"
python-slugify==8.0.4 ; python_version >= "3.10" and python_version < "4.0" python-slugify==8.0.4 ; python_version >= "3.10" and python_version < "4.0"
@ -94,10 +95,10 @@ pyyaml==6.0.2 ; python_version >= "3.10" and python_version < "4.0"
regex==2024.11.6 ; python_version >= "3.10" and python_version < "4.0" regex==2024.11.6 ; python_version >= "3.10" and python_version < "4.0"
requests==2.32.3 ; python_version >= "3.10" and python_version < "4.0" requests==2.32.3 ; python_version >= "3.10" and python_version < "4.0"
retrying==1.3.4 ; python_version >= "3.10" and python_version < "4.0" retrying==1.3.4 ; python_version >= "3.10" and python_version < "4.0"
rfc3986==1.5.0 ; python_version >= "3.10" and python_version < "4.0" rfc3986[idna2008]==1.5.0 ; python_version >= "3.10" and python_version < "4.0"
rich==13.9.4 ; python_version >= "3.10" and python_version < "4.0" rich==13.9.4 ; python_version >= "3.10" and python_version < "4.0"
rsa==4.9 ; python_version >= "3.10" and python_version < "4.0" rsa==4.9 ; python_version >= "3.10" and python_version < "4"
ruamel-yaml-clib==0.2.12 ; python_version >= "3.10" and python_version < "3.13" and platform_python_implementation == "CPython" ruamel-yaml-clib==0.2.12 ; platform_python_implementation == "CPython" and python_version < "3.13" and python_version >= "3.10"
ruamel-yaml==0.18.10 ; python_version >= "3.10" and python_version < "4.0" ruamel-yaml==0.18.10 ; python_version >= "3.10" and python_version < "4.0"
scipy==1.15.1 ; python_version >= "3.10" and python_version < "4.0" scipy==1.15.1 ; python_version >= "3.10" and python_version < "4.0"
sgmllib3k==1.0.0 ; python_version >= "3.10" and python_version < "4.0" sgmllib3k==1.0.0 ; python_version >= "3.10" and python_version < "4.0"
@ -109,17 +110,17 @@ strenum==0.4.15 ; python_version >= "3.10" and python_version < "4.0"
tarina==0.6.8 ; python_version >= "3.10" and python_version < "4.0" tarina==0.6.8 ; python_version >= "3.10" and python_version < "4.0"
tenacity==9.0.0 ; python_version >= "3.10" and python_version < "4.0" tenacity==9.0.0 ; python_version >= "3.10" and python_version < "4.0"
text-unidecode==1.3 ; python_version >= "3.10" and python_version < "4.0" text-unidecode==1.3 ; python_version >= "3.10" and python_version < "4.0"
tomli==2.2.1 ; python_version >= "3.10" and python_version < "3.11" tomli==2.2.1 ; python_version == "3.10"
tomlkit==0.13.2 ; python_version >= "3.10" and python_version < "4.0" tomlkit==0.13.2 ; python_version >= "3.10" and python_version < "4.0"
tortoise-orm==0.20.1 ; python_version >= "3.10" and python_version < "4.0" tortoise-orm[asyncpg]==0.20.0 ; python_version >= "3.10" and python_version < "4.0"
types-python-dateutil==2.9.0.20241206 ; python_version >= "3.10" and python_version < "4.0" types-python-dateutil==2.9.0.20241206 ; python_version >= "3.10" and python_version < "4.0"
typing-extensions==4.12.2 ; python_version >= "3.10" and python_version < "4.0" typing-extensions==4.12.2 ; python_version >= "3.10" and python_version < "4.0"
tzdata==2025.1 ; python_version >= "3.10" and python_version < "4.0" and platform_system == "Windows" tzdata==2025.1 ; python_version >= "3.10" and python_version < "4.0" and platform_system == "Windows"
tzlocal==5.2 ; python_version >= "3.10" and python_version < "4.0" tzlocal==5.2 ; python_version >= "3.10" and python_version < "4.0"
ujson==5.10.0 ; python_version >= "3.10" and python_version < "4.0" ujson==5.10.0 ; python_version >= "3.10" and python_version < "4.0"
urllib3==2.3.0 ; python_version >= "3.10" and python_version < "4.0" urllib3==2.3.0 ; python_version >= "3.10" and python_version < "4.0"
uvicorn==0.34.0 ; python_version >= "3.10" and python_version < "4.0" uvicorn[standard]==0.34.0 ; python_version >= "3.10" and python_version < "4.0"
uvloop==0.21.0 ; python_version >= "3.10" and python_version < "4.0" and (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" uvloop==0.21.0 ; sys_platform != "win32" and sys_platform != "cygwin" and platform_python_implementation != "PyPy" and python_version >= "3.10" and python_version < "4.0"
virtualenv==20.29.2 ; python_version >= "3.10" and python_version < "4.0" virtualenv==20.29.2 ; python_version >= "3.10" and python_version < "4.0"
watchfiles==0.24.0 ; python_version >= "3.10" and python_version < "4.0" watchfiles==0.24.0 ; python_version >= "3.10" and python_version < "4.0"
wcwidth==0.2.13 ; python_version >= "3.10" and python_version < "4.0" wcwidth==0.2.13 ; python_version >= "3.10" and python_version < "4.0"

View File

@ -15,7 +15,8 @@ async def get_task() -> dict[str, str] | None:
return { return {
"name": "被动技能", "name": "被动技能",
"description": "控制群组中的被动技能状态", "description": "控制群组中的被动技能状态",
"usage": "通过 开启/关闭群被动 来控制群被<br>----------<br>" "usage": "通过 开启/关闭群被动 来控制群被动 <br>"
+ " 示例:开启/关闭群被动早晚安 <br> ---------- <br> "
+ "<br>".join([task.name for task in task_list]), + "<br>".join([task.name for task in task_list]),
} }
return None return None

View File

@ -1,3 +1,5 @@
import os
from zhenxun.configs.path_config import DATA_PATH, IMAGE_PATH from zhenxun.configs.path_config import DATA_PATH, IMAGE_PATH
from zhenxun.models.group_console import GroupConsole from zhenxun.models.group_console import GroupConsole
from zhenxun.models.plugin_info import PluginInfo from zhenxun.models.plugin_info import PluginInfo
@ -14,9 +16,9 @@ GROUP_HELP_PATH = DATA_PATH / "group_help"
def delete_help_image(gid: str | None = None): def delete_help_image(gid: str | None = None):
"""删除帮助图片""" """删除帮助图片"""
if gid: if gid:
file = GROUP_HELP_PATH / f"{gid}.png" for file in os.listdir(GROUP_HELP_PATH):
if file.exists(): if file.startswith(f"{gid}"):
file.unlink() os.remove(GROUP_HELP_PATH / file)
else: else:
if HELP_FILE.exists(): if HELP_FILE.exists():
HELP_FILE.unlink() HELP_FILE.unlink()
@ -196,7 +198,7 @@ class PluginManage:
await PluginInfo.filter(plugin_type=PluginType.NORMAL).update( await PluginInfo.filter(plugin_type=PluginType.NORMAL).update(
default_status=status default_status=status
) )
return f'成功将所有功能进群默认状态修改为: {"开启" if status else "关闭"}' return f"成功将所有功能进群默认状态修改为: {'开启' if status else '关闭'}"
if group_id: if group_id:
if group := await GroupConsole.get_or_none( if group := await GroupConsole.get_or_none(
group_id=group_id, channel_id__isnull=True group_id=group_id, channel_id__isnull=True
@ -213,12 +215,12 @@ class PluginManage:
module_list = [f"<{module}" for module in module_list] module_list = [f"<{module}" for module in module_list]
group.block_plugin = ",".join(module_list) + "," # type: ignore group.block_plugin = ",".join(module_list) + "," # type: ignore
await group.save(update_fields=["block_plugin"]) await group.save(update_fields=["block_plugin"])
return f'成功将此群组所有功能状态修改为: {"开启" if status else "关闭"}' return f"成功将此群组所有功能状态修改为: {'开启' if status else '关闭'}"
return "获取群组失败..." return "获取群组失败..."
await PluginInfo.filter(plugin_type=PluginType.NORMAL).update( await PluginInfo.filter(plugin_type=PluginType.NORMAL).update(
status=status, block_type=None if status else BlockType.ALL status=status, block_type=None if status else BlockType.ALL
) )
return f'成功将所有功能全局状态修改为: {"开启" if status else "关闭"}' return f"成功将所有功能全局状态修改为: {'开启' if status else '关闭'}"
@classmethod @classmethod
async def is_wake(cls, group_id: str) -> bool: async def is_wake(cls, group_id: str) -> bool:

View File

@ -1,7 +1,6 @@
from nonebot import on_message from nonebot import on_message
from nonebot.plugin import PluginMetadata from nonebot.plugin import PluginMetadata
from nonebot_plugin_alconna import UniMsg from nonebot_plugin_alconna import UniMsg
from nonebot_plugin_apscheduler import scheduler
from nonebot_plugin_session import EventSession from nonebot_plugin_session import EventSession
from zhenxun.configs.config import Config from zhenxun.configs.config import Config
@ -39,45 +38,17 @@ def rule(message: UniMsg) -> bool:
chat_history = on_message(rule=rule, priority=1, block=False) chat_history = on_message(rule=rule, priority=1, block=False)
TEMP_LIST = []
@chat_history.handle() @chat_history.handle()
async def _(message: UniMsg, session: EventSession): async def handle_message(message: UniMsg, session: EventSession):
# group_id = session.id3 or session.id2 """处理消息存储"""
group_id = session.id2 try:
TEMP_LIST.append( await ChatHistory.create(
ChatHistory(
user_id=session.id1, user_id=session.id1,
group_id=group_id, group_id=session.id2,
text=str(message), text=str(message),
plain_text=message.extract_plain_text(), plain_text=message.extract_plain_text(),
bot_id=session.bot_id, bot_id=session.bot_id,
platform=session.platform, platform=session.platform,
) )
)
@scheduler.scheduled_job(
"interval",
minutes=1,
)
async def _():
try:
message_list = TEMP_LIST.copy()
TEMP_LIST.clear()
if message_list:
await ChatHistory.bulk_create(message_list)
logger.debug(f"批量添加聊天记录 {len(message_list)}", "定时任务")
except Exception as e: except Exception as e:
logger.error("定时批量添加聊天记录", "定时任务", e=e) logger.warning("存储聊天记录失败", "chat_history", e=e)
# @test.handle()
# async def _(event: MessageEvent):
# print(await ChatHistory.get_user_msg(event.user_id, "private"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "private"))
# print(await ChatHistory.get_user_msg(event.user_id, "group"))
# print(await ChatHistory.get_user_msg_count(event.user_id, "group"))
# print(await ChatHistory.get_group_msg(event.group_id))
# print(await ChatHistory.get_group_msg_count(event.group_id))

View File

@ -4,7 +4,7 @@ from datetime import datetime, timedelta
import inspect import inspect
import time import time
from types import MappingProxyType from types import MappingProxyType
from typing import Any, ClassVar, Literal from typing import Any, Literal
from nonebot.adapters import Bot, Event from nonebot.adapters import Bot, Event
from nonebot.compat import model_dump from nonebot.compat import model_dump
@ -69,9 +69,9 @@ class ShopParam(BaseModel):
"""道具单次使用数量""" """道具单次使用数量"""
text: str text: str
"""text""" """text"""
send_success_msg: ClassVar[bool] = True send_success_msg: bool = True
"""是否发送使用成功信息""" """是否发送使用成功信息"""
max_num_limit: ClassVar[int] = 1 max_num_limit: int = 1
"""单次使用最大次数""" """单次使用最大次数"""
session: Uninfo | None = None session: Uninfo | None = None
"""Uninfo""" """Uninfo"""
@ -81,7 +81,7 @@ class ShopParam(BaseModel):
"""At对象""" """At对象"""
at_users: list[str] = [] at_users: list[str] = []
"""At对象列表""" """At对象列表"""
extra_data: ClassVar[dict[str, Any]] = {} extra_data: dict[str, Any] = Field(default_factory=dict)
"""额外数据""" """额外数据"""
class Config: class Config:
@ -403,10 +403,10 @@ class ShopManage:
cls.uuid2goods[uuid] = Goods( cls.uuid2goods[uuid] = Goods(
model=create_model( model=create_model(
f"{uuid}_model", f"{uuid}_model",
send_success_msg=send_success_msg,
max_num_limit=max_num_limit,
__base__=ShopParam, __base__=ShopParam,
extra_data=kwargs, send_success_msg=(bool, Field(default=send_success_msg)),
max_num_limit=(int, Field(default=max_num_limit)),
extra_data=(dict[str, Any], Field(default=kwargs)),
), ),
params=kwargs, params=kwargs,
before_handle=before_handle, before_handle=before_handle,

View File

@ -15,7 +15,8 @@ async def get_task() -> dict[str, str] | None:
return { return {
"name": "被动技能", "name": "被动技能",
"description": "控制群组中的被动技能状态", "description": "控制群组中的被动技能状态",
"usage": "通过 开启/关闭群被动 来控制群被动 <br> ---------- <br> " "usage": "通过 开启/关闭群被动 来控制群被动 <br>"
+ " 示例:开启/关闭群被动早晚安 <br> ---------- <br> "
+ "<br>".join([task.name for task in task_list]), + "<br>".join([task.name for task in task_list]),
} }
return None return None

View File

@ -1,2 +0,0 @@
from .menu import * # noqa: F403f
from .tabs import * # noqa: F403f

View File

@ -142,4 +142,4 @@ async def _(query: QueryModel) -> Result[BaseResultModel]:
async def _(plugin_name: str | None = None) -> Result[dict]: async def _(plugin_name: str | None = None) -> Result[dict]:
if plugin_name: if plugin_name:
return Result.ok(ApiDataSource.SQL_DICT.get(plugin_name)) return Result.ok(ApiDataSource.SQL_DICT.get(plugin_name))
return Result.ok(str(ApiDataSource.SQL_DICT)) return Result.ok(ApiDataSource.SQL_DICT)

View File

@ -1,4 +1,4 @@
from typing import Any, overload from typing import Any, cast, overload
from typing_extensions import Self from typing_extensions import Self
from tortoise import fields from tortoise import fields
@ -10,6 +10,42 @@ from zhenxun.services.db_context import Model
from zhenxun.utils.enum import PluginType from zhenxun.utils.enum import PluginType
def add_disable_marker(name: str) -> str:
"""添加模块禁用标记符
Args:
name: 模块名称
Returns:
添加了禁用标记的模块名 (前缀'<'和后缀',')
"""
return f"<{name},"
@overload
def convert_module_format(data: str) -> list[str]: ...
@overload
def convert_module_format(data: list[str]) -> str: ...
def convert_module_format(data: str | list[str]) -> str | list[str]:
"""
`<aaa,<bbb,<ccc,` `["aaa", "bbb", "ccc"]` (即禁用启用)之间进行相互转换
参数:
data: 要转换的数据
返回:
str | list[str]: 根据输入类型返回转换后的数据
"""
if isinstance(data, str):
return [item.strip(",") for item in data.split("<") if item]
else:
return "".join(format(item) for item in data)
class GroupConsole(Model): class GroupConsole(Model):
id = fields.IntField(pk=True, generated=True, auto_increment=True) id = fields.IntField(pk=True, generated=True, auto_increment=True)
"""自增id""" """自增id"""
@ -51,33 +87,34 @@ class GroupConsole(Model):
table_description = "群组信息表" table_description = "群组信息表"
unique_together = ("group_id", "channel_id") unique_together = ("group_id", "channel_id")
@staticmethod
def format(name: str) -> str:
return f"<{name},"
@overload
@classmethod @classmethod
def convert_module_format(cls, data: str) -> list[str]: ... async def _get_task_modules(cls, *, default_status: bool) -> list[str]:
"""获取默认禁用的任务模块
@overload
@classmethod
def convert_module_format(cls, data: list[str]) -> str: ...
@classmethod
def convert_module_format(cls, data: str | list[str]) -> str | list[str]:
"""
`<aaa,<bbb,<ccc,` `["aaa", "bbb", "ccc"]` 之间进行相互转换
参数:
data (str | list[str]): 输入数据可能是格式化字符串或字符串列表
返回: 返回:
str | list[str]: 根据输入类型返回转换后的数据 list[str]: 任务模块列表
""" """
if isinstance(data, str): return cast(
return [item.strip(",") for item in data.split("<") if item] list[str],
elif isinstance(data, list): await TaskInfo.filter(default_status=default_status).values_list(
return "".join(cls.format(item) for item in data) "module", flat=True
),
)
@classmethod
async def _get_plugin_modules(cls, *, default_status: bool) -> list[str]:
"""获取默认禁用的插件模块
返回:
list[str]: 插件模块列表
"""
return cast(
list[str],
await PluginInfo.filter(
plugin_type__in=[PluginType.NORMAL, PluginType.DEPENDANT],
default_status=default_status,
).values_list("module", flat=True),
)
@classmethod @classmethod
async def create( async def create(
@ -85,20 +122,44 @@ class GroupConsole(Model):
) -> Self: ) -> Self:
"""覆盖create方法""" """覆盖create方法"""
group = await super().create(using_db=using_db, **kwargs) group = await super().create(using_db=using_db, **kwargs)
if modules := await TaskInfo.filter(default_status=False).values_list(
"module", flat=True task_modules = await cls._get_task_modules(default_status=False)
): plugin_modules = await cls._get_plugin_modules(default_status=False)
group.block_task = cls.convert_module_format(modules) # type: ignore
if modules := await PluginInfo.filter( if task_modules or plugin_modules:
plugin_type__in=[PluginType.NORMAL, PluginType.DEPENDANT], await cls._update_modules(group, task_modules, plugin_modules, using_db)
default_status=False,
).values_list("module", flat=True):
group.block_plugin = cls.convert_module_format(modules) # type: ignore
await group.save(
using_db=using_db, update_fields=["block_plugin", "block_task"]
)
return group return group
@classmethod
async def _update_modules(
cls,
group: Self,
task_modules: list[str],
plugin_modules: list[str],
using_db: BaseDBAsyncClient | None = None,
) -> None:
"""更新模块设置
参数:
group: 群组实例
task_modules: 任务模块列表
plugin_modules: 插件模块列表
using_db: 数据库连接
"""
update_fields = []
if task_modules:
group.block_task = convert_module_format(task_modules)
update_fields.append("block_task")
if plugin_modules:
group.block_plugin = convert_module_format(plugin_modules)
update_fields.append("block_plugin")
if update_fields:
await group.save(using_db=using_db, update_fields=update_fields)
@classmethod @classmethod
async def get_or_create( async def get_or_create(
cls, cls,
@ -110,20 +171,15 @@ class GroupConsole(Model):
group, is_create = await super().get_or_create( group, is_create = await super().get_or_create(
defaults=defaults, using_db=using_db, **kwargs defaults=defaults, using_db=using_db, **kwargs
) )
if is_create and ( if not is_create:
modules := await TaskInfo.filter(default_status=False).values_list( return group, is_create
"module", flat=True
) task_modules = await cls._get_task_modules(default_status=False)
): plugin_modules = await cls._get_plugin_modules(default_status=False)
group.block_task = cls.convert_module_format(modules) # type: ignore
if modules := await PluginInfo.filter( if task_modules or plugin_modules:
plugin_type__in=[PluginType.NORMAL, PluginType.DEPENDANT], await cls._update_modules(group, task_modules, plugin_modules, using_db)
default_status=False,
).values_list("module", flat=True):
group.block_plugin = cls.convert_module_format(modules) # type: ignore
await group.save(
using_db=using_db, update_fields=["block_plugin", "block_task"]
)
return group, is_create return group, is_create
@classmethod @classmethod
@ -137,20 +193,15 @@ class GroupConsole(Model):
group, is_create = await super().update_or_create( group, is_create = await super().update_or_create(
defaults=defaults, using_db=using_db, **kwargs defaults=defaults, using_db=using_db, **kwargs
) )
if is_create and ( if not is_create:
modules := await TaskInfo.filter(default_status=False).values_list( return group, is_create
"module", flat=True
) task_modules = await cls._get_task_modules(default_status=False)
): plugin_modules = await cls._get_plugin_modules(default_status=False)
group.block_task = cls.convert_module_format(modules) # type: ignore
if modules := await PluginInfo.filter( if task_modules or plugin_modules:
plugin_type__in=[PluginType.NORMAL, PluginType.DEPENDANT], await cls._update_modules(group, task_modules, plugin_modules, using_db)
default_status=False,
).values_list("module", flat=True):
group.block_plugin = cls.convert_module_format(modules) # type: ignore
await group.save(
using_db=using_db, update_fields=["block_plugin", "block_task"]
)
return group, is_create return group, is_create
@classmethod @classmethod
@ -195,7 +246,7 @@ class GroupConsole(Model):
""" """
return await cls.exists( return await cls.exists(
group_id=group_id, group_id=group_id,
superuser_block_plugin__contains=f"<{module},", superuser_block_plugin__contains=add_disable_marker(module),
) )
@classmethod @classmethod
@ -209,10 +260,11 @@ class GroupConsole(Model):
返回: 返回:
bool: 是否禁用插件 bool: 是否禁用插件
""" """
module = add_disable_marker(module)
return await cls.exists( return await cls.exists(
group_id=group_id, block_plugin__contains=f"<{module}," group_id=group_id, block_plugin__contains=module
) or await cls.exists( ) or await cls.exists(
group_id=group_id, superuser_block_plugin__contains=f"<{module}," group_id=group_id, superuser_block_plugin__contains=module
) )
@classmethod @classmethod
@ -234,12 +286,22 @@ class GroupConsole(Model):
group, _ = await cls.get_or_create( group, _ = await cls.get_or_create(
group_id=group_id, defaults={"platform": platform} group_id=group_id, defaults={"platform": platform}
) )
update_fields = []
if is_superuser: if is_superuser:
if f"<{module}," not in group.superuser_block_plugin: superuser_block_plugin = convert_module_format(group.superuser_block_plugin)
group.superuser_block_plugin += f"<{module}," if module not in superuser_block_plugin:
elif f"<{module}," not in group.block_plugin: superuser_block_plugin.append(module)
group.block_plugin += f"<{module}," group.superuser_block_plugin = convert_module_format(
await group.save(update_fields=["block_plugin", "superuser_block_plugin"]) superuser_block_plugin
)
update_fields.append("superuser_block_plugin")
elif add_disable_marker(module) not in group.block_plugin:
block_plugin = convert_module_format(group.block_plugin)
block_plugin.append(module)
group.block_plugin = convert_module_format(block_plugin)
update_fields.append("block_plugin")
if update_fields:
await group.save(update_fields=update_fields)
@classmethod @classmethod
async def set_unblock_plugin( async def set_unblock_plugin(
@ -260,14 +322,22 @@ class GroupConsole(Model):
group, _ = await cls.get_or_create( group, _ = await cls.get_or_create(
group_id=group_id, defaults={"platform": platform} group_id=group_id, defaults={"platform": platform}
) )
update_fields = []
if is_superuser: if is_superuser:
if f"<{module}," in group.superuser_block_plugin: superuser_block_plugin = convert_module_format(group.superuser_block_plugin)
group.superuser_block_plugin = group.superuser_block_plugin.replace( if module in superuser_block_plugin:
f"<{module},", "" superuser_block_plugin.remove(module)
group.superuser_block_plugin = convert_module_format(
superuser_block_plugin
) )
elif f"<{module}," in group.block_plugin: update_fields.append("superuser_block_plugin")
group.block_plugin = group.block_plugin.replace(f"<{module},", "") elif add_disable_marker(module) in group.block_plugin:
await group.save(update_fields=["block_plugin", "superuser_block_plugin"]) block_plugin = convert_module_format(group.block_plugin)
block_plugin.remove(module)
group.block_plugin = convert_module_format(block_plugin)
update_fields.append("block_plugin")
if update_fields:
await group.save(update_fields=update_fields)
@classmethod @classmethod
async def is_normal_block_plugin( async def is_normal_block_plugin(
@ -302,7 +372,7 @@ class GroupConsole(Model):
""" """
return await cls.exists( return await cls.exists(
group_id=group_id, group_id=group_id,
superuser_block_task__contains=f"<{task},", superuser_block_task__contains=add_disable_marker(task),
) )
@classmethod @classmethod
@ -319,22 +389,23 @@ class GroupConsole(Model):
返回: 返回:
bool: 是否禁用被动 bool: 是否禁用被动
""" """
task = add_disable_marker(task)
if not channel_id: if not channel_id:
return await cls.exists( return await cls.exists(
group_id=group_id, group_id=group_id,
channel_id__isnull=True, channel_id__isnull=True,
block_task__contains=f"<{task},", block_task__contains=task,
) or await cls.exists( ) or await cls.exists(
group_id=group_id, group_id=group_id,
channel_id__isnull=True, channel_id__isnull=True,
superuser_block_task__contains=f"<{task},", superuser_block_task__contains=task,
) )
return await cls.exists( return await cls.exists(
group_id=group_id, channel_id=channel_id, block_task__contains=f"<{task}," group_id=group_id, channel_id=channel_id, block_task__contains=task
) or await cls.exists( ) or await cls.exists(
group_id=group_id, group_id=group_id,
channel_id__isnull=True, channel_id__isnull=True,
superuser_block_task__contains=f"<{task},", superuser_block_task__contains=task,
) )
@classmethod @classmethod
@ -356,12 +427,20 @@ class GroupConsole(Model):
group, _ = await cls.get_or_create( group, _ = await cls.get_or_create(
group_id=group_id, defaults={"platform": platform} group_id=group_id, defaults={"platform": platform}
) )
update_fields = []
if is_superuser: if is_superuser:
if f"<{task}," not in group.superuser_block_task: superuser_block_task = convert_module_format(group.superuser_block_task)
group.superuser_block_task += f"<{task}," if task not in group.superuser_block_task:
elif f"<{task}," not in group.block_task: superuser_block_task.append(task)
group.block_task += f"<{task}," group.superuser_block_task = convert_module_format(superuser_block_task)
await group.save(update_fields=["block_task", "superuser_block_task"]) update_fields.append("superuser_block_task")
elif add_disable_marker(task) not in group.block_task:
block_task = convert_module_format(group.block_task)
block_task.append(task)
group.block_task = convert_module_format(block_task)
update_fields.append("block_task")
if update_fields:
await group.save(update_fields=update_fields)
@classmethod @classmethod
async def set_unblock_task( async def set_unblock_task(
@ -382,14 +461,20 @@ class GroupConsole(Model):
group, _ = await cls.get_or_create( group, _ = await cls.get_or_create(
group_id=group_id, defaults={"platform": platform} group_id=group_id, defaults={"platform": platform}
) )
update_fields = []
if is_superuser: if is_superuser:
if f"<{task}," in group.superuser_block_task: superuser_block_task = convert_module_format(group.superuser_block_task)
group.superuser_block_task = group.superuser_block_task.replace( if task in superuser_block_task:
f"<{task},", "" superuser_block_task.remove(task)
) group.superuser_block_task = convert_module_format(superuser_block_task)
elif f"<{task}," in group.block_task: update_fields.append("superuser_block_task")
group.block_task = group.block_task.replace(f"<{task},", "") elif add_disable_marker(task) in group.block_task:
await group.save(update_fields=["block_task", "superuser_block_task"]) block_task = convert_module_format(group.block_task)
block_task.remove(task)
group.block_task = convert_module_format(block_task)
update_fields.append("block_task")
if update_fields:
await group.save(update_fields=update_fields)
@classmethod @classmethod
def _run_script(cls): def _run_script(cls):

View File

@ -64,8 +64,6 @@ class MessageUtils:
if isinstance(msg, str): if isinstance(msg, str):
if msg.startswith("base64://"): if msg.startswith("base64://"):
message_list.append(Image(raw=BytesIO(base64.b64decode(msg[9:])))) message_list.append(Image(raw=BytesIO(base64.b64decode(msg[9:]))))
elif msg.startswith("http://"):
message_list.append(Image(url=msg))
else: else:
message_list.append(Text(msg)) message_list.append(Text(msg))
elif isinstance(msg, int | float): elif isinstance(msg, int | float):