fix: 恢复全局 JSON.parse patch, 修复售罄按钮不可点击的问题
This commit is contained in:
@@ -124,15 +124,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 仅对目标URL响应做patch的标记
|
// 全局 patch: 页面加载时也需要解除售罄状态,否则按钮不可点击
|
||||||
let _shouldPatch = false;
|
|
||||||
|
|
||||||
JSON.parse = function (text, reviver) {
|
JSON.parse = function (text, reviver) {
|
||||||
const result = _parse(text, reviver);
|
const result = _parse(text, reviver);
|
||||||
if (_shouldPatch) {
|
try { patchSoldOut(result); } catch {}
|
||||||
try { patchSoldOut(result); } catch {}
|
|
||||||
_shouldPatch = false;
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
Object.defineProperty(JSON.parse, 'toString', { value: () => 'function parse() { [native code] }' });
|
Object.defineProperty(JSON.parse, 'toString', { value: () => 'function parse() { [native code] }' });
|
||||||
@@ -155,11 +150,9 @@
|
|||||||
return { ok: false, reason: '429 限流', attempt: attemptNum };
|
return { ok: false, reason: '429 限流', attempt: attemptNum };
|
||||||
}
|
}
|
||||||
|
|
||||||
_shouldPatch = true; // 让 JSON.parse 对此响应做 patch
|
|
||||||
const text = await resp.text();
|
const text = await resp.text();
|
||||||
let data;
|
let data;
|
||||||
try { data = _parse(text); } catch { data = null; }
|
try { data = _parse(text); } catch { data = null; }
|
||||||
_shouldPatch = false;
|
|
||||||
|
|
||||||
if (data && data.code === 200 && data.data && data.data.bizId) {
|
if (data && data.code === 200 && data.data && data.data.bizId) {
|
||||||
const bizId = data.data.bizId;
|
const bizId = data.data.bizId;
|
||||||
|
|||||||
99
inject.js
99
inject.js
@@ -112,15 +112,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 仅对目标URL响应做patch的标记
|
// 全局 patch: 页面加载时也需要解除售罄状态,否则按钮不可点击
|
||||||
let _shouldPatch = false;
|
|
||||||
|
|
||||||
JSON.parse = function (text, reviver) {
|
JSON.parse = function (text, reviver) {
|
||||||
const result = _parse(text, reviver);
|
const result = _parse(text, reviver);
|
||||||
if (_shouldPatch) {
|
try { patchSoldOut(result); } catch {}
|
||||||
try { patchSoldOut(result); } catch {}
|
|
||||||
_shouldPatch = false;
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
Object.defineProperty(JSON.parse, 'toString', { value: () => 'function parse() { [native code] }' });
|
Object.defineProperty(JSON.parse, 'toString', { value: () => 'function parse() { [native code] }' });
|
||||||
@@ -143,11 +138,9 @@
|
|||||||
return { ok: false, reason: '429 限流', attempt: attemptNum };
|
return { ok: false, reason: '429 限流', attempt: attemptNum };
|
||||||
}
|
}
|
||||||
|
|
||||||
_shouldPatch = true; // 让 JSON.parse 对此响应做 patch
|
|
||||||
const text = await resp.text();
|
const text = await resp.text();
|
||||||
let data;
|
let data;
|
||||||
try { data = _parse(text); } catch { data = null; }
|
try { data = _parse(text); } catch { data = null; }
|
||||||
_shouldPatch = false;
|
|
||||||
|
|
||||||
if (data && data.code === 200 && data.data && data.data.bizId) {
|
if (data && data.code === 200 && data.data && data.data.bizId) {
|
||||||
const bizId = data.data.bizId;
|
const bizId = data.data.bizId;
|
||||||
@@ -458,25 +451,93 @@
|
|||||||
|
|
||||||
async function autoRecover() {
|
async function autoRecover() {
|
||||||
if (recovering || recoveryAttempts >= CFG.recoveryMax || !state.lastSuccess) return;
|
if (recovering || recoveryAttempts >= CFG.recoveryMax || !state.lastSuccess) return;
|
||||||
const dialog = findErrorDialog();
|
|
||||||
if (!dialog) return;
|
|
||||||
|
|
||||||
recovering = true;
|
recovering = true;
|
||||||
recoveryAttempts++;
|
recoveryAttempts++;
|
||||||
try {
|
try {
|
||||||
log('检测到错误弹窗, 自动恢复...');
|
// 策略1: 关闭所有弹窗/遮罩 (暴力清理)
|
||||||
setState({ cache: state.lastSuccess });
|
const dialog = findErrorDialog();
|
||||||
dismissDialog(dialog);
|
if (dialog) {
|
||||||
await sleep(400);
|
log('检测到错误弹窗, 清理中...');
|
||||||
const still = findErrorDialog();
|
dismissDialog(dialog);
|
||||||
if (still) { dismissDialog(still); await sleep(200); }
|
await sleep(300);
|
||||||
|
}
|
||||||
|
// 清理所有可能残留的遮罩层
|
||||||
|
document.querySelectorAll('.el-overlay, .v-modal, .el-overlay-dialog, [class*="overlay"], [class*="mask"]').forEach(el => {
|
||||||
|
el.style.display = 'none';
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.el-dialog__wrapper, .el-message-box__wrapper').forEach(el => {
|
||||||
|
el.style.display = 'none';
|
||||||
|
});
|
||||||
|
// 移除 body 上的 overflow:hidden (弹窗锁定滚动)
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
document.body.classList.remove('el-popup-parent--hidden');
|
||||||
|
await sleep(200);
|
||||||
|
|
||||||
|
// 策略2: 缓存响应 + 重新点购买按钮
|
||||||
|
setState({ cache: state.lastSuccess });
|
||||||
const btn = findBuyButton();
|
const btn = findBuyButton();
|
||||||
if (btn) { btn.click(); log('已重新点击购买按钮'); }
|
if (btn) {
|
||||||
else { log('未找到购买按钮'); alert('已获取到商品! 请手动点击购买!'); }
|
btn.click();
|
||||||
|
log('已重新点击购买按钮 (策略2)');
|
||||||
|
await sleep(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略3: 检查支付弹窗是否出现, 没有则直接用 bizId 构造支付
|
||||||
|
const payDialog = document.querySelector('[class*="pay"], [class*="qrcode"], [class*="wechat"], [class*="alipay"]');
|
||||||
|
if (!payDialog || payDialog.offsetParent === null) {
|
||||||
|
const bizId = state.bizId;
|
||||||
|
if (bizId) {
|
||||||
|
log('支付弹窗未出现, 尝试直接调用 check 页面...');
|
||||||
|
// 尝试直接打开支付 — 有些网站 check 接口会返回支付链接
|
||||||
|
try {
|
||||||
|
const checkUrl = `${location.origin}${CFG.CHECK}?bizId=${encodeURIComponent(bizId)}`;
|
||||||
|
const resp = await _fetch(checkUrl, { credentials: 'include' });
|
||||||
|
const data = await resp.json();
|
||||||
|
log('check响应: ' + JSON.stringify(data).substring(0, 200));
|
||||||
|
|
||||||
|
// 如果有支付URL, 直接跳转
|
||||||
|
if (data.data && typeof data.data === 'string' && data.data.startsWith('http')) {
|
||||||
|
log('获取到支付链接, 跳转中...');
|
||||||
|
window.open(data.data, '_blank');
|
||||||
|
} else if (data.data && data.data.payUrl) {
|
||||||
|
log('获取到payUrl, 跳转中...');
|
||||||
|
window.open(data.data.payUrl, '_blank');
|
||||||
|
} else if (data.data && data.data.qrCode) {
|
||||||
|
log('获取到二维码数据');
|
||||||
|
showQRCodeFallback(data.data.qrCode, bizId);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log('check调用失败: ' + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 策略4: 最终兜底 — 弹窗提醒手动操作
|
||||||
|
if (!document.querySelector('[class*="pay"], [class*="qrcode"]')) {
|
||||||
|
log('所有自动恢复策略已尝试, 请手动操作');
|
||||||
|
const bizId = state.bizId;
|
||||||
|
alert(`已抢到 bizId=${bizId}\n\n请尝试:\n1. 刷新页面后立即点击购买\n2. 或手动访问支付页面`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log('支付弹窗已出现!');
|
||||||
|
}
|
||||||
} finally { recovering = false; }
|
} finally { recovering = false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 兜底: 直接在页面上显示二维码 */
|
||||||
|
function showQRCodeFallback(qrData, bizId) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:999999;background:#fff;padding:30px;border-radius:12px;box-shadow:0 8px 32px rgba(0,0,0,.3);text-align:center';
|
||||||
|
div.innerHTML = `
|
||||||
|
<h3 style="margin:0 0 15px;color:#333">扫码支付</h3>
|
||||||
|
<img src="${qrData}" style="width:200px;height:200px" onerror="this.parentElement.innerHTML+='<p>二维码加载失败</p>'">
|
||||||
|
<p style="margin:15px 0 0;color:#666;font-size:13px">bizId: ${bizId}</p>
|
||||||
|
<button onclick="this.parentElement.remove()" style="margin-top:10px;padding:6px 20px;border:1px solid #ddd;border-radius:4px;cursor:pointer">关闭</button>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(div);
|
||||||
|
log('已显示兜底支付二维码');
|
||||||
|
}
|
||||||
|
|
||||||
// MutationObserver 监控弹窗 (替代 setInterval)
|
// MutationObserver 监控弹窗 (替代 setInterval)
|
||||||
function setupDialogWatcher() {
|
function setupDialogWatcher() {
|
||||||
const observer = new MutationObserver(() => {
|
const observer = new MutationObserver(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user