Skip to content

Commit c712fe9

Browse files
authored
完善文本
1 parent cb88ba9 commit c712fe9

File tree

1 file changed

+75
-18
lines changed

1 file changed

+75
-18
lines changed

renew.user.js

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// ==UserScript==
22
// @name Extend VPS Expiration
3+
// @name:zh-CN Xserver VPS 自动续期脚本
34
// @namespace http://tampermonkey.net/
4-
// @version 2025-07-17
5-
// @description Auto Renew Free VPS Expiration
5+
// @version 2025-07-21
6+
// @description Automatically renews the expiration date of free Xserver VPS.
7+
// @description:zh-CN 自动为 Xserver 的免费 VPS 续期。
68
// @author You
79
// @match https://secure.xserver.ne.jp/xapanel*/xvps*
810
// @icon https://www.google.com/s2/favicons?sz=64&domain=xserver.ne.jp
@@ -13,24 +15,62 @@
1315
// @supportURL https://github.com/GitHub30/extend-vps-exp
1416
// ==/UserScript==
1517

18+
/*
19+
* =================================================================================================
20+
* 使用说明 (Usage Instructions)
21+
* =================================================================================================
22+
* 1. 请将登录页面设为浏览器书签: https://secure.xserver.ne.jp/xapanel/login/xvps/
23+
* (Bookmark the login page)
24+
*
25+
* 2. 每天访问一次该书签。
26+
* (Visit the bookmark once every day.)
27+
*
28+
* 3. (可选) 首次访问时,在登录页面输入您的邮箱和密码,脚本会自动保存。之后访问将自动填充和登录。
29+
* (Optional) On your first visit, enter your email and password on the login page.
30+
* The script will save them automatically for future auto-login.
31+
*
32+
* =================================================================================================
33+
* 工作流程 (Workflow)
34+
* =================================================================================================
35+
* 1. 登录页面: 自动填充已保存的凭据并提交。
36+
* (Login Page: Auto-fills saved credentials and submits.)
37+
*
38+
* 2. VPS管理主页: 检查免费VPS的到期日期。如果明天到期,则跳转到续期页面。
39+
* (VPS Dashboard: Checks the expiration date. If it expires tomorrow, it navigates to the renewal page.)
40+
*
41+
* 3. 续期申请页: 自动点击“确认”按钮,进入验证码页面。
42+
* (Renewal Page: Clicks the confirmation button to proceed to the CAPTCHA page.)
43+
*
44+
* 4. 验证码页:
45+
* a. 提取验证码图片。
46+
* b. 发送到外部API服务进行识别。
47+
* c. 自动填充识别结果。
48+
* d. 监听 Cloudflare Turnstile (一种人机验证) 的令牌生成,一旦生成,立即提交表单。
49+
* (CAPTCHA Page: Extracts the CAPTCHA image, sends it to a recognition service,
50+
* fills the result, and submits the form once the Cloudflare Turnstile token is ready.)
51+
* =================================================================================================
52+
*/
53+
1654
(function() {
1755
'use strict';
1856

19-
// Usage:
20-
// 1. Please bookmark https://secure.xserver.ne.jp/xapanel/login/xvps/
21-
// 2. Open the URL everyday
22-
// 3. (option) Save email and password on Login page
23-
24-
// Login
57+
/**
58+
* @description 登录页面逻辑:自动填充并保存用户凭据。
59+
*/
2560
if (location.pathname.startsWith('/xapanel/login/xvps')) {
2661
const memberid = GM_getValue('memberid');
2762
const user_password = GM_getValue('user_password');
63+
64+
// 如果存在已保存的凭据且页面没有显示错误消息,则自动填充并登录
2865
if (memberid && user_password && !document.querySelector('.errorMessage')) {
2966
unsafeWindow.memberid.value = memberid;
3067
unsafeWindow.user_password.value = user_password;
68+
// 调用页面自带的登录函数
3169
unsafeWindow.loginFunc();
3270
}
33-
// eslint-disable-next-line no-undef
71+
72+
// 监听登录表单的提交事件,以便保存用户输入的凭据
73+
// 确保 jQuery 已加载
3474
if (typeof $ !== 'undefined') {
3575
$('#login_area').on('submit', () => {
3676
GM_setValue('memberid', unsafeWindow.memberid.value);
@@ -39,52 +79,69 @@
3979
}
4080
}
4181

42-
// Check expiration date
82+
/**
83+
* @description VPS 管理主页逻辑:检查到期时间并跳转。
84+
*/
4385
if (location.pathname.startsWith('/xapanel/xvps/index')) {
44-
const tomorrow = new Date(Date.now() + 864e5).toLocaleDateString('sv');
86+
// 计算明天的日期,格式为 YYYY-MM-DD (瑞典时区格式)
87+
const tomorrow = new Date(Date.now() + 86400000).toLocaleDateString('sv');
4588
const expireDate = document.querySelector('tr:has(.freeServerIco) .contract__term')?.textContent;
89+
90+
// 如果到期日是明天,则准备续期
4691
if (expireDate === tomorrow) {
4792
const href = document.querySelector('tr:has(.freeServerIco) a[href^="/xapanel/xvps/server/detail?id="]').href;
93+
// 跳转到续期页面
4894
location.href = href.replace('detail?id', 'freevps/extend/index?id_vps');
4995
}
5096
}
5197

52-
// Extend expiration date
98+
/**
99+
* @description 续期申请页面逻辑:自动点击确认按钮。
100+
*/
53101
if (location.pathname.startsWith('/xapanel/xvps/server/freevps/extend/index')) {
54102
const extendButton = document.querySelector('[formaction="/xapanel/xvps/server/freevps/extend/conf"]');
55103
if (extendButton) {
56104
extendButton.click();
57105
}
58106
}
59107

60-
// Solve CAPTCHA
61-
// This part is wrapped in an async IIFE to use 'await'
108+
/**
109+
* @description 验证码页面逻辑:识别并提交验证码。
110+
* 使用 IIFE (立即调用的函数表达式) 来创建异步上下文,以便使用 await。
111+
*/
62112
if ((location.pathname.startsWith('/xapanel/xvps/server/freevps/extend/conf') || location.pathname.startsWith('/xapanel/xvps/server/freevps/extend/do')) && unsafeWindow.submit_button) {
63113
(async function() {
64114
try {
65115
const img = document.querySelector('img[src^="data:"]');
66-
if (!img) return;
116+
if (!img) {
117+
console.log('CAPTCHA image not found.');
118+
return;
119+
}
67120

68121
const body = img.src;
69-
// Use await to fetch the CAPTCHA solution
122+
// 调用外部API来识别验证码图片
70123
const code = await fetch('https://captcha-120546510085.asia-northeast1.run.app', { method: 'POST', body }).then(r => r.text());
71124

72125
const input = document.querySelector('[placeholder="上の画像の数字を入力"]');
73126
if (input) {
74127
input.value = code;
75128
}
76129

130+
// 处理 Cloudflare Turnstile 人机验证
77131
const cf = document.querySelector('.cf-turnstile [name=cf-turnstile-response]');
78132
if (cf) {
133+
// 如果令牌已经存在,直接点击提交
79134
if (cf.value) {
80135
unsafeWindow.submit_button.click();
136+
return;
81137
}
82-
// Observe for the value change and then click
138+
// 如果令牌尚不存在,则使用 MutationObserver 监听其值的变化
139+
// 一旦 cf-turnstile-response 的 value 被填充,就立即点击提交按钮
83140
new MutationObserver((mutationsList, observer) => {
84141
for(const mutation of mutationsList) {
85142
if (mutation.type === 'attributes' && mutation.attributeName === 'value' && cf.value) {
86143
unsafeWindow.submit_button.click();
87-
observer.disconnect(); // Stop observing once clicked
144+
observer.disconnect(); // 任务完成,停止监听
88145
}
89146
}
90147
}).observe(cf, { attributes: true, attributeFilter: ['value'] });

0 commit comments

Comments
 (0)