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. 日志链路打通:将页面防休眠、弹窗拦截与周期刷新事件接入扩展日志控制台。
This commit is contained in:
@@ -11,14 +11,20 @@
|
||||
- 自动在后台提取新鲜的会话凭证(包含 `PASS_ID`、`rckk` 等);
|
||||
- 内置 SHA-256 指纹比对去重机制,无实质变动时不发起网络请求,绝不浪费带宽与频控。
|
||||
|
||||
2. **端到端信封加密(End-to-End Envelope Encryption)**:
|
||||
2. **主动防休眠与防退出引擎(v1.2.0 新增 Anti-Logout Engine)**:
|
||||
- **静默会话续期**:定时(每 2 分钟)自动调用轻量后台接口,主动刷新拼多多服务端 `PASS_ID` 的 TTL 有效期;
|
||||
- **前端防休眠心跳**:向页面派发微弱活跃事件,重置 MMS 前端空闲计时器,从根源阻止「页面停留时间过长」弹窗产生;
|
||||
- **秒级弹窗拦截**:DOM 监听器毫秒级捕获停留过长弹窗并自动秒点「刷新」,彻底避免页面挂死导致 Session 过期被强制踢出登录;
|
||||
- **周期平滑刷新**:长时间挂机自动无感重载,确保 24 小时保持登录与 Token 鲜活。
|
||||
|
||||
3. **端到端信封加密(End-to-End Envelope Encryption)**:
|
||||
- **传输层**:强制走 HTTPS / TLS 1.3 通道;
|
||||
- **应用层**:使用浏览器原生 Web Crypto API 生成随机 256 位 AES-GCM 对称密钥;
|
||||
- **非对称公钥包装**:使用服务端的 RSA-2048 公钥(RSA-OAEP SHA-256)加密 AES 密钥;
|
||||
- **防重放与防篡改**:每个数据包附带 60 秒失效时间戳与加密 Nonce,服务端做幂等校验,AES-GCM Auth Tag 确保任意环节防篡改。
|
||||
- **无私钥泄漏风险**:扩展仅内置/获取公钥,即使他人获取插件源码也绝对无法解密回传数据包。
|
||||
|
||||
3. **双向鉴权与店铺绑定(Pairing Token)**:
|
||||
4. **双向鉴权与店铺绑定(Pairing Token)**:
|
||||
- 每个店铺在管理后台均有独立的 `syncToken`;
|
||||
- 只有持有该配对密钥的插件上报才会被服务端接受。
|
||||
|
||||
|
||||
@@ -380,25 +380,48 @@ chrome.cookies.onChanged.addListener((changeInfo) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 定时任务:每隔 30 分钟进行一次轻量巡检与必要保活上报
|
||||
chrome.alarms.onAlarm.addListener((alarm) => {
|
||||
// 定时任务:每隔 15 分钟进行一次轻量巡检、保活上报与页面 Ping
|
||||
chrome.alarms.onAlarm.addListener(async (alarm) => {
|
||||
if (alarm.name === ALARM_NAME) {
|
||||
syncCookieToServer(false).catch(() => null)
|
||||
|
||||
// 向打开的拼多多商家页面发送保活 Ping
|
||||
try {
|
||||
const tabs = await chrome.tabs.query({ url: '*://mms.pinduoduo.com/*' })
|
||||
for (const tab of tabs) {
|
||||
if (tab.id) {
|
||||
chrome.tabs.sendMessage(tab.id, { action: 'ping_keepalive' }).catch(() => null)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// 忽略查询异常
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 扩展安装或更新时注册定时闹钟
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
chrome.alarms.create(ALARM_NAME, {
|
||||
periodInMinutes: 30,
|
||||
periodInMinutes: 15,
|
||||
})
|
||||
addExtensionLog('info', '扩展安装/更新', '拼多多 Cookie 保活扩展 v1.1.0 后台服务启动')
|
||||
addExtensionLog('info', '扩展安装/更新', '拼多多 Cookie 保活扩展 v1.2.0 (含防退出/弹窗拦截引擎) 已启动')
|
||||
// 首次运行尝试同步一次
|
||||
syncCookieToServer(false).catch(() => null)
|
||||
})
|
||||
|
||||
// 响应来自 popup.js 的前端交互消息
|
||||
// 响应来自 popup.js 与 content.js 的交互消息
|
||||
chrome.runtime.onMessage.addListener((req, _sender, sendResponse) => {
|
||||
if (req.action === 'content_log') {
|
||||
const level = req.level || 'info'
|
||||
const action = req.actionName || req.module || '页面保活'
|
||||
const message = req.message || ''
|
||||
const details = req.details || null
|
||||
addExtensionLog(level, action, message, details).then(() => {
|
||||
sendResponse({ success: true })
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
if (req.action === 'trigger_sync') {
|
||||
syncCookieToServer(true).then(sendResponse).catch((err) => {
|
||||
sendResponse({ success: false, message: String(err) })
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* 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)
|
||||
})()
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "拼多多 Cookie 自动保活同步",
|
||||
"version": "1.1.0",
|
||||
"description": "自动嗅探拼多多商家后台新鲜 Cookie 并通过端到端 RSA+AES-GCM 信封加密安全同步至业务总平台",
|
||||
"version": "1.2.0",
|
||||
"description": "自动嗅探拼多多商家后台新鲜 Cookie 并通过端到端 RSA+AES-GCM 信封加密安全同步,内置防退出与超时弹窗秒级拦截",
|
||||
"permissions": [
|
||||
"cookies",
|
||||
"storage",
|
||||
@@ -15,6 +15,17 @@
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"https://mms.pinduoduo.com/*",
|
||||
"*://mms.pinduoduo.com/*"
|
||||
],
|
||||
"js": ["content.js"],
|
||||
"run_at": "document_end",
|
||||
"all_frames": false
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
<div class="header">
|
||||
<div class="title">
|
||||
<span>拼多多 Cookie 保活</span>
|
||||
<span class="version-tag">v1.1.0</span>
|
||||
<span class="version-tag">v1.2.0</span>
|
||||
</div>
|
||||
<div id="statusBadge" class="badge badge-gray">未检测</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user