Support hoyolab post plugin

This commit is contained in:
omg-xtao 2024-06-27 00:01:49 +08:00 committed by GitHub
parent 5f81e8c810
commit 29f71fb0ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 306 additions and 120 deletions

View File

@ -40,8 +40,10 @@ OWNER=0
# 记录错误并发送消息通知开发人员 可选配置项
# ERROR_NOTIFICATION_CHAT_ID=chat_id
# 文章推送群组 可选配置项
# 文章推送频道 可选配置项
# CHANNELS=[]
# 文章推送群组 可选配置项
# POST_CHAT_ID=0
# 消息帮助频道 可选配置项
# CHANNELS_HELPER=0

View File

@ -0,0 +1,68 @@
from typing import List
from .hyperion import HyperionBase
from ..base.hyperionrequest import HyperionRequest
from ...models.genshin.hyperion import PostInfo, ArtworkImage, PostRecommend, HoYoPostMultiLang
__all__ = ("Hoyolab",)
class Hoyolab(HyperionBase):
POST_FULL_URL = "https://bbs-api-os.hoyolab.com/community/post/wapi/getPostFull"
NEW_LIST_URL = "https://bbs-api-os.hoyolab.com/community/post/wapi/getNewsList"
NEW_BG_URL = "https://bbs-api-os.hoyolab.com/community/painter/wapi/circle/info"
LANG = "zh-cn"
USER_AGENT = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/90.0.4430.72 Safari/537.36"
)
def __init__(self, *args, **kwargs):
self.client = HyperionRequest(headers=self.get_headers(), *args, **kwargs)
def get_headers(self, lang: str = LANG):
return {
"User-Agent": self.USER_AGENT,
"Referer": "https://www.hoyolab.com/",
"X-Rpc-Language": lang,
}
async def get_official_recommended_posts(
self, gids: int, page_size: int = 3, type_: int = 1
) -> List[PostRecommend]:
params = {"gids": gids, "page_size": page_size, "type": type_}
response = await self.client.get(url=self.NEW_LIST_URL, params=params)
return [
PostRecommend(
hoyolab=True,
post_id=data["post"]["post_id"],
subject=data["post"]["subject"],
multi_language_info=HoYoPostMultiLang(**data["post"]["multi_language_info"]),
)
for data in response["list"]
]
async def get_post_info(self, gids: int, post_id: int, read: int = 1, scene: int = 1, lang: str = LANG) -> PostInfo:
params = {"post_id": post_id, "read": read, "scene": scene}
response = await self.client.get(self.POST_FULL_URL, params=params, headers=self.get_headers(lang=lang))
return PostInfo.paste_data(response, hoyolab=True)
async def get_images_by_post_id(self, gids: int, post_id: int) -> List[ArtworkImage]:
post_info = await self.get_post_info(gids, post_id)
task_list = [
self._download_image(post_info.post_id, post_info.image_urls[page], page)
for page in range(len(post_info.image_urls))
]
return await self.get_images_by_post_id_tasks(task_list)
async def _download_image(self, art_id: int, url: str, page: int = 0) -> List[ArtworkImage]:
return await self.download_image(self.client, art_id, url, page)
async def close(self):
await self.client.shutdown()
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.close()

View File

@ -1,40 +1,31 @@
import asyncio
import os
import re
from abc import abstractmethod
from time import time
from typing import List
from typing import List, Tuple
from ..base.hyperionrequest import HyperionRequest
from ...models.genshin.hyperion import PostInfo, ArtworkImage, LiveInfo, LiveCode, LiveCodeHoYo
from ...models.genshin.hyperion import (
PostInfo,
ArtworkImage,
LiveInfo,
LiveCode,
LiveCodeHoYo,
PostRecommend,
PostTypeEnum,
)
from ...typedefs import JSON_DATA
__all__ = ("Hyperion",)
__all__ = (
"HyperionBase",
"Hyperion",
)
class Hyperion:
"""米忽悠bbs相关API请求
该名称来源于米忽悠的安卓BBS包名结尾考虑到大部分重要的功能确实是在移动端实现了
"""
POST_FULL_URL = "https://bbs-api.miyoushe.com/post/wapi/getPostFull"
POST_FULL_IN_COLLECTION_URL = "https://bbs-api.miyoushe.com/post/wapi/getPostFullInCollection"
GET_NEW_LIST_URL = "https://bbs-api.miyoushe.com/post/wapi/getNewsList"
GET_OFFICIAL_RECOMMENDED_POSTS_URL = "https://bbs-api.miyoushe.com/post/wapi/getOfficialRecommendedPosts"
LIVE_INFO_URL = "https://api-takumi.mihoyo.com/event/miyolive/index"
LIVE_CODE_URL = "https://api-takumi-static.mihoyo.com/event/miyolive/refreshCode"
LIVE_CODE_HOYO_URL = "https://bbs-api-os.hoyolab.com/community/painter/wapi/circle/channel/guide/material"
USER_AGENT = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/90.0.4430.72 Safari/537.36"
)
def __init__(self, *args, **kwargs):
self.client = HyperionRequest(headers=self.get_headers(), *args, **kwargs)
class HyperionBase:
@staticmethod
def extract_post_id(text: str) -> int:
def extract_post_id(text: str) -> Tuple[int, PostTypeEnum]:
"""
:param text:
# https://bbs.mihoyo.com/ys/article/8808224
@ -44,20 +35,19 @@ class Hyperion:
:return: post_id
"""
rgx = re.compile(r"(?:bbs|www\.)?(?:miyoushe|mihoyo)\.(.*)/[^.]+/article/(?P<article_id>\d+)")
matches = rgx.search(text)
rgx2 = re.compile(r"(?:bbs|www\.)?(?:hoyolab|hoyoverse)\.(.*)/article/(?P<article_id>\d+)")
matches = rgx.search(text) or rgx2.search(text)
if matches is None:
return -1
return -1, PostTypeEnum.NULL
entries = matches.groupdict()
if entries is None:
return -1
return -1, PostTypeEnum.NULL
try:
art_id = int(entries.get("article_id"))
post_type = PostTypeEnum.CN if "miyoushe" in text or "mihoyo" in text else PostTypeEnum.OS
except (IndexError, ValueError, TypeError):
return -1
return art_id
def get_headers(self, referer: str = "https://www.miyoushe.com/ys/"):
return {"User-Agent": self.USER_AGENT, "Referer": referer}
return -1, PostTypeEnum.NULL
return art_id, post_type
@staticmethod
def get_list_url_params(forum_id: int, is_good: bool = False, is_hot: bool = False, page_size: int = 20) -> dict:
@ -89,10 +79,79 @@ class Hyperion:
)
return {"x-oss-process": params}
async def get_official_recommended_posts(self, gids: int) -> JSON_DATA:
@staticmethod
async def get_images_by_post_id_tasks(task_list: List) -> List[ArtworkImage]:
art_list = []
result_lists = await asyncio.gather(*task_list)
for result_list in result_lists:
for result in result_list:
if isinstance(result, ArtworkImage):
art_list.append(result)
def take_page(elem: ArtworkImage):
return elem.page
art_list.sort(key=take_page)
return art_list
@staticmethod
async def download_image(client: "HyperionRequest", art_id: int, url: str, page: int = 0) -> List[ArtworkImage]:
filename = os.path.basename(url)
_, file_extension = os.path.splitext(filename)
is_image = bool(file_extension in ".jpg" or file_extension in ".png")
response = await client.get(
url, params=Hyperion.get_images_params(resize=2000) if is_image else None, de_json=False
)
return ArtworkImage.gen(
art_id=art_id, page=page, file_name=filename, file_extension=url.split(".")[-1], data=response.content
)
@abstractmethod
async def get_official_recommended_posts(self, gids: int) -> List[PostRecommend]:
"""获取官方推荐帖子"""
@abstractmethod
async def get_post_info(self, gids: int, post_id: int, read: int = 1) -> PostInfo:
"""获取帖子信息"""
@abstractmethod
async def get_images_by_post_id(self, gids: int, post_id: int) -> List[ArtworkImage]:
"""获取帖子图片"""
@abstractmethod
async def close(self):
"""关闭请求会话"""
class Hyperion(HyperionBase):
"""米忽悠bbs相关API请求
该名称来源于米忽悠的安卓BBS包名结尾考虑到大部分重要的功能确实是在移动端实现了
"""
POST_FULL_URL = "https://bbs-api.miyoushe.com/post/wapi/getPostFull"
POST_FULL_IN_COLLECTION_URL = "https://bbs-api.miyoushe.com/post/wapi/getPostFullInCollection"
GET_NEW_LIST_URL = "https://bbs-api.miyoushe.com/post/wapi/getNewsList"
GET_OFFICIAL_RECOMMENDED_POSTS_URL = "https://bbs-api.miyoushe.com/post/wapi/getOfficialRecommendedPosts"
LIVE_INFO_URL = "https://api-takumi.mihoyo.com/event/miyolive/index"
LIVE_CODE_URL = "https://api-takumi-static.mihoyo.com/event/miyolive/refreshCode"
LIVE_CODE_HOYO_URL = "https://bbs-api-os.hoyolab.com/community/painter/wapi/circle/channel/guide/material"
USER_AGENT = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/90.0.4430.72 Safari/537.36"
)
def __init__(self, *args, **kwargs):
self.client = HyperionRequest(headers=self.get_headers(), *args, **kwargs)
def get_headers(self, referer: str = "https://www.miyoushe.com/ys/"):
return {"User-Agent": self.USER_AGENT, "Referer": referer}
async def get_official_recommended_posts(self, gids: int) -> List[PostRecommend]:
params = {"gids": gids}
response = await self.client.get(url=self.GET_OFFICIAL_RECOMMENDED_POSTS_URL, params=params)
return response
return [PostRecommend(**data) for data in response["list"]]
async def get_post_full_in_collection(self, collection_id: int, gids: int = 2, order_type=1) -> JSON_DATA:
params = {"collection_id": collection_id, "gids": gids, "order_type": order_type}
@ -106,33 +165,14 @@ class Hyperion:
async def get_images_by_post_id(self, gids: int, post_id: int) -> List[ArtworkImage]:
post_info = await self.get_post_info(gids, post_id)
art_list = []
task_list = [
self.download_image(post_info.post_id, post_info.image_urls[page], page)
self._download_image(post_info.post_id, post_info.image_urls[page], page)
for page in range(len(post_info.image_urls))
]
result_lists = await asyncio.gather(*task_list)
for result_list in result_lists:
for result in result_list:
if isinstance(result, ArtworkImage):
art_list.append(result)
return await self.get_images_by_post_id_tasks(task_list)
def take_page(elem: ArtworkImage):
return elem.page
art_list.sort(key=take_page)
return art_list
async def download_image(self, art_id: int, url: str, page: int = 0) -> List[ArtworkImage]:
filename = os.path.basename(url)
_, file_extension = os.path.splitext(filename)
is_image = bool(file_extension in ".jpg" or file_extension in ".png")
response = await self.client.get(
url, params=self.get_images_params(resize=2000) if is_image else None, de_json=False
)
return ArtworkImage.gen(
art_id=art_id, page=page, file_name=filename, file_extension=url.split(".")[-1], data=response.content
)
async def _download_image(self, art_id: int, url: str, page: int = 0) -> List[ArtworkImage]:
return await self.download_image(self.client, art_id, url, page)
async def get_new_list(self, gids: int, type_id: int, page_size: int = 20):
"""

View File

@ -1,11 +1,21 @@
from datetime import datetime, timedelta
from enum import Enum
from io import BytesIO
from typing import Any, List, Optional
from PIL import Image, UnidentifiedImageError
from pydantic import BaseModel, PrivateAttr
__all__ = ("ArtworkImage", "PostInfo", "LiveInfo", "LiveCode", "LiveCodeHoYo")
__all__ = (
"ArtworkImage",
"PostInfo",
"LiveInfo",
"LiveCode",
"LiveCodeHoYo",
"PostTypeEnum",
"PostRecommend",
"HoYoPostMultiLang",
)
class ArtworkImage(BaseModel):
@ -55,6 +65,7 @@ class ArtworkImage(BaseModel):
class PostInfo(BaseModel):
_data: dict = PrivateAttr()
hoyolab: bool
post_id: int
user_uid: int
subject: str
@ -67,20 +78,21 @@ class PostInfo(BaseModel):
self._data = _data
@classmethod
def paste_data(cls, data: dict) -> "PostInfo":
def paste_data(cls, data: dict, hoyolab: bool = False) -> "PostInfo":
_data_post = data["post"]
post = _data_post["post"]
post_id = post["post_id"]
subject = post["subject"]
image_list = _data_post["image_list"]
image_urls = [image["url"] for image in image_list]
vod_list = _data_post["vod_list"]
vod_list = _data_post.get("vod_list", [])
video_urls = [vod["resolutions"][-1]["url"] for vod in vod_list]
created_at = post["created_at"]
user = _data_post["user"] # 用户数据
user_uid = user["uid"] # 用户ID
return PostInfo(
_data=data,
hoyolab=hoyolab,
post_id=post_id,
user_uid=user_uid,
subject=subject,
@ -92,6 +104,19 @@ class PostInfo(BaseModel):
def __getitem__(self, item):
return self._data[item]
@property
def type_enum(self) -> "PostTypeEnum":
return PostTypeEnum.CN if not self.hoyolab else PostTypeEnum.OS
def get_url(self, short_name: str) -> str:
if not self.hoyolab:
return f"https://www.miyoushe.com/{short_name}/article/{self.post_id}"
return f"https://www.hoyolab.com/article/{self.post_id}"
def get_fix_url(self, short_name: str) -> str:
url = self.get_url(short_name)
return url.replace(".com/", ".pp.ua/")
class LiveInfo(BaseModel):
act_type: str
@ -125,3 +150,24 @@ class LiveCodeHoYo(BaseModel):
@staticmethod
def guess_offline_at() -> datetime:
return datetime.now().replace(hour=23, minute=59, second=59, microsecond=999999) + timedelta(days=1)
class PostTypeEnum(str, Enum):
"""社区类型枚举"""
NULL = "null"
CN = "cn"
OS = "os"
class HoYoPostMultiLang(BaseModel):
lang_subject: dict
class PostRecommend(BaseModel):
hoyolab: bool = False
post_id: int
subject: str
banner: Optional[str] = None
official_type: Optional[int] = None
multi_language_info: Optional[HoYoPostMultiLang] = None

View File

@ -4,8 +4,8 @@
[metadata]
groups = ["default", "pyro", "test"]
strategy = ["cross_platform", "inherit_metadata"]
lock_version = "4.4.1"
content_hash = "sha256:7c20ef5aa9a946f2b922539de88fe7d316e2937a79c4e6f7c2a383316dca30c7"
lock_version = "4.4.2"
content_hash = "sha256:e65015a34b2d9388ab1032dfe348ae78abd4948a975192d3376819bb2979d8bd"
[[package]]
name = "aiocsv"
@ -1038,7 +1038,7 @@ files = [
[[package]]
name = "importlib-metadata"
version = "7.2.1"
version = "8.0.0"
requires_python = ">=3.8"
summary = "Read metadata from Python packages"
groups = ["default"]
@ -1047,8 +1047,8 @@ dependencies = [
"zipp>=0.5",
]
files = [
{file = "importlib_metadata-7.2.1-py3-none-any.whl", hash = "sha256:ffef94b0b66046dd8ea2d619b701fe978d9264d38f3998bc4c27ec3b146a87c8"},
{file = "importlib_metadata-7.2.1.tar.gz", hash = "sha256:509ecb2ab77071db5137c655e24ceb3eee66e7bbc6574165d0d114d9fc4bbe68"},
{file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"},
{file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"},
]
[[package]]
@ -2127,7 +2127,7 @@ files = [
[[package]]
name = "redis"
version = "5.0.6"
version = "5.0.7"
requires_python = ">=3.7"
summary = "Python client for Redis database and key-value store"
groups = ["default"]
@ -2135,8 +2135,8 @@ dependencies = [
"async-timeout>=4.0.3; python_full_version < \"3.11.3\"",
]
files = [
{file = "redis-5.0.6-py3-none-any.whl", hash = "sha256:c0d6d990850c627bbf7be01c5c4cbaadf67b48593e913bb71c9819c30df37eee"},
{file = "redis-5.0.6.tar.gz", hash = "sha256:38473cd7c6389ad3e44a91f4c3eaf6bcb8a9f746007f29bf4fb20824ff0b2197"},
{file = "redis-5.0.7-py3-none-any.whl", hash = "sha256:0e479e24da960c690be5d9b96d21f7b918a98c0cf49af3b6fafaa0753f93a0db"},
{file = "redis-5.0.7.tar.gz", hash = "sha256:8f611490b93c8109b50adc317b31bfd84fff31def3475b92e7e80bf39f48175b"},
]
[[package]]
@ -2157,7 +2157,7 @@ files = [
[[package]]
name = "sentry-sdk"
version = "2.6.0"
version = "2.7.0"
requires_python = ">=3.6"
summary = "Python client for Sentry (https://sentry.io)"
groups = ["default"]
@ -2166,19 +2166,19 @@ dependencies = [
"urllib3>=1.26.11",
]
files = [
{file = "sentry_sdk-2.6.0-py2.py3-none-any.whl", hash = "sha256:422b91cb49378b97e7e8d0e8d5a1069df23689d45262b86f54988a7db264e874"},
{file = "sentry_sdk-2.6.0.tar.gz", hash = "sha256:65cc07e9c6995c5e316109f138570b32da3bd7ff8d0d0ee4aaf2628c3dd8127d"},
{file = "sentry_sdk-2.7.0-py2.py3-none-any.whl", hash = "sha256:db9594c27a4d21c1ebad09908b1f0dc808ef65c2b89c1c8e7e455143262e37c1"},
{file = "sentry_sdk-2.7.0.tar.gz", hash = "sha256:d846a211d4a0378b289ced3c434480945f110d0ede00450ba631fc2852e7a0d4"},
]
[[package]]
name = "setuptools"
version = "70.1.0"
version = "70.1.1"
requires_python = ">=3.8"
summary = "Easily download, build, install, upgrade, and uninstall Python packages"
groups = ["default"]
files = [
{file = "setuptools-70.1.0-py3-none-any.whl", hash = "sha256:d9b8b771455a97c8a9f3ab3448ebe0b29b5e105f1228bba41028be116985a267"},
{file = "setuptools-70.1.0.tar.gz", hash = "sha256:01a1e793faa5bd89abc851fa15d0a0db26f160890c7102cd8dce643e886b47f5"},
{file = "setuptools-70.1.1-py3-none-any.whl", hash = "sha256:a58a8fde0541dab0419750bcc521fbdf8585f6e5cb41909df3a472ef7b81ca95"},
{file = "setuptools-70.1.1.tar.gz", hash = "sha256:937a48c7cdb7a21eb53cd7f9b59e525503aa8abaf3584c730dc5f7a5bec3a650"},
]
[[package]]

View File

@ -1,7 +1,8 @@
import math
import os
from asyncio import create_subprocess_shell, subprocess
from typing import List, Optional, Tuple, TYPE_CHECKING, Union
from functools import partial
from typing import List, Optional, Tuple, TYPE_CHECKING, Union, Dict
import aiofiles
from arkowrapper import ArkoWrapper
@ -23,9 +24,11 @@ from telegram.helpers import escape_markdown
from core.config import config
from core.plugin import Plugin, conversation, handler
from modules.apihelper.client.components.hyperion import Hyperion
from gram_core.basemodel import Settings
from modules.apihelper.client.components.hoyolab import Hoyolab
from modules.apihelper.client.components.hyperion import Hyperion, HyperionBase
from modules.apihelper.error import APIHelperException
from modules.apihelper.models.genshin.hyperion import ArtworkImage
from modules.apihelper.models.genshin.hyperion import ArtworkImage, PostTypeEnum
from utils.helpers import sha1
from utils.log import logger
@ -44,8 +47,18 @@ class PostHandlerData:
self.tags: Optional[List[str]] = []
class PostConfig(Settings):
"""文章推送配置"""
chat_id: Optional[int] = 0
class Config(Settings.Config):
env_prefix = "post_"
CHECK_POST, SEND_POST, CHECK_COMMAND, GTE_DELETE_PHOTO = range(10900, 10904)
GET_POST_CHANNEL, GET_TAGS, GET_TEXT, GET_VIDEO = range(10904, 10908)
post_config = PostConfig()
class Post(Plugin.Conversation):
@ -58,13 +71,14 @@ class Post(Plugin.Conversation):
def __init__(self):
self.gids = 6
self.short_name = "sr"
self.last_post_id_list: List[int] = []
self.last_post_id_list: Dict[PostTypeEnum, List[int]] = {PostTypeEnum.CN: [], PostTypeEnum.OS: []}
self.ffmpeg_enable = False
self.cache_dir = os.path.join(os.getcwd(), "cache")
@staticmethod
def get_bbs_client() -> Hyperion:
return Hyperion(
def get_bbs_client(bbs_type: "PostTypeEnum") -> "HyperionBase":
class_type = Hyperion if bbs_type == PostTypeEnum.CN else Hoyolab
return class_type(
timeout=Timeout(
connect=config.connect_timeout,
read=config.read_timeout,
@ -76,7 +90,10 @@ class Post(Plugin.Conversation):
async def initialize(self):
if config.channels and len(config.channels) > 0:
logger.success("文章定时推送处理已经开启")
self.application.job_queue.run_repeating(self.task, 60)
cn_task = partial(self.task, post_type=PostTypeEnum.CN)
os_task = partial(self.task, post_type=PostTypeEnum.OS)
self.application.job_queue.run_repeating(cn_task, 30, name="post_cn_task")
self.application.job_queue.run_repeating(os_task, 30, name="post_os_task")
logger.success("文章定时推送处理已经开启")
output, _ = await self.execute("ffmpeg -version")
if "ffmpeg version" in output:
@ -86,8 +103,8 @@ class Post(Plugin.Conversation):
else:
logger.warning("ffmpeg 不可用 已经禁用编码转换")
async def task(self, context: "ContextTypes.DEFAULT_TYPE"):
bbs = self.get_bbs_client()
async def task(self, context: "ContextTypes.DEFAULT_TYPE", post_type: "PostTypeEnum"):
bbs = self.get_bbs_client(post_type)
temp_post_id_list: List[int] = []
# 请求推荐POST列表并处理
@ -97,22 +114,25 @@ class Post(Plugin.Conversation):
logger.error("获取首页推荐信息失败 %s", str(exc))
return
for data_list in official_recommended_posts["list"]:
temp_post_id_list.append(data_list["post_id"])
for data_list in official_recommended_posts:
temp_post_id_list.append(data_list.post_id)
last_post_id_list = self.last_post_id_list[post_type]
# 判断是否为空
if len(self.last_post_id_list) == 0:
if len(last_post_id_list) == 0:
for temp_list in temp_post_id_list:
self.last_post_id_list.append(temp_list)
last_post_id_list.append(temp_list)
return
# 筛选出新推送的文章
new_post_id_list = set(temp_post_id_list).difference(set(self.last_post_id_list))
last_post_id_list = self.last_post_id_list[post_type]
new_post_id_list = set(temp_post_id_list).difference(set(last_post_id_list))
if not new_post_id_list:
return
self.last_post_id_list = temp_post_id_list
self.last_post_id_list[post_type] = temp_post_id_list
chat_id = post_config.chat_id or config.owner
for post_id in new_post_id_list:
try:
@ -121,21 +141,26 @@ class Post(Plugin.Conversation):
logger.error("获取文章信息失败 %s", str(exc))
text = f"获取 post_id[{post_id}] 文章信息失败 {str(exc)}"
try:
await context.bot.send_message(config.owner, text)
await context.bot.send_message(chat_id, text)
except BadRequest as _exc:
logger.error("发送消息失败 %s", _exc.message)
return
type_name = post_info.type_enum.value
buttons = [
[
InlineKeyboardButton("确认", callback_data=f"post_admin|confirm|{post_info.post_id}"),
InlineKeyboardButton("取消", callback_data=f"post_admin|cancel|{post_info.post_id}"),
InlineKeyboardButton("确认", callback_data=f"post_admin|confirm|{type_name}|{post_info.post_id}"),
InlineKeyboardButton("取消", callback_data=f"post_admin|cancel|{type_name}|{post_info.post_id}"),
]
]
url = f"https://www.miyoushe.pp.ua/{self.short_name}/article/{post_info.post_id}"
text = f"发现官网推荐文章 <a href='{url}'>{post_info.subject}</a>\n是否开始处理"
url = post_info.get_fix_url(self.short_name)
tag = f"#{self.short_name} #{post_type.value} #{self.short_name}_{post_type.value}"
text = f"发现官网推荐文章 <a href='{url}'>{post_info.subject}</a>\n是否开始处理 {tag}"
try:
await context.bot.send_message(
config.owner, text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(buttons)
chat_id,
text,
parse_mode=ParseMode.HTML,
reply_markup=InlineKeyboardMarkup(buttons),
)
except BadRequest as exc:
logger.error("发送消息失败 %s", exc.message)
@ -268,29 +293,31 @@ class Post(Plugin.Conversation):
message = callback_query.message
logger.info("用户 %s[%s] POST命令请求", user.full_name, user.id)
async def get_post_admin_callback(callback_query_data: str) -> Tuple[str, int]:
async def get_post_admin_callback(callback_query_data: str) -> Tuple[str, PostTypeEnum, int]:
_data = callback_query_data.split("|")
_result = _data[1]
_post_id = int(_data[2])
logger.debug("callback_query_data函数返回 result[%s] post_id[%s]", _result, _post_id)
return _result, _post_id
_post_type = PostTypeEnum(_data[2])
_post_id = int(_data[3])
logger.debug(
"callback_query_data函数返回 result[%s] _post_type[%s] post_id[%s]", _result, _post_type, _post_id
)
return _result, _post_type, _post_id
result, post_id = await get_post_admin_callback(callback_query.data)
result, post_type, post_id = await get_post_admin_callback(callback_query.data)
if result == "cancel":
await message.reply_text("操作已经取消")
await message.delete()
elif result == "confirm":
reply_text = await message.reply_text("正在处理")
status = await self.send_post_info(post_handler_data, message, post_id)
status = await self.send_post_info(post_handler_data, message, post_id, post_type)
await reply_text.delete()
return status
await message.reply_text("非法参数")
return ConversationHandler.END
@conversation.entry_point
@handler.command(command="post", filters=filters.ChatType.PRIVATE, block=False, admin=True)
@handler.command(command="post", block=False, admin=True)
async def command_start(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
user = update.effective_user
message = update.effective_message
@ -313,14 +340,16 @@ class Post(Plugin.Conversation):
await message.reply_text("退出投稿", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
post_id = Hyperion.extract_post_id(update.message.text)
post_id, post_type = Hyperion.extract_post_id(update.message.text)
if post_id == -1:
await message.reply_text("获取作品ID错误请检查连接是否合法", reply_markup=ReplyKeyboardRemove())
return ConversationHandler.END
return await self.send_post_info(post_handler_data, message, post_id)
return await self.send_post_info(post_handler_data, message, post_id, post_type)
async def send_post_info(self, post_handler_data: PostHandlerData, message: "Message", post_id: int) -> int:
bbs = self.get_bbs_client()
async def send_post_info(
self, post_handler_data: PostHandlerData, message: "Message", post_id: int, post_type: "PostTypeEnum"
) -> int:
bbs = self.get_bbs_client(post_type)
post_info = await bbs.get_post_info(self.gids, post_id)
post_images = await bbs.get_images_by_post_id(self.gids, post_id)
await bbs.close()
@ -329,7 +358,8 @@ class Post(Plugin.Conversation):
post_subject = post_data["subject"]
post_soup = BeautifulSoup(post_data["content"], features="html.parser")
post_text, too_long = self.parse_post_text(post_soup, post_subject)
post_text += f"\n[source](https://www.miyoushe.com/{self.short_name}/article/{post_id})"
url = post_info.get_url(self.short_name)
post_text += f"\n[source]({url})"
if too_long or len(post_text) >= MessageLimit.CAPTION_LENGTH:
post_text = post_text[: MessageLimit.CAPTION_LENGTH]
await message.reply_text(f"警告!图片字符描述已经超过 {MessageLimit.CAPTION_LENGTH} 个字,已经切割")

View File

@ -34,7 +34,7 @@ dependencies = [
"arko-wrapper<1.0.0,>=0.2.8",
"fastapi<1.0.0,>=0.111.0",
"uvicorn[standard]<1.0.0,>=0.30.1",
"sentry-sdk<3.0.0,>=2.6.0",
"sentry-sdk<3.0.0,>=2.7.0",
"GitPython<4.0.0,>=3.1.30",
"openpyxl<4.0.0,>=3.1.1",
"async-lru<3.0.0,>=2.0.4",

View File

@ -6,7 +6,7 @@ aiofiles==24.1.0
aiohttp==3.9.5
aiolimiter==1.1.0
aiosignal==1.3.1
aiosqlite==0.20.0
aiosqlite[sqlite]==0.20.0
alembic==1.13.1
anyio==4.4.0
apscheduler==3.10.4
@ -43,9 +43,9 @@ httpcore==1.0.5
httptools==0.6.1
httpx==0.27.0
idna==3.7
importlib-metadata==7.2.1; python_version < "3.9"
importlib-metadata==8.0.0; python_version < "3.9"
importlib-resources==6.4.0; python_version < "3.9"
influxdb-client==1.44.0
influxdb-client[async,ciso]==1.44.0
iniconfig==2.0.0
jinja2==3.1.4
lxml==5.2.2
@ -77,15 +77,15 @@ pytest-asyncio==0.23.7
python-dateutil==2.9.0.post0
python-dotenv==1.0.1
python-multipart==0.0.9
python-telegram-bot==21.3
python-telegram-bot[ext,rate-limiter]==21.3
pytz==2024.1
pyyaml==6.0.1
rapidfuzz==3.9.3
reactivex==4.0.4
redis==5.0.6
redis==5.0.7
rich==13.7.1
sentry-sdk==2.6.0
setuptools==70.1.0
sentry-sdk==2.7.0
setuptools==70.1.1
shellingham==1.5.4
simnet @ git+https://github.com/PaiGramTeam/SIMNet@277a33321a20909541b46bf4ecf794fd47e19fb1
six==1.16.0
@ -108,7 +108,7 @@ tzdata==2024.1; platform_system == "Windows"
tzlocal==5.2
ujson==5.10.0
urllib3==2.2.2
uvicorn==0.30.1
uvicorn[standard]==0.30.1
uvloop==0.19.0; (sys_platform != "cygwin" and sys_platform != "win32") and platform_python_implementation != "PyPy"
watchfiles==0.22.0
websockets==12.0