// ==UserScript== // @name 请求监听过滤器 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 监听所有HTTP和HTTPS请求 // @author 晚风 // @match http://*/* // @match https://*/* // @grant GM_getValue // @grant GM_setValue // @run-at document-start // @inject-into page // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/520573/%E8%AF%B7%E6%B1%82%E7%9B%91%E5%90%AC%E8%BF%87%E6%BB%A4%E5%99%A8.user.js // @updateURL https://update.greasyfork.icu/scripts/520573/%E8%AF%B7%E6%B1%82%E7%9B%91%E5%90%AC%E8%BF%87%E6%BB%A4%E5%99%A8.meta.js // ==/UserScript== // 立即执行重定向检查,在任何其他代码执行之前 (function() { if (window.location.href.includes('cryptbox.sankuai.com/file/')) { // 阻止页面继续加载 window.stop(); const currentUrl = window.location.href; const newUrl = currentUrl.replace( /^https:\/\/cryptbox\.sankuai\.com\/file\/(.+)$/, 'https://distribute-platform-pub.sankuai.com/distribute/download/v1/$1' ); if (currentUrl !== newUrl) { // 创建一个隐藏的 iframe 来触发下载 const iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = newUrl; document.body.appendChild(iframe); // 等待一段时间后关闭页面 setTimeout(() => { window.close(); }, 1000); // 给予1秒时间让下载开始 return; } } })(); class RequestMonitor { constructor() { if (window._RequestMonitor) { return window._RequestMonitor; } window._RequestMonitor = this; // 初始化状态 this.logs = []; this.filterKeyword = ''; this.initialized = false; this.requestCount = { total: 0, xhr: 0, fetch: 0 }; // 重定向规则 this.redirectRules = [ { pattern: /^https:\/\/cryptbox\.sankuai\.com\/file\/(.+)$/, replacement: 'https://distribute-platform-pub.sankuai.com/distribute/download/v1/$1' } ]; // SVG 图标 this.icons = { copy: ``, success: `` }; // 设置默认状态为折叠 this.isPanelExpanded = false; this.init(); } init() { if (this.initialized) return; this.setupRequestInterceptors(); this.createUI(); this.initialized = true; } // UI 相关方法 createUI() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.renderUI(), { once: true }); } else { this.renderUI(); } } renderUI() { // 创建主容器 const container = this.createMainContainer(); // 创建头部 const header = this.createHeader(); container.appendChild(header); // 创建统计栏 const stats = this.createStatsBar(); container.appendChild(stats); // 创建请求列表 const content = this.createRequestList(); container.appendChild(content); // 创建详情面板 this.createDetailPanel(); // 添加到页面 document.body.appendChild(container); } // 创建主容器 createMainContainer() { const container = document.createElement('div'); container.id = 'requestMonitor'; container.style.cssText = ` position: fixed; top: 20px; right: 20px; width: ${this.isPanelExpanded ? '400px' : '40px'}; height: ${this.isPanelExpanded ? '90vh' : '40px'}; background: rgba(255, 255, 255, 0.95); border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); z-index: 999999; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; backdrop-filter: blur(10px); border: 1px solid rgba(0, 0, 0, 0.1); transition: all 0.3s ease; overflow: hidden; transform-origin: right center; display: ${this.isPanelExpanded ? 'flex' : 'block'}; flex-direction: column; `; // 创建折叠按钮 const toggleButton = document.createElement('div'); toggleButton.style.cssText = ` position: ${this.isPanelExpanded ? 'absolute' : 'relative'}; top: ${this.isPanelExpanded ? '12px' : '0'}; right: ${this.isPanelExpanded ? '12px' : '0'}; width: ${this.isPanelExpanded ? '24px' : '40px'}; height: ${this.isPanelExpanded ? '24px' : '40px'}; background: ${this.isPanelExpanded ? 'rgba(24, 144, 255, 0.1)' : '#1890ff'}; border-radius: ${this.isPanelExpanded ? '4px' : '8px'}; cursor: pointer; display: flex; align-items: center; justify-content: center; color: ${this.isPanelExpanded ? '#1890ff' : 'white'}; font-size: 18px; transition: all 0.3s; z-index: 1; `; toggleButton.innerHTML = this.isPanelExpanded ? '−' : '+'; toggleButton.onmouseover = () => { toggleButton.style.background = this.isPanelExpanded ? 'rgba(24, 144, 255, 0.2)' : 'rgba(24, 144, 255, 0.8)'; }; toggleButton.onmouseout = () => { toggleButton.style.background = this.isPanelExpanded ? 'rgba(24, 144, 255, 0.1)' : '#1890ff'; }; toggleButton.onclick = (e) => { e.stopPropagation(); this.togglePanel(!this.isPanelExpanded); }; container.appendChild(toggleButton); return container; } // 创建头部 createHeader() { const header = document.createElement('div'); header.style.cssText = ` padding: 12px 16px; padding-right: 48px; background: rgba(245, 245, 245, 0.95); border-radius: 8px 8px 0 0; display: flex; justify-content: space-between; align-items: center; cursor: move; user-select: none; border-bottom: 1px solid rgba(0, 0, 0, 0.05); `; // 标题 const title = document.createElement('span'); title.textContent = `${document.title} - 请求监听器`; title.style.cssText = ` font-weight: 600; color: #333; font-size: 14px; `; // 搜索 const filterInput = document.createElement('input'); filterInput.placeholder = '搜索请求...'; filterInput.style.cssText = ` margin-left: 12px; padding: 6px 12px; border: 1px solid rgba(0, 0, 0, 0.1); border-radius: 4px; flex-grow: 1; font-size: 12px; outline: none; transition: all 0.3s; background: rgba(255, 255, 255, 0.9); `; // 添加拖拽功能 this.setupDrag(header); // 添加事件监听 filterInput.oninput = () => { this.filterKeyword = filterInput.value.toLowerCase(); this.updateList(); }; header.appendChild(title); header.appendChild(filterInput); return header; } // 创建统计栏 createStatsBar() { const stats = document.createElement('div'); stats.id = 'requestStats'; stats.style.cssText = ` padding: 8px 16px; background: rgba(250, 250, 250, 0.95); border-bottom: 1px solid rgba(0, 0, 0, 0.05); font-size: 12px; color: #666; display: flex; gap: 16px; `; stats.innerHTML = ` 总请求: 0 XHR: 0 Fetch: 0 `; return stats; } // 创建请求列表 createRequestList() { const content = document.createElement('div'); content.style.cssText = ` flex: 1; overflow-y: auto; display: ${this.isPanelExpanded ? 'block' : 'none'}; background: rgba(255, 255, 255, 0.95); padding: 0; `; const requestList = document.createElement('div'); requestList.id = 'requestList'; content.appendChild(requestList); return content; } // 创建详情面板 createDetailPanel() { const detailPanel = document.createElement('div'); detailPanel.id = 'requestDetail'; detailPanel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 800px; max-height: 80vh; background: rgba(255, 255, 255, 0.98); border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); z-index: 1000000; display: none; overflow: hidden; border: 1px solid rgba(0, 0, 0, 0.1); backdrop-filter: blur(10px); `; const header = document.createElement('div'); header.style.cssText = ` padding: 16px; background: rgba(245, 245, 245, 0.95); border-bottom: 1px solid rgba(0, 0, 0, 0.05); display: flex; justify-content: space-between; align-items: center; `; const title = document.createElement('h3'); title.style.cssText = ` margin: 0; font-size: 16px; color: #333; `; title.textContent = '请求详情'; const closeBtn = document.createElement('button'); closeBtn.textContent = '×'; closeBtn.style.cssText = ` background: none; border: none; font-size: 24px; color: #999; cursor: pointer; padding: 0 8px; line-height: 1; `; const content = document.createElement('div'); content.style.cssText = ` padding: 16px; overflow-y: auto; max-height: calc(80vh - 60px); `; closeBtn.onclick = () => detailPanel.style.display = 'none'; header.appendChild(title); header.appendChild(closeBtn); detailPanel.appendChild(header); detailPanel.appendChild(content); document.body.appendChild(detailPanel); } // 设置拖拽功能 setupDrag(header) { let isDragging = false; let currentX; let currentY; let initialX; let initialY; header.onmousedown = (e) => { isDragging = true; const container = document.getElementById('requestMonitor'); const rect = container.getBoundingClientRect(); initialX = e.clientX - rect.left; initialY = e.clientY - rect.top; }; document.onmousemove = (e) => { if (isDragging) { const container = document.getElementById('requestMonitor'); const viewportWidth = window.innerWidth; // 算新位置 currentX = e.clientX - initialX; currentY = e.clientY - initialY; // 计算距离右侧的距离 const rightDistance = viewportWidth - (currentX + container.offsetWidth); // 更新位置,使用 right 而不是 left container.style.right = `${rightDistance}px`; container.style.top = `${currentY}px`; container.style.left = 'auto'; } }; document.onmouseup = () => isDragging = false; } // 请求拦截相关方法 setupRequestInterceptors() { this.interceptXHR(); this.interceptFetch(); } // XHR拦截实现 interceptXHR() { const originalXHR = XMLHttpRequest.prototype; const originalOpen = originalXHR.open; const originalSend = originalXHR.send; const self = this; originalXHR.open = function(method, url) { if (self.shouldSkipRequest(url)) return; this._requestData = { method, url: url instanceof URL ? url.href : url, status: null, response: null, type: 'xhr', requestData: null, urlParams: self.getUrlParams(url), timestamp: new Date().toLocaleTimeString() }; return originalOpen.apply(this, arguments); }; originalXHR.send = function(data) { if (this._requestData) { if (data) { try { this._requestData.requestData = typeof data === 'string' ? JSON.parse(data) : data; } catch (e) { this._requestData.requestData = data; } } this.addEventListener('load', () => { this._requestData.status = this.status; try { this._requestData.response = this.responseText; } catch (e) { this._requestData.response = '[无法读取响应内容]'; } self.logs.push(this._requestData); self.requestCount.total++; self.requestCount.xhr++; self.updateStats(); self.updateList(); }); } return originalSend.apply(this, arguments); }; } // Fetch拦截实现 interceptFetch() { const originalFetch = window.fetch; const self = this; window.fetch = async function(input, init = {}) { const url = input instanceof Request ? input.url : input; if (self.shouldSkipRequest(url)) return; const method = init.method || (input instanceof Request ? input.method : 'GET'); const logEntry = { method, url: url instanceof URL ? url.href : url, status: null, response: null, type: 'fetch', requestData: init.body || null, urlParams: self.getUrlParams(url), timestamp: new Date().toLocaleTimeString() }; try { const response = await originalFetch.apply(this, arguments); const clone = response.clone(); logEntry.status = clone.status; try { const responseText = await clone.text(); logEntry.response = responseText; } catch (e) { logEntry.response = '[无法读取响应内容]'; } self.logs.push(logEntry); self.requestCount.total++; self.requestCount.fetch++; self.updateStats(); self.updateList(); return response; } catch (error) { logEntry.status = 'ERROR'; logEntry.response = error.message; self.logs.push(logEntry); self.updateStats(); self.updateList(); throw error; } }; } // 更新统计信息 updateStats() { const totalEl = document.getElementById('totalCount'); const xhrEl = document.getElementById('xhrCount'); const fetchEl = document.getElementById('fetchCount'); if (totalEl) totalEl.textContent = this.requestCount.total; if (xhrEl) xhrEl.textContent = this.requestCount.xhr; if (fetchEl) fetchEl.textContent = this.requestCount.fetch; } // 更新请求列表 updateList() { const requestList = document.getElementById('requestList'); if (!requestList) return; const filteredLogs = this.filterKeyword ? this.logs.filter(log => { const searchStr = this.filterKeyword.toLowerCase(); return ( log.url.toLowerCase().includes(searchStr) || (log.response && log.response.toLowerCase().includes(searchStr)) ); }) : this.logs; requestList.innerHTML = ''; filteredLogs.forEach(log => this.createRequestItem(log, requestList)); } // 创建请求列表项 createRequestItem(log, container) { const item = document.createElement('div'); item.style.cssText = ` padding: 12px 16px; border-bottom: 1px solid rgba(0, 0, 0, 0.05); cursor: pointer; font-size: 12px; transition: all 0.2s; position: relative; background: rgba(255, 255, 255, 0.95); `; item.onmouseover = () => { item.style.backgroundColor = 'rgba(24, 144, 255, 0.05)'; }; item.onmouseout = () => { item.style.backgroundColor = 'rgba(255, 255, 255, 0.95)'; }; const statusColor = log.status >= 200 && log.status < 300 ? '#52c41a' : '#f5222d'; item.innerHTML = `