mirror of
https://github.com/A-Minos/nonebot-plugin-tetris-stats.git
synced 2026-03-05 05:36:54 +08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fe69d8d2fe | |||
| 2737119865 | |||
| 34a654b5df | |||
| f9f39618a1 | |||
| 81a3c9cb79 | |||
| 4a15c45e0a | |||
| e90ad53ee6 | |||
| 0c968be163 | |||
| bfadac4f79 | |||
| 89f09cd66c | |||
| 777703362e | |||
| ea5308877c | |||
| 3cc93925a6 | |||
| e0bd0a9252 | |||
| d31ce48a18 | |||
| 7da38e0346 | |||
|
|
84368a16c3 | ||
| 6a10ede5ba | |||
| 4c205e516f |
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -7,6 +7,6 @@ version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
target-branch: "dev"
|
||||
target-branch: "main"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
40
.github/workflows/Release.yml
vendored
40
.github/workflows/Release.yml
vendored
@@ -8,18 +8,42 @@ on:
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install poetry
|
||||
run: pipx install poetry
|
||||
shell: bash
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install Poetry
|
||||
cache: "poetry"
|
||||
|
||||
- run: poetry install
|
||||
shell: bash
|
||||
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
pip install poetry
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
poetry install
|
||||
poetry env use python
|
||||
poetry publish --build -u ${{ secrets.USERNAME }} -p ${{ secrets.PASSWORD }} -n
|
||||
echo "VERSION=$(poetry version -s)" >> $GITHUB_OUTPUT
|
||||
echo "TAG_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
echo "TAG_NAME=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check Version
|
||||
if: steps.version.outputs.VERSION != steps.version.outputs.TAG_VERSION
|
||||
run: exit 1
|
||||
|
||||
- name: Build Package
|
||||
run: poetry build
|
||||
|
||||
- name: Publish Package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
|
||||
- name: Publish Package to GitHub Release
|
||||
run: gh release create ${{ steps.version.outputs.TAG_NAME }} dist/*.tar.gz dist/*.whl -t "🔖 ${{ steps.version.outputs.TAG_NAME }}" --generate-notes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -11,4 +11,4 @@ CACHE_PATH: Path = get_cache_dir('nonebot_plugin_tetris_stats')
|
||||
class Config(BaseModel):
|
||||
"""配置类"""
|
||||
|
||||
db_url: str = 'sqlite://data/nonebot_plugin_tetris_stats/data.db'
|
||||
tetris_req_timeout: float = 30.0
|
||||
|
||||
@@ -64,7 +64,7 @@ def upgrade(name: str = '') -> None:
|
||||
if name:
|
||||
return
|
||||
try:
|
||||
db_path = Path(config.db_path)
|
||||
db_path = Path(config.db_url)
|
||||
except AttributeError:
|
||||
db_path = Path('data/nonebot_plugin_tetris_stats/data.db')
|
||||
if db_path.exists() is False:
|
||||
@@ -84,7 +84,7 @@ def upgrade(name: str = '') -> None:
|
||||
raise RuntimeError('nonebot_plugin_tetris_stats: 请先安装 0.4.4 版本完成迁移之后再升级')
|
||||
logger.info('nonebot_plugin_tetris_stats: 发现来自老版本的数据, 正在迁移...')
|
||||
migrate_old_data(connection)
|
||||
db_path.unlink()
|
||||
db_path.unlink()
|
||||
|
||||
|
||||
def downgrade(name: str = '') -> None:
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot_plugin_alconna import AlcMatches, AlconnaMatcher
|
||||
|
||||
from ..utils.exception import MessageFormatError
|
||||
from ..utils.typing import CommandType, GameType
|
||||
|
||||
|
||||
@@ -65,6 +70,9 @@ class Processor(ABC):
|
||||
|
||||
def __del__(self) -> None:
|
||||
finish_time = datetime.now(tz=UTC)
|
||||
if Recorder.is_error_event(self.event_id):
|
||||
Recorder.del_error_event(self.event_id)
|
||||
return
|
||||
historical_data = Recorder.get_historical_data(self.event_id)
|
||||
historical_data.game_platform = self.game_platform
|
||||
historical_data.command_type = self.command_type
|
||||
@@ -75,6 +83,24 @@ class Processor(ABC):
|
||||
Recorder.update_historical_data(self.event_id, historical_data)
|
||||
|
||||
|
||||
def add_default_handlers(matcher: type[AlconnaMatcher]) -> None:
|
||||
@matcher.handle()
|
||||
async def _(matcher: Matcher, account: MessageFormatError):
|
||||
await matcher.finish(str(account))
|
||||
|
||||
@matcher.handle()
|
||||
async def _(matcher: Matcher, matches: AlcMatches):
|
||||
if matches.head_matched and matches.options != {} or matches.main_args == {}:
|
||||
await matcher.finish(
|
||||
(f'{matches.error_info!r}\n' if matches.error_info is not None else '')
|
||||
+ f'输入"{matches.header_result} --help"查看帮助'
|
||||
)
|
||||
|
||||
@matcher.handle()
|
||||
async def _(matcher: Matcher, other: Any): # noqa: ANN401
|
||||
await matcher.finish()
|
||||
|
||||
|
||||
from . import ( # noqa: F401, E402
|
||||
io_data_processor,
|
||||
top_data_processor,
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
|
||||
from arclet.alconna import Alconna, Arg, ArgFlag, Args, CommandMeta, Option
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot_plugin_alconna import AlcMatches, At, on_alconna
|
||||
from nonebot_plugin_alconna import At, on_alconna
|
||||
from nonebot_plugin_orm import get_session
|
||||
from sqlalchemy import select
|
||||
|
||||
from ...db import query_bind_info
|
||||
from ...utils.exception import MessageFormatError, NeedCatchError
|
||||
from ...utils.exception import NeedCatchError
|
||||
from ...utils.metrics import get_metrics
|
||||
from ...utils.platform import get_platform
|
||||
from ...utils.typing import Me
|
||||
from .. import add_default_handlers
|
||||
from ..constant import BIND_COMMAND, QUERY_COMMAND
|
||||
from .constant import GAME_TYPE
|
||||
from .model import IORank
|
||||
@@ -65,6 +67,7 @@ alc = on_alconna(
|
||||
dest='rank',
|
||||
help_text='查询 IO 段位信息',
|
||||
),
|
||||
Arg('other', Any, flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL]),
|
||||
meta=CommandMeta(
|
||||
description='查询 TETR.IO 的信息',
|
||||
example='io绑定scdhh\nio查我\niorankx',
|
||||
@@ -174,14 +177,4 @@ async def _(event: Event, matcher: Matcher, rank: Rank):
|
||||
await matcher.finish(message)
|
||||
|
||||
|
||||
@alc.handle()
|
||||
async def _(matcher: Matcher, account: MessageFormatError):
|
||||
await matcher.finish(str(account))
|
||||
|
||||
|
||||
@alc.handle()
|
||||
async def _(matcher: Matcher, matches: AlcMatches):
|
||||
if matches.head_matched:
|
||||
await matcher.finish(
|
||||
f'{matches.error_info!r}\n' if matches.error_info is not None else '' + '输入"io --help"查看帮助'
|
||||
)
|
||||
add_default_handlers(alc)
|
||||
|
||||
@@ -29,9 +29,9 @@ class SuccessModel(BaseSuccessModel):
|
||||
prev_at: Literal[-1]
|
||||
percentile: Literal[-1]
|
||||
percentile_rank: Literal['z']
|
||||
apm: None
|
||||
pps: None
|
||||
vs: None
|
||||
apm: None = Field(None)
|
||||
pps: None = Field(None)
|
||||
vs: None = Field(None)
|
||||
decaying: bool
|
||||
|
||||
class NeverRatedLeague(BaseModel):
|
||||
@@ -111,7 +111,7 @@ class SuccessModel(BaseSuccessModel):
|
||||
Ignore this field if the user is not a supporter."""
|
||||
bio: str | None
|
||||
connections: Connections
|
||||
friend_count: int
|
||||
friend_count: int | None
|
||||
distinguishment: Distinguishment | None
|
||||
|
||||
user: User
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
from typing import Any
|
||||
|
||||
from arclet.alconna import Alconna, Arg, ArgFlag, Args, CommandMeta, Option
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot_plugin_alconna import AlcMatches, At, on_alconna
|
||||
from nonebot_plugin_alconna import At, on_alconna
|
||||
from nonebot_plugin_orm import get_session
|
||||
|
||||
from ...db import query_bind_info
|
||||
from ...utils.exception import MessageFormatError, NeedCatchError
|
||||
from ...utils.exception import NeedCatchError
|
||||
from ...utils.platform import get_platform
|
||||
from ...utils.typing import Me
|
||||
from .. import add_default_handlers
|
||||
from ..constant import BIND_COMMAND, QUERY_COMMAND
|
||||
from .constant import GAME_TYPE
|
||||
from .processor import Processor, User, identify_user_info
|
||||
@@ -51,6 +54,7 @@ alc = on_alconna(
|
||||
dest='query',
|
||||
help_text='查询 TOP 游戏信息',
|
||||
),
|
||||
Arg('other', Any, flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL]),
|
||||
meta=CommandMeta(
|
||||
description='查询 TetrisOnline波兰服 的信息',
|
||||
example='top绑定scdhh\ntop查我',
|
||||
@@ -113,14 +117,4 @@ async def _(event: Event, matcher: Matcher, account: User):
|
||||
await matcher.finish(str(e))
|
||||
|
||||
|
||||
@alc.handle()
|
||||
async def _(matcher: Matcher, account: MessageFormatError):
|
||||
await matcher.finish(str(account))
|
||||
|
||||
|
||||
@alc.handle()
|
||||
async def _(matcher: Matcher, matches: AlcMatches):
|
||||
if matches.head_matched:
|
||||
await matcher.finish(
|
||||
f'{matches.error_info!r}\n' if matches.error_info is not None else '' + '输入"top --help"查看帮助'
|
||||
)
|
||||
add_default_handlers(alc)
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
from typing import Any
|
||||
|
||||
from arclet.alconna import Alconna, Arg, ArgFlag, Args, CommandMeta, Option
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot_plugin_alconna import AlcMatches, At, on_alconna
|
||||
from nonebot_plugin_alconna import At, on_alconna
|
||||
from nonebot_plugin_orm import get_session
|
||||
|
||||
from ...db import query_bind_info
|
||||
from ...utils.exception import MessageFormatError, NeedCatchError
|
||||
from ...utils.exception import NeedCatchError
|
||||
from ...utils.platform import get_platform
|
||||
from ...utils.typing import Me
|
||||
from .. import add_default_handlers
|
||||
from ..constant import BIND_COMMAND, QUERY_COMMAND
|
||||
from .constant import GAME_TYPE
|
||||
from .processor import Processor, User, identify_user_info
|
||||
@@ -52,6 +55,7 @@ alc = on_alconna(
|
||||
dest='query',
|
||||
help_text='查询 茶服 游戏信息',
|
||||
),
|
||||
Arg('other', Any, flags=[ArgFlag.HIDDEN, ArgFlag.OPTIONAL]),
|
||||
meta=CommandMeta(
|
||||
description='查询 TetrisOnline茶服 的信息',
|
||||
example='茶服查我',
|
||||
@@ -138,14 +142,4 @@ async def _(event: Event, matcher: Matcher, account: User):
|
||||
await matcher.finish(str(e))
|
||||
|
||||
|
||||
@alc.handle()
|
||||
async def _(matcher: Matcher, account: MessageFormatError):
|
||||
await matcher.finish(str(account))
|
||||
|
||||
|
||||
@alc.handle()
|
||||
async def _(matcher: Matcher, matches: AlcMatches):
|
||||
if matches.head_matched:
|
||||
await matcher.finish(
|
||||
f'{matches.error_info!r}\n' if matches.error_info is not None else '' + '输入"茶服 --help"查看帮助'
|
||||
)
|
||||
add_default_handlers(alc)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import sys
|
||||
from os import environ
|
||||
from platform import system
|
||||
from re import sub
|
||||
|
||||
from nonebot import get_driver
|
||||
from nonebot.log import logger
|
||||
@@ -33,12 +34,12 @@ class BrowserManager:
|
||||
raise ImportError('加载失败, Windows 必须设置 FASTAPI_RELOAD=false 才能正常运行 playwright')
|
||||
logger.info('开始 安装/更新 playwright 浏览器')
|
||||
environ['PLAYWRIGHT_DOWNLOAD_HOST'] = 'https://npmmirror.com/mirrors/playwright/'
|
||||
if cls._handle_error(cls._call_playwright(['', 'install', 'firefox'])):
|
||||
if cls._call_playwright(['', 'install', 'firefox']):
|
||||
logger.success('安装/更新 playwright 浏览器成功')
|
||||
else:
|
||||
logger.warning('playwright 浏览器 安装/更新 失败, 尝试使用原始仓库下载')
|
||||
del environ['PLAYWRIGHT_DOWNLOAD_HOST']
|
||||
if cls._handle_error(cls._call_playwright(['', 'install', 'firefox'])):
|
||||
if cls._call_playwright(['', 'install', 'firefox']):
|
||||
logger.success('安装/更新 playwright 浏览器成功')
|
||||
else:
|
||||
logger.error('安装/更新 playwright 浏览器失败')
|
||||
@@ -52,26 +53,20 @@ class BrowserManager:
|
||||
logger.success('playwright 启动成功')
|
||||
|
||||
@classmethod
|
||||
def _call_playwright(cls, argv: list[str]) -> BaseException:
|
||||
def _call_playwright(cls, argv: list[str]) -> bool:
|
||||
"""等价于调用 playwright 的命令行程序"""
|
||||
argv_backup = sys.argv.copy()
|
||||
from re import sub
|
||||
|
||||
sys.argv[0] = sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.argv = argv
|
||||
try:
|
||||
main()
|
||||
except BaseException as e: # noqa: BLE001 不在这里处理 playwright 的异常
|
||||
return e
|
||||
except SystemExit as e:
|
||||
return e.code == 0
|
||||
except BaseException: # noqa: BLE001
|
||||
return False
|
||||
finally:
|
||||
sys.argv = argv_backup
|
||||
return SystemExit(0)
|
||||
|
||||
@classmethod
|
||||
def _handle_error(cls, error: BaseException) -> bool:
|
||||
if isinstance(error, SystemExit) and error.code == 0:
|
||||
return True
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def _start_browser(cls) -> Browser:
|
||||
|
||||
@@ -15,6 +15,7 @@ driver = get_driver()
|
||||
class Recorder:
|
||||
matchers: ClassVar[set[type[Matcher]]] = set()
|
||||
historical_data: ClassVar[dict[int, tuple[HistoricalData, bool]]] = {}
|
||||
error_event: ClassVar[set[int]] = set()
|
||||
|
||||
@classmethod
|
||||
def create_historical_data(cls, event_id: int, historical_data: HistoricalData) -> None:
|
||||
@@ -32,17 +33,27 @@ class Recorder:
|
||||
|
||||
@classmethod
|
||||
async def save_historical_data(cls, event_id: int) -> None:
|
||||
if event_id not in cls.historical_data:
|
||||
raise KeyError
|
||||
historical_data, completed = cls.historical_data.pop(event_id)
|
||||
historical_data, completed = cls.del_historical_data(event_id)
|
||||
if completed:
|
||||
async with get_session() as session:
|
||||
session.add(historical_data)
|
||||
await session.commit()
|
||||
|
||||
@classmethod
|
||||
def del_historical_data(cls, event_id: int) -> None:
|
||||
cls.historical_data.pop(event_id)
|
||||
def del_historical_data(cls, event_id: int) -> tuple[HistoricalData, bool]:
|
||||
return cls.historical_data.pop(event_id)
|
||||
|
||||
@classmethod
|
||||
def add_error_event(cls, event_id: int) -> None:
|
||||
cls.error_event.add(event_id)
|
||||
|
||||
@classmethod
|
||||
def del_error_event(cls, event_id: int) -> None:
|
||||
cls.error_event.remove(event_id)
|
||||
|
||||
@classmethod
|
||||
def is_error_event(cls, event_id: int) -> bool:
|
||||
return event_id in cls.error_event
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
@@ -73,7 +84,9 @@ def _(bot: Bot, event: Event, matcher: Matcher):
|
||||
@run_postprocessor
|
||||
async def _(event: Event, matcher: Matcher, exception: Exception | None):
|
||||
if isinstance(matcher, tuple(Recorder.matchers)):
|
||||
event_id = id(event)
|
||||
if exception is not None:
|
||||
Recorder.del_historical_data(id(event))
|
||||
Recorder.add_error_event(event_id)
|
||||
Recorder.del_historical_data(event_id)
|
||||
else:
|
||||
await Recorder.save_historical_data(id(event))
|
||||
await Recorder.save_historical_data(event_id)
|
||||
|
||||
@@ -7,11 +7,12 @@ from nonebot.log import logger
|
||||
from playwright.async_api import Response
|
||||
from ujson import JSONDecodeError, dumps, loads
|
||||
|
||||
from ..config.config import CACHE_PATH
|
||||
from ..config.config import CACHE_PATH, Config
|
||||
from .browser import BrowserManager
|
||||
from .exception import RequestError
|
||||
|
||||
driver = get_driver()
|
||||
config = Config.parse_obj(driver.config)
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
@@ -115,7 +116,7 @@ class Request:
|
||||
async def request(cls, url: str, *, is_json: bool = True) -> bytes:
|
||||
"""请求api"""
|
||||
try:
|
||||
async with AsyncClient(cookies=cls._cookies) as session:
|
||||
async with AsyncClient(cookies=cls._cookies, timeout=config.tetris_req_timeout) as session:
|
||||
response = await session.get(url, headers=cls._headers)
|
||||
if is_json:
|
||||
loads(response.content)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = 'nonebot-plugin-tetris-stats'
|
||||
version = '1.0.0.a3'
|
||||
version = '1.0.0.a6'
|
||||
description = '一款基于 NoneBot2 的用于查询 Tetris 相关游戏数据的插件'
|
||||
authors = ['scdhh <wallfjjd@gmail.com>']
|
||||
readme = 'README.md'
|
||||
|
||||
Reference in New Issue
Block a user