您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
导出阿里巴巴国际站聊天记录为Excel,接收定制和询盘导出明细一起,把聊天记录也一起放在单元格,或者帮你自动从聊天记录筛选出WhatsApp,WeChat,邮箱等联系方式到询盘导出明细的表格
// ==UserScript== // @name 阿里巴巴国际站询盘聊天记录导出-树洞先生 // @namespace http://tampermonkey.net/ // @version 2.0 // @license MPL // @description 导出阿里巴巴国际站聊天记录为Excel,接收定制和询盘导出明细一起,把聊天记录也一起放在单元格,或者帮你自动从聊天记录筛选出WhatsApp,WeChat,邮箱等联系方式到询盘导出明细的表格 // @author YourName // @match https://message.alibaba.com/message/maDetail.htm* // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/xlsx.full.min.js // @grant none // ==/UserScript== (function() { 'use strict'; // 自动捕获 getOpMessages.htm 参数 let cachedPageVars = null; (function() { const origFetch = window.fetch; window.fetch = async function() { const url = arguments[0]; const options = arguments[1] || {}; let bodyStr = options.body; if (bodyStr instanceof FormData) { const obj = {}; for (let pair of bodyStr.entries()) { obj[pair[0]] = pair[1]; } bodyStr = obj; } else if (bodyStr instanceof URLSearchParams) { bodyStr = bodyStr.toString(); } if (typeof url === 'string' && url.includes('getOpMessages.htm') && bodyStr) { let paramsStr = ''; if (typeof bodyStr === 'string') { const match = bodyStr.match(/params=([^&]+)/); if (match) paramsStr = decodeURIComponent(match[1]); } else if (bodyStr.params) { paramsStr = decodeURIComponent(bodyStr.params); } if (paramsStr) { try { const paramsObj = JSON.parse(paramsStr); cachedPageVars = { secOwnerAccountId: paramsObj.secOwnerAccountId || '', secTradeId: paramsObj.secTradeId || '', secTargetAccountId: paramsObj.secTargetAccountId || '', targetAliId: paramsObj.targetAliId || paramsObj.contactAccountIdEncrypt || '', }; } catch (e) {} } } return origFetch.apply(this, arguments); }; })(); // 页面右上角插入按钮 function addExportButton() { if (document.getElementById('alibaba-export-btn')) return; // 导出带翻译按钮 const btn1 = document.createElement('button'); btn1.id = 'alibaba-export-btn'; btn1.innerText = '导出聊天记录(含翻译)-树洞先生'; btn1.style.position = 'fixed'; btn1.style.top = '20px'; btn1.style.right = '20px'; btn1.style.zIndex = 9999; btn1.style.background = '#4CAF50'; btn1.style.color = '#fff'; btn1.style.border = 'none'; btn1.style.padding = '10px 20px'; btn1.style.borderRadius = '5px'; btn1.style.cursor = 'pointer'; btn1.onclick = function() { exportMessages(true); }; document.body.appendChild(btn1); // 导出不带翻译按钮 const btn2 = document.createElement('button'); btn2.id = 'alibaba-export-btn-no-translate'; btn2.innerText = '导出聊天记录(无翻译)-树洞先生'; btn2.style.position = 'fixed'; btn2.style.top = '60px'; btn2.style.right = '20px'; btn2.style.zIndex = 9999; btn2.style.background = '#2196F3'; btn2.style.color = '#fff'; btn2.style.border = 'none'; btn2.style.padding = '10px 20px'; btn2.style.borderRadius = '5px'; btn2.style.cursor = 'pointer'; btn2.onclick = function() { exportMessages(false); }; document.body.appendChild(btn2); } // 获取cookie function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } // 获取页面变量 function getPageVars() { if (cachedPageVars) return cachedPageVars; let secOwnerAccountId = ''; let secTradeId = ''; let secTargetAccountId = ''; let targetAliId = ''; const urlParams = new URLSearchParams(window.location.search); secOwnerAccountId = urlParams.get('secOwnerAccountId') || ''; secTradeId = urlParams.get('secTradeId') || ''; secTargetAccountId = urlParams.get('secTargetAccountId') || ''; targetAliId = urlParams.get('targetAliId') || ''; return { secOwnerAccountId, secTradeId, secTargetAccountId, targetAliId }; } // 清洗内容 function cleanContent(content) { if (!content) return ''; content = content.replace(/<img[^>]*>/g, ''); content = content.replace(/https?:\/\/\S+\.(?:gif|png|jpg|jpeg)/gi, '[图片]'); content = content.replace(/https:\/\/clouddisk\.alibaba\.com\/file\/redirectFileUrl\.htm\?[^"\s>]+/g, '[图片]'); content = content.replace(/'/g, "'"); content = content.replace(/ /g, ' '); return content; } // 主导出逻辑 async function exportMessages(withTranslate) { // 采集进度显示 let progressDiv = document.getElementById('alibaba-export-progress'); if (!progressDiv) { progressDiv = document.createElement('div'); progressDiv.id = 'alibaba-export-progress'; progressDiv.style.position = 'fixed'; progressDiv.style.top = '60px'; progressDiv.style.right = '20px'; progressDiv.style.zIndex = 9999; progressDiv.style.background = '#fff'; progressDiv.style.color = '#333'; progressDiv.style.border = '1px solid #4CAF50'; progressDiv.style.padding = '8px 16px'; progressDiv.style.borderRadius = '5px'; progressDiv.style.boxShadow = '0 2px 8px rgba(0,0,0,0.08)'; progressDiv.innerText = '采集进度...'; document.body.appendChild(progressDiv); } function updateProgress(text) { progressDiv.innerText = text; } const { secOwnerAccountId, secTradeId, secTargetAccountId, targetAliId } = getPageVars(); if (!secOwnerAccountId || !secTradeId || !secTargetAccountId || !targetAliId) { alert('未能自动获取必要参数,请检查页面或手动补充代码!'); return; } const _csrf = getCookie('XSRF-TOKEN'); let all_msgs = []; let has_more = true; let page_size = 100; let time_stamp = null; let page_count = 0; while (has_more) { const params = { scene: "", timeSlide: { forward: true, pageSize: page_size }, secOwnerAccountId, secTradeId, secTargetAccountId, openRealTimeTranslation: true, targetAliId }; if (time_stamp) params.timeSlide.timeStamp = time_stamp; const data = new URLSearchParams(); data.append('_csrf', _csrf); data.append('params', JSON.stringify(params)); // 用 XMLHttpRequest 替代 fetch const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://onetalk.alibaba.com/message/getOpMessages.htm', false); // 同步请求 xhr.setRequestHeader('accept', '*/*'); xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded; charset=UTF-8'); xhr.setRequestHeader('referer', 'https://message.alibaba.com/'); xhr.withCredentials = true; xhr.send(data.toString()); let res_json = {}; try { res_json = JSON.parse(xhr.responseText); } catch (e) { res_json = {}; } page_count++; updateProgress(`采集第${page_count}页,已采集${all_msgs.length}条...`); const msg_list = res_json?.data?.list || []; if (!msg_list.length) break; all_msgs = all_msgs.concat(msg_list); time_stamp = msg_list[msg_list.length - 1].sendTime; if (msg_list.length < page_size) break; await new Promise(r => setTimeout(r, 500)); } updateProgress(`采集完成,共${all_msgs.length}条。正在处理...`); // 去重 const unique = new Set(); const deduped = []; for (const msg of all_msgs) { const msg_id = msg.messageId || (msg.sendTime + '_' + msg.content); if (!unique.has(msg_id)) { unique.add(msg_id); deduped.push(msg); } } // 排序 deduped.sort((a, b) => Number(a.sendTime) - Number(b.sendTime)); // 组装Excel数据 let ws_data; if (withTranslate) { ws_data = [ ['sendTime(时间)', 'role(买家/卖家)', 'name(姓名)', 'content(原文)', 'content(中文翻译)'] ]; } else { ws_data = [ ['sendTime(时间)', 'role(买家/卖家)', 'name(姓名)', 'content(原文)'] ]; } const contents = deduped.map(msg => cleanContent(msg.content || '')); let translations = []; if (withTranslate) { async function batchTranslate(texts, targetLang = 'zh-CN') { const results = []; for (const text of texts) { if (/^[\u4e00-\u9fa5\s\p{P}]+$/u.test(text)) { results.push(text); continue; } try { const res = await fetch(`https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`); const data = await res.json(); results.push(data[0]?.map(i => i[0]).join('') || text); } catch { results.push(text); } await new Promise(r => setTimeout(r, 200)); } return results; } translations = await batchTranslate(contents); } for (let i = 0; i < deduped.length; i++) { const msg = deduped[i]; const role = msg.messageType === 'send' ? '卖家' : '买家'; const name = msg.messageType === 'send' ? (msg.owner?.name || '') : (msg.contact?.name || ''); let ts_str = ''; if (msg.sendTime) { try { const dt = new Date(Number(msg.sendTime)); ts_str = dt.toLocaleString(); } catch { ts_str = msg.sendTime; } } const content = contents[i]; if (withTranslate) { const translation = translations[i]; ws_data.push([ts_str, role, name, content, translation]); } else { ws_data.push([ts_str, role, name, content]); } } const ws = XLSX.utils.aoa_to_sheet(ws_data); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'Messages'); let buyerName = ''; for (let i = 0; i < deduped.length; i++) { const msg = deduped[i]; if (msg.messageType !== 'send' && (msg.contact?.name || '')) { buyerName = msg.contact.name; break; } } if (!buyerName) buyerName = '买家'; function pad(n) { return n < 10 ? '0' + n : n; } const now = new Date(); const timeStr = `${now.getFullYear()}${pad(now.getMonth()+1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}`; const fileName = `${buyerName}_${timeStr}.xlsx`; XLSX.writeFile(wb, fileName); updateProgress('导出完成!-树洞先生'); setTimeout(() => progressDiv.remove(), 3000); } setTimeout(addExportButton, 2000); })();