mirror of
https://github.com/PaiGramTeam/PamGram.git
synced 2024-11-30 09:22:48 +00:00
176 lines
6.1 KiB
Python
176 lines
6.1 KiB
Python
|
from pathlib import Path
|
||
|
from typing import List, Optional, Union, TYPE_CHECKING
|
||
|
|
||
|
import aiofiles
|
||
|
import httpx
|
||
|
from httpx import UnsupportedProtocol
|
||
|
from telegram import Chat, Message, ReplyKeyboardRemove, Update
|
||
|
from telegram.error import BadRequest, Forbidden
|
||
|
from telegram.ext import CallbackContext, ConversationHandler, Job
|
||
|
|
||
|
from core.dependence.redisdb import RedisDB
|
||
|
from core.plugin._handler import conversation, handler
|
||
|
from utils.const import CACHE_DIR, REQUEST_HEADERS
|
||
|
from utils.error import UrlResourcesNotFoundError
|
||
|
from utils.helpers import sha1
|
||
|
from utils.log import logger
|
||
|
|
||
|
if TYPE_CHECKING:
|
||
|
from core.application import Application
|
||
|
|
||
|
try:
|
||
|
import ujson as json
|
||
|
except ImportError:
|
||
|
import json
|
||
|
|
||
|
__all__ = (
|
||
|
"PluginFuncs",
|
||
|
"ConversationFuncs",
|
||
|
)
|
||
|
|
||
|
|
||
|
class PluginFuncs:
|
||
|
_application: "Optional[Application]" = None
|
||
|
|
||
|
def set_application(self, application: "Application") -> None:
|
||
|
self._application = application
|
||
|
|
||
|
@property
|
||
|
def application(self) -> "Application":
|
||
|
if self._application is None:
|
||
|
raise RuntimeError("No application was set for this PluginManager.")
|
||
|
return self._application
|
||
|
|
||
|
async def _delete_message(self, context: CallbackContext) -> None:
|
||
|
job = context.job
|
||
|
message_id = job.data
|
||
|
chat_info = f"chat_id[{job.chat_id}]"
|
||
|
|
||
|
try:
|
||
|
chat = await self.get_chat(job.chat_id)
|
||
|
full_name = chat.full_name
|
||
|
if full_name:
|
||
|
chat_info = f"{full_name}[{chat.id}]"
|
||
|
else:
|
||
|
chat_info = f"{chat.title}[{chat.id}]"
|
||
|
except (BadRequest, Forbidden) as exc:
|
||
|
logger.warning("获取 chat info 失败 %s", exc.message)
|
||
|
except Exception as exc:
|
||
|
logger.warning("获取 chat info 消息失败 %s", str(exc))
|
||
|
|
||
|
logger.debug("删除消息 %s message_id[%s]", chat_info, message_id)
|
||
|
|
||
|
try:
|
||
|
# noinspection PyTypeChecker
|
||
|
await context.bot.delete_message(chat_id=job.chat_id, message_id=message_id)
|
||
|
except BadRequest as exc:
|
||
|
logger.warning("删除消息 %s message_id[%s] 失败 %s", chat_info, message_id, exc.message)
|
||
|
|
||
|
async def get_chat(self, chat_id: Union[str, int], redis_db: Optional[RedisDB] = None, ttl: int = 86400) -> Chat:
|
||
|
application = self.application
|
||
|
redis_db: RedisDB = redis_db or self.application.managers.services_map.get(RedisDB, None)
|
||
|
|
||
|
if not redis_db:
|
||
|
return await application.bot.get_chat(chat_id)
|
||
|
|
||
|
qname = f"bot:chat:{chat_id}"
|
||
|
|
||
|
data = await redis_db.client.get(qname)
|
||
|
if data:
|
||
|
json_data = json.loads(data)
|
||
|
return Chat.de_json(json_data, application.telegram.bot)
|
||
|
|
||
|
chat_info = await application.telegram.bot.get_chat(chat_id)
|
||
|
await redis_db.client.set(qname, chat_info.to_json())
|
||
|
await redis_db.client.expire(qname, ttl)
|
||
|
return chat_info
|
||
|
|
||
|
def add_delete_message_job(
|
||
|
self,
|
||
|
message: Optional[Union[int, Message]] = None,
|
||
|
*,
|
||
|
delay: int = 60,
|
||
|
name: Optional[str] = None,
|
||
|
chat: Optional[Union[int, Chat]] = None,
|
||
|
context: Optional[CallbackContext] = None,
|
||
|
) -> Job:
|
||
|
"""延迟删除消息"""
|
||
|
|
||
|
if isinstance(message, Message):
|
||
|
if chat is None:
|
||
|
chat = message.chat_id
|
||
|
message = message.id
|
||
|
|
||
|
chat = chat.id if isinstance(chat, Chat) else chat
|
||
|
|
||
|
job_queue = self.application.job_queue or context.job_queue
|
||
|
|
||
|
if job_queue is None:
|
||
|
raise RuntimeError
|
||
|
|
||
|
return job_queue.run_once(
|
||
|
callback=self._delete_message,
|
||
|
when=delay,
|
||
|
data=message,
|
||
|
name=f"{chat}|{message}|{name}|delete_message" if name else f"{chat}|{message}|delete_message",
|
||
|
chat_id=chat,
|
||
|
job_kwargs={"replace_existing": True, "id": f"{chat}|{message}|delete_message"},
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
async def download_resource(url: str, return_path: bool = False) -> str:
|
||
|
url_sha1 = sha1(url) # url 的 hash 值
|
||
|
pathed_url = Path(url)
|
||
|
|
||
|
file_name = url_sha1 + pathed_url.suffix
|
||
|
file_path = CACHE_DIR.joinpath(file_name)
|
||
|
|
||
|
if not file_path.exists(): # 若文件不存在,则下载
|
||
|
async with httpx.AsyncClient(headers=REQUEST_HEADERS) as client:
|
||
|
try:
|
||
|
response = await client.get(url)
|
||
|
except UnsupportedProtocol:
|
||
|
logger.error("链接不支持 url[%s]", url)
|
||
|
return ""
|
||
|
|
||
|
if response.is_error:
|
||
|
logger.error("请求出现错误 url[%s] status_code[%s]", url, response.status_code)
|
||
|
raise UrlResourcesNotFoundError(url)
|
||
|
|
||
|
if response.status_code != 200:
|
||
|
logger.error("download_resource 获取url[%s] 错误 status_code[%s]", url, response.status_code)
|
||
|
raise UrlResourcesNotFoundError(url)
|
||
|
|
||
|
async with aiofiles.open(file_path, mode="wb") as f:
|
||
|
await f.write(response.content)
|
||
|
|
||
|
logger.debug("download_resource 获取url[%s] 并下载到 file_dir[%s]", url, file_path)
|
||
|
|
||
|
return file_path if return_path else Path(file_path).as_uri()
|
||
|
|
||
|
@staticmethod
|
||
|
def get_args(context: Optional[CallbackContext] = None) -> List[str]:
|
||
|
args = context.args
|
||
|
match = context.match
|
||
|
|
||
|
if args is None:
|
||
|
if match is not None and (command := match.groups()[0]):
|
||
|
temp = []
|
||
|
command_parts = command.split(" ")
|
||
|
for command_part in command_parts:
|
||
|
if command_part:
|
||
|
temp.append(command_part)
|
||
|
return temp
|
||
|
return []
|
||
|
if len(args) >= 1:
|
||
|
return args
|
||
|
return []
|
||
|
|
||
|
|
||
|
class ConversationFuncs:
|
||
|
@conversation.fallback
|
||
|
@handler.command(command="cancel", block=True)
|
||
|
async def cancel(self, update: Update, _) -> int:
|
||
|
await update.effective_message.reply_text("退出命令", reply_markup=ReplyKeyboardRemove())
|
||
|
return ConversationHandler.END
|