mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-28 19:47:41 +08:00
feat:删除消息能力
This commit is contained in:
+14
-1
@@ -22,7 +22,7 @@ from app.helper.service import ServiceConfigHelper
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent, CommingMessage, Notification, \
|
from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent, CommingMessage, Notification, \
|
||||||
WebhookEventInfo, TmdbEpisode, MediaPerson, FileItem, TransferDirectoryConf
|
WebhookEventInfo, TmdbEpisode, MediaPerson, FileItem, TransferDirectoryConf
|
||||||
from app.schemas.types import TorrentStatus, MediaType, MediaImageType, EventType
|
from app.schemas.types import TorrentStatus, MediaType, MediaImageType, EventType, MessageChannel
|
||||||
from app.utils.object import ObjectUtils
|
from app.utils.object import ObjectUtils
|
||||||
|
|
||||||
|
|
||||||
@@ -641,6 +641,19 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
return self.messagequeue.send_message("post_torrents_message", message=message, torrents=torrents,
|
return self.messagequeue.send_message("post_torrents_message", message=message, torrents=torrents,
|
||||||
immediately=True if message.userid else False)
|
immediately=True if message.userid else False)
|
||||||
|
|
||||||
|
def delete_message(self, channel: MessageChannel, source: str,
|
||||||
|
message_id: Union[str, int], chat_id: Optional[Union[str, int]] = None) -> bool:
|
||||||
|
"""
|
||||||
|
删除消息
|
||||||
|
:param channel: 消息渠道
|
||||||
|
:param source: 消息源(指定特定的消息模块)
|
||||||
|
:param message_id: 消息ID
|
||||||
|
:param chat_id: 聊天ID(如群组ID)
|
||||||
|
:return: 删除是否成功
|
||||||
|
"""
|
||||||
|
return self.run_module("delete_message", channel=channel, source=source,
|
||||||
|
message_id=message_id, chat_id=chat_id)
|
||||||
|
|
||||||
def metadata_img(self, mediainfo: MediaInfo,
|
def metadata_img(self, mediainfo: MediaInfo,
|
||||||
season: Optional[int] = None, episode: Optional[int] = None) -> Optional[dict]:
|
season: Optional[int] = None, episode: Optional[int] = None) -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
|
|||||||
+12
-2
@@ -1,4 +1,3 @@
|
|||||||
import gc
|
|
||||||
import re
|
import re
|
||||||
from typing import Any, Optional, Dict, Union, List
|
from typing import Any, Optional, Dict, Union, List
|
||||||
|
|
||||||
@@ -275,6 +274,14 @@ class MessageChain(ChainBase):
|
|||||||
_current_page = 0
|
_current_page = 0
|
||||||
# 保存缓存
|
# 保存缓存
|
||||||
self.save_cache(user_cache, self._cache_file)
|
self.save_cache(user_cache, self._cache_file)
|
||||||
|
# 删除原消息
|
||||||
|
if ChannelCapabilityManager.supports_deletion(channel):
|
||||||
|
self.delete_message(
|
||||||
|
channel=channel,
|
||||||
|
source=source,
|
||||||
|
message_id=original_message_id,
|
||||||
|
chat_id=original_chat_id
|
||||||
|
)
|
||||||
# 发送种子数据
|
# 发送种子数据
|
||||||
logger.info(f"搜索到 {len(contexts)} 条数据,开始发送选择消息 ...")
|
logger.info(f"搜索到 {len(contexts)} 条数据,开始发送选择消息 ...")
|
||||||
self.__post_torrents_message(channel=channel,
|
self.__post_torrents_message(channel=channel,
|
||||||
@@ -505,6 +512,9 @@ class MessageChain(ChainBase):
|
|||||||
"""
|
"""
|
||||||
处理按钮回调
|
处理按钮回调
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
global _current_media
|
||||||
|
|
||||||
# 提取回调数据
|
# 提取回调数据
|
||||||
callback_data = text[9:] # 去掉 "CALLBACK:" 前缀
|
callback_data = text[9:] # 去掉 "CALLBACK:" 前缀
|
||||||
logger.info(f"处理按钮回调:{callback_data}")
|
logger.info(f"处理按钮回调:{callback_data}")
|
||||||
@@ -537,7 +547,7 @@ class MessageChain(ChainBase):
|
|||||||
text=page_text,
|
text=page_text,
|
||||||
original_message_id=original_message_id, original_chat_id=original_chat_id)
|
original_message_id=original_message_id, original_chat_id=original_chat_id)
|
||||||
else:
|
else:
|
||||||
# 发送新消息
|
# 处理新消息
|
||||||
self.handle_message(channel=channel, source=source, userid=userid, username=username,
|
self.handle_message(channel=channel, source=source, userid=userid, username=username,
|
||||||
text=page_text)
|
text=page_text)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
|||||||
@@ -222,13 +222,13 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
|||||||
# 使用CALLBACK前缀标识按钮回调
|
# 使用CALLBACK前缀标识按钮回调
|
||||||
text = f"CALLBACK:{callback_data}"
|
text = f"CALLBACK:{callback_data}"
|
||||||
username = msg_json.get("user", {}).get("name")
|
username = msg_json.get("user", {}).get("name")
|
||||||
|
|
||||||
# 获取原消息信息用于编辑
|
# 获取原消息信息用于编辑
|
||||||
message_info = msg_json.get("message", {})
|
message_info = msg_json.get("message", {})
|
||||||
# Slack消息的时间戳作为消息ID
|
# Slack消息的时间戳作为消息ID
|
||||||
message_ts = message_info.get("ts")
|
message_ts = message_info.get("ts")
|
||||||
channel_id = msg_json.get("channel", {}).get("id") or msg_json.get("container", {}).get("channel_id")
|
channel_id = msg_json.get("channel", {}).get("id") or msg_json.get("container", {}).get("channel_id")
|
||||||
|
|
||||||
logger.info(f"收到来自 {client_config.name} 的Slack按钮回调:"
|
logger.info(f"收到来自 {client_config.name} 的Slack按钮回调:"
|
||||||
f"userid={userid}, username={username}, callback_data={callback_data}")
|
f"userid={userid}, username={username}, callback_data={callback_data}")
|
||||||
|
|
||||||
@@ -320,3 +320,26 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
|||||||
userid=message.userid, buttons=message.buttons,
|
userid=message.userid, buttons=message.buttons,
|
||||||
original_message_id=message.original_message_id,
|
original_message_id=message.original_message_id,
|
||||||
original_chat_id=message.original_chat_id)
|
original_chat_id=message.original_chat_id)
|
||||||
|
|
||||||
|
def delete_message(self, channel: MessageChannel, source: str,
|
||||||
|
message_id: str, chat_id: Optional[str] = None) -> bool:
|
||||||
|
"""
|
||||||
|
删除消息
|
||||||
|
:param channel: 消息渠道
|
||||||
|
:param source: 指定的消息源
|
||||||
|
:param message_id: 消息ID(Slack中为时间戳)
|
||||||
|
:param chat_id: 聊天ID(频道ID)
|
||||||
|
:return: 删除是否成功
|
||||||
|
"""
|
||||||
|
success = False
|
||||||
|
for conf in self.get_configs().values():
|
||||||
|
if channel != self._channel:
|
||||||
|
continue
|
||||||
|
if source != conf.name:
|
||||||
|
continue
|
||||||
|
client: Slack = self.get_instance(conf.name)
|
||||||
|
if client:
|
||||||
|
result = client.delete_msg(message_id=message_id, chat_id=chat_id)
|
||||||
|
if result:
|
||||||
|
success = True
|
||||||
|
return success
|
||||||
|
|||||||
+51
-16
@@ -13,18 +13,16 @@ from app.core.metainfo import MetaInfo
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
|
|
||||||
lock = Lock()
|
lock = Lock()
|
||||||
|
|
||||||
|
|
||||||
class Slack:
|
class Slack:
|
||||||
|
|
||||||
_client: WebClient = None
|
_client: WebClient = None
|
||||||
_service: SocketModeHandler = None
|
_service: SocketModeHandler = None
|
||||||
_ds_url = f"http://127.0.0.1:{settings.PORT}/api/v1/message?token={settings.API_TOKEN}"
|
_ds_url = f"http://127.0.0.1:{settings.PORT}/api/v1/message?token={settings.API_TOKEN}"
|
||||||
_channel = ""
|
_channel = ""
|
||||||
|
|
||||||
def __init__(self, SLACK_OAUTH_TOKEN: Optional[str] = None, SLACK_APP_TOKEN: Optional[str] = None,
|
def __init__(self, SLACK_OAUTH_TOKEN: Optional[str] = None, SLACK_APP_TOKEN: Optional[str] = None,
|
||||||
SLACK_CHANNEL: Optional[str] = None, **kwargs):
|
SLACK_CHANNEL: Optional[str] = None, **kwargs):
|
||||||
|
|
||||||
if not SLACK_OAUTH_TOKEN or not SLACK_APP_TOKEN:
|
if not SLACK_OAUTH_TOKEN or not SLACK_APP_TOKEN:
|
||||||
@@ -197,7 +195,7 @@ class Slack:
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
# 判断是编辑消息还是发送新消息
|
# 判断是编辑消息还是发送新消息
|
||||||
if original_message_id and original_chat_id:
|
if original_message_id and original_chat_id:
|
||||||
# 编辑消息
|
# 编辑消息
|
||||||
@@ -258,7 +256,7 @@ class Slack:
|
|||||||
"type": "divider"
|
"type": "divider"
|
||||||
})
|
})
|
||||||
index = 1
|
index = 1
|
||||||
|
|
||||||
# 如果有自定义按钮,先添加所有媒体项,然后添加统一的按钮
|
# 如果有自定义按钮,先添加所有媒体项,然后添加统一的按钮
|
||||||
if buttons:
|
if buttons:
|
||||||
# 添加媒体列表(不带单独的选择按钮)
|
# 添加媒体列表(不带单独的选择按钮)
|
||||||
@@ -288,7 +286,7 @@ class Slack:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
# 添加统一的自定义按钮(在所有媒体项之后)
|
# 添加统一的自定义按钮(在所有媒体项之后)
|
||||||
for button_row in buttons:
|
for button_row in buttons:
|
||||||
elements = []
|
elements = []
|
||||||
@@ -366,7 +364,7 @@ class Slack:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
# 判断是编辑消息还是发送新消息
|
# 判断是编辑消息还是发送新消息
|
||||||
if original_message_id and original_chat_id:
|
if original_message_id and original_chat_id:
|
||||||
# 编辑消息
|
# 编辑消息
|
||||||
@@ -423,7 +421,7 @@ class Slack:
|
|||||||
}]
|
}]
|
||||||
# 列表
|
# 列表
|
||||||
index = 1
|
index = 1
|
||||||
|
|
||||||
# 如果有自定义按钮,先添加种子列表,然后添加统一的按钮
|
# 如果有自定义按钮,先添加种子列表,然后添加统一的按钮
|
||||||
if buttons:
|
if buttons:
|
||||||
# 添加种子列表(不带单独的选择按钮)
|
# 添加种子列表(不带单独的选择按钮)
|
||||||
@@ -433,9 +431,9 @@ class Slack:
|
|||||||
meta = MetaInfo(torrent.title, torrent.description)
|
meta = MetaInfo(torrent.title, torrent.description)
|
||||||
link = torrent.page_url
|
link = torrent.page_url
|
||||||
title_text = f"{meta.season_episode} " \
|
title_text = f"{meta.season_episode} " \
|
||||||
f"{meta.resource_term} " \
|
f"{meta.resource_term} " \
|
||||||
f"{meta.video_term} " \
|
f"{meta.video_term} " \
|
||||||
f"{meta.release_group}"
|
f"{meta.release_group}"
|
||||||
title_text = re.sub(r"\s+", " ", title_text).strip()
|
title_text = re.sub(r"\s+", " ", title_text).strip()
|
||||||
free = torrent.volume_factor
|
free = torrent.volume_factor
|
||||||
seeder = f"{torrent.seeders}↑"
|
seeder = f"{torrent.seeders}↑"
|
||||||
@@ -453,7 +451,7 @@ class Slack:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
# 添加统一的自定义按钮
|
# 添加统一的自定义按钮
|
||||||
for button_row in buttons:
|
for button_row in buttons:
|
||||||
elements = []
|
elements = []
|
||||||
@@ -493,9 +491,9 @@ class Slack:
|
|||||||
meta = MetaInfo(torrent.title, torrent.description)
|
meta = MetaInfo(torrent.title, torrent.description)
|
||||||
link = torrent.page_url
|
link = torrent.page_url
|
||||||
title_text = f"{meta.season_episode} " \
|
title_text = f"{meta.season_episode} " \
|
||||||
f"{meta.resource_term} " \
|
f"{meta.resource_term} " \
|
||||||
f"{meta.video_term} " \
|
f"{meta.video_term} " \
|
||||||
f"{meta.release_group}"
|
f"{meta.release_group}"
|
||||||
title_text = re.sub(r"\s+", " ", title_text).strip()
|
title_text = re.sub(r"\s+", " ", title_text).strip()
|
||||||
free = torrent.volume_factor
|
free = torrent.volume_factor
|
||||||
seeder = f"{torrent.seeders}↑"
|
seeder = f"{torrent.seeders}↑"
|
||||||
@@ -530,7 +528,7 @@ class Slack:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
# 判断是编辑消息还是发送新消息
|
# 判断是编辑消息还是发送新消息
|
||||||
if original_message_id and original_chat_id:
|
if original_message_id and original_chat_id:
|
||||||
# 编辑消息
|
# 编辑消息
|
||||||
@@ -552,6 +550,43 @@ class Slack:
|
|||||||
logger.error(f"Slack消息发送失败: {msg_e}")
|
logger.error(f"Slack消息发送失败: {msg_e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def delete_msg(self, message_id: str, chat_id: Optional[str] = None) -> Optional[bool]:
|
||||||
|
"""
|
||||||
|
删除Slack消息
|
||||||
|
:param message_id: 消息时间戳(Slack消息ID)
|
||||||
|
:param chat_id: 频道ID
|
||||||
|
:return: 删除是否成功
|
||||||
|
"""
|
||||||
|
if not self._client:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 确定要删除消息的频道ID
|
||||||
|
if chat_id:
|
||||||
|
target_channel = chat_id
|
||||||
|
else:
|
||||||
|
target_channel = self.__find_public_channel()
|
||||||
|
|
||||||
|
if not target_channel:
|
||||||
|
logger.error("无法确定要删除消息的Slack频道")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 删除消息
|
||||||
|
result = self._client.chat_delete(
|
||||||
|
channel=target_channel,
|
||||||
|
ts=message_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.get("ok"):
|
||||||
|
logger.info(f"成功删除Slack消息: channel={target_channel}, ts={message_id}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.error(f"删除Slack消息失败: {result.get('error', 'unknown error')}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"删除Slack消息异常: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
def __find_public_channel(self):
|
def __find_public_channel(self):
|
||||||
"""
|
"""
|
||||||
查找公共频道
|
查找公共频道
|
||||||
|
|||||||
@@ -286,6 +286,29 @@ class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
|||||||
original_message_id=message.original_message_id,
|
original_message_id=message.original_message_id,
|
||||||
original_chat_id=message.original_chat_id)
|
original_chat_id=message.original_chat_id)
|
||||||
|
|
||||||
|
def delete_message(self, channel: MessageChannel, source: str,
|
||||||
|
message_id: int, chat_id: Optional[int] = None) -> bool:
|
||||||
|
"""
|
||||||
|
删除消息
|
||||||
|
:param channel: 消息渠道
|
||||||
|
:param source: 指定的消息源
|
||||||
|
:param message_id: 消息ID
|
||||||
|
:param chat_id: 聊天ID
|
||||||
|
:return: 删除是否成功
|
||||||
|
"""
|
||||||
|
success = False
|
||||||
|
for conf in self.get_configs().values():
|
||||||
|
if channel != self._channel:
|
||||||
|
continue
|
||||||
|
if source != conf.name:
|
||||||
|
continue
|
||||||
|
client: Telegram = self.get_instance(conf.name)
|
||||||
|
if client:
|
||||||
|
result = client.delete_msg(message_id=message_id, chat_id=chat_id)
|
||||||
|
if result:
|
||||||
|
success = True
|
||||||
|
return success
|
||||||
|
|
||||||
def register_commands(self, commands: Dict[str, dict]):
|
def register_commands(self, commands: Dict[str, dict]):
|
||||||
"""
|
"""
|
||||||
注册命令,实现这个函数接收系统可用的命令菜单
|
注册命令,实现这个函数接收系统可用的命令菜单
|
||||||
|
|||||||
@@ -336,6 +336,35 @@ class Telegram:
|
|||||||
logger.error(f"回应回调查询失败:{str(e)}")
|
logger.error(f"回应回调查询失败:{str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def delete_msg(self, message_id: int, chat_id: Optional[int] = None) -> Optional[bool]:
|
||||||
|
"""
|
||||||
|
删除Telegram消息
|
||||||
|
:param message_id: 消息ID
|
||||||
|
:param chat_id: 聊天ID
|
||||||
|
:return: 删除是否成功
|
||||||
|
"""
|
||||||
|
if not self._telegram_token or not self._telegram_chat_id:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 确定要删除消息的聊天ID
|
||||||
|
if chat_id:
|
||||||
|
target_chat_id = chat_id
|
||||||
|
else:
|
||||||
|
target_chat_id = self._telegram_chat_id
|
||||||
|
|
||||||
|
# 删除消息
|
||||||
|
result = self._bot.delete_message(chat_id=target_chat_id, message_id=int(message_id))
|
||||||
|
if result:
|
||||||
|
logger.info(f"成功删除Telegram消息: chat_id={target_chat_id}, message_id={message_id}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.error(f"删除Telegram消息失败: chat_id={target_chat_id}, message_id={message_id}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"删除Telegram消息异常: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
def __edit_message(self, chat_id: str, message_id: int, text: str,
|
def __edit_message(self, chat_id: str, message_id: int, text: str,
|
||||||
buttons: Optional[List[List[dict]]] = None,
|
buttons: Optional[List[List[dict]]] = None,
|
||||||
image: Optional[str] = None) -> Optional[bool]:
|
image: Optional[str] = None) -> Optional[bool]:
|
||||||
@@ -352,7 +381,7 @@ class Telegram:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# 创建按钮键盘
|
# 创建按钮键盘
|
||||||
reply_markup = None
|
reply_markup = None
|
||||||
if buttons:
|
if buttons:
|
||||||
|
|||||||
@@ -145,6 +145,8 @@ class ChannelCapability(Enum):
|
|||||||
MENU_COMMANDS = "menu_commands"
|
MENU_COMMANDS = "menu_commands"
|
||||||
# 支持消息编辑
|
# 支持消息编辑
|
||||||
MESSAGE_EDITING = "message_editing"
|
MESSAGE_EDITING = "message_editing"
|
||||||
|
# 支持消息删除
|
||||||
|
MESSAGE_DELETION = "message_deletion"
|
||||||
# 支持回调查询
|
# 支持回调查询
|
||||||
CALLBACK_QUERIES = "callback_queries"
|
CALLBACK_QUERIES = "callback_queries"
|
||||||
# 支持富文本
|
# 支持富文本
|
||||||
@@ -182,6 +184,7 @@ class ChannelCapabilityManager:
|
|||||||
ChannelCapability.INLINE_BUTTONS,
|
ChannelCapability.INLINE_BUTTONS,
|
||||||
ChannelCapability.MENU_COMMANDS,
|
ChannelCapability.MENU_COMMANDS,
|
||||||
ChannelCapability.MESSAGE_EDITING,
|
ChannelCapability.MESSAGE_EDITING,
|
||||||
|
ChannelCapability.MESSAGE_DELETION,
|
||||||
ChannelCapability.CALLBACK_QUERIES,
|
ChannelCapability.CALLBACK_QUERIES,
|
||||||
ChannelCapability.RICH_TEXT,
|
ChannelCapability.RICH_TEXT,
|
||||||
ChannelCapability.IMAGES,
|
ChannelCapability.IMAGES,
|
||||||
@@ -205,6 +208,8 @@ class ChannelCapabilityManager:
|
|||||||
channel=MessageChannel.Slack,
|
channel=MessageChannel.Slack,
|
||||||
capabilities={
|
capabilities={
|
||||||
ChannelCapability.INLINE_BUTTONS,
|
ChannelCapability.INLINE_BUTTONS,
|
||||||
|
ChannelCapability.MESSAGE_EDITING,
|
||||||
|
ChannelCapability.MESSAGE_DELETION,
|
||||||
ChannelCapability.CALLBACK_QUERIES,
|
ChannelCapability.CALLBACK_QUERIES,
|
||||||
ChannelCapability.RICH_TEXT,
|
ChannelCapability.RICH_TEXT,
|
||||||
ChannelCapability.IMAGES,
|
ChannelCapability.IMAGES,
|
||||||
@@ -290,6 +295,13 @@ class ChannelCapabilityManager:
|
|||||||
"""
|
"""
|
||||||
return cls.supports_capability(channel, ChannelCapability.MESSAGE_EDITING)
|
return cls.supports_capability(channel, ChannelCapability.MESSAGE_EDITING)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def supports_deletion(cls, channel: MessageChannel) -> bool:
|
||||||
|
"""
|
||||||
|
检查渠道是否支持消息删除
|
||||||
|
"""
|
||||||
|
return cls.supports_capability(channel, ChannelCapability.MESSAGE_DELETION)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_max_buttons_per_row(cls, channel: MessageChannel) -> int:
|
def get_max_buttons_per_row(cls, channel: MessageChannel) -> int:
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user