diff --git a/package.json b/package.json index 70b2c5c..2aff180 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "SiteStatistic": { "name": "站点数据统计", "description": "自动统计和展示站点数据。", - "version": "1.8", + "version": "1.9", "icon": "statistic.png", "author": "lightolly", "level": 2 diff --git a/plugins/sitestatistic/__init__.py b/plugins/sitestatistic/__init__.py index 491dddd..34e56b9 100644 --- a/plugins/sitestatistic/__init__.py +++ b/plugins/sitestatistic/__init__.py @@ -43,7 +43,7 @@ class SiteStatistic(_PluginBase): # 插件图标 plugin_icon = "statistic.png" # 插件版本 - plugin_version = "1.8" + plugin_version = "1.9" # 插件作者 plugin_author = "lightolly" # 作者主页 diff --git a/plugins/sitestatistic/siteuserinfo/__init__.py b/plugins/sitestatistic/siteuserinfo/__init__.py index e8e218f..5535a68 100644 --- a/plugins/sitestatistic/siteuserinfo/__init__.py +++ b/plugins/sitestatistic/siteuserinfo/__init__.py @@ -31,6 +31,7 @@ class SiteSchema(Enum): TorrentLeech = "TorrentLeech" FileList = "FileList" TNode = "TNode" + MTorrent = "MTorrent" class ISiteUserInfo(metaclass=ABCMeta): @@ -78,6 +79,9 @@ class ISiteUserInfo(metaclass=ABCMeta): self.seeding_info = [] # 用户详细信息 + self._user_basic_page = None + self._user_basic_params = None + self._user_basic_headers = None self.user_level = None self.join_at = None self.bonus = 0.0 @@ -93,10 +97,18 @@ class ISiteUserInfo(metaclass=ABCMeta): # 站点页面 self._brief_page = "index.php" self._user_detail_page = "userdetails.php?id=" + self._user_detail_params = None + self._user_detail_headers = None self._user_traffic_page = "index.php" - self._torrent_seeding_page = "getusertorrentlistajax.php?userid=" + self._user_traffic_params = None + self._user_traffic_headers = None self._user_mail_unread_page = "messages.php?action=viewmailbox&box=1&unread=yes" self._sys_mail_unread_page = "messages.php?action=viewmailbox&box=-2&unread=yes" + self._mail_unread_params = None + self._mail_unread_headers = None + self._mail_content_params = None + self._mail_content_headers = None + self._torrent_seeding_page = "getusertorrentlistajax.php?userid=" self._torrent_seeding_params = None self._torrent_seeding_headers = None @@ -133,17 +145,43 @@ class ISiteUserInfo(metaclass=ABCMeta): 解析站点信息 :return: """ + # 检查是否已经登录 if not self._parse_logged_in(self._index_html): return - + # 解析站点页面 self._parse_site_page(self._index_html) - self._parse_user_base_info(self._index_html) - self._pase_unread_msgs() - if self._user_traffic_page: - self._parse_user_traffic_info(self._get_page_content(urljoin(self._base_url, self._user_traffic_page))) + # 解析用户基础信息 + if self._user_basic_page: + self._parse_user_base_info( + self._get_page_content( + url=urljoin(self._base_url, self._user_basic_page), + params=self._user_basic_params, + headers=self._user_basic_headers + ) + ) + else: + self._parse_user_base_info(self._index_html) + # 解析用户详细信息 if self._user_detail_page: - self._parse_user_detail_info(self._get_page_content(urljoin(self._base_url, self._user_detail_page))) - + self._parse_user_detail_info( + self._get_page_content( + url=urljoin(self._base_url, self._user_detail_page), + params=self._user_detail_params, + headers=self._user_detail_headers + ) + ) + # 解析用户未读消息 + self._pase_unread_msgs() + # 解析用户上传、下载、分享率等信息 + if self._user_traffic_page: + self._parse_user_traffic_info( + self._get_page_content( + url=urljoin(self._base_url, self._user_traffic_page), + params=self._user_traffic_params, + headers=self._user_traffic_headers + ) + ) + # 解析用户做种信息 self._parse_seeding_pages() self.seeding_info = json.dumps(self.seeding_info) @@ -158,36 +196,59 @@ class ISiteUserInfo(metaclass=ABCMeta): for link in links: if not link: continue - msg_links = [] next_page = self._parse_message_unread_links( - self._get_page_content(urljoin(self._base_url, link)), msg_links) + self._get_page_content( + url=urljoin(self._base_url, link), + params=self._mail_unread_params, + headers=self._mail_unread_headers + ), + msg_links) while next_page: next_page = self._parse_message_unread_links( - self._get_page_content(urljoin(self._base_url, next_page)), msg_links) - + self._get_page_content( + url=urljoin(self._base_url, next_page), + params=self._mail_unread_params, + headers=self._mail_unread_headers + ), + msg_links + ) unread_msg_links.extend(msg_links) - + # 解析未读消息内容 for msg_link in unread_msg_links: logger.debug(f"{self.site_name} 信息链接 {msg_link}") - head, date, content = self._parse_message_content(self._get_page_content(urljoin(self._base_url, msg_link))) + head, date, content = self._parse_message_content( + self._get_page_content( + urljoin(self._base_url, msg_link), + params=self._mail_content_params, + headers=self._mail_content_headers + ) + ) logger.debug(f"{self.site_name} 标题 {head} 时间 {date} 内容 {content}") self.message_unread_contents.append((head, date, content)) def _parse_seeding_pages(self): + """ + 解析做种页面 + """ if self._torrent_seeding_page: # 第一页 next_page = self._parse_user_torrent_seeding_info( - self._get_page_content(urljoin(self._base_url, self._torrent_seeding_page), - self._torrent_seeding_params, - self._torrent_seeding_headers)) + self._get_page_content( + url=urljoin(self._base_url, self._torrent_seeding_page), + params=self._torrent_seeding_params, + headers=self._torrent_seeding_headers + ) + ) # 其他页处理 while next_page: next_page = self._parse_user_torrent_seeding_info( - self._get_page_content(urljoin(urljoin(self._base_url, self._torrent_seeding_page), next_page), - self._torrent_seeding_params, - self._torrent_seeding_headers), + self._get_page_content( + url=urljoin(urljoin(self._base_url, self._torrent_seeding_page), next_page), + params=self._torrent_seeding_params, + headers=self._torrent_seeding_headers + ), multi_page=True) @staticmethod diff --git a/plugins/sitestatistic/siteuserinfo/mtorrent.py b/plugins/sitestatistic/siteuserinfo/mtorrent.py new file mode 100644 index 0000000..39e2883 --- /dev/null +++ b/plugins/sitestatistic/siteuserinfo/mtorrent.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- +import json +from typing import Optional, Tuple +from urllib.parse import urljoin + +from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER, SiteSchema + + +class MTorrentSiteUserInfo(ISiteUserInfo): + schema = SiteSchema.MTorrent + order = SITE_BASE_ORDER + 60 + + @classmethod + def match(cls, html_text: str) -> bool: + return 'M-Team' in html_text + + def _parse_site_page(self, html_text: str): + """ + 获取站点页面地址 + """ + self._user_traffic_page = None + self._user_detail_page = None + self._user_basic_page = "api/member/profile" + self._user_basic_params = { + "uid": self.userid + } + self._sys_mail_unread_page = None + self._user_mail_unread_page = "api/msg/search" + self._mail_unread_params = { + "keyword": "", + "box": "-2", + "type": "pageNumber", + "pageSize": 100 + } + self._torrent_seeding_page = "api/member/getUserTorrentList" + + def _parse_logged_in(self, html_text): + """ + 判断是否登录成功, 通过判断是否存在用户信息 + 暂时跳过检测,待后续优化 + :param html_text: + :return: + """ + return True + + def _parse_user_base_info(self, html_text: str): + """ + 解析用户基本信息,这里把_parse_user_traffic_info和_parse_user_detail_info合并到这里 + """ + if not html_text: + return None + detail = json.loads(html_text) + if not detail or detail.get("code") != "0": + return + user_info = detail.get("data", {}) + self.userid = user_info.get("id") + self.username = user_info.get("username") + self.user_level = user_info.get("role") + self.join_at = user_info.get("memberStatus", {}).get("createdDate") + + self.upload = user_info.get("memberCount", {}).get("uploaded") + self.download = user_info.get("memberCount", {}).get("downloaded") + self.ratio = user_info.get("memberCount", {}).get("shareRate") or 0 + self.bonus = user_info.get("bonus") + self.message_unread = 1 + + self._torrent_seeding_params = { + "pageNumber": "1", + "pageSize": "20000", + "type": "seeding", + "userid": self.userid + } + + def _parse_user_traffic_info(self, html_text: str): + """ + 解析用户流量信息 + """ + pass + + def _parse_user_detail_info(self, html_text: str): + """ + 解析用户详细信息 + """ + pass + + def _parse_user_torrent_seeding_info(self, html_text: str, multi_page: bool = False) -> Optional[str]: + """ + 解析用户做种信息 + """ + if not html_text: + return None + seeding_info = json.loads(html_text) + if not seeding_info or seeding_info.get("code") != "0": + return + + torrents = seeding_info.get("data", {}).get("data", []) + + page_seeding_size = 0 + page_seeding_info = [] + for info in torrents: + torrent = info.get("torrent", {}) + size = int(torrent.get("size") or '0') + seeders = int(torrent.get("source") or '0') + + page_seeding_size += size + page_seeding_info.append([seeders, size]) + + self.seeding += len(torrents) + self.seeding_size += page_seeding_size + self.seeding_info.extend(page_seeding_info) + + # 是否存在下页数据 + return None + + def _parse_message_unread_links(self, html_text: str, msg_links: list) -> Optional[str]: + """ + 解析未读消息链接,这里直接读出详情 + """ + if not html_text: + return None + messages_info = json.loads(html_text) + if not messages_info or messages_info.get("code") != "0": + return None + messages = messages_info.get("data", {}).get("data", []) + for message in messages: + head = message.get("title") + date = message.get("createdDate") + content = message.get("context") + if head and date and content: + self.message_unread_contents.append((head, date, content)) + # 是否存在下页数据 + return None + + def _parse_message_content(self, html_text) -> Tuple[Optional[str], Optional[str], Optional[str]]: + """ + 解析消息内容 + """ + pass diff --git a/plugins/sitestatistic/siteuserinfo/nexus_php.py b/plugins/sitestatistic/siteuserinfo/nexus_php.py index 8d5b6c9..783f92b 100644 --- a/plugins/sitestatistic/siteuserinfo/nexus_php.py +++ b/plugins/sitestatistic/siteuserinfo/nexus_php.py @@ -61,6 +61,9 @@ class NexusPhpSiteUserInfo(ISiteUserInfo): self.message_unread = StringUtils.str_int(message_text) def _parse_user_base_info(self, html_text: str): + """ + 解析用户基本信息 + """ # 合并解析,减少额外请求调用 self._parse_user_traffic_info(html_text) self._user_traffic_page = None @@ -85,6 +88,9 @@ class NexusPhpSiteUserInfo(ISiteUserInfo): return def _parse_user_traffic_info(self, html_text): + """ + 解析用户流量信息 + """ html_text = self._prepare_html_text(html_text) upload_match = re.search(r"[^总]上[传傳]量?[::_<>/a-zA-Z-=\"'\s#;]+([\d,.\s]+[KMGTPI]*B)", html_text, re.IGNORECASE)