From f8f3b1ef87731664b81389550b71a29054a7865e Mon Sep 17 00:00:00 2001 From: Les Freire <64878354+fllesser@users.noreply.github.com> Date: Thu, 9 Jan 2025 23:31:51 +0800 Subject: [PATCH] Merge Dev (#2) --- README.md | 78 +++++----------- nonebot_plugin_fortnite/__init__.py | 18 +++- nonebot_plugin_fortnite/config.py | 11 ++- nonebot_plugin_fortnite/matcher.py | 65 ++++++++++--- nonebot_plugin_fortnite/other.py | 19 +++- nonebot_plugin_fortnite/pve.py | 68 ++++++++++++++ nonebot_plugin_fortnite/shop.py | 56 +++++++++++ nonebot_plugin_fortnite/stats.py | 138 ++++++++++++++++++---------- pyproject.toml | 7 +- 9 files changed, 330 insertions(+), 130 deletions(-) create mode 100644 nonebot_plugin_fortnite/pve.py create mode 100644 nonebot_plugin_fortnite/shop.py diff --git a/README.md b/README.md index 6b9b9c6..4eb14da 100644 --- a/README.md +++ b/README.md @@ -4,65 +4,28 @@
-# nonebot-plugin-PPPPP +# nonebot-plugin-fortnite -_✨ NoneBot 插件简单描述 ✨_ +_✨ NoneBot Fortnite Plguin ✨_ - license + license - - pypi + + pypi python
-这是一个 nonebot2 插件项目的模板库, 你可以直接使用本模板创建你的 nonebot2 插件项目的仓库 -
-模板库使用方法 - -1. 点击 [![start-course](https://user-images.githubusercontent.com/1221423/235727646-4a590299-ffe5-480d-8cd5-8194ea184546.svg)](https://github.com/new?template_owner=fllesser&template_name=nonebot-plugin-template&owner=%40me&name=nonebot-plugin-&visibility=public) 创建仓库 -2. 在创建好的新仓库中, 在 "Add file" 菜单中选择 "Create new file", 在新文件名处输入`LICENSE`, 此时在右侧会出现一个 "Choose a license template" 按钮, 点击此按钮选择开源协议模板, 然后在最下方提交新文件到主分支 -3. 全局替换`owner`为仓库所有者ID; 全局替换`PPPPP`为插件名; 全局替换`ppppp`为包名; 修改 python 徽标中的版本为你插件的运行所需版本 -4. 修改 README 中的插件名和插件描述, 并在下方填充相应的内容 - -
- -> [!NOTE] -> 模板库中自带了一个发布工作流, 你可以使用此工作流自动发布你的插件到 pypi - -
-配置发布工作流 - -1. 前往 https://pypi.org/manage/account/#api-tokens 并创建一个新的 API 令牌。创建成功后不要关闭页面,不然你将无法再次查看此令牌。 -2. 在单独的浏览器选项卡或窗口中,打开 [Actions secrets and variables](./settings/secrets/actions) 页面。你也可以在 Settings - Secrets and variables - Actions 中找到此页面。 -3. 点击 New repository secret 按钮,创建一个名为 `PYPI_API_TOKEN` 的新令牌,并从第一步复制粘贴令牌。 - -
- -> [!IMPORTANT] -> 这个发布工作流需要 pyproject.toml 文件, 并且只支持 [PEP 621](https://peps.python.org/pep-0621/) 标准的 pyproject.toml 文件 - -
-触发发布工作流 -从本地推送任意 tag 即可触发。 - -创建 tag: - - git tag - -推送本地所有 tag: - - git push origin --tags - -
## 📖 介绍 -这里是插件的详细介绍部分 +堡垒之夜战绩/季卡/商城/vb图查询插件 + +自用插件,发来凑个数(万一玩nb的也有人玩堡垒的呢 ## 💿 安装 @@ -70,7 +33,7 @@ _✨ NoneBot 插件简单描述 ✨_ 使用 nb-cli 安装 在 nonebot2 项目的根目录下打开命令行, 输入以下指令即可安装 - nb plugin install nonebot-plugin-PPPPP + nb plugin install nonebot-plugin-fortnite --upgrade @@ -81,27 +44,27 @@ _✨ NoneBot 插件简单描述 ✨_
pip - pip install nonebot-plugin-PPPPP + pip install nonebot-plugin-fortnite
pdm - pdm add nonebot-plugin-PPPPP + pdm add nonebot-plugin-fortnite
poetry - poetry add nonebot-plugin-PPPPP + poetry add nonebot-plugin-fortnite
conda - conda install nonebot-plugin-PPPPP + conda install nonebot-plugin-fortnite
打开 nonebot2 项目根目录下的 `pyproject.toml` 文件, 在 `[tool.nonebot]` 部分追加写入 - plugins = ["nonebot_plugin_ppppp"] + plugins = ["nonebot_plugin_fortnite"] @@ -111,14 +74,15 @@ _✨ NoneBot 插件简单描述 ✨_ | 配置项 | 必填 | 默认值 | 说明 | |:-----:|:----:|:----:|:----:| -| 配置项1 | 是 | 无 | 配置说明 | -| 配置项2 | 否 | 无 | 配置说明 | +| fortnite_api_key | 是 | '' | api-key | ## 🎉 使用 ### 指令表 | 指令 | 权限 | 需要@ | 范围 | 说明 | |:-----:|:----:|:----:|:----:|:----:| -| 指令1 | 主人 | 否 | 私聊 | 指令说明 | -| 指令2 | 群员 | 是 | 群聊 | 指令说明 | -### 效果图 -如果有效果图的话 +| [生涯]战绩 | - | 否 | - | 顾名思义 | +| [生涯]季卡 | - | 否 | - | 顾名思义 | +| 商城 | - | 否 | - | 顾名思义 | +| vb图 | - | 否 | - | 顾名思义 | +| 更新商城 | 主人 | 否 | - | 顾名思义 | +| 更新vb图 | 主人 | 否 | - | 顾名思义 | diff --git a/nonebot_plugin_fortnite/__init__.py b/nonebot_plugin_fortnite/__init__.py index 6c84796..e6c3e91 100644 --- a/nonebot_plugin_fortnite/__init__.py +++ b/nonebot_plugin_fortnite/__init__.py @@ -6,20 +6,32 @@ from nonebot.log import logger from nonebot.plugin import PluginMetadata - require("nonebot_plugin_apscheduler") from nonebot_plugin_apscheduler import scheduler from .config import Config from .matcher import * +from .pve import screenshot_vb_img +from .shop import screenshot_shop_img __plugin_meta__ = PluginMetadata( name="堡垒之夜游戏插件", description="堡垒之夜战绩,季卡,商城,vb图查询", - usage="略", + usage="季卡/生涯季卡/战绩/生涯战绩/商城/vb图", type="application", config=Config, homepage="https://github.com/fllesser/nonebot-plugin-fortnite", - supported_adapters={ "~onebot.v11" } + supported_adapters=None ) + + +@scheduler.scheduled_job( + "cron", + id = 'fortnite', + hour = 8, + minute = 5, +) +async def _(): + await screenshot_shop_img() + await screenshot_vb_img() \ No newline at end of file diff --git a/nonebot_plugin_fortnite/config.py b/nonebot_plugin_fortnite/config.py index 6d377b5..f875005 100644 --- a/nonebot_plugin_fortnite/config.py +++ b/nonebot_plugin_fortnite/config.py @@ -1,10 +1,10 @@ from pydantic import BaseModel from typing import Optional -from nonebot import get_plugin_config +from nonebot import get_plugin_config, require -# from pathlib import Path -# require("nonebot_plugin_localstore") -# import nonebot_plugin_localstore as store +from pathlib import Path +require("nonebot_plugin_localstore") +import nonebot_plugin_localstore as store class Config(BaseModel): @@ -12,4 +12,5 @@ class Config(BaseModel): fconfig: Config = get_plugin_config(Config) -# cache_dir: Path = store.get_plugin_cache_dir() \ No newline at end of file +cache_dir: Path = store.get_plugin_cache_dir() +data_dir: Path = store.get_plugin_data_dir() \ No newline at end of file diff --git a/nonebot_plugin_fortnite/matcher.py b/nonebot_plugin_fortnite/matcher.py index 32f9a38..ab7408c 100644 --- a/nonebot_plugin_fortnite/matcher.py +++ b/nonebot_plugin_fortnite/matcher.py @@ -1,7 +1,9 @@ import re +from pathlib import Path from nonebot import require from nonebot.plugin.on import on_command +from nonebot.permission import SUPERUSER require("nonebot_plugin_uninfo") from nonebot_plugin_uninfo import ( @@ -23,13 +25,16 @@ ) from nonebot_plugin_alconna.uniseg import ( UniMessage, - Image + Image, + Text ) from .stats import ( get_level, get_stats_image ) +from .pve import screenshot_vb_img, vb_file +from .shop import screenshot_shop_img, shop_file timewindow_prefix = ["生涯", ""] name_args = Args["name?", str] @@ -65,25 +70,61 @@ async def _( matcher.set_path_arg('name', match.group(1)) -name_prompt = UniMessage.template("{:At(user, $event.get_user_id())} 请发送游戏名称(群昵称设置为id:name/ID name可快速查询)") +name_prompt = UniMessage.template("{:At(user, $event.get_user_id())} 请发送游戏名称\n群昵称设置如下可快速查询:\n id:name\n ID name") @battle_pass.got_path('name', prompt=name_prompt) async def _(arp: Arparma, name: str): - level_info = await get_level(name, arp.header_match.result) - await battle_pass.finish(level_info) + header = arp.header_match.result + receipt = await UniMessage.text(f'正在查询 {name} 的{header},请稍后...').send() + level_info = await get_level(name, header) + await battle_pass.send(level_info) + await receipt.recall(delay=1) @stats.got_path('name', prompt=name_prompt) async def _(arp: Arparma, name: str): - stats_img = await get_stats_image(name, arp.header_match.result) - if stats_img.startswith('http'): - res = await UniMessage(Image(url=stats_img)).export() - else: - res = stats_img - await stats.finish(res) - + header = arp.header_match.result + receipt = await UniMessage.text(f'正在查询 {name} 的{header},请稍后...').send() + res = await get_stats_image(name, header) + if isinstance(res, Path): + res = await UniMessage(Image(path=res)).export() + await stats.send(res) + await receipt.recall(delay=1) shop = on_command('商城') @shop.handle() async def _(): - await shop.finish('https://www.fortnite.com/item-shop?lang=zh-Hans') \ No newline at end of file + await shop.send(await UniMessage(Image(path=shop_file) + Text('https://www.fortnite.com/item-shop?lang=zh-Hans')).export()) + # await shop.finish('https://www.fortnite.com/item-shop?lang=zh-Hans' + "\n\n" + 'https://fortnite.gg/shop') + +update_shop = on_command('更新商城', permission=SUPERUSER) + +@update_shop.handle() +async def _(): + try: + receipt = await UniMessage.text("正在更新商城,请稍后...").send() + file = await screenshot_shop_img() + await update_vb.send(await UniMessage(Text('手动更新商城成功') + Image(path=file)).export()) + except Exception as e: + await update_vb.send(f'手动更新商城失败 | {e}') + finally: + await receipt.recall(delay=1) + +vb = on_command('vb图', aliases={"VB图"}) + +@vb.handle() +async def _(): + await vb.finish(await UniMessage(Image(path=vb_file)).export()) + +update_vb = on_command('更新vb图', permission=SUPERUSER) + +@update_vb.handle() +async def _(): + try: + receipt = await UniMessage.text("正在更新vb图,请稍后...").send() + file = await screenshot_vb_img() + await update_vb.send(await UniMessage(Text('手动更新vb图成功') + Image(path=file)).export()) + except Exception as e: + await update_vb.send(f'手动更新vb图失败 | {e}') + finally: + await receipt.recall(delay=1) \ No newline at end of file diff --git a/nonebot_plugin_fortnite/other.py b/nonebot_plugin_fortnite/other.py index aecd2a1..fbaee8f 100644 --- a/nonebot_plugin_fortnite/other.py +++ b/nonebot_plugin_fortnite/other.py @@ -3,14 +3,25 @@ def exception_handler(): def decorator(func): @wraps(func) - def wrapper(*args, **kwargs): + async def wrapper(*args, **kwargs): res = None try: - res = func(*args, **kwargs) + res = await func(*args, **kwargs) except Exception as e: - res = e.message + e = str(e) + if "public" in e: + res = "战绩未公开" + elif "exist" in e: + res = "用户不存在" + elif "match" in e: + res = "该玩家当前赛季没有进行过任何对局" + elif "timed out" in e: + res = "请求超时, 请稍后再试" + elif "failed to fetch" in e: + res = "拉取账户信息失败, 稍后再试" + else: + res = f"未知错误: {e}" finally: return res return wrapper return decorator - diff --git a/nonebot_plugin_fortnite/pve.py b/nonebot_plugin_fortnite/pve.py new file mode 100644 index 0000000..5917fcd --- /dev/null +++ b/nonebot_plugin_fortnite/pve.py @@ -0,0 +1,68 @@ +import asyncio + +from PIL import Image +from pathlib import Path +from playwright.async_api import async_playwright + +from .config import data_dir + +vb_file = data_dir / "vb.png" + +async def screenshot_vb_img() -> Path: + url = "https://freethevbucks.com/timed-missions" + + try: + browser = None + async with async_playwright() as p: + browser = await p.chromium.launch(headless=True) + page = await browser.new_page() + await page.goto(url) + # 截取第一个
+ hot_info_1 = page.locator('div.hot-info').nth(0) + await hot_info_1.screenshot(path=data_dir / 'hot_info_1.png') + + # 截取