Files
order_site/apps/extensions/pdd-cookie-sync/content.js
T
yml2213 9a342e7ea6 feat(pdd-extension): 插件升级至 v1.2.0,增加防休眠心跳、会话主动续期与超时弹窗秒级拦截
1. 新增 content.js 页面级保活引擎,自动注入拼多多商家后台 (mms.pinduoduo.com);
2. 主动防休眠与静默续期:每 2 分钟向 MMS 接口发送轻量只读心跳并派发微动作,主动刷新服务端 PASS_ID 有效期,从根本上阻止空闲弹窗生成;
3. 超时弹窗秒级自动拦截:使用 MutationObserver 实时监听 DOM,毫秒级捕获「当前页面停留时间过长」弹窗并自动点击「刷新」,彻底解决页面挂死导致 Cookie 会话过期被踢出账号的问题;
4. 后台长效平滑刷新:后台标签页静置超 25 分钟自动执行无感安全重载,保障 CSRF Token 与会话 24 小时鲜活;
5. 日志链路打通:将页面防休眠、弹窗拦截与周期刷新事件接入扩展日志控制台。
2026-09-19 11:06:32 +08:00

217 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Pinduoduo Merchant Page Active Keepalive & Anti-Inactivity Content Script
* 拼多多商家后台页面主动保活、防休眠心跳与超时弹窗秒级拦截注入脚本
*/
;(function () {
'use strict'
const TAG = '[PDD-KEEPALIVE]'
const HEARTBEAT_INTERVAL_MS = 2 * 60 * 1000 // 每 2 分钟执行一次主动心跳与会话续期
const AUTO_RELOAD_INTERVAL_MS = 25 * 60 * 1000 // 每 25 分钟自动平滑刷新一次页面以保证全状态新鲜
let lastUserActivityTime = Date.now()
let isReloading = false
console.log(`${TAG} 拼多多页面主动保活与防休眠助手已加载:`, window.location.href)
// 辅助:向 background 记录运行日志
function reportLog(level, action, message, details = null) {
try {
chrome.runtime.sendMessage({
action: 'content_log',
level,
module: '页面保活',
actionName: action,
message,
details,
})
} catch {
// 忽略通信异常
}
}
// 1. 监听真实用户操作,记录活跃时间戳
const userEvents = ['mousemove', 'mousedown', 'keydown', 'scroll', 'touchstart']
userEvents.forEach((evt) => {
window.addEventListener(
evt,
() => {
lastUserActivityTime = Date.now()
},
{ passive: true },
)
})
// 2. 模拟轻量用户活动(重置拼多多前端页面的空闲计时器)
function dispatchFakeUserActivity() {
try {
const evt = new MouseEvent('mousemove', {
view: window,
bubbles: true,
cancelable: true,
clientX: Math.floor(Math.random() * 200) + 100,
clientY: Math.floor(Math.random() * 200) + 100,
})
document.dispatchEvent(evt)
window.dispatchEvent(new Event('focus'))
} catch (e) {
console.warn(`${TAG} 模拟微动作异常:`, e)
}
}
// 3. 发送轻量级静默 API 心跳(主动向拼多多服务端续期 PASS_ID 与会话 TTL)
async function sendSilentSessionHeartbeat() {
try {
dispatchFakeUserActivity()
// 选择拼多多商家后台最轻量的只读接口进行探测与续期
const endpoints = [
'/mangkhut/mms/recentOrderList?page=1&pageSize=1',
'/home/',
'/orders/list',
]
const targetEndpoint = endpoints[Math.floor(Math.random() * endpoints.length)]
const resp = await fetch(targetEndpoint, {
method: 'GET',
credentials: 'include',
headers: {
'X-Requested-With': 'XMLHttpRequest',
Accept: 'application/json, text/plain, */*',
},
})
if (resp.ok) {
console.log(`${TAG} 服务端会话静默续期成功 (${targetEndpoint})`)
} else if (resp.status === 401 || resp.status === 403) {
reportLog('warn', '会话预警', `页面心跳返回 HTTP ${resp.status},可能需要重新登录`)
}
} catch (err) {
console.warn(`${TAG} 发送静默续期心跳异常:`, err)
}
}
// 4. 实时拦截超时提示遮罩弹窗(秒级自动点击刷新/恢复)
function scanAndBustInactivityModal() {
if (isReloading) return
// 拼多多超时弹窗典型文案特征
const triggerKeywords = [
'当前页面停留时间过长',
'为了避免数据延迟',
'建议刷新一次页面',
'停留时间过长',
]
// 查找所有可能的弹窗/Modal 容器
const modalContainers = document.querySelectorAll(
'.ant-modal, .pdd-modal, [class*="modal"], [class*="dialog"], [role="dialog"]',
)
for (const container of modalContainers) {
const text = container.textContent || ''
const hasKeyword = triggerKeywords.some((kw) => text.includes(kw))
if (!hasKeyword) continue
console.warn(`${TAG} 🚨 捕获到拼多多超时休眠弹窗!立即执行秒级自动恢复...`)
reportLog('warn', '弹窗拦截', '捕获到「页面停留时间过长」弹窗,立即自动点击刷新续期会话')
// 寻找弹窗内的「刷新」或确定按钮
const buttons = container.querySelectorAll('button, a, [role="button"]')
let clicked = false
for (const btn of buttons) {
const btnText = (btn.textContent || '').trim()
if (btnText.includes('刷新') || btnText.includes('确定') || btnText.includes('知道了')) {
btn.click()
clicked = true
console.log(`${TAG} ✅ 已自动触发弹窗内【${btnText}】按钮`)
break
}
}
if (!clicked) {
// 若未找到合适按钮,直接调用页面无感平滑重载
isReloading = true
console.log(`${TAG} 未找到确定按钮,执行 location.reload()`)
setTimeout(() => {
window.location.reload()
}, 500)
}
return
}
// 全局文本扫描(兜底非标准 modal 结构)
const allDivs = document.querySelectorAll('div')
for (const div of allDivs) {
if (div.children.length === 0 && div.textContent) {
if (div.textContent.includes('当前页面停留时间过长')) {
const parent = div.closest('div[style*="z-index"]') || div.parentElement
const btn = parent ? parent.querySelector('button') : null
if (btn) {
console.warn(`${TAG} 兜底捕获超时弹窗,触发点击`)
btn.click()
} else {
isReloading = true
window.location.reload()
}
break
}
}
}
}
// 5. DOM 变化观察器(毫秒级感知弹窗挂载)
const observer = new MutationObserver(() => {
scanAndBustInactivityModal()
})
if (document.body) {
observer.observe(document.body, {
childList: true,
subtree: true,
})
} else {
document.addEventListener('DOMContentLoaded', () => {
observer.observe(document.body, {
childList: true,
subtree: true,
})
})
}
// 6. 定时执行静默会话心跳
setInterval(() => {
sendSilentSessionHeartbeat()
scanAndBustInactivityModal()
}, HEARTBEAT_INTERVAL_MS)
// 7. 定时平滑刷新兜底(每 25 分钟:若用户 10 分钟未操作,自动安全刷新当前页面刷新凭证)
setInterval(() => {
const idleTime = Date.now() - lastUserActivityTime
// 如果页面处于静置状态(超过 5 分钟未打字或点击),执行一次平滑刷新保持全局新鲜
if (idleTime > 5 * 60 * 1000 && !isReloading) {
console.log(`${TAG} 页面静置已超周期,执行安全平滑重载以更新全量会话凭证`)
reportLog('info', '周期刷新', '后台页面静置超 25 分钟,自动平滑重载以保持 Token 绝对新鲜')
isReloading = true
window.location.reload()
}
}, AUTO_RELOAD_INTERVAL_MS)
// 8. 响应来自 Background 的保活 Ping
chrome.runtime.onMessage.addListener((req, _sender, sendResponse) => {
if (req.action === 'ping_keepalive') {
sendSilentSessionHeartbeat()
scanAndBustInactivityModal()
sendResponse({ status: 'alive', timestamp: Date.now() })
}
})
// 初始化首次扫描与心跳
setTimeout(() => {
sendSilentSessionHeartbeat()
scanAndBustInactivityModal()
reportLog('info', '页面接入', '拼多多商家后台已成功注入主动保活与防退出引擎')
}, 3000)
})()