From 3c379282d4ba431ac22961fd330c4aedb922a7ef Mon Sep 17 00:00:00 2001 From: jxxghp Date: Thu, 11 Apr 2024 21:01:32 +0800 Subject: [PATCH] =?UTF-8?q?add=20IYUU=E7=AB=99=E7=82=B9=E7=BB=91=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 8 + plugins/iyuuauth/__init__.py | 225 ++++++++++++++++++++++++++++ plugins/iyuuauth/iyuu_helper.py | 90 +++++++++++ plugins/iyuuautoseed/iyuu_helper.py | 49 +----- 4 files changed, 330 insertions(+), 42 deletions(-) create mode 100644 plugins/iyuuauth/__init__.py create mode 100644 plugins/iyuuauth/iyuu_helper.py diff --git a/package.json b/package.json index 5c34843..2e5c266 100644 --- a/package.json +++ b/package.json @@ -496,5 +496,13 @@ "icon": "FeiShu_A.png", "author": "InfinityPacer", "level": 2 + }, + "IyuuAuth": { + "name": "IYUU站点绑定", + "description": "为IYUU账号绑定认证站点,以便用于用户认证和辅种。", + "version": "1.0", + "icon": "Iyuu_A.png", + "author": "jxxghp", + "level": 1 } } diff --git a/plugins/iyuuauth/__init__.py b/plugins/iyuuauth/__init__.py new file mode 100644 index 0000000..2554840 --- /dev/null +++ b/plugins/iyuuauth/__init__.py @@ -0,0 +1,225 @@ +import os +from typing import Any, List, Dict, Tuple + +from app.log import logger +from app.plugins import _PluginBase +from app.plugins.iyuuauth.iyuu_helper import IyuuHelper + + +class IyuuAuth(_PluginBase): + # 插件名称 + plugin_name = "IYUU站点绑定" + # 插件描述 + plugin_desc = "为IYUU账号绑定认证站点,以便用于用户认证和辅种。" + # 插件图标 + plugin_icon = "Iyuu_A.png" + # 插件版本 + plugin_version = "1.0" + # 插件作者 + plugin_author = "jxxghp" + # 作者主页 + author_url = "https://github.com/jxxghp" + # 插件配置项ID前缀 + plugin_config_prefix = "iyuuauth_" + # 加载顺序 + plugin_order = 25 + # 可使用的用户级别 + auth_level = 1 + + # 私有属性 + iyuu = None + _enabled = False + _token = None + _site = None + _passkey = None + _uid = None + + def init_plugin(self, config: dict = None): + if config: + self._enabled = config.get("enabled") + self._token = config.get("token") or os.environ.get("IYUU_SIGN") + self._site = config.get("site") + self._passkey = config.get("passkey") + self._uid = config.get("uid") + if self._token: + self.iyuu = IyuuHelper(self._token) + # 开始绑定站点 + if self._enabled: + if not self._token or not self._passkey or not self._uid: + logger.warn("IYUU站点绑定插件配置不完整,请检查配置!") + self.systemmessage.put("IYUU站点绑定插件配置不完整,请检查配置!") + return + state, message = self.iyuu.bind_site(site=self._site, passkey=self._passkey, uid=self._uid) + if not state: + logger.warn(f"IYUU站点绑定失败,错误信息:{message}") + self.systemmessage.put(f"IYUU站点绑定失败,错误信息:{message}") + else: + logger.info("IYUU站点绑定成功!") + self.systemmessage.put("IYUU站点绑定成功!") + self._enabled = False + self.update_config({ + "enabled": self._enabled, + "token": self._token, + "site": self._site, + "passkey": self._passkey, + "uid": self._uid + }) + + def get_state(self) -> bool: + return self._enabled and (True if self._token else False) + + @staticmethod + def get_command() -> List[Dict[str, Any]]: + pass + + def get_api(self) -> List[Dict[str, Any]]: + pass + + def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: + """ + 拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构 + """ + # 编历 NotificationType 枚举,生成消息类型选项 + SiteOptions = [] + if self.iyuu: + for item in self.iyuu.get_auth_sites() or []: + SiteOptions.append({ + "title": item.get("site"), + "value": item.get("id") + }) + return [ + { + 'component': 'VForm', + 'content': [ + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 6 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'enabled', + 'label': '启用插件', + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'token', + 'label': 'IYUU令牌', + 'placeholder': 'IYUUxxx', + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4, + }, + 'content': [ + { + 'component': 'VSelect', + 'props': { + 'model': 'site', + 'label': '绑定站点', + 'items': SiteOptions + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4, + }, + 'content': [ + { + 'component': 'VTextfield', + 'props': { + 'model': 'passkey', + 'label': '站点密钥', + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 4, + }, + 'content': [ + { + 'component': 'VTextfield', + 'props': { + 'model': 'uid', + 'label': '用户UID', + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + }, + 'content': [ + { + 'component': 'VAlert', + 'props': { + 'type': 'info', + 'variant': 'tonal', + 'text': '如果设置了`IYUU_SIGN`环境变量则会自动读取,否则需要先填写 IYUU令牌 保存,重新打开插件才能选择绑定站点。' + } + } + ] + } + ] + } + ] + } + ], { + "enabled": False, + "token": os.environ.get("IYUU_SIGN") + } + + def get_page(self) -> List[dict]: + pass + + def stop_service(self): + """ + 退出插件 + """ + pass diff --git a/plugins/iyuuauth/iyuu_helper.py b/plugins/iyuuauth/iyuu_helper.py new file mode 100644 index 0000000..2d3dcb7 --- /dev/null +++ b/plugins/iyuuauth/iyuu_helper.py @@ -0,0 +1,90 @@ +import hashlib +from typing import Optional, Tuple, List + +from app.utils.http import RequestUtils + + +class IyuuHelper(object): + _version = "2.0.0" + _api_base = "http://api.bolahg.cn/%s" + _sites = {} + _token = None + + def __init__(self, token: str): + self._token = token + if self._token: + self.init_config() + + def init_config(self): + pass + + def __request_iyuu(self, url: str, method: str = "get", params: dict = None) -> Tuple[Optional[dict], str]: + """ + 向IYUUApi发送请求 + """ + if params: + if not params.get("sign"): + params.update({"sign": self._token}) + if not params.get("version"): + params.update({"version": self._version}) + else: + params = {"sign": self._token, "version": self._version} + # 开始请求 + if method == "get": + ret = RequestUtils( + accept_type="application/json" + ).get_res(f"{url}", params=params) + else: + ret = RequestUtils( + accept_type="application/json" + ).post_res(f"{url}", data=params) + if ret: + result = ret.json() + if result.get('ret') == 200: + return result.get('data'), "" + else: + return None, f"请求IYUU失败,状态码:{result.get('ret')},返回信息:{result.get('msg')}" + elif ret is not None: + return None, f"请求IYUU失败,状态码:{ret.status_code},错误原因:{ret.reason}" + else: + return None, f"请求IYUU失败,未获取到返回信息" + + @staticmethod + def get_sha1(json_str: str) -> str: + return hashlib.sha1(json_str.encode('utf-8')).hexdigest() + + def get_auth_sites(self) -> List[dict]: + """ + 返回支持鉴权的站点列表 + [ + { + "id": 2, + "site": "pthome", + "bind_check": "passkey,uid" + } + ] + """ + result, msg = self.__request_iyuu(url=self._api_base % 'App.Api.GetRecommendSites') + if result: + return result.get('recommend') or [] + else: + print(msg) + return [] + + def bind_site(self, site: str, passkey: str, uid: str): + """ + 绑定站点 + :param site: 站点名称 + :param passkey: passkey + :param uid: 用户id + :return: 状态码、错误信息 + """ + result, msg = self.__request_iyuu(url=self._api_base % 'App.Api.Bind', + method="get", + params={ + "token": self._token, + "site": site, + "passkey": self.get_sha1(passkey), + "id": uid + }) + return result, msg diff --git a/plugins/iyuuautoseed/iyuu_helper.py b/plugins/iyuuautoseed/iyuu_helper.py index e7f3cee..914595a 100644 --- a/plugins/iyuuautoseed/iyuu_helper.py +++ b/plugins/iyuuautoseed/iyuu_helper.py @@ -1,6 +1,7 @@ import hashlib import json import time +from typing import Tuple, Optional from app.utils.http import RequestUtils @@ -11,7 +12,7 @@ class IyuuHelper(object): _sites = {} _token = None - def __init__(self, token): + def __init__(self, token: str): self._token = token if self._token: self.init_config() @@ -19,7 +20,7 @@ class IyuuHelper(object): def init_config(self): pass - def __request_iyuu(self, url, method="get", params=None): + def __request_iyuu(self, url: str, method: str = "get", params: dict = None) -> Tuple[Optional[dict], str]: """ 向IYUUApi发送请求 """ @@ -50,7 +51,7 @@ class IyuuHelper(object): else: return None, f"请求IYUU失败,未获取到返回信息" - def get_torrent_url(self, sid): + def get_torrent_url(self, sid: str) -> Tuple[Optional[str], Optional[str]]: if not sid: return None, None if not self._sites: @@ -60,7 +61,7 @@ class IyuuHelper(object): site = self._sites.get(sid) return site.get('base_url'), site.get('download_page') - def __get_sites(self): + def __get_sites(self) -> dict: """ 返回支持辅种的全部站点 :return: 站点列表、错误信息 @@ -92,7 +93,7 @@ class IyuuHelper(object): print(msg) return {} - def get_seed_info(self, info_hashs: list): + def get_seed_info(self, info_hashs: list) -> Tuple[Optional[dict], str]: """ 返回info_hash对应的站点id、种子id { @@ -126,41 +127,5 @@ class IyuuHelper(object): return result, msg @staticmethod - def get_sha1(json_str) -> str: + def get_sha1(json_str: str) -> str: return hashlib.sha1(json_str.encode('utf-8')).hexdigest() - - def get_auth_sites(self): - """ - 返回支持鉴权的站点列表 - [ - { - "id": 2, - "site": "pthome", - "bind_check": "passkey,uid" - } - ] - """ - result, msg = self.__request_iyuu(url=self._api_base % 'App.Api.GetRecommendSites') - if result: - return result.get('recommend') or [] - else: - print(msg) - return [] - - def bind_site(self, site, passkey, uid): - """ - 绑定站点 - :param site: 站点名称 - :param passkey: passkey - :param uid: 用户id - :return: 状态码、错误信息 - """ - result, msg = self.__request_iyuu(url=self._api_base % 'App.Api.Bind', - method="get", - params={ - "token": self._token, - "site": site, - "passkey": self.get_sha1(passkey), - "id": uid - }) - return result, msg