From 7e883f4d2c0e1c5a293dcdd751c857cc9c18b2bc Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Tue, 29 Jun 2021 10:33:08 +0800 Subject: [PATCH 01/11] reconstruct --- .../nonebot_hk_reporter/platform/__init__.py | 8 +- .../nonebot_hk_reporter/platform/arknights.py | 21 +- .../nonebot_hk_reporter/platform/bilibili.py | 9 +- .../platform/monster_siren.py | 11 +- .../nonebot_hk_reporter/platform/platform.py | 273 +++++++++--------- .../nonebot_hk_reporter/platform/rss.py | 9 +- .../nonebot_hk_reporter/platform/wechat.py | 108 +++---- .../nonebot_hk_reporter/platform/weibo.py | 16 +- src/plugins/nonebot_hk_reporter/scheduler.py | 9 +- tests/platforms/arknights-detail-675.html | 24 ++ tests/platforms/arknights_list_0.json | 108 +++++++ tests/platforms/arknights_list_1.json | 1 + tests/platforms/test_arknights.py | 49 ++++ tests/platforms/test_platform.py | 232 +++++++++++++++ tests/platforms/test_weibo.py | 5 +- 15 files changed, 647 insertions(+), 236 deletions(-) create mode 100644 tests/platforms/arknights-detail-675.html create mode 100644 tests/platforms/arknights_list_0.json create mode 100644 tests/platforms/arknights_list_1.json create mode 100644 tests/platforms/test_arknights.py create mode 100644 tests/platforms/test_platform.py diff --git a/src/plugins/nonebot_hk_reporter/platform/__init__.py b/src/plugins/nonebot_hk_reporter/platform/__init__.py index d62cbb3..e181c8f 100644 --- a/src/plugins/nonebot_hk_reporter/platform/__init__.py +++ b/src/plugins/nonebot_hk_reporter/platform/__init__.py @@ -1,4 +1,4 @@ -from .platform import PlatformProto +from .platform import Platform from pkgutil import iter_modules from pathlib import Path from importlib import import_module @@ -9,9 +9,9 @@ for (_, module_name, _) in iter_modules([_package_dir]): async def check_sub_target(target_type, target): - return await platform_manager[target_type].get_account_name(target) + return await platform_manager[target_type].get_target_name(target) -platform_manager: dict[str, PlatformProto] = { +platform_manager: dict[str, Platform] = { obj.platform_name: obj() for obj in \ - filter(lambda platform: platform.enabled, PlatformProto.registory) + filter(lambda platform: platform.enabled, Platform.registory) } diff --git a/src/plugins/nonebot_hk_reporter/platform/arknights.py b/src/plugins/nonebot_hk_reporter/platform/arknights.py index 4fcef50..8f0278b 100644 --- a/src/plugins/nonebot_hk_reporter/platform/arknights.py +++ b/src/plugins/nonebot_hk_reporter/platform/arknights.py @@ -1,21 +1,17 @@ from typing import Any import httpx import json -import time -from collections import defaultdict from bs4 import BeautifulSoup as bs -from datetime import datetime -from nonebot import logger -from ..types import Category, RawPost, Tag, Target +from ..types import RawPost, Target -from .platform import PlatformNoTarget, CategoryNotSupport +from .platform import NewMessage, NoTargetMixin, CategoryNotSupport -from ..utils import Singleton, Render +from ..utils import Render from ..post import Post -class Arknights(PlatformNoTarget): +class Arknights(NewMessage, NoTargetMixin): categories = {} platform_name = 'arknights' @@ -23,13 +19,14 @@ class Arknights(PlatformNoTarget): enable_tag = False enabled = True is_common = False - schedule_interval = 30 + schedule_type = 'interval' + schedule_kw = {'seconds': 30} @staticmethod - async def get_account_name(_: Target) -> str: + async def get_target_name(_: Target) -> str: return '明日方舟游戏内公告' - async def get_sub_list(self) -> list[RawPost]: + async def get_sub_list(self, _) -> list[RawPost]: async with httpx.AsyncClient() as client: raw_data = await client.get('http://ak-fs.hypergryph.com/announce/IOS/announcement.meta.json') return json.loads(raw_data.text)['announceList'] @@ -37,7 +34,7 @@ class Arknights(PlatformNoTarget): def get_id(self, post: RawPost) -> Any: return post['announceId'] - def get_date(self, post: RawPost) -> None: + def get_date(self, _: RawPost) -> None: return None async def parse(self, raw_post: RawPost) -> Post: diff --git a/src/plugins/nonebot_hk_reporter/platform/bilibili.py b/src/plugins/nonebot_hk_reporter/platform/bilibili.py index 861a707..02924cc 100644 --- a/src/plugins/nonebot_hk_reporter/platform/bilibili.py +++ b/src/plugins/nonebot_hk_reporter/platform/bilibili.py @@ -5,9 +5,9 @@ import httpx from ..post import Post from ..types import Category, RawPost, Tag, Target -from .platform import CategoryNotSupport, Platform +from .platform import NewMessage, TargetMixin, CategoryNotSupport -class Bilibili(Platform): +class Bilibili(NewMessage, TargetMixin): categories = { 1: "一般动态", @@ -20,11 +20,12 @@ class Bilibili(Platform): enable_tag = True enabled = True is_common = True - schedule_interval = 10 + schedule_type = 'interval' + schedule_kw = {'seconds': 10} name = 'B站' @staticmethod - async def get_account_name(target: Target) -> Optional[str]: + async def get_target_name(target: Target) -> Optional[str]: async with httpx.AsyncClient() as client: res = await client.get('https://api.bilibili.com/x/space/acc/info', params={'mid': target}) res_data = json.loads(res.text) diff --git a/src/plugins/nonebot_hk_reporter/platform/monster_siren.py b/src/plugins/nonebot_hk_reporter/platform/monster_siren.py index 2fc5e49..7d0044e 100644 --- a/src/plugins/nonebot_hk_reporter/platform/monster_siren.py +++ b/src/plugins/nonebot_hk_reporter/platform/monster_siren.py @@ -1,24 +1,23 @@ from typing import Any import httpx -import json -from .platform import PlatformNoTarget -from ..utils import Singleton +from .platform import NewMessage, NoTargetMixin from ..types import RawPost from ..post import Post -class MonsterSiren(PlatformNoTarget): +class MonsterSiren(NewMessage, NoTargetMixin): categories = {} platform_name = 'monster-siren' enable_tag = False enabled = True is_common = False - schedule_interval = 30 + schedule_type = 'interval' + schedule_kw = {'seconds': 30} name = '塞壬唱片官网新闻' @staticmethod - async def get_account_name(_) -> str: + async def get_target_name(_) -> str: return '塞壬唱片新闻' async def get_sub_list(self) -> list[RawPost]: diff --git a/src/plugins/nonebot_hk_reporter/platform/platform.py b/src/plugins/nonebot_hk_reporter/platform/platform.py index 7909bea..e5cd769 100644 --- a/src/plugins/nonebot_hk_reporter/platform/platform.py +++ b/src/plugins/nonebot_hk_reporter/platform/platform.py @@ -1,7 +1,7 @@ -from abc import abstractmethod +from abc import abstractmethod, ABC +from dataclasses import dataclass import time -from collections import defaultdict -from typing import Any, Collection, Optional +from typing import Any, Collection, Optional, Literal import httpx from nonebot import logger @@ -18,98 +18,119 @@ class CategoryNotSupport(Exception): class RegistryMeta(type): def __new__(cls, name, bases, namespace, **kwargs): - if name not in ['PlatformProto', 'Platform', 'PlatformNoTarget'] and \ - 'platform_name' not in namespace: - raise TypeError('Platform has no `platform_name`') - return super().__new__(cls, name, bases, namespace, **kwargs) + return super().__new__(cls, name, bases, namespace) def __init__(cls, name, bases, namespace, **kwargs): - if not hasattr(cls, 'registory'): + if kwargs.get('base'): # this is the base class cls.registory = [] - elif name not in ['Platform', 'PlatformNoTarget']: + elif not kwargs.get('abstract'): # this is the subclass cls.registory.append(cls) super().__init__(name, bases, namespace, **kwargs) +class RegistryABCMeta(RegistryMeta, ABC): + ... -class PlatformProto(metaclass=RegistryMeta): - - categories: dict[Category, str] - reverse_category: dict[str, Category] +class StorageMixinProto(metaclass=RegistryABCMeta, abstract=True): + has_target: bool - platform_name: str - name: str - enable_tag: bool - cache: dict[Any, Post] - enabled: bool - is_common: bool - schedule_interval: int @abstractmethod - async def fetch_new_post(self, target: Target, users: list[UserSubInfo]) -> list[tuple[User, list[Post]]]: + def get_stored_data(self, target: Target) -> Any: ... - @staticmethod @abstractmethod - async def get_account_name(target: Target) -> Optional[str]: - "return the username(name) of the target" + def set_stored_data(self, target: Target, data: Any): + ... - @abstractmethod - def get_id(self, post: RawPost) -> Any: - "Get post id of given RawPost" +class TargetMixin(StorageMixinProto, abstract=True): - @abstractmethod - def get_date(self, post: RawPost) -> Optional[int]: - "Get post timestamp and return, return None if can't get the time" + has_target = True + + def __init__(self): + super().__init__() + self.store: dict[Target, Any] = dict() + + def get_stored_data(self, target: Target) -> Any: + return self.store.get(target) + + def set_stored_data(self, target: Target, data: Any): + self.store[target] = data + + +class NoTargetMixin(StorageMixinProto, abstract=True): + + has_target = False + + def __init__(self): + super().__init__() + self.store = None + + def get_stored_data(self, _: Target) -> Any: + return self.store + + def set_stored_data(self, _: Target, data: Any): + self.store = data + +class PlaformNameMixin(metaclass=RegistryABCMeta, abstract=True): + platform_name: str + +class CategoryMixin(metaclass=RegistryABCMeta, abstract=True): @abstractmethod def get_category(self, post: RawPost) -> Optional[Category]: "Return category of given Rawpost" raise NotImplementedError() +class MessageProcessMixin(PlaformNameMixin, CategoryMixin, abstract=True): + "General message process fetch, parse, filter progress" + + def __init__(self): + super().__init__() + self.parse_cache: dict[Any, Post] = dict() + @abstractmethod - def get_tags(self, raw_post: RawPost) -> Optional[Collection[Tag]]: - "Return Tag list of given RawPost" + def get_id(self, post: RawPost) -> Any: + "Get post id of given RawPost" @abstractmethod async def parse(self, raw_post: RawPost) -> Post: "parse RawPost into post" + ... - @abstractmethod - def filter_platform_custom(self, post: RawPost) -> bool: - "a customed filter" - raise NotImplementedError() - - async def _parse_with_cache(self, post: RawPost) -> Post: - post_id = self.get_id(post) - if post_id not in self.cache: + async def _parse_with_cache(self, raw_post: RawPost) -> Post: + post_id = self.get_id(raw_post) + if post_id not in self.parse_cache: retry_times = 3 while retry_times: try: - self.cache[post_id] = await self.parse(post) + self.parse_cache[post_id] = await self.parse(raw_post) break except Exception as err: if not retry_times: raise err retry_times -= 1 - return self.cache[post_id] + return self.parse_cache[post_id] - def _do_filter_common(self, raw_post_list: list[RawPost], exists_posts_set: set) -> list[RawPost]: + @abstractmethod + async def get_sub_list(self, target: Target) -> list[RawPost]: + "Get post list of the given target" + + @abstractmethod + def get_date(self, post: RawPost) -> Optional[int]: + "Get post timestamp and return, return None if can't get the time" + + async def filter_common(self, raw_post_list: list[RawPost]) -> list[RawPost]: res = [] for raw_post in raw_post_list: - post_id = self.get_id(raw_post) - if post_id in exists_posts_set: - continue + # post_id = self.get_id(raw_post) + # if post_id in exists_posts_set: + # continue if (post_time := self.get_date(raw_post)) and time.time() - post_time > 2 * 60 * 60 and \ plugin_config.hk_reporter_init_filter: continue - try: - if not self.filter_platform_custom(raw_post): - continue - except NotImplementedError: - pass try: self.get_category(raw_post) except CategoryNotSupport: @@ -117,9 +138,45 @@ class PlatformProto(metaclass=RegistryMeta): except NotImplementedError: pass res.append(raw_post) - exists_posts_set.add(post_id) return res +class NewMessageProcessMixin(StorageMixinProto, MessageProcessMixin, abstract=True): + "General message process, fetch, parse, filter, and only returns the new Post" + + @dataclass + class MessageStorage(): + inited: bool + exists_posts: set[Any] + + async def filter_common_with_diff(self, target: Target, raw_post_list: list[RawPost]) -> list[RawPost]: + filtered_post = await self.filter_common(raw_post_list) + store = self.get_stored_data(target) or self.MessageStorage(False, set()) + res = [] + if not store.inited and plugin_config.hk_reporter_init_filter: + # target not init + for raw_post in filtered_post: + post_id = self.get_id(raw_post) + store.exists_posts.add(post_id) + logger.info('init {}-{} with {}'.format(self.platform_name, target, store.exists_posts)) + store.inited = True + else: + for raw_post in filtered_post: + post_id = self.get_id(raw_post) + if post_id in store.exists_posts: + continue + res.append(raw_post) + self.set_stored_data(target, store) + return res + +class UserCustomFilterMixin(CategoryMixin, abstract=True): + + categories: dict[Category, str] + enable_tag: bool + + @abstractmethod + def get_tags(self, raw_post: RawPost) -> Optional[Collection[Tag]]: + "Return Tag list of given RawPost" + async def filter_user_custom(self, raw_post_list: list[RawPost], cats: list[Category], tags: list[Tag]) -> list[RawPost]: res: list[RawPost] = [] for raw_post in raw_post_list: @@ -139,48 +196,44 @@ class PlatformProto(metaclass=RegistryMeta): res.append(raw_post) return res +class Platform(metaclass=RegistryABCMeta, base=True): + + # schedule_interval: int + schedule_type: Literal['date', 'interval', 'cron'] + schedule_kw: dict + is_common: bool + enabled: bool + name: str -class Platform(PlatformProto): - "platform with target(account), like weibo, bilibili" - - categories: dict[Category, str] - has_target: bool = True - platform_name: str - enable_tag: bool - - def __init__(self): - self.exists_posts = defaultdict(set) - self.inited = dict() - self.reverse_category = {} - self.cache: dict[Any, Post] = {} - for key, val in self.categories.items(): - self.reverse_category[val] = key + @staticmethod + @abstractmethod + async def get_target_name(target: Target) -> Optional[str]: + ... @abstractmethod - async def get_sub_list(self, target: Target) -> list[RawPost]: - "Get post list of the given target" - - async def filter_common(self, target: Target, raw_post_list: list[RawPost]) -> list[RawPost]: - if not self.inited.get(target, False) and plugin_config.hk_reporter_init_filter: - # target not init - for raw_post in raw_post_list: - post_id = self.get_id(raw_post) - self.exists_posts[target].add(post_id) - logger.info('init {}-{} with {}'.format(self.platform_name, target, self.exists_posts[target])) - self.inited[target] = True - return [] - return self._do_filter_common(raw_post_list, self.exists_posts[target]) + async def fetch_new_post(self, target: Target, users: list[UserSubInfo]) -> list[tuple[User, list[Post]]]: + ... +class NewMessage( + Platform, + NewMessageProcessMixin, + UserCustomFilterMixin, + abstract=True + ): + async def fetch_new_post(self, target: Target, users: list[UserSubInfo]) -> list[tuple[User, list[Post]]]: try: post_list = await self.get_sub_list(target) - new_posts = await self.filter_common(target, post_list) + new_posts = await self.filter_common_with_diff(target, post_list) res: list[tuple[User, list[Post]]] = [] if not new_posts: return [] else: for post in new_posts: - logger.info('fetch new post from {} {}: {}'.format(self.platform_name, target, self.get_id(post))) + logger.info('fetch new post from {} {}: {}'.format( + self.platform_name, + target if self.has_target else '-', + self.get_id(post))) for user, category_getter, tag_getter in users: required_tags = tag_getter(target) if self.enable_tag else [] cats = category_getter(target) @@ -189,63 +242,9 @@ class Platform(PlatformProto): for raw_post in user_raw_post: user_post.append(await self._parse_with_cache(raw_post)) res.append((user, user_post)) - self.cache = {} + self.parse_cache = {} return res except httpx.RequestError as err: logger.warning("network connection error: {}, url: {}".format(type(err), err.request.url)) return [] - -class PlatformNoTarget(PlatformProto): - - categories: dict[Category, str] - has_target = False - platform_name: str - enable_tag: bool - - async def get_sub_list(self) -> list[RawPost]: - "Get post list of the given target" - raise NotImplementedError() - - def __init__(self): - self.exists_posts = set() - self.inited = False - self.reverse_category = {} - self.cache: dict[Any, Post] = {} - for key, val in self.categories.items(): - self.reverse_category[val] = key - - async def filter_common(self, raw_post_list: list[RawPost]) -> list[RawPost]: - if not self.inited and plugin_config.hk_reporter_init_filter: - # target not init - for raw_post in raw_post_list: - post_id = self.get_id(raw_post) - self.exists_posts.add(post_id) - logger.info('init {} with {}'.format(self.platform_name, self.exists_posts)) - self.inited = True - return [] - return self._do_filter_common(raw_post_list, self.exists_posts) - - async def fetch_new_post(self, _: Target, users: list[UserSubInfo]) -> list[tuple[User, list[Post]]]: - try: - post_list = await self.get_sub_list() - new_posts = await self.filter_common(post_list) - res: list[tuple[User, list[Post]]] = [] - if not new_posts: - return [] - else: - for post in new_posts: - logger.info('fetch new post from {}: {}'.format(self.platform_name, self.get_id(post))) - for user, category_getter, tag_getter in users: - required_tags = tag_getter(Target('default')) - cats = category_getter(Target('default')) - user_raw_post = await self.filter_user_custom(new_posts, cats, required_tags) - user_post: list[Post] = [] - for raw_post in user_raw_post: - user_post.append(await self._parse_with_cache(raw_post)) - res.append((user, user_post)) - self.cache = {} - return res - except httpx.RequestError as err: - logger.warning("network connection error: {}, url: {}".format(type(err), err.request.url)) - return [] diff --git a/src/plugins/nonebot_hk_reporter/platform/rss.py b/src/plugins/nonebot_hk_reporter/platform/rss.py index 3642045..d8a99c7 100644 --- a/src/plugins/nonebot_hk_reporter/platform/rss.py +++ b/src/plugins/nonebot_hk_reporter/platform/rss.py @@ -7,9 +7,9 @@ import httpx from ..post import Post from ..types import RawPost, Target -from .platform import Platform +from .platform import NewMessage, TargetMixin -class Rss(Platform): +class Rss(NewMessage, TargetMixin): categories = {} enable_tag = False @@ -17,10 +17,11 @@ class Rss(Platform): name = "Rss" enabled = True is_common = True - schedule_interval = 30 + schedule_type = 'interval' + schedule_kw = {'seconds': 30} @staticmethod - async def get_account_name(target: Target) -> Optional[str]: + async def get_target_name(target: Target) -> Optional[str]: async with httpx.AsyncClient() as client: res = await client.get(target, timeout=10.0) feed = feedparser.parse(res.text) diff --git a/src/plugins/nonebot_hk_reporter/platform/wechat.py b/src/plugins/nonebot_hk_reporter/platform/wechat.py index d696826..7c04306 100644 --- a/src/plugins/nonebot_hk_reporter/platform/wechat.py +++ b/src/plugins/nonebot_hk_reporter/platform/wechat.py @@ -9,70 +9,70 @@ import httpx from ..post import Post from ..types import * -from .platform import Platform +# from .platform import Platform -class Wechat(Platform): +# class Wechat(Platform): - categories = {} - enable_tag = False - platform_name = 'wechat' - enabled = False - is_common = False - name = '微信公众号' +# categories = {} +# enable_tag = False +# platform_name = 'wechat' +# enabled = False +# is_common = False +# name = '微信公众号' - @classmethod - def _get_query_url(cls, target: Target): - return 'https://weixin.sogou.com/weixin?type=1&s_from=input&query={}&ie=utf8&_sug_=n&_sug_type_='.format(target) +# @classmethod +# def _get_query_url(cls, target: Target): +# return 'https://weixin.sogou.com/weixin?type=1&s_from=input&query={}&ie=utf8&_sug_=n&_sug_type_='.format(target) - @classmethod - async def _get_target_soup(cls, target: Target) -> Optional[bs]: - target_url = cls._get_query_url(target) - async with httpx.AsyncClient() as client: - res = await client.get(target_url) - soup = bs(res.text, 'html.parser') - blocks = soup.find(class_='news-list2').find_all('li',recursive=False) - for block in blocks: - if block.find(string=[target]): - return block +# @classmethod +# async def _get_target_soup(cls, target: Target) -> Optional[bs]: +# target_url = cls._get_query_url(target) +# async with httpx.AsyncClient() as client: +# res = await client.get(target_url) +# soup = bs(res.text, 'html.parser') +# blocks = soup.find(class_='news-list2').find_all('li',recursive=False) +# for block in blocks: +# if block.find(string=[target]): +# return block - @classmethod - async def get_account_name(cls, target: Target) -> Optional[str]: - if not (block := await cls._get_target_soup(target)): - return None - return block.find('p', class_='tit').find('a').text +# @classmethod +# async def get_account_name(cls, target: Target) -> Optional[str]: +# if not (block := await cls._get_target_soup(target)): +# return None +# return block.find('p', class_='tit').find('a').text - async def get_sub_list(self, target: Target) -> list[RawPost]: - block = await self._get_target_soup(target) - if (last_post_dt := block.find('dt', string='最近文章:')): - post = { - 'title': last_post_dt.find_parent().find('a').text, - 'target': target, - 'page_url': self._get_query_url(target), - 'name': block.find('p', class_='tit').find('a').text - } - return [post] - else: - return [] +# async def get_sub_list(self, target: Target) -> list[RawPost]: +# block = await self._get_target_soup(target) +# if (last_post_dt := block.find('dt', string='最近文章:')): +# post = { +# 'title': last_post_dt.find_parent().find('a').text, +# 'target': target, +# 'page_url': self._get_query_url(target), +# 'name': block.find('p', class_='tit').find('a').text +# } +# return [post] +# else: +# return [] - def get_id(self, post: RawPost) -> Any: - return post['title'] +# def get_id(self, post: RawPost) -> Any: +# return post['title'] - def get_date(self, post: RawPost): - return None +# def get_date(self, post: RawPost): +# return None - def get_tags(self, post: RawPost): - return None +# def get_tags(self, post: RawPost): +# return None - def get_category(self, post: RawPost): - return None +# def get_category(self, post: RawPost): +# return None - async def parse(self, raw_post: RawPost) -> Post: - # TODO get content of post - return Post(target_type='wechat', - text='{}\n详细内容请自行查看公众号'.format(raw_post['title']), - target_name=raw_post['name'], - pics=[], - url='' - ) +# async def parse(self, raw_post: RawPost) -> Post: +# # TODO get content of post +# return Post(target_type='wechat', +# text='{}\n详细内容请自行查看公众号'.format(raw_post['title']), +# target_name=raw_post['name'], +# pics=[], +# url='' +# ) diff --git a/src/plugins/nonebot_hk_reporter/platform/weibo.py b/src/plugins/nonebot_hk_reporter/platform/weibo.py index a084a23..aed0027 100644 --- a/src/plugins/nonebot_hk_reporter/platform/weibo.py +++ b/src/plugins/nonebot_hk_reporter/platform/weibo.py @@ -9,9 +9,9 @@ from nonebot import logger from ..post import Post from ..types import * -from .platform import Platform +from .platform import NewMessage, TargetMixin -class Weibo(Platform): +class Weibo(NewMessage, TargetMixin): categories = { 1: '转发', @@ -23,14 +23,11 @@ class Weibo(Platform): name = '新浪微博' enabled = True is_common = True - schedule_interval = 10 - - def __init__(self): - self.top : dict[Target, RawPost] = dict() - super().__init__() + schedule_type = 'interval' + schedule_kw = {'seconds': 10} @staticmethod - async def get_account_name(target: Target) -> Optional[str]: + async def get_target_name(target: Target) -> Optional[str]: async with httpx.AsyncClient() as client: param = {'containerid': '100505' + target} res = await client.get('https://m.weibo.cn/api/container/getIndex', params=param) @@ -47,7 +44,8 @@ class Weibo(Platform): res_data = json.loads(res.text) if not res_data['ok']: return [] - return res_data['data']['cards'] + custom_filter: Callable[[RawPost], bool] = lambda d: d['card_type'] == 9 + return list(filter(custom_filter, res_data['data']['cards'])) def get_id(self, post: RawPost) -> Any: return post['mblog']['id'] diff --git a/src/plugins/nonebot_hk_reporter/scheduler.py b/src/plugins/nonebot_hk_reporter/scheduler.py index f9a4414..74fbe53 100644 --- a/src/plugins/nonebot_hk_reporter/scheduler.py +++ b/src/plugins/nonebot_hk_reporter/scheduler.py @@ -10,11 +10,12 @@ from .types import UserSubInfo scheduler = AsyncIOScheduler() +@get_driver().on_startup async def _start(): scheduler.configure({"apscheduler.timezone": "Asia/Shanghai"}) scheduler.start() -get_driver().on_startup(_start) +# get_driver().on_startup(_start) async def fetch_and_send(target_type: str): config = Config() @@ -41,10 +42,10 @@ async def fetch_and_send(target_type: str): send_msgs(bot, user.user, user.user_type, await send_post.generate_messages()) for platform_name, platform in platform_manager.items(): - if isinstance(platform.schedule_interval, int): - logger.info(f'start scheduler for {platform_name} with interval {platform.schedule_interval}') + if platform.schedule_type in ['cron', 'interval', 'date']: + logger.info(f'start scheduler for {platform_name} with {platform.schedule_type} {platform.schedule_kw}') scheduler.add_job( - fetch_and_send, 'interval', seconds=platform.schedule_interval, + fetch_and_send, platform.schedule_type, **platform.schedule_kw, args=(platform_name,)) scheduler.add_job(do_send_msgs, 'interval', seconds=0.3) diff --git a/tests/platforms/arknights-detail-675.html b/tests/platforms/arknights-detail-675.html new file mode 100644 index 0000000..d5d8694 --- /dev/null +++ b/tests/platforms/arknights-detail-675.html @@ -0,0 +1,24 @@ + + + + + + + + + + 公告 + + + +
+
+ +
+
+ + diff --git a/tests/platforms/arknights_list_0.json b/tests/platforms/arknights_list_0.json new file mode 100644 index 0000000..c010167 --- /dev/null +++ b/tests/platforms/arknights_list_0.json @@ -0,0 +1,108 @@ +{ + "focusAnnounceId": "677", + "announceList": [ + { + "announceId": "677", + "title": "联锁竞赛预告\n「荷谟伊智境」", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/677.html", + "day": 28, + "month": 6, + "group": "ACTIVITY" + }, + { + "announceId": "676", + "title": "「制作组通讯」\n#12期", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/676.html", + "day": 23, + "month": 6, + "group": "SYSTEM" + }, + { + "announceId": "672", + "title": "时代系列\n复刻限时上架", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/672.html", + "day": 17, + "month": 6, + "group": "ACTIVITY" + }, + { + "announceId": "671", + "title": "生命之地系列\n新装限时上架", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/671.html", + "day": 17, + "month": 6, + "group": "ACTIVITY" + }, + { + "announceId": "670", + "title": "【君影轻灵】\n复刻寻访开启", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/670.html", + "day": 17, + "month": 6, + "group": "ACTIVITY" + }, + { + "announceId": "667", + "title": "沃伦姆德的薄暮\n限时复刻开启", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/667.html", + "day": 17, + "month": 6, + "group": "ACTIVITY" + }, + { + "announceId": "97", + "title": "新人寻访特惠\n必得六星干员", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/97.html", + "day": 30, + "month": 4, + "group": "ACTIVITY" + }, + { + "announceId": "95", + "title": "通关特定关卡\n赠送专属时装", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/95.html", + "day": 30, + "month": 4, + "group": "ACTIVITY" + }, + { + "announceId": "192", + "title": "《明日方舟》\n公测开启说明", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/192.html", + "day": 30, + "month": 4, + "group": "SYSTEM" + }, + { + "announceId": "98", + "title": "《明日方舟》\n公平运营申明", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/98.html", + "day": 30, + "month": 4, + "group": "SYSTEM" + }, + { + "announceId": "94", + "title": "常驻活动介绍", + "isWebUrl": true, + "webUrl": "https://ak-fs.hypergryph.com/announce/IOS/announcement/94.html", + "day": 30, + "month": 4, + "group": "ACTIVITY" + } + ], + "extra": { + "enable": false, + "name": "额外活动" + } +} diff --git a/tests/platforms/arknights_list_1.json b/tests/platforms/arknights_list_1.json new file mode 100644 index 0000000..8493893 --- /dev/null +++ b/tests/platforms/arknights_list_1.json @@ -0,0 +1 @@ +{"focusAnnounceId":"677","announceList":[{"announceId":"677","title":"联锁竞赛预告\n「荷谟伊智境」","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/677.html","day":28,"month":6,"group":"ACTIVITY"},{"announceId":"675","title":"特定干员\n限时出率上升","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/675.html","day":24,"month":6,"group":"ACTIVITY"},{"announceId":"676","title":"「制作组通讯」\n#12期","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/676.html","day":23,"month":6,"group":"SYSTEM"},{"announceId":"672","title":"时代系列\n复刻限时上架","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/672.html","day":17,"month":6,"group":"ACTIVITY"},{"announceId":"671","title":"生命之地系列\n新装限时上架","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/671.html","day":17,"month":6,"group":"ACTIVITY"},{"announceId":"670","title":"【君影轻灵】\n复刻寻访开启","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/670.html","day":17,"month":6,"group":"ACTIVITY"},{"announceId":"667","title":"沃伦姆德的薄暮\n限时复刻开启","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/667.html","day":17,"month":6,"group":"ACTIVITY"},{"announceId":"97","title":"新人寻访特惠\n必得六星干员","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/97.html","day":30,"month":4,"group":"ACTIVITY"},{"announceId":"95","title":"通关特定关卡\n赠送专属时装","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/95.html","day":30,"month":4,"group":"ACTIVITY"},{"announceId":"192","title":"《明日方舟》\n公测开启说明","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/192.html","day":30,"month":4,"group":"SYSTEM"},{"announceId":"98","title":"《明日方舟》\n公平运营申明","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/98.html","day":30,"month":4,"group":"SYSTEM"},{"announceId":"94","title":"常驻活动介绍","isWebUrl":true,"webUrl":"https://ak-fs.hypergryph.com/announce/IOS/announcement/94.html","day":30,"month":4,"group":"ACTIVITY"}],"extra":{"enable":false,"name":"额外活动"}} \ No newline at end of file diff --git a/tests/platforms/test_arknights.py b/tests/platforms/test_arknights.py new file mode 100644 index 0000000..6f6db9d --- /dev/null +++ b/tests/platforms/test_arknights.py @@ -0,0 +1,49 @@ +import pytest +import typing +import respx +from httpx import Response +import feedparser + +if typing.TYPE_CHECKING: + import sys + sys.path.append('./src/plugins') + import nonebot_hk_reporter + +from .utils import get_json, get_file + +@pytest.fixture +def arknights(plugin_module: 'nonebot_hk_reporter'): + return plugin_module.platform.platform_manager['arknights'] + +@pytest.fixture(scope='module') +def arknights_list_0(): + return get_json('arknights_list_0.json') + +@pytest.fixture(scope='module') +def arknights_list_1(): + return get_json('arknights_list_1.json') + +@pytest.mark.asyncio +@respx.mock +async def test_fetch_new(arknights, dummy_user_subinfo, arknights_list_0, arknights_list_1): + ak_list_router = respx.get("http://ak-fs.hypergryph.com/announce/IOS/announcement.meta.json") + detail_router = respx.get("https://ak-fs.hypergryph.com/announce/IOS/announcement/675.html") + ak_list_router.mock(return_value=Response(200, json=arknights_list_0)) + detail_router.mock(return_value=Response(200, text=get_file('arknights-detail-675.html'))) + target = '' + res = await arknights.fetch_new_post(target, [dummy_user_subinfo]) + assert(ak_list_router.called) + assert(len(res) == 0) + assert(not detail_router.called) + mock_data = arknights_list_1 + ak_list_router.mock(return_value=Response(200, json=mock_data)) + res3 = await arknights.fetch_new_post(target, [dummy_user_subinfo]) + assert(len(res3[0][1]) == 1) + assert(detail_router.called) + post = res3[0][1][0] + assert(post.target_type == 'arknights') + assert(post.text == '') + assert(post.url == '') + assert(post.target_name == '明日方舟游戏内公告') + assert(len(post.pics) == 1) + assert(post.pics == ['https://ak-fs.hypergryph.com/announce/images/20210623/e6f49aeb9547a2278678368a43b95b07.jpg']) diff --git a/tests/platforms/test_platform.py b/tests/platforms/test_platform.py new file mode 100644 index 0000000..086ed6e --- /dev/null +++ b/tests/platforms/test_platform.py @@ -0,0 +1,232 @@ +import sys +import typing +from typing import Any + +import pytest + +if typing.TYPE_CHECKING: + import sys + sys.path.append('./src/plugins') + import nonebot_hk_reporter + from nonebot_hk_reporter.types import * + from nonebot_hk_reporter.post import Post + +from time import time +now = time() +passed = now - 3 * 60 * 60 + +raw_post_list_1 = [ + {'id': 1, 'text': 'p1', 'date': now, 'tags': ['tag1'], 'category': 1} + ] + +raw_post_list_2 = raw_post_list_1 + [ + {'id': 2, 'text': 'p2', 'date': now, 'tags': ['tag1'], 'category': 1}, + {'id': 3, 'text': 'p3', 'date': now, 'tags': ['tag2'], 'category': 2}, + {'id': 4, 'text': 'p4', 'date': now, 'tags': ['tag2'], 'category': 3} + ] + +@pytest.fixture +def dummy_user(plugin_module: 'nonebot_hk_reporter'): + user = plugin_module.types.User('123', 'group') + return user + +@pytest.fixture +def user_info_factory(plugin_module: 'nonebot_hk_reporter', dummy_user): + def _user_info(category_getter, tag_getter): + return plugin_module.types.UserSubInfo(dummy_user, category_getter, tag_getter) + return _user_info + +@pytest.fixture +def mock_platform_without_cats_tags(plugin_module: 'nonebot_hk_reporter'): + class MockPlatform(plugin_module.platform.platform.NewMessage, + plugin_module.platform.platform.TargetMixin): + + platform_name = 'mock_platform' + name = 'Mock Platform' + enabled = True + is_common = True + schedule_interval = 10 + enable_tag = False + categories = {} + + def __init__(self): + self.sub_index = 0 + super().__init__() + + @staticmethod + async def get_target_name(_: 'Target'): + return 'MockPlatform' + + def get_id(self, post: 'RawPost') -> Any: + return post['id'] + + def get_date(self, raw_post: 'RawPost') -> float: + return raw_post['date'] + + async def parse(self, raw_post: 'RawPost') -> 'Post': + return plugin_module.post.Post('mock_platform', raw_post['text'], 'http://t.tt/' + str(self.get_id(raw_post)), target_name='Mock') + + async def get_sub_list(self, _: 'Target'): + if self.sub_index == 0: + self.sub_index += 1 + return raw_post_list_1 + else: + return raw_post_list_2 + + return MockPlatform() + +@pytest.fixture +def mock_platform(plugin_module: 'nonebot_hk_reporter'): + class MockPlatform(plugin_module.platform.platform.NewMessage, + plugin_module.platform.platform.TargetMixin): + + platform_name = 'mock_platform' + name = 'Mock Platform' + enabled = True + is_common = True + schedule_interval = 10 + enable_tag = True + categories = { + 1: '转发', + 2: '视频', + } + def __init__(self): + self.sub_index = 0 + super().__init__() + + @staticmethod + async def get_target_name(_: 'Target'): + return 'MockPlatform' + + def get_id(self, post: 'RawPost') -> Any: + return post['id'] + + def get_date(self, raw_post: 'RawPost') -> float: + return raw_post['date'] + + def get_tags(self, raw_post: 'RawPost') -> list['Tag']: + return raw_post['tags'] + + def get_category(self, raw_post: 'RawPost') -> 'Category': + return raw_post['category'] + + async def parse(self, raw_post: 'RawPost') -> 'Post': + return plugin_module.post.Post('mock_platform', raw_post['text'], 'http://t.tt/' + str(self.get_id(raw_post)), target_name='Mock') + + async def get_sub_list(self, _: 'Target'): + if self.sub_index == 0: + self.sub_index += 1 + return raw_post_list_1 + else: + return raw_post_list_2 + + return MockPlatform() + +@pytest.fixture +def mock_platform_no_target(plugin_module: 'nonebot_hk_reporter'): + class MockPlatform(plugin_module.platform.platform.NewMessage, + plugin_module.platform.platform.NoTargetMixin): + + platform_name = 'mock_platform' + name = 'Mock Platform' + enabled = True + is_common = True + schedule_interval = 10 + enable_tag = True + categories = { + 1: '转发', + 2: '视频', + 3: '不支持' + } + def __init__(self): + self.sub_index = 0 + super().__init__() + + @staticmethod + async def get_target_name(_: 'Target'): + return 'MockPlatform' + + def get_id(self, post: 'RawPost') -> Any: + return post['id'] + + def get_date(self, raw_post: 'RawPost') -> float: + return raw_post['date'] + + def get_tags(self, raw_post: 'RawPost') -> list['Tag']: + return raw_post['tags'] + + def get_category(self, raw_post: 'RawPost') -> 'Category': + if raw_post['category'] == 3: + raise plugin_module.platform.platform.CategoryNotSupport() + return raw_post['category'] + + async def parse(self, raw_post: 'RawPost') -> 'Post': + return plugin_module.post.Post('mock_platform', raw_post['text'], 'http://t.tt/' + str(self.get_id(raw_post)), target_name='Mock') + + async def get_sub_list(self, _: 'Target'): + if self.sub_index == 0: + self.sub_index += 1 + return raw_post_list_1 + else: + return raw_post_list_2 + + return MockPlatform() + +@pytest.mark.asyncio +async def test_new_message_target_without_cats_tags(mock_platform_without_cats_tags, user_info_factory): + res1 = await mock_platform_without_cats_tags.fetch_new_post('dummy', [user_info_factory(lambda _: [1,2], lambda _: [])]) + assert(len(res1) == 0) + res2 = await mock_platform_without_cats_tags.fetch_new_post('dummy', [ + user_info_factory(lambda _: [], lambda _: []), + ]) + assert(len(res2) == 1) + posts_1 = res2[0][1] + assert(len(posts_1) == 3) + id_set_1 = set(map(lambda x: x.text, posts_1)) + assert('p2' in id_set_1 and 'p3' in id_set_1 and 'p4' in id_set_1) + +@pytest.mark.asyncio +async def test_new_message_target(mock_platform, user_info_factory): + res1 = await mock_platform.fetch_new_post('dummy', [user_info_factory(lambda _: [1,2], lambda _: [])]) + assert(len(res1) == 0) + res2 = await mock_platform.fetch_new_post('dummy', [ + user_info_factory(lambda _: [1,2], lambda _: []), + user_info_factory(lambda _: [1], lambda _: []), + user_info_factory(lambda _: [1,2], lambda _: ['tag1']) + ]) + assert(len(res2) == 3) + posts_1 = res2[0][1] + posts_2 = res2[1][1] + posts_3 = res2[2][1] + assert(len(posts_1) == 2) + assert(len(posts_2) == 1) + assert(len(posts_3) == 1) + id_set_1 = set(map(lambda x: x.text, posts_1)) + id_set_2 = set(map(lambda x: x.text, posts_2)) + id_set_3 = set(map(lambda x: x.text, posts_3)) + assert('p2' in id_set_1 and 'p3' in id_set_1) + assert('p2' in id_set_2) + assert('p2' in id_set_3) + +@pytest.mark.asyncio +async def test_new_message_no_target(mock_platform_no_target, user_info_factory): + res1 = await mock_platform_no_target.fetch_new_post('dummy', [user_info_factory(lambda _: [1,2], lambda _: [])]) + assert(len(res1) == 0) + res2 = await mock_platform_no_target.fetch_new_post('dummy', [ + user_info_factory(lambda _: [1,2], lambda _: []), + user_info_factory(lambda _: [1], lambda _: []), + user_info_factory(lambda _: [1,2], lambda _: ['tag1']) + ]) + assert(len(res2) == 3) + posts_1 = res2[0][1] + posts_2 = res2[1][1] + posts_3 = res2[2][1] + assert(len(posts_1) == 2) + assert(len(posts_2) == 1) + assert(len(posts_3) == 1) + id_set_1 = set(map(lambda x: x.text, posts_1)) + id_set_2 = set(map(lambda x: x.text, posts_2)) + id_set_3 = set(map(lambda x: x.text, posts_3)) + assert('p2' in id_set_1 and 'p3' in id_set_1) + assert('p2' in id_set_2) + assert('p2' in id_set_3) diff --git a/tests/platforms/test_weibo.py b/tests/platforms/test_weibo.py index 8beaf73..d19337c 100644 --- a/tests/platforms/test_weibo.py +++ b/tests/platforms/test_weibo.py @@ -23,7 +23,7 @@ def weibo_ak_list_1(): @pytest.mark.asyncio async def test_get_name(weibo): - name = await weibo.get_account_name('6279793937') + name = await weibo.get_target_name('6279793937') assert(name == "明日方舟Arknights") @pytest.mark.asyncio @@ -40,6 +40,7 @@ async def test_fetch_new(weibo, dummy_user_subinfo): assert(not detail_router.called) mock_data = get_json('weibo_ak_list_1.json') ak_list_router.mock(return_value=Response(200, json=mock_data)) + # import ipdb; ipdb.set_trace() res2 = await weibo.fetch_new_post(target, [dummy_user_subinfo]) assert(len(res2) == 0) mock_data['data']['cards'][1]['mblog']['created_at'] = \ @@ -80,7 +81,7 @@ def test_tag(weibo, weibo_ak_list_1): assert(weibo.get_tags(raw_post) == ['明日方舟', '音律联觉']) @pytest.mark.asyncio -async def test_rsshub_compare(weibo, dummy_user_subinfo): +async def test_rsshub_compare(weibo): target = '6279793937' raw_posts = filter(weibo.filter_platform_custom, await weibo.get_sub_list(target)) posts = [] From d0b29abed753918dded0ab1f85eeae16d60726f5 Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Tue, 29 Jun 2021 11:14:46 +0800 Subject: [PATCH 02/11] add compare marker --- pyproject.toml | 5 +++++ tests/platforms/test_weibo.py | 1 + 2 files changed, 6 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index c53998d..5d269f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,3 +45,8 @@ build-backend = "poetry.masonry.api" name = "aliyun" url = "https://mirrors.aliyun.com/pypi/simple/" default = true + +[tool.pytest.ini_options] +markers = [ + "compare: compare fetching result with rsshub" +] diff --git a/tests/platforms/test_weibo.py b/tests/platforms/test_weibo.py index d19337c..b8c58c1 100644 --- a/tests/platforms/test_weibo.py +++ b/tests/platforms/test_weibo.py @@ -81,6 +81,7 @@ def test_tag(weibo, weibo_ak_list_1): assert(weibo.get_tags(raw_post) == ['明日方舟', '音律联觉']) @pytest.mark.asyncio +@pytest.mark.compare async def test_rsshub_compare(weibo): target = '6279793937' raw_posts = filter(weibo.filter_platform_custom, await weibo.get_sub_list(target)) From 9e6a9228f61d1dedcd4153b0d0d54ff2550baec3 Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Tue, 29 Jun 2021 19:43:06 +0800 Subject: [PATCH 03/11] Add status chage and test --- .../platform/monster_siren.py | 2 +- .../nonebot_hk_reporter/platform/platform.py | 75 +++++++++++++++---- tests/platforms/monster-siren_list_0.json | 63 ++++++++++++++++ tests/platforms/monster-siren_list_1.json | 1 + tests/platforms/test_monster-siren.py | 44 +++++++++++ tests/platforms/test_platform.py | 70 ++++++++++++++++- 6 files changed, 238 insertions(+), 17 deletions(-) create mode 100644 tests/platforms/monster-siren_list_0.json create mode 100644 tests/platforms/monster-siren_list_1.json create mode 100644 tests/platforms/test_monster-siren.py diff --git a/src/plugins/nonebot_hk_reporter/platform/monster_siren.py b/src/plugins/nonebot_hk_reporter/platform/monster_siren.py index 7d0044e..3768e33 100644 --- a/src/plugins/nonebot_hk_reporter/platform/monster_siren.py +++ b/src/plugins/nonebot_hk_reporter/platform/monster_siren.py @@ -20,7 +20,7 @@ class MonsterSiren(NewMessage, NoTargetMixin): async def get_target_name(_) -> str: return '塞壬唱片新闻' - async def get_sub_list(self) -> list[RawPost]: + async def get_sub_list(self, _) -> list[RawPost]: async with httpx.AsyncClient() as client: raw_data = await client.get('https://monster-siren.hypergryph.com/api/news') return raw_data.json()['data']['list'] diff --git a/src/plugins/nonebot_hk_reporter/platform/platform.py b/src/plugins/nonebot_hk_reporter/platform/platform.py index e5cd769..b1ecf12 100644 --- a/src/plugins/nonebot_hk_reporter/platform/platform.py +++ b/src/plugins/nonebot_hk_reporter/platform/platform.py @@ -84,7 +84,14 @@ class CategoryMixin(metaclass=RegistryABCMeta, abstract=True): "Return category of given Rawpost" raise NotImplementedError() -class MessageProcessMixin(PlaformNameMixin, CategoryMixin, abstract=True): +class ParsePostMixin(metaclass=RegistryABCMeta, abstract=True): + + @abstractmethod + async def parse(self, raw_post: RawPost) -> Post: + "parse RawPost into post" + ... + +class MessageProcessMixin(PlaformNameMixin, CategoryMixin, ParsePostMixin, abstract=True): "General message process fetch, parse, filter progress" def __init__(self): @@ -95,10 +102,6 @@ class MessageProcessMixin(PlaformNameMixin, CategoryMixin, abstract=True): def get_id(self, post: RawPost) -> Any: "Get post id of given RawPost" - @abstractmethod - async def parse(self, raw_post: RawPost) -> Post: - "parse RawPost into post" - ... async def _parse_with_cache(self, raw_post: RawPost) -> Post: post_id = self.get_id(raw_post) @@ -168,7 +171,7 @@ class NewMessageProcessMixin(StorageMixinProto, MessageProcessMixin, abstract=Tr self.set_stored_data(target, store) return res -class UserCustomFilterMixin(CategoryMixin, abstract=True): +class UserCustomFilterMixin(CategoryMixin, ParsePostMixin, abstract=True): categories: dict[Category, str] enable_tag: bool @@ -196,6 +199,21 @@ class UserCustomFilterMixin(CategoryMixin, abstract=True): res.append(raw_post) return res + async def dispatch_user_post(self, target: Target, new_posts: list[RawPost], users: list[UserSubInfo]) -> list[tuple[User, list[Post]]]: + res: list[tuple[User, list[Post]]] = [] + for user, category_getter, tag_getter in users: + required_tags = tag_getter(target) if self.enable_tag else [] + cats = category_getter(target) + user_raw_post = await self.filter_user_custom(new_posts, cats, required_tags) + user_post: list[Post] = [] + for raw_post in user_raw_post: + if isinstance(self, MessageProcessMixin): + user_post.append(await self._parse_with_cache(raw_post)) + else: + user_post.append(await self.parse(raw_post)) + res.append((user, user_post)) + return res + class Platform(metaclass=RegistryABCMeta, base=True): # schedule_interval: int @@ -220,12 +238,12 @@ class NewMessage( UserCustomFilterMixin, abstract=True ): + "Fetch a list of messages, filter the new messages, dispatch it to different users" async def fetch_new_post(self, target: Target, users: list[UserSubInfo]) -> list[tuple[User, list[Post]]]: try: post_list = await self.get_sub_list(target) new_posts = await self.filter_common_with_diff(target, post_list) - res: list[tuple[User, list[Post]]] = [] if not new_posts: return [] else: @@ -234,17 +252,44 @@ class NewMessage( self.platform_name, target if self.has_target else '-', self.get_id(post))) - for user, category_getter, tag_getter in users: - required_tags = tag_getter(target) if self.enable_tag else [] - cats = category_getter(target) - user_raw_post = await self.filter_user_custom(new_posts, cats, required_tags) - user_post: list[Post] = [] - for raw_post in user_raw_post: - user_post.append(await self._parse_with_cache(raw_post)) - res.append((user, user_post)) + res = await self.dispatch_user_post(target, new_posts, users) self.parse_cache = {} return res except httpx.RequestError as err: logger.warning("network connection error: {}, url: {}".format(type(err), err.request.url)) return [] +class StatusChange( + Platform, + StorageMixinProto, + PlaformNameMixin, + UserCustomFilterMixin, + abstract=True + ): + "Watch a status, and fire a post when status changes" + + @abstractmethod + async def get_status(self, target: Target) -> Any: + ... + + @abstractmethod + def compare_status(self, target: Target, old_status, new_status) -> Optional[RawPost]: + ... + + @abstractmethod + async def parse(self, raw_post: RawPost) -> Post: + ... + + async def fetch_new_post(self, target: Target, users: list[UserSubInfo]) -> list[tuple[User, list[Post]]]: + try: + new_status = await self.get_status(target) + res = [] + if old_status := self.get_stored_data(target): + diff = self.compare_status(target, old_status, new_status) + if diff: + res = await self.dispatch_user_post(target, [diff], users) + self.set_stored_data(target, new_status) + return res + except httpx.RequestError as err: + logger.warning("network connection error: {}, url: {}".format(type(err), err.request.url)) + return [] diff --git a/tests/platforms/monster-siren_list_0.json b/tests/platforms/monster-siren_list_0.json new file mode 100644 index 0000000..b0c40be --- /dev/null +++ b/tests/platforms/monster-siren_list_0.json @@ -0,0 +1,63 @@ +{ + "code": 0, + "msg": "", + "data": { + "list": [ + { + "cid": "114091", + "title": "#AUS小屋", + "cate": 8, + "date": "2021-06-23" + }, + { + "cid": "027726", + "title": "「音律联觉原声EP」正式上架", + "cate": 1, + "date": "2021-06-12" + }, + { + "cid": "750459", + "title": "「ManiFesto:」MV正式公开", + "cate": 1, + "date": "2021-06-08" + }, + { + "cid": "241304", + "title": "「Real Me」正式上架", + "cate": 1, + "date": "2021-06-01" + }, + { + "cid": "578835", + "title": "#D.D.D.PHOTO", + "cate": 8, + "date": "2021-05-24" + }, + { + "cid": "489188", + "title": "#AUS小屋", + "cate": 8, + "date": "2021-05-19" + }, + { + "cid": "992677", + "title": "「Immutable」正式上架", + "cate": 1, + "date": "2021-05-02" + }, + { + "cid": "605962", + "title": "「Voices」正式上架", + "cate": 1, + "date": "2021-05-01" + }, + { + "cid": "336213", + "title": "#D.D.D.PHOTO", + "cate": 8, + "date": "2021-04-28" + } + ], + "end": false + } +} diff --git a/tests/platforms/monster-siren_list_1.json b/tests/platforms/monster-siren_list_1.json new file mode 100644 index 0000000..9f65976 --- /dev/null +++ b/tests/platforms/monster-siren_list_1.json @@ -0,0 +1 @@ +{"code":0,"msg":"","data":{"list":[{"cid":"241303","title":"#D.D.D.PHOTO","cate":8,"date":"2021-06-29"},{"cid":"114091","title":"#AUS小屋","cate":8,"date":"2021-06-23"},{"cid":"027726","title":"「音律联觉原声EP」正式上架","cate":1,"date":"2021-06-12"},{"cid":"750459","title":"「ManiFesto:」MV正式公开","cate":1,"date":"2021-06-08"},{"cid":"241304","title":"「Real Me」正式上架","cate":1,"date":"2021-06-01"},{"cid":"578835","title":"#D.D.D.PHOTO","cate":8,"date":"2021-05-24"},{"cid":"489188","title":"#AUS小屋","cate":8,"date":"2021-05-19"},{"cid":"992677","title":"「Immutable」正式上架","cate":1,"date":"2021-05-02"},{"cid":"605962","title":"「Voices」正式上架","cate":1,"date":"2021-05-01"},{"cid":"336213","title":"#D.D.D.PHOTO","cate":8,"date":"2021-04-28"}],"end":false}} \ No newline at end of file diff --git a/tests/platforms/test_monster-siren.py b/tests/platforms/test_monster-siren.py new file mode 100644 index 0000000..832271f --- /dev/null +++ b/tests/platforms/test_monster-siren.py @@ -0,0 +1,44 @@ +import pytest +import typing +import respx +from httpx import Response +import feedparser + +if typing.TYPE_CHECKING: + import sys + sys.path.append('./src/plugins') + import nonebot_hk_reporter + +from .utils import get_json, get_file + +@pytest.fixture +def monster_siren(plugin_module: 'nonebot_hk_reporter'): + return plugin_module.platform.platform_manager['monster-siren'] + +@pytest.fixture(scope='module') +def monster_siren_list_0(): + return get_json('monster-siren_list_0.json') + +@pytest.fixture(scope='module') +def monster_siren_list_1(): + return get_json('monster-siren_list_1.json') + +@pytest.mark.asyncio +@respx.mock +async def test_fetch_new(monster_siren, dummy_user_subinfo, monster_siren_list_0, monster_siren_list_1): + ak_list_router = respx.get("https://monster-siren.hypergryph.com/api/news") + ak_list_router.mock(return_value=Response(200, json=monster_siren_list_0)) + target = '' + res = await monster_siren.fetch_new_post(target, [dummy_user_subinfo]) + assert(ak_list_router.called) + assert(len(res) == 0) + mock_data = monster_siren_list_1 + ak_list_router.mock(return_value=Response(200, json=mock_data)) + res3 = await monster_siren.fetch_new_post(target, [dummy_user_subinfo]) + assert(len(res3[0][1]) == 1) + post = res3[0][1][0] + assert(post.target_type == 'monster-siren') + assert(post.text == '#D.D.D.PHOTO') + assert(post.url == 'https://monster-siren.hypergryph.com/info/241303') + assert(post.target_name == '塞壬唱片新闻') + assert(len(post.pics) == 0) diff --git a/tests/platforms/test_platform.py b/tests/platforms/test_platform.py index 086ed6e..c6c42b0 100644 --- a/tests/platforms/test_platform.py +++ b/tests/platforms/test_platform.py @@ -1,6 +1,6 @@ import sys import typing -from typing import Any +from typing import Any, Optional import pytest @@ -172,6 +172,52 @@ def mock_platform_no_target(plugin_module: 'nonebot_hk_reporter'): return MockPlatform() +@pytest.fixture +def mock_status_change(plugin_module: 'nonebot_hk_reporter'): + class MockPlatform(plugin_module.platform.platform.StatusChange, + plugin_module.platform.platform.NoTargetMixin): + + platform_name = 'mock_platform' + name = 'Mock Platform' + enabled = True + is_common = True + enable_tag = False + schedule_type = 'interval' + schedule_kw = {'seconds': 10} + categories = { + 1: '转发', + 2: '视频', + } + def __init__(self): + self.sub_index = 0 + super().__init__() + + async def get_status(self, _: 'Target'): + if self.sub_index == 0: + self.sub_index += 1 + return {'s': False} + elif self.sub_index == 1: + self.sub_index += 1 + return {'s': True} + else: + return {'s': False} + + def compare_status(self, target, old_status, new_status) -> Optional['RawPost']: + if old_status['s'] == False and new_status['s'] == True: + return {'text': 'on', 'cat': 1} + elif old_status['s'] == True and new_status['s'] == False: + return {'text': 'off', 'cat': 2} + return None + + async def parse(self, raw_post) -> 'Post': + return plugin_module.post.Post('mock_status', raw_post['text'], '') + + def get_category(self, raw_post): + return raw_post['cat'] + + return MockPlatform() + + @pytest.mark.asyncio async def test_new_message_target_without_cats_tags(mock_platform_without_cats_tags, user_info_factory): res1 = await mock_platform_without_cats_tags.fetch_new_post('dummy', [user_info_factory(lambda _: [1,2], lambda _: [])]) @@ -230,3 +276,25 @@ async def test_new_message_no_target(mock_platform_no_target, user_info_factory) assert('p2' in id_set_1 and 'p3' in id_set_1) assert('p2' in id_set_2) assert('p2' in id_set_3) + +@pytest.mark.asyncio +async def test_status_change(mock_status_change, user_info_factory): + res1 = await mock_status_change.fetch_new_post('dummy', [user_info_factory(lambda _: [1,2], lambda _: [])]) + assert(len(res1) == 0) + res2 = await mock_status_change.fetch_new_post('dummy', [ + user_info_factory(lambda _: [1,2], lambda _:[]) + ]) + assert(len(res2) == 1) + posts = res2[0][1] + assert(len(posts) == 1) + assert(posts[0].text == 'on') + res3 = await mock_status_change.fetch_new_post('dummy', [ + user_info_factory(lambda _: [1,2], lambda _: []), + user_info_factory(lambda _: [1], lambda _: []), + ]) + assert(len(res3) == 2) + assert(len(res3[0][1]) == 1) + assert(res3[0][1][0].text == 'off') + assert(len(res3[1][1]) == 0) + res4 = await mock_status_change.fetch_new_post('dummy', [user_info_factory(lambda _: [1,2], lambda _: [])]) + assert(len(res4) == 0) From c8f5adc96e3a8bc5a635f8e3a8cc7704be6ffb44 Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Wed, 30 Jun 2021 09:15:07 +0800 Subject: [PATCH 04/11] fix emergency bug --- src/plugins/nonebot_hk_reporter/platform/platform.py | 1 + tests/platforms/test_platform.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/plugins/nonebot_hk_reporter/platform/platform.py b/src/plugins/nonebot_hk_reporter/platform/platform.py index b1ecf12..bc2e777 100644 --- a/src/plugins/nonebot_hk_reporter/platform/platform.py +++ b/src/plugins/nonebot_hk_reporter/platform/platform.py @@ -168,6 +168,7 @@ class NewMessageProcessMixin(StorageMixinProto, MessageProcessMixin, abstract=Tr if post_id in store.exists_posts: continue res.append(raw_post) + store.exists_posts.add(post_id) self.set_stored_data(target, store) return res diff --git a/tests/platforms/test_platform.py b/tests/platforms/test_platform.py index c6c42b0..468eb6d 100644 --- a/tests/platforms/test_platform.py +++ b/tests/platforms/test_platform.py @@ -276,6 +276,8 @@ async def test_new_message_no_target(mock_platform_no_target, user_info_factory) assert('p2' in id_set_1 and 'p3' in id_set_1) assert('p2' in id_set_2) assert('p2' in id_set_3) + res3 = await mock_platform_no_target.fetch_new_post('dummy', [user_info_factory(lambda _: [1,2], lambda _: [])]) + assert(len(res3) == 0) @pytest.mark.asyncio async def test_status_change(mock_status_change, user_info_factory): From 145dcf870a2cbab552d12b459f05af09ec330a57 Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Wed, 30 Jun 2021 09:17:10 +0800 Subject: [PATCH 05/11] strip text --- src/plugins/nonebot_hk_reporter/platform/rss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/nonebot_hk_reporter/platform/rss.py b/src/plugins/nonebot_hk_reporter/platform/rss.py index d8a99c7..214b2ed 100644 --- a/src/plugins/nonebot_hk_reporter/platform/rss.py +++ b/src/plugins/nonebot_hk_reporter/platform/rss.py @@ -44,6 +44,6 @@ class Rss(NewMessage, TargetMixin): async def parse(self, raw_post: RawPost) -> Post: soup = bs(raw_post.description, 'html.parser') - text = soup.text + text = soup.text.strip() pics = list(map(lambda x: x.attrs['src'], soup('img'))) return Post('rss', text=text, url=raw_post.link, pics=pics, target_name=raw_post['_target_name']) From 9f08e3cf16c01aa0ae089685fc60ec9a5132281c Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Thu, 1 Jul 2021 09:12:09 +0800 Subject: [PATCH 06/11] fix bug --- src/plugins/nonebot_hk_reporter/platform/platform.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugins/nonebot_hk_reporter/platform/platform.py b/src/plugins/nonebot_hk_reporter/platform/platform.py index bc2e777..7f73087 100644 --- a/src/plugins/nonebot_hk_reporter/platform/platform.py +++ b/src/plugins/nonebot_hk_reporter/platform/platform.py @@ -177,6 +177,12 @@ class UserCustomFilterMixin(CategoryMixin, ParsePostMixin, abstract=True): categories: dict[Category, str] enable_tag: bool + def __init__(self): + super().__init__() + self.reverse_category = {} + for key, val in self.categories.items(): + self.reverse_category[val] = key + @abstractmethod def get_tags(self, raw_post: RawPost) -> Optional[Collection[Tag]]: "Return Tag list of given RawPost" From 6b81e2e7c1f778872ae7bc0984eeb68577753c3c Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Tue, 6 Jul 2021 13:30:27 +0800 Subject: [PATCH 07/11] add weibo text category, support super topic --- src/plugins/nonebot_hk_reporter/platform/weibo.py | 14 ++++++++++++-- tests/platforms/test_weibo.py | 15 +++++++++++++++ tests/platforms/weibo_ys_list_0.json | 1 + 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 tests/platforms/weibo_ys_list_0.json diff --git a/src/plugins/nonebot_hk_reporter/platform/weibo.py b/src/plugins/nonebot_hk_reporter/platform/weibo.py index aed0027..57d39ae 100644 --- a/src/plugins/nonebot_hk_reporter/platform/weibo.py +++ b/src/plugins/nonebot_hk_reporter/platform/weibo.py @@ -17,6 +17,7 @@ class Weibo(NewMessage, TargetMixin): 1: '转发', 2: '视频', 3: '图文', + 4: '文字', } enable_tag = True platform_name = 'weibo' @@ -61,21 +62,30 @@ class Weibo(NewMessage, TargetMixin): "Return Tag list of given RawPost" text = raw_post['mblog']['text'] soup = bs(text, 'html.parser') - return list(map( + res = list(map( lambda x: x[1:-1], filter( lambda s: s[0] == '#' and s[-1] == '#', map(lambda x:x.text, soup.find_all('span', class_='surl-text')) ) )) + super_topic_img = soup.find('img', src=re.compile(r'timeline_card_small_super_default')) + if super_topic_img: + try: + res.append(super_topic_img.parent.parent.find('span', class_='surl-text').text + '超话') + except: + logger.info('super_topic extract error: {}'.format(text)) + return res def get_category(self, raw_post: RawPost) -> Category: if raw_post['mblog'].get('retweeted_status'): return Category(1) elif raw_post['mblog'].get('page_info') and raw_post['mblog']['page_info'].get('type') == 'video': return Category(2) - else: + elif raw_post['mblog'].get('pics'): return Category(3) + else: + return Category(4) def _get_text(self, raw_text: str) -> str: text = raw_text.replace('
', '\n') diff --git a/tests/platforms/test_weibo.py b/tests/platforms/test_weibo.py index b8c58c1..f7cc715 100644 --- a/tests/platforms/test_weibo.py +++ b/tests/platforms/test_weibo.py @@ -62,9 +62,12 @@ async def test_classification(weibo): tuwen = mock_data['data']['cards'][1] retweet = mock_data['data']['cards'][3] video = mock_data['data']['cards'][0] + mock_data_ys = get_json('weibo_ys_list_0.json') + text = mock_data_ys['data']['cards'][2] assert(weibo.get_category(retweet) == 1) assert(weibo.get_category(video) == 2) assert(weibo.get_category(tuwen) == 3) + assert(weibo.get_category(text) == 4) @pytest.mark.asyncio @respx.mock @@ -93,3 +96,15 @@ async def test_rsshub_compare(weibo): for entry in feedres.entries[:5]: # print(entry) assert(entry.link in url_set) + +test_post = { + "mblog": { + "text": "#刚出生的小羊驼长啥样#
小羊驼三三来也[好喜欢]
小羊驼三三 ", + "bid": "KnssqeqKK" + } +} +def test_chaohua_tag(weibo): + tags = weibo.get_tags(test_post) + assert('刚出生的小羊驼长啥样' in tags) + assert('小羊驼三三超话' in tags) + diff --git a/tests/platforms/weibo_ys_list_0.json b/tests/platforms/weibo_ys_list_0.json new file mode 100644 index 0000000..dbf454a --- /dev/null +++ b/tests/platforms/weibo_ys_list_0.json @@ -0,0 +1 @@ +{"ok":1,"data":{"cardlistInfo":{"containerid":"1076031907518591","v_p":42,"show_style":1,"total":582,"page":2},"cards":[{"card_type":9,"itemid":"1076031907518591_-_4640329573078415","scheme":"https:\/\/m.weibo.cn\/status\/KgV9HcUPR?mblogid=KgV9HcUPR&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Mon May 24 10:44:06 +0800 2021","id":"4640329573078415","mid":"4640329573078415","edit_count":3,"can_edit":false,"edit_at":"Fri May 28 10:38:12 +0800 2021","version":3,"show_additional_indication":0,"text":"\u6c38\u8fdc\u7231LPL\uff0c\u4e5f\u6c38\u8fdc\u7231\u82f1\u96c4\u8054\u76df\uff01
\u8fd9\u662f\u6211\u6362\u7684\u7b2c\u591a\u5c11\u4e2a\u7f6e\u9876\u5df2\u7ecf\u8bb0\u4e0d\u6e05\u4e86\uff0c\u5e0c\u671b\u4eca\u5e74\u8fd8\u80fd\u7ee7\u7eed\u3002

\u4e0d\u77e5\u4e0d\u89c9\u5df2\u7ecf\u8fc7\u4e86\u4e94\u4e2a\u5e74\u5934\u4e86\uff0c\u8fd8\u8bb0\u5f97\u7b2c\u4e00\u4e2a\u7f6e\u9876\u662f\u56e0\u4e3a\u6211\u7b2c\u4e00\u6b21\u89c1\u8bc1LPL\u6367\u676f\uff0c\u4ece\u6211\u52a0\u5165LPL\u5c31\u6709\u592a\u591a\u96be\u5fd8\u7684\u8bb0\u5fc6\u771f\u7684\u5f88\u5e78\u8fd0\uff0c\u4e00\u66435\u5e74\u8fc7\u53bb\u4e86\uff0c\u73b0\u5728\u4e5d\u5f20\u56fe\u5df2\u7ecf\u6ee1\u6ee1\u4e86\uff0c\u771f\u7684\u5f88\u5e78\u798f\uff0c\u5728\u77ed\u77ed\u76845\u5e74\u91cc\u5728\u73b0\u573a\u89c1\u8bc1\u4e86LPL\u76849\u6b21 ...\u5168\u6587<\/a>","textLength":2045,"source":"","favorited":false,"pic_ids":["71b26c7fly1gqtbff0qrrj20rt3rlb2a","71b26c7fly1gqtbglvczlj20rs4jcqv5","71b26c7fly1gqtbgwjbu9j20rs4bu7wm","71b26c7fly1gqtbgzzs30j20rs3nx4qu","71b26c7fly1gqxxwras3ij20rs4n0npj","71b26c7fly1gqtbfhvf41j20rs2uyqv8","71b26c7fly1gqtbflm1xvj20rs407b2e","71b26c7fly1gqtbgj1en8j20rs4gwnpi","71b26c7fly1gqtbgsblbpj20rs3che84"],"pic_types":"0,0,0,0,0,0,0,0,0","thumbnail_pic":"https:\/\/wx4.sinaimg.cn\/thumbnail\/71b26c7fly1gqtbff0qrrj20rt3rlb2a.jpg","bmiddle_pic":"http:\/\/wx4.sinaimg.cn\/bmiddle\/71b26c7fly1gqtbff0qrrj20rt3rlb2a.jpg","original_pic":"https:\/\/wx4.sinaimg.cn\/large\/71b26c7fly1gqtbff0qrrj20rt3rlb2a.jpg","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"picStatus":"0:1,1:1,2:1,3:1,4:1,5:1,6:1,7:1,8:1","reposts_count":206,"comments_count":773,"attitudes_count":13758,"pending_approval_count":0,"isLongText":true,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4640329573078415&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=1fae731ec42857449a6b7f5ade1b1868","hide_flag":0,"mlevel":0,"darwin_tags":[],"mblogtype":2,"rid":"0_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_1319","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"pic_num":9,"alchemy_params":{"ug_red_envelope":false},"mblog_menu_new_style":0,"edit_config":{"edited":true,"menu_edit_history":{"scheme":"sinaweibo:\/\/cardlist?containerid=231440_-_4640329573078415&title=\u7f16\u8f91\u8bb0\u5f55","title":"\u67e5\u770b\u7f16\u8f91\u8bb0\u5f55"}},"isTop":1,"page_info":{"type":"search_topic","object_type":0,"page_pic":{"url":"https:\/\/wx3.sinaimg.cn\/large\/006f8wK2ly1g8yk40afgwj30dw0dwt9b.jpg"},"page_url":"https:\/\/m.weibo.cn\/search?containerid=231522type%3D1%26t%3D10%26q%3D%23%E8%8B%B1%E9%9B%84%E8%81%94%E7%9B%9F%23&isnewpage=1&luicode=10000011&lfid=1076031907518591","page_title":"#\u82f1\u96c4\u8054\u76df#","content1":"0\u8ba8\u8bba 0\u9605\u8bfb "},"pics":[{"pid":"71b26c7fly1gqtbff0qrrj20rt3rlb2a","url":"https:\/\/wx4.sinaimg.cn\/orj360\/71b26c7fly1gqtbff0qrrj20rt3rlb2a.jpg","size":"orj360","geo":{"width":360,"height":1755,"croped":false},"large":{"size":"large","url":"https:\/\/wx4.sinaimg.cn\/large\/71b26c7fly1gqtbff0qrrj20rt3rlb2a.jpg","geo":{"width":"1001","height":"4881","croped":false}}},{"pid":"71b26c7fly1gqtbglvczlj20rs4jcqv5","url":"https:\/\/wx4.sinaimg.cn\/orj360\/71b26c7fly1gqtbglvczlj20rs4jcqv5.jpg","size":"orj360","geo":{"width":360,"height":2116,"croped":false},"large":{"size":"large","url":"https:\/\/wx4.sinaimg.cn\/large\/71b26c7fly1gqtbglvczlj20rs4jcqv5.jpg","geo":{"width":"1000","height":"5880","croped":false}}},{"pid":"71b26c7fly1gqtbgwjbu9j20rs4bu7wm","url":"https:\/\/wx4.sinaimg.cn\/orj360\/71b26c7fly1gqtbgwjbu9j20rs4bu7wm.jpg","size":"orj360","geo":{"width":360,"height":2019,"croped":false},"large":{"size":"large","url":"https:\/\/wx4.sinaimg.cn\/large\/71b26c7fly1gqtbgwjbu9j20rs4bu7wm.jpg","geo":{"width":"1000","height":"5610","croped":false}}},{"pid":"71b26c7fly1gqtbgzzs30j20rs3nx4qu","url":"https:\/\/wx2.sinaimg.cn\/orj360\/71b26c7fly1gqtbgzzs30j20rs3nx4qu.jpg","size":"orj360","geo":{"width":360,"height":1709,"croped":false},"large":{"size":"large","url":"https:\/\/wx2.sinaimg.cn\/large\/71b26c7fly1gqtbgzzs30j20rs3nx4qu.jpg","geo":{"width":"1000","height":"4749","croped":false}}},{"pid":"71b26c7fly1gqxxwras3ij20rs4n0npj","url":"https:\/\/wx4.sinaimg.cn\/orj360\/71b26c7fly1gqxxwras3ij20rs4n0npj.jpg","size":"orj360","geo":{"width":360,"height":2164,"croped":false},"large":{"size":"large","url":"https:\/\/wx4.sinaimg.cn\/large\/71b26c7fly1gqxxwras3ij20rs4n0npj.jpg","geo":{"width":"1000","height":"6012","croped":false}}},{"pid":"71b26c7fly1gqtbfhvf41j20rs2uyqv8","url":"https:\/\/wx4.sinaimg.cn\/orj360\/71b26c7fly1gqtbfhvf41j20rs2uyqv8.jpg","size":"orj360","geo":{"width":360,"height":1334,"croped":false},"large":{"size":"large","url":"https:\/\/wx4.sinaimg.cn\/large\/71b26c7fly1gqtbfhvf41j20rs2uyqv8.jpg","geo":{"width":"1000","height":"3706","croped":false}}},{"pid":"71b26c7fly1gqtbflm1xvj20rs407b2e","url":"https:\/\/wx1.sinaimg.cn\/orj360\/71b26c7fly1gqtbflm1xvj20rs407b2e.jpg","size":"orj360","geo":{"width":360,"height":1868,"croped":false},"large":{"size":"large","url":"https:\/\/wx1.sinaimg.cn\/large\/71b26c7fly1gqtbflm1xvj20rs407b2e.jpg","geo":{"width":"1000","height":"5191","croped":false}}},{"pid":"71b26c7fly1gqtbgj1en8j20rs4gwnpi","url":"https:\/\/wx1.sinaimg.cn\/orj360\/71b26c7fly1gqtbgj1en8j20rs4gwnpi.jpg","size":"orj360","geo":{"width":360,"height":2085,"croped":false},"large":{"size":"large","url":"https:\/\/wx1.sinaimg.cn\/large\/71b26c7fly1gqtbgj1en8j20rs4gwnpi.jpg","geo":{"width":"1000","height":"5792","croped":false}}},{"pid":"71b26c7fly1gqtbgsblbpj20rs3che84","url":"https:\/\/wx3.sinaimg.cn\/orj360\/71b26c7fly1gqtbgsblbpj20rs3che84.jpg","size":"orj360","geo":{"width":360,"height":1561,"croped":false},"large":{"size":"large","url":"https:\/\/wx3.sinaimg.cn\/large\/71b26c7fly1gqtbgsblbpj20rs3che84.jpg","geo":{"width":"1000","height":"4337","croped":false}}}],"title":{"text":"\u7f6e\u9876","base_color":1},"bid":"KgV9HcUPR"}},{"card_type":9,"itemid":"1076031907518591_-_4655758022804664","scheme":"https:\/\/m.weibo.cn\/status\/KnowibLCw?mblogid=KnowibLCw&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Tue Jul 06 00:31:15 +0800 2021","id":"4655758022804664","mid":"4655758022804664","can_edit":false,"show_additional_indication":0,"text":"\u6211\u4eec\u7edd\u4e0d\u4f1a\u5229\u7528\u7b7e\u540d\u6765\u8d5a\u94b1\uff0c\u4e5f\u4e0d\u4f1a\u7ed9\u673a\u6784\u4e2a\u4eba\u5927\u91cf\u7b7e\u540d\u3002\u5e0c\u671b\u5927\u5bb6\u5343\u4e07\u4e0d\u8981\u82b1\u94b1\u4e70\u4efb\u4f55\u7b7e\u540d\uff0c\u8981\u4f60\u82b1\u94b1\u4e70\u7684\u7b7e\u540d\u4e00\u5f8b\u662f\u4f2a\u9020\u7684\uff01\uff01\uff01\uff01\uff01","source":"","favorited":false,"pic_ids":[],"pic_types":"","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"retweeted_status":{"visible":{"type":0,"list_id":0},"created_at":"Tue Jul 06 00:28:38 +0800 2021","id":"4655757364823177","mid":"4655757364823177","can_edit":false,"show_additional_indication":0,"text":"\u26a0\ufe0f\u26a0\ufe0f\u26a0\ufe0f\u5404\u4f4d\u5144\u5f1f\u59d0\u59b9\u4eec\uff0c\u6211\u521a\u5728\u7fa4\u91cc\u542c\u5230\u4e00\u4ef6\u5f88\u79bb\u8c31\u7684\u4e8b\uff0c\u6709\u7c89\u4e1d\u5728\u5916\u9762\u4e70\u4e86\u6211\u7684\u7b7e\u540d\u7167\uff0c\u6211\u770b\u4e86\u4e00\u4e0b\uff0c\u5e76\u4e14\u4e5f\u53bb\u95ee\u4e86\u971c\u971c\u6c42\u8bc1\u4e86\u5979\uff0c\u53d1\u73b0\u6211\u4eec\u7684\u90fd\u662f\u88ab\u4eff\u9020\u7684\uff0c\u4e70\u7684\u4eba\u4e5f\u90fd\u8fd8\u662f\u5b66\u751f\uff01\uff01\u5b9e\u5728\u592a\u6c14\u4e86\uff0c\u60f3\u8ddf\u5927\u5bb6\u8bf4\u6211\u4eec\u4e0d\u4f1a\u7ed9\u4efb\u4f55\u673a\u6784\u548c\u4e2a\u4eba\u5927\u91cf\u5730\u7b7e\u7167\u7247\uff0c\u66f4\u4e0d\u4f1a\u7528\u8fd9\u4e2a\u6765\u8d5a\u94b1\u3002\u5927\u5bb6\u5982\u679c\u6709\u673a\u4f1a\u7684\u8bdd\u53ef\u4ee5\u6765lpl\u73b0 ...\u5168\u6587<\/a>","textLength":437,"source":"iPhone 12 Pro Max","favorited":false,"pic_ids":["7c5552e5ly1gs6j3oy93kj20u01t0tj5","7c5552e5ly1gs6j3pkawvj20u01sw4gu","7c5552e5ly1gs6j4adcgcj20u01swb14"],"pic_types":"0,0,0","thumbnail_pic":"https:\/\/wx3.sinaimg.cn\/thumbnail\/7c5552e5ly1gs6j3oy93kj20u01t0tj5.jpg","bmiddle_pic":"http:\/\/wx3.sinaimg.cn\/bmiddle\/7c5552e5ly1gs6j3oy93kj20u01t0tj5.jpg","original_pic":"https:\/\/wx3.sinaimg.cn\/large\/7c5552e5ly1gs6j3oy93kj20u01t0tj5.jpg","is_paid":false,"mblog_vip_type":0,"user":{"id":2085966565,"screen_name":"\u9a86\u6b46\u540c\u5b66","profile_image_url":"https:\/\/tvax3.sinaimg.cn\/crop.0.0.1080.1080.180\/7c5552e5ly8gop1xke6xbj20u00u0gpz.jpg?KID=imgbed,tva&Expires=1625558632&ssig=Ia7KR4GD%2F6","profile_url":"https:\/\/m.weibo.cn\/u\/2085966565?uid=2085966565&luicode=10000011&lfid=1076031907518591","statuses_count":2789,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba \u6597\u9c7cTV\u7b7e\u7ea6\u4e3b\u64ad","close_blue_v":false,"description":"\u5de5\u4f5c\u8bf7\u8054\u7cfb\u90ae\u7bb1\uff1avdoumi@foxmail.com","gender":"f","mbtype":12,"urank":45,"mbrank":7,"follow_me":false,"following":false,"followers_count":2477473,"follow_count":590,"cover_image_phone":"https:\/\/wx2.sinaimg.cn\/crop.0.0.640.640.640\/6b97e7cegy1fisd9jzlb5j20yi0yin0u.jpg","avatar_hd":"https:\/\/wx3.sinaimg.cn\/orj480\/7c5552e5ly8gop1xke6xbj20u00u0gpz.jpg","like":false,"like_me":false,"badge":{"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"panda":1,"user_name_certificate":1,"wenchuan_10th":1,"super_star_2018":1,"asiad_2018":1,"lol_s8":1,"weibo_display_fans":1,"relation_display":1,"hongbaofei_2019":1,"hongrenjie_2019":1,"hongbao_2020":2,"hongbaofeifuniu_2021":1,"party_cardid_state":1}},"picStatus":"0:1,1:1,2:1","reposts_count":59,"comments_count":322,"attitudes_count":4498,"pending_approval_count":0,"isLongText":true,"reward_exhibition_type":0,"hide_flag":0,"mlevel":0,"darwin_tags":[],"mblogtype":0,"rid":"1_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_1060","number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"content_auth":0,"pic_num":3,"edit_config":{"edited":false},"pics":[{"pid":"7c5552e5ly1gs6j3oy93kj20u01t0tj5","url":"https:\/\/wx3.sinaimg.cn\/orj360\/7c5552e5ly1gs6j3oy93kj20u01t0tj5.jpg","size":"orj360","geo":{"width":360,"height":780,"croped":false},"large":{"size":"large","url":"https:\/\/wx3.sinaimg.cn\/large\/7c5552e5ly1gs6j3oy93kj20u01t0tj5.jpg","geo":{"width":"1080","height":"2340","croped":false}}},{"pid":"7c5552e5ly1gs6j3pkawvj20u01sw4gu","url":"https:\/\/wx2.sinaimg.cn\/orj360\/7c5552e5ly1gs6j3pkawvj20u01sw4gu.jpg","size":"orj360","geo":{"width":360,"height":778,"croped":false},"large":{"size":"large","url":"https:\/\/wx2.sinaimg.cn\/large\/7c5552e5ly1gs6j3pkawvj20u01sw4gu.jpg","geo":{"width":"1080","height":"2336","croped":false}}},{"pid":"7c5552e5ly1gs6j4adcgcj20u01swb14","url":"https:\/\/wx2.sinaimg.cn\/orj360\/7c5552e5ly1gs6j4adcgcj20u01swb14.jpg","size":"orj360","geo":{"width":360,"height":778,"croped":false},"large":{"size":"large","url":"https:\/\/wx2.sinaimg.cn\/large\/7c5552e5ly1gs6j4adcgcj20u01swb14.jpg","geo":{"width":"1080","height":"2336","croped":false}}}],"bid":"KnovekeJb"},"reposts_count":23,"comments_count":169,"attitudes_count":982,"pending_approval_count":0,"isLongText":false,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4655758022804664&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=fd2c708aabcc26fd1de043badfab7152","hide_flag":0,"mlevel":0,"darwin_tags":[],"mblogtype":0,"rid":"1_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_116205703014","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"repost_type":3,"pic_num":0,"alchemy_params":{"ug_red_envelope":false},"mblog_menu_new_style":0,"edit_config":{"edited":false},"raw_text":"\u6211\u4eec\u7edd\u4e0d\u4f1a\u5229\u7528\u7b7e\u540d\u6765\u8d5a\u94b1\uff0c\u4e5f\u4e0d\u4f1a\u7ed9\u673a\u6784\u4e2a\u4eba\u5927\u91cf\u7b7e\u540d\u3002\u5e0c\u671b\u5927\u5bb6\u5343\u4e07\u4e0d\u8981\u82b1\u94b1\u4e70\u4efb\u4f55\u7b7e\u540d\uff0c\u8981\u4f60\u82b1\u94b1\u4e70\u7684\u7b7e\u540d\u4e00\u5f8b\u662f\u4f2a\u9020\u7684\uff01\uff01\uff01\uff01\uff01","bid":"KnowibLCw"}},{"card_type":9,"itemid":"1076031907518591_-_4655727546995839","scheme":"https:\/\/m.weibo.cn\/status\/KnnJ8tlW7?mblogid=KnnJ8tlW7&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Mon Jul 05 22:30:09 +0800 2021","id":"4655727546995839","mid":"4655727546995839","can_edit":false,"show_additional_indication":0,"text":"JDG\uff01zoom\u9752\u94a2\u5f71\u592a\u96be\u5904\u7406\u4e86\uff5e\u540e\u671f
\u6700\u8fd1\u6bd4\u8d5b\u90fd\u662f\u8ba9\u4e00\u8ffd\u4e8c\uff1f

#2021lpl#<\/span><\/a> ","textLength":65,"source":"","favorited":false,"pic_ids":[],"pic_types":"","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"reposts_count":0,"comments_count":66,"attitudes_count":564,"pending_approval_count":0,"isLongText":false,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4655727546995839&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=a2caf9db2068f47772f51599fe37ffcc","hide_flag":0,"mlevel":0,"darwin_tags":[],"mblogtype":0,"rid":"2_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_116205703014","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"pic_num":0,"alchemy_params":{"ug_red_envelope":false},"mblog_menu_new_style":0,"edit_config":{"edited":false},"page_info":{"type":"search_topic","object_type":0,"page_pic":{"url":"https:\/\/wx1.sinaimg.cn\/large\/006OVbdely8gr3vh6w5l1j30dw0dwgmx.jpg"},"page_url":"https:\/\/m.weibo.cn\/search?containerid=231522type%3D1%26t%3D10%26q%3D%232021lpl%23&extparam=%232021lpl%23&luicode=10000011&lfid=1076031907518591","page_title":"#2021lpl#","content1":"0\u8ba8\u8bba 0\u9605\u8bfb "},"bid":"KnnJ8tlW7"}},{"card_type":9,"itemid":"1076031907518591_-_4655701919795833","scheme":"https:\/\/m.weibo.cn\/status\/Knn3NF6lj?mblogid=Knn3NF6lj&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Mon Jul 05 20:48:19 +0800 2021","id":"4655701919795833","mid":"4655701919795833","can_edit":false,"show_additional_indication":0,"text":"FOFO[\u4f5c\u63d6]<\/span>[\u4f5c\u63d6]<\/span>#2021lpl#<\/span><\/a> ","textLength":25,"source":"","favorited":false,"pic_ids":[],"pic_types":"","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"reposts_count":0,"comments_count":38,"attitudes_count":487,"pending_approval_count":0,"isLongText":false,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4655701919795833&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=e8c14e0fe2ddc2b27dcaeeca6d881431","hide_flag":0,"mlevel":0,"darwin_tags":[],"mblogtype":0,"rid":"3_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_116205703014","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"pic_num":0,"alchemy_params":{"ug_red_envelope":false},"mblog_menu_new_style":0,"edit_config":{"edited":false},"page_info":{"type":"search_topic","object_type":0,"page_pic":{"url":"https:\/\/wx1.sinaimg.cn\/large\/006OVbdely8gr3vh6w5l1j30dw0dwgmx.jpg"},"page_url":"https:\/\/m.weibo.cn\/search?containerid=231522type%3D1%26t%3D10%26q%3D%232021lpl%23&extparam=%232021lpl%23&luicode=10000011&lfid=1076031907518591","page_title":"#2021lpl#","content1":"0\u8ba8\u8bba 0\u9605\u8bfb "},"bid":"Knn3NF6lj"}},{"card_type":9,"itemid":"1076031907518591_-_4655682274199795","scheme":"https:\/\/m.weibo.cn\/status\/Knmy7hCyD?mblogid=Knmy7hCyD&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Mon Jul 05 19:30:15 +0800 2021","id":"4655682274199795","mid":"4655682274199795","edit_count":1,"can_edit":false,"edit_at":"Mon Jul 05 19:31:00 +0800 2021","version":1,"show_additional_indication":0,"text":"\u6700\u8fd1\u6bd4\u8d5b\u771f\u7684[\u5403\u60ca]<\/span>
\u5403\u70b9\u964d\u8840\u538b\u7684\ud83d\udc8a\u5427

#2021lpl#<\/span><\/a> ","textLength":48,"source":"","favorited":false,"pic_ids":[],"pic_types":"","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"reposts_count":1,"comments_count":165,"attitudes_count":1434,"pending_approval_count":0,"isLongText":false,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4655682274199795&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=811532334afb3ea9f248c2064621d05c","hide_flag":0,"mlevel":0,"darwin_tags":[],"mblogtype":0,"rid":"4_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_116205703014","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"pic_num":0,"alchemy_params":{"ug_red_envelope":false},"mblog_menu_new_style":0,"edit_config":{"edited":true,"menu_edit_history":{"scheme":"sinaweibo:\/\/cardlist?containerid=231440_-_4655682274199795&title=\u7f16\u8f91\u8bb0\u5f55","title":"\u67e5\u770b\u7f16\u8f91\u8bb0\u5f55"}},"page_info":{"type":"search_topic","object_type":0,"page_pic":{"url":"https:\/\/wx1.sinaimg.cn\/large\/006OVbdely8gr3vh6w5l1j30dw0dwgmx.jpg"},"page_url":"https:\/\/m.weibo.cn\/search?containerid=231522type%3D1%26t%3D10%26q%3D%232021lpl%23&extparam=%232021lpl%23&luicode=10000011&lfid=1076031907518591","page_title":"#2021lpl#","content1":"0\u8ba8\u8bba 0\u9605\u8bfb "},"bid":"Knmy7hCyD"}},{"card_type":9,"itemid":"1076031907518591_-_4655669099106091","scheme":"https:\/\/m.weibo.cn\/status\/KnmcRCcUr?mblogid=KnmcRCcUr&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Mon Jul 05 18:37:54 +0800 2021","id":"4655669099106091","mid":"4655669099106091","can_edit":false,"show_additional_indication":0,"text":"\uff1f\uff1f\uff1f\uff1f\uff1f\uff1f\uff1f\uff1f\uff1f\uff1f\uff1f

\u554a\u8fd9
#2021lpl#<\/span><\/a> ","textLength":37,"source":"","favorited":false,"pic_ids":[],"pic_types":"","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"reposts_count":0,"comments_count":106,"attitudes_count":1270,"pending_approval_count":0,"isLongText":false,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4655669099106091&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=63cbd134c43bee18bbe7d3980e081f60","hide_flag":0,"mlevel":0,"darwin_tags":[],"mblogtype":0,"rid":"5_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_116205703014","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"pic_num":0,"alchemy_params":{"ug_red_envelope":false},"mblog_menu_new_style":0,"edit_config":{"edited":false},"page_info":{"type":"search_topic","object_type":0,"page_pic":{"url":"https:\/\/wx1.sinaimg.cn\/large\/006OVbdely8gr3vh6w5l1j30dw0dwgmx.jpg"},"page_url":"https:\/\/m.weibo.cn\/search?containerid=231522type%3D1%26t%3D10%26q%3D%232021lpl%23&extparam=%232021lpl%23&luicode=10000011&lfid=1076031907518591","page_title":"#2021lpl#","content1":"0\u8ba8\u8bba 0\u9605\u8bfb "},"bid":"KnmcRCcUr"}},{"card_type":9,"itemid":"1076031907518591_-_4655623977568742","scheme":"https:\/\/m.weibo.cn\/status\/Knl25vKYu?mblogid=Knl25vKYu&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Mon Jul 05 15:38:36 +0800 2021","id":"4655623977568742","mid":"4655623977568742","can_edit":false,"version":1,"show_additional_indication":0,"text":"\u5bb3\u81ea\u5df1\u7ec3\u5c31\u662f\u592a\u6e29\u67d4\u4e86[\u8349\u6ce5\u9a6c]<\/span>

\u6709\u6c2730min
100\u4e0a\u5377\u8179\u8d1f\u91cd10kg\uff0c60\u4e0b\u5377\u8179
\u518d\u968f\u610f10kg 4\u7ec4\u6df1\u8e72

<\/span>\u4f59\u971c<\/span><\/a> ","textLength":100,"source":"\u4f59\u971c\u8d85\u8bdd","favorited":false,"pic_ids":["71b26c7fly1gs6437ojgsj21hc0u1n58","71b26c7fly1gs64365lpnj21u02hmqv5"],"pic_types":"0,0","thumbnail_pic":"https:\/\/wx2.sinaimg.cn\/thumbnail\/71b26c7fly1gs6437ojgsj21hc0u1n58.jpg","bmiddle_pic":"http:\/\/wx2.sinaimg.cn\/bmiddle\/71b26c7fly1gs6437ojgsj21hc0u1n58.jpg","original_pic":"https:\/\/wx2.sinaimg.cn\/large\/71b26c7fly1gs6437ojgsj21hc0u1n58.jpg","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"pic_video":"0:000jcYFIjx07NZrbY8Yf0f0f0100e7rx0k01,1:002bqprTjx07NZrbUv0Y0f0f0100lKBO0k01","reposts_count":9,"comments_count":197,"attitudes_count":1088,"pending_approval_count":0,"isLongText":false,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4655623977568742&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=2d79b25dde21428194c8eb71842d0c99","hide_flag":0,"mlevel":0,"topic_id":"1022:100808f1a54ae62c00242a895e722812cea0b9","sync_mblog":true,"is_imported_topic":true,"darwin_tags":[],"mblogtype":0,"rid":"6_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_116205703014","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"pic_num":2,"alchemy_params":{"ug_red_envelope":false},"jump_type":1,"mblog_menu_new_style":0,"edit_config":{"edited":false},"page_info":{"type":"topic","object_type":0,"page_pic":{"url":"https:\/\/wx1.sinaimg.cn\/thumbnail\/71b26c7fly1fv7b72xuh6j20k00k0wf2.jpg"},"page_url":"https:\/\/m.weibo.cn\/p\/index?extparam=%E4%BD%99%E9%9C%9C&containerid=100808f1a54ae62c00242a895e722812cea0b9&luicode=10000011&lfid=1076031907518591","page_title":"\u3010\u8d85\u8bdd\u3011\u4f59\u971c","content1":"\u4f59\u971c \n\u76f4\u64ad\u95f4\uff1a\u6597\u9c7c233233\n\u6296\u97f3\uff1a\u4f59\u971cYSCandice\nB\u7ad9\uff1a\u4f59\u971cCandice\n\u5934\u6761\uff1a\u4f59\u971cYSCandice\nIns: yushuangys\nTwitter:\u4f59\u971cYSCandice","content2":"12710\u4eba\u5173\u6ce8"},"pics":[{"pid":"71b26c7fly1gs6437ojgsj21hc0u1n58","url":"https:\/\/wx2.sinaimg.cn\/orj360\/71b26c7fly1gs6437ojgsj21hc0u1n58.jpg","size":"orj360","geo":{"width":479,"height":270,"croped":false},"large":{"size":"large","url":"https:\/\/wx2.sinaimg.cn\/large\/71b26c7fly1gs6437ojgsj21hc0u1n58.jpg","geo":{"width":"1920","height":"1081","croped":false}}},{"pid":"71b26c7fly1gs64365lpnj21u02hmqv5","url":"https:\/\/wx3.sinaimg.cn\/orj360\/71b26c7fly1gs64365lpnj21u02hmqv5.jpg","size":"orj360","geo":{"width":360,"height":488,"croped":false},"large":{"size":"large","url":"https:\/\/wx3.sinaimg.cn\/large\/71b26c7fly1gs64365lpnj21u02hmqv5.jpg","geo":{"width":2048,"height":2780,"croped":false}}}],"live_photo":["https:\/\/video.weibo.com\/media\/play?livephoto=https%3A%2F%2Flivephoto.us.sinaimg.cn%2F000jcYFIjx07NZrbY8Yf0f0f0100e7rx0k01.mov","https:\/\/video.weibo.com\/media\/play?livephoto=https%3A%2F%2Flivephoto.us.sinaimg.cn%2F002bqprTjx07NZrbUv0Y0f0f0100lKBO0k01.mov"],"bid":"Knl25vKYu"}},{"card_type":9,"itemid":"1076031907518591_-_4655591404078569","scheme":"https:\/\/m.weibo.cn\/status\/Knkbyh71n?mblogid=Knkbyh71n&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Mon Jul 05 13:29:10 +0800 2021","id":"4655591404078569","mid":"4655591404078569","can_edit":false,"version":1,"show_additional_indication":0,"text":"\u66f4\u65b0\u81ea\u62cd\ud83e\udd33

<\/span>\u4f59\u971c<\/span><\/a> ","textLength":26,"source":"\u4f59\u971c\u8d85\u8bdd","favorited":false,"pic_ids":["71b26c7fly1gs60d7fcdkj20u11hcql0","71b26c7fly1gs60d4d5ldj20u11hc47r"],"pic_types":"0,0","thumbnail_pic":"https:\/\/wx1.sinaimg.cn\/thumbnail\/71b26c7fly1gs60d7fcdkj20u11hcql0.jpg","bmiddle_pic":"http:\/\/wx1.sinaimg.cn\/bmiddle\/71b26c7fly1gs60d7fcdkj20u11hcql0.jpg","original_pic":"https:\/\/wx1.sinaimg.cn\/large\/71b26c7fly1gs60d7fcdkj20u11hcql0.jpg","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"pic_video":"0:001B86GYjx07NZigM1uw0f0f0100uyQg0k01,1:001XiN3Xjx07NZiiQJ7G0f0f0100bPND0k01","reposts_count":47,"comments_count":498,"attitudes_count":2481,"pending_approval_count":0,"isLongText":false,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4655591404078569&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=37a2e5d959d9a978315d913d15c54173","hide_flag":0,"mlevel":0,"topic_id":"1022:100808f1a54ae62c00242a895e722812cea0b9","sync_mblog":true,"is_imported_topic":true,"darwin_tags":[],"mblogtype":0,"rid":"7_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_116205703014","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"pic_num":2,"alchemy_params":{"ug_red_envelope":false},"jump_type":1,"mblog_menu_new_style":0,"edit_config":{"edited":false},"page_info":{"type":"topic","object_type":0,"page_pic":{"url":"https:\/\/wx1.sinaimg.cn\/thumbnail\/71b26c7fly1fv7b72xuh6j20k00k0wf2.jpg"},"page_url":"https:\/\/m.weibo.cn\/p\/index?extparam=%E4%BD%99%E9%9C%9C&containerid=100808f1a54ae62c00242a895e722812cea0b9&luicode=10000011&lfid=1076031907518591","page_title":"\u3010\u8d85\u8bdd\u3011\u4f59\u971c","content1":"\u4f59\u971c \n\u76f4\u64ad\u95f4\uff1a\u6597\u9c7c233233\n\u6296\u97f3\uff1a\u4f59\u971cYSCandice\nB\u7ad9\uff1a\u4f59\u971cCandice\n\u5934\u6761\uff1a\u4f59\u971cYSCandice\nIns: yushuangys\nTwitter:\u4f59\u971cYSCandice","content2":"12710\u4eba\u5173\u6ce8"},"pics":[{"pid":"71b26c7fly1gs60d7fcdkj20u11hcql0","url":"https:\/\/wx1.sinaimg.cn\/orj360\/71b26c7fly1gs60d7fcdkj20u11hcql0.jpg","size":"orj360","geo":{"width":360,"height":639,"croped":false},"large":{"size":"large","url":"https:\/\/wx1.sinaimg.cn\/large\/71b26c7fly1gs60d7fcdkj20u11hcql0.jpg","geo":{"width":"1081","height":"1920","croped":false}}},{"pid":"71b26c7fly1gs60d4d5ldj20u11hc47r","url":"https:\/\/wx1.sinaimg.cn\/orj360\/71b26c7fly1gs60d4d5ldj20u11hc47r.jpg","size":"orj360","geo":{"width":360,"height":639,"croped":false},"large":{"size":"large","url":"https:\/\/wx1.sinaimg.cn\/large\/71b26c7fly1gs60d4d5ldj20u11hc47r.jpg","geo":{"width":"1081","height":"1920","croped":false}}}],"live_photo":["https:\/\/video.weibo.com\/media\/play?livephoto=https%3A%2F%2Flivephoto.us.sinaimg.cn%2F001B86GYjx07NZigM1uw0f0f0100uyQg0k01.mov","https:\/\/video.weibo.com\/media\/play?livephoto=https%3A%2F%2Flivephoto.us.sinaimg.cn%2F001XiN3Xjx07NZiiQJ7G0f0f0100bPND0k01.mov"],"bid":"Knkbyh71n"}},{"card_type":9,"itemid":"1076031907518591_-_4655435766039395","scheme":"https:\/\/m.weibo.cn\/status\/Kng8wpl7B?mblogid=Kng8wpl7B&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Mon Jul 05 03:10:43 +0800 2021","id":"4655435766039395","mid":"4655435766039395","can_edit":false,"version":1,"show_additional_indication":0,"text":"\u4e00\u70b9\u90fd\u7761\u4e0d\u7740 ","textLength":12,"source":"","favorited":false,"pic_ids":["007jMTffly1gmhp2npeoqg306n06nae0"],"pic_types":"3","thumbnail_pic":"https:\/\/wx4.sinaimg.cn\/thumbnail\/007jMTffly1gmhp2npeoqg306n06nae0.gif","bmiddle_pic":"http:\/\/wx4.sinaimg.cn\/bmiddle\/007jMTffly1gmhp2npeoqg306n06nae0.gif","original_pic":"https:\/\/wx4.sinaimg.cn\/large\/007jMTffly1gmhp2npeoqg306n06nae0.gif","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"reposts_count":7,"comments_count":292,"attitudes_count":1735,"pending_approval_count":0,"isLongText":false,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4655435766039395&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=3df7203fd4c6c33c4e6507018e804323","hide_flag":0,"mlevel":0,"darwin_tags":[],"mblogtype":0,"rid":"8_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_116205703014","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"pic_num":1,"alchemy_params":{"ug_red_envelope":false},"mblog_menu_new_style":0,"edit_config":{"edited":false},"pics":[{"pid":"007jMTffly1gmhp2npeoqg306n06nae0","url":"https:\/\/wx4.sinaimg.cn\/orj360\/007jMTffly1gmhp2npeoqg306n06nae0.gif","size":"orj360","geo":{"width":"239","height":"239","croped":false},"large":{"size":"large","url":"https:\/\/wx4.sinaimg.cn\/large\/007jMTffly1gmhp2npeoqg306n06nae0.gif","geo":{"width":"239","height":"239","croped":false}}}],"bid":"Kng8wpl7B"}},{"card_type":9,"itemid":"1076031907518591_-_4655401851422792","scheme":"https:\/\/m.weibo.cn\/status\/KnffP5Y8g?mblogid=KnffP5Y8g&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Mon Jul 05 00:55:57 +0800 2021","id":"4655401851422792","mid":"4655401851422792","can_edit":false,"version":1,"show_additional_indication":0,"text":"\u665a\u5b89\u5566\uff01[\u5fc3]<\/span>

\ud83d\udcf7\uff1a
@\u5fc3\u6001\u7206\u70b8\u7684\u5927\u9f84\u59e8\u6bcd<\/a>
9
@\u54cd\u561b\u54cd\u53ee\u5f53<\/a>
3 studioin
<\/span>\u4f59\u971c<\/span><\/a>#2021lpl#<\/span><\/a> ","textLength":88,"source":"\u4f59\u971c\u8d85\u8bdd","favorited":false,"pic_ids":["71b26c7fly1gs5dxrr6ppj21s016ohdu","71b26c7fly1gs5dxteynvj21s016oww5","71b26c7fly1gs5dxqv6akj22bc1jkx6p","71b26c7fly1gs5dxyev4kj21s016o1ky","71b26c7fly1gs5dxwsv6jj21s016oe82","71b26c7fly1gs5ejrtn6hj223u35sx6y","71b26c7fly1gs5dxsi8e0j20vh1671ky","71b26c7fly1gs5ejpvss4j20y91plqv6","71b26c7fly1gs5eju7l9gj21eo2dvhdx"],"pic_types":"0,0,0,0,0,0,0,0,0","thumbnail_pic":"https:\/\/wx4.sinaimg.cn\/thumbnail\/71b26c7fly1gs5dxrr6ppj21s016ohdu.jpg","bmiddle_pic":"http:\/\/wx4.sinaimg.cn\/bmiddle\/71b26c7fly1gs5dxrr6ppj21s016ohdu.jpg","original_pic":"https:\/\/wx4.sinaimg.cn\/large\/71b26c7fly1gs5dxrr6ppj21s016ohdu.jpg","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"picStatus":"0:1,1:1,2:1,3:1,4:1,5:1,6:1,7:1,8:1","reposts_count":31,"comments_count":151,"attitudes_count":1117,"pending_approval_count":0,"isLongText":false,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4655401851422792&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=2f52b2de9dba74250cb2bd60aaf6358d","hide_flag":0,"mlevel":0,"topic_id":"1022:100808f1a54ae62c00242a895e722812cea0b9","sync_mblog":true,"is_imported_topic":true,"darwin_tags":[],"mblogtype":0,"rid":"9_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_116205703014","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"pic_num":13,"alchemy_params":{"ug_red_envelope":false},"jump_type":1,"mblog_menu_new_style":0,"edit_config":{"edited":false},"page_info":{"type":"topic","object_type":0,"page_pic":{"url":"https:\/\/wx1.sinaimg.cn\/thumbnail\/71b26c7fly1fv7b72xuh6j20k00k0wf2.jpg"},"page_url":"https:\/\/m.weibo.cn\/p\/index?extparam=%E4%BD%99%E9%9C%9C&containerid=100808f1a54ae62c00242a895e722812cea0b9&luicode=10000011&lfid=1076031907518591","page_title":"\u3010\u8d85\u8bdd\u3011\u4f59\u971c","content1":"\u4f59\u971c \n\u76f4\u64ad\u95f4\uff1a\u6597\u9c7c233233\n\u6296\u97f3\uff1a\u4f59\u971cYSCandice\nB\u7ad9\uff1a\u4f59\u971cCandice\n\u5934\u6761\uff1a\u4f59\u971cYSCandice\nIns: yushuangys\nTwitter:\u4f59\u971cYSCandice","content2":"12710\u4eba\u5173\u6ce8"},"pics":[{"pid":"71b26c7fly1gs5dxrr6ppj21s016ohdu","url":"https:\/\/wx4.sinaimg.cn\/orj360\/71b26c7fly1gs5dxrr6ppj21s016ohdu.jpg","size":"orj360","geo":{"width":405,"height":270,"croped":false},"large":{"size":"large","url":"https:\/\/wx4.sinaimg.cn\/large\/71b26c7fly1gs5dxrr6ppj21s016ohdu.jpg","geo":{"width":2048,"height":1365,"croped":false}}},{"pid":"71b26c7fly1gs5dxteynvj21s016oww5","url":"https:\/\/wx1.sinaimg.cn\/orj360\/71b26c7fly1gs5dxteynvj21s016oww5.jpg","size":"orj360","geo":{"width":405,"height":270,"croped":false},"large":{"size":"large","url":"https:\/\/wx1.sinaimg.cn\/large\/71b26c7fly1gs5dxteynvj21s016oww5.jpg","geo":{"width":2048,"height":1365,"croped":false}}},{"pid":"71b26c7fly1gs5dxqv6akj22bc1jkx6p","url":"https:\/\/wx2.sinaimg.cn\/orj360\/71b26c7fly1gs5dxqv6akj22bc1jkx6p.jpg","size":"orj360","geo":{"width":405,"height":270,"croped":false},"large":{"size":"large","url":"https:\/\/wx2.sinaimg.cn\/large\/71b26c7fly1gs5dxqv6akj22bc1jkx6p.jpg","geo":{"width":2048,"height":1365,"croped":false}}},{"pid":"71b26c7fly1gs5dxyev4kj21s016o1ky","url":"https:\/\/wx3.sinaimg.cn\/orj360\/71b26c7fly1gs5dxyev4kj21s016o1ky.jpg","size":"orj360","geo":{"width":405,"height":270,"croped":false},"large":{"size":"large","url":"https:\/\/wx3.sinaimg.cn\/large\/71b26c7fly1gs5dxyev4kj21s016o1ky.jpg","geo":{"width":2048,"height":1365,"croped":false}}},{"pid":"71b26c7fly1gs5dxwsv6jj21s016oe82","url":"https:\/\/wx4.sinaimg.cn\/orj360\/71b26c7fly1gs5dxwsv6jj21s016oe82.jpg","size":"orj360","geo":{"width":405,"height":270,"croped":false},"large":{"size":"large","url":"https:\/\/wx4.sinaimg.cn\/large\/71b26c7fly1gs5dxwsv6jj21s016oe82.jpg","geo":{"width":2048,"height":1365,"croped":false}}},{"pid":"71b26c7fly1gs5ejrtn6hj223u35sx6y","url":"https:\/\/wx3.sinaimg.cn\/orj360\/71b26c7fly1gs5ejrtn6hj223u35sx6y.jpg","size":"orj360","geo":{"width":360,"height":540,"croped":false},"large":{"size":"large","url":"https:\/\/wx3.sinaimg.cn\/large\/71b26c7fly1gs5ejrtn6hj223u35sx6y.jpg","geo":{"width":2048,"height":3072,"croped":false}}},{"pid":"71b26c7fly1gs5dxsi8e0j20vh1671ky","url":"https:\/\/wx4.sinaimg.cn\/orj360\/71b26c7fly1gs5dxsi8e0j20vh1671ky.jpg","size":"orj360","geo":{"width":360,"height":482,"croped":false},"large":{"size":"large","url":"https:\/\/wx4.sinaimg.cn\/large\/71b26c7fly1gs5dxsi8e0j20vh1671ky.jpg","geo":{"width":"1133","height":"1519","croped":false}}},{"pid":"71b26c7fly1gs5ejpvss4j20y91plqv6","url":"https:\/\/wx1.sinaimg.cn\/orj360\/71b26c7fly1gs5ejpvss4j20y91plqv6.jpg","size":"orj360","geo":{"width":360,"height":647,"croped":false},"large":{"size":"large","url":"https:\/\/wx1.sinaimg.cn\/large\/71b26c7fly1gs5ejpvss4j20y91plqv6.jpg","geo":{"width":"1233","height":"2217","croped":false}}},{"pid":"71b26c7fly1gs5eju7l9gj21eo2dvhdx","url":"https:\/\/wx3.sinaimg.cn\/orj360\/71b26c7fly1gs5eju7l9gj21eo2dvhdx.jpg","size":"orj360","geo":{"width":360,"height":610,"croped":false},"large":{"size":"large","url":"https:\/\/wx3.sinaimg.cn\/large\/71b26c7fly1gs5eju7l9gj21eo2dvhdx.jpg","geo":{"width":"1824","height":"3091","croped":false}}}],"bid":"KnffP5Y8g"}},{"card_type":9,"itemid":"1076031907518591_-_4655363733591226","scheme":"https:\/\/m.weibo.cn\/status\/Kneglf4f0?mblogid=Kneglf4f0&luicode=10000011&lfid=1076031907518591","mblog":{"visible":{"type":0,"list_id":0},"created_at":"Sun Jul 04 22:24:29 +0800 2021","id":"4655363733591226","mid":"4655363733591226","can_edit":false,"show_additional_indication":0,"text":"\u8981\u8111\u6ea2\u8840\u4e86[\u6655]<\/span>[\u6655]<\/span>[\u6655]<\/span>[\u6655]<\/span>\u4eca\u5929\u62c9\u6ee1\ud83c\ude35#2021lpl#<\/span><\/a> ","textLength":47,"source":"","favorited":false,"pic_ids":[],"pic_types":"","is_paid":false,"mblog_vip_type":0,"user":{"id":1907518591,"screen_name":"\u4f59\u971cYSCandice","profile_image_url":"https:\/\/tvax2.sinaimg.cn\/crop.0.0.1080.1080.180\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg?KID=imgbed,tva&Expires=1625558632&ssig=qlEhhYE9RH","profile_url":"https:\/\/m.weibo.cn\/u\/1907518591?uid=1907518591&luicode=10000011&lfid=1076031907518591","statuses_count":4456,"verified":true,"verified_type":0,"verified_type_ext":1,"verified_reason":"\u82f1\u96c4\u8054\u76df\u5b98\u65b9\u4e3b\u6301\u4eba","close_blue_v":false,"description":"\u5546\u52a1\u5408\u4f5cvx: zhuzixiha","gender":"f","mbtype":12,"urank":48,"mbrank":7,"follow_me":false,"following":false,"followers_count":5605313,"follow_count":1607,"cover_image_phone":"https:\/\/wx4.sinaimg.cn\/crop.0.0.640.640.640\/71b26c7fly1gs0csjd55kj20zo0zojsm.jpg","avatar_hd":"https:\/\/wx2.sinaimg.cn\/orj480\/71b26c7fly8grwz25ljbyj20u00u00vk.jpg","like":false,"like_me":false,"badge":{"bind_taobao":1,"dzwbqlx_2016":1,"follow_whitelist_video":1,"lol_msi_2017":1,"lol_gm_2017":1,"user_name_certificate":1,"super_star_2018":1,"worldcup_2018":34,"wenda_v2":1,"asiad_2018":1,"qixi_2018":1,"lol_s8":1,"v_influence_2018":1,"hongbaofei_2019":1,"status_visible":1,"denglong_2019":1,"suishoupai_2019":1,"hongrenjie_2019":1,"china_2019":1,"hongkong_2019":1,"dzwbqlx_2019":1,"rrgyj_2019":1,"wbzy_2019":1,"hongbao_2020":2,"feiyan_2020":1,"graduation_2020":1,"china_2020":1,"hongbaofeifuniu_2021":1,"gaokao_2021":1,"party_cardid_state":2}},"reposts_count":3,"comments_count":237,"attitudes_count":1198,"pending_approval_count":0,"isLongText":false,"reward_exhibition_type":2,"reward_scheme":"sinaweibo:\/\/reward?bid=1000293251&enter_id=1000293251&enter_type=1&oid=4655363733591226&seller=1907518591&share=18cb5613ebf3d8aadd9975c1036ab1f47&sign=06d3b123fbe74afcbd59c0152889d7dc","hide_flag":0,"mlevel":0,"darwin_tags":[],"mblogtype":0,"rid":"10_0_50_2633257454420959822_0_0_0","more_info_type":0,"cardid":"star_116205703014","extern_safe":0,"number_display_strategy":{"apply_scenario_flag":3,"display_text_min_number":1000000,"display_text":"100\u4e07+"},"enable_comment_guide":true,"content_auth":0,"pic_num":0,"alchemy_params":{"ug_red_envelope":false},"mblog_menu_new_style":0,"edit_config":{"edited":false},"page_info":{"type":"search_topic","object_type":0,"page_pic":{"url":"https:\/\/wx1.sinaimg.cn\/large\/006OVbdely8gr3vh6w5l1j30dw0dwgmx.jpg"},"page_url":"https:\/\/m.weibo.cn\/search?containerid=231522type%3D1%26t%3D10%26q%3D%232021lpl%23&extparam=%232021lpl%23&luicode=10000011&lfid=1076031907518591","page_title":"#2021lpl#","content1":"0\u8ba8\u8bba 0\u9605\u8bfb "},"bid":"Kneglf4f0"}}],"scheme":"sinaweibo:\/\/cardlist?containerid=1076031907518591&luicode=10000011&lfid=1076035498972025&v_p=42","showAppTips":0}} \ No newline at end of file From 9270f018436fc3920e4f3e6b78fcb3571ab24c3d Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Tue, 6 Jul 2021 13:31:47 +0800 Subject: [PATCH 08/11] update change log --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6485e76..795ddc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,4 +7,4 @@ ## [0.2.12] - 2021-06-24 - 微博tag支持 - 修复bug - +- 增加微博超话和纯文字支持 From 10a2fbfa276247d0ae6a529b8bbaccee74ab8478 Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Tue, 6 Jul 2021 20:01:34 +0800 Subject: [PATCH 09/11] update browser --- .circleci/config.yml | 3 ++ CHANGELOG.md | 3 +- README.md | 6 ++- pyproject.toml | 3 +- src/plugins/nonebot_hk_reporter/__init__.py | 1 + .../nonebot_hk_reporter/plugin_config.py | 5 +++ src/plugins/nonebot_hk_reporter/utils.py | 45 +++++++++++-------- tests/test_render.py | 14 ++++++ 8 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 tests/test_render.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 1aa3473..27ab0b9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,6 +29,9 @@ jobs: test: docker: - image: cimg/python:3.9 + - image: browserless/chrome + environment: + HK_REPORTER_BROWSER: ws://localhost:3000 steps: - checkout # - run: sed -e '41,45d' -i pyproject.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 795ddc4..f8bbd29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ - 增加了简单的单元测试 - 增加了管理员直接管理订阅的能力 -## [0.2.12] - 2021-06-24 +## [0.3.0] - 2021-06-24 - 微博tag支持 - 修复bug - 增加微博超话和纯文字支持 +- 更改浏览器配置 diff --git a/README.md b/README.md index 153af69..0e25a6d 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,11 @@ go-cqhttp镜像可使用`felinae98/go-cqhttp-ffmpeg`(数据目录为`/data`) ### 配置变量 * `HK_REPORTER_CONFIG_PATH` (str) 配置文件保存目录,如果不设置,则为当前目录下的`data`文件夹 * `HK_REPORTER_USE_PIC` (bool) 以图片形式发送文字(推荐在帐号被风控时使用) -* `HK_REPORTER_USE_LOCAL` (bool) 使用本地chromium(文字转图片时需要),否则第一次启动会下载chromium +* ~~`HK_REPORTER_USE_LOCAL` (bool) 使用本地chromium(文字转图片时需要),否则第一次启动会下载chromium~~ +* `HK_REPORTER_BROWSER` (str) 明日方舟游戏公告和以以图片形式发送文字需要浏览器支持,如果不设置会在使用到 + 功能的时候自动下载Chromium(不推荐) + * 使用本地安装的Chromiun: 设置为`local:` + * 使用browserless提供的服务浏览器管理服务(推荐):设置为`ws://********` 同时,建议配置`SUPERUSERS`环境变量便于机器人管理 diff --git a/pyproject.toml b/pyproject.toml index 647cc09..bf3890e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,5 +48,6 @@ default = true [tool.pytest.ini_options] markers = [ - "compare: compare fetching result with rsshub" + "compare: compare fetching result with rsshub", + "render: render img by chrome" ] diff --git a/src/plugins/nonebot_hk_reporter/__init__.py b/src/plugins/nonebot_hk_reporter/__init__.py index 0d32056..b97f0b9 100644 --- a/src/plugins/nonebot_hk_reporter/__init__.py +++ b/src/plugins/nonebot_hk_reporter/__init__.py @@ -7,3 +7,4 @@ from . import send from . import post from . import platform from . import types +from . import utils diff --git a/src/plugins/nonebot_hk_reporter/plugin_config.py b/src/plugins/nonebot_hk_reporter/plugin_config.py index ca9749c..03a5bdb 100644 --- a/src/plugins/nonebot_hk_reporter/plugin_config.py +++ b/src/plugins/nonebot_hk_reporter/plugin_config.py @@ -1,4 +1,6 @@ from pydantic import BaseSettings + +import warnings import nonebot class PlugConfig(BaseSettings): @@ -6,6 +8,7 @@ class PlugConfig(BaseSettings): hk_reporter_config_path: str = "" hk_reporter_use_pic: bool = False hk_reporter_use_local: bool = False + hk_reporter_browser: str = '' hk_reporter_init_filter: bool = True class Config: @@ -13,3 +16,5 @@ class PlugConfig(BaseSettings): global_config = nonebot.get_driver().config plugin_config = PlugConfig(**global_config.dict()) +if plugin_config.hk_reporter_use_local: + warnings.warn('HK_REPORTER_USE_LOCAL is deprecated, please use HK_REPORTER_BROWSER') diff --git a/src/plugins/nonebot_hk_reporter/utils.py b/src/plugins/nonebot_hk_reporter/utils.py index 93e6d0b..7e0e475 100644 --- a/src/plugins/nonebot_hk_reporter/utils.py +++ b/src/plugins/nonebot_hk_reporter/utils.py @@ -1,14 +1,12 @@ import asyncio from html import escape -import os -from tempfile import NamedTemporaryFile from typing import Awaitable, Callable, Optional +from urllib.parse import quote -from pyppeteer import launch -from pyppeteer.browser import Browser -from pyppeteer.chromium_downloader import check_chromium, download_chromium -from pyppeteer.page import Page from nonebot.log import logger +from pyppeteer import connect, launch +from pyppeteer.browser import Browser +from pyppeteer.page import Page from .plugin_config import plugin_config @@ -19,9 +17,6 @@ class Singleton(type): cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] -if not plugin_config.hk_reporter_use_local and not check_chromium(): - - download_chromium() class Render(metaclass=Singleton): @@ -29,6 +24,24 @@ class Render(metaclass=Singleton): self.lock = asyncio.Lock() self.browser: Browser self.interval_log = '' + self.remote_browser = False + + async def get_browser(self) -> Browser: + if plugin_config.hk_reporter_browser: + if plugin_config.hk_reporter_browser.startswith('local:'): + path = plugin_config.hk_reporter_browser.split('local:', 1)[1] + return await launch(executablePath=path, args=['--no-sandbox']) + if plugin_config.hk_reporter_browser.startswith('ws:'): + self.remote_browser = True + return await connect(browserWSEndpoint=plugin_config.hk_reporter_browser) + raise RuntimeError('HK_REPORTER_BROWSER error') + if plugin_config.hk_reporter_use_local: + return await launch(executablePath='/usr/bin/chromium', args=['--no-sandbox']) + return await launch(args=['--no-sandbox']) + + async def close_browser(self): + if not self.remote_browser: + await self.browser.close() async def render(self, url: str, viewport: Optional[dict] = None, target: Optional[str] = None, operation: Optional[Callable[[Page], Awaitable[None]]] = None) -> Optional[str]: @@ -51,10 +64,7 @@ class Render(metaclass=Singleton): async def do_render(self, url: str, viewport: Optional[dict] = None, target: Optional[str] = None, operation: Optional[Callable[[Page], Awaitable[None]]] = None) -> str: async with self.lock: - if plugin_config.hk_reporter_use_local: - self.browser = await launch(executablePath='/usr/bin/chromium', args=['--no-sandbox']) - else: - self.browser = await launch(args=['--no-sandbox']) + self.browser = await self.get_browser() self._inter_log('open browser') page = await self.browser.newPage() if operation: @@ -73,7 +83,7 @@ class Render(metaclass=Singleton): self._inter_log('screenshot') await page.close() self._inter_log('close page') - await self.browser.close() + await self.close_browser() self._inter_log('close browser') return str(data) @@ -81,11 +91,8 @@ class Render(metaclass=Singleton): lines = text.split('\n') parsed_lines = list(map(lambda x: '

{}

'.format(escape(x)), lines)) html_text = '
{}
'.format(''.join(parsed_lines)) - with NamedTemporaryFile('wt', suffix='.html', delete=False) as tmp: - tmp_path = tmp.name - tmp.write(html_text) - data = await self.render('file://{}'.format(tmp_path), target='div') - os.remove(tmp_path) + url = 'data:text/html,{}'.format(quote(html_text)) + data = await self.render(url, target='div') return data async def text_to_pic_cqcode(self, text:str) -> str: diff --git a/tests/test_render.py b/tests/test_render.py new file mode 100644 index 0000000..f61ca19 --- /dev/null +++ b/tests/test_render.py @@ -0,0 +1,14 @@ +import pytest +import typing + +if typing.TYPE_CHECKING: + import sys + sys.path.append('./src/plugins') + import nonebot_hk_reporter + +@pytest.mark.asyncio +@pytest.mark.render +async def test_render(plugin_module: 'nonebot_hk_reporter'): + render = plugin_module.utils.Render() + res = await render.text_to_pic('a\nbbbbbbbbbbbbbbbbbbbbbb\ncd') + print(res) From eb2f3252237fbeefe843ef7fc104fd781142b662 Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Tue, 6 Jul 2021 20:08:51 +0800 Subject: [PATCH 10/11] fix #13 --- src/plugins/nonebot_hk_reporter/post.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/nonebot_hk_reporter/post.py b/src/plugins/nonebot_hk_reporter/post.py index ff0720d..11de8df 100644 --- a/src/plugins/nonebot_hk_reporter/post.py +++ b/src/plugins/nonebot_hk_reporter/post.py @@ -98,7 +98,7 @@ class Post: async def generate_messages(self): await self._pic_merge() msgs = [] - text = '来源: {}'.format(self.target_type) + text = '' if self.target_name: text += ' {}'.format(self.target_name) if self.text: @@ -111,6 +111,7 @@ class Post: if self.url: text += ' \n详情: {}'.format(self.url) msgs.append(text) + text += '来源: {}'.format(self.target_type) for pic in self.pics: msgs.append("[CQ:image,file={url}]".format(url=pic)) if self.compress: From b74defd205724df65e0fa657433c55a0aacc9e02 Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Tue, 6 Jul 2021 20:12:46 +0800 Subject: [PATCH 11/11] bump to v0.3.0 --- CHANGELOG.md | 5 ++++- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8bbd29..f684c51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,11 @@ - 增加了简单的单元测试 - 增加了管理员直接管理订阅的能力 -## [0.3.0] - 2021-06-24 +## [0.3.0] - 2021-07-06 - 微博tag支持 - 修复bug - 增加微博超话和纯文字支持 - 更改浏览器配置 +- 将“来源”移动到文末 +- 使用组合来构建新的platform,新增状态改变类型订阅 + diff --git a/pyproject.toml b/pyproject.toml index bf3890e..ae10894 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nonebot-hk-reporter" -version = "0.2.12" +version = "0.3.0" description = "Subscribe message from social medias" authors = ["felinae98 "] license = "MIT"