mirror of
https://github.com/PaiGramTeam/PamGram.git
synced 2024-11-16 12:02:16 +00:00
✨ 为 post
插件添加定时推送功能
This commit is contained in:
parent
3fe62c0100
commit
b122d840f5
24
.env.example
24
.env.example
@ -16,19 +16,19 @@ REDIS_DB=0
|
|||||||
# 联系 https://t.me/BotFather 使用 /newbot 命令创建机器人并获取 token
|
# 联系 https://t.me/BotFather 使用 /newbot 命令创建机器人并获取 token
|
||||||
BOT_TOKEN="xxxxxxx"
|
BOT_TOKEN="xxxxxxx"
|
||||||
|
|
||||||
# 记录错误并发送消息通知开发人员 可选配置项
|
|
||||||
ERROR_NOTIFICATION_CHAT_ID=chat_id
|
|
||||||
|
|
||||||
# 文章推送群组 可选配置项
|
|
||||||
CHANNELS=[{ "name": "", "chat_id": 1}]
|
|
||||||
|
|
||||||
# bot 管理员
|
# bot 管理员
|
||||||
ADMINS=[{ "username": "", "user_id": -1 }]
|
ADMINS=[{ "username": "", "user_id": -1 }]
|
||||||
|
|
||||||
# 群验证功能 可选配置项
|
# 记录错误并发送消息通知开发人员 可选配置项
|
||||||
VERIFY_GROUPS=[]
|
# ERROR_NOTIFICATION_CHAT_ID=chat_id
|
||||||
|
|
||||||
# logger 配置
|
# 文章推送群组 可选配置项
|
||||||
|
# CHANNELS=[{ "name": "", "chat_id": 1}]
|
||||||
|
|
||||||
|
# 群验证功能 可选配置项
|
||||||
|
# VERIFY_GROUPS=[]
|
||||||
|
|
||||||
|
# logger 配置 可选配置项
|
||||||
LOGGER_WIDTH=180
|
LOGGER_WIDTH=180
|
||||||
LOGGER_LOG_PATH="logs"
|
LOGGER_LOG_PATH="logs"
|
||||||
LOGGER_TIME_FORMAT="[%Y-%m-%d %X]"
|
LOGGER_TIME_FORMAT="[%Y-%m-%d %X]"
|
||||||
@ -36,11 +36,11 @@ LOGGER_TRACEBACK_MAX_FRAMES=20
|
|||||||
LOGGER_RENDER_KEYWORDS=["BOT"]
|
LOGGER_RENDER_KEYWORDS=["BOT"]
|
||||||
|
|
||||||
# mtp 客户端 可选配置项
|
# mtp 客户端 可选配置项
|
||||||
API_ID=12345
|
# API_ID=12345
|
||||||
API_HASH="abcdefg"
|
# API_HASH="abcdefg"
|
||||||
|
|
||||||
# ENKA_NETWORK_API 可选配置项
|
# ENKA_NETWORK_API 可选配置项
|
||||||
ENKA_NETWORK_API_AGENT=""
|
# ENKA_NETWORK_API_AGENT=""
|
||||||
|
|
||||||
# Web Server
|
# Web Server
|
||||||
# 目前只用于预览模板,仅开发环境启动
|
# 目前只用于预览模板,仅开发环境启动
|
||||||
|
@ -5,8 +5,8 @@ from json import JSONDecodeError
|
|||||||
from typing import List, Optional, Dict
|
from typing import List, Optional, Dict
|
||||||
|
|
||||||
from genshin import Client, InvalidCookies
|
from genshin import Client, InvalidCookies
|
||||||
from genshin.utility.uid import recognize_genshin_server
|
|
||||||
from genshin.utility.ds import generate_dynamic_secret
|
from genshin.utility.ds import generate_dynamic_secret
|
||||||
|
from genshin.utility.uid import recognize_genshin_server
|
||||||
from httpx import AsyncClient
|
from httpx import AsyncClient
|
||||||
|
|
||||||
from modules.apihelper.base import ArtworkImage, PostInfo
|
from modules.apihelper.base import ArtworkImage, PostInfo
|
||||||
@ -25,6 +25,8 @@ class Hyperion:
|
|||||||
POST_FULL_URL = "https://bbs-api.mihoyo.com/post/wapi/getPostFull"
|
POST_FULL_URL = "https://bbs-api.mihoyo.com/post/wapi/getPostFull"
|
||||||
POST_FULL_IN_COLLECTION_URL = "https://bbs-api.mihoyo.com/post/wapi/getPostFullInCollection"
|
POST_FULL_IN_COLLECTION_URL = "https://bbs-api.mihoyo.com/post/wapi/getPostFullInCollection"
|
||||||
GET_NEW_LIST_URL = "https://bbs-api.mihoyo.com/post/wapi/getNewsList"
|
GET_NEW_LIST_URL = "https://bbs-api.mihoyo.com/post/wapi/getNewsList"
|
||||||
|
GET_OFFICIAL_RECOMMENDED_POSTS_URL = "https://bbs-api.mihoyo.com/post/wapi/getOfficialRecommendedPosts"
|
||||||
|
|
||||||
USER_AGENT = (
|
USER_AGENT = (
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||||
"Chrome/90.0.4430.72 Safari/537.36"
|
"Chrome/90.0.4430.72 Safari/537.36"
|
||||||
@ -89,6 +91,11 @@ class Hyperion:
|
|||||||
)
|
)
|
||||||
return {"x-oss-process": params}
|
return {"x-oss-process": params}
|
||||||
|
|
||||||
|
async def get_official_recommended_posts(self, gids: int) -> JSONDict:
|
||||||
|
params = {"gids": gids}
|
||||||
|
response = await self.client.get(url=self.GET_OFFICIAL_RECOMMENDED_POSTS_URL, params=params)
|
||||||
|
return response
|
||||||
|
|
||||||
async def get_post_full_in_collection(self, collection_id: int, gids: int = 2, order_type=1) -> JSONDict:
|
async def get_post_full_in_collection(self, collection_id: int, gids: int = 2, order_type=1) -> JSONDict:
|
||||||
params = {"collection_id": collection_id, "gids": gids, "order_type": order_type}
|
params = {"collection_id": collection_id, "gids": gids, "order_type": order_type}
|
||||||
response = await self.client.get(url=self.POST_FULL_IN_COLLECTION_URL, params=params)
|
response = await self.client.get(url=self.POST_FULL_IN_COLLECTION_URL, params=params)
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
from typing import Optional, List
|
from typing import Optional, List, Tuple
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove, InputMediaPhoto
|
from telegram import (
|
||||||
|
Update,
|
||||||
|
ReplyKeyboardMarkup,
|
||||||
|
ReplyKeyboardRemove,
|
||||||
|
InputMediaPhoto,
|
||||||
|
InlineKeyboardButton,
|
||||||
|
InlineKeyboardMarkup,
|
||||||
|
Message,
|
||||||
|
)
|
||||||
from telegram.constants import ParseMode, MessageLimit
|
from telegram.constants import ParseMode, MessageLimit
|
||||||
from telegram.error import BadRequest
|
from telegram.error import BadRequest
|
||||||
from telegram.ext import CallbackContext, ConversationHandler, filters
|
from telegram.ext import CallbackContext, ConversationHandler, filters
|
||||||
@ -9,8 +17,10 @@ from telegram.helpers import escape_markdown
|
|||||||
|
|
||||||
from core.baseplugin import BasePlugin
|
from core.baseplugin import BasePlugin
|
||||||
from core.bot import bot
|
from core.bot import bot
|
||||||
|
from core.config import config
|
||||||
from core.plugin import Plugin, conversation, handler
|
from core.plugin import Plugin, conversation, handler
|
||||||
from modules.apihelper.base import ArtworkImage
|
from modules.apihelper.base import ArtworkImage
|
||||||
|
from modules.apihelper.error import APIHelperException
|
||||||
from modules.apihelper.hyperion import Hyperion
|
from modules.apihelper.hyperion import Hyperion
|
||||||
from utils.decorators.admins import bot_admins_rights_check
|
from utils.decorators.admins import bot_admins_rights_check
|
||||||
from utils.decorators.error import error_callable
|
from utils.decorators.error import error_callable
|
||||||
@ -38,6 +48,95 @@ class Post(Plugin.Conversation, BasePlugin.Conversation):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.bbs = Hyperion()
|
self.bbs = Hyperion()
|
||||||
|
self.last_post_id_list: List[int] = []
|
||||||
|
if config.channels is not None and len(config.channels) > 0:
|
||||||
|
logger.success("文章定时推送处理已经开启")
|
||||||
|
bot.app.job_queue.run_repeating(self.task, 60 * 3)
|
||||||
|
|
||||||
|
async def task(self, context: CallbackContext):
|
||||||
|
temp_post_id_list: List[int] = []
|
||||||
|
|
||||||
|
# 请求推荐POST列表并处理
|
||||||
|
try:
|
||||||
|
official_recommended_posts = await self.bbs.get_official_recommended_posts(2)
|
||||||
|
except APIHelperException as exc:
|
||||||
|
logger.error(f"获取首页推荐信息失败 {repr(exc)}")
|
||||||
|
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))
|
||||||
|
|
||||||
|
if len(new_post_id_list) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.last_post_id_list = temp_post_id_list
|
||||||
|
|
||||||
|
for post_id in temp_post_id_list:
|
||||||
|
try:
|
||||||
|
post_info = await self.bbs.get_post_info(2, post_id)
|
||||||
|
except APIHelperException as exc:
|
||||||
|
logger.error(f"获取文章信息失败 {repr(exc)}")
|
||||||
|
text = f"获取 post_id[{post_id}] 文章信息失败 {repr(exc)}"
|
||||||
|
for user in config.admins:
|
||||||
|
try:
|
||||||
|
await context.bot.send_message(user.user_id, text)
|
||||||
|
except BadRequest as _exc:
|
||||||
|
logger.error(f"发送消息失败 {repr(_exc)}")
|
||||||
|
return
|
||||||
|
buttons = [
|
||||||
|
[
|
||||||
|
InlineKeyboardButton("确认", callback_data=f"post_admin|confirm|{post_info.post_id}"),
|
||||||
|
InlineKeyboardButton("取消", callback_data=f"post_admin|cancel|{post_info.post_id}"),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
url = f"https://bbs.mihoyo.com/ys/article/{post_info.post_id}"
|
||||||
|
text = f"发现官网推荐文章 <a href='{url}'>{post_info.subject}</a>\n是否开始处理"
|
||||||
|
for user in config.admins:
|
||||||
|
try:
|
||||||
|
await context.bot.send_message(user.user_id, text, reply_markup=InlineKeyboardMarkup(buttons))
|
||||||
|
except BadRequest as exc:
|
||||||
|
logger.error(f"发送消息失败 {repr(exc)}")
|
||||||
|
|
||||||
|
@conversation.entry_point
|
||||||
|
@handler.callback_query(pattern=r"^post_admin\|", block=False)
|
||||||
|
@bot_admins_rights_check
|
||||||
|
@error_callable
|
||||||
|
async def callback_query_start(self, update: Update, context: CallbackContext) -> int:
|
||||||
|
post_handler_data: PostHandlerData = context.chat_data.get("post_handler_data")
|
||||||
|
callback_query = update.callback_query
|
||||||
|
user = callback_query.from_user
|
||||||
|
message = callback_query.message
|
||||||
|
logger.info(f"用户 {user.full_name}[{user.id}] POST命令请求")
|
||||||
|
|
||||||
|
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])
|
||||||
|
logger.debug(f"callback_query_data函数返回 result[{_result}] post_id[{_post_id}]")
|
||||||
|
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
|
||||||
|
|
||||||
@conversation.entry_point
|
@conversation.entry_point
|
||||||
@handler.command(command="post", filters=filters.ChatType.PRIVATE, block=True)
|
@handler.command(command="post", filters=filters.ChatType.PRIVATE, block=True)
|
||||||
@ -71,6 +170,9 @@ class Post(Plugin.Conversation, BasePlugin.Conversation):
|
|||||||
if post_id == -1:
|
if post_id == -1:
|
||||||
await message.reply_text("获取作品ID错误,请检查连接是否合法", reply_markup=ReplyKeyboardRemove())
|
await message.reply_text("获取作品ID错误,请检查连接是否合法", reply_markup=ReplyKeyboardRemove())
|
||||||
return ConversationHandler.END
|
return ConversationHandler.END
|
||||||
|
return await self.send_post_info(post_handler_data, message, post_id)
|
||||||
|
|
||||||
|
async def send_post_info(self, post_handler_data: PostHandlerData, message: Message, post_id: int) -> int:
|
||||||
post_info = await self.bbs.get_post_info(2, post_id)
|
post_info = await self.bbs.get_post_info(2, post_id)
|
||||||
post_images = await self.bbs.get_images_by_post_id(2, post_id)
|
post_images = await self.bbs.get_images_by_post_id(2, post_id)
|
||||||
post_data = post_info["post"]["post"]
|
post_data = post_info["post"]["post"]
|
||||||
|
@ -43,3 +43,16 @@ async def test_get_post_info(hyperion):
|
|||||||
async def test_get_images_by_post_id(hyperion):
|
async def test_get_images_by_post_id(hyperion):
|
||||||
post_images = await hyperion.get_images_by_post_id(2, 29023709)
|
post_images = await hyperion.get_images_by_post_id(2, 29023709)
|
||||||
assert len(post_images) == 1
|
assert len(post_images) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyShadowingNames
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@flaky(3, 1)
|
||||||
|
async def test_official_recommended_posts(hyperion):
|
||||||
|
official_recommended_posts = await hyperion.get_official_recommended_posts(2)
|
||||||
|
assert len(official_recommended_posts["list"]) > 0
|
||||||
|
for data_list in official_recommended_posts["list"]:
|
||||||
|
post_info = await hyperion.get_post_info(2, data_list["post_id"])
|
||||||
|
assert post_info.post_id
|
||||||
|
assert post_info.subject
|
||||||
|
LOGGER.info("official_recommended_posts: post_id[%s] subject[%s]", post_info.post_id, post_info.subject)
|
||||||
|
Loading…
Reference in New Issue
Block a user