From 9e5dcb39126f2e69f6c536c7d29c17049c696b53 Mon Sep 17 00:00:00 2001 From: Cateon Huo <44727214+GuGuMur@users.noreply.github.com> Date: Sun, 4 Aug 2024 18:39:12 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E5=AE=9E=E7=8E=B0=20`Post.content?= =?UTF-8?q?`=20=E7=9B=B8=E5=85=B3=E6=89=A9=E5=B1=95=E5=8D=8F=E8=AE=AE=20(#?= =?UTF-8?q?553)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :sparkles: `post` 新增 `get_content()` 方法 * :rotating_light: make linter happy * :lipstick: auto fix by pre-commit hooks * :bug: fix: 调整函数使用 * :lipstick: auto fix by pre-commit hooks * :sparkles: 转用函数处理文本 * :lipstick: auto fix by pre-commit hooks * :hammer: 使用`Dict`存储`content_handlers` * :lipstick: auto fix by pre-commit hooks * :art: fix * :arts: 简化函数使用 * :bug: 移除`Theme`的过时参数 * :wastebasket: 复用 `self.plain_content` * :lipstick: auto fix by pre-commit hooks * :sparkles: 注册式装饰器写法 * :lipstick: auto fix by pre-commit hooks * :bug: fix * :lipstick: auto fix by pre-commit hooks * :feat: 通用纯文本处理函数 * :lipstick: auto fix by pre-commit hooks * :downgrade: 复用`==`处理标题 * :art: 简化(?)写法 * :white_check_mark: 测试修复 * :recycle: via ContentSupport extensions * :bug: fix test * :lipstick: auto fix by pre-commit hooks * :bug: for clean text * :bug: fix * :lipstick: auto fix by pre-commit hooks * fix: for xml * :lipstick: auto fix by pre-commit hooks * chore: art * :lipstick: auto fix by pre-commit hooks --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- nonebot_bison/platform/arknights.py | 42 +++- nonebot_bison/platform/bilibili/platforms.py | 4 +- nonebot_bison/platform/ff14.py | 2 +- nonebot_bison/platform/ncm.py | 4 +- nonebot_bison/platform/rss.py | 23 ++- nonebot_bison/platform/weibo.py | 8 +- nonebot_bison/post/post.py | 20 +- nonebot_bison/post/protocol.py | 16 ++ nonebot_bison/theme/themes/arknights/build.py | 7 +- nonebot_bison/theme/themes/basic/build.py | 7 +- .../theme/themes/ceobe_canteen/build.py | 6 +- nonebot_bison/theme/themes/ht2i/build.py | 17 +- nonebot_bison/theme/types.py | 1 - .../static/arknights-plaintext-805.txt | 18 ++ .../static/arknights-plaintext-807.txt | 186 ++++++++++++++++++ tests/platforms/test_arknights.py | 16 +- tests/post/test_generate.py | 26 ++- tests/theme/test_themes.py | 6 +- 18 files changed, 363 insertions(+), 46 deletions(-) create mode 100644 nonebot_bison/post/protocol.py create mode 100644 tests/platforms/static/arknights-plaintext-805.txt create mode 100644 tests/platforms/static/arknights-plaintext-807.txt diff --git a/nonebot_bison/platform/arknights.py b/nonebot_bison/platform/arknights.py index fb9732f..f22fd28 100644 --- a/nonebot_bison/platform/arknights.py +++ b/nonebot_bison/platform/arknights.py @@ -1,3 +1,5 @@ +import re +import html from typing import Any from functools import partial @@ -10,6 +12,7 @@ from nonebot.compat import type_validate_python from ..post import Post from ..utils import Site from ..types import Target, RawPost, Category +from ..post.protocol import HTMLContentSupport from .platform import NewMessage, StatusChange @@ -58,6 +61,39 @@ class ArknightsSite(Site): schedule_setting = {"seconds": 30} +class ArknightsPost(Post, HTMLContentSupport): + def _cleantext(self, text: str, old_split="\n", new_split="\n") -> str: + """清理文本:去掉所有多余的空格和换行""" + lines = text.strip().split(old_split) + cleaned_lines = [line.strip() for line in lines if line != ""] + return new_split.join(cleaned_lines) + + async def get_html_content(self) -> str: + return self.content + + async def get_plain_content(self) -> str: + content = html.unescape(self.content) # 转义HTML特殊字符 + content = re.sub( + r'\

(.*?)\(.*?)\(.*?)\<\/span\>(.*?)\<\/strong\>(.*?)<\/p\>', # noqa: E501 + r"==\4==\n", + content, + flags=re.DOTALL, + ) # 去“标题型”p + content = re.sub( + r'\

(.*?)\<\/p\>', + r"\2\n", + content, + flags=re.DOTALL, + ) # 去左右对齐的p + content = re.sub(r"\(.*?)\", r"\1\n", content, flags=re.DOTALL) # 去普通p + content = re.sub(r'\(.*?)\<\/a\>', r"\1", content, flags=re.DOTALL) # 去a + content = re.sub(r"
", "\n", content) # 去br + content = re.sub(r"\(.*?)\", r"\1", content) # 去strong + content = re.sub(r'(.*?)', r"\2", content) # 去color + content = re.sub(r'

(.*?)
', "", content) # 去img + return self._cleantext(content) + + class Arknights(NewMessage): categories = {1: "游戏公告"} platform_name = "arknights" @@ -108,7 +144,7 @@ class Arknights(NewMessage): # 只有一张图片 title = title_escape(data.title) - return Post( + return ArknightsPost( self, content=data.content, title=title, @@ -205,7 +241,7 @@ class MonsterSiren(NewMessage): text = f'{raw_post["title"]}\n{soup.text.strip()}' return Post( self, - text, + content=text, images=imgs, url=url, nickname="塞壬唱片新闻", @@ -246,7 +282,7 @@ class TerraHistoricusComic(NewMessage): url = f'https://terra-historicus.hypergryph.com/comic/{raw_post["comicCid"]}/episode/{raw_post["episodeCid"]}' return Post( self, - raw_post["subtitle"], + content=raw_post["subtitle"], title=f'{raw_post["title"]} - {raw_post["episodeShortTitle"]}', images=[raw_post["coverUrl"]], url=url, diff --git a/nonebot_bison/platform/bilibili/platforms.py b/nonebot_bison/platform/bilibili/platforms.py index 5324156..3aeafa3 100644 --- a/nonebot_bison/platform/bilibili/platforms.py +++ b/nonebot_bison/platform/bilibili/platforms.py @@ -465,7 +465,7 @@ class Bilibililive(StatusChange): target_name = f"{raw_post.uname} {raw_post.area_name}" return Post( self, - "", + content="", title=title, url=url, images=list(pic), @@ -550,7 +550,7 @@ class BilibiliBangumi(StatusChange): title = lastest_episode["share_copy"] return Post( self, - content, + content=content, title=title, url=url, images=list(pic), diff --git a/nonebot_bison/platform/ff14.py b/nonebot_bison/platform/ff14.py index 31df714..412a463 100644 --- a/nonebot_bison/platform/ff14.py +++ b/nonebot_bison/platform/ff14.py @@ -44,4 +44,4 @@ class FF14(NewMessage): title = raw_post["Title"] text = raw_post["Summary"] url = raw_post["Author"] - return Post(self, text, title=title, url=url, nickname="最终幻想XIV官方公告") + return Post(self, content=text, title=title, url=url, nickname="最终幻想XIV官方公告") diff --git a/nonebot_bison/platform/ncm.py b/nonebot_bison/platform/ncm.py index 1595cc5..069ef9c 100644 --- a/nonebot_bison/platform/ncm.py +++ b/nonebot_bison/platform/ncm.py @@ -69,7 +69,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(self, text, url=url, images=pics, nickname=target_name) + return Post(self, content=text, url=url, images=pics, nickname=target_name) class NcmRadio(NewMessage): @@ -130,4 +130,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(self, text, url=url, images=pics, nickname=target_name) + return Post(self, content=text, url=url, images=pics, nickname=target_name) diff --git a/nonebot_bison/platform/rss.py b/nonebot_bison/platform/rss.py index 3936042..895b1e2 100644 --- a/nonebot_bison/platform/rss.py +++ b/nonebot_bison/platform/rss.py @@ -9,7 +9,7 @@ from bs4 import BeautifulSoup as bs from ..post import Post from .platform import NewMessage from ..types import Target, RawPost -from ..utils import Site, text_similarity +from ..utils import Site, text_fletten, text_similarity class RssSite(Site): @@ -18,6 +18,23 @@ class RssSite(Site): schedule_setting = {"seconds": 30} +class RssPost(Post): + + async def get_plain_content(self) -> str: + soup = bs(self.content, "html.parser") + + for img in soup.find_all("img"): + img.replace_with("[图片]") + + for br in soup.find_all("br"): + br.replace_with("\n") + + for p in soup.find_all("p"): + p.insert_after("\n") + + return text_fletten(soup.get_text()) + + class Rss(NewMessage): categories = {} enable_tag = False @@ -72,9 +89,9 @@ class Rss(NewMessage): for media in raw_post["media_content"]: if media.get("medium") == "image" and media.get("url"): pics.append(media.get("url")) - return Post( + return RssPost( self, - desc, + content=desc, title=title, url=raw_post.link, images=pics, diff --git a/nonebot_bison/platform/weibo.py b/nonebot_bison/platform/weibo.py index ee77131..1c4876d 100644 --- a/nonebot_bison/platform/weibo.py +++ b/nonebot_bison/platform/weibo.py @@ -183,7 +183,13 @@ class Weibo(NewMessage): res.raise_for_status() pics.append(res.content) detail_url = f"https://weibo.com/{info['user']['id']}/{info['bid']}" - return Post(self, parsed_text, url=detail_url, images=pics, nickname=info["user"]["screen_name"]) + return Post( + self, + content=parsed_text, + url=detail_url, + images=pics, + nickname=info["user"]["screen_name"], + ) async def parse(self, raw_post: RawPost) -> Post: info = raw_post["mblog"] diff --git a/nonebot_bison/post/post.py b/nonebot_bison/post/post.py index e7bbd07..2a1a8a5 100644 --- a/nonebot_bison/post/post.py +++ b/nonebot_bison/post/post.py @@ -10,6 +10,7 @@ from nonebot_plugin_saa import MessageSegmentFactory from ..theme import theme_manager from .abstract_post import AbstractPost from ..plugin_config import plugin_config +from .protocol import PlainContentSupport from ..theme.types import ThemeRenderError, ThemeRenderUnsupportError if TYPE_CHECKING: @@ -17,7 +18,7 @@ if TYPE_CHECKING: @dataclass -class Post(AbstractPost): +class Post(AbstractPost, PlainContentSupport): """最通用的Post,理论上包含所有常用的数据 对于更特殊的需要,可以考虑另外实现一个Post @@ -62,6 +63,12 @@ class Post(AbstractPost): themes_by_priority.append("basic") return themes_by_priority + async def get_content(self): + return self.content + + async def get_plain_content(self): + return self.content + async def generate(self) -> list[MessageSegmentFactory]: """生成消息""" themes = self.get_priority_themes() @@ -95,12 +102,13 @@ class Post(AbstractPost): 来源: """ post_format += "附加信息:\n" - for field in fields(self): - if field.name in ("content", "platform", "repost"): + for cls_field in fields(self): + if cls_field.name in ("content", "platform", "repost"): continue - value = getattr(self, field.name) - if value is not None: - post_format += f"- {field.name}: {aRepr.repr(value)}\n" + else: + value = getattr(self, cls_field.name) + if value is not None: + post_format += f"- {cls_field.name}: {aRepr.repr(value)}\n" if self.repost: post_format += "\n转发:\n" diff --git a/nonebot_bison/post/protocol.py b/nonebot_bison/post/protocol.py new file mode 100644 index 0000000..4bdf912 --- /dev/null +++ b/nonebot_bison/post/protocol.py @@ -0,0 +1,16 @@ +from typing import Protocol, runtime_checkable + + +@runtime_checkable +class PlainContentSupport(Protocol): + async def get_plain_content(self) -> str: ... + + +@runtime_checkable +class HTMLContentSupport(Protocol): + async def get_html_content(self) -> str: ... + + +@runtime_checkable +class MarkdownContentSupport(Protocol): + async def get_markdown_content(self) -> str: ... diff --git a/nonebot_bison/theme/themes/arknights/build.py b/nonebot_bison/theme/themes/arknights/build.py index 01c932e..ec6265d 100644 --- a/nonebot_bison/theme/themes/arknights/build.py +++ b/nonebot_bison/theme/themes/arknights/build.py @@ -10,7 +10,7 @@ from nonebot_bison.theme.utils import web_embed_image from nonebot_bison.theme import Theme, ThemeRenderError, ThemeRenderUnsupportError if TYPE_CHECKING: - from nonebot_bison.post import Post + from nonebot_bison.platform.arknights import ArknightsPost @dataclass @@ -32,7 +32,7 @@ class ArknightsTheme(Theme): template_path: Path = Path(__file__).parent / "templates" template_name: str = "announce.html.jinja" - async def render(self, post: "Post"): + async def render(self, post: "ArknightsPost"): from nonebot_plugin_htmlrender import template_to_pic if not post.title: @@ -49,10 +49,9 @@ class ArknightsTheme(Theme): raise ThemeRenderUnsupportError( f"图片类型错误, 期望 str | Path | bytes | BytesIO | None, 实际为 {type(banner)}" ) - ark_data = ArkData( announce_title=text_fletten(post.title), - content=post.content, + content=await post.get_content(), banner_image_url=banner, ) diff --git a/nonebot_bison/theme/themes/basic/build.py b/nonebot_bison/theme/themes/basic/build.py index 8b33278..95d2249 100644 --- a/nonebot_bison/theme/themes/basic/build.py +++ b/nonebot_bison/theme/themes/basic/build.py @@ -24,12 +24,15 @@ class BasicTheme(Theme): text += f"{post.title}\n\n" if post.title else "" - text += post.content if len(post.content) < 500 else f"{post.content[:500]}..." + content = await post.get_plain_content() + text += content if len(content) < 500 else f"{content[:500]}..." if rp := post.repost: text += f"\n--------------\n转发自 {rp.nickname or ''}:\n" text += f"{rp.title}\n\n" if rp.title else "" - text += rp.content if len(rp.content) < 500 else f"{rp.content[:500]}..." + rp_content = await rp.get_plain_content() + + text += rp_content if len(rp_content) < 500 else f"{rp_content[:500]}..." text += "\n--------------\n" diff --git a/nonebot_bison/theme/themes/ceobe_canteen/build.py b/nonebot_bison/theme/themes/ceobe_canteen/build.py index 862b111..9d3a2be 100644 --- a/nonebot_bison/theme/themes/ceobe_canteen/build.py +++ b/nonebot_bison/theme/themes/ceobe_canteen/build.py @@ -95,7 +95,7 @@ class CeobeCanteenTheme(Theme): if post.images: images = await self.merge_pics(post.images, http_client) - content = CeoboContent(text=post.content) + content = CeoboContent(text=await post.get_content()) retweet: CeoboRetweet | None = None if post.repost: @@ -106,7 +106,9 @@ class CeobeCanteenTheme(Theme): images.extend(repost_images) repost_nickname = f"@{post.repost.nickname}:" if post.repost.nickname else "" - retweet = CeoboRetweet(image=repost_head_pic, content=post.repost.content, author=repost_nickname) + retweet = CeoboRetweet( + image=repost_head_pic, content=await post.repost.get_content(), author=repost_nickname + ) return ( CeobeCard( diff --git a/nonebot_bison/theme/themes/ht2i/build.py b/nonebot_bison/theme/themes/ht2i/build.py index 3134a28..4f95fa4 100644 --- a/nonebot_bison/theme/themes/ht2i/build.py +++ b/nonebot_bison/theme/themes/ht2i/build.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Literal from nonebot_plugin_saa import Text, Image, MessageSegmentFactory from nonebot_bison.theme import Theme, ThemeRenderError +from nonebot_bison.post.protocol import HTMLContentSupport from nonebot_bison.utils import pic_merge, is_pics_mergable if TYPE_CHECKING: @@ -30,16 +31,28 @@ class Ht2iTheme(Theme): raise ThemeRenderError(f"渲染文本失败: {e}") async def render(self, post: "Post"): + md_text = "" md_text += f"## {post.title}\n\n" if post.title else "" - md_text += post.content if len(post.content) < 500 else f"{post.content[:500]}..." + if isinstance(post, HTMLContentSupport): + content = await post.get_html_content() + else: + content = await post.get_content() + md_text += content if len(content) < 500 else f"{content[:500]}..." md_text += "\n\n" if rp := post.repost: md_text += f"> 转发自 {f'**{rp.nickname}**' if rp.nickname else ''}: \n" md_text += f"> {rp.title} \n" if rp.title else "" - md_text += "> \n> " + rp.content if len(rp.content) < 500 else f"{rp.content[:500]}..." + " \n" + if isinstance(rp, HTMLContentSupport): + rp_content = await rp.get_html_content() + else: + rp_content = await rp.get_content() + + md_text += ( + "> \n> " + rp_content if len(rp_content) < 500 else f"{rp_content[:500]}..." + " \n" # noqa: E501 + ) # noqa: E501 md_text += "\n\n" md_text += f"###### 来源: {post.platform.name} {post.nickname or ''}\n" diff --git a/nonebot_bison/theme/types.py b/nonebot_bison/theme/types.py index 3d4a312..8802a08 100644 --- a/nonebot_bison/theme/types.py +++ b/nonebot_bison/theme/types.py @@ -38,7 +38,6 @@ class Theme(ABC, BaseModel): raise ThemeRenderUnsupportError(f"Theme [{self.name}] does not support render {post} by support check") await self.prepare() - return await self.render(post) def check_htmlrender_plugin_enable(self): diff --git a/tests/platforms/static/arknights-plaintext-805.txt b/tests/platforms/static/arknights-plaintext-805.txt new file mode 100644 index 0000000..78a295f --- /dev/null +++ b/tests/platforms/static/arknights-plaintext-805.txt @@ -0,0 +1,18 @@ +感谢您对《明日方舟》的关注与支持。《明日方舟》将于08月01日10:00 ~16:00的更新维护中对游戏内【公开招募】进行新增干员。具体新增干员及标签强制刷新注意事项如下: +公开招募新增干员: +★★★★★★ 棘刺 +★★★★★ 安哲拉 +★★★★★ 贾维 +★★★★★ 蜜蜡 +★★★★ 孑 +注意: +◆本次调整更新时,将对未开始进行招募的标签进行强制刷新 +◆本次调整更新时,处于已开始招募状态的标签将不受本次刷新影响 +◆为避免带来不必要的损失,请您在本次调整前尽快聘用您所招募的干员 +◆本次新增干员不加入调整更新时处于已开始招募状态的公开招募中 +◆本次强制刷新标签补偿:【招聘许可】*5、【加急许可】*5 +◆补偿发放时间:08月01日16:00 +◆补偿发放对象:08月01日16:00更新前所有注册并创建角色的玩家 +本次调整时间不排除延迟进行的可能,如若延迟则请关注官网发布的具体调整时间。更多后续内容及最新消息请关注《明日方舟》官网、官方微博及微信公众号。 +【明日方舟】运营组 +2023年07月23日 diff --git a/tests/platforms/static/arknights-plaintext-807.txt b/tests/platforms/static/arknights-plaintext-807.txt new file mode 100644 index 0000000..4af9f67 --- /dev/null +++ b/tests/platforms/static/arknights-plaintext-807.txt @@ -0,0 +1,186 @@ +==夏日嘉年华活动Part.1== +一、「夏日嘉年华」,SideStory「火山旅梦」活动开启 +活动时间:08月01日 16:00 - 08月29日 03:59 +解锁条件:通关主线1-10 +活动说明:活动期间将开放「火山旅梦」活动,玩家可通过活动关卡作战、汐斯塔风情街、活动任务以及活动商店来获取活动奖励 +活动关卡将进行分段式开启: +◆<第一段> +开放时间:08月01日 16:00~08月22日 03:59 +开放关卡: +【温泉假日】 +◆<第二段> +开放时间:08月08日 16:00~08月22日 03:59 +开放关卡: +【城市寻宝】 +◆<第三段> +开放时间:08月15日 16:00~08月22日 03:59 +开放关卡: +【岩浆冲浪】 +【汐斯塔风情街】 +活动说明:活动期间,玩家可在“汐斯塔风情街”中完成商品进货和商品售卖获取奖励 +主要奖励:峯联贸易物流补给(使用后随机获得指定养成素材)、【“纯白火山”之旅】家具(部分)、模组养成材料、高级养成素材、龙门币、作战记录等 +【多利的任务单】 +活动说明:活动期间,将开放【多利的任务单】,玩家可通过活动关卡作战等方式,完成【多利的任务单】中相关任务获取奖励 +主要奖励:活动干员【★★★★★:苍苔】、【“纯白火山”之旅】家具(部分)、「毛绒绒生物的毛」、模组养成材料等 +【“纯白火山”】 +开放时间:08月01日 16:00 - 08月29日 03:59 +兑换说明:活动期间,玩家可通过活动收集「毛绒绒生物的毛」,消耗「毛绒绒生物的毛」兑换【“纯白火山”】内物品 +主要奖励:【★★★★★:苍苔】信物、【“纯白火山”之旅】家具(部分)、寻访凭证、模组养成材料、高级养成素材、龙门币、作战记录、家具零件等 + +◆详细活动玩法介绍可关注后续官方微博相关内容 +◆本次活动期间【情报处理室】将无法浏览当期活动剧情,活动结束后该活动剧情将收录至【情报处理室】中 +二、「夏日嘉年华」,【云间清醒梦】限定寻访开启 +活动时间:08月01日 16:00 - 08月15日 03:59 +活动说明:活动期间【限定寻访·夏季】-【云间清醒梦】寻访开启,该寻访中以下干员出现率上升 +★★★★★★:纯烬艾雅法拉[限定] \ 琳琅诗怀雅(占6★出率的70%) +★★★★★★:百炼嘉维尔 [限定] \ 假日威龙陈 [限定] (在6★剩余出率【30%】中以5倍权值出率提升) +★★★★★:青枳(占5★出率的50%) +注意: +◆【云间清醒梦】为【限定寻访·夏季】系列寻访 +◆【限定寻访·夏季】寻访为【限定寻访】 +◆新增干员【琳琅诗怀雅】、【青枳】除加入【云间清醒梦】外,将在08月03日04:00后加入并常驻【标准寻访】卡池 +◆新增干员【纯烬艾雅法拉】为【限定寻访·夏季】限定干员,不会加入任何【标准寻访】 +◆限定干员【纯烬艾雅法拉】在本次寻访结束后,将不会在2024年07月01日前加入任何【限定寻访】 +◆注意:该限定寻访详细说明请参照相关卡池公告说明 +三、「夏日嘉年华」,特别登录活动开启 +活动时间:08月01日 16:00 - 08月15日 03:59 +活动说明:玩家在活动期间进行登录即可在活动页面中领取登录奖励:[云过天空寻访凭证]*1 +注意: +◆[云过天空寻访凭证] 仅可用于限定寻访【云间清醒梦】中进行一次十连寻访 +◆该登录奖励活动期间内每个账号仅可领取一次 +◆[云过天空寻访凭证] 过期时间为:08月15日03:59 +四、「夏日嘉年华」,限定寻访每日赠送 +活动时间:08月01日 16:00 - 08月15日 03:59 +活动说明:活动期间,玩家每日可在【云间清醒梦】限定寻访中免费进行寻访一次 +注意: +◆每日赠送寻访刷新时间为每日凌晨04:00 +◆未进行使用的“每日赠送寻访”将在每日凌晨04:00 进行刷新,无法进行累计 +五、「夏日嘉年华」新干员登场,信赖获取提升 +新增干员: +★★★★★★:纯烬艾雅法拉[限定] +★★★★★★:琳琅诗怀雅 +★★★★★:青枳 +★★★★★:苍苔 +注意: +◆新增干员【苍苔】仅在本次「火山旅梦」活动中获取,暂不加入【云间清醒梦】、任何【标准寻访】及【中坚寻访】 +◆08月01日 16:00 - 08月22日 03:59期间以上新增干员在活动关卡内信赖获取提升 +六、「夏日嘉年华」,“氤氲奇境”签到活动开启 +活动时间:08月01日 16:00 - 08月15日 03:59 +活动说明:活动期间内,玩家累计活动签到10日可获得褐果专属时装、活动家具、 合成玉等活动奖励 +第一日:应急理智浓缩液*1、龙门币 *30000 +第二日:合成玉*200、招聘许可*5 +第三日:技巧概要·卷2*20、家具【“暖身浴”】*1 +第四日:芯片助剂*1、家具零件*300 +第五日:数据增补条*10、中级作战记录*20 +第六日:合成玉*200、赤金*20 +第七日:数据增补仪*3、技巧概要·卷3*8 +第八日:龙门币 *30000、时装【珊瑚海岸系列-“悠然假日 HDm57”-褐果】*1 +第九日:模组数据块*1、加急许可*5 +第十日:合成玉*200、中级作战记录*20 +注意:本次签到活动中领取的“应急理智浓缩液”到期时间请以游戏内显示为准 +七、「夏日嘉年华」,【汐斯塔涂鸦墙】活动开启 +活动时间:08月01日 16:00 - 08月15日 03:59 +活动说明:在活动期间玩家每日登录游戏后均可获得2次可选签数。在【汐斯塔涂鸦墙】中消耗可选签数来进行游戏。在选取的心动券中,心动券所对应的合成玉数量最多的1张心动券为当日最终奖励,玩家需要用尽当日可选签数选取心动券,方可领取奖励。 +注意事项: +◆活动期间可进行抽取的天数最高为14个游戏日 +◆心动券中可出现的合成玉数量为以下几档: +(合成玉*200 、合成玉*300 、合成玉*400 、合成玉*500 、合成玉*600、合成玉*800) +◆当日心动券合成玉最终获得奖励不足400时(不含400),次日将增加一次可选签数。该次数不可累积叠加,玩家每日最多可获得的可选签数为3次(含每日登录赠送次数) +◆当日所有可选签数将在次日凌晨04:00 进行刷新,无法累计 +八、「夏日嘉年华」,【珊瑚海岸】系列新装上架 +活动时间:08月01日 16:00 - 08月29日 03:59 +活动说明:活动期间以下干员新增时装将在时装商店上架并进行限时贩售: +◆【珊瑚海岸】系列 -“悠然假日 HD26”- 百炼嘉维尔 +◆【珊瑚海岸】系列 -“夏卉 FA394”- 澄闪 +◆【珊瑚海岸】系列 -“夏卉 FA062”- 桃金娘 +九、「夏日嘉年华」,【珊瑚海岸】系列时装复刻上架 +贩售时间:08月01日 16:00 - 08月29日 03:59 +活动说明:活动期间以下干员复刻时装将在时装商店上架并进行限时贩售 +◆【珊瑚海岸】系列 -“夏卉 FA018”- 艾雅法拉 +◆【珊瑚海岸】系列 -“夏卉 FA017”- 安洁莉娜 +◆【珊瑚海岸】系列 -“静谧午夜 DN04”- 特米米 +◆【珊瑚海岸】系列 -“静谧午夜DN06”- 泥岩 +◆【珊瑚海岸】系列 -“轻风LB01”- 红 +◆【珊瑚海岸】系列 -“轻风LB03”- 铸铁 +◆【珊瑚海岸】系列 -“沙滩护卫GT002”- 孑 +十、「夏日嘉年华」,【万重山】时装特别复刻 +贩售时间:08月01日 16:00 - 08月29日 03:59 +活动说明:活动期间以下干员时装将在时装商店特别复刻并进行限时贩售 +◆【斗争血脉】系列 - “万重山” - 假日威龙陈 +十一、「夏日嘉年华」,【罗德岛风尚回顾】限时开启 +活动时间:08月01日 16:00 - 08月29日 03:59 +活动说明:活动期间,【斗争血脉系列 - “乌云” - 山】【斗争血脉系列 - “熔锻铸匠” - 森蚺】【音律联觉系列 - “爆裂菲林” - 煌】等84款干员时装将在时装商店复刻上架并进行限时贩售 +十二、「夏日嘉年华」,新增主题家具限时获取 +活动说明:活动期间新增活动限定家具【“求索者之梦”科考小屋】系列与【“纯白火山”之旅】系列 +【“求索者之梦”科考小屋】系列 +获取方式: +◆家具商店售卖时间:08月01日 16:00 - 08月29日 03:59 +【“纯白火山”之旅】系列 +获取方式: +◆「火山旅梦」活动奖励及兑换(部分) +◆家具商店限时售卖(部分) +◆家具商店售卖时间:08月01日 16:00 - 08月29日 03:59 +十三、「夏日嘉年华」,夏日组合包限时上架 +活动说明:「夏日嘉年华」活动开启后,采购中心将限时售卖以下组合包 +售卖时间:08月01日 16:00 - 08月29日 03:59 +【汐斯塔风物组合包】 +组合包内容:至纯源石*13、烧结核凝晶*2、双极纳米片*2、聚合凝胶*2、半自然溶剂*6 +【假日氤氲组合包】 +组合包内容:合成玉*4500、龙门币*100000、芯片助剂*2、技巧概要·卷1*80、模组数据块*2 +【温泉观光组合包】 +组合包内容:至纯源石*24、十连寻访凭证*2、龙门币*200000、高级作战记录*20、中级作战记录*30、芯片助剂*2、模组数据块*2、切削原液*4 +【火山狂欢组合包】 +组合包内容:至纯源石*90、十连寻访凭证*1、龙门币*220000、高级作战记录*30、芯片助剂*4、模组数据块*4、数据增补仪*20、数据增补条*60 +[诗怀雅的手提箱] +组合包内容:至纯源石*16、龙门币*700000 +[特训意向礼包] +组合包内容:至纯源石*11、资深干员特训装置*1 +资深干员特训装置:使用该特训装置后可选择一名已晋升至精英阶段2的5星干员直接升至精英阶段2,等级80。干员升级后不会返还已投入的养成资源。 +[高级特训意向礼包] +组合包内容:至纯源石*19、高级资深干员特训装置*1 +高级资深干员特训装置:使用该特训装置后可选择一名已晋升至精英阶段2的6星干员直接升至精英阶段2,等级90。干员升级后不会返还已投入的养成资源。 +十四、「夏日嘉年华」,芯片&材料礼包上架 +【医疗芯片礼包】 +售卖时间:08月01日 16:00 - 08月29日 03:59 +组合包内容:医疗双芯片*4、医疗芯片*5、技巧概要·卷3*45、技巧概要·卷2*25、技巧概要·卷1*10 +【特种芯片礼包】 +售卖时间:08月01日 16:00 - 08月29日 03:59 +组合包内容:特种双芯片*4、特种芯片*5、技巧概要·卷3*45、技巧概要·卷2*25、技巧概要·卷1*10 +[每月材料自选组合包] +售卖时间:08月01日 16:00起常驻售卖(每月限购两次) +组合包内容:特级材料提货券*8、高级材料提货券*12 +特级材料提货券:使用后可获得任一特级品质精英材料 +高级材料提货券:使用后可获得任一高级品质精英材料 +注意: +◆当月特级材料提货券与高级材料提货券过期时间为:次月10日 03:59 +◆特级材料提货券与高级材料提货券的材料可选范围会随后续材料种类更新而扩充 +十五、「夏日嘉年华」,【公开招募】干员更新 +更新时间:08月01日 16:00 +更新说明:更新后,以下干员将加入并常驻【公开招募】 +★★★★★★:棘刺 +★★★★★:安哲拉 +★★★★★:贾维 +★★★★★:蜜蜡 +★★★★:孑 +注意: +本次【公开招募】干员更新时,将对未开始进行招募的标签进行强制刷新,强制刷新注意事项及相关补偿请参照官方相关更新公告 +==【珊瑚海岸系列 - “悠然假日 HD49” - 锡兰】== +活动时间:08月18日 16:00 - 09月01日 03:59 +活动说明:活动期间【联合行动】特选干员定向寻访开启,该寻访卡池列表中六星干员与五星干员仅出现以下干员 +★★★★★★(6★出率:2%):鸿雪 / 斥罪 / 玛恩纳 / 老鲤 +★★★★★(5★出率:8%):极光 / 洋灰 / 赤冬 / 絮雨 / 和弦 / 玫拉 +注意:本次活动【联合行动】寻访为【标准寻访】 +三、「夏日嘉年华」,中坚甄选开启 +活动时间:08月24日 16:00 - 09月07日 03:59 +活动说明:活动期间,玩家可以在当期甄选的干员范围内自主选择并锁定2位六星干员和3位五星干员作为当期【中坚甄选】寻访出现概率提升的干员。相应地,原【高级凭证区】和【通用凭证区】内可兑换的【中坚寻访】中出率上升的干员,在【中坚甄选】期间将转变为:在当期【中坚甄选】寻访获得概率提升的干员范围内自行选定1位六星干员和1位五星干员进入【高级凭证区】和【通用凭证区】内可供兑换 +当期甄选的干员范围: +★★★★★★(出率2%):能天使/安洁莉娜/风笛/闪灵/斯卡蒂/陈/赫拉格/煌/星熊/夜莺/阿/艾雅法拉 +★★★★★(出率8%):赫默/夜魔/天火/真理/槐琥/初雪/梅尔/守林人/白面鸮/临光/普罗旺斯/崖心/芙兰卡/蓝毒/雷蛇/可颂/食铁兽/苇草/布洛卡/灰喉/吽/惊蛰/慑砂/巫恋 +注意:【中坚甄选】会继承切换前的常驻【中坚寻访】六星干员获得概率 +四、【0011/韵】系列,新装限时上架 +活动时间:08月22日 04:00 - 09月05日 03:59 +活动说明:活动期间以下干员时装将在时装商店上架并进行限时贩售: +◆【0011/韵】系列 -“棠云巧梦”- 子月 +更多活动内容请持续关注《明日方舟》游戏内公告及官方公告。 +【明日方舟】运营组 +2023年07月23日 diff --git a/tests/platforms/test_arknights.py b/tests/platforms/test_arknights.py index ddd0705..4e245c9 100644 --- a/tests/platforms/test_arknights.py +++ b/tests/platforms/test_arknights.py @@ -170,6 +170,7 @@ async def test_fetch_new( ): from nonebot_bison.post import Post from nonebot_bison.types import Target, SubUnit + from nonebot_bison.platform.arknights import ArknightsPost ak_list_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletinList?target=IOS") detail_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/5716") @@ -201,7 +202,7 @@ async def test_fetch_new( res2 = await arknights.fetch_new_post(SubUnit(target, [dummy_user_subinfo])) assert len(res2[0][1]) == 1 assert detail_router.called - post2: Post = res2[0][1][0] + post2: ArknightsPost = res2[0][1][0] assert post2.platform.platform_name == "arknights" assert post2.content assert post2.title == "2023「夏日嘉年华」限时活动即将开启" @@ -212,7 +213,9 @@ async def test_fetch_new( assert post2.timestamp assert "arknights" == post2.get_priority_themes()[0] # assert(post.pics == ['https://ak-fs.hypergryph.com/announce/images/20210623/e6f49aeb9547a2278678368a43b95b07.jpg']) - + post2_plain_content = await post2.get_plain_content() + assert post2_plain_content.strip() == get_file("arknights-plaintext-807.txt").strip() + assert await post2.generate() terra_list.mock(return_value=Response(200, json=get_json("terra-hist-1.json"))) res3 = await arknights.fetch_new_post(SubUnit(target, [dummy_user_subinfo])) assert len(res3) == 1 @@ -236,8 +239,8 @@ async def test_send_with_render( monster_siren_list_0, monster_siren_list_1, ): - from nonebot_bison.post import Post from nonebot_bison.types import Target, SubUnit + from nonebot_bison.platform.arknights import ArknightsPost ak_list_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletinList?target=IOS") detail_router = respx.get("https://ak-webview.hypergryph.com/api/game/bulletin/8397") @@ -267,9 +270,12 @@ async def test_send_with_render( res2 = await arknights.fetch_new_post(SubUnit(target, [dummy_user_subinfo])) assert len(res2[0][1]) == 1 assert detail_router.called - post2: Post = res2[0][1][0] + post2: ArknightsPost = res2[0][1][0] assert post2.platform.platform_name == "arknights" - assert "《明日方舟》将于08月01日10:00 ~16:00的更新维护中对游戏内【公开招募】进行新增干员。" in post2.content + assert post2.content + post2_plain_content = await post2.get_plain_content() + assert post2_plain_content.strip() == get_file("arknights-plaintext-805.txt").strip() + assert await post2.generate() assert post2.title == "【公开招募】标签强制刷新通知" assert post2.nickname == "明日方舟游戏内公告" assert not post2.images diff --git a/tests/post/test_generate.py b/tests/post/test_generate.py index 06dc60a..be3bbb8 100644 --- a/tests/post/test_generate.py +++ b/tests/post/test_generate.py @@ -1,5 +1,6 @@ from time import time from typing import Any +from inspect import cleandoc import pytest from nonebug.app import App @@ -48,7 +49,7 @@ def mock_platform(app: App): async def parse(self, raw_post: "RawPost") -> "Post": return Post( self, - raw_post["text"], + content=raw_post["text"], url="http://t.tt/" + str(self.get_id(raw_post)), nickname="MockNick", ) @@ -67,12 +68,15 @@ def mock_platform(app: App): async def test_display(mock_platform): from nonebot_bison.post import Post - post = Post( - mock_platform, + post1_content = cleandoc( "Rebum delenit iusto augue in rebum sanctus diam stet clita voluptua amet tempor sea in.\n" "Vel ullamcorper dolore clita eos amet tempor velit amet in.\n" "Vero hendrerit vero diam et lorem blandit ex diam ex amet.\n" "Voluptua et sed diam erat et diam lorem lorem no euismod sadipscing rebum feugiat est elitr autem.\n", + ) + post1 = Post( + mock_platform, + content=post1_content, title="Ipsum consectetuer voluptua eirmod aliquyam dolore eu volutpat ipsum ipsum eirmod nulla.", images=[ b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89", @@ -87,8 +91,8 @@ async def test_display(mock_platform): description="Labore amet ut invidunt dolor consectetuer ipsum sadipscing sed minim diam rebum justo tincidunt.", ) assert ( - str(post) - == rf"""## Post: {id(post):X} ## + str(post1) + == rf"""## Post: {id(post1):X} ## Rebum delenit iusto augue in rebum sanctus diam stet clita voluptua amet tempor sea in. Vel ullamcorper dolore clita eos amet tempor velit amet in. @@ -106,12 +110,16 @@ Vero hendrerit vero diam et lorem blandit ex diam ex... - description: 'Labore amet ut invidunt dolor consectetuer ipsum sadipscing sed minim diam rebum justo tincidunt.' """ ) # noqa: E501 - post2 = Post( - mock_platform, + + post2_content = cleandoc( "Rebum delenit iusto augue in rebum sanctus diam stet clita voluptua amet tempor sea in.\n" "Vel ullamcorper dolore clita eos amet tempor velit amet in.\n" "Vero hendrerit vero diam et lorem blandit ex diam ex amet.\n" "Voluptua et sed diam erat et diam lorem lorem no euismod sadipscing rebum feugiat est elitr autem.\n", + ) + post2 = Post( + mock_platform, + content=post2_content, title="Ipsum consectetuer voluptua eirmod aliquyam dolore eu volutpat ipsum ipsum eirmod nulla.", images=[ b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89", @@ -124,7 +132,7 @@ Vero hendrerit vero diam et lorem blandit ex diam ex... ), nickname="Mock2", description="Labore amet ut invidunt dolor consectetuer ipsum sadipscing sed minim diam rebum justo tincidunt.", - repost=post, + repost=post1, ) assert ( str(post2) @@ -146,7 +154,7 @@ Vero hendrerit vero diam et lorem blandit ex diam ex... - description: 'Labore amet ut invidunt dolor consectetuer ipsum sadipscing sed minim diam rebum justo tincidunt.' 转发: -## Post: {id(post):X} ## +## Post: {id(post1):X} ## Rebum delenit iusto augue in rebum sanctus diam stet clita voluptua amet tempor sea in. Vel ullamcorper dolore clita eos amet tempor velit amet in. diff --git a/tests/theme/test_themes.py b/tests/theme/test_themes.py index af2acec..bc27c04 100644 --- a/tests/theme/test_themes.py +++ b/tests/theme/test_themes.py @@ -65,7 +65,7 @@ def mock_platform(app: App): async def parse(self, raw_post: "RawPost") -> "Post": return Post( self, - raw_post["text"], + content=raw_post["text"], url="http://t.tt/" + str(self.get_id(raw_post)), nickname="Mock", ) @@ -88,7 +88,7 @@ def mock_post(app: App, mock_platform): return Post( m := mock_platform(ProcessContext(DefaultClientManager())), - "text", + content="text", title="title", images=["http://t.tt/1.jpg"], timestamp=1234567890, @@ -98,7 +98,7 @@ def mock_post(app: App, mock_platform): description="description", repost=Post( m, - "repost", + content="repost", title="repost-title", images=["http://t.tt/2.jpg"], timestamp=1234567891,