格式化代码,细节优化,修复了图片边框不完整的问题

This commit is contained in:
EillesWan 2024-08-22 22:50:18 +08:00
parent a6e607443c
commit 50ad57ec22
9 changed files with 145 additions and 85 deletions

View File

@ -10,7 +10,6 @@
_✨ 从哔哩哔哩会员购获取展览简易信息 ✨_ _✨ 从哔哩哔哩会员购获取展览简易信息 ✨_
<a href="./LICENSE"> <a href="./LICENSE">
<img src="https://img.shields.io/github/license/LiteyukiStudio/nonebot-plugin-acgnshow.svg" alt="license"> <img src="https://img.shields.io/github/license/LiteyukiStudio/nonebot-plugin-acgnshow.svg" alt="license">
</a> </a>
@ -21,7 +20,6 @@ _✨ 从哔哩哔哩会员购获取展览简易信息 ✨_
</div> </div>
## 📖 介绍 ## 📖 介绍
一个简单的 Nonebot2 插件,可以从哔哩哔哩会员购 API 获取展览(漫展等)的时间,地点,票价等信息。 一个简单的 Nonebot2 插件,可以从哔哩哔哩会员购 API 获取展览(漫展等)的时间,地点,票价等信息。
@ -44,21 +42,25 @@ _✨ 从哔哩哔哩会员购获取展览简易信息 ✨_
<summary>pip</summary> <summary>pip</summary>
pip install nonebot-plugin-acgnshow pip install nonebot-plugin-acgnshow
</details> </details>
<details> <details>
<summary>pdm</summary> <summary>pdm</summary>
pdm add nonebot-plugin-acgnshow pdm add nonebot-plugin-acgnshow
</details> </details>
<details> <details>
<summary>poetry</summary> <summary>poetry</summary>
poetry add nonebot-plugin-acgnshow poetry add nonebot-plugin-acgnshow
</details> </details>
<details> <details>
<summary>conda</summary> <summary>conda</summary>
conda install nonebot-plugin-acgnshow conda install nonebot-plugin-acgnshow
</details> </details>
打开 nonebot2 项目根目录下的 `pyproject.toml` 文件, 在 `[tool.nonebot]` 部分追加写入 打开 nonebot2 项目根目录下的 `pyproject.toml` 文件, 在 `[tool.nonebot]` 部分追加写入
@ -67,13 +69,14 @@ _✨ 从哔哩哔哩会员购获取展览简易信息 ✨_
</details> </details>
## 🎉 使用 ## 🎉 使用
发送`展览`指令可以获取使用说明 发送`展览`指令可以获取使用说明
## ⚙️ 配置 ## ⚙️ 配置
在 nonebot2 项目的`.env`文件中添加下表中的配置 在 nonebot2 项目的`.env`文件中添加下表中的配置
| 配置项 | 必填 | 默认值 | 说明 | | 配置项 | 必填 | 默认值 | 说明 |
|:-----:|:----:|:----:|:----:| | :---------------: | :--: | :----: | :----------------------------------------------------------: |
| ACGNSHOW_PAGESIZE | 否 | 8 | 单个图片的条目数最大为20条目数过大可能导致 Bot 无法发送 | | ACGNSHOW_PAGESIZE | 否 | 8 | 单个图片的条目数,最大为 20条目数过大可能导致 Bot 无法发送 |

View File

@ -1,9 +1,11 @@
from nonebot.plugin import PluginMetadata, inherit_supported_adapters, require from nonebot.plugin import PluginMetadata, inherit_supported_adapters, require
require("nonebot_plugin_htmlrender") require("nonebot_plugin_htmlrender")
require("nonebot_plugin_alconna") require("nonebot_plugin_alconna")
from .acgnshower import * from .acgnshower import *
from nonebot import get_driver from nonebot import get_driver
from .config import ConfigModel from .config import ConfigModel
usage = """命令格式: usage = """命令格式:
展览 <地区> [页码] 展览 <地区> [页码]
@ -27,8 +29,8 @@ __plugin_meta__ = PluginMetadata(
config=ConfigModel, config=ConfigModel,
homepage="https://github.com/LiteyukiStudio/nonebot-plugin-acgnshow", homepage="https://github.com/LiteyukiStudio/nonebot-plugin-acgnshow",
supported_adapters=inherit_supported_adapters("nonebot_plugin_alconna"), supported_adapters=inherit_supported_adapters("nonebot_plugin_alconna"),
#extra={"License":"MIT","Author":"Swankily"} snowykami的奇妙纠错 # extra={"License":"MIT","Author":"Swankily"} snowykami的奇妙纠错
extra={"License":"MIT","Author":"Asankilp"} extra={"License": "MIT", "Author": "Asankilp"},
) )
driver = get_driver() driver = get_driver()
@ -36,4 +38,3 @@ driver = get_driver()
@driver.on_startup @driver.on_startup
async def _(): async def _():
pass pass

View File

@ -9,7 +9,7 @@ from .util import *
CITY_API_ROOT = "https://show.bilibili.com/api/ticket/city/list?channel=3" CITY_API_ROOT = "https://show.bilibili.com/api/ticket/city/list?channel=3"
SHOWS_API_ROOT = "https://show.bilibili.com/api/ticket/project/listV2" SHOWS_API_ROOT = "https://show.bilibili.com/api/ticket/project/listV2"
HEADERS = { HEADERS = {
"user-agent": "Mozilla/5.0 (Linux; Android 14; 114514YAJU Build/UKQ1.114514.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.165 Mobile Safari/537.36 BiliApp/7810200 mobi_app/android isNotchWindow/0 NotchHeight=34 mallVersion/7810200 mVersion/242 disable_rcmd/0 7.81.0 os/android model/114514YAJU mobi_app/android build/7810200 channel/bilih5 innerVer/7810210 osVer/14 network/2" "user-agent": "Mozilla/5.0 (Linux; Android 14; 114514YAJU Build/UKQ1.114514.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.165 Mobile Safari/537.36 BiliApp/7810200 mobi_app/android isNotchWindow/0 NotchHeight=34 mallVersion/7810200 mVersion/242 disable_rcmd/0 7.81.0 os/android model/114514YAJU mobi_app/android build/7810200 channel/bilih5 innerVer/7810210 osVer/14 network/2"
} }
@ -31,13 +31,8 @@ async def get_regions_dict() -> Dict[str, int]:
city_data = await get_cities_data() city_data = await get_cities_data()
for i in city_data.data.list: for i in city_data.data.list:
for j in i.city_list: for j in i.city_list:
data.update({ data.update({j.name: j.id})
j.name: j.id data.update({"全国": -1, "海外": 900000})
})
data.update({
"全国": -1,
"海外": 900000
})
return data return data
@ -50,13 +45,13 @@ async def get_shows_data(region_id: int, page=1, pagesize=20):
pagesize: 一页最大条目数最大20 pagesize: 一页最大条目数最大20
""" """
param = { param = {
"version" : 133, "version": 133,
"area" : region_id, "area": region_id,
"page" : page, "page": page,
"pagesize": pagesize, "pagesize": pagesize,
"platform": "web", "platform": "web",
"p_type" : "展览", "p_type": "展览",
"style" : 1 "style": 1,
} }
async with ClientSession() as session: async with ClientSession() as session:
async with session.get(SHOWS_API_ROOT, headers=HEADERS, params=param) as resp: async with session.get(SHOWS_API_ROOT, headers=HEADERS, params=param) as resp:
@ -108,30 +103,31 @@ def process_shows_data_to_template(shows_data: dict):
district_name = i["district_name"] district_name = i["district_name"]
wish = i["wish"] wish = i["wish"]
cover = "https:" + i["cover"] cover = "https:" + i["cover"]
if district_name == None: district_name = "" if district_name == None:
district_name = ""
guests_list = i["guests"] guests_list = i["guests"]
guests = ""
if guests_list != None: if guests_list != None:
for n in guests_list: guests = "".join(n["name"] for n in guests_list)
guests += n["name"] + "," else:
guests = ""
item_dict = { item_dict = {
"name" : name, "name": name,
"location" : district_name + venue_name, "location": district_name + venue_name,
"sale_flag" : sale_flag, "sale_flag": sale_flag,
"id" : project_id, "id": project_id,
"price" : price_low, "price": price_low,
"start_time" : start_time, "start_time": start_time,
"end_time" : end_time, "end_time": end_time,
"wish" : wish, "wish": wish,
"image_url" : cover, "image_url": cover,
"guests" : guests, "guests": guests,
"page" : page, "page": page,
"total_pages": total_pages "total_pages": total_pages,
} }
showlist.append(item_dict) showlist.append(item_dict)
global_data_dict = { global_data_dict = {
"page" : page, "page": page,
"total_pages" : total_pages, "total_pages": total_pages,
"total_results": total_results "total_results": total_results,
} }
return [showlist, global_data_dict] return [showlist, global_data_dict]

View File

@ -20,36 +20,44 @@ showcmd = on_alconna(
showcmd.shortcut( showcmd.shortcut(
r"(?P<region>.+?)展览\s*(?P<page>\d+)?\s*(?P<date>.+)?", r"(?P<region>.+?)展览\s*(?P<page>\d+)?\s*(?P<date>.+)?",
{ {
"prefix" : True, "prefix": True,
"command": "展览", "command": "展览",
"args" : ["{region}", "{page}", "{date}"], "args": ["{region}", "{page}", "{date}"],
} },
) )
@showcmd.handle() @showcmd.handle()
async def find_show( async def find_show(
region: Optional[str] = None, page: Optional[int] = None, date: Optional[str] = None, region: Optional[str] = None,
page: Optional[int] = None,
date: Optional[str] = None,
): ):
if not region: await UniMessage(__plugin_meta__.usage).send(); return if not region:
if not page: page = 1 await UniMessage(__plugin_meta__.usage).send()
if not date: date = "" return
if not page:
page = 1
if not date:
date = ""
regions_dict = await get_regions_dict() regions_dict = await get_regions_dict()
regionid = regions_dict.get(region, None) regionid = regions_dict.get(region, None)
if regionid == None: await UniMessage("不支持此地区").send(); return if regionid == None:
await UniMessage("地区未寻得或输入格式错误").send()
return
# await showcmd.send("日期:"+ date) # await showcmd.send("日期:"+ date)
shows = await get_shows_data(regionid, page=page, pagesize=config.acgnshow_pagesize) shows = await get_shows_data(regionid, page=page, pagesize=config.acgnshow_pagesize)
# print(shows) # print(shows)
try: try:
shows_data = process_shows_data_to_template(shows) shows_data = process_shows_data_to_template(shows)
template = { template = {
"shows" : shows_data[0], "shows": shows_data[0],
"bgimage" : choose_random_bgimage(), "bgimage": choose_random_bgimage(),
"global_data": shows_data[1] "global_data": shows_data[1],
} }
pic = await template_to_pic(RES_PATH, TEMPLATE_NAME, template) pic = await template_to_pic(str(RES_PATH), TEMPLATE_NAME, template)
except Exception as e: except Exception as e:
await UniMessage("发生错误").send() await UniMessage("图片生成时产生未知错误").send()
traceback.print_exc() traceback.print_exc()
return return

View File

@ -1,11 +1,14 @@
from pathlib import Path from pathlib import Path
from pydantic import BaseModel from pydantic import BaseModel
from nonebot import get_plugin_config from nonebot import get_plugin_config
RES_PATH = Path(__file__).parent / "res" RES_PATH = Path(__file__).parent / "res"
TEMPLATE_NAME = "template.html" TEMPLATE_NAME = "template.html"
BGIMAGE_PATH = RES_PATH / "bgimage" BGIMAGE_PATH = RES_PATH / "bgimage"
class ConfigModel(BaseModel): class ConfigModel(BaseModel):
acgnshow_pagesize: int = 8 acgnshow_pagesize: int = 8
config: ConfigModel = get_plugin_config(ConfigModel)
config: ConfigModel = get_plugin_config(ConfigModel)

View File

@ -17,6 +17,7 @@ class City(BaseModel):
""" """
热门城市 热门城市
""" """
id: int id: int
type: int type: int
first_letter: str first_letter: str
@ -31,6 +32,7 @@ class CityDataList(BaseModel):
""" """
城市首字母 城市首字母
""" """
letter: str letter: str
city_list: List[City] city_list: List[City]
@ -39,6 +41,7 @@ class CityData(BaseModel):
""" """
城市数据 城市数据
""" """
hot: List[City] hot: List[City]
list: List[CityDataList] list: List[CityDataList]
located_id: int located_id: int
@ -48,6 +51,7 @@ class CityResp(BaseModel):
""" """
城市数据 城市数据
""" """
errno: int errno: int
errtag: int errtag: int
msg: str msg: str

View File

@ -1,17 +1,20 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<title>Show Information</title> <title>Show Information</title>
<style> <style>
@font-face { @font-face {
font-family: 'Source Han Sans'; font-family: "Source Han Sans";
src: url('SourceHanSans.otf') format('opentype'); src: url("SourceHanSans.otf") format("opentype");
} }
body { body {
font-family: 'Source Han Sans', sans-serif; font-family: "Source Han Sans", sans-serif;
background-image: url('{{ bgimage }}'); background-image: url("{{ bgimage }}");
} }
.background { .background {
position: fixed; position: fixed;
top: 0; top: 0;
@ -22,14 +25,17 @@
filter: blur(40%); filter: blur(40%);
z-index: -1; z-index: -1;
} }
.container { .container {
width: 98%; width: 98%;
margin: 1%; margin: 1%;
border: 1px solid #fff; border: 1px solid #fff;
padding: 10px; padding: 10px;
box-sizing: border-box; box-sizing: border-box;
background-color: rgba(255, 255, 255, 0.8); /* 半透明白色背景 */ background-color: rgba(255, 255, 255, 0.8);
/* 半透明白色背景 */
} }
.header { .header {
border: 1px solid black; border: 1px solid black;
padding: 5px; padding: 5px;
@ -38,6 +44,7 @@
font-size: 10px; font-size: 10px;
font-weight: bold; font-weight: bold;
} }
.content { .content {
display: flex; display: flex;
border: 1px solid black; border: 1px solid black;
@ -46,13 +53,15 @@
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
} }
.image { .image {
width: 148px; width: 149px;
height: 200px; height: 201px;
border: 1px solid black; border: 1px solid rgba(128, 64, 128, 0.5);
text-align: center; text-align: center;
box-sizing: border-box; box-sizing: border-box;
} }
.details { .details {
flex: 1; flex: 1;
padding-left: 10px; padding-left: 10px;
@ -62,26 +71,32 @@
position: relative; position: relative;
font-size: 10px; font-size: 10px;
} }
.details .title { .details .title {
font-size: 10px; font-size: 10px;
font-weight: bold; font-weight: bold;
margin-bottom: 5px; margin-bottom: 5px;
} }
.details .venue_name { .details .venue_name {
font-size: 10px; font-size: 10px;
font-weight: normal; font-weight: normal;
margin-bottom: 5px; margin-bottom: 5px;
} }
.details .guests { .details .guests {
font-size: 10px; font-size: 10px;
word-wrap: break-word; word-wrap: break-word;
white-space: pre-wrap; /* 自动换行 */ white-space: pre-wrap;
/* 自动换行 */
margin-bottom: 5px; margin-bottom: 5px;
} }
.details .placeholder { .details .placeholder {
font-size: 10px; font-size: 10px;
margin-bottom: 5px; margin-bottom: 5px;
} }
.details .sale_flag { .details .sale_flag {
color: red; color: red;
font-size: 10px; font-size: 10px;
@ -89,6 +104,7 @@
top: 5px; top: 5px;
right: 5px; right: 5px;
} }
.details .id { .details .id {
color: black; color: black;
font-size: 10px; font-size: 10px;
@ -96,34 +112,40 @@
top: 20px; top: 20px;
right: 5px; right: 5px;
} }
.details .price { .details .price {
color: #FB7299; color: #fb7299;
font-size: 10px; font-size: 10px;
font-weight: bold; font-weight: bold;
position: absolute; position: absolute;
bottom: 5px; bottom: 5px;
left: 5px; left: 5px;
} }
.details .wish { .details .wish {
color: gray; color: gray;
position: absolute; position: absolute;
bottom: 5px; bottom: 5px;
left: 60px; left: 60px;
} }
.details .start-time, .details .start-time,
.details .end-time { .details .end-time {
font-size: 10px; font-size: 10px;
} }
.details .start-time { .details .start-time {
position: absolute; position: absolute;
bottom: 20px; bottom: 20px;
right: 5px; right: 5px;
} }
.details .end-time { .details .end-time {
position: absolute; position: absolute;
bottom: 5px; bottom: 5px;
right: 5px; right: 5px;
} }
.footer { .footer {
border: 1px solid black; border: 1px solid black;
flex: 1; flex: 1;
@ -134,40 +156,53 @@
position: relative; position: relative;
font-size: 10px; font-size: 10px;
} }
.footer .designer { .footer .designer {
text-align: center; text-align: center;
font-weight: bold; font-weight: bold;
font-size: 10px;
padding: 5px; padding: 5px;
margin-bottom: 10px; margin-bottom: 10px;
margin-top: 10px; margin-top: 10px;
} }
.footer .pages { .footer .pages {
position: absolute; position: absolute;
bottom: 5px; bottom: 5px;
right: 5px; right: 5px;
font-size: 10px; font-size: 10px;
} }
.footer .total_results { .footer .total_results {
position: absolute; position: absolute;
bottom: 5px; bottom: 5px;
left: 5px; left: 5px;
font-size: 10px; font-size: 10px;
} }
.footer .project_name { .footer .project_name {
position: relative; position: relative;
font-size: 10px; font-size: 10px;
text-align: center; text-align: center;
color: rgb(50, 227, 227); color: rgb(50, 227, 227);
} }
.footer .notice_text {
position: relative;
font-size: 8px;
text-align: center;
color: slateblue;
}
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<div class="header">展览信息</div> <div class="header">展览信息</div>
{% for show in shows %} {% for show in shows %}
<div class="content"> <div class="content">
<div class="image"> <div class="image">
<img src="{{ show.image_url }}" alt="Image" width="148" height="200"> <img src="{{ show.image_url }}" alt="Image" width="147px" height="199px" />
</div> </div>
<div class="details"> <div class="details">
<div class="title">{{ show.name }}</div> <div class="title">{{ show.name }}</div>
@ -186,11 +221,17 @@
</div> </div>
{% endfor %} {% endfor %}
<div class="footer"> <div class="footer">
<div class="designer"> Designed by Asankilp? </div> <div class="designer">Designed by Asankilp?</div>
<div class="total_results">共 {{ global_data.total_results }} 个结果</div> <div class="total_results">
<div class="pages">{{ global_data.page }} / {{ global_data.total_pages }} 页</div> 共 {{ global_data.total_results }} 个结果
</div>
<div class="pages">
{{ global_data.page }} / {{ global_data.total_pages }} 页
</div>
<div class="project_name">nonebot-plugin-acgnshow</div> <div class="project_name">nonebot-plugin-acgnshow</div>
<div class="notice_text">本页信息仅供参考,具体内容请访问展览官方详情页,并自行检索实际信息</div>
</div> </div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,15 +1,21 @@
from .config import BGIMAGE_PATH import os
import random import random
import datetime import datetime
from .config import BGIMAGE_PATH
def choose_random_bgimage():
randomfile = random.choice(list(BGIMAGE_PATH.iterdir())) def choose_random_bgimage() -> str:
randomurl = randomfile.as_uri() """
从背景图片文件夹中随机选择一张图片返回图片的uri地址
"""
randomfile = random.choice(os.listdir(BGIMAGE_PATH))
randomurl = (BGIMAGE_PATH / randomfile).as_uri()
return randomurl return randomurl
def convert_timestamp(timestamp): def convert_timestamp(timestamp) -> str:
obj = datetime.datetime.fromtimestamp(timestamp) """
formatted_time = obj.strftime('%Y-%m-%d %H:%M:%S') 将时间戳转换为日期格式
return formatted_time """
return datetime.datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")

View File

@ -4,9 +4,7 @@ version = "0.1.5"
description = "Nonebot2插件从哔哩哔哩会员购获取简易展览数据" description = "Nonebot2插件从哔哩哔哩会员购获取简易展览数据"
readme = "README.md" readme = "README.md"
requires-python = "<4.0,>=3.9" requires-python = "<4.0,>=3.9"
authors = [ authors = [{ name = "Asankilp", email = "asankilp@outlook.com" }]
{ name = "Asankilp", email = "asankilp@outlook.com" },
]
dependencies = [ dependencies = [
"nonebot2[fastapi, websockets]>=2.2.0", "nonebot2[fastapi, websockets]>=2.2.0",
"nonebot-plugin-alconna>=0.48.0", "nonebot-plugin-alconna>=0.48.0",