From 5795e02548169ecbee4c204100ae194d7232dbbc Mon Sep 17 00:00:00 2001 From: Leon Date: Wed, 11 Aug 2021 18:33:18 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=82=F0=9F=94=A8FN=5Fextend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Scripts/py/FreeNom/FN_extend.py | 53 ++++ Scripts/py/FreeNom/requirements.txt | 7 + Scripts/py/FreeNom/templates/default.html | 344 ++++++++++++++++++++++ Scripts/py/FreeNom/utils/__init__.py | 0 Scripts/py/FreeNom/utils/exception.py | 9 + Scripts/py/FreeNom/utils/freenom.py | 126 ++++++++ Scripts/py/FreeNom/utils/mail.py | 52 ++++ Scripts/py/FreeNom/utils/settings.py | 15 + Scripts/py/mimotion.py | 2 +- 9 files changed, 607 insertions(+), 1 deletion(-) create mode 100644 Scripts/py/FreeNom/FN_extend.py create mode 100644 Scripts/py/FreeNom/requirements.txt create mode 100644 Scripts/py/FreeNom/templates/default.html create mode 100644 Scripts/py/FreeNom/utils/__init__.py create mode 100644 Scripts/py/FreeNom/utils/exception.py create mode 100644 Scripts/py/FreeNom/utils/freenom.py create mode 100644 Scripts/py/FreeNom/utils/mail.py create mode 100644 Scripts/py/FreeNom/utils/settings.py diff --git a/Scripts/py/FreeNom/FN_extend.py b/Scripts/py/FreeNom/FN_extend.py new file mode 100644 index 0000000..e100123 --- /dev/null +++ b/Scripts/py/FreeNom/FN_extend.py @@ -0,0 +1,53 @@ +# -*- coding: utf8 -*- +''' +Author: shuai93 +Modifier: Oreo +Date: Wed Aug 11 10:15:41 UTC 2021 +建议cron: 25 7 */10 * * python3 FN_extend.py +------------ +环境变量说明 示例 +FN_ID: Freenom 用户名 1234567890@gmail.com +FN_PW: Freenom 密码 12345678 +MAIL_USER: 发件人邮箱用户名 address@vip.qq.com 或 123456@qq.com +MAIL_ADDRESS: 发件人邮箱地址 address@vip.qq.com 或 123456@qq.com +* MAIL_HOST: 发件人邮箱服务器 smt.qq.com 不填默认为这个 +* MAIL_PORT: 邮箱服务器端口 465 不填默认为这个 +MAIL_TO: 收件人邮箱可与发件人相同 address@vip.qq.com 或 123456@qq.com + +填写总参考:https://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=369 +------------ +依赖模块说明 +pip install -r requirements.txt / pip3 install -r requirements.txt +''' +from utils.settings import * +from utils.exception import CustomException +from utils.freenom import FreeNom +from utils.mail import EmailPoster + + +def main(): + print("配置信息") + print([MAIL_TO, MAIL_PORT, MAIL_HOST, MAIL_ADDRESS, MAIL_PW, MAIL_USER, FN_ID, FN_PW]) + if not all([MAIL_TO, MAIL_PORT, MAIL_HOST, MAIL_ADDRESS, MAIL_PW, MAIL_USER, FN_ID, FN_PW]): + raise CustomException("参数缺失") + + to = [MAIL_TO] + + body = { + 'subject': "FreeNom 自动续期", + 'to': to, + } + try: + results = FreeNom().run() + body['payload'] = { + "results": results, + "user": FN_ID + } + except CustomException as e: + body['body'] = e.message + + EmailPoster().send(data=body) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Scripts/py/FreeNom/requirements.txt b/Scripts/py/FreeNom/requirements.txt new file mode 100644 index 0000000..d3379d8 --- /dev/null +++ b/Scripts/py/FreeNom/requirements.txt @@ -0,0 +1,7 @@ +certifi==2020.12.5 +chardet==4.0.0 +idna==2.10 +Jinja2==3.0.0 +MarkupSafe==2.0.0 +requests==2.25.1 +urllib3==1.26.4 diff --git a/Scripts/py/FreeNom/templates/default.html b/Scripts/py/FreeNom/templates/default.html new file mode 100644 index 0000000..748a1f3 --- /dev/null +++ b/Scripts/py/FreeNom/templates/default.html @@ -0,0 +1,344 @@ + + + + + + Simple Transactional Email + + + + + + + + + +
  +
+

账户 {{ payload.get('user') }} 今天所有域名续期情况如下:

+ + + + + + + + + +
+ + + + + + + + + + + {% for data in payload.get('results') %} + + + + + + + {% endfor %} + +
域名剩余天数结果详情
{{data[0]}}{{data[1]}}{{data[3]}}查看详情
+
+ + + + + + +
+
 
+ + diff --git a/Scripts/py/FreeNom/utils/__init__.py b/Scripts/py/FreeNom/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Scripts/py/FreeNom/utils/exception.py b/Scripts/py/FreeNom/utils/exception.py new file mode 100644 index 0000000..f6e4aef --- /dev/null +++ b/Scripts/py/FreeNom/utils/exception.py @@ -0,0 +1,9 @@ + +class CustomException(Exception): + + def __init__(self, message): + super().__init__(self) + self.message = message + + def __str__(self): + return self.message diff --git a/Scripts/py/FreeNom/utils/freenom.py b/Scripts/py/FreeNom/utils/freenom.py new file mode 100644 index 0000000..eea91b3 --- /dev/null +++ b/Scripts/py/FreeNom/utils/freenom.py @@ -0,0 +1,126 @@ +import re +import time + +import requests + +from utils import settings +from utils.exception import CustomException + + +class FreeNom(object): + """ + FreeNom api请求 + """ + # 登录 + LOGIN_URL = 'https://my.freenom.com/dologin.php' + # 查看域名状态 + DOMAIN_STATUS_URL = 'https://my.freenom.com/domains.php?a=renewals' + # 域名续期 + RENEW_DOMAIN_URL = 'https://my.freenom.com/domains.php?submitrenewals=true' + + TOKEN_REGEX = 'name="token"\svalue="(?P[a-z||A-Z||0-9]+)"' + DOMAIN_INFO_REGEX = '(?P[^<]+)<\/td>[^<]+<\/td>[^<]+(?P\d+)[' \ + '^&]+&domain=(?P\d+)"' + LOGIN_STATUS_REGEX = '' + + def __init__(self): + self.headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + } + self.session = requests.session() + self.token_pattern = re.compile(self.TOKEN_REGEX) + self.domain_info_pattern = re.compile(self.DOMAIN_INFO_REGEX) + self.login_pattern = re.compile(self.LOGIN_STATUS_REGEX) + + def run(self) -> list: + self.login() + html = self.get_domains() + token_match = self.token_pattern.findall(html) + domain_info_match = self.domain_info_pattern.findall(html) + login_match = self.login_pattern.findall(html) + + if not login_match: + print("FreeNom login parse failed") + raise CustomException("登录检查失败") + + if not token_match: + print("FreeNom token parse failed") + raise CustomException("页面token检查失败") + + if not domain_info_match: + print("FreeNom domain info parse failed") + raise CustomException("页面没有获取到域名信息") + + token = token_match[0] + print(f"waiting for renew domain info is {domain_info_match}") + + result = [] + + for info in domain_info_match: + time.sleep(1) + domain, days, domain_id = info + msg = "失败" + + if int(days) > 14: + print(f"FreeNom domain {domain} can not renew, days until expiry is {days}") + + else: + response = self.renew_domain(token, domain_id) + + if response.find("Order Confirmation") != -1: + msg = "成功" + print(f"FreeNom renew domain {domain} is success") + + result.append((domain, days, domain_id, msg)) + return result + + def login(self) -> bool: + data = { + 'username': settings.FN_ID, + 'password': settings.FN_PW + } + headers = { + **self.headers, + 'Referer': 'https://my.freenom.com/clientarea.php' + } + response = self.session.post(self.LOGIN_URL, data=data, headers=headers) + + if response.status_code == 200: + return True + else: + print("FreeNom login failed") + raise CustomException("调用登录接口失败") + + def get_domains(self) -> str: + headers = { + 'Referer': 'https://my.freenom.com/clientarea.php' + } + response = self.session.get(self.DOMAIN_STATUS_URL, headers=headers) + + if response.status_code == 200: + return response.text + else: + print("FreeNom check domain status failed") + raise CustomException("调用获取域名信息接口失败") + + def renew_domain(self, token, renewalid) -> str: + headers = { + **self.headers, + "Referer": "https://my.freenom.com/domains.php?a=renewdomain&domain=" + "renewalid" + } + data = { + "token": token, + "renewalid": renewalid, + f"renewalperiod[{renewalid}]": "12M", + 'paymentmethod': 'credit' + } + + response = self.session.post(self.RENEW_DOMAIN_URL, data=data, headers=headers) + if response.status_code == 200: + return response.text + else: + print("FreeNom renew domain failed") + raise CustomException("调用续期接口失败接口失败") + + def __del__(self): + self.session.close() diff --git a/Scripts/py/FreeNom/utils/mail.py b/Scripts/py/FreeNom/utils/mail.py new file mode 100644 index 0000000..a323cb6 --- /dev/null +++ b/Scripts/py/FreeNom/utils/mail.py @@ -0,0 +1,52 @@ +import smtplib +import traceback +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from jinja2 import FileSystemLoader, Environment, Template + +from . import settings + + +class EmailPoster(object): + """ + 邮件发送基础类 + + """ + + @staticmethod + def get_template(): + loader = FileSystemLoader('templates') + env = Environment(loader=loader) + template = env.get_template("default.html") + return template + + def send(self, data: dict): + payload = data.get("payload", {}) + if payload: + template = self.get_template() + content = template.render(payload=payload) + else: + content = data.get('body', '') + subject = data.get('subject', '') + mail_to = data.get('to', []) + mail_from = data.get('from', settings.MAIL_ADDRESS) + self._send(content, subject, mail_from, mail_to) + + @staticmethod + def _send(content: str, subject: str, mail_from: str, mail_to: list): + msg_root = MIMEMultipart('related') + msg_text = MIMEText(content, 'html', 'utf-8') + msg_root.attach(msg_text) + msg_root['Subject'] = subject + msg_root['From'] = mail_from + msg_root['To'] = ";".join(mail_to) + + try: + smtp = smtplib.SMTP_SSL(settings.MAIL_HOST, settings.MAIL_PORT) + # smtp.set_debuglevel(1) + smtp.ehlo() + smtp.login(settings.MAIL_USER, settings.MAIL_PW) + smtp.sendmail(settings.MAIL_ADDRESS, mail_to, msg_root.as_string()) + smtp.quit() + except Exception as e: + print(traceback.format_exc(e)) diff --git a/Scripts/py/FreeNom/utils/settings.py b/Scripts/py/FreeNom/utils/settings.py new file mode 100644 index 0000000..2b12f08 --- /dev/null +++ b/Scripts/py/FreeNom/utils/settings.py @@ -0,0 +1,15 @@ +import os + +# qq mail +MAIL_ADDRESS = os.getenv("MAIL_ADDRESS", "") +MAIL_HOST = os.getenv("SMTP_HOST", "smtp.qq.com") +MAIL_PW= os.getenv("MAIL_PW", "") +MAIL_PORT = int(os.getenv("SMTP_PORT", 465)) +MAIL_TO = os.getenv("MAIL_TO", "") +MAIL_USER = os.getenv("MAIL_USER", "") + + +# free nom +FN_ID = os.getenv("FN_ID", "") +FN_PW = os.getenv("FN_PW", "") + diff --git a/Scripts/py/mimotion.py b/Scripts/py/mimotion.py index a092007..698bc11 100644 --- a/Scripts/py/mimotion.py +++ b/Scripts/py/mimotion.py @@ -11,7 +11,7 @@ Date: Tue Aug 10 08:24:30 UTC 2021 MI_USER: 账号 仅支持手机号,多账号用 # 分隔 MI_PWD: 密码 多账号用 # 分隔,且与账号一一对应 STEP: 步数 空或不填则为 18000-25000 之间随机,自定义示例: 18763 或 19000-24000 -PMODE: 推送模式 || PKEY: 具体推送格式填写(不带 [ ],请用具体的值代替) +PMODE: 推送模式 || PKEY: 具体推送格式填写(不带 [TG: ],请用具体的值代替) wx [Server 酱: skey] nwx [新 Server 酱: skey] tg [TG: tg_bot_token@user_id]