feat(flow): unify sms multi-provider fallback and stabilize proxy/auth retries
This commit is contained in:
@@ -13,7 +13,10 @@ function normalizeIpProxyService(value = '') {
|
||||
const ipProxyActionState = {
|
||||
busy: false,
|
||||
action: '',
|
||||
startedAt: 0,
|
||||
};
|
||||
const IP_PROXY_ACTION_LOCK_TIMEOUT_MS = 25000;
|
||||
let ipProxyDeferredProbeTimer = 0;
|
||||
const IP_PROXY_SECTION_EXPANDED_STORAGE_KEY = 'multipage-ip-proxy-section-expanded';
|
||||
let ipProxySectionExpanded = false;
|
||||
|
||||
@@ -78,12 +81,14 @@ function getIpProxyActionState() {
|
||||
return {
|
||||
busy: Boolean(ipProxyActionState.busy),
|
||||
action: normalizeIpProxyActionType(ipProxyActionState.action),
|
||||
startedAt: Number(ipProxyActionState.startedAt) || 0,
|
||||
};
|
||||
}
|
||||
|
||||
function setIpProxyActionBusy(action = '', busy = false) {
|
||||
ipProxyActionState.busy = Boolean(busy);
|
||||
ipProxyActionState.action = ipProxyActionState.busy ? normalizeIpProxyActionType(action) : '';
|
||||
ipProxyActionState.startedAt = ipProxyActionState.busy ? Date.now() : 0;
|
||||
}
|
||||
|
||||
async function runIpProxyActionWithLock(action = '', runner) {
|
||||
@@ -104,10 +109,23 @@ async function runIpProxyActionWithLock(action = '', runner) {
|
||||
updateIpProxyUI(latestState);
|
||||
}
|
||||
|
||||
const actionLabel = getIpProxyActionLabel(nextAction);
|
||||
const timeoutMs = Math.max(5000, Number(IP_PROXY_ACTION_LOCK_TIMEOUT_MS) || 25000);
|
||||
let timeoutId = 0;
|
||||
try {
|
||||
const value = await runner();
|
||||
const value = await Promise.race([
|
||||
Promise.resolve().then(() => runner()),
|
||||
new Promise((_, reject) => {
|
||||
timeoutId = globalThis.setTimeout(() => {
|
||||
reject(new Error(`${actionLabel}超时(${Math.round(timeoutMs / 1000)} 秒),已自动解锁,请重试。`));
|
||||
}, timeoutMs);
|
||||
}),
|
||||
]);
|
||||
return { skipped: false, value };
|
||||
} finally {
|
||||
if (timeoutId) {
|
||||
globalThis.clearTimeout(timeoutId);
|
||||
}
|
||||
setIpProxyActionBusy(nextAction, false);
|
||||
if (typeof updateIpProxyUI === 'function') {
|
||||
updateIpProxyUI(latestState);
|
||||
@@ -115,6 +133,24 @@ async function runIpProxyActionWithLock(action = '', runner) {
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleIpProxyExitProbe(options = {}) {
|
||||
const {
|
||||
silent = true,
|
||||
delayMs = 80,
|
||||
} = options;
|
||||
const delay = Math.max(0, Number(delayMs) || 0);
|
||||
if (ipProxyDeferredProbeTimer) {
|
||||
globalThis.clearTimeout(ipProxyDeferredProbeTimer);
|
||||
ipProxyDeferredProbeTimer = 0;
|
||||
}
|
||||
ipProxyDeferredProbeTimer = globalThis.setTimeout(() => {
|
||||
ipProxyDeferredProbeTimer = 0;
|
||||
Promise.resolve()
|
||||
.then(() => probeIpProxyExit({ silent }))
|
||||
.catch(() => {});
|
||||
}, delay);
|
||||
}
|
||||
|
||||
function normalizeIpProxyMode(value = '') {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return SUPPORTED_IP_PROXY_MODES.includes(normalized) ? normalized : DEFAULT_IP_PROXY_MODE;
|
||||
@@ -210,6 +246,8 @@ function normalizeIpProxyServiceProfile(rawValue = {}) {
|
||||
accountSessionPrefix: normalizeIpProxyAccountSessionPrefix(raw.accountSessionPrefix || ''),
|
||||
accountLifeMinutes: normalizeIpProxyAccountLifeMinutes(raw.accountLifeMinutes || ''),
|
||||
poolTargetCount: normalizeIpProxyPoolTargetCount(raw.poolTargetCount || '', 20),
|
||||
autoSyncEnabled: Boolean(raw.autoSyncEnabled),
|
||||
autoSyncIntervalMinutes: String(Math.max(1, Math.min(1440, Number.parseInt(String(raw.autoSyncIntervalMinutes ?? '').trim(), 10) || 15))),
|
||||
host: String(raw.host || '').trim(),
|
||||
port: String(normalizeIpProxyPort(raw.port || '') || ''),
|
||||
protocol: normalizeIpProxyProtocol(raw.protocol),
|
||||
@@ -227,6 +265,8 @@ function buildIpProxyServiceProfileFromFlatState(state = {}) {
|
||||
accountSessionPrefix: state?.ipProxyAccountSessionPrefix,
|
||||
accountLifeMinutes: state?.ipProxyAccountLifeMinutes,
|
||||
poolTargetCount: state?.ipProxyPoolTargetCount,
|
||||
autoSyncEnabled: state?.ipProxyAutoSyncEnabled,
|
||||
autoSyncIntervalMinutes: state?.ipProxyAutoSyncIntervalMinutes,
|
||||
host: state?.ipProxyHost,
|
||||
port: state?.ipProxyPort,
|
||||
protocol: state?.ipProxyProtocol,
|
||||
@@ -387,6 +427,8 @@ function buildCurrentIpProxyServiceProfileFromInputs() {
|
||||
accountSessionPrefix: inputIpProxyAccountSessionPrefix?.value || '',
|
||||
accountLifeMinutes: inputIpProxyAccountLifeMinutes?.value || '',
|
||||
poolTargetCount: inputIpProxyPoolTargetCount?.value || '',
|
||||
autoSyncEnabled: Boolean(inputIpProxyAutoSyncEnabled?.checked),
|
||||
autoSyncIntervalMinutes: inputIpProxyAutoSyncIntervalMinutes?.value || '',
|
||||
host: inputIpProxyHost?.value || '',
|
||||
port: inputIpProxyPort?.value || '',
|
||||
protocol: selectIpProxyProtocol?.value || '',
|
||||
@@ -419,6 +461,8 @@ function buildIpProxyStatePatchFromServiceProfile(service = '', profile = {}) {
|
||||
ipProxyAccountSessionPrefix: normalizedProfile.accountSessionPrefix,
|
||||
ipProxyAccountLifeMinutes: normalizedProfile.accountLifeMinutes,
|
||||
ipProxyPoolTargetCount: normalizedProfile.poolTargetCount,
|
||||
ipProxyAutoSyncEnabled: normalizedProfile.autoSyncEnabled,
|
||||
ipProxyAutoSyncIntervalMinutes: Number.parseInt(String(normalizedProfile.autoSyncIntervalMinutes || '15').trim(), 10) || 15,
|
||||
ipProxyHost: normalizedProfile.host,
|
||||
ipProxyPort: normalizedProfile.port,
|
||||
ipProxyProtocol: normalizedProfile.protocol,
|
||||
@@ -449,6 +493,12 @@ function applyIpProxyServiceProfileToInputs(profile = {}, options = {}) {
|
||||
if (inputIpProxyPoolTargetCount) {
|
||||
inputIpProxyPoolTargetCount.value = normalizedProfile.poolTargetCount;
|
||||
}
|
||||
if (inputIpProxyAutoSyncEnabled) {
|
||||
inputIpProxyAutoSyncEnabled.checked = Boolean(normalizedProfile.autoSyncEnabled);
|
||||
}
|
||||
if (inputIpProxyAutoSyncIntervalMinutes) {
|
||||
inputIpProxyAutoSyncIntervalMinutes.value = String(normalizedProfile.autoSyncIntervalMinutes || '15');
|
||||
}
|
||||
if (inputIpProxyHost) {
|
||||
inputIpProxyHost.value = normalizedProfile.host;
|
||||
}
|
||||
@@ -1058,6 +1108,7 @@ function formatIpProxyRuntimeStatus(state = latestState) {
|
||||
stateClass: 'state-idle',
|
||||
text: '未启用,沿用浏览器默认/全局代理。',
|
||||
details: '',
|
||||
hideCurrentDisplay: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1073,6 +1124,7 @@ function formatIpProxyRuntimeStatus(state = latestState) {
|
||||
stateClass: warningText ? 'state-warning' : 'state-applied',
|
||||
text: `${statusText}${briefWarning}`,
|
||||
details,
|
||||
hideCurrentDisplay: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1082,6 +1134,7 @@ function formatIpProxyRuntimeStatus(state = latestState) {
|
||||
stateClass: 'state-warning',
|
||||
text: '已启用,但当前没有可用代理(已阻断直连)。',
|
||||
details: errorText,
|
||||
hideCurrentDisplay: false,
|
||||
};
|
||||
}
|
||||
return {
|
||||
@@ -1090,6 +1143,7 @@ function formatIpProxyRuntimeStatus(state = latestState) {
|
||||
? '已启用,但账号模式没有可用代理。请先填写代理列表,或填写 Host/Port。已阻断所有网站直连。'
|
||||
: '已启用,但当前没有可用代理。请先点击“拉取”获取 IP 列表。已阻断所有网站直连。',
|
||||
details: '',
|
||||
hideCurrentDisplay: false,
|
||||
};
|
||||
}
|
||||
if (reason === 'proxy_api_unavailable') {
|
||||
@@ -1097,6 +1151,7 @@ function formatIpProxyRuntimeStatus(state = latestState) {
|
||||
stateClass: 'state-error',
|
||||
text: '已启用,但当前浏览器不支持扩展代理 API,无法应用。',
|
||||
details,
|
||||
hideCurrentDisplay: false,
|
||||
};
|
||||
}
|
||||
if (reason === 'apply_failed') {
|
||||
@@ -1104,6 +1159,7 @@ function formatIpProxyRuntimeStatus(state = latestState) {
|
||||
stateClass: 'state-error',
|
||||
text: '已启用,但代理应用失败(已回退默认代理)。',
|
||||
details: errorText || details,
|
||||
hideCurrentDisplay: false,
|
||||
};
|
||||
}
|
||||
if (reason === 'connectivity_failed') {
|
||||
@@ -1112,6 +1168,7 @@ function formatIpProxyRuntimeStatus(state = latestState) {
|
||||
stateClass: 'state-error',
|
||||
text: `${prefix};连通性失败,请切换节点或重试。`,
|
||||
details: errorText || details,
|
||||
hideCurrentDisplay: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1121,6 +1178,7 @@ function formatIpProxyRuntimeStatus(state = latestState) {
|
||||
? `已启用,等待生效:${endpointWithRegion}${authSuffix}`
|
||||
: '已启用,等待拉取并应用代理。',
|
||||
details,
|
||||
hideCurrentDisplay: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1137,6 +1195,10 @@ function setIpProxyRuntimeStatusDisplay(status = {}) {
|
||||
if (ipProxyRuntimeDot) {
|
||||
ipProxyRuntimeDot.title = text;
|
||||
}
|
||||
const runtimeMeta = ipProxyCurrent?.closest?.('.ip-proxy-runtime-meta') || null;
|
||||
if (runtimeMeta) {
|
||||
runtimeMeta.style.display = status?.hideCurrentDisplay ? 'none' : '';
|
||||
}
|
||||
const detailsText = String(status?.details || '').trim();
|
||||
if (ipProxyRuntimeDetails && ipProxyRuntimeDetailsText) {
|
||||
if (detailsText) {
|
||||
@@ -1241,6 +1303,12 @@ function updateIpProxyUI(state = latestState) {
|
||||
if (rowIpProxyPoolTargetCount) {
|
||||
rowIpProxyPoolTargetCount.style.display = showSettings ? '' : 'none';
|
||||
}
|
||||
if (rowIpProxyAutoSyncEnabled) {
|
||||
rowIpProxyAutoSyncEnabled.style.display = showSettings ? '' : 'none';
|
||||
}
|
||||
if (rowIpProxyAutoSyncInterval) {
|
||||
rowIpProxyAutoSyncInterval.style.display = showSettings ? '' : 'none';
|
||||
}
|
||||
if (rowIpProxyHost) {
|
||||
rowIpProxyHost.style.display = showSettings && isAccountMode ? '' : 'none';
|
||||
}
|
||||
@@ -1334,6 +1402,16 @@ function updateIpProxyUI(state = latestState) {
|
||||
if (inputIpProxyAccountList) {
|
||||
inputIpProxyAccountList.disabled = !enabled || !isAccountMode || !accountListAvailable;
|
||||
}
|
||||
if (inputIpProxyAutoSyncEnabled) {
|
||||
inputIpProxyAutoSyncEnabled.disabled = !enabled;
|
||||
}
|
||||
if (inputIpProxyAutoSyncIntervalMinutes) {
|
||||
const autoSyncEnabled = Boolean(inputIpProxyAutoSyncEnabled?.checked);
|
||||
if (!Number.isFinite(Number.parseInt(String(inputIpProxyAutoSyncIntervalMinutes.value || '').trim(), 10))) {
|
||||
inputIpProxyAutoSyncIntervalMinutes.value = '15';
|
||||
}
|
||||
inputIpProxyAutoSyncIntervalMinutes.disabled = !enabled || !autoSyncEnabled;
|
||||
}
|
||||
|
||||
const runtimeStatus = formatIpProxyRuntimeStatus(runtimeState);
|
||||
setIpProxyRuntimeStatusDisplay(runtimeStatus);
|
||||
@@ -1395,6 +1473,7 @@ async function refreshIpProxyPoolByApi(options = {}) {
|
||||
source: 'sidepanel',
|
||||
payload: {
|
||||
mode,
|
||||
skipExitProbe: true,
|
||||
},
|
||||
});
|
||||
if (response?.error) {
|
||||
@@ -1425,7 +1504,7 @@ async function refreshIpProxyPoolByApi(options = {}) {
|
||||
syncLatestState(patch);
|
||||
}
|
||||
updateIpProxyUI(latestState);
|
||||
await probeIpProxyExit({ silent: true }).catch(() => {});
|
||||
scheduleIpProxyExitProbe({ silent: true });
|
||||
|
||||
if (!silent) {
|
||||
if (mode === 'account') {
|
||||
@@ -1447,6 +1526,7 @@ async function switchIpProxyToNext(options = {}) {
|
||||
direction: 'next',
|
||||
mode,
|
||||
forceRefresh: false,
|
||||
skipExitProbe: true,
|
||||
},
|
||||
});
|
||||
if (response?.error) {
|
||||
@@ -1476,7 +1556,7 @@ async function switchIpProxyToNext(options = {}) {
|
||||
syncLatestState(patch);
|
||||
}
|
||||
updateIpProxyUI(latestState);
|
||||
await probeIpProxyExit({ silent: true }).catch(() => {});
|
||||
scheduleIpProxyExitProbe({ silent: true });
|
||||
if (!silent) {
|
||||
showToast(`已切换代理:${response?.display || formatIpProxyCurrentDisplay(latestState).text}`, 'success', 1800);
|
||||
}
|
||||
@@ -1491,6 +1571,7 @@ async function changeIpProxyExitBySession(options = {}) {
|
||||
source: 'sidepanel',
|
||||
payload: {
|
||||
mode,
|
||||
skipExitProbe: true,
|
||||
},
|
||||
});
|
||||
if (response?.error) {
|
||||
@@ -1520,7 +1601,7 @@ async function changeIpProxyExitBySession(options = {}) {
|
||||
syncLatestState(patch);
|
||||
}
|
||||
updateIpProxyUI(latestState);
|
||||
await probeIpProxyExit({ silent: true }).catch(() => {});
|
||||
scheduleIpProxyExitProbe({ silent: true });
|
||||
if (!silent) {
|
||||
showToast(`已执行 Change:${response?.display || formatIpProxyCurrentDisplay(latestState).text}`, 'success', 1800);
|
||||
}
|
||||
|
||||
@@ -1161,6 +1161,59 @@ header {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.country-order-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-base);
|
||||
color: var(--text-primary);
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.country-order-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.country-order-chip-label {
|
||||
max-width: 240px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.country-order-remove {
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 999px;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.country-order-remove:hover {
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-hover);
|
||||
}
|
||||
|
||||
.country-order-separator {
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.data-value.has-value { color: var(--text-primary); }
|
||||
|
||||
.mono {
|
||||
@@ -2114,6 +2167,14 @@ header {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.hero-sms-runtime-select {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
height: 33px;
|
||||
min-height: 33px;
|
||||
}
|
||||
|
||||
.hero-sms-price-preview-stack {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
+479
-325
@@ -420,331 +420,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="phone-verification-section" class="data-card phone-verification-card">
|
||||
<div class="section-mini-header">
|
||||
<div class="section-mini-copy">
|
||||
<span class="section-label">接码设置</span>
|
||||
<span class="data-value">手机号验证与 HeroSMS 获取策略</span>
|
||||
</div>
|
||||
<div id="row-phone-verification-enabled" class="section-mini-actions phone-verification-header-actions">
|
||||
<button id="btn-toggle-phone-verification-section" class="btn btn-ghost btn-xs" type="button"
|
||||
aria-expanded="false" aria-controls="row-phone-verification-fold">展开设置</button>
|
||||
<label class="toggle-switch" for="input-phone-verification-enabled" title="启用或禁用手机号接码流程">
|
||||
<input type="checkbox" id="input-phone-verification-enabled" />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row phone-verification-fold-row" id="row-phone-verification-fold" style="display:none;">
|
||||
<div id="phone-verification-fold" class="phone-verification-fold">
|
||||
<div class="phone-verification-fold-body">
|
||||
<div class="data-row" id="row-account-run-history-helper-base-url" style="display:none;">
|
||||
<span class="data-label">同步服务</span>
|
||||
<input type="text" id="input-account-run-history-helper-base-url" class="data-input mono"
|
||||
placeholder="http://127.0.0.1:17373" />
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-platform" style="display:none;">
|
||||
<span class="data-label">接码平台</span>
|
||||
<span id="display-hero-sms-platform" class="data-value mono">HeroSMS / OpenAI / Thailand</span>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-country" style="display:none;">
|
||||
<span class="data-label">国家优先级</span>
|
||||
<div class="data-inline hero-sms-country-stack">
|
||||
<select id="select-hero-sms-country" class="data-input mono" multiple size="6" style="display:none;">
|
||||
<option value="52" selected>Thailand</option>
|
||||
</select>
|
||||
<div class="hero-sms-country-mainline">
|
||||
<div id="hero-sms-country-menu-shell" class="hero-sms-country-menu">
|
||||
<button id="btn-hero-sms-country-menu" class="btn btn-outline btn-sm hero-sms-country-menu-btn" type="button" aria-haspopup="listbox" aria-expanded="false">
|
||||
Thailand (1/3)
|
||||
</button>
|
||||
<div id="hero-sms-country-menu" class="hero-sms-country-menu-dropdown" role="listbox" aria-multiselectable="true" hidden></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="data-value hero-sms-country-note">多选最多 3 个,按点击顺序生效。</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-country-fallback" style="display:none;">
|
||||
<span class="data-label">生效顺序</span>
|
||||
<div class="data-inline data-value-actions">
|
||||
<span id="display-hero-sms-country-fallback-order" class="data-value data-value-fill mono">Thailand(52)</span>
|
||||
<button id="btn-hero-sms-country-clear" class="btn btn-ghost btn-xs data-inline-btn" type="button">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-acquire-priority" style="display:none;">
|
||||
<span class="data-label">拿号优先级</span>
|
||||
<select id="select-hero-sms-acquire-priority" class="data-input mono">
|
||||
<option value="country">国家优先(默认)</option>
|
||||
<option value="price">价格优先(同价按国家顺序)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-api-key" style="display:none;">
|
||||
<span class="data-label">接码 API</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-hero-sms-api-key" class="data-input data-input-with-icon mono"
|
||||
placeholder="请输入 HeroSMS API Key" />
|
||||
<button id="btn-toggle-hero-sms-api-key" class="input-icon-btn" type="button"
|
||||
data-password-toggle="input-hero-sms-api-key" data-show-label="显示 HeroSMS API Key"
|
||||
data-hide-label="隐藏 HeroSMS API Key" aria-label="显示 HeroSMS API Key" title="显示 HeroSMS API Key"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-max-price" style="display:none;">
|
||||
<span class="data-label">价格</span>
|
||||
<div class="data-inline hero-sms-price-preview-stack">
|
||||
<div class="hero-sms-price-preview-head">
|
||||
<button id="btn-hero-sms-price-preview" class="btn btn-outline btn-xs data-inline-btn" type="button">查询价格</button>
|
||||
</div>
|
||||
<div id="row-hero-sms-price-tiers" class="hero-sms-price-preview-result" style="display:none;">
|
||||
<span id="display-hero-sms-price-tiers" class="data-value mono hero-sms-price-preview-text">未获取</span>
|
||||
</div>
|
||||
<div class="hero-sms-price-controls-grid">
|
||||
<div class="hero-sms-price-control">
|
||||
<span class="hero-sms-settings-caption">价格上限</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-hero-sms-max-price" class="data-input auto-delay-input mono hero-sms-max-price-input" placeholder="0.12" min="0" step="0.0001" title="接码价格上限;可空(空=自动价格)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-sms-price-control hero-sms-price-control-reuse">
|
||||
<span class="hero-sms-settings-caption">号码复用</span>
|
||||
<div class="setting-controls hero-sms-toggle-controls">
|
||||
<label class="toggle-switch hero-sms-price-reuse-toggle" for="input-hero-sms-reuse-enabled" title="开启后会优先复用未超次数的可用号码">
|
||||
<input type="checkbox" id="input-hero-sms-reuse-enabled" />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-phone-code-settings-group" style="display:none;">
|
||||
<span class="data-label">接码参数</span>
|
||||
<div class="data-inline hero-sms-settings-grid">
|
||||
<div id="row-phone-verification-resend-count" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">验证码重发</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-verification-resend-count" class="data-input auto-delay-input" value="4"
|
||||
min="0" max="20" step="1" title="自动点击重新发送验证码的次数" />
|
||||
<span class="data-unit">次</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="row-phone-replacement-limit" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">换号上限</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-phone-replacement-limit" class="data-input auto-delay-input" value="3" min="1" max="20" step="1" title="步骤 9 内部允许更换号码的最大次数" />
|
||||
<span class="data-unit">次</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="row-phone-code-wait-seconds" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">验证码限时</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-phone-code-wait-seconds" class="data-input auto-delay-input" value="60" min="15" max="300" step="1" title="每轮等待验证码的秒数" />
|
||||
<span class="data-unit">秒</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="row-phone-code-timeout-windows" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">超时次数</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-phone-code-timeout-windows" class="data-input auto-delay-input" value="2" min="1" max="10" step="1" title="验证码超时后,最多继续等待几轮再换号" />
|
||||
<span class="data-unit">轮</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="row-phone-code-poll-interval-seconds" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">轮询间隔</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-phone-code-poll-interval-seconds" class="data-input auto-delay-input" value="5" min="1" max="30" step="1" title="向 HeroSMS 查询验证码状态的间隔秒数" />
|
||||
<span class="data-unit">秒</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="row-phone-code-poll-max-rounds" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">轮询次数</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-phone-code-poll-max-rounds" class="data-input auto-delay-input" value="4" min="1" max="120" step="1" title="每轮验证码等待窗口最多轮询次数" />
|
||||
<span class="data-unit">次</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-runtime-pair" style="display:none;">
|
||||
<span class="data-label">运行状态</span>
|
||||
<div class="data-inline hero-sms-runtime-grid">
|
||||
<div id="row-hero-sms-current-number" class="hero-sms-runtime-cell" style="display:none;">
|
||||
<span class="hero-sms-runtime-key">当前分配</span>
|
||||
<span id="display-hero-sms-current-number" class="data-value mono hero-sms-runtime-value">未分配</span>
|
||||
</div>
|
||||
<div id="row-hero-sms-current-code" class="hero-sms-runtime-cell" style="display:none;">
|
||||
<span class="hero-sms-runtime-key">验证码</span>
|
||||
<span id="display-hero-sms-current-code" class="data-value mono hero-sms-runtime-value">未获取</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ip-proxy-section" class="data-card ip-proxy-card">
|
||||
<div class="section-mini-header">
|
||||
<div class="section-mini-copy">
|
||||
<span class="section-label">IP 代理</span>
|
||||
<span class="data-value">用于浏览器代理接管与出口切换</span>
|
||||
</div>
|
||||
<div id="row-ip-proxy-enabled" class="section-mini-actions ip-proxy-header-actions">
|
||||
<button id="btn-toggle-ip-proxy-section" class="btn btn-ghost btn-xs" type="button"
|
||||
aria-expanded="false" aria-controls="row-ip-proxy-fold">展开设置</button>
|
||||
<label class="toggle-switch" for="input-ip-proxy-enabled" title="启用或禁用 IP 代理接管">
|
||||
<input type="checkbox" id="input-ip-proxy-enabled" />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-fold-row" id="row-ip-proxy-fold" style="display:none;">
|
||||
<div id="ip-proxy-fold" class="ip-proxy-fold">
|
||||
<div class="ip-proxy-fold-body">
|
||||
<div class="data-row" id="row-ip-proxy-service" style="display:none;">
|
||||
<span class="data-label">代理服务</span>
|
||||
<div class="data-inline">
|
||||
<select id="select-ip-proxy-service" class="data-select" disabled>
|
||||
<option value="711proxy">711Proxy(首版)</option>
|
||||
</select>
|
||||
<button id="btn-ip-proxy-service-login" class="btn btn-outline btn-sm data-inline-btn" type="button">
|
||||
注册
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-ip-proxy-mode" style="display:none;">
|
||||
<span class="data-label">代理模式</span>
|
||||
<div id="ip-proxy-mode-group" class="choice-group" role="group" aria-label="IP代理模式">
|
||||
<button type="button" class="choice-btn" data-ip-proxy-mode="account">账号密码</button>
|
||||
<button type="button" class="choice-btn" data-ip-proxy-mode="api" disabled title="首版暂未开放">API 拉取(暂未开放)</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-layout-row" id="row-ip-proxy-layout" style="display:none;">
|
||||
<div class="ip-proxy-layout" id="ip-proxy-layout">
|
||||
<div class="ip-proxy-column ip-proxy-column-account" id="ip-proxy-account-panel">
|
||||
<div class="ip-proxy-column-header">账号密码模式</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-account-list" style="display:none;">
|
||||
<span class="data-label">账号代理列表</span>
|
||||
<textarea id="input-ip-proxy-account-list" class="data-textarea mono"
|
||||
placeholder="每行一条:host:port 或 host:port:username:password 例如 global.rotgb.711proxy.com:10000:username:password"></textarea>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-account-session-prefix" style="display:none;">
|
||||
<span class="data-label">会话(session)</span>
|
||||
<input type="text" id="input-ip-proxy-account-session-prefix" class="data-input mono"
|
||||
placeholder="会话前缀,例如 ZC28qZ0KQL" />
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-account-life-minutes" style="display:none;">
|
||||
<span class="data-label">时长(life)</span>
|
||||
<div class="data-inline">
|
||||
<input type="number" id="input-ip-proxy-account-life-minutes" class="data-input" min="1" max="1440" step="1"
|
||||
placeholder="5-180(分钟)" title="711Proxy 会话时长范围:5-180 分钟" />
|
||||
<span class="data-unit">分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-host" style="display:none;">
|
||||
<span class="data-label">代理 Host</span>
|
||||
<input type="text" id="input-ip-proxy-host" class="data-input" placeholder="例如 global.rotgb.711proxy.com" />
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-port" style="display:none;">
|
||||
<span class="data-label">代理 Port</span>
|
||||
<input type="number" id="input-ip-proxy-port" class="data-input" min="1" max="65535" step="1"
|
||||
placeholder="例如 10000" />
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-protocol" style="display:none;">
|
||||
<span class="data-label">代理协议</span>
|
||||
<select id="select-ip-proxy-protocol" class="data-select">
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
<option value="socks5">SOCKS5</option>
|
||||
<option value="socks4">SOCKS4</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-username" style="display:none;">
|
||||
<span class="data-label">代理账号</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-ip-proxy-username" class="data-input data-input-with-icon"
|
||||
placeholder="例如 USER047152-zone-custom-region-US-asn-ASN81" />
|
||||
<button id="btn-toggle-ip-proxy-username" class="input-icon-btn" type="button" aria-label="显示代理账号"
|
||||
title="显示代理账号"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-password" style="display:none;">
|
||||
<span class="data-label">代理密码</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-ip-proxy-password" class="data-input data-input-with-icon"
|
||||
placeholder="账号密码代理的密码" />
|
||||
<button id="btn-toggle-ip-proxy-password" class="input-icon-btn" type="button" aria-label="显示代理密码"
|
||||
title="显示代理密码"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-region" style="display:none;">
|
||||
<span class="data-label">地区参数</span>
|
||||
<input type="text" id="input-ip-proxy-region" class="data-input" placeholder="可选,例如 US / DE / HK" />
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-pool-target-count" style="display:none;">
|
||||
<span class="data-label">任务切换阈值</span>
|
||||
<div class="data-inline">
|
||||
<input type="number" id="input-ip-proxy-pool-target-count" class="data-input" min="1" max="500" step="1"
|
||||
placeholder="20(成功轮次)" title="每成功多少轮任务后自动切换一次代理;仅 1 条节点时会跳过自动切换" />
|
||||
<span class="data-unit">轮</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ip-proxy-column ip-proxy-column-api" id="ip-proxy-api-panel">
|
||||
<div class="ip-proxy-column-header">API 模式</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-api-url" style="display:none;">
|
||||
<span class="data-label">代理API</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-ip-proxy-api-url" class="data-input data-input-with-icon"
|
||||
placeholder="粘贴完整 API 链接" />
|
||||
<button id="btn-toggle-ip-proxy-api-url" class="input-icon-btn" type="button" aria-label="显示代理 API"
|
||||
title="显示代理 API"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-ip-proxy-actions" style="display:none;">
|
||||
<span class="data-label">切换代理</span>
|
||||
<div class="data-inline ip-proxy-actions-inline">
|
||||
<span id="ip-proxy-action-buttons" class="ip-proxy-action-grid">
|
||||
<button id="btn-ip-proxy-refresh" class="btn btn-outline btn-sm data-inline-btn" type="button">拉取</button>
|
||||
<button id="btn-ip-proxy-next" class="btn btn-outline btn-sm data-inline-btn" type="button">下一条</button>
|
||||
<button id="btn-ip-proxy-change" class="btn btn-outline btn-sm data-inline-btn" type="button" title="保持当前会话并刷新出口(仅 711 + session)">Change</button>
|
||||
<button id="btn-ip-proxy-probe" class="btn btn-outline btn-sm data-inline-btn" type="button">检测出口</button>
|
||||
</span>
|
||||
<span id="ip-proxy-action-hint" class="ip-proxy-action-hint">
|
||||
下一条:切到代理池下一条。Change:保持当前 session 重绑链路(仅 711 + session)。
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-ip-proxy-runtime-status" style="display:none;">
|
||||
<span class="data-label">代理状态</span>
|
||||
<div id="ip-proxy-runtime-status" class="ip-proxy-runtime-status state-idle">
|
||||
<span id="ip-proxy-runtime-dot" class="ip-proxy-runtime-dot" aria-hidden="true"></span>
|
||||
<div class="ip-proxy-runtime-content">
|
||||
<div id="ip-proxy-runtime-text" class="ip-proxy-runtime-main">未启用,沿用浏览器默认/全局代理。</div>
|
||||
<div class="ip-proxy-runtime-meta">
|
||||
<span id="ip-proxy-current" class="ip-proxy-runtime-current">暂无可用代理</span>
|
||||
</div>
|
||||
<div class="ip-proxy-runtime-details-row">
|
||||
<details id="ip-proxy-runtime-details" class="ip-proxy-runtime-details" hidden>
|
||||
<summary>详情</summary>
|
||||
<div id="ip-proxy-runtime-details-text" class="ip-proxy-runtime-details-text"></div>
|
||||
</details>
|
||||
<button id="btn-ip-proxy-check-ip" class="btn btn-outline btn-xs ip-proxy-check-ip-btn" type="button">检查IP</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cloudflare-temp-email-section" class="data-card hotmail-card" style="display:none;">
|
||||
<div class="section-mini-header">
|
||||
<div class="section-mini-copy">
|
||||
@@ -1103,6 +778,485 @@
|
||||
<div id="icloud-list" class="icloud-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ip-proxy-section" class="data-card ip-proxy-card">
|
||||
<div class="section-mini-header">
|
||||
<div class="section-mini-copy">
|
||||
<span class="section-label">IP 代理</span>
|
||||
<span class="data-value">用于浏览器代理接管与出口切换</span>
|
||||
</div>
|
||||
<div id="row-ip-proxy-enabled" class="section-mini-actions ip-proxy-header-actions">
|
||||
<button id="btn-toggle-ip-proxy-section" class="btn btn-ghost btn-xs" type="button"
|
||||
aria-expanded="false" aria-controls="row-ip-proxy-fold">展开设置</button>
|
||||
<label class="toggle-switch" for="input-ip-proxy-enabled" title="启用或禁用 IP 代理接管">
|
||||
<input type="checkbox" id="input-ip-proxy-enabled" />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-fold-row" id="row-ip-proxy-fold" style="display:none;">
|
||||
<div id="ip-proxy-fold" class="ip-proxy-fold">
|
||||
<div class="ip-proxy-fold-body">
|
||||
<div class="data-row" id="row-ip-proxy-service" style="display:none;">
|
||||
<span class="data-label">代理服务</span>
|
||||
<div class="data-inline">
|
||||
<select id="select-ip-proxy-service" class="data-select" disabled>
|
||||
<option value="711proxy">711Proxy(首版)</option>
|
||||
</select>
|
||||
<button id="btn-ip-proxy-service-login" class="btn btn-outline btn-sm data-inline-btn" type="button">
|
||||
注册
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-ip-proxy-mode" style="display:none;">
|
||||
<span class="data-label">代理模式</span>
|
||||
<div id="ip-proxy-mode-group" class="choice-group" role="group" aria-label="IP代理模式">
|
||||
<button type="button" class="choice-btn" data-ip-proxy-mode="account">账号密码</button>
|
||||
<button type="button" class="choice-btn" data-ip-proxy-mode="api" disabled title="首版暂未开放">API 拉取(暂未开放)</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-layout-row" id="row-ip-proxy-layout" style="display:none;">
|
||||
<div class="ip-proxy-layout" id="ip-proxy-layout">
|
||||
<div class="ip-proxy-column ip-proxy-column-account" id="ip-proxy-account-panel">
|
||||
<div class="ip-proxy-column-header">账号密码模式</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-account-list" style="display:none;">
|
||||
<span class="data-label">账号代理列表</span>
|
||||
<textarea id="input-ip-proxy-account-list" class="data-textarea mono"
|
||||
placeholder="每行一条:host:port 或 host:port:username:password 例如 global.rotgb.711proxy.com:10000:username:password"></textarea>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-account-session-prefix" style="display:none;">
|
||||
<span class="data-label">会话(session)</span>
|
||||
<input type="text" id="input-ip-proxy-account-session-prefix" class="data-input mono"
|
||||
placeholder="会话前缀,例如 ZC28qZ0KQL" />
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-account-life-minutes" style="display:none;">
|
||||
<span class="data-label">时长(life)</span>
|
||||
<div class="data-inline">
|
||||
<input type="number" id="input-ip-proxy-account-life-minutes" class="data-input" min="1" max="1440" step="1"
|
||||
placeholder="5-180(分钟)" title="711Proxy 会话时长范围:5-180 分钟" />
|
||||
<span class="data-unit">分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-host" style="display:none;">
|
||||
<span class="data-label">代理 Host</span>
|
||||
<input type="text" id="input-ip-proxy-host" class="data-input" placeholder="例如 global.rotgb.711proxy.com" />
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-port" style="display:none;">
|
||||
<span class="data-label">代理 Port</span>
|
||||
<input type="number" id="input-ip-proxy-port" class="data-input" min="1" max="65535" step="1"
|
||||
placeholder="例如 10000" />
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-protocol" style="display:none;">
|
||||
<span class="data-label">代理协议</span>
|
||||
<select id="select-ip-proxy-protocol" class="data-select">
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
<option value="socks5">SOCKS5</option>
|
||||
<option value="socks4">SOCKS4</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-username" style="display:none;">
|
||||
<span class="data-label">代理账号</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-ip-proxy-username" class="data-input data-input-with-icon"
|
||||
placeholder="例如 USER047152-zone-custom-region-US-asn-ASN81" />
|
||||
<button id="btn-toggle-ip-proxy-username" class="input-icon-btn" type="button" aria-label="显示代理账号"
|
||||
title="显示代理账号"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-password" style="display:none;">
|
||||
<span class="data-label">代理密码</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-ip-proxy-password" class="data-input data-input-with-icon"
|
||||
placeholder="账号密码代理的密码" />
|
||||
<button id="btn-toggle-ip-proxy-password" class="input-icon-btn" type="button" aria-label="显示代理密码"
|
||||
title="显示代理密码"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-region" style="display:none;">
|
||||
<span class="data-label">地区参数</span>
|
||||
<input type="text" id="input-ip-proxy-region" class="data-input" placeholder="可选,例如 US / DE / HK" />
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-pool-target-count" style="display:none;">
|
||||
<span class="data-label">任务切换阈值</span>
|
||||
<div class="data-inline">
|
||||
<input type="number" id="input-ip-proxy-pool-target-count" class="data-input" min="1" max="500" step="1"
|
||||
placeholder="20(成功轮次)" title="每成功多少轮任务后自动切换一次代理;仅 1 条节点时会跳过自动切换" />
|
||||
<span class="data-unit">轮</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-auto-sync-enabled" style="display:none;">
|
||||
<span class="data-label">自动同步</span>
|
||||
<div class="data-inline">
|
||||
<label class="toggle-switch" for="input-ip-proxy-auto-sync-enabled" title="启用后会按间隔自动执行一次同步代理">
|
||||
<input type="checkbox" id="input-ip-proxy-auto-sync-enabled" />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
</label>
|
||||
<span class="data-value">定时刷新代理,降低账号凭据过期导致的中断概率</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-auto-sync-interval" style="display:none;">
|
||||
<span class="data-label">同步间隔</span>
|
||||
<div class="data-inline">
|
||||
<input type="number" id="input-ip-proxy-auto-sync-interval-minutes" class="data-input" min="1" max="1440" step="1"
|
||||
placeholder="15" title="自动同步间隔(分钟)" />
|
||||
<span class="data-unit">分钟</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ip-proxy-column ip-proxy-column-api" id="ip-proxy-api-panel">
|
||||
<div class="ip-proxy-column-header">API 模式</div>
|
||||
<div class="data-row ip-proxy-column-row" id="row-ip-proxy-api-url" style="display:none;">
|
||||
<span class="data-label">代理API</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-ip-proxy-api-url" class="data-input data-input-with-icon"
|
||||
placeholder="粘贴完整 API 链接" />
|
||||
<button id="btn-toggle-ip-proxy-api-url" class="input-icon-btn" type="button" aria-label="显示代理 API"
|
||||
title="显示代理 API"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-ip-proxy-actions" style="display:none;">
|
||||
<span class="data-label">切换代理</span>
|
||||
<div class="data-inline ip-proxy-actions-inline">
|
||||
<span id="ip-proxy-action-buttons" class="ip-proxy-action-grid">
|
||||
<button id="btn-ip-proxy-refresh" class="btn btn-outline btn-sm data-inline-btn" type="button">拉取</button>
|
||||
<button id="btn-ip-proxy-next" class="btn btn-outline btn-sm data-inline-btn" type="button">下一条</button>
|
||||
<button id="btn-ip-proxy-change" class="btn btn-outline btn-sm data-inline-btn" type="button" title="保持当前会话并刷新出口(仅 711 + session)">Change</button>
|
||||
<button id="btn-ip-proxy-probe" class="btn btn-outline btn-sm data-inline-btn" type="button">检测出口</button>
|
||||
</span>
|
||||
<span id="ip-proxy-action-hint" class="ip-proxy-action-hint">
|
||||
下一条:切到代理池下一条。Change:保持当前 session 重绑链路(仅 711 + session)。
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-ip-proxy-runtime-status" style="display:none;">
|
||||
<span class="data-label">代理状态</span>
|
||||
<div id="ip-proxy-runtime-status" class="ip-proxy-runtime-status state-idle">
|
||||
<span id="ip-proxy-runtime-dot" class="ip-proxy-runtime-dot" aria-hidden="true"></span>
|
||||
<div class="ip-proxy-runtime-content">
|
||||
<div id="ip-proxy-runtime-text" class="ip-proxy-runtime-main">未启用,沿用浏览器默认/全局代理。</div>
|
||||
<div class="ip-proxy-runtime-meta">
|
||||
<span id="ip-proxy-current" class="ip-proxy-runtime-current">暂无可用代理</span>
|
||||
</div>
|
||||
<div class="ip-proxy-runtime-details-row">
|
||||
<details id="ip-proxy-runtime-details" class="ip-proxy-runtime-details" hidden>
|
||||
<summary>详情</summary>
|
||||
<div id="ip-proxy-runtime-details-text" class="ip-proxy-runtime-details-text"></div>
|
||||
</details>
|
||||
<button id="btn-ip-proxy-check-ip" class="btn btn-outline btn-xs ip-proxy-check-ip-btn" type="button">检查IP</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="phone-verification-section" class="data-card phone-verification-card">
|
||||
<div class="section-mini-header">
|
||||
<div class="section-mini-copy">
|
||||
<span class="section-label">接码设置</span>
|
||||
<span class="data-value">手机号验证与接码服务商获取策略</span>
|
||||
</div>
|
||||
<div id="row-phone-verification-enabled" class="section-mini-actions phone-verification-header-actions">
|
||||
<button id="btn-toggle-phone-verification-section" class="btn btn-ghost btn-xs" type="button"
|
||||
aria-expanded="false" aria-controls="row-phone-verification-fold">展开设置</button>
|
||||
<label class="toggle-switch" for="input-phone-verification-enabled" title="启用或禁用手机号接码流程">
|
||||
<input type="checkbox" id="input-phone-verification-enabled" />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row phone-verification-fold-row" id="row-phone-verification-fold" style="display:none;">
|
||||
<div id="phone-verification-fold" class="phone-verification-fold">
|
||||
<div class="phone-verification-fold-body">
|
||||
<div class="data-row" id="row-account-run-history-helper-base-url" style="display:none;">
|
||||
<span class="data-label">同步服务</span>
|
||||
<input type="text" id="input-account-run-history-helper-base-url" class="data-input mono"
|
||||
placeholder="http://127.0.0.1:17373" />
|
||||
</div>
|
||||
<div class="data-row" id="row-phone-sms-provider" style="display:none;">
|
||||
<span class="data-label">接码服务商</span>
|
||||
<select id="select-phone-sms-provider" class="data-select mono">
|
||||
<option value="hero-sms">HeroSMS(原有)</option>
|
||||
<option value="5sim">5sim(新增)</option>
|
||||
<option value="nexsms">NexSMS(新增)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="data-row" id="row-phone-sms-provider-order" style="display:none;">
|
||||
<span class="data-label">服务商顺序</span>
|
||||
<div class="data-inline hero-sms-country-stack">
|
||||
<select id="select-phone-sms-provider-order" class="data-input mono" multiple size="3" style="display:none;">
|
||||
<option value="hero-sms" selected>HeroSMS</option>
|
||||
<option value="5sim" selected>5sim</option>
|
||||
<option value="nexsms" selected>NexSMS</option>
|
||||
</select>
|
||||
<div class="hero-sms-country-mainline">
|
||||
<div id="phone-sms-provider-order-menu-shell" class="hero-sms-country-menu">
|
||||
<button id="btn-phone-sms-provider-order-menu" class="btn btn-outline btn-sm hero-sms-country-menu-btn" type="button" aria-haspopup="listbox" aria-expanded="false">
|
||||
HeroSMS / 5sim / NexSMS (3/3)
|
||||
</button>
|
||||
<div id="phone-sms-provider-order-menu" class="hero-sms-country-menu-dropdown" role="listbox" aria-multiselectable="true" hidden></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="data-value hero-sms-country-note">第一位为主服务商,后续按顺序自动回退。</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-phone-sms-provider-order-actions" style="display:none;">
|
||||
<span class="data-label">顺序管理</span>
|
||||
<div class="data-inline data-value-actions">
|
||||
<span id="display-phone-sms-provider-order" class="data-value data-value-fill mono country-order-list">未设置</span>
|
||||
<button id="btn-phone-sms-provider-order-reset" class="btn btn-ghost btn-xs data-inline-btn" type="button">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-platform" style="display:none;">
|
||||
<span class="data-label">接码平台</span>
|
||||
<span id="display-hero-sms-platform" class="data-value mono">HeroSMS / OpenAI / Thailand</span>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-country" style="display:none;">
|
||||
<span class="data-label">国家优先级</span>
|
||||
<div class="data-inline hero-sms-country-stack">
|
||||
<select id="select-hero-sms-country" class="data-input mono" multiple size="6" style="display:none;">
|
||||
<option value="52" selected>Thailand</option>
|
||||
</select>
|
||||
<div class="hero-sms-country-mainline">
|
||||
<div id="hero-sms-country-menu-shell" class="hero-sms-country-menu">
|
||||
<button id="btn-hero-sms-country-menu" class="btn btn-outline btn-sm hero-sms-country-menu-btn" type="button" aria-haspopup="listbox" aria-expanded="false">
|
||||
Thailand (1/3)
|
||||
</button>
|
||||
<div id="hero-sms-country-menu" class="hero-sms-country-menu-dropdown" role="listbox" aria-multiselectable="true" hidden></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="data-value hero-sms-country-note">多选最多 3 个,按点击顺序生效。</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-country-fallback" style="display:none;">
|
||||
<span class="data-label">生效顺序</span>
|
||||
<div class="data-inline data-value-actions">
|
||||
<span id="display-hero-sms-country-fallback-order" class="data-value data-value-fill mono country-order-list">未设置</span>
|
||||
<button id="btn-hero-sms-country-clear" class="btn btn-ghost btn-xs data-inline-btn" type="button">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-acquire-priority" style="display:none;">
|
||||
<span class="data-label">拿号优先级</span>
|
||||
<select id="select-hero-sms-acquire-priority" class="data-input mono">
|
||||
<option value="country">国家优先(默认)</option>
|
||||
<option value="price">低价优先(同价按国家顺序)</option>
|
||||
<option value="price_high">高价优先(同价按国家顺序)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-api-key" style="display:none;">
|
||||
<span class="data-label">接码 API</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-hero-sms-api-key" class="data-input data-input-with-icon mono"
|
||||
placeholder="请输入 HeroSMS API Key" />
|
||||
<button id="btn-toggle-hero-sms-api-key" class="input-icon-btn" type="button"
|
||||
data-password-toggle="input-hero-sms-api-key" data-show-label="显示 HeroSMS API Key"
|
||||
data-hide-label="隐藏 HeroSMS API Key" aria-label="显示 HeroSMS API Key" title="显示 HeroSMS API Key"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-five-sim-api-key" style="display:none;">
|
||||
<span class="data-label">5sim API</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-five-sim-api-key" class="data-input data-input-with-icon mono"
|
||||
placeholder="请输入 5sim API Key(Bearer Token)" />
|
||||
<button id="btn-toggle-five-sim-api-key" class="input-icon-btn" type="button"
|
||||
data-password-toggle="input-five-sim-api-key" data-show-label="显示 5sim API Key"
|
||||
data-hide-label="隐藏 5sim API Key" aria-label="显示 5sim API Key" title="显示 5sim API Key"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-five-sim-country" style="display:none;">
|
||||
<span class="data-label">国家优先级</span>
|
||||
<div class="data-inline hero-sms-country-stack">
|
||||
<select id="select-five-sim-country" class="data-input mono" multiple size="6" style="display:none;">
|
||||
<option value="thailand" selected>泰国 (Thailand) [TH]</option>
|
||||
</select>
|
||||
<div class="hero-sms-country-mainline">
|
||||
<div id="five-sim-country-menu-shell" class="hero-sms-country-menu">
|
||||
<button id="btn-five-sim-country-menu" class="btn btn-outline btn-sm hero-sms-country-menu-btn" type="button" aria-haspopup="listbox" aria-expanded="false">
|
||||
未选择 (0/3)
|
||||
</button>
|
||||
<div id="five-sim-country-menu" class="hero-sms-country-menu-dropdown" role="listbox" aria-multiselectable="true" hidden></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="data-value hero-sms-country-note">多选最多 3 个,按点击顺序生效。</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-five-sim-country-fallback" style="display:none;">
|
||||
<span class="data-label">生效顺序</span>
|
||||
<div class="data-inline data-value-actions">
|
||||
<span id="display-five-sim-country-fallback-order" class="data-value data-value-fill mono country-order-list">未设置</span>
|
||||
<button id="btn-five-sim-country-clear" class="btn btn-ghost btn-xs data-inline-btn" type="button">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-five-sim-operator" style="display:none;">
|
||||
<span class="data-label">运营商</span>
|
||||
<input type="text" id="input-five-sim-operator" class="data-input mono"
|
||||
placeholder="any" />
|
||||
</div>
|
||||
<div class="data-row" id="row-five-sim-product" style="display:none;">
|
||||
<span class="data-label">产品代码</span>
|
||||
<input type="text" id="input-five-sim-product" class="data-input mono"
|
||||
placeholder="openai" />
|
||||
</div>
|
||||
<div class="data-row" id="row-nex-sms-api-key" style="display:none;">
|
||||
<span class="data-label">NexSMS API</span>
|
||||
<div class="input-with-icon">
|
||||
<input type="password" id="input-nex-sms-api-key" class="data-input data-input-with-icon mono"
|
||||
placeholder="请输入 NexSMS API Key" />
|
||||
<button id="btn-toggle-nex-sms-api-key" class="input-icon-btn" type="button"
|
||||
data-password-toggle="input-nex-sms-api-key" data-show-label="显示 NexSMS API Key"
|
||||
data-hide-label="隐藏 NexSMS API Key" aria-label="显示 NexSMS API Key" title="显示 NexSMS API Key"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-nex-sms-country" style="display:none;">
|
||||
<span class="data-label">国家优先级</span>
|
||||
<div class="data-inline hero-sms-country-stack">
|
||||
<select id="select-nex-sms-country" class="data-input mono" multiple size="6" style="display:none;">
|
||||
<option value="1" selected>Country #1</option>
|
||||
</select>
|
||||
<div class="hero-sms-country-mainline">
|
||||
<div id="nex-sms-country-menu-shell" class="hero-sms-country-menu">
|
||||
<button id="btn-nex-sms-country-menu" class="btn btn-outline btn-sm hero-sms-country-menu-btn" type="button" aria-haspopup="listbox" aria-expanded="false">
|
||||
Country #1 (1/3)
|
||||
</button>
|
||||
<div id="nex-sms-country-menu" class="hero-sms-country-menu-dropdown" role="listbox" aria-multiselectable="true" hidden></div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="data-value hero-sms-country-note">多选最多 3 个,按点击顺序生效。</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-nex-sms-country-fallback" style="display:none;">
|
||||
<span class="data-label">生效顺序</span>
|
||||
<div class="data-inline data-value-actions">
|
||||
<span id="display-nex-sms-country-fallback-order" class="data-value data-value-fill mono country-order-list">未设置</span>
|
||||
<button id="btn-nex-sms-country-clear" class="btn btn-ghost btn-xs data-inline-btn" type="button">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-nex-sms-service-code" style="display:none;">
|
||||
<span class="data-label">服务代码</span>
|
||||
<input type="text" id="input-nex-sms-service-code" class="data-input mono"
|
||||
placeholder="ot" />
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-max-price" style="display:none;">
|
||||
<span class="data-label">价格</span>
|
||||
<div class="data-inline hero-sms-price-preview-stack">
|
||||
<div class="hero-sms-price-preview-head">
|
||||
<button id="btn-hero-sms-price-preview" class="btn btn-outline btn-xs data-inline-btn" type="button">查询价格</button>
|
||||
</div>
|
||||
<div id="row-hero-sms-price-tiers" class="hero-sms-price-preview-result" style="display:none;">
|
||||
<span id="display-hero-sms-price-tiers" class="data-value mono hero-sms-price-preview-text">未获取</span>
|
||||
</div>
|
||||
<div class="hero-sms-price-controls-grid">
|
||||
<div class="hero-sms-price-control">
|
||||
<span class="hero-sms-settings-caption">价格上限</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-hero-sms-max-price" class="data-input auto-delay-input mono hero-sms-max-price-input" placeholder="0.12" min="0" step="0.0001" title="接码价格上限;可空(空=自动价格)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-sms-price-control">
|
||||
<span class="hero-sms-settings-caption">指定档位</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-hero-sms-preferred-price" class="data-input auto-delay-input mono hero-sms-max-price-input" placeholder="例如 0.0512(可空)" min="0" step="0.0001" title="优先尝试该价格档位;可空(空=按优先级策略)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-sms-price-control hero-sms-price-control-reuse">
|
||||
<span class="hero-sms-settings-caption">号码复用</span>
|
||||
<div class="setting-controls hero-sms-toggle-controls">
|
||||
<label class="toggle-switch hero-sms-price-reuse-toggle" for="input-hero-sms-reuse-enabled" title="开启后会优先复用未超次数的可用号码">
|
||||
<input type="checkbox" id="input-hero-sms-reuse-enabled" />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-phone-code-settings-group" style="display:none;">
|
||||
<span class="data-label">接码参数</span>
|
||||
<div class="data-inline hero-sms-settings-grid">
|
||||
<div id="row-phone-verification-resend-count" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">验证码重发</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-verification-resend-count" class="data-input auto-delay-input" value="4"
|
||||
min="0" max="20" step="1" title="自动点击重新发送验证码的次数" />
|
||||
<span class="data-unit">次</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="row-phone-replacement-limit" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">换号上限</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-phone-replacement-limit" class="data-input auto-delay-input" value="3" min="1" max="20" step="1" title="步骤 9 内部允许更换号码的最大次数" />
|
||||
<span class="data-unit">次</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="row-phone-code-wait-seconds" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">验证码限时</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-phone-code-wait-seconds" class="data-input auto-delay-input" value="60" min="15" max="300" step="1" title="每轮等待验证码的秒数" />
|
||||
<span class="data-unit">秒</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="row-phone-code-timeout-windows" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">超时次数</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-phone-code-timeout-windows" class="data-input auto-delay-input" value="2" min="1" max="10" step="1" title="验证码超时后,最多继续等待几轮再换号" />
|
||||
<span class="data-unit">轮</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="row-phone-code-poll-interval-seconds" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">轮询间隔</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-phone-code-poll-interval-seconds" class="data-input auto-delay-input" value="5" min="1" max="30" step="1" title="向 HeroSMS 查询验证码状态的间隔秒数" />
|
||||
<span class="data-unit">秒</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="row-phone-code-poll-max-rounds" class="hero-sms-settings-cell" style="display:none;">
|
||||
<span class="hero-sms-settings-caption">轮询次数</span>
|
||||
<div class="setting-controls">
|
||||
<input type="number" id="input-phone-code-poll-max-rounds" class="data-input auto-delay-input" value="4" min="1" max="120" step="1" title="每轮验证码等待窗口最多轮询次数" />
|
||||
<span class="data-unit">次</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-hero-sms-runtime-pair" style="display:none;">
|
||||
<span class="data-label">运行状态</span>
|
||||
<div class="data-inline hero-sms-runtime-grid">
|
||||
<div id="row-hero-sms-current-number" class="hero-sms-runtime-cell" style="display:none;">
|
||||
<span class="hero-sms-runtime-key">当前分配</span>
|
||||
<span id="display-hero-sms-current-number" class="data-value mono hero-sms-runtime-value">未分配</span>
|
||||
</div>
|
||||
<div id="row-hero-sms-current-countdown" class="hero-sms-runtime-cell" style="display:none;">
|
||||
<span class="hero-sms-runtime-key">倒计时</span>
|
||||
<span id="display-hero-sms-current-countdown" class="data-value mono hero-sms-runtime-value">未启动</span>
|
||||
</div>
|
||||
<div id="row-hero-sms-current-code" class="hero-sms-runtime-cell" style="display:none;">
|
||||
<span class="hero-sms-runtime-key">验证码</span>
|
||||
<span id="display-hero-sms-current-code" class="data-value mono hero-sms-runtime-value">未获取</span>
|
||||
</div>
|
||||
<div id="row-hero-sms-preferred-activation" class="hero-sms-runtime-cell hero-sms-runtime-cell-span2" style="display:none;">
|
||||
<span class="hero-sms-runtime-key">优先号码</span>
|
||||
<select id="select-hero-sms-preferred-activation" class="data-input mono hero-sms-runtime-select">
|
||||
<option value="">自动(先复用已有可用号,再创建新号)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="status-bar" class="status-bar">
|
||||
<div class="status-dot"></div>
|
||||
<span id="display-status">就绪</span>
|
||||
|
||||
+3435
-370
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user