✨初次提交
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"
|