Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b65ac2126 | ||
|
|
3611678e13 |
@@ -1,20 +0,0 @@
|
||||
.devcontainer/
|
||||
.github/
|
||||
.vscode/
|
||||
.idea/
|
||||
.pytest_cache/
|
||||
.ruff_cache/
|
||||
.venv/
|
||||
docs_image/
|
||||
k8s/
|
||||
tests/
|
||||
.dockerignore
|
||||
.editorconfig
|
||||
.gitignore
|
||||
.pre-commit-config.yaml
|
||||
.prettier*
|
||||
.env.dev
|
||||
docker-compose.yml
|
||||
Dockerfile
|
||||
LICENSE
|
||||
*.md
|
||||
@@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
SUPERUSERS=[""]
|
||||
|
||||
COMMAND_START=[""]
|
||||
@@ -6,72 +8,9 @@ SESSION_RUNNING_EXPRESSION="别急呀,小真寻要宕机了!QAQ"
|
||||
|
||||
NICKNAME=["真寻", "小真寻", "绪山真寻", "小寻子"]
|
||||
|
||||
SESSION_EXPIRE_TIMEOUT=00:00:30
|
||||
SESSION_EXPIRE_TIMEOUT=30
|
||||
|
||||
ALCONNA_USE_COMMAND_START=True
|
||||
|
||||
# 全局图片统一使用bytes发送,当真寻与协议端不在同一服务器上时为True
|
||||
IMAGE_TO_BYTES = True
|
||||
|
||||
# 回复消息时自称
|
||||
SELF_NICKNAME="小真寻"
|
||||
|
||||
# 官bot appid:bot账号
|
||||
QBOT_ID_DATA = '{
|
||||
|
||||
}'
|
||||
|
||||
# 数据库配置
|
||||
# 示例: "postgres://user:password@127.0.0.1:5432/database"
|
||||
# 示例: "mysql://user:password@127.0.0.1:3306/database"
|
||||
# 示例: "sqlite:data/db/zhenxun.db" 在data目录下建立db文件夹
|
||||
DB_URL = ""
|
||||
|
||||
# 系统代理
|
||||
# SYSTEM_PROXY = "http://127.0.0.1:7890"
|
||||
|
||||
PLATFORM_SUPERUSERS = '
|
||||
{
|
||||
"qq": [""],
|
||||
"dodo": [""]
|
||||
}
|
||||
'
|
||||
|
||||
DRIVER=~fastapi+~httpx+~websockets
|
||||
|
||||
|
||||
# LOG_LEVEL=DEBUG
|
||||
DEBUG=true
|
||||
# 服务器和端口
|
||||
HOST = 127.0.0.1
|
||||
PORT = 8080
|
||||
|
||||
# kook adapter toekn
|
||||
# kaiheila_bots =[{"token": ""}]
|
||||
|
||||
# # discode adapter
|
||||
# DISCORD_BOTS='
|
||||
# [
|
||||
# {
|
||||
# "token": "",
|
||||
# "intent": {
|
||||
# "guild_messages": true,
|
||||
# "direct_messages": true
|
||||
# },
|
||||
# "application_commands": {"*": ["*"]}
|
||||
# }
|
||||
# ]
|
||||
# '
|
||||
# DISCORD_PROXY=''
|
||||
|
||||
# # dodo adapter
|
||||
# DODO_BOTS='
|
||||
# [
|
||||
# {
|
||||
# "client_id": "",
|
||||
# "token": ""
|
||||
# }
|
||||
# ]
|
||||
# '
|
||||
|
||||
# application_commands的{"*": ["*"]}代表将全部应用命令注册为全局应用命令
|
||||
# {"admin": ["123", "456"]}则代表将admin命令注册为id是123、456服务器的局部命令,其余命令不注册
|
||||
@@ -1,98 +0,0 @@
|
||||
name: Bug 反馈
|
||||
title: "Bug: "
|
||||
description: 提交 Bug 反馈以帮助我们改进代码
|
||||
labels: [ "bug" ]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: 提交前检查项
|
||||
description: 在提交问题之前,请确认以下事项:
|
||||
options:
|
||||
- label: 我已搜索相关的 issue,但没有找到类似的问题
|
||||
required: true
|
||||
- label: 我已更新到最新版本(包括但不限于真寻本体,插件以及相关依赖),问题仍然存在
|
||||
required: true
|
||||
- label: 我已仔细阅读文档,确认我的配置正确
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: env-os
|
||||
attributes:
|
||||
label: 操作系统
|
||||
description: 选择运行 zhenxun_bot 的系统
|
||||
options:
|
||||
- Windows
|
||||
- MacOS
|
||||
- Linux
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: env-python-ver
|
||||
attributes:
|
||||
label: Python 版本
|
||||
description: 填写运行 zhenxun_bot 的 Python 版本
|
||||
placeholder: e.g. 3.11.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: env-zhenxun-ver
|
||||
attributes:
|
||||
label: zhenxun_bot 版本
|
||||
description: 填写 zhenxun_bot 版本
|
||||
placeholder: e.g. 0.1.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: env-adapter
|
||||
attributes:
|
||||
label: 适配器
|
||||
description: 填写使用的适配器以及版本
|
||||
placeholder: e.g. OneBot v11 2.2.2
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: env-protocol
|
||||
attributes:
|
||||
label: 协议端
|
||||
description: 填写连接 zhenxun_bot 的协议端及版本
|
||||
placeholder: e.g. NapCat V4.0.3
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: describe
|
||||
attributes:
|
||||
label: 描述问题
|
||||
description: 清晰简洁地说明问题是什么
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: 复现步骤
|
||||
description: 提供能复现此问题的详细操作步骤
|
||||
placeholder: |
|
||||
1. 首先……
|
||||
2. 然后……
|
||||
3. 发生……
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: 期望的结果
|
||||
description: 清晰简洁地描述你期望发生的事情
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: 截图或日志(请勿包含敏感信息如密码、令牌等)
|
||||
description: 提供有助于诊断问题的任何日志和截图
|
||||
@@ -1 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
@@ -1,18 +0,0 @@
|
||||
name: 文档改进
|
||||
title: "Docs: 描述"
|
||||
description: 文档错误及改进意见反馈
|
||||
labels: ["documentation"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: 描述问题或主题
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: improve
|
||||
attributes:
|
||||
label: 需做出的修改
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,27 +0,0 @@
|
||||
name: 功能建议
|
||||
title: "Feature: 功能描述"
|
||||
description: 提出关于项目新功能的想法
|
||||
labels: [ "enhancement" ]
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: 希望能解决的问题
|
||||
description: 在使用中遇到什么问题而需要新的功能?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: feature
|
||||
attributes:
|
||||
label: 描述所需要的功能
|
||||
description: 请说明需要的功能或解决方法
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: 我有能力且愿意为这个功能贡献代码
|
||||
options:
|
||||
- label: 我有能力且愿意为这个功能贡献代码
|
||||
@@ -1,40 +0,0 @@
|
||||
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
|
||||
@@ -1,77 +0,0 @@
|
||||
template: $CHANGES
|
||||
name-template: "v$RESOLVED_VERSION"
|
||||
tag-template: "v$RESOLVED_VERSION"
|
||||
exclude-labels:
|
||||
- reverted
|
||||
- no-changelog
|
||||
- skip-changelog
|
||||
- invalid
|
||||
autolabeler:
|
||||
- label: "bug"
|
||||
title:
|
||||
- "/:bug:.+/"
|
||||
- "/🐛.+/"
|
||||
- label: "enhancement"
|
||||
title:
|
||||
- "/:sparkles:.+/"
|
||||
- "/✨.+/"
|
||||
- label: "ci"
|
||||
files:
|
||||
- .github/**/*
|
||||
- label: "breaking-change"
|
||||
title:
|
||||
- "/.+!:.+/"
|
||||
- label: "documentation"
|
||||
files:
|
||||
- "*.md"
|
||||
- label: "dependencies"
|
||||
files:
|
||||
- "pyproject.toml"
|
||||
- "requirements.txt"
|
||||
- "poetry.lock"
|
||||
title:
|
||||
- "/:wrench:.+/"
|
||||
- "/🔧.+/"
|
||||
- label: "resources"
|
||||
files:
|
||||
- resources/**/*
|
||||
categories:
|
||||
- title: 💥 破坏性变更
|
||||
labels:
|
||||
- breaking-change
|
||||
- title: 🚀 新功能
|
||||
labels:
|
||||
- enhancement
|
||||
- title: 🐛 Bug 修复
|
||||
labels:
|
||||
- bug
|
||||
- title: 📝 文档更新
|
||||
labels:
|
||||
- documentation
|
||||
- title: 👻 自动化程序
|
||||
labels:
|
||||
- chore
|
||||
- internal
|
||||
- maintenance
|
||||
- title: 🚦 测试
|
||||
labels:
|
||||
- test
|
||||
- tests
|
||||
- title: 📦 依赖更新
|
||||
labels:
|
||||
- dependencies
|
||||
collapse-after: 15
|
||||
- title: 💫 杂项
|
||||
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
|
||||
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
|
||||
version-resolver:
|
||||
major:
|
||||
labels:
|
||||
- "major"
|
||||
minor:
|
||||
labels:
|
||||
- "minor"
|
||||
patch:
|
||||
labels:
|
||||
- "patch"
|
||||
default: patch
|
||||
@@ -1,84 +0,0 @@
|
||||
name: 检查bot是否运行正常
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- zhenxun/**
|
||||
- tests/**
|
||||
- .github/workflows/bot_check.yml
|
||||
- bot.py
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- zhenxun/**
|
||||
- tests/**
|
||||
- .github/workflows/bot_check.yml
|
||||
- bot.py
|
||||
|
||||
jobs:
|
||||
bot-check:
|
||||
runs-on: ubuntu-latest
|
||||
name: bot check
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
id: setup_python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install Poetry
|
||||
run: pip install poetry
|
||||
|
||||
# Poetry cache depends on OS, Python version and Poetry version.
|
||||
- name: Cache Poetry cache
|
||||
id: cache-poetry
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pypoetry
|
||||
key: poetry-cache-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }}
|
||||
|
||||
- name: Cache playwright cache
|
||||
id: cache-playwright
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: playwright-cache-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}
|
||||
|
||||
- name: Cache Data cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: data
|
||||
key: data-cache-${{ runner.os }}-${{ steps.setup_python.outputs.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.cache-poetry.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
rm -rf poetry.lock
|
||||
poetry source remove aliyun
|
||||
poetry install --no-root
|
||||
|
||||
- name: Install playwright
|
||||
if: steps.cache-playwright.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
poetry run sudo apt-get update
|
||||
poetry run sudo apt-get install -y libgstreamer-plugins-base1.0-0 libgstreamer1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav flite x264 libx264-dev
|
||||
poetry run pip install playwright
|
||||
poetry run playwright install-deps
|
||||
poetry run playwright install
|
||||
|
||||
- name: Run tests
|
||||
run: poetry run pytest --cov=zhenxun --cov-report xml
|
||||
|
||||
- name: Check bot run
|
||||
id: bot_check_run
|
||||
run: |
|
||||
mv scripts/bot_check.py bot_check.py
|
||||
sed -i "s|^.*\?DB_URL.*|DB_URL=\"${{ env.DB_URL }}\"|g" .env.dev
|
||||
sed -i "s/^.*\?LOG_LEVEL.*/LOG_LEVEL=${{ env.LOG_LEVEL }}/g" .env.dev
|
||||
poetry run python3 bot_check.py
|
||||
env:
|
||||
DB_URL: "sqlite://:memory:"
|
||||
LOG_LEVEL: DEBUG
|
||||
@@ -1,94 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL Code Security Analysis"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '45 21 * * 2'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: python
|
||||
build-mode: none
|
||||
- language: javascript-typescript
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# If the analyze step fails for one of the languages you are analyzing with
|
||||
# "We were unable to automatically build your code", modify the matrix above
|
||||
# to set the build mode to "manual" for that language. Then modify this step
|
||||
# to build your code.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
- if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||
'languages you are analyzing, replace this with the commands to build' \
|
||||
'your code, for example:'
|
||||
echo ' make bootstrap'
|
||||
echo ' make release'
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
@@ -1,11 +0,0 @@
|
||||
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
|
||||
@@ -1,55 +0,0 @@
|
||||
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
|
||||
@@ -1,18 +0,0 @@
|
||||
name: Release Drafter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
update_release_draft:
|
||||
name: Update Release Draft
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@v6
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
@@ -1,20 +0,0 @@
|
||||
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
|
||||
@@ -1,73 +0,0 @@
|
||||
name: Update Version
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/update_version_pr.yml
|
||||
- zhenxun/**
|
||||
- resources/**
|
||||
- bot.py
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
update-version:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Read current version
|
||||
id: read_version
|
||||
run: |
|
||||
version_line=$(grep '__version__' __version__)
|
||||
version=$(echo $version_line | sed -E 's/__version__:\s*v([0-9]+\.[0-9]+\.[0-9]+)(-.+)?/\1/')
|
||||
echo "Current version: $version"
|
||||
echo "current_version=$version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check for version file changes
|
||||
id: check_diff
|
||||
run: |
|
||||
if git diff --name-only HEAD~1 HEAD | grep -q '__version__'; then
|
||||
echo "Version file has changes"
|
||||
echo "version_changed=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Version file has no changes"
|
||||
echo "version_changed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Get commit hash
|
||||
id: get_commit_hash
|
||||
run: echo "commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version file
|
||||
id: update_version
|
||||
if: steps.check_diff.outputs.version_changed == 'false'
|
||||
run: |
|
||||
current_version="${{ steps.read_version.outputs.current_version }}"
|
||||
commit_hash="${{ steps.get_commit_hash.outputs.commit_hash }}"
|
||||
new_version="v${current_version}-${commit_hash}"
|
||||
echo "new_version=$new_version" >> $GITHUB_OUTPUT
|
||||
echo "Updating version to: $new_version"
|
||||
echo "__version__: $new_version" > __version__
|
||||
|
||||
- name: Check updated version
|
||||
if: steps.check_diff.outputs.version_changed == 'false'
|
||||
run: cat __version__
|
||||
|
||||
- name: Create or update PR
|
||||
if: steps.check_diff.outputs.version_changed == 'false'
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
branch: create-pr/update_version
|
||||
title: ":tada: chore(version): 自动更新版本到 ${{ steps.update_version.outputs.new_version }}"
|
||||
body: "This PR updates the version file."
|
||||
commit-message: ":tada: chore(version): Update version to ${{ steps.update_version.outputs.new_version }}"
|
||||
add-paths: __version__
|
||||
author: "AkashiCoin <i@loli.vet>"
|
||||
committer: "${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com>"
|
||||
labels: automated-update
|
||||
@@ -1,160 +0,0 @@
|
||||
### Python template
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
.env.dev
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
demo.py
|
||||
test.py
|
||||
server_ip.py
|
||||
member_activity_handle.py
|
||||
Yu-Gi-Oh/
|
||||
csgo/
|
||||
fantasy_card/
|
||||
data/
|
||||
log/
|
||||
backup/
|
||||
extensive_plugin/
|
||||
test/
|
||||
bot.py
|
||||
.idea/
|
||||
resources/
|
||||
/configs/config.py
|
||||
configs/config.yaml
|
||||
.vscode/launch.json
|
||||
plugins_/
|
||||
@@ -1,4 +0,0 @@
|
||||
MD013: false
|
||||
MD024: # 重复标题
|
||||
siblings_only: true
|
||||
MD033: false # 允许 html
|
||||
@@ -1,16 +0,0 @@
|
||||
default_install_hook_types: [pre-commit]
|
||||
ci:
|
||||
autofix_commit_msg: ":rotating_light: auto fix by pre-commit hooks"
|
||||
autofix_prs: true
|
||||
autoupdate_branch: main
|
||||
autoupdate_schedule: monthly
|
||||
autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.8.2
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [--fix]
|
||||
stages: [pre-commit]
|
||||
- id: ruff-format
|
||||
stages: [pre-commit]
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"charliermarsh.ruff",
|
||||
"esbenp.prettier-vscode",
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance"
|
||||
]
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
{
|
||||
"C_Cpp.errorSquiggles": "enabled",
|
||||
"terminal.integrated.env.linux": {
|
||||
"PYTHONPATH": "${workspaceFolder}${pathSeparator}${env:PYTHONPATH}"
|
||||
},
|
||||
"cSpell.words": [
|
||||
"aiofiles",
|
||||
"Alconna",
|
||||
"arclet",
|
||||
"Arparma",
|
||||
"displayname",
|
||||
"flmt",
|
||||
"getbbox",
|
||||
"hibiapi",
|
||||
"httpx",
|
||||
"jsdelivr",
|
||||
"kaiheila",
|
||||
"lolicon",
|
||||
"Mahiro",
|
||||
"nonebot",
|
||||
"onebot",
|
||||
"pixiv",
|
||||
"qbot",
|
||||
"Setu",
|
||||
"tobytes",
|
||||
"ujson",
|
||||
"unban",
|
||||
"Uninfo",
|
||||
"userinfo",
|
||||
"zhenxun"
|
||||
],
|
||||
"python.analysis.autoImportCompletions": true,
|
||||
"python.testing.pytestArgs": ["tests"],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true,
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "charliermarsh.ruff",
|
||||
"editor.wordBasedSuggestions": "allDocuments",
|
||||
"editor.formatOnType": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.ruff": "explicit",
|
||||
"source.organizeImports": "explicit"
|
||||
}
|
||||
},
|
||||
"ruff.format.preview": false,
|
||||
"isort.check": true,
|
||||
"ruff.importStrategy": "useBundled",
|
||||
"ruff.organizeImports": false,
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[yaml]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
# zhenxun_bot 贡献者公约
|
||||
|
||||
## 我们的承诺
|
||||
|
||||
身为社区成员、贡献者和负责人,我们承诺使社区参与者不受骚扰,无论其年龄、体型、可见或不可见的缺陷、族裔、性征、性别认同和表达、经验水平、教育程度、社会与经济地位、国籍、相貌、种族、种姓、肤色、宗教信仰、性倾向或性取向如何。
|
||||
|
||||
我们承诺以有助于建立开放、友善、多样化、包容、健康社区的方式行事和互动。
|
||||
|
||||
## 我们的准则
|
||||
|
||||
有助于为我们的社区创造积极环境的行为例子包括但不限于:
|
||||
|
||||
* 表现出对他人的同情和善意
|
||||
* 尊重不同的主张、观点和感受
|
||||
* 提出和大方接受建设性意见
|
||||
* 承担责任并向受我们错误影响的人道歉
|
||||
* 注重社区共同诉求,而非个人得失
|
||||
|
||||
不当行为例子包括:
|
||||
|
||||
* 使用情色化的语言或图像,及性引诱或挑逗
|
||||
* 嘲弄、侮辱或诋毁性评论,以及人身或政治攻击
|
||||
* 公开或私下的骚扰行为
|
||||
* 未经他人明确许可,公布他人的私人信息,如物理或电子邮件地址
|
||||
* 其他有理由认定为违反职业操守的不当行为
|
||||
|
||||
## 责任和权力
|
||||
|
||||
社区负责人有责任解释和落实我们所认可的行为准则,并妥善公正地对他们认为不当、威胁、冒犯或有害的任何行为采取纠正措施。
|
||||
|
||||
社区负责人有权力和责任删除、编辑或拒绝或拒绝与本行为准则不相符的评论(comment)、提交(commits)、代码、维基(wiki)编辑、议题(issues)或其他贡献,并在适当时机知采取措施的理由。
|
||||
|
||||
## 适用范围
|
||||
|
||||
本行为准则适用于所有社区场合,也适用于在公共场所代表社区时的个人。
|
||||
|
||||
代表社区的情形包括使用官方电子邮件地址、通过官方社交媒体帐户发帖或在线上或线下活动中担任指定代表。
|
||||
|
||||
## 监督
|
||||
|
||||
辱骂、骚扰或其他不可接受的行为可通过 775757368@qq.com 向负责监督的社区负责人报告。
|
||||
所有投诉都将得到及时和公平的审查和调查。
|
||||
|
||||
所有社区负责人都有义务尊重任何事件报告者的隐私和安全。
|
||||
|
||||
## 处理方针
|
||||
|
||||
社区负责人将遵循下列社区处理方针来明确他们所认定违反本行为准则的行为的处理方式:
|
||||
|
||||
### 1. 纠正
|
||||
|
||||
**社区影响**:使用不恰当的语言或其他在社区中被认定为不符合职业道德或不受欢迎的行为。
|
||||
|
||||
**处理意见**:由社区负责人发出非公开的书面警告,明确说明违规行为的性质,并解释举止如何不妥。或将要求公开道歉。
|
||||
|
||||
### 2. 警告
|
||||
|
||||
**社区影响**:单个或一系列违规行为。
|
||||
|
||||
**处理意见**:警告并对连续性行为进行处理。在指定时间内,不得与相关人员互动,包括主动与行为准则执行者互动。这包括避免在社区场所和外部渠道中的互动。违反这些条款可能会导致临时或永久封禁。
|
||||
|
||||
### 3. 临时封禁
|
||||
|
||||
**社区影响**: 严重违反社区准则,包括持续的不当行为。
|
||||
|
||||
**处理意见**: 在指定时间内,暂时禁止与社区进行任何形式的互动或公开交流。在此期间,不得与相关人员进行公开或私下互动,包括主动与行为准则执行者互动。违反这些条款可能会导致永久封禁。
|
||||
|
||||
### 4. 永久封禁
|
||||
|
||||
**社区影响**:行为模式表现出违反社区准则,包括持续的不当行为、骚扰个人或攻击或贬低某个类别的个体。
|
||||
|
||||
**处理意见**:永久禁止在社区内进行任何形式的公开互动。
|
||||
|
||||
## 参见
|
||||
|
||||
本行为准则改编自 [Contributor Covenant][homepage] 2.1 版, 参见 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。
|
||||
|
||||
社区处理方针灵感来源于 [Mozilla's code of conduct enforcement ladder][Mozilla CoC]。
|
||||
|
||||
有关本行为准则的常见问题的答案,参见 [https://www.contributor-covenant.org/faq][FAQ]。
|
||||
其他语言翻译参见 [https://www.contributor-covenant.org/translations][translations]。
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
@@ -1,95 +0,0 @@
|
||||
# zhenxun_bot 贡献指南
|
||||
|
||||
首先,感谢你愿意为 zhenxun_bot 贡献自己的一份力量!
|
||||
|
||||
本指南旨在引导你更规范地向 zhenxun_bot 提交贡献,请务必认真阅读。
|
||||
|
||||
## 提交 Issue
|
||||
|
||||
在提交 Issue 前,我们建议你先查看 [已有的 Issues](https://github.com/HibiKier/zhenxun_bot/issues),以防重复提交。
|
||||
|
||||
### 报告问题、故障与漏洞
|
||||
|
||||
如果你在使用过程中发现问题并确信是由 zhenxun_bot 引起的,欢迎提交 Issue。
|
||||
|
||||
请使用我们提供的 **Bug 反馈** 模板,并尽可能详细地描述:
|
||||
|
||||
- 问题描述
|
||||
- 重现步骤
|
||||
- 你的环境信息(如操作系统、依赖版本等)
|
||||
|
||||
### 建议功能
|
||||
|
||||
如果你有新的功能需求或改进建议,欢迎提出。
|
||||
|
||||
请使用 **功能建议** 模板,并详细描述你所需要的特性,可能的话可以提出你认为可行的解决方案。
|
||||
|
||||
### 文档相关
|
||||
|
||||
如果你觉得文档有误或缺乏更新,欢迎提出。
|
||||
|
||||
请使用 **文档改进** 模板,并详细描述问题或主题,希望我们做出的修改
|
||||
|
||||
## Pull Request
|
||||
|
||||
### 分支管理
|
||||
|
||||
请从 `main` 分支创建新功能分支,例如:
|
||||
|
||||
- 新功能:`feature/功能描述`
|
||||
- 问题修复:`bugfix/问题描述`
|
||||
|
||||
### 代码风格
|
||||
|
||||
zhenxun_bot 使用 `pre-commit` 进行代码格式化和检查,请在提交前确保代码通过检查。
|
||||
|
||||
```bash
|
||||
# 在安装项目依赖后安装 pre-commit 钩子
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
> 未通过 `pre-commit` 检查的代码将无法合并。
|
||||
|
||||
### Commit 规范
|
||||
|
||||
请确保你的每一个 commit 都能清晰地描述其意图,一个 commit 尽量只有一个目的。
|
||||
|
||||
我们建议遵循 [gitmoji](https://gitmoji.dev/) 的 commit message 格式,在创建 commit 时请牢记这一点。
|
||||
|
||||
### 工作流程概述
|
||||
|
||||
`main` 分支为 zhenxun_bot 的主分支,在任何情况下都请不要直接修改 `main` 分支,而是创建一个目标分支为 `main` 的 Pull Request 来提交修改。Pull Request 标题请尽量清晰,以便维护者进行审核。
|
||||
|
||||
如果你不是 zhenxun_bot 团队的成员,可在 fork 本仓库后,向本仓库的 `main` 分支发起 Pull Request,注意遵循先前提到的 commit message 规范创建 commit。我们将在 code review 通过后合并你的贡献。
|
||||
|
||||
### 撰写文档
|
||||
|
||||
如果你对文档有改进建议,欢迎提交 Pull Request 或者 Issue。
|
||||
|
||||
[//]: # (我们使用 Markdown 编写文档,建议遵循以下规范:)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (1. 中文与英文、数字、半角符号之间需要有空格。例:`zhenxun_bot 是一个高效的聊天机器人。`)
|
||||
|
||||
[//]: # (2. 若非英文整句,使用全角标点符号。例:`现在你可以看到机器人回复你:“Hello,世界!”。`)
|
||||
|
||||
[//]: # (3. 直引号`「」`和弯引号`“”`都可接受,但同一份文件里应使用同种引号。)
|
||||
|
||||
[//]: # (4. **不要使用斜体**,你不需要一种与粗体不同的强调。)
|
||||
|
||||
[//]: # (5. 文档中应以“我们”指代开发者,以“用户”指代机器人的使用者。)
|
||||
|
||||
[//]: # ()
|
||||
[//]: # (如果你需要编辑器检查 Markdown 规范,可以在 VSCode 中安装 `markdownlint` 扩展。)
|
||||
|
||||
### 参与开发
|
||||
|
||||
zhenxun_bot 的代码风格遵循 [PEP 8](https://www.python.org/dev/peps/pep-0008/) 与 [PEP 484](https://www.python.org/dev/peps/pep-0484/) 规范,请确保你的代码风格和项目已有的代码保持一致,变量命名清晰,有适当的注释与测试代码。
|
||||
|
||||
> 暂未搭建测试框架,因此暂不要求添加测试代码。
|
||||
|
||||
## 项目沟通
|
||||
|
||||
如有关于贡献流程的疑问或需要进一步指导,请通过 [QQ群](https://jq.qq.com/?_wv=1027&k=u8PgBkMZ) 联系我们。
|
||||
|
||||
再次感谢你的贡献!
|
||||
@@ -1,67 +0,0 @@
|
||||
FROM python:3.11-bookworm AS requirements-stage
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
ENV POETRY_HOME="/opt/poetry" PATH="${PATH}:/opt/poetry/bin"
|
||||
|
||||
RUN curl -sSL https://install.python-poetry.org | python - -y && \
|
||||
poetry self add poetry-plugin-export
|
||||
|
||||
COPY ./pyproject.toml ./poetry.lock* /tmp/
|
||||
|
||||
RUN poetry export \
|
||||
-f requirements.txt \
|
||||
--output requirements.txt \
|
||||
--without-hashes \
|
||||
--without-urls
|
||||
|
||||
FROM python:3.11-bookworm AS build-stage
|
||||
|
||||
WORKDIR /wheel
|
||||
|
||||
COPY --from=requirements-stage /tmp/requirements.txt /wheel/requirements.txt
|
||||
|
||||
# RUN python3 -m pip config set global.index-url https://mirrors.aliyun.com/pypi/simple
|
||||
|
||||
RUN pip wheel --wheel-dir=/wheel --no-cache-dir --requirement /wheel/requirements.txt
|
||||
|
||||
FROM python:3.11-bookworm AS metadata-stage
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
RUN --mount=type=bind,source=./.git/,target=/tmp/.git/ \
|
||||
git describe --tags --exact-match > /tmp/VERSION 2>/dev/null \
|
||||
|| git rev-parse --short HEAD > /tmp/VERSION \
|
||||
&& echo "Building version: $(cat /tmp/VERSION)"
|
||||
|
||||
FROM python:3.11-slim-bookworm
|
||||
|
||||
WORKDIR /app/zhenxun
|
||||
|
||||
ENV TZ=Asia/Shanghai PYTHONUNBUFFERED=1
|
||||
#COPY ./scripts/docker/start.sh /start.sh
|
||||
#RUN chmod +x /start.sh
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
RUN apt update && \
|
||||
apt install -y --no-install-recommends curl fontconfig fonts-noto-color-emoji \
|
||||
&& apt clean \
|
||||
&& fc-cache -fv \
|
||||
&& apt-get purge -y --auto-remove curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 复制依赖项和应用代码
|
||||
COPY --from=build-stage /wheel /wheel
|
||||
COPY . .
|
||||
|
||||
RUN pip install --no-cache-dir --no-index --find-links=/wheel -r /wheel/requirements.txt && rm -rf /wheel
|
||||
|
||||
RUN playwright install --with-deps chromium \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/*
|
||||
|
||||
COPY --from=metadata-stage /tmp/VERSION /app/VERSION
|
||||
|
||||
VOLUME ["/app/zhenxun/data", "/app/zhenxun/resources", "/app/zhenxun/log"]
|
||||
|
||||
CMD ["python", "bot.py"]
|
||||
@@ -1,5 +1,5 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
@@ -7,15 +7,17 @@
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
@@ -24,34 +26,44 @@ them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
@@ -60,7 +72,7 @@ modification follow.
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
@@ -537,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
@@ -633,29 +635,40 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
__version__: v0.2.4-2c97eea
|
||||
@@ -1,27 +1,19 @@
|
||||
import nonebot
|
||||
|
||||
# from nonebot.adapters.discord import Adapter as DiscordAdapter
|
||||
# from nonebot.adapters.dodo import Adapter as DoDoAdapter
|
||||
# from nonebot.adapters.kaiheila import Adapter as KaiheilaAdapter
|
||||
from nonebot.adapters.onebot.v11 import Adapter as OneBotV11Adapter
|
||||
from nonebot.adapters.cqhttp import Bot as CQHTTPBot
|
||||
from services.db_context import init, disconnect
|
||||
|
||||
nonebot.init()
|
||||
|
||||
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter(OneBotV11Adapter)
|
||||
# driver.register_adapter(KaiheilaAdapter)
|
||||
# driver.register_adapter(DoDoAdapter)
|
||||
# driver.register_adapter(DiscordAdapter)
|
||||
|
||||
from zhenxun.services.db_context import disconnect, init
|
||||
|
||||
driver.register_adapter("cqhttp", CQHTTPBot)
|
||||
config = driver.config
|
||||
driver.on_startup(init)
|
||||
driver.on_shutdown(disconnect)
|
||||
|
||||
# nonebot.load_builtin_plugins("echo")
|
||||
nonebot.load_plugins("zhenxun/builtin_plugins")
|
||||
nonebot.load_plugins("zhenxun/plugins")
|
||||
nonebot.load_builtin_plugins()
|
||||
nonebot.load_plugins("plugins")
|
||||
nonebot.load_plugins("plugins/shop")
|
||||
nonebot.load_plugins("plugins/genshin")
|
||||
nonebot.load_plugins("plugins/alapi")
|
||||
nonebot.load_plugins("plugins/pix_gallery")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
from .utils.util import get_config_data
|
||||
from typing import List, Optional, Tuple
|
||||
from services.service_config import TL_M_KEY, SYSTEM_M_PROXY, ALAPI_M_TOKEN
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
|
||||
# 是否使用配置文件
|
||||
USE_CONFIG_FILE: bool = False
|
||||
|
||||
|
||||
# API KEY(必要)
|
||||
RSSHUBAPP: str = "https://rsshub.app" # rsshub
|
||||
ALAPI_TOKEN: str = "" # ALAPI https://admin.alapi.cn/user/login
|
||||
HIBIAPI: str = "https://api.obfs.dev"
|
||||
# 图灵
|
||||
TL_KEY: List[str] = []
|
||||
|
||||
# 数据库(必要)
|
||||
# 如果填写了bind就不需要再填写后面的字段了#)
|
||||
# 示例:"bind": "postgresql://user:password@127.0.0.1:5432/database"
|
||||
bind: str = "" # 数据库连接链接
|
||||
sql_name: str = "postgresql"
|
||||
user: str = "" # 数据用户名
|
||||
password: str = "" # 数据库密码
|
||||
address: str = "" # 数据库地址
|
||||
port: str = "" # 数据库端口
|
||||
database: str = "" # 数据库名称
|
||||
|
||||
# 代理
|
||||
SYSTEM_PROXY: Optional[str] = None # 全局代理
|
||||
BUFF_PROXY: Optional[str] = None # Buff代理
|
||||
|
||||
# 公开图库列表
|
||||
IMAGE_DIR_LIST: List[str] = ["色图", "美图", "萝莉", "壁纸"]
|
||||
|
||||
# 对被ban用户发送的消息
|
||||
BAN_RESULT: str = "才不会给你发消息."
|
||||
|
||||
|
||||
# 插件配置
|
||||
MAXINFO_REIMU: int = 7 # 上车(reimu)功能查找目的地的最大数
|
||||
COUNT_PER_DAY_REIMU: int = 5 # 每日上车(reimu)次数限制
|
||||
MAXINFO_BT: int = 10 # bt功能单次查找最大数
|
||||
MAXINFO_PRIVATE_ANIME: int = 20 # 私聊搜索动漫返回的最大数量
|
||||
MAXINFO_GROUP_ANIME: int = 5 # 群搜索动漫返回的最大数量
|
||||
MAX_FIND_IMG_COUNT: int = 3 # 识图最大返回数
|
||||
# 参1:延迟撤回色图时间(秒),0 为关闭 | 参2:监控聊天类型,0(私聊) 1(群聊) 2(群聊+私聊)
|
||||
WITHDRAW_SETU_TIME: Tuple[int, int] = (0, 1)
|
||||
|
||||
# 各种卡池的开关
|
||||
PRTS_FLAG = True # 明日方舟
|
||||
GENSHIN_FLAG = True # 原神
|
||||
PRETTY_FLAG = True # 赛马娘
|
||||
GUARDIAN_FLAG = True # 坎公骑冠剑
|
||||
PCR_FLAG = True # 公主连结
|
||||
AZUR_FLAG = True # 碧蓝航线
|
||||
FGO_FLAG = True # 命运-冠位指定(FGO)
|
||||
ONMYOJI_FLAG = True # 阴阳师
|
||||
|
||||
PCR_TAI = True # pcr是否开启台服卡池
|
||||
SEMAPHORE = 5 # 限制碧蓝航线和FGO并发数
|
||||
|
||||
ADMIN_DEFAULT_AUTH: int = 5 # 默认群管理员权限
|
||||
|
||||
MAX_SIGN_GOLD: int = 200 # 签到好感度加成额外获得的最大金币数
|
||||
MAX_RUSSIAN_BET_GOLD: int = 1000 # 俄罗斯轮盘最大赌注金额
|
||||
|
||||
INITIAL_SETU_PROBABILITY: float = 0.7 # 色图概率
|
||||
FUDU_PROBABILITY: float = 0.7 # 复读概率
|
||||
|
||||
INITIAL_OPEN_CASE_COUNT: int = 20 # 初始开箱次数
|
||||
MUTE_DEFAULT_COUNT: int = 10 # 刷屏禁言默认检测次数
|
||||
MUTE_DEFAULT_TIME: int = 7 # 刷屏检测默认规定时间
|
||||
MUTE_DEFAULT_DURATION: int = 10 # 刷屏检测默禁言时长(分钟)
|
||||
|
||||
# 注:即在 MALICIOUS_CHECK_TIME 时间内触发相同命令 MALICIOUS_BAN_COUNT 将被ban MALICIOUS_BAN_TIME 分钟
|
||||
MALICIOUS_BAN_TIME: int = 30 # 恶意命令触发检测触发后ban的时长(分钟)
|
||||
MALICIOUS_BAN_COUNT: int = 8 # 恶意命令触发检测最大触发次数
|
||||
MALICIOUS_CHECK_TIME: int = 5 # 恶意命令触发检测规定时间内(秒)
|
||||
|
||||
# LEVEL
|
||||
DELETE_IMG_LEVEL: int = 7 # 删除图片权限
|
||||
MOVE_IMG_LEVEL: int = 7 # 移动图片权限
|
||||
UPLOAD_LEVEL: int = 6 # 上传图片权限
|
||||
BAN_LEVEL: int = 5 # BAN权限
|
||||
OC_LEVEL: int = 2 # 开关群功能权限
|
||||
MUTE_LEVEL: int = 5 # 更改禁言设置权限
|
||||
MEMBER_ACTIVITY_LEVEL = 5 # 群员活跃检测设置权限
|
||||
|
||||
# 是否开启HIBIAPI搜图功能(该功能会搜索群友提交的xp)
|
||||
HIBIAPI_FLAG: bool = True
|
||||
# HIBIAPI搜图图片的最低收藏
|
||||
HIBIAPI_BOOKMARKS: int = 5000
|
||||
|
||||
# 需要为哪些群更新最新版gocq吗?(上传最新版gocq)
|
||||
# 示例:[434995955, 239483248]
|
||||
UPDATE_GOCQ_GROUP: List[int] = []
|
||||
|
||||
# 是否存储色图
|
||||
DOWNLOAD_SETU: bool = True
|
||||
# 仅仅使用本地色图
|
||||
ONLY_USE_LOCAL_SETU: bool = False
|
||||
# 是否自动同意好友添加
|
||||
AUTO_ADD_FRIEND: bool = True
|
||||
# 当含有ALAPI_TOKEN时是否检测文本合规,开启检测会减慢回复速度
|
||||
ALAPI_AI_CHECK: bool = True
|
||||
# 导入商店自带的三个商品
|
||||
IMPORT_DEFAULT_SHOP_GOODS: bool = True
|
||||
# 真寻是否自动更新
|
||||
AUTO_UPDATE_ZHENXUN: bool = True
|
||||
|
||||
# 群管理员功能 与 对应权限
|
||||
admin_plugins_auth = {
|
||||
"admin_bot_manage": OC_LEVEL,
|
||||
"ban": BAN_LEVEL,
|
||||
"delete_img": DELETE_IMG_LEVEL,
|
||||
"move_img": MOVE_IMG_LEVEL,
|
||||
"upload_img": UPLOAD_LEVEL,
|
||||
"admin_help": 1,
|
||||
"mute": MUTE_LEVEL,
|
||||
"member_activity_handle": MEMBER_ACTIVITY_LEVEL,
|
||||
}
|
||||
|
||||
|
||||
# 模块与对应命令和对应群权限
|
||||
# 用于生成帮助图片 和 开关功能
|
||||
plugins2info_dict = {
|
||||
"sign_in": {"level": 5, "cmd": ["签到"]},
|
||||
"send_img": {"level": 5, "cmd": ["发送图片", "发图", "萝莉", "美图", "壁纸"]},
|
||||
"send_setu": {"level": 9, "cmd": ["色图", "涩图", "瑟图", "查色图"]},
|
||||
"white2black": {"level": 5, "cmd": ["黑白图", "黑白草图"]},
|
||||
"coser": {"level": 9, "cmd": ["coser", "cos"]},
|
||||
"quotations": {"level": 5, "cmd": ["语录"]},
|
||||
"jitang": {"level": 5, "cmd": ["鸡汤"]},
|
||||
"send_dinggong_voice": {"level": 5, "cmd": ["骂我", "骂老子", "骂劳资"]},
|
||||
"open_cases": {"level": 5, "cmd": ["开箱", "我的开箱", "群开箱统计", "我的金色"]},
|
||||
"luxun": {"level": 5, "cmd": ["鲁迅说", "鲁迅说过"]},
|
||||
"fake_msg": {"level": 5, "cmd": ["假消息"]},
|
||||
"buy": {"level": 5, "cmd": ["购买", "购买道具"]},
|
||||
"my_gold": {"level": 5, "cmd": ["我的金币"]},
|
||||
"my_props": {"level": 5, "cmd": ["我的道具"]},
|
||||
"shop_handle": {"level": 5, "cmd": ["商店"]},
|
||||
"update_pic": {"level": 5, "cmd": ["图片", "操作图片", "修改图片"]},
|
||||
"search_buff_skin_price": {"level": 5, "cmd": ["查询皮肤"]},
|
||||
"weather": {"level": 5, "cmd": ["天气", "查询天气", "天气查询"]},
|
||||
"yiqing": {"level": 5, "cmd": ["疫情", "疫情查询", "查询疫情"]},
|
||||
"what_anime": {"level": 5, "cmd": ["识番"]},
|
||||
"search_anime": {"level": 5, "cmd": ["搜番"]},
|
||||
"songpicker2": {"level": 5, "cmd": ["点歌"]},
|
||||
"epic": {"level": 5, "cmd": ["epic"]},
|
||||
"pixiv": {"level": 9, "cmd": ["pixiv", "p站排行", "搜图"]},
|
||||
"poke": {"level": 5, "cmd": ["戳一戳", "拍一拍"]},
|
||||
"draw_card": {
|
||||
"level": 5,
|
||||
"cmd": [
|
||||
"抽卡",
|
||||
"游戏抽卡",
|
||||
"原神抽卡",
|
||||
"方舟抽卡",
|
||||
"坎公骑冠剑抽卡",
|
||||
"pcr抽卡",
|
||||
"fgo抽卡",
|
||||
"碧蓝抽卡",
|
||||
"碧蓝航线抽卡",
|
||||
"阴阳师抽卡",
|
||||
],
|
||||
},
|
||||
"ai": {"level": 5, "cmd": ["ai", "Ai", "AI", "aI"]},
|
||||
"one_friend": {"level": 5, "cmd": ["我有一个朋友", "我有一个朋友想问问"]},
|
||||
"translate": {"level": 5, "cmd": ["翻译", "英翻", "翻英", "日翻", "翻日", "韩翻", "翻韩"]},
|
||||
"nonebot_plugin_picsearcher": {"level": 5, "cmd": ["识图"]},
|
||||
"almanac": {"level": 5, "cmd": ["原神黄历", "黄历"]},
|
||||
"material_remind": {"level": 5, "cmd": ["今日素材", "天赋材料"]},
|
||||
"qiu_qiu_translation": {"level": 5, "cmd": ["丘丘翻译", "丘丘一下", "丘丘语翻译"]},
|
||||
"query_resource_points": {"level": 5, "cmd": ["原神资源查询", "原神资源列表"]},
|
||||
"russian": {"level": 5, "cmd": ["俄罗斯轮盘", "俄罗斯转盘", "装弹"]},
|
||||
"gold_redbag": {"level": 5, "cmd": ["塞红包", "红包", "抢红包"]},
|
||||
"poetry": {"level": 5, "cmd": ["念诗", "来首诗", "念首诗"]},
|
||||
"comments_163": {"level": 5, "cmd": ["到点了", "12点了", "网易云热评", "网易云评论"]},
|
||||
"cover": {"level": 5, "cmd": ["b封面", "B封面"]},
|
||||
"pid_search": {"level": 9, "cmd": ["p搜", "P搜"]},
|
||||
"pix": {
|
||||
"level": 5,
|
||||
"cmd": ["pix", "PIX", "pIX", "Pix", "PIx"],
|
||||
},
|
||||
}
|
||||
|
||||
if TL_M_KEY:
|
||||
TL_KEY = TL_M_KEY
|
||||
if SYSTEM_M_PROXY:
|
||||
SYSTEM_PROXY = SYSTEM_M_PROXY
|
||||
if ALAPI_M_TOKEN:
|
||||
ALAPI_TOKEN = ALAPI_M_TOKEN
|
||||
|
||||
|
||||
HIBIAPI = HIBIAPI[:-1] if HIBIAPI[-1] == "/" else HIBIAPI
|
||||
RSSHUBAPP = RSSHUBAPP[:-1] if RSSHUBAPP[-1] == "/" else RSSHUBAPP
|
||||
|
||||
# 配置文件应用
|
||||
# if USE_CONFIG_FILE:
|
||||
# config = get_config_data()
|
||||
# if config:
|
||||
# for key in config.keys():
|
||||
# if isinstance(config[key], str):
|
||||
# config[key] = config[key].strip()
|
||||
# if key.find("proxy") != -1:
|
||||
# if not config[key]:
|
||||
# config[key] = None
|
||||
# # if not configs[key] and key.find("PATH") == -1:
|
||||
# # configs[key] = None
|
||||
# globals().update(config)
|
||||
@@ -0,0 +1,62 @@
|
||||
from .utils.util import get_config_data
|
||||
from pathlib import Path
|
||||
from configs.config import USE_CONFIG_FILE
|
||||
|
||||
# 图片路径
|
||||
IMAGE_PATH = Path("resources/img/")
|
||||
# 音频路径
|
||||
VOICE_PATH = Path("resources/voice/")
|
||||
# 文本路径
|
||||
TXT_PATH = Path("resources/txt/")
|
||||
# 日志路径
|
||||
LOG_PATH = Path("log/")
|
||||
# 字体路径
|
||||
TTF_PATH = Path("resources/ttf/")
|
||||
# 数据路径
|
||||
DATA_PATH = Path("data/")
|
||||
# 临时图片路径
|
||||
TEMP_PATH = Path("resources/img/temp/")
|
||||
|
||||
|
||||
def init_path():
|
||||
global IMAGE_PATH, VOICE_PATH, TXT_PATH, LOG_PATH, TTF_PATH, DATA_PATH, TEMP_PATH
|
||||
if USE_CONFIG_FILE:
|
||||
data = get_config_data()
|
||||
if data.get('IMAGE_PATH'):
|
||||
IMAGE_PATH = Path(data['IMAGE_PATH'])
|
||||
if data.get('VOICE_PATH'):
|
||||
VOICE_PATH = Path(data['VOICE_PATH'])
|
||||
if data.get('TXT_PATH'):
|
||||
TXT_PATH = Path(data['TXT_PATH'])
|
||||
if data.get('LOG_PATH'):
|
||||
LOG_PATH = Path(data['LOG_PATH'])
|
||||
if data.get('TTF_PATH'):
|
||||
TTF_PATH = Path(data['TTF_PATH'])
|
||||
if data.get('DATA_PATH'):
|
||||
DATA_PATH = Path(data['DATA_PATH'])
|
||||
if data.get('DRAW_PATH'):
|
||||
DRAW_PATH = Path(data['DRAW_PATH'])
|
||||
if data.get('TEMP_PATH'):
|
||||
TEMP_PATH = Path(data['TEMP_PATH'])
|
||||
IMAGE_PATH.mkdir(parents=True, exist_ok=True)
|
||||
VOICE_PATH.mkdir(parents=True, exist_ok=True)
|
||||
TXT_PATH.mkdir(parents=True, exist_ok=True)
|
||||
LOG_PATH.mkdir(parents=True, exist_ok=True)
|
||||
TTF_PATH.mkdir(parents=True, exist_ok=True)
|
||||
DATA_PATH.mkdir(parents=True, exist_ok=True)
|
||||
TEMP_PATH.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
IMAGE_PATH = str(IMAGE_PATH.absolute()) + '/'
|
||||
VOICE_PATH = str(VOICE_PATH.absolute()) + '/'
|
||||
TXT_PATH = str(TXT_PATH.absolute()) + '/'
|
||||
LOG_PATH = str(LOG_PATH.absolute()) + '/'
|
||||
TTF_PATH = str(TTF_PATH.absolute()) + '/'
|
||||
DATA_PATH = str(DATA_PATH.absolute()) + '/'
|
||||
TEMP_PATH = str(TEMP_PATH.absolute()) + '/'
|
||||
|
||||
|
||||
init_path()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(IMAGE_PATH)
|
||||
@@ -0,0 +1,176 @@
|
||||
# import nonebot
|
||||
from pathlib import Path
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
# driver: nonebot.Driver = nonebot.get_driver()
|
||||
|
||||
base_config = Path() / 'config.json'
|
||||
plugins_cmd_config = Path() / 'configs' / 'plugins2cmd_config.json'
|
||||
plugins_setting = Path() / 'configs' / 'plugins_setting.json'
|
||||
|
||||
|
||||
def init_config():
|
||||
if not base_config.exists():
|
||||
base_config.parent.mkdir(parents=True, exist_ok=True)
|
||||
config_dict = {
|
||||
'apikey': {
|
||||
'LOLICON_KEY': '',
|
||||
'TL_KEY': [],
|
||||
},
|
||||
'sql': {
|
||||
'bind': '',
|
||||
'sql_name': '',
|
||||
'user': '',
|
||||
'password': '',
|
||||
'address': '',
|
||||
'port': '',
|
||||
'database': '',
|
||||
},
|
||||
'path': {
|
||||
'IMAGE_PATH': '',
|
||||
'VOICE_PATH': '',
|
||||
'TXT_PATH': '',
|
||||
'LOG_PATH': '',
|
||||
'DATA_PATH': '',
|
||||
'DRAW_PATH': '',
|
||||
'TEMP_PATH': '',
|
||||
},
|
||||
'proxy': {
|
||||
'system_proxy': '',
|
||||
'buff_proxy': ''
|
||||
},
|
||||
'level': {
|
||||
'DELETE_IMG_LEVEL': 7,
|
||||
'MOVE_IMG_LEVEL': 7,
|
||||
'UPLOAD_LEVEL': 6,
|
||||
'BAN_LEVEL': 5,
|
||||
'OC_LEVEL': 2,
|
||||
'MUTE_LEVEL': 5,
|
||||
},
|
||||
'auth': {
|
||||
'ADMIN_DEFAULT_AUTH': 5,
|
||||
'admin_plugins_auth': {
|
||||
"admin_bot_manage": 2,
|
||||
"ban": 5,
|
||||
"delete_img": 7,
|
||||
"move_img": 7,
|
||||
"upload_img": 6,
|
||||
"admin_help": 1,
|
||||
"mute": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
with open(base_config, 'w', encoding='utf8') as f:
|
||||
json.dump(config_dict, f, indent=4, ensure_ascii=False)
|
||||
if not plugins_cmd_config.exists():
|
||||
plugins_cmd_config.parent.mkdir(parents=True, exist_ok=True)
|
||||
config_dict = {
|
||||
'base_config': {
|
||||
'sign_in': ['签到'],
|
||||
'send_img': ['发送图片', '萝莉', '美图', '壁纸'],
|
||||
'send_setu': ['色图', '涩图', '瑟图', '查色图'],
|
||||
'white2black': ['黑白图', '黑白草图'],
|
||||
'coser': ['coser', 'cos'],
|
||||
'quotations': ['语录'],
|
||||
'jitang': ['鸡汤'],
|
||||
'send_dinggong_voice': ['骂我', '骂老子', '骂劳资'],
|
||||
'open_cases': ['开箱', '我的开箱', '群开箱统计', '我的金色'],
|
||||
'luxun': ['鲁迅说过', '鲁迅说'],
|
||||
'fake_msg': ['假消息'],
|
||||
'buy': ['购买', '购买道具'],
|
||||
'my_gold': ['我的金币'],
|
||||
'my_props': ['我的道具'],
|
||||
'shop_handle': ['商店'],
|
||||
'nonebot_plugin_cocdicer': ['骰子娘'],
|
||||
'update_pic': ['图片', '操作图片', '修改图片'],
|
||||
'search_buff_skin_price': ['查询皮肤'],
|
||||
'weather': ['天气', '查询天气', '天气查询'],
|
||||
'yiqing': ['疫情', '疫情查询', '查询疫情'],
|
||||
'what_anime': ['识番'],
|
||||
'search_anime': ['搜番'],
|
||||
'songpicker2': ['点歌'],
|
||||
'epic': ['epic'],
|
||||
'pixiv': ['pixiv', 'p站排行', '搜图'],
|
||||
'poke': ['戳一戳'],
|
||||
'draw_card': ['游戏抽卡', '原神一井', '原神来一井', '方舟一井', '方舟来一井'],
|
||||
'ai': ['ai', 'Ai', 'AI', 'aI'],
|
||||
'one_friend': ['我有一个朋友', '我有一个朋友想问问'],
|
||||
'translate': ['翻译', '英翻', '翻英', '日翻', '翻日', '韩翻', '翻韩'],
|
||||
'nonebot_plugin_picsearcher': ['识图'],
|
||||
'almanac': ['原神黄历', '黄历'],
|
||||
'material_remind': ['今日素材', '天赋材料'],
|
||||
'qiu_qiu_translation': ['丘丘翻译', '丘丘一下', '丘丘语翻译'],
|
||||
'query_resource_points': ['原神资源查询', '原神资源列表'],
|
||||
}
|
||||
}
|
||||
with open(plugins_cmd_config, 'w', encoding='utf8') as f:
|
||||
json.dump(config_dict, f, indent=4, ensure_ascii=False)
|
||||
if not plugins_setting.exists():
|
||||
plugins_setting.parent.mkdir(parents=True, exist_ok=True)
|
||||
config_dict = {
|
||||
'base': {
|
||||
'IMAGE_DIR_LIST': ["色图", "美图", "萝莉", "壁纸"],
|
||||
'BAN_RESULT': "才不会给你发消息.",
|
||||
},
|
||||
'draw_card': {
|
||||
'PRTS_FLAG': True, # 明日方舟
|
||||
'GENSHIN_FLAG': True, # 原神
|
||||
'PRETTY_FLAG': True, # 赛马娘
|
||||
'GUARDIAN_FLAG': True, # 坎公骑冠剑
|
||||
'PCR_FLAG': True, # 公主连结
|
||||
'AZUR_FLAG': True, # 碧蓝航线
|
||||
'FGO_FLAG': True, # 命运-冠位指定(FGO)
|
||||
'ONMYOJI_FLAG': True, # 阴阳师
|
||||
|
||||
'PCR_TAI': False # pcr是否开启台服卡池
|
||||
},
|
||||
'fudu': {
|
||||
'FUDU_PROBABILITY': 0.7,
|
||||
},
|
||||
'reimu': {
|
||||
'MAXINFO_REIMU': 7,
|
||||
'COUNT_PER_DAY_REIMU': 5,
|
||||
},
|
||||
'bt': {
|
||||
'MAXINFO_BT': 10,
|
||||
},
|
||||
'search_anime': {
|
||||
'MAXINFO_PRIVATE_ANIME': 20,
|
||||
'MAXINFO_GROUP_ANIME': 5,
|
||||
},
|
||||
'picsearcher': {
|
||||
'MAX_FIND_IMG_COUNT': 3,
|
||||
},
|
||||
'sign': {
|
||||
'MAX_SIGN_GOLD': 200,
|
||||
},
|
||||
'send_setu': {
|
||||
'INITIAL_SETU_PROBABILITY': 0.7,
|
||||
'MAX_SETU_R_COUNT': 5,
|
||||
'DOWNLOAD_SETU': True,
|
||||
},
|
||||
'malicious_ban': {
|
||||
'MALICIOUS_BAN_TIME': 30,
|
||||
'MALICIOUS_BAN_COUNT': 8,
|
||||
'MALICIOUS_CHECK_TIME': 5,
|
||||
},
|
||||
'open_case': {
|
||||
'INITIAL_OPEN_CASE_COUNT': 20,
|
||||
},
|
||||
'mute': {
|
||||
'MUTE_DEFAULT_COUNT': 10,
|
||||
'MUTE_DEFAULT_TIME': 7,
|
||||
'MUTE_DEFAULT_DURATION': 10,
|
||||
},
|
||||
'update_gocq': {
|
||||
'UPDATE_GOCQ_GROUP': [],
|
||||
},
|
||||
}
|
||||
with open(plugins_setting, 'w', encoding='utf8') as f:
|
||||
json.dump(config_dict, f, indent=4, ensure_ascii=False)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
from pathlib import Path
|
||||
from configs.utils.init_config import init_config
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
|
||||
data: dict = {}
|
||||
|
||||
|
||||
def get_config_data():
|
||||
global data
|
||||
if not data:
|
||||
try:
|
||||
base_config = json.load(open(Path() / "config.json", 'r', encoding='utf8'))
|
||||
plugins2cmd_config = json.load(open(Path() / 'configs' / 'plugins2cmd_config.json', 'r', encoding='utf8'))
|
||||
other_config = json.load(open(Path() / 'configs' / 'other_config.json', 'r', encoding='utf8'))
|
||||
for key in base_config.keys():
|
||||
data.update(base_config[key])
|
||||
for key in plugins2cmd_config.keys():
|
||||
data.update(plugins2cmd_config[key])
|
||||
for key in other_config.keys():
|
||||
data.update(other_config[key])
|
||||
except FileNotFoundError:
|
||||
# logger.warning('配置文件不存在,生成默认配置....请填写数据库等必要数据后再次启动bot...')
|
||||
init_config()
|
||||
raise FileNotFoundError('配置文件不存在,生成默认配置....请填写数据库等必要数据后再次启动bot...')
|
||||
except ValueError:
|
||||
# logger.error('配置文件错误....')
|
||||
raise ValueError('配置文件错误....')
|
||||
return data
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 697 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 289 KiB |
|
After Width: | Height: | Size: 424 KiB |
|
After Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 330 KiB |
|
After Width: | Height: | Size: 341 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 799 KiB |
|
Before Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 694 KiB |
|
Before Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 315 KiB |
|
Before Width: | Height: | Size: 352 KiB |
|
Before Width: | Height: | Size: 279 KiB |
|
Before Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 200 KiB |
|
Before Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 193 KiB |
|
Before Width: | Height: | Size: 917 KiB |
@@ -1,140 +0,0 @@
|
||||
[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"
|
||||
@@ -1,140 +0,0 @@
|
||||
[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"
|
||||
@@ -0,0 +1,231 @@
|
||||
from services.db_context import db
|
||||
from typing import Optional, List
|
||||
|
||||
|
||||
class BagUser(db.Model):
|
||||
__tablename__ = "bag_users"
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False)
|
||||
belonging_group = db.Column(db.BigInteger(), nullable=False)
|
||||
gold = db.Column(db.Integer(), default=100)
|
||||
props = db.Column(db.TEXT(), nullable=False, default="")
|
||||
spend_total_gold = db.Column(db.Integer(), default=0)
|
||||
get_total_gold = db.Column(db.Integer(), default=0)
|
||||
get_today_gold = db.Column(db.Integer(), default=0)
|
||||
spend_today_gold = db.Column(db.Integer(), default=0)
|
||||
|
||||
_idx1 = db.Index("bag_group_users_idx1", "user_qq", "belonging_group", unique=True)
|
||||
|
||||
@classmethod
|
||||
async def get_my_total_gold(cls, user_qq: int, belonging_group: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
获取金币概况
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param belonging_group: 所在群号
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
|
||||
)
|
||||
user = await query.gino.first()
|
||||
if not user:
|
||||
user = await cls.create(
|
||||
user_qq=user_qq,
|
||||
belonging_group=belonging_group,
|
||||
)
|
||||
return (
|
||||
f"当前金币:{user.gold}\n今日获取金币:{user.get_today_gold}\n今日花费金币:{user.spend_today_gold}"
|
||||
f"\n今日收益:{user.get_today_gold - user.spend_today_gold}"
|
||||
f"\n总赚取金币:{user.get_total_gold}\n总花费金币:{user.spend_total_gold}"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_gold(cls, user_qq: int, belonging_group: int) -> int:
|
||||
"""
|
||||
说明:
|
||||
获取当前金币
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param belonging_group: 所在群号
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
|
||||
)
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
return user.gold
|
||||
else:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
belonging_group=belonging_group,
|
||||
)
|
||||
return 100
|
||||
|
||||
@classmethod
|
||||
async def get_props(cls, user_qq: int, belonging_group: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
获取当前道具
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param belonging_group: 所在群号
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
|
||||
)
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
return user.props
|
||||
else:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
belonging_group=belonging_group,
|
||||
)
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
async def add_gold(cls, user_qq: int, belonging_group: int, num: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
增加金币
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param belonging_group: 所在群号
|
||||
:param num: 金币数量
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
|
||||
)
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
try:
|
||||
if user:
|
||||
await user.update(
|
||||
gold=user.gold + num,
|
||||
get_total_gold=user.get_total_gold + num,
|
||||
get_today_gold=user.get_today_gold + num,
|
||||
).apply()
|
||||
else:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
belonging_group=belonging_group,
|
||||
gold=100 + num,
|
||||
get_total_gold=num,
|
||||
get_today_gold=num,
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def spend_gold(cls, user_qq: int, belonging_group: int, num: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
花费金币
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param belonging_group: 所在群号
|
||||
:param num: 金币数量
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
|
||||
)
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
try:
|
||||
if user:
|
||||
await user.update(
|
||||
gold=user.gold - num,
|
||||
spend_total_gold=user.spend_total_gold + num,
|
||||
spend_today_gold=user.spend_today_gold + num,
|
||||
).apply()
|
||||
else:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
belonging_group=belonging_group,
|
||||
gold=100 - num,
|
||||
spend_total_gold=num,
|
||||
spend_today_gold=num,
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def add_props(cls, user_qq: int, belonging_group: int, name: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
增加道具
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param belonging_group: 所在群号
|
||||
:param name: 道具名称
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
|
||||
)
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
try:
|
||||
if user:
|
||||
await user.update(props=user.props + f"{name},").apply()
|
||||
else:
|
||||
await cls.create(
|
||||
user_qq=user_qq, belonging_group=belonging_group, props=f"{name},"
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def del_props(cls, user_qq: int, belonging_group: int, name: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
使用道具
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param belonging_group: 所在群号
|
||||
:param name: 道具名称
|
||||
"""
|
||||
query = cls.query.where(
|
||||
(cls.user_qq == user_qq) & (cls.belonging_group == belonging_group)
|
||||
)
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
try:
|
||||
if user:
|
||||
rst = ""
|
||||
props = user.props
|
||||
if props.find(name) != -1:
|
||||
props = props.split(",")
|
||||
try:
|
||||
index = props.index(name)
|
||||
except ValueError:
|
||||
return False
|
||||
props = props[:index] + props[index + 1 :]
|
||||
for p in props:
|
||||
if p != "":
|
||||
rst += p + ","
|
||||
await user.update(props=rst).apply()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_all_users(cls, group_id: Optional[int] = None) -> List["BagUser"]:
|
||||
"""
|
||||
说明:
|
||||
获取所有用户数据
|
||||
参数:
|
||||
:param group_id: 群号
|
||||
"""
|
||||
if not group_id:
|
||||
query = await cls.query.gino.all()
|
||||
else:
|
||||
query = await cls.query.where((cls.belonging_group == group_id)).gino.all()
|
||||
return query
|
||||
@@ -0,0 +1,105 @@
|
||||
from services.db_context import db
|
||||
import time
|
||||
|
||||
|
||||
class BanUser(db.Model):
|
||||
__tablename__ = "ban_users"
|
||||
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True)
|
||||
ban_level = db.Column(db.Integer(), nullable=False)
|
||||
ban_time = db.Column(db.BigInteger())
|
||||
duration = db.Column(db.BigInteger())
|
||||
|
||||
_idx1 = db.Index("ban_group_users_idx1", "user_qq", unique=True)
|
||||
|
||||
@classmethod
|
||||
async def check_ban_level(cls, user_qq: int, level: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
检测ban掉目标的用户与unban用户的权限等级大小
|
||||
参数:
|
||||
:param user_qq: unban用户的qq号
|
||||
:param level: ban掉目标用户的权限等级
|
||||
"""
|
||||
user = await cls.query.where((cls.user_qq == user_qq)).gino.first()
|
||||
if not user:
|
||||
return False
|
||||
if user.ban_level > level:
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def check_ban_time(cls, user_qq: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
检测用户被ban时长
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq))
|
||||
user = await query.gino.first()
|
||||
if not user:
|
||||
return ""
|
||||
if time.time() - (user.ban_time + user.duration) > 0 and user.duration != -1:
|
||||
return ""
|
||||
if user.duration == -1:
|
||||
return "∞"
|
||||
return time.time() - user.ban_time - user.duration
|
||||
|
||||
@classmethod
|
||||
async def is_ban(cls, user_qq: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
判断用户是否被ban
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
if await cls.check_ban_time(user_qq):
|
||||
return True
|
||||
else:
|
||||
await cls.unban(user_qq)
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def ban(cls, user_qq: int, ban_level: int, duration: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
ban掉目标用户
|
||||
参数:
|
||||
:param user_qq: 目标用户qq号
|
||||
:param ban_level: 使用ban命令用户的权限
|
||||
:param duration: ban时长
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if not await cls.check_ban_time(user_qq):
|
||||
await cls.unban(user_qq)
|
||||
user = None
|
||||
if user is None:
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
ban_level=ban_level,
|
||||
ban_time=time.time(),
|
||||
duration=duration,
|
||||
)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def unban(cls, user_qq: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
unban用户
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
if user is None:
|
||||
return False
|
||||
else:
|
||||
await cls.delete.where((cls.user_qq == user_qq)).gino.status()
|
||||
return True
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from services.db_context import db
|
||||
|
||||
# 1.狂牙武器箱
|
||||
|
||||
|
||||
class BuffPrice(db.Model):
|
||||
__tablename__ = 'buff_prices'
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
case_id = db.Column(db.Integer(), nullable=False)
|
||||
skin_name = db.Column(db.Unicode(), nullable=False)
|
||||
skin_price = db.Column(db.Float(), nullable=False)
|
||||
update_date = db.Column(db.DateTime(), nullable=False)
|
||||
|
||||
_idx1 = db.Index('buff_price_idx1', 'skin_name', unique=True)
|
||||
|
||||
@classmethod
|
||||
async def ensure(cls, skin_name: str, for_update: bool = False) -> 'BuffPrice':
|
||||
query = cls.query.where(
|
||||
(cls.skin_name == skin_name)
|
||||
)
|
||||
if for_update:
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
return user or await cls.create(
|
||||
case_id=1,
|
||||
skin_name=skin_name,
|
||||
skin_price=0,
|
||||
update_date=datetime.min,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
from services.db_context import db
|
||||
|
||||
|
||||
class UserCount(db.Model):
|
||||
__tablename__ = "count_users"
|
||||
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True)
|
||||
reimu_count = db.Column(db.Integer(), nullable=False, default=0)
|
||||
setu_r18_count = db.Column(db.Integer(), nullable=False, default=0)
|
||||
|
||||
_idx1 = db.Index("sign_reimu_users_idx1", "user_qq", unique=True)
|
||||
|
||||
@classmethod
|
||||
async def add_count(cls, user_qq: int, name: str, count: int = 1):
|
||||
"""
|
||||
说明:
|
||||
用户添加次数
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param name: 目标名称
|
||||
:param count: 增加次数
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq))
|
||||
query = query.with_for_update()
|
||||
user = await query.gino.first()
|
||||
user = user if user else await cls.create(user_qq=user_qq)
|
||||
if name == "reimu":
|
||||
await user.update(reimu_count=cls.reimu_count + count).apply()
|
||||
if name == "setu_r18":
|
||||
await user.update(setu_r18_count=cls.setu_r18_count + count).apply()
|
||||
|
||||
@classmethod
|
||||
async def check_count(cls, user_qq: int, name: str, max_count: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
检测次数是否到达最大值
|
||||
参数:
|
||||
:param user_qq: qq号
|
||||
:param name: 目标名称
|
||||
:param max_count: 最大值
|
||||
"""
|
||||
query = cls.query.where((cls.user_qq == user_qq))
|
||||
user = await query.gino.first()
|
||||
user = user if user else await cls.create(user_qq=user_qq)
|
||||
if name == "reimu":
|
||||
if user.reimu_count == max_count:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
if name == "setu_r18":
|
||||
if user.setu_r18_count == max_count:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def reset_count(cls):
|
||||
"""
|
||||
说明:
|
||||
重置每日次数
|
||||
"""
|
||||
for user in await cls.query.gino.all():
|
||||
await user.update(reimu_count=0, setu_r18_count=0).apply()
|
||||
@@ -0,0 +1,109 @@
|
||||
from services.db_context import db
|
||||
|
||||
|
||||
class FriendUser(db.Model):
|
||||
__tablename__ = "friend_users"
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
user_id = db.Column(db.BigInteger(), nullable=False)
|
||||
user_name = db.Column(db.Unicode(), nullable=False, default="")
|
||||
nickname = db.Column(db.Unicode())
|
||||
|
||||
_idx1 = db.Index("friend_users_idx1", "user_id", unique=True)
|
||||
|
||||
@classmethod
|
||||
async def get_user_name(cls, user_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
获取好友用户名称
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
"""
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
return user.user_name
|
||||
else:
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
async def add_friend_info(cls, user_id: int, user_name: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
添加好友信息
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
:param user_name: 用户名称
|
||||
"""
|
||||
try:
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
user = await query.with_for_update().gino.first()
|
||||
if not user:
|
||||
await cls.create(
|
||||
user_id=user_id,
|
||||
user_name=user_name,
|
||||
)
|
||||
else:
|
||||
await user.update(
|
||||
user_name=user_name,
|
||||
).apply()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def delete_friend_info(cls, user_id: int) -> bool:
|
||||
"""
|
||||
说明:
|
||||
删除好友信息
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
"""
|
||||
try:
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
user = await query.with_for_update().gino.first()
|
||||
if user:
|
||||
await user.delete()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
async def get_friend_nickname(cls, user_id: int) -> str:
|
||||
"""
|
||||
说明:
|
||||
获取用户昵称
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
"""
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
user = await query.gino.first()
|
||||
if user:
|
||||
if user.nickname:
|
||||
return user.nickname
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
async def set_friend_nickname(cls, user_id: int, nickname: str) -> bool:
|
||||
"""
|
||||
说明:
|
||||
设置用户昵称
|
||||
参数:
|
||||
:param user_id: qq号
|
||||
:param nickname: 昵称
|
||||
"""
|
||||
try:
|
||||
query = cls.query.where(cls.user_id == user_id)
|
||||
user = await query.with_for_update().gino.first()
|
||||
if not user:
|
||||
await cls.create(
|
||||
user_id=user_id,
|
||||
nickname=nickname,
|
||||
)
|
||||
else:
|
||||
await user.update(
|
||||
nickname=nickname,
|
||||
).apply()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||