mirror of
https://github.com/suyiiyii/nonebot-bison.git
synced 2026-05-09 18:27:56 +08:00
✨ 添加 Theme 功能
This commit is contained in:
@@ -1,14 +1,56 @@
|
||||
from typing import Any
|
||||
from pathlib import Path
|
||||
from functools import partial
|
||||
|
||||
from httpx import AsyncClient
|
||||
from nonebot.plugin import require
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from pydantic import Field, BaseModel
|
||||
|
||||
from ..post import Post
|
||||
from ..types import Target, RawPost, Category
|
||||
from .platform import NewMessage, StatusChange
|
||||
from ..utils.scheduler_config import SchedulerConfig
|
||||
from .platform import NewMessage, StatusChange, CategoryNotRecognize
|
||||
|
||||
|
||||
class ArkResponseBase(BaseModel):
|
||||
code: int
|
||||
msg: str
|
||||
|
||||
|
||||
class BulletinListItem(BaseModel):
|
||||
cid: str
|
||||
title: str
|
||||
category: int
|
||||
display_time: str = Field(alias="displayTime")
|
||||
updated_at: int = Field(alias="updatedAt")
|
||||
sticky: bool
|
||||
|
||||
|
||||
class BulletinList(BaseModel):
|
||||
list: list[BulletinListItem]
|
||||
|
||||
class Config:
|
||||
extra = "ignore"
|
||||
|
||||
|
||||
class BulletinData(BaseModel):
|
||||
cid: str
|
||||
display_type: int = Field(alias="displayType")
|
||||
title: str
|
||||
category: int
|
||||
header: str
|
||||
content: str
|
||||
jump_link: str = Field(alias="jumpLink")
|
||||
banner_image_url: str = Field(alias="bannerImageUrl")
|
||||
display_time: str = Field(alias="displayTime")
|
||||
updated_at: int = Field(alias="updatedAt")
|
||||
|
||||
|
||||
class ArkBulletinListResponse(ArkResponseBase):
|
||||
data: BulletinList
|
||||
|
||||
|
||||
class ArkBulletinResponse(ArkResponseBase):
|
||||
data: BulletinData
|
||||
|
||||
|
||||
class ArknightsSchedConf(SchedulerConfig):
|
||||
@@ -26,74 +68,52 @@ class Arknights(NewMessage):
|
||||
is_common = False
|
||||
scheduler = ArknightsSchedConf
|
||||
has_target = False
|
||||
default_theme = "arknights"
|
||||
|
||||
@classmethod
|
||||
async def get_target_name(cls, client: AsyncClient, target: Target) -> str | None:
|
||||
return "明日方舟游戏信息"
|
||||
|
||||
async def get_sub_list(self, _) -> list[RawPost]:
|
||||
async def get_sub_list(self, _) -> list[BulletinListItem]:
|
||||
raw_data = await self.client.get("https://ak-webview.hypergryph.com/api/game/bulletinList?target=IOS")
|
||||
return raw_data.json()["data"]["list"]
|
||||
return ArkBulletinListResponse.parse_obj(raw_data.json()).data.list
|
||||
|
||||
def get_id(self, post: RawPost) -> Any:
|
||||
return post["cid"]
|
||||
def get_id(self, post: BulletinListItem) -> Any:
|
||||
return post.cid
|
||||
|
||||
def get_date(self, _: RawPost) -> Any:
|
||||
return None
|
||||
def get_date(self, post: BulletinListItem) -> Any:
|
||||
return post.updated_at
|
||||
|
||||
def get_category(self, _) -> Category:
|
||||
return Category(1)
|
||||
|
||||
async def parse(self, raw_post: RawPost) -> Post:
|
||||
async def parse(self, raw_post: BulletinListItem) -> Post:
|
||||
raw_data = await self.client.get(
|
||||
f"https://ak-webview.hypergryph.com/api/game/bulletin/{self.get_id(post=raw_post)}"
|
||||
)
|
||||
raw_data = raw_data.json()["data"]
|
||||
data = ArkBulletinResponse.parse_obj(raw_data.json()).data
|
||||
|
||||
announce_title = raw_data.get("header") if raw_data.get("header") != "" else raw_data.get("title")
|
||||
text = ""
|
||||
def title_escape(text: str) -> str:
|
||||
return text.replace("\n", " - ")
|
||||
|
||||
pics = []
|
||||
if raw_data["bannerImageUrl"]:
|
||||
pics.append(raw_post["bannerImageUrl"])
|
||||
|
||||
elif raw_data["content"]:
|
||||
require("nonebot_plugin_htmlrender")
|
||||
from nonebot_plugin_htmlrender import template_to_pic
|
||||
|
||||
template_path = str(Path(__file__).parent.parent / "post/templates/ark_announce")
|
||||
pic_data = await template_to_pic(
|
||||
template_path=template_path,
|
||||
template_name="index.html",
|
||||
templates={
|
||||
"bannerImageUrl": raw_data["bannerImageUrl"],
|
||||
"announce_title": announce_title,
|
||||
"content": raw_data["content"],
|
||||
},
|
||||
pages={
|
||||
"viewport": {"width": 400, "height": 100},
|
||||
"base_url": f"file://{template_path}",
|
||||
},
|
||||
)
|
||||
# render = Render()
|
||||
# viewport = {"width": 320, "height": 6400, "deviceScaleFactor": 3}
|
||||
# pic_data = await render.render(
|
||||
# announce_url, viewport=viewport, target="div.main"
|
||||
# )
|
||||
if pic_data:
|
||||
pics.append(pic_data)
|
||||
else:
|
||||
text = "图片渲染失败"
|
||||
# gen title, content
|
||||
if data.header:
|
||||
# header是title的更详细版本
|
||||
# header会和content一起出现
|
||||
title = data.header
|
||||
else:
|
||||
raise CategoryNotRecognize("未找到可渲染部分")
|
||||
# 只有一张图片
|
||||
title = title_escape(data.title)
|
||||
|
||||
return Post(
|
||||
"arknights",
|
||||
text=text,
|
||||
url="",
|
||||
target_name="明日方舟游戏内公告",
|
||||
pics=pics,
|
||||
self,
|
||||
content=data.content,
|
||||
title=title,
|
||||
nickname="明日方舟游戏内公告",
|
||||
images=[data.banner_image_url] if data.banner_image_url else None,
|
||||
url=data.jump_link or None,
|
||||
timestamp=data.updated_at,
|
||||
compress=True,
|
||||
override_use_pic=False,
|
||||
)
|
||||
|
||||
|
||||
@@ -106,6 +126,7 @@ class AkVersion(StatusChange):
|
||||
is_common = False
|
||||
scheduler = ArknightsSchedConf
|
||||
has_target = False
|
||||
default_theme = "brief"
|
||||
|
||||
@classmethod
|
||||
async def get_target_name(cls, client: AsyncClient, target: Target) -> str | None:
|
||||
@@ -122,18 +143,15 @@ class AkVersion(StatusChange):
|
||||
|
||||
def compare_status(self, _, old_status, new_status):
|
||||
res = []
|
||||
ArkUpdatePost = partial(Post, self, "", nickname="明日方舟更新信息")
|
||||
if old_status.get("preAnnounceType") == 2 and new_status.get("preAnnounceType") == 0:
|
||||
res.append(
|
||||
Post("arknights", text="登录界面维护公告上线(大概是开始维护了)", target_name="明日方舟更新信息")
|
||||
)
|
||||
res.append(ArkUpdatePost(title="登录界面维护公告上线(大概是开始维护了)"))
|
||||
elif old_status.get("preAnnounceType") == 0 and new_status.get("preAnnounceType") == 2:
|
||||
res.append(
|
||||
Post("arknights", text="登录界面维护公告下线(大概是开服了,冲!)", target_name="明日方舟更新信息")
|
||||
)
|
||||
res.append(ArkUpdatePost(title="登录界面维护公告下线(大概是开服了,冲!)"))
|
||||
if old_status.get("clientVersion") != new_status.get("clientVersion"):
|
||||
res.append(Post("arknights", text="游戏本体更新(大更新)", target_name="明日方舟更新信息"))
|
||||
res.append(ArkUpdatePost(title="游戏本体更新(大更新)"))
|
||||
if old_status.get("resVersion") != new_status.get("resVersion"):
|
||||
res.append(Post("arknights", text="游戏资源更新(小更新)", target_name="明日方舟更新信息"))
|
||||
res.append(ArkUpdatePost(title="游戏资源更新(小更新)"))
|
||||
return res
|
||||
|
||||
def get_category(self, _):
|
||||
@@ -180,13 +198,12 @@ class MonsterSiren(NewMessage):
|
||||
imgs = [x["src"] for x in soup("img")]
|
||||
text = f'{raw_post["title"]}\n{soup.text.strip()}'
|
||||
return Post(
|
||||
"monster-siren",
|
||||
text=text,
|
||||
pics=imgs,
|
||||
self,
|
||||
text,
|
||||
images=imgs,
|
||||
url=url,
|
||||
target_name="塞壬唱片新闻",
|
||||
nickname="塞壬唱片新闻",
|
||||
compress=True,
|
||||
override_use_pic=False,
|
||||
)
|
||||
|
||||
|
||||
@@ -199,6 +216,7 @@ class TerraHistoricusComic(NewMessage):
|
||||
is_common = False
|
||||
scheduler = ArknightsSchedConf
|
||||
has_target = False
|
||||
default_theme = "brief"
|
||||
|
||||
@classmethod
|
||||
async def get_target_name(cls, client: AsyncClient, target: Target) -> str | None:
|
||||
@@ -220,11 +238,11 @@ class TerraHistoricusComic(NewMessage):
|
||||
async def parse(self, raw_post: RawPost) -> Post:
|
||||
url = f'https://terra-historicus.hypergryph.com/comic/{raw_post["comicCid"]}/episode/{raw_post["episodeCid"]}'
|
||||
return Post(
|
||||
"terra-historicus",
|
||||
text=f'{raw_post["title"]} - {raw_post["episodeShortTitle"]}',
|
||||
pics=[raw_post["coverUrl"]],
|
||||
self,
|
||||
raw_post["subtitle"],
|
||||
title=f'{raw_post["title"]} - {raw_post["episodeShortTitle"]}',
|
||||
images=[raw_post["coverUrl"]],
|
||||
url=url,
|
||||
target_name="泰拉记事社漫画",
|
||||
nickname="泰拉记事社漫画",
|
||||
compress=True,
|
||||
override_use_pic=False,
|
||||
)
|
||||
|
||||
@@ -193,7 +193,7 @@ class Bilibili(NewMessage):
|
||||
text += orig_text
|
||||
else:
|
||||
raise CategoryNotSupport(post_type)
|
||||
return Post("bilibili", text=text, url=url, pics=pic, target_name=target_name)
|
||||
return Post(self, text, url=url, images=pic, nickname=target_name)
|
||||
|
||||
|
||||
class Bilibililive(StatusChange):
|
||||
@@ -206,6 +206,7 @@ class Bilibililive(StatusChange):
|
||||
name = "Bilibili直播"
|
||||
has_target = True
|
||||
use_batch = True
|
||||
default_theme = "brief"
|
||||
|
||||
@unique
|
||||
class LiveStatus(Enum):
|
||||
@@ -334,11 +335,12 @@ class Bilibililive(StatusChange):
|
||||
title = f"[{self.categories[raw_post.category].rstrip('提醒')}] {raw_post.title}"
|
||||
target_name = f"{raw_post.uname} {raw_post.area_name}"
|
||||
return Post(
|
||||
self.name,
|
||||
text=title,
|
||||
self,
|
||||
"",
|
||||
title=title,
|
||||
url=url,
|
||||
pics=list(pic),
|
||||
target_name=target_name,
|
||||
images=list(pic),
|
||||
nickname=target_name,
|
||||
compress=True,
|
||||
)
|
||||
|
||||
@@ -353,6 +355,7 @@ class BilibiliBangumi(StatusChange):
|
||||
name = "Bilibili剧集"
|
||||
has_target = True
|
||||
parse_target_promot = "请输入剧集主页"
|
||||
default_theme = "brief"
|
||||
|
||||
_url = "https://api.bilibili.com/pgc/review/user"
|
||||
|
||||
@@ -384,7 +387,7 @@ class BilibiliBangumi(StatusChange):
|
||||
if res_dict["code"] == 0:
|
||||
return {
|
||||
"index": res_dict["result"]["media"]["new_ep"]["index"],
|
||||
"index_show": res_dict["result"]["media"]["new_ep"]["index"],
|
||||
"index_show": res_dict["result"]["media"]["new_ep"]["index_show"],
|
||||
"season_id": res_dict["result"]["media"]["season_id"],
|
||||
}
|
||||
else:
|
||||
@@ -412,13 +415,15 @@ class BilibiliBangumi(StatusChange):
|
||||
url = lastest_episode["link"]
|
||||
pic: list[str] = [lastest_episode["cover"]]
|
||||
target_name = detail_dict["result"]["season_title"]
|
||||
text = lastest_episode["share_copy"]
|
||||
content = raw_post["index_show"]
|
||||
title = lastest_episode["share_copy"]
|
||||
return Post(
|
||||
self.name,
|
||||
text=text,
|
||||
self,
|
||||
content,
|
||||
title=title,
|
||||
url=url,
|
||||
pics=list(pic),
|
||||
target_name=target_name,
|
||||
images=list(pic),
|
||||
nickname=target_name,
|
||||
compress=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ class FF14(NewMessage):
|
||||
return None
|
||||
|
||||
async def parse(self, raw_post: RawPost) -> Post:
|
||||
text = f'{raw_post["Title"]}\n{raw_post["Summary"]}'
|
||||
title = raw_post["Title"]
|
||||
text = raw_post["Summary"]
|
||||
url = raw_post["Author"]
|
||||
return Post("ff14", text=text, url=url, target_name="最终幻想XIV官方公告")
|
||||
return Post(self, text, title=title, url=url, nickname="最终幻想XIV官方公告")
|
||||
|
||||
@@ -155,11 +155,11 @@ class McbbsNews(NewMessage):
|
||||
pics = await self._news_render(post_url, f"#{post_id}")
|
||||
|
||||
return Post(
|
||||
self.name,
|
||||
text="{}\n│\n└由 {} 发表".format(post["title"], post["author"]),
|
||||
self,
|
||||
"{}\n│\n└由 {} 发表".format(post["title"], post["author"]),
|
||||
url=post_url,
|
||||
pics=list(pics),
|
||||
target_name=post["category"],
|
||||
images=list(pics),
|
||||
nickname=post["category"],
|
||||
)
|
||||
|
||||
async def _news_render(self, url: str, selector: str) -> list[bytes]:
|
||||
|
||||
@@ -68,7 +68,7 @@ class NcmArtist(NewMessage):
|
||||
target_name = raw_post["artist"]["name"]
|
||||
pics = [raw_post["picUrl"]]
|
||||
url = "https://music.163.com/#/album?id={}".format(raw_post["id"])
|
||||
return Post("ncm-artist", text=text, url=url, pics=pics, target_name=target_name)
|
||||
return Post(self, text, url=url, images=pics, nickname=target_name)
|
||||
|
||||
|
||||
class NcmRadio(NewMessage):
|
||||
@@ -126,4 +126,4 @@ class NcmRadio(NewMessage):
|
||||
target_name = raw_post["radio"]["name"]
|
||||
pics = [raw_post["coverUrl"]]
|
||||
url = "https://music.163.com/#/program/{}".format(raw_post["id"])
|
||||
return Post("ncm-radio", text=text, url=url, pics=pics, target_name=target_name)
|
||||
return Post(self, text, url=url, images=pics, nickname=target_name)
|
||||
|
||||
@@ -95,6 +95,8 @@ class Platform(metaclass=PlatformABCMeta, base=True):
|
||||
client: AsyncClient
|
||||
reverse_category: dict[str, Category]
|
||||
use_batch: bool = False
|
||||
# TODO: 限定可使用的theme名称
|
||||
default_theme: str = "basic"
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
|
||||
@@ -53,28 +53,29 @@ class Rss(NewMessage):
|
||||
entry["_target_name"] = feed.feed.title
|
||||
return feed.entries
|
||||
|
||||
def _text_process(self, title: str, desc: str) -> str:
|
||||
def _text_process(self, title: str, desc: str) -> tuple[str | None, str]:
|
||||
"""检查标题和描述是否相似,如果相似则标题为None, 否则返回标题和描述"""
|
||||
similarity = 1.0 if len(title) == 0 or len(desc) == 0 else text_similarity(title, desc)
|
||||
if similarity > 0.8:
|
||||
text = title if len(title) > len(desc) else desc
|
||||
else:
|
||||
text = title + "\n\n" + desc
|
||||
return text
|
||||
return None, title if len(title) > len(desc) else desc
|
||||
|
||||
return title, desc
|
||||
|
||||
async def parse(self, raw_post: RawPost) -> Post:
|
||||
title = raw_post.get("title", "")
|
||||
soup = bs(raw_post.description, "html.parser")
|
||||
desc = soup.text.strip()
|
||||
text = self._text_process(title, desc)
|
||||
title, desc = self._text_process(title, desc)
|
||||
pics = [x.attrs["src"] for x in soup("img")]
|
||||
if raw_post.get("media_content"):
|
||||
for media in raw_post["media_content"]:
|
||||
if media.get("medium") == "image" and media.get("url"):
|
||||
pics.append(media.get("url"))
|
||||
return Post(
|
||||
"rss",
|
||||
text=text,
|
||||
self,
|
||||
desc,
|
||||
title=title,
|
||||
url=raw_post.link,
|
||||
pics=pics,
|
||||
target_name=raw_post["_target_name"],
|
||||
images=pics,
|
||||
nickname=raw_post["_target_name"],
|
||||
)
|
||||
|
||||
@@ -155,9 +155,9 @@ class Weibo(NewMessage):
|
||||
detail_url = f"https://weibo.com/{info['user']['id']}/{info['bid']}"
|
||||
# return parsed_text, detail_url, pic_urls
|
||||
return Post(
|
||||
"weibo",
|
||||
text=parsed_text,
|
||||
self,
|
||||
parsed_text,
|
||||
url=detail_url,
|
||||
pics=pics,
|
||||
target_name=info["user"]["screen_name"],
|
||||
images=pics,
|
||||
nickname=info["user"]["screen_name"],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user