Compare commits

..

6 Commits
1.8.0 ... 1.8.2

Author SHA1 Message Date
243a8a286d 🔖 1.8.2 2025-04-28 04:09:04 +08:00
8b7de6745b 添加 development 模式 2025-04-28 04:08:55 +08:00
d0fc82d88d 添加开发依赖 nonebot-plugin-tarina-lang-turbo 2025-04-28 04:06:48 +08:00
bb4da8accc 🔖 1.8.1
Some checks are pending
Code Coverage / Test (macos-latest, 3.10) (push) Waiting to run
Code Coverage / Test (macos-latest, 3.11) (push) Waiting to run
Code Coverage / Test (macos-latest, 3.12) (push) Waiting to run
Code Coverage / Test (ubuntu-latest, 3.10) (push) Waiting to run
Code Coverage / Test (ubuntu-latest, 3.11) (push) Waiting to run
Code Coverage / Test (ubuntu-latest, 3.12) (push) Waiting to run
Code Coverage / Test (windows-latest, 3.10) (push) Waiting to run
Code Coverage / Test (windows-latest, 3.11) (push) Waiting to run
Code Coverage / Test (windows-latest, 3.12) (push) Waiting to run
Code Coverage / check (push) Blocked by required conditions
TypeCheck / TypeCheck (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
2025-04-27 15:18:25 +08:00
56e06a7001 🐛 修复 _lang 为私有变量不会默认序列化的bug 2025-04-27 15:17:12 +08:00
renovate[bot]
7c0b3cd240 ⬆️ Upgrade astral-sh/setup-uv action to v6 (#537)
Some checks failed
Code Coverage / Test (macos-latest, 3.10) (push) Has been cancelled
Code Coverage / Test (macos-latest, 3.11) (push) Has been cancelled
Code Coverage / Test (macos-latest, 3.12) (push) Has been cancelled
Code Coverage / Test (ubuntu-latest, 3.10) (push) Has been cancelled
Code Coverage / Test (ubuntu-latest, 3.11) (push) Has been cancelled
Code Coverage / Test (ubuntu-latest, 3.12) (push) Has been cancelled
Code Coverage / Test (windows-latest, 3.10) (push) Has been cancelled
Code Coverage / Test (windows-latest, 3.11) (push) Has been cancelled
Code Coverage / Test (windows-latest, 3.12) (push) Has been cancelled
TypeCheck / TypeCheck (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Code Coverage / check (push) Has been cancelled
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-24 22:50:26 +08:00
25 changed files with 69 additions and 36 deletions

View File

@@ -14,7 +14,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5 - uses: astral-sh/setup-uv@v6
name: Setup UV name: Setup UV
with: with:
enable-cache: true enable-cache: true

View File

@@ -28,7 +28,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup uv - name: Setup uv
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v6
with: with:
enable-cache: true enable-cache: true
cache-suffix: ${{ env.PYTHON_VERSION }}_${{ env.OS }} cache-suffix: ${{ env.PYTHON_VERSION }}_${{ env.OS }}

View File

@@ -9,7 +9,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5 - uses: astral-sh/setup-uv@v6
name: Setup UV name: Setup UV
with: with:
enable-cache: true enable-cache: true

View File

@@ -18,6 +18,7 @@ class ScopedConfig(BaseModel):
request_timeout: float = 30.0 request_timeout: float = 30.0
screenshot_quality: float = 2 screenshot_quality: float = 2
proxy: Proxy = Field(default_factory=Proxy) proxy: Proxy = Field(default_factory=Proxy)
development: bool = False
class Config(BaseModel): class Config(BaseModel):

View File

@@ -81,7 +81,7 @@ async def _(nb_user: User, account: Player, event_session: EventSession, bot_inf
name=bot_info.user_name, name=bot_info.user_name,
), ),
prompt='io查我', prompt='io查我',
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -89,7 +89,7 @@ async def _(
for i in league.data.entries for i in league.data.entries
if isinstance(i, Entry) if isinstance(i, Entry)
], ],
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -93,7 +93,7 @@ async def make_query_image_v1(player: Player) -> bytes:
sprint=sprint_value, sprint=sprint_value,
blitz=blitz_value, blitz=blitz_value,
), ),
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -188,7 +188,7 @@ async def make_query_image_v2(player: Player) -> bytes:
if blitz.data.record is not None if blitz.data.record is not None
else None, else None,
zen=Zen(level=zen.data.level, score=zen.data.score), zen=Zen(level=zen.data.level, score=zen.data.score),
_lang=get_lang(), lang=get_lang(),
), ),
), ),
) as page_hash: ) as page_hash:

View File

@@ -12,6 +12,7 @@ from nonebot_plugin_apscheduler import scheduler
from nonebot_plugin_orm import get_session from nonebot_plugin_orm import get_session
from sqlalchemy import select from sqlalchemy import select
from ....config.config import config
from ....utils.exception import RequestError from ....utils.exception import RequestError
from ....utils.retry import retry from ....utils.retry import retry
from .. import alc from .. import alc
@@ -136,14 +137,16 @@ async def get_tetra_league_data() -> None:
await session.commit() await session.commit()
@driver.on_startup if not config.tetris.development:
async def _() -> None:
async with get_session() as session: @driver.on_startup
latest_time = await session.scalar( async def _() -> None:
select(TETRIOLeagueStats.update_time).order_by(TETRIOLeagueStats.id.desc()).limit(1) async with get_session() as session:
) latest_time = await session.scalar(
if latest_time is None or datetime.now(tz=UTC) - latest_time.replace(tzinfo=UTC) > timedelta(hours=6): select(TETRIOLeagueStats.update_time).order_by(TETRIOLeagueStats.id.desc()).limit(1)
await get_tetra_league_data() )
if latest_time is None or datetime.now(tz=UTC) - latest_time.replace(tzinfo=UTC) > timedelta(hours=6):
await get_tetra_league_data()
from . import all, detail # noqa: A004, E402 from . import all, detail # noqa: A004, E402

View File

@@ -83,7 +83,7 @@ async def make_image_v1(latest_data: TETRIOLeagueStats, compare_data: TETRIOLeag
for i in zip(latest_data.fields, compare_data.fields, strict=True) for i in zip(latest_data.fields, compare_data.fields, strict=True)
}, },
updated_at=latest_data.update_time, updated_at=latest_data.update_time,
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:
@@ -111,7 +111,7 @@ async def make_image_v2(latest_data: TETRIOLeagueStats, compare_data: TETRIOLeag
for i in zip(latest_data.fields, compare_data.fields, strict=True) for i in zip(latest_data.fields, compare_data.fields, strict=True)
}, },
updated_at=latest_data.update_time, updated_at=latest_data.update_time,
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -123,7 +123,7 @@ async def make_image(rank: ValidRank, latest: TETRIOLeagueStats, compare: TETRIO
vs_holder=latest_data.high_vs.username.upper(), vs_holder=latest_data.high_vs.username.upper(),
), ),
updated_at=latest.update_time.replace(tzinfo=UTC).astimezone(ZoneInfo('Asia/Shanghai')), updated_at=latest.update_time.replace(tzinfo=UTC).astimezone(ZoneInfo('Asia/Shanghai')),
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -146,7 +146,7 @@ async def make_blitz_image(player: Player) -> bytes:
level=stats.level, level=stats.level,
), ),
play_at=blitz.data.record.ts, play_at=blitz.data.record.ts,
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -146,7 +146,7 @@ async def make_sprint_image(player: Player) -> bytes:
), ),
), ),
play_at=sprint.data.record.ts, play_at=sprint.data.record.ts,
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -69,7 +69,7 @@ async def _(nb_user: User, event_session: EventSession, bot_info: UserInfo = Bot
name=bot_info.user_name, name=bot_info.user_name,
), ),
prompt='io绑定{游戏ID}', prompt='io绑定{游戏ID}',
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -55,7 +55,7 @@ async def _(
name=bot_info.user_name, name=bot_info.user_name,
), ),
prompt='top查我', prompt='top查我',
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -96,7 +96,7 @@ async def make_query_image(profile: UserProfile) -> bytes:
apl=history.apl, apl=history.apl,
apm_trending=Trending.KEEP, apm_trending=Trending.KEEP,
), ),
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -57,7 +57,7 @@ async def _(
name=bot_info.user_name, name=bot_info.user_name,
), ),
prompt='top绑定{游戏ID}', prompt='top绑定{游戏ID}',
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -55,7 +55,7 @@ async def _(
name=bot_info.user_remark or bot_info.user_displayname or bot_info.user_name, name=bot_info.user_remark or bot_info.user_displayname or bot_info.user_name,
), ),
prompt='茶服查我', prompt='茶服查我',
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -304,7 +304,7 @@ async def make_query_image(user_info: UserInfoSuccess, game_data: GameData, even
challenge=f'{int(user_info.data.pb_challenge):,}' if user_info.data.pb_challenge != '0' else 'N/A', challenge=f'{int(user_info.data.pb_challenge):,}' if user_info.data.pb_challenge != '0' else 'N/A',
marathon=f'{int(user_info.data.pb_marathon):,}' if user_info.data.pb_marathon != '0' else 'N/A', marathon=f'{int(user_info.data.pb_marathon):,}' if user_info.data.pb_marathon != '0' else 'N/A',
), ),
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -60,7 +60,7 @@ async def _(
name=bot_info.user_name, name=bot_info.user_name,
), ),
prompt='茶服绑定{游戏ID}', prompt='茶服绑定{游戏ID}',
_lang=get_lang(), lang=get_lang(),
), ),
) )
) as page_hash: ) as page_hash:

View File

@@ -10,6 +10,8 @@ from nonebot.log import logger
from playwright.__main__ import main from playwright.__main__ import main
from playwright.async_api import Browser, BrowserContext, async_playwright from playwright.async_api import Browser, BrowserContext, async_playwright
from ..config.config import config
driver = get_driver() driver = get_driver()
global_config = driver.config global_config = driver.config
@@ -76,6 +78,7 @@ class BrowserManager:
"""启动浏览器实例""" """启动浏览器实例"""
playwright = await async_playwright().start() playwright = await async_playwright().start()
cls._browser = await playwright.firefox.launch( cls._browser = await playwright.firefox.launch(
headless=not config.tetris.development,
firefox_user_prefs={ firefox_user_prefs={
'network.http.max-persistent-connections-per-server': 64, 'network.http.max-persistent-connections-per-server': 64,
}, },

View File

@@ -12,7 +12,7 @@ from nonebot import get_app, get_driver
from nonebot.log import logger from nonebot.log import logger
from yarl import URL from yarl import URL
from ..config.config import CACHE_PATH from ..config.config import CACHE_PATH, config
from ..games.tetrio.api.cache import request from ..games.tetrio.api.cache import request
from .image import img_to_png from .image import img_to_png
from .templates import TEMPLATES_DIR from .templates import TEMPLATES_DIR
@@ -45,8 +45,14 @@ class HostPage:
async def __aenter__(self) -> str: async def __aenter__(self) -> str:
return self.page_hash return self.page_hash
async def __aexit__(self, exc_type, exc, tb) -> None: # noqa: ANN001 if not config.tetris.development:
self.pages.pop(self.page_hash, None)
async def __aexit__(self, exc_type, exc, tb) -> None: # noqa: ANN001
self.pages.pop(self.page_hash, None)
else:
async def __aexit__(self, exc_type, exc, tb) -> None: # noqa: ANN001
pass
@driver.on_startup @driver.on_startup

View File

@@ -8,7 +8,7 @@ from ...typedefs import Lang, Number
class Base(BaseModel): class Base(BaseModel):
_lang: Lang lang: Lang
class Avatar(BaseModel): class Avatar(BaseModel):

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "nonebot-plugin-tetris-stats" name = "nonebot-plugin-tetris-stats"
version = "1.8.0" version = "1.8.2"
description = "一款基于 NoneBot2 的用于查询 Tetris 相关游戏数据的插件" description = "一款基于 NoneBot2 的用于查询 Tetris 相关游戏数据的插件"
readme = "README.md" readme = "README.md"
authors = [{ name = "shoucandanghehe", email = "wallfjjd@gmail.com" }] authors = [{ name = "shoucandanghehe", email = "wallfjjd@gmail.com" }]
@@ -58,6 +58,7 @@ dev = [
"nonebot-adapter-kaiheila>=0.3.4", "nonebot-adapter-kaiheila>=0.3.4",
"nonebot-adapter-onebot>=2.4.6", "nonebot-adapter-onebot>=2.4.6",
"nonebot-adapter-qq>=1.5.3", "nonebot-adapter-qq>=1.5.3",
"nonebot-plugin-tarina-lang-turbo>=0.1.1",
"ruff>=0.7.1", "ruff>=0.7.1",
] ]
typecheck = [ typecheck = [
@@ -159,7 +160,7 @@ defineConstant = { PYDANTIC_V2 = true }
typeCheckingMode = "standard" typeCheckingMode = "standard"
[tool.bumpversion] [tool.bumpversion]
current_version = "1.8.0" current_version = "1.8.2"
tag = true tag = true
sign_tags = true sign_tags = true
tag_name = "{new_version}" tag_name = "{new_version}"
@@ -176,4 +177,4 @@ asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session" asyncio_default_fixture_loop_scope = "session"
[tool.nonebot] [tool.nonebot]
plugins = ["nonebot_plugin_tetris_stats"] plugins = ["nonebot_plugin_tetris_stats", "nonebot_plugin_tarina_lang_turbo"]

23
uv.lock generated
View File

@@ -1294,7 +1294,7 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8c/de/8eb6fffecd9c5f129461edcdd7e1ac944f9de15783e3d89c84ed6e0374bc/lxml-5.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa837e6ee9534de8d63bc4c1249e83882a7ac22bd24523f83fad68e6ffdf41ae", size = 5652903 }, { url = "https://files.pythonhosted.org/packages/8c/de/8eb6fffecd9c5f129461edcdd7e1ac944f9de15783e3d89c84ed6e0374bc/lxml-5.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa837e6ee9534de8d63bc4c1249e83882a7ac22bd24523f83fad68e6ffdf41ae", size = 5652903 },
{ url = "https://files.pythonhosted.org/packages/95/79/80f4102a08495c100014593680f3f0f7bd7c1333b13520aed855fc993326/lxml-5.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:da4c9223319400b97a2acdfb10926b807e51b69eb7eb80aad4942c0516934858", size = 5491813 }, { url = "https://files.pythonhosted.org/packages/95/79/80f4102a08495c100014593680f3f0f7bd7c1333b13520aed855fc993326/lxml-5.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:da4c9223319400b97a2acdfb10926b807e51b69eb7eb80aad4942c0516934858", size = 5491813 },
{ url = "https://files.pythonhosted.org/packages/15/f5/9b1f7edf6565ee31e4300edb1bcc61eaebe50a3cff4053c0206d8dc772f2/lxml-5.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dc0e9bdb3aa4d1de703a437576007d366b54f52c9897cae1a3716bb44fc1fc85", size = 5227837 }, { url = "https://files.pythonhosted.org/packages/15/f5/9b1f7edf6565ee31e4300edb1bcc61eaebe50a3cff4053c0206d8dc772f2/lxml-5.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dc0e9bdb3aa4d1de703a437576007d366b54f52c9897cae1a3716bb44fc1fc85", size = 5227837 },
{ url = "https://files.pythonhosted.org/packages/5c/17/c31d94364c02e3492215658917f5590c00edce8074aeb06d05b7771465d9/lxml-5.3.2-cp310-cp310-win32.whl", hash = "sha256:5f94909a1022c8ea12711db7e08752ca7cf83e5b57a87b59e8a583c5f35016ad", size = 3477533 }, { url = "https://files.pythonhosted.org/packages/dd/53/a187c4ccfcd5fbfca01e6c96da39499d8b801ab5dcf57717db95d7a968a8/lxml-5.3.2-cp310-cp310-win32.win32.whl", hash = "sha256:dd755a0a78dd0b2c43f972e7b51a43be518ebc130c9f1a7c4480cf08b4385486", size = 3477533 },
{ url = "https://files.pythonhosted.org/packages/f2/2c/397c5a9d76a7a0faf9e5b13143ae1a7e223e71d2197a45da71c21aacb3d4/lxml-5.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:d64ea1686474074b38da13ae218d9fde0d1dc6525266976808f41ac98d9d7980", size = 3805160 }, { url = "https://files.pythonhosted.org/packages/f2/2c/397c5a9d76a7a0faf9e5b13143ae1a7e223e71d2197a45da71c21aacb3d4/lxml-5.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:d64ea1686474074b38da13ae218d9fde0d1dc6525266976808f41ac98d9d7980", size = 3805160 },
{ url = "https://files.pythonhosted.org/packages/84/b8/2b727f5a90902f7cc5548349f563b60911ca05f3b92e35dfa751349f265f/lxml-5.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9d61a7d0d208ace43986a92b111e035881c4ed45b1f5b7a270070acae8b0bfb4", size = 8163457 }, { url = "https://files.pythonhosted.org/packages/84/b8/2b727f5a90902f7cc5548349f563b60911ca05f3b92e35dfa751349f265f/lxml-5.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9d61a7d0d208ace43986a92b111e035881c4ed45b1f5b7a270070acae8b0bfb4", size = 8163457 },
{ url = "https://files.pythonhosted.org/packages/91/84/23135b2dc72b3440d68c8f39ace2bb00fe78e3a2255f7c74f7e76f22498e/lxml-5.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856dfd7eda0b75c29ac80a31a6411ca12209183e866c33faf46e77ace3ce8a79", size = 4433445 }, { url = "https://files.pythonhosted.org/packages/91/84/23135b2dc72b3440d68c8f39ace2bb00fe78e3a2255f7c74f7e76f22498e/lxml-5.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:856dfd7eda0b75c29ac80a31a6411ca12209183e866c33faf46e77ace3ce8a79", size = 4433445 },
@@ -1926,9 +1926,26 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/96/2e/6f8e30055aa0c28e19df2659e325b27ea5b6c788dbc8b99591b0935cdaaa/nonebot_plugin_session_orm-0.2.2-py3-none-any.whl", hash = "sha256:ab2bbc9fbf245b3e236789aabb2bef4d501dd720d74002744091503d33548ba7", size = 7842 }, { url = "https://files.pythonhosted.org/packages/96/2e/6f8e30055aa0c28e19df2659e325b27ea5b6c788dbc8b99591b0935cdaaa/nonebot_plugin_session_orm-0.2.2-py3-none-any.whl", hash = "sha256:ab2bbc9fbf245b3e236789aabb2bef4d501dd720d74002744091503d33548ba7", size = 7842 },
] ]
[[package]]
name = "nonebot-plugin-tarina-lang-turbo"
version = "0.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "msgspec" },
{ name = "nonebot-plugin-alconna" },
{ name = "nonebot-plugin-localstore" },
{ name = "nonebot-plugin-session" },
{ name = "nonebot2" },
{ name = "tarina" },
]
sdist = { url = "https://files.pythonhosted.org/packages/24/db/f91bceeff96e3e6de959bd31ed8eba1dea9f297f141879bd0a08758fcbfd/nonebot_plugin_tarina_lang_turbo-0.1.1.tar.gz", hash = "sha256:33d0854d39c8fbb07020e7d1c2036943ee90a487ba4da90e0c0c89adc172cd19", size = 124382 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/54/be4dc0c899b6f154677cb502b81c3ec8ba5a37ded850f09e47916143c2d7/nonebot_plugin_tarina_lang_turbo-0.1.1-py3-none-any.whl", hash = "sha256:c231dab228e3a52993b282dbdcb5faeebd7aa4e433c9794405651334f54a96a8", size = 7988 },
]
[[package]] [[package]]
name = "nonebot-plugin-tetris-stats" name = "nonebot-plugin-tetris-stats"
version = "1.7.2" version = "1.8.1"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "aiocache" }, { name = "aiocache" },
@@ -1973,6 +1990,7 @@ dev = [
{ name = "nonebot-adapter-kaiheila" }, { name = "nonebot-adapter-kaiheila" },
{ name = "nonebot-adapter-onebot" }, { name = "nonebot-adapter-onebot" },
{ name = "nonebot-adapter-qq" }, { name = "nonebot-adapter-qq" },
{ name = "nonebot-plugin-tarina-lang-turbo" },
{ name = "ruff" }, { name = "ruff" },
] ]
release = [ release = [
@@ -2037,6 +2055,7 @@ dev = [
{ name = "nonebot-adapter-kaiheila", specifier = ">=0.3.4" }, { name = "nonebot-adapter-kaiheila", specifier = ">=0.3.4" },
{ name = "nonebot-adapter-onebot", specifier = ">=2.4.6" }, { name = "nonebot-adapter-onebot", specifier = ">=2.4.6" },
{ name = "nonebot-adapter-qq", specifier = ">=1.5.3" }, { name = "nonebot-adapter-qq", specifier = ">=1.5.3" },
{ name = "nonebot-plugin-tarina-lang-turbo", specifier = ">=0.1.1" },
{ name = "ruff", specifier = ">=0.7.1" }, { name = "ruff", specifier = ">=0.7.1" },
] ]
release = [{ name = "bump-my-version", specifier = ">=0.28.0" }] release = [{ name = "bump-my-version", specifier = ">=0.28.0" }]