from typing import Any from functools import partial from httpx import AsyncClient from bs4 import BeautifulSoup as bs from pydantic import Field, BaseModel from nonebot.compat import type_validate_python from ..post import Post from ..types import Target, RawPost, Category from .platform import NewMessage, StatusChange from ..utils.scheduler_config import SchedulerConfig 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 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): name = "arknights" schedule_type = "interval" schedule_setting = {"seconds": 30} class Arknights(NewMessage): categories = {1: "游戏公告"} platform_name = "arknights" name = "明日方舟游戏信息" enable_tag = False enabled = True 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[BulletinListItem]: raw_data = await self.client.get("https://ak-webview.hypergryph.com/api/game/bulletinList?target=IOS") return type_validate_python(ArkBulletinListResponse, raw_data.json()).data.list def get_id(self, post: BulletinListItem) -> Any: return post.cid def get_date(self, post: BulletinListItem) -> Any: # 为什么不使用post.updated_at? # update_at的时间是上传鹰角服务器的时间,而不是公告发布的时间 # 也就是说鹰角可能会在中午就把晚上的公告上传到服务器,但晚上公告才会显示,但是update_at就是中午的时间不会改变 # 如果指定了get_date,那么get_date会被优先使用, 并在获取到的值超过2小时时忽略这条post,导致其不会被发送 return None def get_category(self, _) -> Category: return Category(1) 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)}" ) data = type_validate_python(ArkBulletinResponse, raw_data.json()).data def title_escape(text: str) -> str: return text.replace("\\n", " - ") # gen title, content if data.header: # header是title的更详细版本 # header会和content一起出现 title = data.header else: # 只有一张图片 title = title_escape(data.title) return Post( 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, ) class AkVersion(StatusChange): categories = {2: "更新信息"} platform_name = "arknights" name = "明日方舟游戏信息" enable_tag = False enabled = True is_common = False scheduler = ArknightsSchedConf has_target = False default_theme = "brief" @classmethod async def get_target_name(cls, client: AsyncClient, target: Target) -> str | None: return "明日方舟游戏信息" async def get_status(self, _): res_ver = await self.client.get("https://ak-conf.hypergryph.com/config/prod/official/IOS/version") res_preanounce = await self.client.get( "https://ak-conf.hypergryph.com/config/prod/announce_meta/IOS/preannouncement.meta.json" ) res = res_ver.json() res.update(res_preanounce.json()) return res 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(ArkUpdatePost(title="登录界面维护公告上线(大概是开始维护了)")) elif old_status.get("preAnnounceType") == 0 and new_status.get("preAnnounceType") == 2: res.append(ArkUpdatePost(title="登录界面维护公告下线(大概是开服了,冲!)")) if old_status.get("clientVersion") != new_status.get("clientVersion"): res.append(ArkUpdatePost(title="游戏本体更新(大更新)")) if old_status.get("resVersion") != new_status.get("resVersion"): res.append(ArkUpdatePost(title="游戏资源更新(小更新)")) return res def get_category(self, _): return Category(2) async def parse(self, raw_post): return raw_post class MonsterSiren(NewMessage): categories = {3: "塞壬唱片新闻"} platform_name = "arknights" name = "明日方舟游戏信息" enable_tag = False enabled = True is_common = False scheduler = ArknightsSchedConf has_target = False @classmethod async def get_target_name(cls, client: AsyncClient, target: Target) -> str | None: return "明日方舟游戏信息" async def get_sub_list(self, _) -> list[RawPost]: raw_data = await self.client.get("https://monster-siren.hypergryph.com/api/news") return raw_data.json()["data"]["list"] def get_id(self, post: RawPost) -> Any: return post["cid"] def get_date(self, _) -> None: return None def get_category(self, _) -> Category: return Category(3) async def parse(self, raw_post: RawPost) -> Post: url = f'https://monster-siren.hypergryph.com/info/{raw_post["cid"]}' res = await self.client.get(f'https://monster-siren.hypergryph.com/api/news/{raw_post["cid"]}') raw_data = res.json() content = raw_data["data"]["content"] content = content.replace("

", "

\n") soup = bs(content, "html.parser") imgs = [x["src"] for x in soup("img")] text = f'{raw_post["title"]}\n{soup.text.strip()}' return Post( self, text, images=imgs, url=url, nickname="塞壬唱片新闻", compress=True, ) class TerraHistoricusComic(NewMessage): categories = {4: "泰拉记事社漫画"} platform_name = "arknights" name = "明日方舟游戏信息" enable_tag = False enabled = True is_common = False scheduler = ArknightsSchedConf has_target = False default_theme = "brief" @classmethod async def get_target_name(cls, client: AsyncClient, target: Target) -> str | None: return "明日方舟游戏信息" async def get_sub_list(self, _) -> list[RawPost]: raw_data = await self.client.get("https://terra-historicus.hypergryph.com/api/recentUpdate") return raw_data.json()["data"] def get_id(self, post: RawPost) -> Any: return f'{post["comicCid"]}/{post["episodeCid"]}' def get_date(self, _) -> None: return None def get_category(self, _) -> Category: return Category(4) 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( self, raw_post["subtitle"], title=f'{raw_post["title"]} - {raw_post["episodeShortTitle"]}', images=[raw_post["coverUrl"]], url=url, nickname="泰拉记事社漫画", compress=True, )