mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-09 09:26:55 +08:00
fix: deduplicate SunnyPT site messages
This commit is contained in:
+33
-17
@@ -78,23 +78,7 @@ class SiteChain(ChainBase):
|
|||||||
eventmanager.send_event(EventType.SiteRefreshed, {
|
eventmanager.send_event(EventType.SiteRefreshed, {
|
||||||
"site_id": site.get("id")
|
"site_id": site.get("id")
|
||||||
})
|
})
|
||||||
# 发送站点消息
|
self._post_site_messages(site=site, userdata=userdata)
|
||||||
if userdata.message_unread:
|
|
||||||
if userdata.message_unread_contents and len(userdata.message_unread_contents) > 0:
|
|
||||||
for head, date, content in userdata.message_unread_contents:
|
|
||||||
msg_title = f"【站点 {site.get('name')} 消息】"
|
|
||||||
msg_text = f"时间:{date}\n标题:{head}\n内容:\n{content}"
|
|
||||||
self.post_message(Notification(
|
|
||||||
mtype=NotificationType.SiteMessage,
|
|
||||||
title=msg_title, text=msg_text, link=site.get("url")
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
self.post_message(Notification(
|
|
||||||
mtype=NotificationType.SiteMessage,
|
|
||||||
title=f"站点 {site.get('name')} 收到 "
|
|
||||||
f"{userdata.message_unread} 条新消息,请登陆查看",
|
|
||||||
link=site.get("url")
|
|
||||||
))
|
|
||||||
# 低分享率警告
|
# 低分享率警告
|
||||||
if userdata.ratio and float(userdata.ratio) < 1 and not bool(
|
if userdata.ratio and float(userdata.ratio) < 1 and not bool(
|
||||||
re.search(r"(贵宾|VIP?)", userdata.user_level or "", re.IGNORECASE)):
|
re.search(r"(贵宾|VIP?)", userdata.user_level or "", re.IGNORECASE)):
|
||||||
@@ -105,6 +89,38 @@ class SiteChain(ChainBase):
|
|||||||
))
|
))
|
||||||
return userdata
|
return userdata
|
||||||
|
|
||||||
|
def _post_site_messages(self, site: dict, userdata: SiteUserData) -> None:
|
||||||
|
"""
|
||||||
|
发送站点未读消息,并按解析器提供的来源标识做持久化去重。
|
||||||
|
|
||||||
|
:param site: 站点索引配置
|
||||||
|
:param userdata: 本次刷新的站点用户数据
|
||||||
|
"""
|
||||||
|
if not userdata.message_unread:
|
||||||
|
return
|
||||||
|
if not userdata.message_unread_contents:
|
||||||
|
self.post_message(Notification(
|
||||||
|
mtype=NotificationType.SiteMessage,
|
||||||
|
title=f"站点 {site.get('name')} 收到 "
|
||||||
|
f"{userdata.message_unread} 条新消息,请登陆查看",
|
||||||
|
link=site.get("url")
|
||||||
|
))
|
||||||
|
return
|
||||||
|
for message in userdata.message_unread_contents:
|
||||||
|
head, date, content, *metadata = message
|
||||||
|
message_source = metadata[0] if metadata else None
|
||||||
|
if message_source and self.messageoper.exists_by_source(message_source):
|
||||||
|
continue
|
||||||
|
msg_title = f"【站点 {site.get('name')} 消息】"
|
||||||
|
msg_text = f"时间:{date}\n标题:{head}\n内容:\n{content}"
|
||||||
|
self.post_message(Notification(
|
||||||
|
source=message_source,
|
||||||
|
mtype=NotificationType.SiteMessage,
|
||||||
|
title=msg_title,
|
||||||
|
text=msg_text,
|
||||||
|
link=site.get("url")
|
||||||
|
))
|
||||||
|
|
||||||
def refresh_userdatas(
|
def refresh_userdatas(
|
||||||
self,
|
self,
|
||||||
progress_callback: Optional[Callable[..., None]] = None,
|
progress_callback: Optional[Callable[..., None]] = None,
|
||||||
|
|||||||
@@ -105,6 +105,15 @@ class MessageOper(DbOper):
|
|||||||
"""
|
"""
|
||||||
return Message.list_by_page(self._db, page, count)
|
return Message.list_by_page(self._db, page, count)
|
||||||
|
|
||||||
|
def exists_by_source(self, source: str) -> bool:
|
||||||
|
"""
|
||||||
|
判断指定来源标识的消息记录是否存在。
|
||||||
|
|
||||||
|
:param source: 消息来源唯一标识
|
||||||
|
:return: 是否存在匹配记录
|
||||||
|
"""
|
||||||
|
return Message.exists_by_source(self._db, source)
|
||||||
|
|
||||||
async def async_list_by_page(
|
async def async_list_by_page(
|
||||||
self, page: Optional[int] = 1, count: Optional[int] = 30
|
self, page: Optional[int] = 1, count: Optional[int] = 30
|
||||||
) -> list[Message]:
|
) -> list[Message]:
|
||||||
|
|||||||
@@ -62,6 +62,18 @@ class Message(Base):
|
|||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@db_query
|
||||||
|
def exists_by_source(cls, db: Session, source: str) -> bool:
|
||||||
|
"""
|
||||||
|
判断指定来源标识的消息记录是否存在。
|
||||||
|
|
||||||
|
:param db: 数据库会话
|
||||||
|
:param source: 消息来源唯一标识
|
||||||
|
:return: 是否存在匹配记录
|
||||||
|
"""
|
||||||
|
return db.query(cls.id).filter(cls.source == source).first() is not None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@async_db_query
|
@async_db_query
|
||||||
async def async_list_by_page(
|
async def async_list_by_page(
|
||||||
|
|||||||
@@ -123,8 +123,12 @@ class SunnyPTSiteUserInfo(SiteParserBase):
|
|||||||
title = message.get("title")
|
title = message.get("title")
|
||||||
content = message.get("content")
|
content = message.get("content")
|
||||||
created_at = StringUtils.unify_datetime_str(message.get("created_at"))
|
created_at = StringUtils.unify_datetime_str(message.get("created_at"))
|
||||||
|
message_id = message.get("id")
|
||||||
if title and content and created_at:
|
if title and content and created_at:
|
||||||
self.message_unread_contents.append((title, created_at, content))
|
message_source = f"sunnypt-message:{message_id}" if message_id is not None else None
|
||||||
|
self.message_unread_contents.append(
|
||||||
|
(title, created_at, content, message_source)
|
||||||
|
)
|
||||||
return "next" if messages_data.get("has_more") else None
|
return "next" if messages_data.get("has_more") else None
|
||||||
|
|
||||||
def _parse_user_traffic_info(self, html_text: str) -> None:
|
def _parse_user_traffic_info(self, html_text: str) -> None:
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ from app.chain.download import DownloadChain
|
|||||||
from app.chain.site import SiteChain
|
from app.chain.site import SiteChain
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.context import TorrentInfo
|
from app.core.context import TorrentInfo
|
||||||
|
from app.db.message_oper import MessageOper
|
||||||
from app.modules.indexer import IndexerModule
|
from app.modules.indexer import IndexerModule
|
||||||
from app.modules.indexer.parser.sunnypt import SunnyPTSiteUserInfo
|
from app.modules.indexer.parser.sunnypt import SunnyPTSiteUserInfo
|
||||||
from app.modules.indexer.spider.sunnypt import SunnyPTSpider
|
from app.modules.indexer.spider.sunnypt import SunnyPTSpider
|
||||||
from app.schemas import MediaType
|
from app.schemas import MediaType, NotificationType
|
||||||
|
|
||||||
|
|
||||||
class _FakeResponse:
|
class _FakeResponse:
|
||||||
@@ -286,11 +287,47 @@ def test_sunnypt_user_parser_reads_profile_and_messages_without_marking_read(mon
|
|||||||
assert parser.leeching_size == 10737418240
|
assert parser.leeching_size == 10737418240
|
||||||
assert parser.message_unread == 1
|
assert parser.message_unread == 1
|
||||||
assert parser.message_unread_contents == [
|
assert parser.message_unread_contents == [
|
||||||
("种子审核通过", "2026-07-21 16:30:00", "你发布的种子已审核通过。")
|
(
|
||||||
|
"种子审核通过",
|
||||||
|
"2026-07-21 16:30:00",
|
||||||
|
"你发布的种子已审核通过。",
|
||||||
|
"sunnypt-message:9001",
|
||||||
|
)
|
||||||
]
|
]
|
||||||
assert not any(url.endswith("/read") or url.endswith("/read-all") for url in requested_urls)
|
assert not any(url.endswith("/read") or url.endswith("/read-all") for url in requested_urls)
|
||||||
|
|
||||||
|
|
||||||
|
def test_site_messages_are_deduplicated_by_persisted_source(monkeypatch):
|
||||||
|
"""站点消息应使用解析器保留的消息 ID 来源标识做持久化去重。"""
|
||||||
|
duplicate_source = "sunnypt-message:9001-dedup-test"
|
||||||
|
MessageOper().add(
|
||||||
|
source=duplicate_source,
|
||||||
|
mtype=NotificationType.SiteMessage,
|
||||||
|
title="existing",
|
||||||
|
text="existing",
|
||||||
|
)
|
||||||
|
sent_messages = []
|
||||||
|
chain = object.__new__(SiteChain)
|
||||||
|
chain.messageoper = MessageOper()
|
||||||
|
monkeypatch.setattr(chain, "post_message", sent_messages.append)
|
||||||
|
userdata = SimpleNamespace(
|
||||||
|
message_unread=2,
|
||||||
|
message_unread_contents=[
|
||||||
|
("重复消息", "2026-07-21 16:30:00", "旧内容", duplicate_source),
|
||||||
|
("新消息", "2026-07-22 16:30:00", "新内容", "sunnypt-message:9002-dedup-test"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
chain._post_site_messages(
|
||||||
|
site={"name": "Sunny", "url": "https://sunnypt.top/"},
|
||||||
|
userdata=userdata,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(sent_messages) == 1
|
||||||
|
assert sent_messages[0].source == "sunnypt-message:9002-dedup-test"
|
||||||
|
assert sent_messages[0].title == "【站点 Sunny 消息】"
|
||||||
|
|
||||||
|
|
||||||
def test_indexer_module_dispatches_sunnypt_search(monkeypatch):
|
def test_indexer_module_dispatches_sunnypt_search(monkeypatch):
|
||||||
"""IndexerModule 应把 SunnyPT 同步搜索参数交给专用 API Spider。"""
|
"""IndexerModule 应把 SunnyPT 同步搜索参数交给专用 API Spider。"""
|
||||||
captured = {}
|
captured = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user