nonebot-plugin-weather-fighter 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nonebot_plugin_weather_fighter-0.4.0/.env.example +31 -0
- nonebot_plugin_weather_fighter-0.4.0/.github/workflows/ci.yml +53 -0
- nonebot_plugin_weather_fighter-0.4.0/.github/workflows/publish.yml +24 -0
- nonebot_plugin_weather_fighter-0.4.0/.gitignore +39 -0
- nonebot_plugin_weather_fighter-0.4.0/LICENSE +21 -0
- nonebot_plugin_weather_fighter-0.4.0/PKG-INFO +146 -0
- nonebot_plugin_weather_fighter-0.4.0/README.md +123 -0
- nonebot_plugin_weather_fighter-0.4.0/docs/main-chart.png +0 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/__init__.py +87 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/config.py +25 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/consts.py +127 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/game/__init__.py +1 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/game/amount.py +47 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/game/engine.py +1282 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/game/wxi.py +592 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/matcher.py +405 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/models.py +134 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/render/__init__.py +1 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/render/charts.py +407 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/render/renderer.py +39 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/render/static/echarts.min.js +45 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/render/static/fonts/OFL.txt +92 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/render/static/fonts/weather-fighter.woff2 +0 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/render/static/theme.css +136 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/render/templates/main_chart.html +61 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/resources/provinces.json +810 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/resources/regions.json +6810 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/__init__.py +1 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/cache.py +110 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/caiyun.py +40 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/coords.py +13 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/http.py +48 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/market_time.py +146 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/openmeteo.py +480 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/provinces.py +90 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/regions.py +118 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/store.py +839 -0
- nonebot_plugin_weather_fighter-0.4.0/nonebot_plugin_weather_fighter/services/weather.py +112 -0
- nonebot_plugin_weather_fighter-0.4.0/pyproject.toml +69 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/__init__.py +1 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/conftest.py +11 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_amount.py +25 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_cache.py +138 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_charts.py +107 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_engine.py +368 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_market_time.py +34 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_matcher.py +274 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_openmeteo.py +95 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_plugin.py +22 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_provinces.py +75 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_store.py +222 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_weather.py +72 -0
- nonebot_plugin_weather_fighter-0.4.0/tests/test_wxi.py +214 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
ENVIRONMENT=dev
|
|
2
|
+
DRIVER=~fastapi
|
|
3
|
+
HOST=127.0.0.1
|
|
4
|
+
PORT=8080
|
|
5
|
+
SUPERUSERS=[]
|
|
6
|
+
COMMAND_START=["/"]
|
|
7
|
+
|
|
8
|
+
# 行情/缓存
|
|
9
|
+
WEATHER_FIGHTER_BUCKET_BARS=1800
|
|
10
|
+
WEATHER_FIGHTER_BUCKET_REALTIME=300
|
|
11
|
+
WEATHER_FIGHTER_BUCKET_HOURLY=3600
|
|
12
|
+
WEATHER_FIGHTER_BUCKET_DAILY=10800
|
|
13
|
+
WEATHER_FIGHTER_STALE_SECONDS=21600
|
|
14
|
+
WEATHER_FIGHTER_HTTP_TIMEOUT=30
|
|
15
|
+
WEATHER_FIGHTER_PRELOAD_SEASON_SERIES=true
|
|
16
|
+
WEATHER_FIGHTER_PRINCIPAL=10000
|
|
17
|
+
# 杠杆上限(玩家用「倍数 N」在 1~该值之间自选)
|
|
18
|
+
WEATHER_FIGHTER_LEVERAGE=100
|
|
19
|
+
WEATHER_FIGHTER_SAFETY_ENTRY_PCT=20
|
|
20
|
+
WEATHER_FIGHTER_SAFETY_GRANT_PCT=5
|
|
21
|
+
WEATHER_FIGHTER_SAFETY_TARGET_PCT=35
|
|
22
|
+
WEATHER_FIGHTER_CHART_BARS=64
|
|
23
|
+
WEATHER_FIGHTER_ENABLE_REGION=true
|
|
24
|
+
WEATHER_FIGHTER_DEFAULT_PROVINCE=河北
|
|
25
|
+
|
|
26
|
+
# 彩云实时/临近(M1 预留;留空则只使用 Open-Meteo)
|
|
27
|
+
WEATHER_FIGHTER_CAIYUN_TOKEN=
|
|
28
|
+
|
|
29
|
+
# 可选:自定义 Chromium(默认使用 Playwright 安装的浏览器)
|
|
30
|
+
PLAYWRIGHT_CHROMIUM_EXECUTABLE=
|
|
31
|
+
PLAYWRIGHT_CHROMIUM_ARGS=--no-sandbox --disable-crashpad
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: Python-CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ci-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
lint:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: astral-sh/setup-uv@v6
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
enable-cache: true
|
|
23
|
+
- run: uv sync --dev
|
|
24
|
+
- run: uv run ruff check .
|
|
25
|
+
- run: uv run ruff format --check .
|
|
26
|
+
- run: uv run mypy nonebot_plugin_weather_fighter
|
|
27
|
+
|
|
28
|
+
test:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
strategy:
|
|
31
|
+
fail-fast: false
|
|
32
|
+
matrix:
|
|
33
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
- uses: astral-sh/setup-uv@v6
|
|
37
|
+
with:
|
|
38
|
+
python-version: ${{ matrix.python-version }}
|
|
39
|
+
enable-cache: true
|
|
40
|
+
- run: uv sync --dev
|
|
41
|
+
- run: uv run pytest
|
|
42
|
+
|
|
43
|
+
build:
|
|
44
|
+
needs: [lint, test]
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
- uses: astral-sh/setup-uv@v6
|
|
49
|
+
with:
|
|
50
|
+
python-version: "3.12"
|
|
51
|
+
enable-cache: true
|
|
52
|
+
- run: uv sync --dev
|
|
53
|
+
- run: uv build
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
publish:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
environment: pypi_upload
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: astral-sh/setup-uv@v6
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
enable-cache: true
|
|
23
|
+
- run: uv build
|
|
24
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.venv/
|
|
6
|
+
venv/
|
|
7
|
+
|
|
8
|
+
# 本地配置与数据库
|
|
9
|
+
.env
|
|
10
|
+
.env.*
|
|
11
|
+
!.env.example
|
|
12
|
+
data/
|
|
13
|
+
*.db
|
|
14
|
+
*.db-wal
|
|
15
|
+
*.db-shm
|
|
16
|
+
*.sqlite
|
|
17
|
+
*.sqlite3
|
|
18
|
+
|
|
19
|
+
# 缓存
|
|
20
|
+
.pytest_cache/
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
.ruff_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
|
|
26
|
+
# 本地工具
|
|
27
|
+
.commandcode/
|
|
28
|
+
|
|
29
|
+
# 仅本地开发(不入库)
|
|
30
|
+
uv.lock
|
|
31
|
+
|
|
32
|
+
# 构建产物
|
|
33
|
+
build/
|
|
34
|
+
dist/
|
|
35
|
+
*.whl
|
|
36
|
+
|
|
37
|
+
# Playwright
|
|
38
|
+
.playwright/
|
|
39
|
+
test-results/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 qwq12738qwq
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: nonebot-plugin-weather-fighter
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: NoneBot2 群盘天气指数小游戏 + 天气查询(M2:所有群共用一个盘 / 多省 / 真实交易时间)
|
|
5
|
+
Author: weather-fighter contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: game,kline,nonebot,nonebot2,qq,weather
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: aiosqlite<1.0,>=0.20
|
|
15
|
+
Requires-Dist: chinesecalendar<2,>=1.11
|
|
16
|
+
Requires-Dist: httpx<1.0,>=0.27
|
|
17
|
+
Requires-Dist: jinja2<4.0,>=3.1
|
|
18
|
+
Requires-Dist: nonebot-adapter-onebot<3.0.0,>=2.4.0
|
|
19
|
+
Requires-Dist: nonebot-plugin-htmlrender[playwright]<0.9.0,>=0.8.0
|
|
20
|
+
Requires-Dist: nonebot-plugin-localstore<0.9.0,>=0.7.0
|
|
21
|
+
Requires-Dist: nonebot2[fastapi]<3.0.0,>=2.4.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
<div align="center">
|
|
25
|
+
<a href="https://v2.nonebot.dev/store"><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/nbp_logo.png" width="180" height="180" alt="NoneBotPluginLogo"></a>
|
|
26
|
+
<br>
|
|
27
|
+
<p><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/NoneBotPlugin.svg" width="240" alt="NoneBotPluginText"></p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
# nonebot-plugin-weather-fighter
|
|
31
|
+
|
|
32
|
+
## 📖 介绍
|
|
33
|
+
|
|
34
|
+
本项目基于[weather-fighter](https://github.com/lolikonnn/weather-fighter)二次开发
|
|
35
|
+
可以先给原项目一个star哦(●′ω`●)
|
|
36
|
+
|
|
37
|
+
## 💿 安装(以下选择其一安装即可)
|
|
38
|
+
|
|
39
|
+
<summary>使用 nb-cli 安装 (十分甚至九分的推荐)</summary>
|
|
40
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
|
|
41
|
+
|
|
42
|
+
nb plugin install nonebot-plugin-weather-fighter
|
|
43
|
+
|
|
44
|
+
</details>
|
|
45
|
+
|
|
46
|
+
<details>
|
|
47
|
+
<summary>使用 pip 安装</summary>
|
|
48
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
|
|
49
|
+
|
|
50
|
+
pip install nonebot-plugin-weather-fighter
|
|
51
|
+
|
|
52
|
+
如果安装完插件不载入的话,在nonebot文件夹中找到**pyproject.toml**,在**plugins**里面添加**nonebot-plugin-beatsaberscore**就完成了
|
|
53
|
+
|
|
54
|
+
</details>
|
|
55
|
+
|
|
56
|
+
>如果你的Nonebot2项目下已经启用虚拟环境,可以使用Pipenv安装到项目中,但你需要安装pipenv包
|
|
57
|
+
>
|
|
58
|
+
<details>
|
|
59
|
+
<summary>使用 pipenv 安装</summary>
|
|
60
|
+
先安装pipenv
|
|
61
|
+
|
|
62
|
+
pip install pipenv
|
|
63
|
+
|
|
64
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
|
|
65
|
+
|
|
66
|
+
pipenv install nonebot-plugin-weather-fighter
|
|
67
|
+
|
|
68
|
+
</details>
|
|
69
|
+
<summary>使用 uv 安装</summary>
|
|
70
|
+
|
|
71
|
+
uv sync
|
|
72
|
+
uv add nonebot-plugin-weather-fighter
|
|
73
|
+
|
|
74
|
+
</details>
|
|
75
|
+
|
|
76
|
+
## 🔄 更新
|
|
77
|
+
> [!NOTE]
|
|
78
|
+
> ***请不要用上面的安装方式更新!!!!!!!!!!!!***
|
|
79
|
+
<details open>
|
|
80
|
+
<summary>使用 nb-cli 更新</summary>
|
|
81
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令更新
|
|
82
|
+
|
|
83
|
+
nb plugin install nonebot-plugin-weather-fighter --upgrade
|
|
84
|
+
|
|
85
|
+
</details>
|
|
86
|
+
|
|
87
|
+
<details>
|
|
88
|
+
<summary>使用 pip 更新</summary>
|
|
89
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可更新
|
|
90
|
+
|
|
91
|
+
pip install nonebot-plugin-weather-fighter --upgrade
|
|
92
|
+
|
|
93
|
+
</details>
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
## 🎉 命令
|
|
97
|
+
|
|
98
|
+
| 命令 | 说明 |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `天气 [省份/城市]` | 主图截图 + 现在天气 + 今日高低温 + 未来 1 小时;`行情` / `大盘` / `K线` 是同一命令的别名;不用开盘 |
|
|
101
|
+
| `买入 [省份] [金额] [倍数 N]` | 做多;金额不填默认 20%;可选 `倍数 5` / `杠杆 5` |
|
|
102
|
+
| `卖出 [省份] [金额] [倍数 N]` | 写金额做空;不写金额默认平仓;可选 `倍数 5` / `杠杆 5` |
|
|
103
|
+
| `到价 [省份] 价格 买入|卖出 [金额]` | 价格触及自动买入/卖出 |
|
|
104
|
+
| `到价 [省份] 价格 平` | 价格触及自动平掉该省 |
|
|
105
|
+
| `平仓 [省份]` | 平掉(全部卖掉)该省;不带省平掉全部持仓(只平仓,不主动结算) |
|
|
106
|
+
| `撤单` | 取消自己的到价单 |
|
|
107
|
+
| `倍数 [N]` | 设置杠杆倍数(1~上限,有持仓时不能改);不带参数查看当前倍数 |
|
|
108
|
+
| `帮助 [省份]` | 命令说明 / 省份详情 |
|
|
109
|
+
|
|
110
|
+
## 📝 PS
|
|
111
|
+
|
|
112
|
+
- **所有群共用一个盘**:所有群共用一条行情时间轴
|
|
113
|
+
- 每个玩家的余额、持仓、到价单互相独立;**可以同时在多个省持仓**,每个省最多一个持仓;账户按群隔离。
|
|
114
|
+
- 真实交易时间:周一至周五 `09:30-11:30`、`13:00-15:00`;`11:30-13:00` 午休;周末休市。
|
|
115
|
+
- **完全按真实时间连续运行,没有盘到期**;发命令时懒补齐到当前真实时间。
|
|
116
|
+
- 简化规则:投入多少最多亏多少;不显示手续费/滑点/保证金/强平价/手数;倍数默认 1 倍,可在下单时带 `杠杆 5`,或先用 `倍数 5` 设置;上限由 `WEATHER_FIGHTER_LEVERAGE` 决定。
|
|
117
|
+
- 地区当前只开放全国 34 个省级行政区的天气;352 城数据保留在 `regions.json`,后续开放。
|
|
118
|
+
- 法定节假日:使用 `chinesecalendar`(随依赖更新)
|
|
119
|
+
- 彩云 v2.6 客户端预留,如果没配置彩云天气的API(`WEATHER_FIGHTER_CAIYUN_TOKEN`)默认使用免费的`Open-Meteo`数据
|
|
120
|
+
- 缓存:懒刷新 + 30 分钟桶 + 单飞 + 负缓存 + 旧值兜底,尽可能减少请求次数
|
|
121
|
+
- 保底机制: 没钱可以等着吃`保底`,低于默认余额(`WEATHER_FIGHTER_PRINCIPAL`)的 `20%`(2,000)时进入保底;每个真实自然日补默认余额的 `5%`(500),补到 `35%`(3,500)停止。
|
|
122
|
+
- **中文字体**已随插件打包,**不需要**系统安装 CJK 字体。
|
|
123
|
+
- 如果你有更好的想法和建议,随时欢迎提出[issues](https://github.com/qwq12738qwq/nonebot-plugin-weather-fighter/issues/new)~(●´∀`●)
|
|
124
|
+
|
|
125
|
+
## 配置
|
|
126
|
+
|
|
127
|
+
| 环境变量 | 默认值 | 说明 |
|
|
128
|
+
|---|---:|---|
|
|
129
|
+
| `WEATHER_FIGHTER_BUCKET_BARS` | 1800 | 15 分钟行情桶(秒) |
|
|
130
|
+
| `WEATHER_FIGHTER_STALE_SECONDS` | 21600 | 旧值最多可用时长 |
|
|
131
|
+
| `WEATHER_FIGHTER_HTTP_TIMEOUT` | 30 | 单次 HTTP 超时(秒) |
|
|
132
|
+
| `WEATHER_FIGHTER_PRINCIPAL` | 10000 | 每个玩家在每个群的初始余额 |
|
|
133
|
+
| `WEATHER_FIGHTER_LEVERAGE` | 100 | 杠杆上限(玩家可在 1~该值之间自选) |
|
|
134
|
+
| `WEATHER_FIGHTER_SAFETY_ENTRY_PCT` | 20 | 低于默认余额的百分比时进入保底 |
|
|
135
|
+
| `WEATHER_FIGHTER_SAFETY_GRANT_PCT` | 5 | 每个真实自然日补默认余额的百分比 |
|
|
136
|
+
| `WEATHER_FIGHTER_SAFETY_TARGET_PCT` | 35 | 补到默认余额的百分比后停止触发 |
|
|
137
|
+
| `WEATHER_FIGHTER_CHART_BARS` | 64 | 主图节点数(64 根 ≈ 4 个交易日,其中约 15% 为预测尾段) |
|
|
138
|
+
| `WEATHER_FIGHTER_ENABLE_REGION` | true | 是否加载同省区域指数 |
|
|
139
|
+
| `WEATHER_FIGHTER_DEFAULT_PROVINCE` | 河北 | 启动时用于建盘的默认省份 |
|
|
140
|
+
| `WEATHER_FIGHTER_CAIYUN_TOKEN` | 空 | 彩云 token(预留) |
|
|
141
|
+
|
|
142
|
+
## 主图
|
|
143
|
+
|
|
144
|
+

|
|
145
|
+
|
|
146
|
+
> 这只是一个示例~~~。
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<a href="https://v2.nonebot.dev/store"><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/nbp_logo.png" width="180" height="180" alt="NoneBotPluginLogo"></a>
|
|
3
|
+
<br>
|
|
4
|
+
<p><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/NoneBotPlugin.svg" width="240" alt="NoneBotPluginText"></p>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
# nonebot-plugin-weather-fighter
|
|
8
|
+
|
|
9
|
+
## 📖 介绍
|
|
10
|
+
|
|
11
|
+
本项目基于[weather-fighter](https://github.com/lolikonnn/weather-fighter)二次开发
|
|
12
|
+
可以先给原项目一个star哦(●′ω`●)
|
|
13
|
+
|
|
14
|
+
## 💿 安装(以下选择其一安装即可)
|
|
15
|
+
|
|
16
|
+
<summary>使用 nb-cli 安装 (十分甚至九分的推荐)</summary>
|
|
17
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
|
|
18
|
+
|
|
19
|
+
nb plugin install nonebot-plugin-weather-fighter
|
|
20
|
+
|
|
21
|
+
</details>
|
|
22
|
+
|
|
23
|
+
<details>
|
|
24
|
+
<summary>使用 pip 安装</summary>
|
|
25
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
|
|
26
|
+
|
|
27
|
+
pip install nonebot-plugin-weather-fighter
|
|
28
|
+
|
|
29
|
+
如果安装完插件不载入的话,在nonebot文件夹中找到**pyproject.toml**,在**plugins**里面添加**nonebot-plugin-beatsaberscore**就完成了
|
|
30
|
+
|
|
31
|
+
</details>
|
|
32
|
+
|
|
33
|
+
>如果你的Nonebot2项目下已经启用虚拟环境,可以使用Pipenv安装到项目中,但你需要安装pipenv包
|
|
34
|
+
>
|
|
35
|
+
<details>
|
|
36
|
+
<summary>使用 pipenv 安装</summary>
|
|
37
|
+
先安装pipenv
|
|
38
|
+
|
|
39
|
+
pip install pipenv
|
|
40
|
+
|
|
41
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装
|
|
42
|
+
|
|
43
|
+
pipenv install nonebot-plugin-weather-fighter
|
|
44
|
+
|
|
45
|
+
</details>
|
|
46
|
+
<summary>使用 uv 安装</summary>
|
|
47
|
+
|
|
48
|
+
uv sync
|
|
49
|
+
uv add nonebot-plugin-weather-fighter
|
|
50
|
+
|
|
51
|
+
</details>
|
|
52
|
+
|
|
53
|
+
## 🔄 更新
|
|
54
|
+
> [!NOTE]
|
|
55
|
+
> ***请不要用上面的安装方式更新!!!!!!!!!!!!***
|
|
56
|
+
<details open>
|
|
57
|
+
<summary>使用 nb-cli 更新</summary>
|
|
58
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令更新
|
|
59
|
+
|
|
60
|
+
nb plugin install nonebot-plugin-weather-fighter --upgrade
|
|
61
|
+
|
|
62
|
+
</details>
|
|
63
|
+
|
|
64
|
+
<details>
|
|
65
|
+
<summary>使用 pip 更新</summary>
|
|
66
|
+
在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可更新
|
|
67
|
+
|
|
68
|
+
pip install nonebot-plugin-weather-fighter --upgrade
|
|
69
|
+
|
|
70
|
+
</details>
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
## 🎉 命令
|
|
74
|
+
|
|
75
|
+
| 命令 | 说明 |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `天气 [省份/城市]` | 主图截图 + 现在天气 + 今日高低温 + 未来 1 小时;`行情` / `大盘` / `K线` 是同一命令的别名;不用开盘 |
|
|
78
|
+
| `买入 [省份] [金额] [倍数 N]` | 做多;金额不填默认 20%;可选 `倍数 5` / `杠杆 5` |
|
|
79
|
+
| `卖出 [省份] [金额] [倍数 N]` | 写金额做空;不写金额默认平仓;可选 `倍数 5` / `杠杆 5` |
|
|
80
|
+
| `到价 [省份] 价格 买入|卖出 [金额]` | 价格触及自动买入/卖出 |
|
|
81
|
+
| `到价 [省份] 价格 平` | 价格触及自动平掉该省 |
|
|
82
|
+
| `平仓 [省份]` | 平掉(全部卖掉)该省;不带省平掉全部持仓(只平仓,不主动结算) |
|
|
83
|
+
| `撤单` | 取消自己的到价单 |
|
|
84
|
+
| `倍数 [N]` | 设置杠杆倍数(1~上限,有持仓时不能改);不带参数查看当前倍数 |
|
|
85
|
+
| `帮助 [省份]` | 命令说明 / 省份详情 |
|
|
86
|
+
|
|
87
|
+
## 📝 PS
|
|
88
|
+
|
|
89
|
+
- **所有群共用一个盘**:所有群共用一条行情时间轴
|
|
90
|
+
- 每个玩家的余额、持仓、到价单互相独立;**可以同时在多个省持仓**,每个省最多一个持仓;账户按群隔离。
|
|
91
|
+
- 真实交易时间:周一至周五 `09:30-11:30`、`13:00-15:00`;`11:30-13:00` 午休;周末休市。
|
|
92
|
+
- **完全按真实时间连续运行,没有盘到期**;发命令时懒补齐到当前真实时间。
|
|
93
|
+
- 简化规则:投入多少最多亏多少;不显示手续费/滑点/保证金/强平价/手数;倍数默认 1 倍,可在下单时带 `杠杆 5`,或先用 `倍数 5` 设置;上限由 `WEATHER_FIGHTER_LEVERAGE` 决定。
|
|
94
|
+
- 地区当前只开放全国 34 个省级行政区的天气;352 城数据保留在 `regions.json`,后续开放。
|
|
95
|
+
- 法定节假日:使用 `chinesecalendar`(随依赖更新)
|
|
96
|
+
- 彩云 v2.6 客户端预留,如果没配置彩云天气的API(`WEATHER_FIGHTER_CAIYUN_TOKEN`)默认使用免费的`Open-Meteo`数据
|
|
97
|
+
- 缓存:懒刷新 + 30 分钟桶 + 单飞 + 负缓存 + 旧值兜底,尽可能减少请求次数
|
|
98
|
+
- 保底机制: 没钱可以等着吃`保底`,低于默认余额(`WEATHER_FIGHTER_PRINCIPAL`)的 `20%`(2,000)时进入保底;每个真实自然日补默认余额的 `5%`(500),补到 `35%`(3,500)停止。
|
|
99
|
+
- **中文字体**已随插件打包,**不需要**系统安装 CJK 字体。
|
|
100
|
+
- 如果你有更好的想法和建议,随时欢迎提出[issues](https://github.com/qwq12738qwq/nonebot-plugin-weather-fighter/issues/new)~(●´∀`●)
|
|
101
|
+
|
|
102
|
+
## 配置
|
|
103
|
+
|
|
104
|
+
| 环境变量 | 默认值 | 说明 |
|
|
105
|
+
|---|---:|---|
|
|
106
|
+
| `WEATHER_FIGHTER_BUCKET_BARS` | 1800 | 15 分钟行情桶(秒) |
|
|
107
|
+
| `WEATHER_FIGHTER_STALE_SECONDS` | 21600 | 旧值最多可用时长 |
|
|
108
|
+
| `WEATHER_FIGHTER_HTTP_TIMEOUT` | 30 | 单次 HTTP 超时(秒) |
|
|
109
|
+
| `WEATHER_FIGHTER_PRINCIPAL` | 10000 | 每个玩家在每个群的初始余额 |
|
|
110
|
+
| `WEATHER_FIGHTER_LEVERAGE` | 100 | 杠杆上限(玩家可在 1~该值之间自选) |
|
|
111
|
+
| `WEATHER_FIGHTER_SAFETY_ENTRY_PCT` | 20 | 低于默认余额的百分比时进入保底 |
|
|
112
|
+
| `WEATHER_FIGHTER_SAFETY_GRANT_PCT` | 5 | 每个真实自然日补默认余额的百分比 |
|
|
113
|
+
| `WEATHER_FIGHTER_SAFETY_TARGET_PCT` | 35 | 补到默认余额的百分比后停止触发 |
|
|
114
|
+
| `WEATHER_FIGHTER_CHART_BARS` | 64 | 主图节点数(64 根 ≈ 4 个交易日,其中约 15% 为预测尾段) |
|
|
115
|
+
| `WEATHER_FIGHTER_ENABLE_REGION` | true | 是否加载同省区域指数 |
|
|
116
|
+
| `WEATHER_FIGHTER_DEFAULT_PROVINCE` | 河北 | 启动时用于建盘的默认省份 |
|
|
117
|
+
| `WEATHER_FIGHTER_CAIYUN_TOKEN` | 空 | 彩云 token(预留) |
|
|
118
|
+
|
|
119
|
+
## 主图
|
|
120
|
+
|
|
121
|
+

|
|
122
|
+
|
|
123
|
+
> 这只是一个示例~~~。
|
|
Binary file
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from nonebot import get_driver, get_plugin_config, logger, require
|
|
8
|
+
from nonebot.plugin import PluginMetadata
|
|
9
|
+
|
|
10
|
+
require("nonebot_plugin_localstore")
|
|
11
|
+
|
|
12
|
+
_driver = get_driver()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _playwright_cache_dir() -> str:
|
|
16
|
+
configured = os.environ.get("PLAYWRIGHT_BROWSERS_PATH")
|
|
17
|
+
if configured:
|
|
18
|
+
return configured
|
|
19
|
+
if platform.system() == "Windows":
|
|
20
|
+
base = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local"))
|
|
21
|
+
return str(base / "ms-playwright")
|
|
22
|
+
if platform.system() == "Darwin":
|
|
23
|
+
return str(Path.home() / "Library" / "Caches" / "ms-playwright")
|
|
24
|
+
return str(Path.home() / ".cache" / "ms-playwright")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if getattr(_driver.config, "render", None) is None:
|
|
28
|
+
render_config: dict[str, object] = {"provider": "playwright"}
|
|
29
|
+
executable = os.environ.get("PLAYWRIGHT_CHROMIUM_EXECUTABLE")
|
|
30
|
+
if executable:
|
|
31
|
+
render_config["provider_config"] = {
|
|
32
|
+
"executable_path": executable,
|
|
33
|
+
"launch_args": os.environ.get(
|
|
34
|
+
"PLAYWRIGHT_CHROMIUM_ARGS", "--no-sandbox --disable-crashpad"
|
|
35
|
+
),
|
|
36
|
+
}
|
|
37
|
+
else:
|
|
38
|
+
render_config["provider_config"] = {"storage_path": _playwright_cache_dir()}
|
|
39
|
+
_driver.config.render = render_config # type: ignore[attr-defined]
|
|
40
|
+
|
|
41
|
+
require("nonebot_plugin_htmlrender")
|
|
42
|
+
|
|
43
|
+
from . import consts # noqa: E402
|
|
44
|
+
from . import matcher as matcher # noqa: E402
|
|
45
|
+
from .config import Config # noqa: E402
|
|
46
|
+
from .game.engine import get_engine # noqa: E402
|
|
47
|
+
from .services.http import close_http # noqa: E402
|
|
48
|
+
from .services.openmeteo import OpenMeteoConfig, configure_client # noqa: E402
|
|
49
|
+
from .services.provinces import resolve_province # noqa: E402
|
|
50
|
+
from .services.store import close_store, init_store # noqa: E402
|
|
51
|
+
|
|
52
|
+
__plugin_meta__ = PluginMetadata(
|
|
53
|
+
name="天气战士",
|
|
54
|
+
description="老实看天气,很枯燥吧",
|
|
55
|
+
usage=consts.HELP_TEXT,
|
|
56
|
+
type="application",
|
|
57
|
+
config=Config,
|
|
58
|
+
homepage="https://github.com/qwq12738qwq/nonebot-plugin-beatsaberscore",
|
|
59
|
+
supported_adapters={"~onebot.v11"},
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@_driver.on_startup
|
|
64
|
+
async def _startup() -> None:
|
|
65
|
+
config = get_plugin_config(Config)
|
|
66
|
+
configure_client(
|
|
67
|
+
OpenMeteoConfig(
|
|
68
|
+
bucket_seconds=config.weather_fighter_bucket_bars,
|
|
69
|
+
stale_seconds=config.weather_fighter_stale_seconds,
|
|
70
|
+
timeout=config.weather_fighter_http_timeout,
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
await init_store()
|
|
74
|
+
province = resolve_province(config.weather_fighter_default_province)
|
|
75
|
+
if province is None:
|
|
76
|
+
logger.warning("默认省份 {} 无效,跳过启动开盘", config.weather_fighter_default_province)
|
|
77
|
+
return
|
|
78
|
+
try:
|
|
79
|
+
await get_engine().open_board(province)
|
|
80
|
+
except Exception as exc: # noqa: BLE001 - 启动开盘失败时等首次命令重试
|
|
81
|
+
logger.opt(exception=exc).warning("启动开盘失败,首次命令时会重试")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@_driver.on_shutdown
|
|
85
|
+
async def _shutdown() -> None:
|
|
86
|
+
await close_store()
|
|
87
|
+
await close_http()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Config(BaseModel):
|
|
7
|
+
"""插件配置;环境变量前缀见 .env.example。"""
|
|
8
|
+
|
|
9
|
+
weather_fighter_bucket_bars: int = Field(default=1800, ge=60)
|
|
10
|
+
weather_fighter_bucket_realtime: int = Field(default=300, ge=30)
|
|
11
|
+
weather_fighter_bucket_hourly: int = Field(default=3600, ge=60)
|
|
12
|
+
weather_fighter_bucket_daily: int = Field(default=10800, ge=300)
|
|
13
|
+
weather_fighter_stale_seconds: int = Field(default=6 * 3600, ge=60)
|
|
14
|
+
weather_fighter_http_timeout: float = Field(default=30.0, gt=0)
|
|
15
|
+
weather_fighter_preload_season_series: bool = True
|
|
16
|
+
weather_fighter_caiyun_token: str = ""
|
|
17
|
+
weather_fighter_command_cooldown: float = Field(default=5.0, ge=0)
|
|
18
|
+
weather_fighter_principal: float = Field(default=10_000.0, ge=1_000)
|
|
19
|
+
weather_fighter_leverage: float = Field(default=100.0, ge=1, le=100)
|
|
20
|
+
weather_fighter_safety_entry_pct: float = Field(default=20.0, ge=1, le=100)
|
|
21
|
+
weather_fighter_safety_grant_pct: float = Field(default=5.0, gt=0, le=100)
|
|
22
|
+
weather_fighter_safety_target_pct: float = Field(default=35.0, gt=0, le=100)
|
|
23
|
+
weather_fighter_chart_bars: int = Field(default=64, ge=8, le=1000)
|
|
24
|
+
weather_fighter_enable_region: bool = True
|
|
25
|
+
weather_fighter_default_province: str = "河北"
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
PLUGIN_NAME = "weather_fighter"
|
|
4
|
+
PLUGIN_VERSION = "0.4.0"
|
|
5
|
+
|
|
6
|
+
CMD_WEATHER = "天气"
|
|
7
|
+
CMD_LONG = "买入"
|
|
8
|
+
CMD_SHORT = "卖出"
|
|
9
|
+
CMD_CLOSE = "平仓"
|
|
10
|
+
CMD_ORDER = "到价"
|
|
11
|
+
CMD_CANCEL = "撤单"
|
|
12
|
+
CMD_LEVERAGE = "倍数"
|
|
13
|
+
CMD_HELP = "帮助"
|
|
14
|
+
|
|
15
|
+
DB_FILE = "game.db"
|
|
16
|
+
PROVINCES_FILE = "provinces.json"
|
|
17
|
+
REGIONS_FILE = "regions.json"
|
|
18
|
+
|
|
19
|
+
BASE_PRICE = 1000.0
|
|
20
|
+
BARS_PER_DAY = 16
|
|
21
|
+
OPENMETEO_PAST_DAYS = 92
|
|
22
|
+
|
|
23
|
+
WEATHER_NAMES = {
|
|
24
|
+
0: "晴",
|
|
25
|
+
1: "基本晴",
|
|
26
|
+
2: "多云",
|
|
27
|
+
3: "阴",
|
|
28
|
+
45: "雾",
|
|
29
|
+
48: "雾凇",
|
|
30
|
+
51: "毛毛雨",
|
|
31
|
+
53: "毛毛雨",
|
|
32
|
+
55: "毛毛雨",
|
|
33
|
+
56: "冻毛雨",
|
|
34
|
+
57: "冻毛雨",
|
|
35
|
+
61: "小雨",
|
|
36
|
+
63: "中雨",
|
|
37
|
+
65: "大雨",
|
|
38
|
+
66: "冻雨",
|
|
39
|
+
67: "冻雨",
|
|
40
|
+
71: "小雪",
|
|
41
|
+
73: "中雪",
|
|
42
|
+
75: "大雪",
|
|
43
|
+
77: "米雪",
|
|
44
|
+
80: "阵雨",
|
|
45
|
+
81: "阵雨",
|
|
46
|
+
82: "强阵雨",
|
|
47
|
+
85: "阵雪",
|
|
48
|
+
86: "强阵雪",
|
|
49
|
+
95: "雷暴",
|
|
50
|
+
96: "雷暴夹雹",
|
|
51
|
+
99: "强雷暴",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
WEATHER_ICONS = {
|
|
55
|
+
0: "☀",
|
|
56
|
+
1: "🌤",
|
|
57
|
+
2: "⛅",
|
|
58
|
+
3: "☁",
|
|
59
|
+
45: "🌫",
|
|
60
|
+
48: "🌫",
|
|
61
|
+
51: "🌦",
|
|
62
|
+
53: "🌦",
|
|
63
|
+
55: "🌦",
|
|
64
|
+
56: "🌧",
|
|
65
|
+
57: "🌧",
|
|
66
|
+
61: "🌧",
|
|
67
|
+
63: "🌧",
|
|
68
|
+
65: "🌧",
|
|
69
|
+
66: "🌧",
|
|
70
|
+
67: "🌧",
|
|
71
|
+
71: "🌨",
|
|
72
|
+
73: "🌨",
|
|
73
|
+
75: "❄",
|
|
74
|
+
77: "🌨",
|
|
75
|
+
80: "🌦",
|
|
76
|
+
81: "🌧",
|
|
77
|
+
82: "⛈",
|
|
78
|
+
85: "🌨",
|
|
79
|
+
86: "❄",
|
|
80
|
+
95: "⛈",
|
|
81
|
+
96: "⛈",
|
|
82
|
+
99: "⛈",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_HELP_TEXT = """天气战士 · 所有群共用一个盘
|
|
86
|
+
|
|
87
|
+
主图 + 天气(天气 / 行情 / 大盘 / K线 同一命令,不用开盘):
|
|
88
|
+
天气 [省份] / 天气 [市级地] —— 主图 + 现在天气 + 今日高低温
|
|
89
|
+
|
|
90
|
+
交易(按真实时间自动运行,不需要开盘):
|
|
91
|
+
买入 [省份] 1000 / 卖出 河北 1万 —— 顾名思义
|
|
92
|
+
买入 [省份] 1000 杠杆 5 —— 下单时指定倍数
|
|
93
|
+
卖出 [省份] —— 不写金额默认全卖出
|
|
94
|
+
金额:1000 / 1.5万 / 20% / 全;不填默认 20%
|
|
95
|
+
到价 [省份] 16.5 买入 1000 —— 价格碰到 16.5℃ 自动买入
|
|
96
|
+
到价 [省份] 16.5 卖出 1000 —— 价格碰到 16.5℃ 自动卖出
|
|
97
|
+
到价 [省份] 18.0 平 —— 价格碰到 18.0℃ 自动平掉河北
|
|
98
|
+
平仓 [省] —— 平掉该省;不带省则平掉全部持仓
|
|
99
|
+
撤单 —— 取消自己的到价单
|
|
100
|
+
倍数 10 —— 设置杠杆倍数(1~上限,有持仓时不能改)
|
|
101
|
+
|
|
102
|
+
规则:
|
|
103
|
+
· 能吃保底
|
|
104
|
+
· 每个玩家自己的余额、持仓、到价单互相独立。
|
|
105
|
+
· 倍数默认 1 倍,可下单时带「杠杆 5」或先用「倍数 5」设置;
|
|
106
|
+
· 按照 A 股时间:09:30-11:30、13:00-15:00;午休和周末停盘。
|
|
107
|
+
· 默认余额 10,000。低于 20%(2,000)时进入保底:每个真实自然日补 5%(500),
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
HELP_TEXT = _HELP_TEXT
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def weather_name(code: int) -> str:
|
|
114
|
+
return WEATHER_NAMES.get(code, f"码 {code}")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def weather_icon(code: int) -> str:
|
|
118
|
+
return WEATHER_ICONS.get(code, "🌡")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def wind_level(gust: float) -> int:
|
|
122
|
+
"""把阵风 m/s 粗换成风力等级,给玩家一个直观数字。"""
|
|
123
|
+
thresholds = (0.3, 1.6, 3.4, 5.5, 8.0, 10.8, 13.9, 17.2, 20.8, 24.5, 28.5, 32.7)
|
|
124
|
+
for level, limit in enumerate(thresholds):
|
|
125
|
+
if gust < limit:
|
|
126
|
+
return level
|
|
127
|
+
return 12
|