✨初次提交
22
nonebot_plugin_acgnshow/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
from nonebot.plugin import PluginMetadata, inherit_supported_adapters, require
|
||||
require("nonebot_plugin_htmlrender")
|
||||
require("nonebot_plugin_alconna")
|
||||
from .acgnshower import *
|
||||
from nonebot import get_driver
|
||||
from .config import ConfigModel
|
||||
__author__ = "Asankilp"
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="漫展/展览查询",
|
||||
description="从哔哩哔哩会员购获取简易展览数据",
|
||||
usage="application",
|
||||
config=ConfigModel,
|
||||
homepage="https://github.com/LiteyukiStudio/nonebot-plugin-acgnshow",
|
||||
supported_adapters=inherit_supported_adapters("nonebot_plugin_alconna")
|
||||
)
|
||||
driver = get_driver()
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
async def _():
|
||||
pass
|
||||
|
99
nonebot_plugin_acgnshow/acgnapis.py
Normal file
@ -0,0 +1,99 @@
|
||||
import json
|
||||
import requests
|
||||
from nonebot_plugin_htmlrender import template_to_pic
|
||||
from jinja2 import Template
|
||||
CITY_API_ROOT="https://show.bilibili.com/api/ticket/city/list?channel=3"
|
||||
SHOWS_API_ROOT="https://show.bilibili.com/api/ticket/project/listV2"
|
||||
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"
|
||||
}
|
||||
def get_regions_data():
|
||||
'''
|
||||
返回支持的地区数据
|
||||
'''
|
||||
regions_data = json.loads(requests.get(CITY_API_ROOT, headers=HEADERS).text)
|
||||
return regions_data
|
||||
def get_regions_dict():
|
||||
'''
|
||||
返回支持的地区,键名为地区名,键值为地区id
|
||||
'''
|
||||
dicts = {}
|
||||
cityjson = get_regions_data()
|
||||
for i in cityjson["data"]["list"]:
|
||||
for j in i["city_list"]:
|
||||
name = j["name"]
|
||||
id = j["id"]
|
||||
dicts.update({name: id})
|
||||
dicts.update({"全国": -1,"海外": 900000}) #添加api中未返回的结果
|
||||
#print(dicts)
|
||||
return dicts
|
||||
def get_shows_data(region_id: int, page=1, pagesize=20):
|
||||
'''
|
||||
返回举办中/即将举办/取消举办的展览数据
|
||||
Args:
|
||||
region_id: 地区id
|
||||
page: 页码
|
||||
pagesize: 一页最大条目数,最大20
|
||||
'''
|
||||
param = {
|
||||
"version": 133,
|
||||
"area": region_id,
|
||||
"page": page,
|
||||
"pagesize": pagesize,
|
||||
"platform": "web",
|
||||
"p_type": "展览",
|
||||
"style": 1
|
||||
}
|
||||
shows_data = json.loads(requests.get(SHOWS_API_ROOT, headers=HEADERS,params=param).text)
|
||||
return shows_data
|
||||
def process_shows_data_to_text(shows_data: dict):
|
||||
showlist = []
|
||||
for i in shows_data["data"]["result"]:
|
||||
name = i["project_name"]
|
||||
venue_name = i["venue_name"]
|
||||
project_id = i["project_id"]
|
||||
sale_flag = i["sale_flag"]
|
||||
start_time = i["start_time"]
|
||||
end_time = i["end_time"]
|
||||
price_low = i["price_low"] / 100
|
||||
price_high = i["price_high"] / 100
|
||||
district_name = i["district_name"]
|
||||
text = f"名称:{name}\n举办地:{venue_name}\nid:{project_id}\nflag:{sale_flag}\n开始时间:{start_time}\n结束时间:{end_time}\n最低票价:{price_low}\n最高票价:{price_high}\n区名:{district_name}\n\n"
|
||||
showlist.append(text)
|
||||
return showlist
|
||||
def read_template_file(file_path):
|
||||
with open(file_path, 'r', encoding='utf-8') as file:
|
||||
return file.read()
|
||||
|
||||
def process_shows_data_to_template(shows_data: dict):
|
||||
showlist = []
|
||||
# show_template = read_template_file('/home/asankilp/LiteyukiBot/src/plugins/acgnshow/res/template.html')
|
||||
for i in shows_data["data"]["result"]:
|
||||
name = i["project_name"]
|
||||
venue_name = i["venue_name"]
|
||||
project_id = i["project_id"]
|
||||
sale_flag = i["sale_flag"]
|
||||
start_time = i["start_time"]
|
||||
end_time = i["end_time"]
|
||||
price_low = i["price_low"] / 100
|
||||
price_high = i["price_high"] / 100
|
||||
district_name = i["district_name"]
|
||||
wish = i["wish"]
|
||||
cover = "https:" + i["cover"]
|
||||
if district_name == None : district_name = ""
|
||||
dicts = {}
|
||||
dicts.update({
|
||||
"name": name,
|
||||
"location": district_name + venue_name,
|
||||
"sale_flag": sale_flag,
|
||||
"price": price_low,
|
||||
"start_time": start_time,
|
||||
"end_time": end_time,
|
||||
"wish": wish,
|
||||
"image_url": cover
|
||||
})
|
||||
showlist.append(dicts)
|
||||
return showlist
|
||||
template = Template(show_template)
|
||||
rendered = template.render(exhibitions=showlist)
|
||||
return rendered
|
49
nonebot_plugin_acgnshow/acgnshower.py
Normal file
@ -0,0 +1,49 @@
|
||||
from nonebot import require, on_endswith
|
||||
from nonebot.adapters import satori
|
||||
from nonebot.adapters.onebot.v11 import MessageSegment
|
||||
from nonebot.typing import T_State
|
||||
from typing import Optional
|
||||
from .acgnapis import *
|
||||
from nonebot_plugin_htmlrender import text_to_pic, template_to_pic
|
||||
from nonebot_plugin_alconna import on_alconna
|
||||
from arclet.alconna import Alconna, Args
|
||||
from .config import RES_PATH, TEMPLATE_NAME, config
|
||||
from .util import *
|
||||
|
||||
showcmd = on_alconna(
|
||||
Alconna(
|
||||
"展览",
|
||||
Args["region?", str]["page?", int]["date?", str],
|
||||
)
|
||||
)
|
||||
showcmd.shortcut(
|
||||
r"(?P<region>.+?)展览\s*(?P<page>\d+)?\s*(?P<date>.+)?",
|
||||
{
|
||||
"prefix": True,
|
||||
"command": "展览",
|
||||
"args": ["{region}", "{page}", "{date}"],
|
||||
}
|
||||
)
|
||||
|
||||
@showcmd.handle()
|
||||
async def find_show(
|
||||
state: T_State, region: Optional[str] = None, page: Optional[int] = None, date: Optional[str] = None,
|
||||
):
|
||||
if not region: await showcmd.finish("未指定地区")
|
||||
if not page: page = 1
|
||||
if not date: date = ""
|
||||
regions_dict = get_regions_dict()
|
||||
regionid = regions_dict.get(region,None)
|
||||
if regionid == None: await showcmd.finish("未找到此地区") ; return
|
||||
#await showcmd.send("日期:"+ date)
|
||||
shows = get_shows_data(regionid,page=page,pagesize=config.pagesize)
|
||||
# print(shows)
|
||||
template = {
|
||||
"shows":process_shows_data_to_template(shows),
|
||||
"bgimage": choose_random_bgimage()
|
||||
}
|
||||
pic = await template_to_pic(RES_PATH,TEMPLATE_NAME,template)
|
||||
# print(pic)
|
||||
# a = Image.open(io.BytesIO(pic))
|
||||
# a.save("template2pic.png", format="PNG")
|
||||
await showcmd.finish(MessageSegment.image(pic))
|
11
nonebot_plugin_acgnshow/config.py
Normal file
@ -0,0 +1,11 @@
|
||||
from pathlib import Path
|
||||
from pydantic import BaseModel
|
||||
from nonebot import get_plugin_config
|
||||
RES_PATH = Path(__file__).parent / "res"
|
||||
TEMPLATE_NAME = "template.html"
|
||||
BGIMAGE_PATH = RES_PATH / "bgimage"
|
||||
|
||||
class ConfigModel(BaseModel):
|
||||
pagesize: int = 8
|
||||
|
||||
config: ConfigModel = get_plugin_config(ConfigModel)
|
BIN
nonebot_plugin_acgnshow/res/SourceHanSans.otf
Normal file
BIN
nonebot_plugin_acgnshow/res/bgimage/bg1.webp
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
nonebot_plugin_acgnshow/res/bgimage/bg2.webp
Normal file
After Width: | Height: | Size: 397 KiB |
BIN
nonebot_plugin_acgnshow/res/bgimage/bg3.webp
Normal file
After Width: | Height: | Size: 433 KiB |
BIN
nonebot_plugin_acgnshow/res/bgimage/bg4.webp
Normal file
After Width: | Height: | Size: 412 KiB |
BIN
nonebot_plugin_acgnshow/res/bgimage/bg5.webp
Normal file
After Width: | Height: | Size: 314 KiB |
BIN
nonebot_plugin_acgnshow/res/bgimage/bg6.webp
Normal file
After Width: | Height: | Size: 410 KiB |
BIN
nonebot_plugin_acgnshow/res/bgimage/bg7.webp
Normal file
After Width: | Height: | Size: 270 KiB |
141
nonebot_plugin_acgnshow/res/template.html
Normal file
@ -0,0 +1,141 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Show Information</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Source Han Sans';
|
||||
src: url('SourceHanSans.otf') format('opentype');
|
||||
}
|
||||
body {
|
||||
font-family: 'Source Han Sans', sans-serif;
|
||||
background-image: url('{{ bgimage }}');
|
||||
|
||||
}
|
||||
.background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/*background-image: url('{{ bgimage }}');*/
|
||||
background-size: cover;
|
||||
filter: blur(40%);
|
||||
z-index: -1;
|
||||
}
|
||||
.container {
|
||||
width: 98%;
|
||||
margin: 1%;
|
||||
border: 1px solid #fff;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(255, 255, 255, 0.8); /* 半透明白色背景 */
|
||||
}
|
||||
.header {
|
||||
border: 1px solid black;
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
border: 1px solid black;
|
||||
margin-bottom: 10px;
|
||||
padding: 5px;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.image {
|
||||
width: 148px;
|
||||
height: 200px;
|
||||
border: 1px solid black;
|
||||
text-align: center;
|
||||
/*padding: 10px; */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.details {
|
||||
flex: 1;
|
||||
padding-left: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
font-size: 10px;
|
||||
}
|
||||
.details .title {
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.details .venue_name {
|
||||
font-size: 10px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.details .sale_flag {
|
||||
color: red;
|
||||
font-size: 10px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
.details .price {
|
||||
color: #FB7299;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 5px;
|
||||
}
|
||||
.details .wish {
|
||||
color: gray;
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 60px;
|
||||
}
|
||||
.details .start-time,
|
||||
.details .end-time {
|
||||
font-size: 10px;
|
||||
}
|
||||
.details .start-time {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
right: 5px;
|
||||
}
|
||||
.details .end-time {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">展览信息</div>
|
||||
{% for show in shows %}
|
||||
<div class="content">
|
||||
<div class="image">
|
||||
<img src="{{ show.image_url }}" alt="Image" width="148" height="200">
|
||||
</div>
|
||||
<div class="details">
|
||||
<div class="title">
|
||||
<div>{{ show.name }}</div>
|
||||
<div class="venue_name">
|
||||
<div>举办地点:{{ show.location }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sale_flag">{{ show.sale_flag }}</div>
|
||||
<div class="price">¥{{ show.price }}起</div>
|
||||
<div class="wish">{{ show.wish }}人想去</div>
|
||||
<div class="start-time">开始时间:{{ show.start_time }}</div>
|
||||
<div class="end-time">结束时间:{{ show.end_time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="header">
|
||||
Designed by Asankilp?
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
3
nonebot_plugin_acgnshow/typing.py
Normal file
@ -0,0 +1,3 @@
|
||||
from nonebot.adapters.onebot import v11, v12
|
||||
from nonebot.adapters import satori
|
||||
T_MessageEvent = v11.MessageEvent | v12.MessageEvent | satori.MessageEvent
|
6
nonebot_plugin_acgnshow/util.py
Normal file
@ -0,0 +1,6 @@
|
||||
from .config import BGIMAGE_PATH
|
||||
import random
|
||||
from pathlib import Path
|
||||
def choose_random_bgimage():
|
||||
randomfile = random.choice(list(BGIMAGE_PATH.iterdir()))
|
||||
return str(randomfile)
|
43
pyproject.toml
Normal file
@ -0,0 +1,43 @@
|
||||
[project]
|
||||
name = "nonebot-plugin-acgnshow"
|
||||
version = "0.1.0"
|
||||
description = "Nonebot2插件,从哔哩哔哩会员购获取简易展览数据"
|
||||
readme = "README.md"
|
||||
requires-python = "<4.0,>=3.8"
|
||||
authors = [
|
||||
{name = "Asankilp", email = "asankilp@outlook.com"},
|
||||
]
|
||||
dependencies = [
|
||||
"nonebot2>=2.2.0",
|
||||
"nonebot-plugin-alconna>=0.48.0",
|
||||
"nonebot-plugin-htmlrender>=0.3.2",
|
||||
"jinja2>=3.1.4",
|
||||
|
||||
|
||||
]
|
||||
license = {text = "MIT"}
|
||||
|
||||
[tool.nonebot]
|
||||
plugins = ["nonebot_plugin_acgnshow"]
|
||||
adapters = [
|
||||
{ name = "OneBot V11", module_name = "nonebot.adapters.onebot.v11" }
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
nonebot2 = { version = "^2.2.0", extras = ["fastapi"] }
|
||||
nonebot-plugin-alconna = ">=0.38.0,<1.0.0"
|
||||
|
||||
|
||||
[tool.pdm]
|
||||
distribution = true
|
||||
|
||||
[tool.pdm.version]
|
||||
source = "file"
|
||||
path = "nonebot_plugin_acgnshow/__init__.py"
|
||||
|
||||
[tool.pdm.build]
|
||||
includes = []
|
||||
[build-system]
|
||||
requires = ["pdm-backend"]
|
||||
build-backend = "pdm.backend"
|