2023-09-30 02:49:04 +00:00
|
|
|
|
import math
|
2023-04-14 03:53:15 +00:00
|
|
|
|
import os
|
2023-04-14 12:51:39 +00:00
|
|
|
|
from asyncio import create_subprocess_shell, subprocess
|
2023-03-22 05:06:05 +00:00
|
|
|
|
from typing import List, Optional, Tuple, TYPE_CHECKING, Union
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2023-04-14 03:53:15 +00:00
|
|
|
|
import aiofiles
|
2023-09-30 02:49:04 +00:00
|
|
|
|
from arkowrapper import ArkoWrapper
|
2023-03-14 01:27:22 +00:00
|
|
|
|
from bs4 import BeautifulSoup
|
2023-03-22 05:09:24 +00:00
|
|
|
|
from httpx import Timeout
|
2022-10-13 07:38:47 +00:00
|
|
|
|
from telegram import (
|
|
|
|
|
InlineKeyboardButton,
|
|
|
|
|
InlineKeyboardMarkup,
|
2023-03-14 01:27:22 +00:00
|
|
|
|
InputMediaPhoto,
|
|
|
|
|
ReplyKeyboardMarkup,
|
|
|
|
|
ReplyKeyboardRemove,
|
2023-03-22 05:06:05 +00:00
|
|
|
|
InputMediaDocument,
|
|
|
|
|
InputMediaVideo,
|
2022-10-13 07:38:47 +00:00
|
|
|
|
)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
from telegram.constants import MessageLimit, ParseMode
|
2022-08-06 08:55:53 +00:00
|
|
|
|
from telegram.error import BadRequest
|
2023-03-22 05:06:05 +00:00
|
|
|
|
from telegram.ext import ConversationHandler, filters
|
2022-08-06 08:55:53 +00:00
|
|
|
|
from telegram.helpers import escape_markdown
|
|
|
|
|
|
2022-10-13 07:38:47 +00:00
|
|
|
|
from core.config import config
|
2022-09-08 01:08:37 +00:00
|
|
|
|
from core.plugin import Plugin, conversation, handler
|
2022-12-10 12:37:43 +00:00
|
|
|
|
from modules.apihelper.client.components.hyperion import Hyperion
|
2022-10-13 07:38:47 +00:00
|
|
|
|
from modules.apihelper.error import APIHelperException
|
2023-04-14 12:51:39 +00:00
|
|
|
|
from utils.helpers import sha1
|
2022-09-08 01:08:37 +00:00
|
|
|
|
from utils.log import logger
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2023-03-22 05:06:05 +00:00
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
from bs4 import Tag
|
|
|
|
|
from telegram import Update, Message
|
|
|
|
|
from telegram.ext import ContextTypes
|
|
|
|
|
from modules.apihelper.models.genshin.hyperion import ArtworkImage
|
|
|
|
|
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
|
|
|
|
class PostHandlerData:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.post_text: str = ""
|
2023-03-22 05:06:05 +00:00
|
|
|
|
self.post_images: Optional[List["ArtworkImage"]] = None
|
2022-08-06 08:55:53 +00:00
|
|
|
|
self.delete_photo: Optional[List[int]] = []
|
|
|
|
|
self.channel_id: int = -1
|
|
|
|
|
self.tags: Optional[List[str]] = []
|
|
|
|
|
|
|
|
|
|
|
2022-09-08 01:08:37 +00:00
|
|
|
|
CHECK_POST, SEND_POST, CHECK_COMMAND, GTE_DELETE_PHOTO = range(10900, 10904)
|
|
|
|
|
GET_POST_CHANNEL, GET_TAGS, GET_TEXT = range(10904, 10907)
|
|
|
|
|
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2023-03-14 01:27:22 +00:00
|
|
|
|
class Post(Plugin.Conversation):
|
2022-09-08 01:08:37 +00:00
|
|
|
|
"""文章推送"""
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
|
|
|
|
MENU_KEYBOARD = ReplyKeyboardMarkup([["推送频道", "添加TAG"], ["编辑文字", "删除图片"], ["退出"]], True, True)
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
2023-04-10 14:13:29 +00:00
|
|
|
|
self.gids = 2
|
|
|
|
|
self.short_name = "ys"
|
2023-05-14 14:42:42 +00:00
|
|
|
|
self.last_post_id_list: List[int] = []
|
|
|
|
|
self.ffmpeg_enable = False
|
|
|
|
|
self.cache_dir = os.path.join(os.getcwd(), "cache")
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def get_bbs_client() -> Hyperion:
|
|
|
|
|
return Hyperion(
|
2023-03-22 05:09:24 +00:00
|
|
|
|
timeout=Timeout(
|
|
|
|
|
connect=config.connect_timeout,
|
|
|
|
|
read=config.read_timeout,
|
|
|
|
|
write=config.write_timeout,
|
|
|
|
|
pool=config.pool_timeout,
|
|
|
|
|
)
|
|
|
|
|
)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
|
|
|
|
|
async def initialize(self):
|
2022-10-22 06:35:18 +00:00
|
|
|
|
if config.channels and len(config.channels) > 0:
|
2022-10-13 07:38:47 +00:00
|
|
|
|
logger.success("文章定时推送处理已经开启")
|
2023-03-14 01:27:22 +00:00
|
|
|
|
self.application.job_queue.run_repeating(self.task, 60)
|
2023-04-14 03:53:15 +00:00
|
|
|
|
logger.success("文章定时推送处理已经开启")
|
2023-04-14 12:51:39 +00:00
|
|
|
|
output, _ = await self.execute("ffmpeg -version")
|
2023-04-14 03:53:15 +00:00
|
|
|
|
if "ffmpeg version" in output:
|
|
|
|
|
self.ffmpeg_enable = True
|
|
|
|
|
logger.info("检测到 ffmpeg 可用 已经启动编码转换")
|
|
|
|
|
logger.debug("ffmpeg version info\n%s", output)
|
|
|
|
|
else:
|
2023-04-14 12:51:39 +00:00
|
|
|
|
logger.warning("ffmpeg 不可用 已经禁用编码转换")
|
2022-10-13 07:38:47 +00:00
|
|
|
|
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def task(self, context: "ContextTypes.DEFAULT_TYPE"):
|
2023-05-14 14:42:42 +00:00
|
|
|
|
bbs = self.get_bbs_client()
|
2022-10-13 07:38:47 +00:00
|
|
|
|
temp_post_id_list: List[int] = []
|
|
|
|
|
|
|
|
|
|
# 请求推荐POST列表并处理
|
|
|
|
|
try:
|
2023-05-14 14:42:42 +00:00
|
|
|
|
official_recommended_posts = await bbs.get_official_recommended_posts(self.gids)
|
2022-10-13 07:38:47 +00:00
|
|
|
|
except APIHelperException as exc:
|
2022-12-07 02:29:00 +00:00
|
|
|
|
logger.error("获取首页推荐信息失败 %s", str(exc))
|
2022-10-13 07:38:47 +00:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
for data_list in official_recommended_posts["list"]:
|
|
|
|
|
temp_post_id_list.append(data_list["post_id"])
|
|
|
|
|
|
|
|
|
|
# 判断是否为空
|
|
|
|
|
if len(self.last_post_id_list) == 0:
|
|
|
|
|
for temp_list in temp_post_id_list:
|
|
|
|
|
self.last_post_id_list.append(temp_list)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 筛选出新推送的文章
|
|
|
|
|
new_post_id_list = set(temp_post_id_list).difference(set(self.last_post_id_list))
|
|
|
|
|
|
2023-04-10 14:13:29 +00:00
|
|
|
|
if not new_post_id_list:
|
2022-10-13 07:38:47 +00:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.last_post_id_list = temp_post_id_list
|
|
|
|
|
|
2022-10-22 06:35:18 +00:00
|
|
|
|
for post_id in new_post_id_list:
|
2022-10-13 07:38:47 +00:00
|
|
|
|
try:
|
2023-05-14 14:42:42 +00:00
|
|
|
|
post_info = await bbs.get_post_info(self.gids, post_id)
|
2022-10-13 07:38:47 +00:00
|
|
|
|
except APIHelperException as exc:
|
2022-12-07 02:29:00 +00:00
|
|
|
|
logger.error("获取文章信息失败 %s", str(exc))
|
|
|
|
|
text = f"获取 post_id[{post_id}] 文章信息失败 {str(exc)}"
|
2022-10-13 07:38:47 +00:00
|
|
|
|
for user in config.admins:
|
|
|
|
|
try:
|
|
|
|
|
await context.bot.send_message(user.user_id, text)
|
|
|
|
|
except BadRequest as _exc:
|
2023-03-22 05:06:05 +00:00
|
|
|
|
logger.error("发送消息失败 %s", _exc.message)
|
2022-10-13 07:38:47 +00:00
|
|
|
|
return
|
|
|
|
|
buttons = [
|
|
|
|
|
[
|
|
|
|
|
InlineKeyboardButton("确认", callback_data=f"post_admin|confirm|{post_info.post_id}"),
|
|
|
|
|
InlineKeyboardButton("取消", callback_data=f"post_admin|cancel|{post_info.post_id}"),
|
|
|
|
|
]
|
|
|
|
|
]
|
2024-03-20 13:17:59 +00:00
|
|
|
|
url = f"https://www.miyoushe.pp.ua/{self.short_name}/article/{post_info.post_id}"
|
2022-10-13 07:38:47 +00:00
|
|
|
|
text = f"发现官网推荐文章 <a href='{url}'>{post_info.subject}</a>\n是否开始处理"
|
2023-03-14 10:19:29 +00:00
|
|
|
|
try:
|
|
|
|
|
await context.bot.send_message(
|
|
|
|
|
config.owner, text, parse_mode=ParseMode.HTML, reply_markup=InlineKeyboardMarkup(buttons)
|
|
|
|
|
)
|
|
|
|
|
except BadRequest as exc:
|
|
|
|
|
logger.error("发送消息失败 %s", exc.message)
|
2023-05-14 14:42:42 +00:00
|
|
|
|
await bbs.close()
|
2022-10-13 07:38:47 +00:00
|
|
|
|
|
2023-03-22 05:06:05 +00:00
|
|
|
|
@staticmethod
|
2023-09-30 02:49:04 +00:00
|
|
|
|
def parse_post_text(soup: BeautifulSoup, post_subject: str) -> Tuple[str, bool]:
|
2023-03-22 05:06:05 +00:00
|
|
|
|
def parse_tag(_tag: "Tag") -> str:
|
2023-05-23 11:27:02 +00:00
|
|
|
|
if _tag.name == "a":
|
|
|
|
|
href = _tag.get("href")
|
|
|
|
|
if href and href.startswith("/"):
|
|
|
|
|
href = f"https://www.miyoushe.com{href}"
|
|
|
|
|
if href and href.startswith("http"):
|
|
|
|
|
return f"[{escape_markdown(_tag.get_text(), version=2)}]({href})"
|
2023-03-22 05:06:05 +00:00
|
|
|
|
return escape_markdown(_tag.get_text(), version=2)
|
|
|
|
|
|
|
|
|
|
post_text = f"*{escape_markdown(post_subject, version=2)}*\n\n"
|
|
|
|
|
start = True
|
2023-09-30 02:49:04 +00:00
|
|
|
|
too_long = False
|
2023-04-10 14:13:29 +00:00
|
|
|
|
if post_p := soup.find_all("p"):
|
2023-09-30 02:49:04 +00:00
|
|
|
|
try:
|
|
|
|
|
for p in post_p:
|
|
|
|
|
t = p.get_text()
|
|
|
|
|
if not t and start:
|
|
|
|
|
continue
|
|
|
|
|
start = False
|
|
|
|
|
for tag in p.contents:
|
|
|
|
|
post_text_ = post_text + parse_tag(tag)
|
|
|
|
|
if len(post_text_) >= (MessageLimit.CAPTION_LENGTH - 55):
|
|
|
|
|
raise RecursionError
|
|
|
|
|
post_text = post_text_
|
|
|
|
|
post_text += "\n"
|
|
|
|
|
except RecursionError:
|
|
|
|
|
too_long = True
|
2023-04-10 14:13:29 +00:00
|
|
|
|
else:
|
|
|
|
|
post_text += f"{escape_markdown(soup.get_text(), version=2)}\n"
|
2023-09-30 02:49:04 +00:00
|
|
|
|
return post_text, too_long
|
2023-03-22 05:06:05 +00:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def input_media(
|
|
|
|
|
media: "ArtworkImage", *args, **kwargs
|
|
|
|
|
) -> Union[None, InputMediaDocument, InputMediaPhoto, InputMediaVideo]:
|
2023-04-14 03:53:15 +00:00
|
|
|
|
file_extension = media.file_extension
|
|
|
|
|
filename = media.file_name
|
2023-09-30 02:49:04 +00:00
|
|
|
|
doc = None
|
2023-04-14 03:53:15 +00:00
|
|
|
|
if file_extension is not None:
|
|
|
|
|
if file_extension in {"jpg", "jpeg", "png", "webp"}:
|
2023-09-30 02:49:04 +00:00
|
|
|
|
doc = InputMediaPhoto(media.data, *args, **kwargs)
|
2023-04-14 03:53:15 +00:00
|
|
|
|
if file_extension in {"gif", "mp4", "mov", "avi", "mkv", "webm", "flv"}:
|
2023-09-30 02:49:04 +00:00
|
|
|
|
doc = InputMediaVideo(media.data, filename=filename, *args, **kwargs)
|
|
|
|
|
if not doc:
|
|
|
|
|
doc = InputMediaDocument(media.data, *args, **kwargs)
|
|
|
|
|
doc._frozen = False
|
|
|
|
|
return doc
|
2023-03-22 05:06:05 +00:00
|
|
|
|
|
2023-04-14 12:51:39 +00:00
|
|
|
|
@staticmethod
|
|
|
|
|
async def execute(command: str) -> Tuple[str, int]:
|
|
|
|
|
process = await create_subprocess_shell(
|
|
|
|
|
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE
|
|
|
|
|
)
|
|
|
|
|
stdout, stderr = await process.communicate()
|
|
|
|
|
try:
|
|
|
|
|
result = str(stdout.decode().strip()) + str(stderr.decode().strip())
|
|
|
|
|
except UnicodeDecodeError:
|
|
|
|
|
result = str(stdout.decode("gbk").strip()) + str(stderr.decode("gbk").strip())
|
|
|
|
|
return result, process.returncode
|
|
|
|
|
|
2023-04-14 03:53:15 +00:00
|
|
|
|
@staticmethod
|
|
|
|
|
def get_ffmpeg_command(input_file: str, output_file: str):
|
2023-04-14 12:51:39 +00:00
|
|
|
|
return (
|
|
|
|
|
f'ffmpeg -i "{input_file}" '
|
|
|
|
|
f'-c:v libx264 -crf 20 -vf "fps=30,format=yuv420p,'
|
|
|
|
|
f'scale=trunc(iw/2)*2:trunc(ih/2)*2" -y "{output_file}"'
|
|
|
|
|
)
|
2023-04-14 03:53:15 +00:00
|
|
|
|
|
|
|
|
|
async def gif_to_mp4(self, media: "List[ArtworkImage]"):
|
|
|
|
|
if self.ffmpeg_enable:
|
|
|
|
|
for i in media:
|
|
|
|
|
if i.file_extension == "gif":
|
|
|
|
|
file_path = os.path.join(self.cache_dir, i.file_name)
|
|
|
|
|
file_name, _ = os.path.splitext(i.file_name)
|
|
|
|
|
output_file = file_name + ".mp4"
|
|
|
|
|
output_path = os.path.join(self.cache_dir, output_file)
|
|
|
|
|
if os.path.exists(output_path):
|
|
|
|
|
async with aiofiles.open(output_path, mode="rb") as f:
|
|
|
|
|
i.data = await f.read()
|
|
|
|
|
i.file_name = output_file
|
|
|
|
|
i.file_extension = "mp4"
|
|
|
|
|
continue
|
|
|
|
|
async with aiofiles.open(file_path, mode="wb") as f:
|
|
|
|
|
await f.write(i.data)
|
|
|
|
|
temp_file = sha1(file_name) + ".mp4"
|
|
|
|
|
temp_path = os.path.join(self.cache_dir, temp_file)
|
|
|
|
|
command = self.get_ffmpeg_command(file_path, temp_path)
|
2023-04-14 12:51:39 +00:00
|
|
|
|
result, return_code = await self.execute(command)
|
|
|
|
|
if return_code == 0:
|
|
|
|
|
if os.path.exists(temp_path):
|
|
|
|
|
logger.debug("ffmpeg 执行成功\n%s", result)
|
|
|
|
|
os.rename(temp_path, output_path)
|
|
|
|
|
async with aiofiles.open(output_path, mode="rb") as f:
|
|
|
|
|
i.data = await f.read()
|
|
|
|
|
i.file_name = output_file
|
|
|
|
|
i.file_extension = "mp4"
|
|
|
|
|
else:
|
|
|
|
|
logger.error(
|
|
|
|
|
"输出文件不存在!可能是 ffmpeg 命令执行失败!\n"
|
|
|
|
|
"file_path[%s]\noutput_path[%s]\ntemp_file[%s]\nffmpeg result[%s]",
|
|
|
|
|
file_path,
|
|
|
|
|
output_path,
|
|
|
|
|
temp_path,
|
|
|
|
|
result,
|
|
|
|
|
)
|
2023-04-14 03:53:15 +00:00
|
|
|
|
else:
|
2023-04-14 12:51:39 +00:00
|
|
|
|
logger.error("ffmpeg 执行失败\n%s", result)
|
2023-04-14 03:53:15 +00:00
|
|
|
|
return media
|
|
|
|
|
|
2022-10-13 07:38:47 +00:00
|
|
|
|
@conversation.entry_point
|
|
|
|
|
@handler.callback_query(pattern=r"^post_admin\|", block=False)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def callback_query_start(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-10-14 06:10:27 +00:00
|
|
|
|
post_handler_data = context.chat_data.get("post_handler_data")
|
|
|
|
|
if post_handler_data is None:
|
|
|
|
|
post_handler_data = PostHandlerData()
|
|
|
|
|
context.chat_data["post_handler_data"] = post_handler_data
|
2022-10-13 07:38:47 +00:00
|
|
|
|
callback_query = update.callback_query
|
|
|
|
|
user = callback_query.from_user
|
|
|
|
|
message = callback_query.message
|
2022-12-07 02:29:00 +00:00
|
|
|
|
logger.info("用户 %s[%s] POST命令请求", user.full_name, user.id)
|
2022-10-13 07:38:47 +00:00
|
|
|
|
|
|
|
|
|
async def get_post_admin_callback(callback_query_data: str) -> Tuple[str, int]:
|
|
|
|
|
_data = callback_query_data.split("|")
|
|
|
|
|
_result = _data[1]
|
|
|
|
|
_post_id = int(_data[2])
|
2022-12-07 02:29:00 +00:00
|
|
|
|
logger.debug("callback_query_data函数返回 result[%s] post_id[%s]", _result, _post_id)
|
2022-10-13 07:38:47 +00:00
|
|
|
|
return _result, _post_id
|
|
|
|
|
|
|
|
|
|
result, 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)
|
|
|
|
|
await reply_text.delete()
|
|
|
|
|
return status
|
|
|
|
|
|
|
|
|
|
await message.reply_text("非法参数")
|
|
|
|
|
return ConversationHandler.END
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2022-09-08 01:08:37 +00:00
|
|
|
|
@conversation.entry_point
|
2023-03-14 01:27:22 +00:00
|
|
|
|
@handler.command(command="post", filters=filters.ChatType.PRIVATE, block=False, admin=True)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def command_start(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-08-06 08:55:53 +00:00
|
|
|
|
user = update.effective_user
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
2022-12-07 02:29:00 +00:00
|
|
|
|
logger.info("用户 %s[%s] POST命令请求", user.full_name, user.id)
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_handler_data = context.chat_data.get("post_handler_data")
|
|
|
|
|
if post_handler_data is None:
|
|
|
|
|
post_handler_data = PostHandlerData()
|
|
|
|
|
context.chat_data["post_handler_data"] = post_handler_data
|
2022-08-06 09:21:58 +00:00
|
|
|
|
text = f"✿✿ヽ(°▽°)ノ✿ 你好! {user.username} ,\n" "只需复制URL回复即可 \n" "退出投稿只需回复退出"
|
2022-08-06 08:55:53 +00:00
|
|
|
|
reply_keyboard = [["退出"]]
|
|
|
|
|
await message.reply_text(text, reply_markup=ReplyKeyboardMarkup(reply_keyboard, True, True))
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return CHECK_POST
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2022-09-08 01:08:37 +00:00
|
|
|
|
@conversation.state(state=CHECK_POST)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def check_post(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_handler_data: PostHandlerData = context.chat_data.get("post_handler_data")
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
|
|
|
|
if message.text == "退出":
|
2022-08-06 08:55:53 +00:00
|
|
|
|
await message.reply_text("退出投稿", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
return ConversationHandler.END
|
|
|
|
|
|
2023-05-14 14:42:42 +00:00
|
|
|
|
post_id = Hyperion.extract_post_id(update.message.text)
|
2022-08-06 08:55:53 +00:00
|
|
|
|
if post_id == -1:
|
|
|
|
|
await message.reply_text("获取作品ID错误,请检查连接是否合法", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
return ConversationHandler.END
|
2022-10-13 07:38:47 +00:00
|
|
|
|
return await self.send_post_info(post_handler_data, message, post_id)
|
|
|
|
|
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def send_post_info(self, post_handler_data: PostHandlerData, message: "Message", post_id: int) -> int:
|
2023-05-14 14:42:42 +00:00
|
|
|
|
bbs = self.get_bbs_client()
|
|
|
|
|
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()
|
2023-04-14 03:53:15 +00:00
|
|
|
|
post_images = await self.gif_to_mp4(post_images)
|
2022-10-08 00:59:08 +00:00
|
|
|
|
post_data = post_info["post"]["post"]
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_subject = post_data["subject"]
|
|
|
|
|
post_soup = BeautifulSoup(post_data["content"], features="html.parser")
|
2023-09-30 02:49:04 +00:00
|
|
|
|
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})"
|
|
|
|
|
if too_long or len(post_text) >= MessageLimit.CAPTION_LENGTH:
|
2022-12-07 03:26:18 +00:00
|
|
|
|
post_text = post_text[: MessageLimit.CAPTION_LENGTH]
|
|
|
|
|
await message.reply_text(f"警告!图片字符描述已经超过 {MessageLimit.CAPTION_LENGTH} 个字,已经切割")
|
2022-08-06 08:55:53 +00:00
|
|
|
|
try:
|
|
|
|
|
if len(post_images) > 1:
|
2023-04-14 03:53:15 +00:00
|
|
|
|
media = [self.input_media(img_info) for img_info in post_images if not img_info.is_error]
|
2023-10-25 05:38:54 +00:00
|
|
|
|
index = (math.ceil(len(media) / 10) - 1) * 10
|
2023-09-30 02:49:04 +00:00
|
|
|
|
media[index].caption = post_text
|
|
|
|
|
media[index].parse_mode = ParseMode.MARKDOWN_V2
|
|
|
|
|
for group in ArkoWrapper(media).group(10): # 每 10 张图片分一个组
|
|
|
|
|
await message.reply_media_group(list(group), write_timeout=len(group) * 5)
|
2022-08-06 08:55:53 +00:00
|
|
|
|
elif len(post_images) == 1:
|
|
|
|
|
image = post_images[0]
|
|
|
|
|
await message.reply_photo(image.data, caption=post_text, parse_mode=ParseMode.MARKDOWN_V2)
|
|
|
|
|
else:
|
2023-05-23 11:27:02 +00:00
|
|
|
|
await message.reply_text(post_text, parse_mode=ParseMode.MARKDOWN_V2)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
except BadRequest as exc:
|
|
|
|
|
await message.reply_text(f"发送图片时发生错误 {exc.message}", reply_markup=ReplyKeyboardRemove())
|
2023-03-22 13:16:03 +00:00
|
|
|
|
logger.error("Post模块发送图片时发生错误 %s", exc.message)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
return ConversationHandler.END
|
|
|
|
|
except TypeError as exc:
|
2022-08-06 08:55:53 +00:00
|
|
|
|
await message.reply_text("发送图片时发生错误,错误信息已经写到日记", reply_markup=ReplyKeyboardRemove())
|
2023-03-22 05:06:05 +00:00
|
|
|
|
logger.error("Post模块发送图片时发生错误", exc_info=exc)
|
|
|
|
|
|
2022-08-06 08:55:53 +00:00
|
|
|
|
return ConversationHandler.END
|
|
|
|
|
post_handler_data.post_text = post_text
|
|
|
|
|
post_handler_data.post_images = post_images
|
|
|
|
|
post_handler_data.delete_photo = []
|
|
|
|
|
post_handler_data.tags = []
|
|
|
|
|
post_handler_data.channel_id = -1
|
|
|
|
|
await message.reply_text("请选择你的操作", reply_markup=self.MENU_KEYBOARD)
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return CHECK_COMMAND
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2022-09-08 01:08:37 +00:00
|
|
|
|
@conversation.state(state=CHECK_COMMAND)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def check_command(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
2022-08-06 08:55:53 +00:00
|
|
|
|
if message.text == "退出":
|
|
|
|
|
await message.reply_text("退出任务", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
return ConversationHandler.END
|
2023-03-14 01:27:22 +00:00
|
|
|
|
if message.text == "推送频道":
|
2022-08-06 08:55:53 +00:00
|
|
|
|
return await self.get_channel(update, context)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
if message.text == "添加TAG":
|
2022-08-06 08:55:53 +00:00
|
|
|
|
return await self.add_tags(update, context)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
if message.text == "编辑文字":
|
2022-08-06 08:55:53 +00:00
|
|
|
|
return await self.edit_text(update, context)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
if message.text == "删除图片":
|
2022-08-06 08:55:53 +00:00
|
|
|
|
return await self.delete_photo(update, context)
|
|
|
|
|
return ConversationHandler.END
|
|
|
|
|
|
2022-10-11 06:45:07 +00:00
|
|
|
|
@staticmethod
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def delete_photo(update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_handler_data: PostHandlerData = context.chat_data.get("post_handler_data")
|
|
|
|
|
photo_len = len(post_handler_data.post_images)
|
2022-09-18 08:35:37 +00:00
|
|
|
|
message = update.effective_message
|
2024-03-16 01:44:15 +00:00
|
|
|
|
await message.reply_text(
|
|
|
|
|
"请回复你要删除的图片的序列,从1开始,如果删除多张图片回复的序列请以空格作为分隔符,"
|
|
|
|
|
f"当前一共有 {photo_len} 张图片"
|
|
|
|
|
)
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return GTE_DELETE_PHOTO
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2022-09-08 01:08:37 +00:00
|
|
|
|
@conversation.state(state=GTE_DELETE_PHOTO)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def get_delete_photo(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_handler_data: PostHandlerData = context.chat_data.get("post_handler_data")
|
|
|
|
|
photo_len = len(post_handler_data.post_images)
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
2022-08-06 08:55:53 +00:00
|
|
|
|
args = message.text.split(" ")
|
|
|
|
|
index: List[int] = []
|
|
|
|
|
try:
|
|
|
|
|
for temp in args:
|
|
|
|
|
if int(temp) > photo_len:
|
|
|
|
|
raise ValueError
|
|
|
|
|
index.append(int(temp))
|
|
|
|
|
except ValueError:
|
|
|
|
|
await message.reply_text("数据不合法,请重新操作")
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return GTE_DELETE_PHOTO
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_handler_data.delete_photo = index
|
|
|
|
|
await message.reply_text("删除成功")
|
|
|
|
|
await message.reply_text("请选择你的操作", reply_markup=self.MENU_KEYBOARD)
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return CHECK_COMMAND
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def get_channel(self, update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
2022-08-06 08:55:53 +00:00
|
|
|
|
reply_keyboard = []
|
|
|
|
|
try:
|
2023-03-14 01:27:22 +00:00
|
|
|
|
for channel_id in config.channels:
|
|
|
|
|
chat = await self.get_chat(chat_id=channel_id)
|
|
|
|
|
reply_keyboard.append([f"{chat.username}"])
|
2022-08-06 08:55:53 +00:00
|
|
|
|
except KeyError as error:
|
2022-12-10 12:37:43 +00:00
|
|
|
|
logger.error("从配置文件获取频道信息发生错误,退出任务", exc_info=error)
|
2022-08-06 08:55:53 +00:00
|
|
|
|
await message.reply_text("从配置文件获取频道信息发生错误,退出任务", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
return ConversationHandler.END
|
|
|
|
|
await message.reply_text("请选择你要推送的频道", reply_markup=ReplyKeyboardMarkup(reply_keyboard, True, True))
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return GET_POST_CHANNEL
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2022-09-08 01:08:37 +00:00
|
|
|
|
@conversation.state(state=GET_POST_CHANNEL)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def get_post_channel(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_handler_data: PostHandlerData = context.chat_data.get("post_handler_data")
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
2022-08-06 08:55:53 +00:00
|
|
|
|
channel_id = -1
|
|
|
|
|
try:
|
2023-03-14 01:27:22 +00:00
|
|
|
|
for channel_chat_id in config.channels:
|
2023-03-14 07:13:22 +00:00
|
|
|
|
chat = await self.get_chat(chat_id=channel_chat_id)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
if message.text == chat.username:
|
|
|
|
|
channel_id = channel_chat_id
|
2022-09-08 01:08:37 +00:00
|
|
|
|
except KeyError as exc:
|
2022-12-10 12:37:43 +00:00
|
|
|
|
logger.error("从配置文件获取频道信息发生错误,退出任务", exc_info=exc)
|
2022-09-08 01:08:37 +00:00
|
|
|
|
logger.exception(exc)
|
2022-08-06 08:55:53 +00:00
|
|
|
|
await message.reply_text("从配置文件获取频道信息发生错误,退出任务", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
return ConversationHandler.END
|
|
|
|
|
if channel_id == -1:
|
|
|
|
|
await message.reply_text("获取频道信息失败,请检查你输入的内容是否正确", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
return ConversationHandler.END
|
|
|
|
|
post_handler_data.channel_id = channel_id
|
|
|
|
|
reply_keyboard = [["确认", "退出"]]
|
|
|
|
|
await message.reply_text("请核对你修改的信息", reply_markup=ReplyKeyboardMarkup(reply_keyboard, True, True))
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return SEND_POST
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2022-10-11 06:45:07 +00:00
|
|
|
|
@staticmethod
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def add_tags(update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
2024-03-16 01:44:15 +00:00
|
|
|
|
await message.reply_text(
|
|
|
|
|
"请回复添加的tag名称,如果要添加多个tag请以空格作为分隔符,不用添加 # 作为开头,推送时程序会自动添加"
|
|
|
|
|
)
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return GET_TAGS
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2022-09-08 01:08:37 +00:00
|
|
|
|
@conversation.state(state=GET_TAGS)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def get_tags(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_handler_data: PostHandlerData = context.chat_data.get("post_handler_data")
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
2022-08-06 08:55:53 +00:00
|
|
|
|
args = message.text.split(" ")
|
|
|
|
|
post_handler_data.tags = args
|
|
|
|
|
await message.reply_text("添加成功")
|
|
|
|
|
await message.reply_text("请选择你的操作", reply_markup=self.MENU_KEYBOARD)
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return CHECK_COMMAND
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2022-10-11 06:45:07 +00:00
|
|
|
|
@staticmethod
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def edit_text(update: "Update", _: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
2022-08-06 08:55:53 +00:00
|
|
|
|
await message.reply_text("请回复替换的文本")
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return GET_TEXT
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2022-09-08 01:08:37 +00:00
|
|
|
|
@conversation.state(state=GET_TEXT)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def get_edit_text(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_handler_data: PostHandlerData = context.chat_data.get("post_handler_data")
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_handler_data.post_text = message.text_markdown_v2
|
|
|
|
|
await message.reply_text("替换成功")
|
|
|
|
|
await message.reply_text("请选择你的操作", reply_markup=self.MENU_KEYBOARD)
|
2022-09-09 05:47:41 +00:00
|
|
|
|
return CHECK_COMMAND
|
2022-08-06 08:55:53 +00:00
|
|
|
|
|
2022-09-09 09:59:08 +00:00
|
|
|
|
@conversation.state(state=SEND_POST)
|
2023-03-14 01:27:22 +00:00
|
|
|
|
@handler.message(filters=filters.TEXT & ~filters.COMMAND, block=False)
|
2023-03-22 05:06:05 +00:00
|
|
|
|
async def send_post(self, update: "Update", context: "ContextTypes.DEFAULT_TYPE") -> int:
|
2022-08-06 08:55:53 +00:00
|
|
|
|
post_handler_data: PostHandlerData = context.chat_data.get("post_handler_data")
|
2022-09-08 01:08:37 +00:00
|
|
|
|
message = update.effective_message
|
|
|
|
|
if message.text == "退出":
|
2022-08-06 08:55:53 +00:00
|
|
|
|
await message.reply_text(text="退出任务", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
return ConversationHandler.END
|
|
|
|
|
await message.reply_text("正在推送", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
channel_id = post_handler_data.channel_id
|
|
|
|
|
channel_name = None
|
|
|
|
|
try:
|
2023-03-14 01:27:22 +00:00
|
|
|
|
for channel_info in config.channels:
|
2023-03-14 07:13:22 +00:00
|
|
|
|
if post_handler_data.channel_id == channel_info:
|
2023-03-14 01:27:22 +00:00
|
|
|
|
chat = await self.get_chat(chat_id=channel_id)
|
|
|
|
|
channel_name = chat.username
|
2022-09-08 01:08:37 +00:00
|
|
|
|
except KeyError as exc:
|
|
|
|
|
logger.error("从配置文件获取频道信息发生错误,退出任务")
|
|
|
|
|
logger.exception(exc)
|
2022-08-06 08:55:53 +00:00
|
|
|
|
await message.reply_text("从配置文件获取频道信息发生错误,退出任务", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
return ConversationHandler.END
|
|
|
|
|
post_text = post_handler_data.post_text
|
|
|
|
|
post_images = []
|
|
|
|
|
for index, _ in enumerate(post_handler_data.post_images):
|
|
|
|
|
if index + 1 not in post_handler_data.delete_photo:
|
|
|
|
|
post_images.append(post_handler_data.post_images[index])
|
2023-03-22 05:06:05 +00:00
|
|
|
|
post_text += f" @{escape_markdown(channel_name, version=2)}"
|
2022-08-06 08:55:53 +00:00
|
|
|
|
for tag in post_handler_data.tags:
|
|
|
|
|
post_text += f" \\#{tag}"
|
|
|
|
|
try:
|
|
|
|
|
if len(post_images) > 1:
|
2023-04-14 03:53:15 +00:00
|
|
|
|
media = [self.input_media(img_info) for img_info in post_images if not img_info.is_error]
|
2023-10-25 05:38:54 +00:00
|
|
|
|
index = (math.ceil(len(media) / 10) - 1) * 10
|
2023-09-30 02:49:04 +00:00
|
|
|
|
media[index].caption = post_text
|
|
|
|
|
media[index].parse_mode = ParseMode.MARKDOWN_V2
|
|
|
|
|
for group in ArkoWrapper(media).group(10): # 每 10 张图片分一个组
|
|
|
|
|
await context.bot.send_media_group(channel_id, media=list(group), write_timeout=len(group) * 5)
|
2022-08-06 08:55:53 +00:00
|
|
|
|
elif len(post_images) == 1:
|
|
|
|
|
image = post_images[0]
|
|
|
|
|
await context.bot.send_photo(
|
|
|
|
|
channel_id, photo=image.data, caption=post_text, parse_mode=ParseMode.MARKDOWN_V2
|
|
|
|
|
)
|
2022-10-11 12:20:11 +00:00
|
|
|
|
elif not post_images:
|
2022-08-06 08:55:53 +00:00
|
|
|
|
await context.bot.send_message(channel_id, post_text, parse_mode=ParseMode.MARKDOWN_V2)
|
|
|
|
|
else:
|
|
|
|
|
await message.reply_text("图片获取错误", reply_markup=ReplyKeyboardRemove()) # excuse?
|
|
|
|
|
return ConversationHandler.END
|
2023-03-22 05:06:05 +00:00
|
|
|
|
except BadRequest as exc:
|
|
|
|
|
await message.reply_text(f"发送图片时发生错误 {exc.message}", reply_markup=ReplyKeyboardRemove())
|
2023-03-22 13:16:03 +00:00
|
|
|
|
logger.error("Post模块发送图片时发生错误 %s", exc.message)
|
2022-08-06 08:55:53 +00:00
|
|
|
|
return ConversationHandler.END
|
2023-03-22 05:06:05 +00:00
|
|
|
|
except TypeError as exc:
|
|
|
|
|
await message.reply_text("发送图片时发生错误,错误信息已经写到日记", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
logger.error("Post模块发送图片时发生错误", exc_info=exc)
|
2022-08-06 08:55:53 +00:00
|
|
|
|
await message.reply_text("推送成功", reply_markup=ReplyKeyboardRemove())
|
|
|
|
|
return ConversationHandler.END
|