diff --git a/README.md b/README.md index 42e04aa..1899d14 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,44 @@ # EUserv_extend -使用 [腾讯云函数 SCF](https://console.cloud.tencent.com/scf/) 自动续期EUserv免费IPv6 VPS脚本 +使用 [腾讯云函数 SCF](https://console.cloud.tencent.com/scf/) 自动续期 EUserv 免费 IPv6 VPS 脚本 + ## 说明 自动获取账号内所有的VPS项目,并检测是否需要续期,需要续期会自动续期。 -## 使用说明 - -1、修改 **main.py** 中的用户名,密码,并配置合适的推送方式([Server酱](https://sc.ftqq.com/?c=code)、[酷推](https://cp.xuthus.cc)、[PushPlus](https://pushplus.hxtrip.com/message)、[Telegram Bot Push](https://core.telegram.org/bots/api#authorizing-your-bot) 或 [wecomchan](https://github.com/easychen/wecomchan)) +## 使用说明 -USERNAME: 你的EUserv账户邮箱或Customer ID +1、修改 **main.py** 中的用户名,密码 +USERNAME: 你的 EUserv 账户邮箱或 Customer ID ``` USERNAME = 'user@gmail.com' USERNAME = 'user1@gmail.com user2@gmail.com' # 多个账号写法 ``` -PASSWORD: 账户的密码 +PASSWORD: 账户的密码 ``` PASSWORD = 'password' PASSWORD = 'password1 password2' # 多个账号写法 ``` + +2、配置 TrueCaptcha 验证码接口 + +默认使用 TrueCaptcha 官方 Demo API。每个 APIKEY 每天有 100 次免费额度,建议自行注册以确保稳定性。 + +``` +TRUECAPTCHA_USERID = 'arun56' +TRUECAPTCHA_APIKEY = 'wMjXmBIcHcdYqO2RrsVN' +``` + +检查 API 使用次数,一般为 True,保持默认即可 +``` +TRUECAPTCHA_CHECK_USAGE = True +``` + +3、配置合适的推送方式([Server酱](https://sc.ftqq.com/?c=code)、[酷推](https://cp.xuthus.cc)、[PushPlus](https://pushplus.hxtrip.com/message)、[Telegram Bot Push](https://core.telegram.org/bots/api#authorizing-your-bot) 或 [wecomchan](https://github.com/easychen/wecomchan)) +
Server酱
 
@@ -64,19 +81,21 @@ WECOMCHAN_TO_USER = '@all'  # 默认全部推送, 对个别人推送可用 User1
 
-2、新建层 **BeautifulSoup** 将 [BeautifulSoup.zip](https://github.com/o0oo0ooo0/EUserv_extend/releases/download/0.1/BeautifulSoup.zip) 导入 ,添加运行环境 Python 3.6。 +4、新建层 **BeautifulSoup** 将 [BeautifulSoup.zip](https://github.com/o0oo0ooo0/EUserv_extend/releases/download/0.1/BeautifulSoup.zip) 导入 ,添加运行环境 Python 3.6。 -3、新建腾讯云函数 **EUserv_extend** ,运行环境选择 Python 3.6,创建方式选择 空白函数,内存选择 64M,执行超时时间建议为 300 s(网站在国外访问比较慢,建议部署在非大陆区域,例如HK、SG等),将修改后的 **main.py** 粘贴进去。 +5、新建腾讯云函数 **EUserv_extend** ,运行环境选择 Python 3.6,创建方式选择 空白函数,内存选择 64M,执行超时时间建议为 300 s(网站在国外访问比较慢,建议部署在非大陆区域,例如HK、SG等),将修改后的 **main.py** 粘贴进去。 -4、在 EUserv_extend⇨函数管理⇨层管理 里绑定层 **BeautifulSoup**。 +6、在 EUserv_extend ⇨ 函数管理 ⇨ 层管理 里绑定层 **BeautifulSoup**。 -5、测试,没有错误就在 EUserv_extend⇨触发管理⇨创建触发器触发周期⇨自定义触发周期 填入 +7、测试,没有错误就在 EUserv_extend ⇨ 触发管理 ⇨ 创建触发器触发周期 ⇨ 自定义触发周期 填入 ``` 0 0 8 */7 * * * # 每 7 天的 8 点执行,修改成你想要的时间。Cron 相关文档: https://cloud.tencent.com/document/product/583/9708 ``` -6、完成。 + +8、完成。 + ## 其他说明 -本项目直接修改自 [CokeMine/EUserv_extend](https://github.com/CokeMine/EUserv_extend) 以适用于腾讯云函数。 +本项目直接修改自 [CokeMine/EUserv_extend](https://github.com/CokeMine/EUserv_extend) 与 [ZetaoYang/main.py](https://gist.github.com/ZetaoYang/e182453efadc90739a14daf2bd829087) 以适用于腾讯云函数。 diff --git a/main.py b/main.py index 4bd9b42..eccbcda 100644 --- a/main.py +++ b/main.py @@ -3,13 +3,22 @@ import json import time import requests +import base64 from bs4 import BeautifulSoup + # 强烈建议部署在非大陆区域,例如HK、SG等 # 常量命名使用全部大写的方式,可以使用下划线。 USERNAME = '' # 这里填用户名,邮箱也可 PASSWORD = '' # 这里填密码 +# TrueCaptcha https://apitruecaptcha.org +# https://gist.github.com/ZetaoYang/e182453efadc90739a14daf2bd829087 +# 验证码识别,默认使用 Demo API,每天有100次免费额度,建议自行注册以确保稳定性 +TRUECAPTCHA_USERID = 'arun56' +TRUECAPTCHA_APIKEY = 'wMjXmBIcHcdYqO2RrsVN' +TRUECAPTCHA_CHECK_USAGE = True + # Server酱 http://sc.ftqq.com/?c=code SCKEY = '' # 这里填Server酱的key,无需推送可不填 示例: SCU646xxxxxxxxdacd6a5dc3f6 @@ -48,6 +57,7 @@ def login(username, password) -> (str, requests.session): "origin": "https://www.euserv.com", } url = "https://support.euserv.com/index.iphp" + captcha_image_url = "https://support.euserv.com/securimage_show.php" session = requests.Session() sess = session.get(url, headers=headers) @@ -67,9 +77,51 @@ def login(username, password) -> (str, requests.session): f = session.post(url, headers=headers, data=login_data) f.raise_for_status() - if f.text.find('Hello') == -1: - return '-1', session - return sess_id, session + if ( + f.text.find("Hello") == -1 + and f.text.find("Confirm or change your customer data here") == -1 + ): + if ( + f.text.find( + "To finish the login process please solve the following captcha." + ) + == -1 + ): + return "-1", session + else: + print_("[Captcha Solver] 进行验证码识别...") + solved_result = captcha_solver(captcha_image_url, session) + captcha_code = handle_captcha_solved_result(solved_result) + print_("[Captcha Solver] 识别的验证码是: {}".format(captcha_code)) + if TRUECAPTCHA_CHECK_USAGE: + usage = get_captcha_solver_usage() + print_( + "[Captcha Solver] current date {0} api usage count: {1}".format( + usage[0]["date"], usage[0]["count"] + ) + ) + f2 = session.post( + url, + headers=headers, + data={ + "subaction": "login", + "sess_id": sess_id, + "captcha_code": captcha_code, + }, + ) + if ( + f2.text.find( + "To finish the login process please solve the following captcha." + ) + == -1 + ): + print_("[Captcha Solver] 验证通过") + return sess_id, session + else: + print_("[Captcha Solver] 验证失败") + return "-1", session + else: + return sess_id, session def get_servers(sess_id: str, session: requests.session) -> {}: @@ -87,8 +139,14 @@ def get_servers(sess_id: str, session: requests.session) -> {}: server_id = tr.select('.td-z1-sp1-kc') if not len(server_id) == 1: continue - flag = True if tr.select('.td-z1-sp2-kc .kc2_order_action_container')[ - 0].get_text().find('Contract extension possible from') == -1 else False + flag = ( + True + if tr.select(".td-z1-sp2-kc .kc2_order_action_container")[0] + .get_text() + .find("Contract extension possible from") + == -1 + else False + ) d[server_id[0].get_text()] = flag return d @@ -142,6 +200,71 @@ def check(sess_id: str, session: requests.session): print_("ServerID: %s Renew Failed!" % key) if flag: print_("ALL Work Done! Enjoy") + + +# TrueCaptcha https://apitruecaptcha.org +def captcha_solver(captcha_image_url: str, session: requests.session) -> dict: + response = session.get(captcha_image_url) + encoded_string = base64.b64encode(response.content) + url = "https://api.apitruecaptcha.org/one/gettext" + + data = { + "userid": TRUECAPTCHA_USERID, + "apikey": TRUECAPTCHA_APIKEY, + "case": "mixed", + "mode": "human", + "data": str(encoded_string)[2:-1], + } + r = requests.post(url=url, json=data) + j = json.loads(r.text) + return j + +def handle_captcha_solved_result(solved: dict) -> str: + if "result" in solved: + solved_text = solved["result"] + if "RESULT IS" in solved_text: + print_("[Captcha Solver] You are using the demo apikey.") + print("There is no guarantee that demo apikey will work in the future!") + # because using demo apikey + text = re.findall(r"RESULT IS . (.*) .", solved_text)[0] + else: + # using your own apikey + print_("[Captcha Solver] You are using your own apikey.") + text = solved_text + operators = ["X", "x", "+", "-"] + if any(x in text for x in operators): + for operator in operators: + operator_pos = text.find(operator) + if operator == "x" or operator == "X": + operator = "*" + if operator_pos != -1: + left_part = text[:operator_pos] + right_part = text[operator_pos + 1 :] + if left_part.isdigit() and right_part.isdigit(): + return eval( + "{left} {operator} {right}".format( + left=left_part, operator=operator, right=right_part + ) + ) + else: + # Because these symbols("X", "x", "+", "-") do not appear at the same time, + # it just contains an arithmetic symbol. + return text + else: + return text + else: + print(solved) + raise KeyError("Failed to find parsed results.") + +def get_captcha_solver_usage() -> dict: + url = "https://api.apitruecaptcha.org/one/getusage" + params = { + "username": TRUECAPTCHA_USERID, + "apikey": TRUECAPTCHA_APIKEY, + } + r = requests.get(url=url, params=params) + j = json.loads(r.text) + return j # Server酱 http://sc.ftqq.com/?c=code