fix plugins

This commit is contained in:
jxxghp 2023-06-08 15:47:14 +08:00
parent c7d745a752
commit 0d0b078a31
25 changed files with 191 additions and 125 deletions

View File

@ -1,3 +1,6 @@
import importlib
from pathlib import Path
from alembic.command import upgrade from alembic.command import upgrade
from alembic.config import Config from alembic.config import Config
@ -13,6 +16,10 @@ def init_db():
""" """
初始化数据库 初始化数据库
""" """
# 导入模块,避免建表缺失
for module in Path(__file__).with_name("models").glob("*.py"):
importlib.import_module(f"app.db.models.{module.stem}")
# 全量建表
Base.metadata.create_all(bind=Engine) Base.metadata.create_all(bind=Engine)
# 初始化超级管理员 # 初始化超级管理员
_db = SessionLocal() _db = SessionLocal()

22
app/db/models/plugin.py Normal file
View File

@ -0,0 +1,22 @@
from sqlalchemy import Column, Integer, String, Sequence
from sqlalchemy.orm import Session
from app.db.models import Base
class PluginData(Base):
"""
插件数据表
"""
id = Column(Integer, Sequence('id'), primary_key=True, index=True)
plugin_id = Column(String, nullable=False, index=True)
key = Column(String, index=True, nullable=False)
value = Column(String)
@staticmethod
def get_plugin_data(db: Session, plugin_id: str):
return db.query(PluginData).filter(PluginData.plugin_id == plugin_id).all()
@staticmethod
def get_plugin_data_by_key(db: Session, plugin_id: str, key: str):
return db.query(PluginData.value).filter(PluginData.plugin_id == plugin_id, PluginData.key == key).first()

View File

@ -7,6 +7,9 @@ from app.db.models import Base
class Site(Base): class Site(Base):
"""
站点表
"""
id = Column(Integer, Sequence('id'), primary_key=True, index=True) id = Column(Integer, Sequence('id'), primary_key=True, index=True)
name = Column(String, nullable=False) name = Column(String, nullable=False)
domain = Column(String, index=True) domain = Column(String, index=True)

View File

@ -5,6 +5,9 @@ from app.db.models import Base
class Subscribe(Base): class Subscribe(Base):
"""
订阅表
"""
id = Column(Integer, Sequence('id'), primary_key=True, index=True) id = Column(Integer, Sequence('id'), primary_key=True, index=True)
name = Column(String, nullable=False, index=True) name = Column(String, nullable=False, index=True)
year = Column(String) year = Column(String)

View File

@ -5,6 +5,9 @@ from app.db.models import Base
class SystemConfig(Base): class SystemConfig(Base):
"""
配置表
"""
id = Column(Integer, Sequence('id'), primary_key=True, index=True) id = Column(Integer, Sequence('id'), primary_key=True, index=True)
key = Column(String, index=True) key = Column(String, index=True)
value = Column(String, nullable=True) value = Column(String, nullable=True)

View File

@ -6,6 +6,9 @@ from app.db.models import Base
class User(Base): class User(Base):
"""
用户表
"""
id = Column(Integer, Sequence('id'), primary_key=True, index=True) id = Column(Integer, Sequence('id'), primary_key=True, index=True)
full_name = Column(String, index=True) full_name = Column(String, index=True)
email = Column(String, unique=True, index=True, nullable=False) email = Column(String, unique=True, index=True, nullable=False)

View File

@ -24,7 +24,9 @@ def shutdown_server():
""" """
服务关闭 服务关闭
""" """
# 停止定时服务
Scheduler().stop() Scheduler().stop()
# 停止插件
PluginManager().stop() PluginManager().stop()

View File

@ -1,10 +1,15 @@
import json
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from pathlib import Path from pathlib import Path
from typing import Any, Optional from typing import Any, Optional
from app.chain import ChainBase from app.chain import ChainBase
from app.core import settings, Context from app.core import settings, Context
from app.db import SessionLocal
from app.db.models import Base
from app.db.models.plugin import PluginData
from app.db.systemconfigs import SystemConfigs from app.db.systemconfigs import SystemConfigs
from app.utils.object import ObjectUtils
class PluginChian(ChainBase): class PluginChian(ChainBase):
@ -34,6 +39,7 @@ class _PluginBase(metaclass=ABCMeta):
plugin_desc: str = "" plugin_desc: str = ""
def __init__(self): def __init__(self):
self.db = SessionLocal()
self.chain = PluginChian() self.chain = PluginChian()
@abstractmethod @abstractmethod
@ -80,3 +86,24 @@ class _PluginBase(metaclass=ABCMeta):
if not data_path.exists(): if not data_path.exists():
data_path.mkdir(parents=True) data_path.mkdir(parents=True)
return data_path return data_path
def save_data(self, key: str, value: Any) -> Base:
"""
保存插件数据
:param key: 数据key
:param value: 数据值
"""
if ObjectUtils.is_obj(value):
value = json.dumps(value)
plugin = PluginData(plugin_id=self.__class__.__name__, key=key, value=value)
return plugin.create(self.db)
def get_data(self, key: str) -> Any:
"""
获取插件数据
:param key: 数据key
"""
data = PluginData.get_plugin_data_by_key(self.db, self.__class__.__name__, key)
if ObjectUtils.is_obj(data):
return json.load(data)
return data

View File

@ -5,7 +5,6 @@ from typing import Any
from urllib.parse import urljoin from urllib.parse import urljoin
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.schedulers.background import BackgroundScheduler
from lxml import etree
from ruamel.yaml import CommentedMap from ruamel.yaml import CommentedMap
from app.core import EventManager, settings, eventmanager from app.core import EventManager, settings, eventmanager
@ -15,6 +14,7 @@ from app.helper.sites import SitesHelper
from app.log import logger from app.log import logger
from app.plugins import _PluginBase from app.plugins import _PluginBase
from app.utils.http import RequestUtils from app.utils.http import RequestUtils
from app.utils.site import SiteUtils
from app.utils.timer import TimerUtils from app.utils.timer import TimerUtils
from app.utils.types import EventType from app.utils.types import EventType
@ -81,6 +81,8 @@ class AutoSignIn(_PluginBase):
""" """
自动签到 自动签到
""" """
if event:
logger.info("收到远程签到命令,开始执行签到任务 ...")
# 查询签到站点 # 查询签到站点
sign_sites = self.sites.get_indexers() sign_sites = self.sites.get_indexers()
if not sign_sites: if not sign_sites:
@ -123,7 +125,8 @@ class AutoSignIn(_PluginBase):
else: else:
return self.__signin_base(site_info) return self.__signin_base(site_info)
def __signin_base(self, site_info: CommentedMap) -> str: @staticmethod
def __signin_base(site_info: CommentedMap) -> str:
""" """
通用签到处理 通用签到处理
:param site_info: 站点信息 :param site_info: 站点信息
@ -158,7 +161,7 @@ class AutoSignIn(_PluginBase):
).get_res(url=site_url) ).get_res(url=site_url)
# 判断登录状态 # 判断登录状态
if res and res.status_code in [200, 500, 403]: if res and res.status_code in [200, 500, 403]:
if not self.is_logged_in(res.text): if not SiteUtils.is_logged_in(res.text):
if under_challenge(res.text): if under_challenge(res.text):
msg = "站点被Cloudflare防护请更换Cookie和UA" msg = "站点被Cloudflare防护请更换Cookie和UA"
elif res.status_code == 200: elif res.status_code == 200:
@ -194,32 +197,3 @@ class AutoSignIn(_PluginBase):
self._scheduler = None self._scheduler = None
except Exception as e: except Exception as e:
logger.error("退出插件失败:%s" % str(e)) logger.error("退出插件失败:%s" % str(e))
@classmethod
def is_logged_in(cls, html_text: str) -> bool:
"""
判断站点是否已经登陆
:param html_text:
:return:
"""
html = etree.HTML(html_text)
if not html:
return False
# 存在明显的密码输入框,说明未登录
if html.xpath("//input[@type='password']"):
return False
# 是否存在登出和用户面板等链接
xpaths = ['//a[contains(@href, "logout")'
' or contains(@data-url, "logout")'
' or contains(@href, "mybonus") '
' or contains(@onclick, "logout")'
' or contains(@href, "usercp")]',
'//form[contains(@action, "logout")]']
for xpath in xpaths:
if html.xpath(xpath):
return True
user_info_div = html.xpath('//div[@class="user-info-side"]')
if user_info_div:
return True
return False

View File

@ -1,9 +1,10 @@
from datetime import datetime from datetime import datetime
from multiprocessing.dummy import Pool as ThreadPool from multiprocessing.dummy import Pool as ThreadPool
from threading import Lock from threading import Lock
from typing import Optional, Any from typing import Optional, Any, List
import requests import requests
from apscheduler.schedulers.background import BackgroundScheduler
from ruamel.yaml import CommentedMap from ruamel.yaml import CommentedMap
from app.core import settings from app.core import settings
@ -11,23 +12,28 @@ from app.helper import ModuleHelper
from app.helper.sites import SitesHelper from app.helper.sites import SitesHelper
from app.log import logger from app.log import logger
from app.plugins import _PluginBase from app.plugins import _PluginBase
from app.plugins.sitestatistics.siteuserinfo import ISiteUserInfo from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo
from app.utils.http import RequestUtils from app.utils.http import RequestUtils
from app.utils.timer import TimerUtils
import warnings
warnings.filterwarnings("ignore", category=FutureWarning)
lock = Lock() lock = Lock()
class SiteStatistics(_PluginBase): class SiteStatistic(_PluginBase):
sites = None sites = None
_scheduler: BackgroundScheduler = None
_MAX_CONCURRENCY: int = 10 _MAX_CONCURRENCY: int = 10
_last_update_time: Optional[datetime] = None _last_update_time: Optional[datetime] = None
_sites_data: dict = {} _sites_data: dict = {}
_site_schema: list = None _site_schema: List[ISiteUserInfo] = None
def init_plugin(self, config: dict = None): def init_plugin(self, config: dict = None):
# 加载模块 # 加载模块
self._site_schema = ModuleHelper.load('app.plugins.sitestatistics.siteuserinfo', self._site_schema = ModuleHelper.load('app.plugins.sitestatistic.siteuserinfo',
filter_func=lambda _, obj: hasattr(obj, 'schema')) filter_func=lambda _, obj: hasattr(obj, 'schema'))
self._site_schema.sort(key=lambda x: x.order) self._site_schema.sort(key=lambda x: x.order)
# 站点管理 # 站点管理
@ -36,6 +42,20 @@ class SiteStatistics(_PluginBase):
self._last_update_time = None self._last_update_time = None
# 站点数据 # 站点数据
self._sites_data = {} self._sites_data = {}
# 定时服务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
triggers = TimerUtils.random_scheduler(num_executions=1,
begin_hour=0,
end_hour=1,
min_interval=1,
max_interval=60)
for trigger in triggers:
self._scheduler.add_job(self.refresh_all_site_data, "cron", hour=trigger.hour, minute=trigger.minute)
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
def stop_service(self): def stop_service(self):
pass pass
@ -46,13 +66,16 @@ class SiteStatistics(_PluginBase):
if site_schema.match(html_text): if site_schema.match(html_text):
return site_schema return site_schema
except Exception as e: except Exception as e:
logger.error(f"站点 {site_schema.name} 匹配失败 {e}") logger.error(f"站点匹配失败 {e}")
return None return None
def build(self, url: str, site_name: str, def build(self, url: str, site_name: str,
site_cookie: str = None, site_cookie: str = None,
ua: str = None, ua: str = None,
proxy: bool = False) -> Any: proxy: bool = False) -> Optional[ISiteUserInfo]:
"""
构建站点信息
"""
if not site_cookie: if not site_cookie:
return None return None
session = requests.Session() session = requests.Session()
@ -121,22 +144,22 @@ class SiteStatistics(_PluginBase):
return None return None
return site_schema(site_name, url, site_cookie, html_text, session=session, ua=ua, proxy=proxy) return site_schema(site_name, url, site_cookie, html_text, session=session, ua=ua, proxy=proxy)
def __refresh_site_data(self, site_info: CommentedMap): def __refresh_site_data(self, site_info: CommentedMap) -> Optional[ISiteUserInfo]:
""" """
更新单个site 数据信息 更新单个site 数据信息
:param site_info: :param site_info:
:return: :return:
""" """
site_name = site_info.get("name") site_name = site_info.get("name")
site_url = site_info.get("strict_url") site_url = site_info.get("url")
if not site_url: if not site_url:
return return None
site_cookie = site_info.get("cookie") site_cookie = site_info.get("cookie")
ua = site_info.get("ua") ua = site_info.get("ua")
unread_msg_notify = True unread_msg_notify = True
proxy = site_info.get("proxy") proxy = site_info.get("proxy")
try: try:
site_user_info = self.build(url=site_url, site_user_info: ISiteUserInfo = self.build(url=site_url,
site_name=site_name, site_name=site_name,
site_cookie=site_cookie, site_cookie=site_cookie,
ua=ua, ua=ua,
@ -150,7 +173,7 @@ class SiteStatistics(_PluginBase):
# 获取不到数据时,仅返回错误信息,不做历史数据更新 # 获取不到数据时,仅返回错误信息,不做历史数据更新
if site_user_info.err_msg: if site_user_info.err_msg:
self._sites_data.update({site_name: {"err_msg": site_user_info.err_msg}}) self._sites_data.update({site_name: {"err_msg": site_user_info.err_msg}})
return return None
# 发送通知,存在未读消息 # 发送通知,存在未读消息
self.__notify_unread_msg(site_name, site_user_info, unread_msg_notify) self.__notify_unread_msg(site_name, site_user_info, unread_msg_notify)
@ -173,11 +196,11 @@ class SiteStatistics(_PluginBase):
"message_unread": site_user_info.message_unread "message_unread": site_user_info.message_unread
} }
}) })
return site_user_info return site_user_info
except Exception as e: except Exception as e:
logger.error(f"站点 {site_name} 获取流量数据失败:{str(e)}") logger.error(f"站点 {site_name} 获取流量数据失败:{str(e)}")
return None
def __notify_unread_msg(self, site_name: str, site_user_info: ISiteUserInfo, unread_msg_notify: bool): def __notify_unread_msg(self, site_name: str, site_user_info: ISiteUserInfo, unread_msg_notify: bool):
if site_user_info.message_unread <= 0: if site_user_info.message_unread <= 0:
@ -228,35 +251,15 @@ class SiteStatistics(_PluginBase):
# 并发刷新 # 并发刷新
with ThreadPool(min(len(refresh_sites), self._MAX_CONCURRENCY)) as p: with ThreadPool(min(len(refresh_sites), self._MAX_CONCURRENCY)) as p:
site_user_infos = p.map(self.__refresh_site_data, refresh_sites) site_user_infos: List[ISiteUserInfo] = p.map(self.__refresh_site_data, refresh_sites)
site_user_infos = [info for info in site_user_infos if info] site_user_infos = [info for info in site_user_infos if info]
# 保存数据
print(site_user_infos) for site_user_info in site_user_infos:
# TODO 登记历史数据 # 获取今天的日期
# TODO 实时用户数据 key = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# TODO 更新站点图标 value = site_user_info.to_dict()
# TODO 实时做种信息 # 按日期保存为字典
self.save_data(key, value)
# 更新时间 # 更新时间
self._last_update_time = datetime.now() self._last_update_time = datetime.now()
@staticmethod
def __todict(raw_statistics):
statistics = []
for site in raw_statistics:
statistics.append({"site": site.SITE,
"username": site.USERNAME,
"user_level": site.USER_LEVEL,
"join_at": site.JOIN_AT,
"update_at": site.UPDATE_AT,
"upload": site.UPLOAD,
"download": site.DOWNLOAD,
"ratio": site.RATIO,
"seeding": site.SEEDING,
"leeching": site.LEECHING,
"seeding_size": site.SEEDING_SIZE,
"bonus": site.BONUS,
"url": site.URL,
"msg_unread": site.MSG_UNREAD
})
return statistics

View File

@ -14,6 +14,7 @@ from app.core import settings
from app.helper.cloudflare import under_challenge from app.helper.cloudflare import under_challenge
from app.log import logger from app.log import logger
from app.utils.http import RequestUtils from app.utils.http import RequestUtils
from app.utils.site import SiteUtils
from app.utils.types import SiteSchema from app.utils.types import SiteSchema
SITE_BASE_ORDER = 1000 SITE_BASE_ORDER = 1000
@ -101,7 +102,7 @@ class ISiteUserInfo(metaclass=ABCMeta):
self._emulate = emulate self._emulate = emulate
self._proxy = proxy self._proxy = proxy
def site_schema(self): def site_schema(self) -> SiteSchema:
""" """
站点解析模型 站点解析模型
:return: 站点解析模型 :return: 站点解析模型
@ -196,7 +197,7 @@ class ISiteUserInfo(metaclass=ABCMeta):
""" """
pass pass
def _parse_favicon(self, html_text): def _parse_favicon(self, html_text: str):
""" """
解析站点favicon,返回base64 fav图标 解析站点favicon,返回base64 fav图标
:param html_text: :param html_text:
@ -213,7 +214,7 @@ class ISiteUserInfo(metaclass=ABCMeta):
if res: if res:
self.site_favicon = base64.b64encode(res.content).decode() self.site_favicon = base64.b64encode(res.content).decode()
def _get_page_content(self, url, params=None, headers=None): def _get_page_content(self, url: str, params: dict = None, headers: dict = None):
""" """
:param url: 网页地址 :param url: 网页地址
:param params: post参数 :param params: post参数
@ -285,7 +286,7 @@ class ISiteUserInfo(metaclass=ABCMeta):
:param html_text: :param html_text:
:return: True/False :return: True/False
""" """
logged_in = self.is_logged_in(html_text) logged_in = SiteUtils.is_logged_in(html_text)
if not logged_in: if not logged_in:
self.err_msg = "未检测到已登陆请检查cookies是否过期" self.err_msg = "未检测到已登陆请检查cookies是否过期"
logger.warn(f"{self.site_name} 未登录,跳过后续操作") logger.warn(f"{self.site_name} 未登录,跳过后续操作")
@ -330,31 +331,16 @@ class ISiteUserInfo(metaclass=ABCMeta):
""" """
pass pass
@classmethod def to_dict(self):
def is_logged_in(cls, html_text: str) -> bool:
""" """
判断站点是否已经登陆 转化为字典
:param html_text:
:return:
""" """
html = etree.HTML(html_text) attributes = [
if not html: attr for attr in dir(self)
return False if not callable(getattr(self, attr)) and not attr.startswith("_")
# 存在明显的密码输入框,说明未登录 ]
if html.xpath("//input[@type='password']"): return {
return False attr: getattr(self, attr).value
# 是否存在登出和用户面板等链接 if isinstance(getattr(self, attr), SiteSchema)
xpaths = ['//a[contains(@href, "logout")' else getattr(self, attr) for attr in attributes
' or contains(@data-url, "logout")' }
' or contains(@href, "mybonus") '
' or contains(@onclick, "logout")'
' or contains(@href, "usercp")]',
'//form[contains(@action, "logout")]']
for xpath in xpaths:
if html.xpath(xpath):
return True
user_info_div = html.xpath('//div[@class="user-info-side"]')
if user_info_div:
return True
return False

View File

@ -4,7 +4,7 @@ from typing import Optional
from lxml import etree from lxml import etree
from app.plugins.sitestatistics.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER
from app.utils.string import StringUtils from app.utils.string import StringUtils
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

View File

@ -4,7 +4,7 @@ from typing import Optional
from lxml import etree from lxml import etree
from app.plugins.sitestatistics.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER
from app.utils.string import StringUtils from app.utils.string import StringUtils
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

View File

@ -4,7 +4,7 @@ from typing import Optional
from lxml import etree from lxml import etree
from app.plugins.sitestatistics.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER
from app.utils.string import StringUtils from app.utils.string import StringUtils
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

View File

@ -4,7 +4,7 @@ from typing import Optional
from lxml import etree from lxml import etree
from app.plugins.sitestatistics.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER
from app.utils.string import StringUtils from app.utils.string import StringUtils
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

View File

@ -5,7 +5,7 @@ from typing import Optional
from lxml import etree from lxml import etree
from app.log import logger from app.log import logger
from app.plugins.sitestatistics.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER
from app.utils.string import StringUtils from app.utils.string import StringUtils
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import re import re
from app.plugins.sitestatistics.siteuserinfo import SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import SITE_BASE_ORDER
from app.plugins.sitestatistics.siteuserinfo.nexus_php import NexusPhpSiteUserInfo from app.plugins.sitestatistic.siteuserinfo.nexus_php import NexusPhpSiteUserInfo
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

View File

@ -5,8 +5,8 @@ from typing import Optional
from lxml import etree from lxml import etree
from app.log import logger from app.log import logger
from app.plugins.sitestatistics.siteuserinfo import SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import SITE_BASE_ORDER
from app.plugins.sitestatistics.siteuserinfo.nexus_php import NexusPhpSiteUserInfo from app.plugins.sitestatistic.siteuserinfo.nexus_php import NexusPhpSiteUserInfo
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

View File

@ -4,7 +4,7 @@ from typing import Optional
from lxml import etree from lxml import etree
from app.plugins.sitestatistics.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER
from app.utils.string import StringUtils from app.utils.string import StringUtils
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

View File

@ -3,7 +3,7 @@ import json
import re import re
from typing import Optional from typing import Optional
from app.plugins.sitestatistics.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER
from app.utils.string import StringUtils from app.utils.string import StringUtils
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

View File

@ -4,7 +4,7 @@ from typing import Optional
from lxml import etree from lxml import etree
from app.plugins.sitestatistics.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER
from app.utils.string import StringUtils from app.utils.string import StringUtils
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

View File

@ -4,7 +4,7 @@ from typing import Optional
from lxml import etree from lxml import etree
from app.plugins.sitestatistics.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER from app.plugins.sitestatistic.siteuserinfo import ISiteUserInfo, SITE_BASE_ORDER
from app.utils.string import StringUtils from app.utils.string import StringUtils
from app.utils.types import SiteSchema from app.utils.types import SiteSchema

33
app/utils/site.py Normal file
View File

@ -0,0 +1,33 @@
from lxml import etree
class SiteUtils:
@classmethod
def is_logged_in(cls, html_text: str) -> bool:
"""
判断站点是否已经登陆
:param html_text:
:return:
"""
html = etree.HTML(html_text)
if not html:
return False
# 存在明显的密码输入框,说明未登录
if html.xpath("//input[@type='password']"):
return False
# 是否存在登出和用户面板等链接
xpaths = ['//a[contains(@href, "logout")'
' or contains(@data-url, "logout")'
' or contains(@href, "mybonus") '
' or contains(@onclick, "logout")'
' or contains(@href, "usercp")]',
'//form[contains(@action, "logout")]']
for xpath in xpaths:
if html.xpath(xpath):
return True
user_info_div = html.xpath('//div[@class="user-info-side"]')
if user_info_div:
return True
return False

View File

@ -30,8 +30,8 @@ class TimerUtils:
random_interval = datetime.timedelta(minutes=interval_minutes) random_interval = datetime.timedelta(minutes=interval_minutes)
# 更新当前时间为下一个任务的时间触发器 # 更新当前时间为下一个任务的时间触发器
random_trigger += random_interval random_trigger += random_interval
# 达到结时间时退出 # 达到结时间时退出
if now.hour > end_hour: if random_trigger.hour > end_hour:
break break
# 添加到队列 # 添加到队列
trigger.append(random_trigger) trigger.append(random_trigger)

View File

@ -15,9 +15,9 @@ if __name__ == '__main__':
# 测试名称识别 # 测试名称识别
suite.addTest(MetaInfoTest('test_metainfo')) suite.addTest(MetaInfoTest('test_metainfo'))
# 测试媒体识别 # 测试媒体识别
# suite.addTest(RecognizeTest('test_recognize')) suite.addTest(RecognizeTest('test_recognize'))
# 测试CookieCloud同步 # 测试CookieCloud同步
# suite.addTest(CookieCloudTest('test_cookiecloud')) suite.addTest(CookieCloudTest('test_cookiecloud'))
# 测试文件转移 # 测试文件转移
# suite.addTest(TransferTest('test_transfer')) # suite.addTest(TransferTest('test_transfer'))
# 测试豆瓣同步 # 测试豆瓣同步