您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在微博页面添加推送按钮,支持快速复制博文内容、下载图片视频资源并自动发送到自己微博。v1.4新增跨域名数据传递支持,解决www.weibo.com和weibo.com之间的数据同步问题,v1.4.1移除调试信息,代码更加简洁。
// ==UserScript== // @name 微博推送助手 // @namespace http://tampermonkey.net/ // @version 1.4.1 // @description 在微博页面添加推送按钮,支持快速复制博文内容、下载图片视频资源并自动发送到自己微博。v1.4新增跨域名数据传递支持,解决www.weibo.com和weibo.com之间的数据同步问题,v1.4.1移除调试信息,代码更加简洁。 // @author You // @match https://weibo.com/* // @match https://www.weibo.com/* // @match https://m.weibo.cn/* // @grant GM_addStyle // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @grant GM_download // @grant GM_openInTab // @grant GM_setValue // @grant GM_getValue // @connect * // @license MIT // ==/UserScript== (function() { 'use strict'; // 全局变量 let downloadedFiles = []; let currentPostContent = ''; let isProcessing = false; // 添加样式 GM_addStyle(` .weibo-pusher-btn { display: inline-block !important; margin-left: 10px; padding: 4px 12px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 20px; cursor: pointer; font-size: 12px; border: none; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); transition: all 0.3s ease; font-weight: 500; } .weibo-pusher-btn:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); background: linear-gradient(135deg, #764ba2 0%, #667eea 100%); } .weibo-pusher-btn:active { transform: translateY(0); } .weibo-pusher-btn.processing { background: linear-gradient(135deg, #ffa726 0%, #ff7043 100%); cursor: not-allowed; animation: processing-pulse 1.5s ease-in-out infinite; } @keyframes processing-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.8; transform: scale(1.02); } } .weibo-pusher-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); z-index: 10000; display: none; backdrop-filter: blur(5px); } .weibo-pusher-modal-content { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 12px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); max-width: 600px; width: 90%; max-height: 80vh; overflow-y: auto; padding: 0; } .weibo-pusher-modal-header { padding: 20px 24px 16px; border-bottom: 1px solid #f0f0f0; display: flex; justify-content: space-between; align-items: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 12px 12px 0 0; } .weibo-pusher-modal-title { font-size: 18px; font-weight: 600; margin: 0; } .weibo-pusher-modal-close { cursor: pointer; font-size: 24px; color: white; opacity: 0.8; transition: opacity 0.2s; } .weibo-pusher-modal-close:hover { opacity: 1; } .weibo-pusher-modal-body { padding: 24px; } .weibo-pusher-content-preview { background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; padding: 16px; margin-bottom: 20px; max-height: 200px; overflow-y: auto; white-space: pre-wrap; font-size: 14px; line-height: 1.6; } .weibo-pusher-media-list { margin-bottom: 20px; } .weibo-pusher-media-item { display: flex; align-items: center; padding: 12px; border: 1px solid #e9ecef; border-radius: 8px; margin-bottom: 8px; background: white; cursor: pointer; transition: background-color 0.2s ease; } .weibo-pusher-media-item:hover { background: #f8f9fa; } .weibo-pusher-media-item.selected { background: #e7f3ff; border-color: #667eea; } .weibo-pusher-media-preview { width: 60px; height: 60px; object-fit: cover; border-radius: 6px; margin-right: 12px; } .weibo-pusher-media-info { flex: 1; min-width: 0; } .weibo-pusher-media-name { font-size: 14px; font-weight: 500; margin-bottom: 4px; word-break: break-all; } .weibo-pusher-media-size { font-size: 12px; color: #666; } .weibo-pusher-media-status { font-size: 12px; padding: 2px 8px; border-radius: 12px; margin-left: 8px; } .weibo-pusher-media-status.downloading { background: #fff3cd; color: #856404; } .weibo-pusher-media-status.completed { background: #d4edda; color: #155724; } .weibo-pusher-media-status.failed { background: #f8d7da; color: #721c24; } .weibo-pusher-actions { display: flex; gap: 12px; justify-content: flex-end; margin-top: 20px; padding-top: 20px; border-top: 1px solid #f0f0f0; } .weibo-pusher-action-btn { padding: 10px 20px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s; } .weibo-pusher-action-btn.primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; } .weibo-pusher-action-btn.primary:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); } .weibo-pusher-action-btn.secondary { background: #f8f9fa; color: #495057; border: 1px solid #dee2e6; } .weibo-pusher-action-btn.secondary:hover { background: #e9ecef; } .weibo-pusher-action-btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none !important; } .weibo-pusher-progress { margin-top: 16px; padding: 12px; background: #f8f9fa; border-radius: 6px; font-size: 14px; color: #495057; } .weibo-pusher-toast { position: fixed; top: 20px; right: 20px; background: white; border-radius: 8px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); padding: 16px 20px; z-index: 10001; max-width: 400px; transform: translateX(100%); transition: transform 0.3s ease; } .weibo-pusher-toast.show { transform: translateX(0); } .weibo-pusher-toast.success { border-left: 4px solid #28a745; } .weibo-pusher-toast.error { border-left: 4px solid #dc3545; } .weibo-pusher-toast.info { border-left: 4px solid #17a2b8; } `); // 工具函数 function showToast(message, type = 'info', duration = 3000) { const toast = document.createElement('div'); toast.className = `weibo-pusher-toast ${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => toast.classList.add('show'), 100); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => document.body.removeChild(toast), 300); }, duration); } // 保存内容到localStorage和Cookie用于跨页面传递 function saveContentForPaste(content, mediaFileNames = []) { const data = { content: content, mediaFiles: mediaFileNames, timestamp: Date.now() }; try { // 主要使用localStorage localStorage.setItem('weibo_pusher_content', JSON.stringify(data)); // 同时保存到Cookie作为备用(设置为在.weibo.com域下共享) try { const cookieData = encodeURIComponent(JSON.stringify(data)); document.cookie = `weibo_pusher_content=${cookieData}; path=/; domain=.weibo.com; max-age=900`; // 15分钟 } catch (cookieError) { // Cookie保存失败时静默处理,localStorage成功即可 } } catch (error) { console.error('微博推送助手: 保存数据失败', error); } } // 从Cookie获取数据 function getDataFromCookie() { try { const cookies = document.cookie.split(';'); for (let cookie of cookies) { const [name, value] = cookie.trim().split('='); if (name === 'weibo_pusher_content') { const decodedValue = decodeURIComponent(value); return JSON.parse(decodedValue); } } } catch (error) { // 静默处理Cookie读取错误 } return null; } // 获取保存的内容(不清除,只检查) function getSavedContent() { let data = null; try { // 首先尝试从localStorage获取 const saved = localStorage.getItem('weibo_pusher_content'); if (saved) { data = JSON.parse(saved); } else { // 如果localStorage没有数据,尝试从Cookie获取 data = getDataFromCookie(); if (data) { // 将Cookie数据同步到localStorage localStorage.setItem('weibo_pusher_content', JSON.stringify(data)); } } if (data) { const age = Date.now() - data.timestamp; // 检查是否过期(15分钟)- 不删除,只是返回null if (age < 15 * 60 * 1000) { return data; } else { return null; } } } catch (error) { // 静默处理获取数据错误 } return null; } // 清除保存的内容 function clearSavedContent() { localStorage.removeItem('weibo_pusher_content'); // 同时清除Cookie document.cookie = 'weibo_pusher_content=; path=/; domain=.weibo.com; max-age=0'; } // 清理过期数据 function cleanupExpiredData() { try { let hasCleanup = false; // 清理localStorage中的过期数据 const saved = localStorage.getItem('weibo_pusher_content'); if (saved) { const data = JSON.parse(saved); const age = Date.now() - data.timestamp; // 如果数据超过15分钟,清理掉 if (age > 15 * 60 * 1000) { localStorage.removeItem('weibo_pusher_content'); hasCleanup = true; } } // 清理Cookie中的过期数据 const cookieData = getDataFromCookie(); if (cookieData) { const age = Date.now() - cookieData.timestamp; if (age > 15 * 60 * 1000) { document.cookie = 'weibo_pusher_content=; path=/; domain=.weibo.com; max-age=0'; hasCleanup = true; } } return hasCleanup; } catch (error) { // 静默处理清理错误 } return false; } // 获取并清除保存的内容(兼容原有功能) function getAndClearSavedContent() { const data = getSavedContent(); if (data) { clearSavedContent(); } return data; } // 自动粘贴功能 function tryAutoPaste() { const savedData = getSavedContent(); // 先不清除数据 if (!savedData) { return false; } // 无论是否找到发博框,都先复制到剪贴板作为备份 GM_setClipboard(savedData.content); // 查找微博主页的发博框 - 扩展选择器列表 const composeSelectors = [ // 2024年最新版微博发博框 '.Form_input_2gtXx', 'textarea.Form_input_2gtXx', '.wbpro-form textarea', '.Form_wbproform_3UDoi textarea', 'div[contenteditable="true"]', '.woo-editable-text[contenteditable="true"]', // 通用contenteditable选择器(优先级更高) '[contenteditable="true"]:not([role="textbox"]):not(.woo-comment-input)', '[contenteditable="true"][placeholder*="想说"]', '[contenteditable="true"][placeholder*="分享"]', '[contenteditable="true"][placeholder*="新鲜事"]', // 新版微博发博框 '.woo-box-flex textarea', '.woo-input-main textarea', '.woo-input-wrap textarea', '.woo-editable-text textarea', '.Feed_publish textarea', '.publish-content textarea', '.composer-input textarea', // 常见的textarea选择器 'textarea[placeholder*="分享"]', 'textarea[placeholder*="想说"]', 'textarea[placeholder*="新鲜事"]', 'textarea[placeholder*="有什么新鲜事"]', 'textarea[placeholder*="想法"]', 'textarea[placeholder*="今天"]', '.CommentInput_wrap_3s1XL textarea', '.publisherTextarea', // 主页发博区域 '.woo-publish-main [contenteditable="true"]', '.publish-main [contenteditable="true"]', '.woo-publish-wrap [contenteditable="true"]', '.publish-wrap [contenteditable="true"]', '.composer-wrap [contenteditable="true"]', // 通用发博区域 '.publish-box textarea', '.composer-box textarea', '.feed-publish textarea', '#Publisher textarea', '.wb-publisher textarea', '.wb-compose textarea', '.weibo-composer textarea', // 移动端适配 '.m-publish textarea', '.mobile-composer textarea' ]; let composeBox = null; let foundSelector = ''; for (const selector of composeSelectors) { try { composeBox = document.querySelector(selector); if (composeBox) { foundSelector = selector; break; } } catch (error) { // 静默处理选择器错误 } } if (composeBox) { // 设置内容 try { if (composeBox.tagName.toLowerCase() === 'textarea') { composeBox.value = savedData.content; composeBox.dispatchEvent(new Event('input', { bubbles: true })); composeBox.dispatchEvent(new Event('change', { bubbles: true })); composeBox.dispatchEvent(new Event('focus', { bubbles: true })); } else if (composeBox.getAttribute('contenteditable') === 'true') { // 对于contenteditable元素,将换行转换为<br>标签 const htmlContent = savedData.content.replace(/\n/g, '<br>'); composeBox.innerHTML = htmlContent; composeBox.dispatchEvent(new Event('input', { bubbles: true })); composeBox.dispatchEvent(new Event('change', { bubbles: true })); composeBox.dispatchEvent(new Event('focus', { bubbles: true })); // 将光标定位到末尾 const range = document.createRange(); const selection = window.getSelection(); range.selectNodeContents(composeBox); range.collapse(false); selection.removeAllRanges(); selection.addRange(range); } else { composeBox.textContent = savedData.content; composeBox.dispatchEvent(new Event('input', { bubbles: true })); composeBox.dispatchEvent(new Event('change', { bubbles: true })); composeBox.dispatchEvent(new Event('focus', { bubbles: true })); } // 聚焦到发博框 composeBox.focus(); // 成功粘贴后清除数据 clearSavedContent(); let message = '✅ 内容已自动填入发博框并复制到剪贴板!'; if (savedData.mediaFiles && savedData.mediaFiles.length > 0) { message += `\n📁 请手动上传 ${savedData.mediaFiles.length} 个媒体文件:\n${savedData.mediaFiles.join(', ')}`; } showToast(message, 'success', 5000); return true; // 表示成功 } catch (error) { // 静默处理设置内容错误 } } else { let message = '⚠️ 未找到发博框,内容已复制到剪贴板'; if (savedData.mediaFiles && savedData.mediaFiles.length > 0) { message += `\n📁 需要上传 ${savedData.mediaFiles.length} 个文件:\n${savedData.mediaFiles.join(', ')}`; } showToast(message, 'info', 5000); } return false; // 表示未成功粘贴 } // 通过文本内容查找元素的辅助函数 function findElementsByText(parentElement, searchText) { if (!parentElement) return []; const result = []; const allElements = parentElement.querySelectorAll('*'); allElements.forEach(element => { if (element.textContent && element.textContent.includes(searchText)) { result.push(element); } }); return result; } // 检查并展开折叠内容 async function expandContent(element) { if (!element) return false; // 可能的展开按钮选择器 const expandSelectors = [ // 新版微博 'button.woo-button-flat.woo-button-s[class*="ContentMore"]', 'button.woo-button-flat.woo-button-s.ContentMore', 'button[class*="expand"]', 'button[class*="Expand"]', 'div[class*="expand"]', 'a[class*="expand"]', // 旧版微博 'a[action-type="fl_unfold"]', '.WB_text_opt', '.WB_text a[onclick*="unfold"]', // 特殊的span展开按钮 'span.expand', '.expand' ]; // 搜索展开按钮 let expandButton = null; // 首先尝试使用标准选择器 for (const selector of expandSelectors) { try { expandButton = element.querySelector(selector) || element.parentNode.querySelector(selector); if (expandButton) { break; } } catch (error) { console.error('微博推送助手: 选择器错误', selector, error); } } // 如果没找到,尝试通过文本内容查找 if (!expandButton) { // 查找包含"展开"或"展开全文"的元素 const textButtons = [ ...findElementsByText(element, '展开'), ...findElementsByText(element, '展开全文') ]; // 筛选可能的按钮元素,包括span元素 const possibleButtons = textButtons.filter(el => { const tagName = el.tagName.toLowerCase(); return tagName === 'button' || tagName === 'a' || tagName === 'span' || el.role === 'button' || el.getAttribute('role') === 'button' || el.classList.contains('expand') || // 特别处理展开类 el.onclick || el.getAttribute('onclick'); }); if (possibleButtons.length > 0) { expandButton = possibleButtons[0]; } } // 如果找到展开按钮,点击它 if (expandButton) { try { // 对于span.expand元素尝试多种点击方式 if (expandButton.tagName.toLowerCase() === 'span' && expandButton.classList.contains('expand')) { // 方法1: 直接点击 expandButton.click(); // 方法2: 创建点击事件 const clickEvent = document.createEvent('MouseEvents'); clickEvent.initEvent('click', true, true); expandButton.dispatchEvent(clickEvent); // 方法3: 尝试点击父元素 if (expandButton.parentElement) { expandButton.parentElement.click(); } // 方法4: 尝试移除展开元素并修改父元素样式以显示全部内容 const parentEl = expandButton.parentElement; if (parentEl && parentEl.classList.contains('detail_wbtext_4CRf9')) { // 保存原始内容但移除展开按钮 const fullText = parentEl.innerHTML.replace(/<span class="expand">展开<\/span>/, ''); parentEl.innerHTML = fullText; // 移除可能的最大高度限制 parentEl.style.maxHeight = 'none'; parentEl.style.overflow = 'visible'; } } else { // 正常点击 expandButton.click(); } // 等待内容展开 await new Promise(resolve => setTimeout(resolve, 800)); // 增加等待时间,确保内容展开 return true; } catch (error) { console.error('微博推送助手: 展开内容时出错', error); } } return false; } // 提取博文内容 async function extractPostContent(postElement) { const contentSelectors = [ // 原有的选择器 '.detail_wbtext_4CRf9', '.wbpro-feed-content .detail_text_1U10O', '.wbpro-feed-content', '.Feed_body_3R0rO .Feed_content_2YeHn', '.WB_text', '.WB_detail .WB_text', '[node-type="feed_list_content"]', '.weibo-text', '.m-text-box', // 新增对微博详情页内容的支持 '.WB_detail_warp .WB_text', '.WB_detail_wrap .WB_text', '.detail_main_3PZZf .WB_text', '.detail_card_main_4fhCu .WB_text', '.detail_body_1Dpoa .WB_text', '.detail_info_4spYr .WB_text', '.WB_feed_type .WB_text', '.wb-detail .WB_text', '.wb-item .WB_text', // 新版详情页选择器 '.Feed_main_1b1q9 .WB_text', '.Feed_detail_1b1q9 .WB_text', '.WBDetail_main_3u .WB_text', '.WBDetail_wrap_3u .WB_text', '.wbpro-detail-content .WB_text', '.wbpro-detail-wrapper .WB_text', // 通用详情页内容选择器 '.WB_detail .WB_text', '.detail-content', '.weibo-detail-content', '.post-content .WB_text', '.wb-content', '.detail_text', '.woo-detail-content', '.woo-detail-text' ]; let contentElement = null; for (const selector of contentSelectors) { contentElement = postElement.querySelector(selector); if (contentElement) break; } if (!contentElement) return ''; // 先尝试展开折叠内容 try { await expandContent(contentElement); } catch (error) { console.error('微博推送助手: 展开内容时出错', error); // 继续处理,即使展开失败,也尝试获取已有内容 } // 创建一个副本来处理内容,避免修改原始DOM const tempContainer = document.createElement('div'); tempContainer.innerHTML = contentElement.innerHTML; // 移除脚本添加的按钮 const scriptButtons = tempContainer.querySelectorAll('.weibo-pusher-btn, .copy-btn, .extract-links-btn, .copy-format-btn'); scriptButtons.forEach(btn => { if (btn.parentNode) { btn.parentNode.removeChild(btn); } }); // 移除"收起"按钮和相关元素 const hideElements = tempContainer.querySelectorAll([ 'a.woo-box-flex[role="button"]', // 新版收起按钮 'button[content="收起"]', // 收起按钮 'a[action-type="fl_fold"]', // 旧版收起按钮 '.WB_text_opt_fold', // 旧版收起区域 '.content_opt_fold', // 收起选项区域 'span.expand' // 展开按钮 ].join(', ')); hideElements.forEach(el => { if (el.parentNode) { el.parentNode.removeChild(el); } }); // 单独处理包含"收起"、"展开"文本的元素 const hideSpans = [ ...findElementsByText(tempContainer, '收起'), ...findElementsByText(tempContainer, '展开'), ...findElementsByText(tempContainer, '展开全文') ]; hideSpans.forEach(span => { const text = span.textContent.trim(); if (text === '收起' || text === '展开' || text === '展开全文') { if (span.parentNode) { span.parentNode.removeChild(span); } } }); // 改进的文本提取方法,更好地保留换行格式 function extractTextWithLineBreaks(element) { let text = ''; function processNode(node) { if (node.nodeType === Node.TEXT_NODE) { // 文本节点,直接添加内容 text += node.textContent; } else if (node.nodeType === Node.ELEMENT_NODE) { const tagName = node.tagName.toLowerCase(); // 在块级元素前后添加换行 const blockElements = ['div', 'p', 'br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li']; const isBlockElement = blockElements.includes(tagName); if (isBlockElement && text && !text.endsWith('\n')) { text += '\n'; } // 处理特殊的br标签 if (tagName === 'br') { text += '\n'; return; } // 递归处理子节点 for (let child of node.childNodes) { processNode(child); } // 在块级元素后添加换行 if (isBlockElement && text && !text.endsWith('\n')) { text += '\n'; } } } processNode(element); return text; } // 使用改进的方法提取文本 let text = extractTextWithLineBreaks(tempContainer); // 清理内容 text = text.replace(/\s*展开全文\s*/g, '') .replace(/\s*收起全文\s*/g, '') .replace(/\s*全文\s*/g, '') .replace(/\s*展开\s*/g, '') .replace(/\s*收起\s*/g, '') .replace(/📤\s*推送到我的微博\s*/g, '') // 移除脚本按钮文本 .replace(/[\u200B-\u200D\uFEFF]/g, '') // 移除零宽字符 .replace(/\n{3,}/g, '\n\n') // 合并多余换行 .replace(/^\s+|\s+$/g, '') // 移除首尾空白 .replace(/[ \t]+\n/g, '\n') // 移除行尾空格 .replace(/\n[ \t]+/g, '\n'); // 移除行首空格 return text; } // 提取媒体资源 function extractMediaResources(postElement) { const mediaList = []; // 生成时间戳前缀,便于用户识别下载的文件 const timestamp = new Date().toISOString().slice(0, 16).replace(/[-:T]/g, ''); const prefix = `微博推送_${timestamp}_`; // 首先尝试找到博文内容区域,限制搜索范围 const contentSelectors = [ // 原有的选择器 '.detail_wbtext_4CRf9', '.wbpro-feed-content .detail_text_1U10O', '.wbpro-feed-content', '.Feed_body_3R0rO .Feed_content_2YeHn', '.WB_text', '.WB_detail .WB_text', '[node-type="feed_list_content"]', '.weibo-text', '.m-text-box', // 新增对微博详情页的支持 '.WB_detail_warp .WB_text', '.WB_detail_wrap .WB_text', '.detail_main_3PZZf .WB_text', '.detail_card_main_4fhCu .WB_text', '.detail_body_1Dpoa .WB_text', '.detail_info_4spYr .WB_text', '.WB_feed_type .WB_text', '.wb-detail .WB_text', '.wb-item .WB_text', // 新版详情页选择器 '.Feed_main_1b1q9 .WB_text', '.Feed_detail_1b1q9 .WB_text', '.WBDetail_main_3u .WB_text', '.WBDetail_wrap_3u .WB_text', '.wbpro-detail-content .WB_text', '.wbpro-detail-wrapper .WB_text', // 通用媒体容器 '.detail-content', '.weibo-detail-content', '.post-content', '.wb-content' ]; // 查找媒体容器(通常在内容区域的同级或父级) const mediaContainerSelectors = [ // 原有的选择器 '.wbpro-feed-content', '.Feed_body_3R0rO', '.WB_detail', '.WB_feed_detail', '.WB_cardwrap', '[node-type="feed_list_item"]', // 新增对微博详情页的支持 '.WB_detail_warp', '.WB_detail_wrap', '.detail_main_3PZZf', '.detail_card_main_4fhCu', '.detail_body_1Dpoa', '.detail_info_4spYr', '.WB_feed_type', '.wb-detail', '.wb-item', // 新版详情页媒体容器 '.Feed_main_1b1q9', '.Feed_detail_1b1q9', '.WBDetail_main_3u', '.WBDetail_wrap_3u', '.wbpro-detail-content', '.wbpro-detail-wrapper', // 通用媒体容器 '.detail-content', '.weibo-detail-content', '.post-content', '.wb-content' ]; let searchContainer = postElement; // 尝试找到更精确的搜索容器 for (const selector of mediaContainerSelectors) { const container = postElement.querySelector(selector); if (container) { searchContainer = container; break; } } // 如果没找到专门的媒体容器,尝试找到内容区域作为搜索范围 if (searchContainer === postElement) { for (const selector of contentSelectors) { const contentArea = postElement.querySelector(selector); if (contentArea && contentArea.parentElement) { searchContainer = contentArea.parentElement; break; } } } // 提取图片 - 使用更精确的选择器,排除头像等 const imageSelectors = [ 'img[src*="sinaimg.cn"][src*="large"]', // 优先高清图片 'img[src*="sinaimg.cn"][src*="bmiddle"]', // 中等尺寸图片 'img[src*="sinaimg.cn"][src*="orj360"]', // 原图 'img[src*="sinaimg.cn"]:not([src*="avatar"]):not([src*="head"])', // 排除头像 'img[src*="weibo"]:not([src*="avatar"]):not([src*="head"])', // 微博图片,排除头像 '.WB_pic img', '.media-pic img', '.picture img', '.woo-picture-img' ]; let images = []; for (const selector of imageSelectors) { const foundImages = searchContainer.querySelectorAll(selector); if (foundImages.length > 0) { images = [...foundImages]; break; } } images.forEach((img, index) => { let src = img.src; // 跳过明显不是博文内容的图片 if (src.includes('avatar') || src.includes('head') || src.includes('icon') || src.includes('emoji') || img.width < 50 || img.height < 50) { return; } // 获取高清图片链接 if (src.includes('thumbnail') || src.includes('bmiddle') || src.includes('orj360')) { src = src.replace(/\/thumbnail\/|\/bmiddle\/|\/orj360\//g, '/large/'); } if (src.includes('?')) { src = src.split('?')[0]; } mediaList.push({ type: 'image', url: src, name: `${prefix}image_${index + 1}.jpg`, element: img }); }); // 提取视频 - 限制在搜索容器内 const videos = searchContainer.querySelectorAll('video, .WB_video video, .media-video video'); videos.forEach((video, index) => { let src = video.src || video.querySelector('source')?.src; if (src) { mediaList.push({ type: 'video', url: src, name: `${prefix}video_${index + 1}.mp4`, element: video }); } }); // 提取视频链接(通过data属性)- 限制在搜索容器内 const videoContainers = searchContainer.querySelectorAll('[video-sources], [action-data*="video"]'); videoContainers.forEach((container, index) => { const videoData = container.getAttribute('video-sources') || container.getAttribute('action-data'); if (videoData) { try { const data = JSON.parse(videoData); if (data.mp4_hd_url || data.mp4_url) { mediaList.push({ type: 'video', url: data.mp4_hd_url || data.mp4_url, name: `${prefix}video_${mediaList.filter(m => m.type === 'video').length + 1}.mp4`, element: container }); } } catch (e) { // 解析视频数据失败,跳过 } } }); return mediaList; } // 下载媒体文件 function downloadMedia(mediaItem, onProgress) { return new Promise((resolve, reject) => { onProgress('downloading'); GM_xmlhttpRequest({ method: 'GET', url: mediaItem.url, responseType: 'blob', headers: { 'Referer': 'https://weibo.com/', 'User-Agent': navigator.userAgent }, onload: function(response) { if (response.status === 200) { const blob = response.response; const url = URL.createObjectURL(blob); // 使用GM_download下载文件 GM_download(url, mediaItem.name, url); onProgress('completed'); resolve({ ...mediaItem, blob: blob, localUrl: url }); } else { onProgress('failed'); reject(new Error(`下载失败: ${response.status}`)); } }, onerror: function(error) { onProgress('failed'); reject(error); } }); }); } // 创建推送模态框 function createPushModal(content, mediaList) { const modal = document.createElement('div'); modal.className = 'weibo-pusher-modal'; modal.innerHTML = ` <div class="weibo-pusher-modal-content"> <div class="weibo-pusher-modal-header"> <h3 class="weibo-pusher-modal-title">微博推送助手</h3> <span class="weibo-pusher-modal-close">×</span> </div> <div class="weibo-pusher-modal-body"> <h4>博文内容预览:</h4> <div class="weibo-pusher-content-preview">${content}</div> <h4>媒体资源 (${mediaList.length}个): ${mediaList.length > 0 ? `<label style="font-weight: normal; font-size: 14px; margin-left: 10px;"> <input type="checkbox" id="selectAllMedia" checked> 全选 </label>` : ''} </h4> <div class="weibo-pusher-media-list"> ${mediaList.map((media, index) => ` <div class="weibo-pusher-media-item" data-index="${index}"> <label style="margin-right: 10px;"> <input type="checkbox" class="media-checkbox" data-index="${index}" checked> </label> ${media.type === 'image' ? `<img class="weibo-pusher-media-preview" src="${media.url}" alt="预览">` : `<div class="weibo-pusher-media-preview" style="background: #f0f0f0; display: flex; align-items: center; justify-content: center; color: #666;">📹</div>` } <div class="weibo-pusher-media-info"> <div class="weibo-pusher-media-name">${media.name}</div> <div class="weibo-pusher-media-size">${media.type === 'image' ? '图片' : '视频'}</div> </div> <div class="weibo-pusher-media-status" data-status="pending">等待下载</div> </div> `).join('')} </div> <div class="weibo-pusher-actions"> <button class="weibo-pusher-action-btn secondary" id="copyContentBtn">仅复制内容</button> <button class="weibo-pusher-action-btn primary" id="downloadAndPushBtn">下载选中资源并推送</button> </div> <div class="weibo-pusher-progress" id="progressInfo" style="display: none;"></div> </div> </div> `; document.body.appendChild(modal); // 绑定事件 const closeBtn = modal.querySelector('.weibo-pusher-modal-close'); const copyBtn = modal.querySelector('#copyContentBtn'); const downloadBtn = modal.querySelector('#downloadAndPushBtn'); const progressInfo = modal.querySelector('#progressInfo'); const selectAllCheckbox = modal.querySelector('#selectAllMedia'); const mediaCheckboxes = modal.querySelectorAll('.media-checkbox'); // 全选/取消全选功能 if (selectAllCheckbox) { selectAllCheckbox.onchange = () => { const isChecked = selectAllCheckbox.checked; mediaCheckboxes.forEach(checkbox => { checkbox.checked = isChecked; }); updateDownloadButtonText(); updateMediaItemStyles(); }; } // 单个媒体复选框变化时更新全选状态 mediaCheckboxes.forEach(checkbox => { checkbox.onchange = () => { const checkedCount = Array.from(mediaCheckboxes).filter(cb => cb.checked).length; if (selectAllCheckbox) { selectAllCheckbox.checked = checkedCount === mediaCheckboxes.length; selectAllCheckbox.indeterminate = checkedCount > 0 && checkedCount < mediaCheckboxes.length; } updateDownloadButtonText(); updateMediaItemStyles(); }; }); // 添加整行点击功能 const mediaItems = modal.querySelectorAll('.weibo-pusher-media-item'); mediaItems.forEach((item, index) => { item.onclick = (e) => { // 如果点击的是checkbox本身,不处理(避免重复触发) if (e.target.type === 'checkbox') return; const checkbox = item.querySelector('.media-checkbox'); if (checkbox) { checkbox.checked = !checkbox.checked; checkbox.dispatchEvent(new Event('change')); } }; }); // 更新媒体项的视觉状态 function updateMediaItemStyles() { mediaItems.forEach((item, index) => { const checkbox = item.querySelector('.media-checkbox'); if (checkbox && checkbox.checked) { item.classList.add('selected'); } else { item.classList.remove('selected'); } }); } // 初始化媒体项样式 updateMediaItemStyles(); // 更新下载按钮文本 function updateDownloadButtonText() { const checkedCount = Array.from(mediaCheckboxes).filter(cb => cb.checked).length; if (checkedCount === 0) { downloadBtn.textContent = '仅推送文本内容'; } else { downloadBtn.textContent = `下载选中资源(${checkedCount}个)并推送`; } } // 初始化按钮文本 updateDownloadButtonText(); closeBtn.onclick = () => { document.body.removeChild(modal); }; modal.onclick = (e) => { if (e.target === modal) { document.body.removeChild(modal); } }; copyBtn.onclick = () => { GM_setClipboard(content); showToast('内容已复制到剪贴板', 'success'); }; downloadBtn.onclick = async () => { // 获取选中的媒体 const selectedMedia = mediaList.filter((_, index) => { const checkbox = modal.querySelector(`.media-checkbox[data-index="${index}"]`); return checkbox && checkbox.checked; }); if (selectedMedia.length === 0) { // 如果没有选中媒体,保存内容并打开发博页面 saveContentForPaste(content, []); // 使用当前域名跳转,确保localStorage可访问 const targetUrl = window.location.hostname === 'www.weibo.com' ? 'https://www.weibo.com/' : 'https://weibo.com/'; GM_openInTab(targetUrl, { active: true }); showToast('📋 将为您打开微博主页,内容会自动填入并复制到剪贴板', 'success'); document.body.removeChild(modal); return; } downloadBtn.disabled = true; downloadBtn.textContent = '下载中...'; progressInfo.style.display = 'block'; progressInfo.textContent = '开始下载选中的媒体资源...'; try { downloadedFiles = []; const downloadedFileNames = []; for (let i = 0; i < selectedMedia.length; i++) { const media = selectedMedia[i]; const originalIndex = mediaList.indexOf(media); const statusElement = modal.querySelector(`[data-index="${originalIndex}"] .weibo-pusher-media-status`); progressInfo.textContent = `正在下载 ${i + 1}/${selectedMedia.length}: ${media.name}`; try { const downloadedFile = await downloadMedia(media, (status) => { statusElement.textContent = status === 'downloading' ? '下载中...' : status === 'completed' ? '已完成' : '下载失败'; statusElement.className = `weibo-pusher-media-status ${status}`; }); downloadedFiles.push(downloadedFile); downloadedFileNames.push(downloadedFile.name); } catch (error) { console.error('下载失败:', error); statusElement.textContent = '下载失败'; statusElement.className = 'weibo-pusher-media-status failed'; } } progressInfo.textContent = `下载完成!已下载 ${downloadedFiles.length}/${selectedMedia.length} 个文件。正在跳转到微博主页...`; // 保存内容和文件名列表,用于自动粘贴 saveContentForPaste(content, downloadedFileNames); // 打开微博发布页面 setTimeout(() => { // 使用当前域名跳转,确保localStorage可访问 const targetUrl = window.location.hostname === 'www.weibo.com' ? 'https://www.weibo.com/' : 'https://weibo.com/'; GM_openInTab(targetUrl, { active: true }); showToast(`📋 下载完成!内容会自动填入并复制到剪贴板,请手动上传 ${downloadedFiles.length} 个文件`, 'success', 6000); document.body.removeChild(modal); }, 1000); } catch (error) { console.error('下载过程出错:', error); progressInfo.textContent = '下载过程中出现错误,请重试。'; showToast('下载失败,请重试', 'error'); } finally { downloadBtn.disabled = false; updateDownloadButtonText(); } }; modal.style.display = 'block'; return modal; } // 处理推送 async function handlePush(postElement) { if (isProcessing) { showToast('正在处理中,请稍候...', 'info'); return; } // 在开始新的推送前,清理可能的过期数据 cleanupExpiredData(); isProcessing = true; // 找到当前的推送按钮并更新状态 const pushBtn = postElement.querySelector('.weibo-pusher-btn'); const originalText = pushBtn ? pushBtn.textContent : ''; try { // 更新按钮状态 if (pushBtn) { pushBtn.textContent = '正在提取内容...'; pushBtn.disabled = true; pushBtn.classList.add('processing'); } // 提取内容和媒体 const content = await extractPostContent(postElement); const mediaList = extractMediaResources(postElement); if (!content && mediaList.length === 0) { showToast('未找到可推送的内容', 'error'); return; } currentPostContent = content; // 显示推送模态框 createPushModal(content, mediaList); } catch (error) { console.error('处理推送时出错:', error); showToast('处理失败,请重试', 'error'); } finally { // 恢复按钮状态 if (pushBtn) { pushBtn.textContent = originalText; pushBtn.disabled = false; pushBtn.classList.remove('processing'); } isProcessing = false; } } // 添加推送按钮 function addPushButton(postElement) { // 避免重复添加 if (postElement.querySelector('.weibo-pusher-btn')) { return; } // 查找合适的位置插入按钮 const buttonContainers = [ // 原有的选择器 '.detail_wbtext_4CRf9', '.wbpro-feed-content', '.detail_text_1U10O', '.Feed_body_3R0rO .Feed_content_2YeHn', '.WB_detail .WB_text', '.WB_text', '[node-type="feed_list_content"]', '.weibo-text', // 新增对微博详情页的支持 '.WB_detail_warp .WB_text', '.WB_detail_wrap .WB_text', '.detail_main_3PZZf .WB_text', '.detail_card_main_4fhCu .WB_text', '.detail_body_1Dpoa .WB_text', '.detail_info_4spYr .WB_text', '.WB_feed_type .WB_text', '.wb-detail .WB_text', '.wb-item .WB_text', // 新版详情页选择器 '.Feed_main_1b1q9 .WB_text', '.Feed_detail_1b1q9 .WB_text', '.WBDetail_main_3u .WB_text', '.WBDetail_wrap_3u .WB_text', '.wbpro-detail-content .WB_text', '.wbpro-detail-wrapper .WB_text', // 通用详情页按钮容器 '.detail-content', '.weibo-detail-content', '.post-content .WB_text', '.wb-content', '.detail_text', '.woo-detail-content', '.woo-detail-text' ]; let targetContainer = null; for (const selector of buttonContainers) { targetContainer = postElement.querySelector(selector); if (targetContainer) { break; } } if (!targetContainer) { return; } // 创建推送按钮 const pushBtn = document.createElement('button'); pushBtn.className = 'weibo-pusher-btn'; pushBtn.textContent = '📤 推送到我的微博'; pushBtn.title = '复制内容并下载媒体资源,然后推送到自己的微博'; pushBtn.onclick = async (e) => { e.preventDefault(); e.stopPropagation(); await handlePush(postElement); }; // 插入按钮 targetContainer.appendChild(pushBtn); } // 扫描页面并添加按钮 function scanAndAddButtons() { const postSelectors = [ // 原有的选择器 'article.Feed_wrap_3v9LH', '.Feed_body_3R0rO', '.wbpro-feed-content', '.WB_detail', '.WB_cardwrap', '[node-type="feed_list_item"]', '.weibo-item', // 新增对微博详情页的支持 '.WB_detail_warp', '.WB_detail_wrap', '.detail_main_3PZZf', '.detail_card_main_4fhCu', '.detail_body_1Dpoa', '.detail_info_4spYr', '.WB_feed_type .WB_detail', '.WB_feed_type .WB_cardwrap', '.WB_feed_type', '.wb-item', '.wb-detail', // 对新版详情页的支持 '.Feed_main_1b1q9', '.Feed_detail_1b1q9', '.WBDetail_main_3u', '.WBDetail_wrap_3u', '.wbpro-detail-wrapper', '.wbpro-detail-content', // 通用容器选择器 '[data-pid]', '[mid]', '[post-id]', '.post-detail', '.weibo-detail' ]; postSelectors.forEach(selector => { const posts = document.querySelectorAll(selector); posts.forEach((post, index) => { addPushButton(post); }); }); } // 监听页面变化 function startObserver() { const observer = new MutationObserver((mutations) => { let shouldScan = false; mutations.forEach(mutation => { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { shouldScan = true; } }); if (shouldScan) { setTimeout(scanAndAddButtons, 500); } }); observer.observe(document.body, { childList: true, subtree: true }); } // 初始化 function init() { // 检查是否在微博主页,如果是则尝试自动粘贴 if ((window.location.hostname === 'weibo.com' || window.location.hostname === 'www.weibo.com') && window.location.pathname === '/') { // 延迟检测发博框,确保页面完全加载 setTimeout(() => { tryAutoPaste(); }, 2000); } // 等待页面加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { scanAndAddButtons(); startObserver(); }, 1000); }); } else { setTimeout(() => { scanAndAddButtons(); startObserver(); }, 1000); } // 定期扫描 setInterval(scanAndAddButtons, 3000); // 如果在主页,检查是否有待粘贴的内容并自动粘贴 if ((window.location.hostname === 'weibo.com' || window.location.hostname === 'www.weibo.com') && window.location.pathname === '/') { // 等待页面加载完成后,检查一次是否有待粘贴内容 setTimeout(() => { const savedData = getSavedContent(); if (savedData) { const success = tryAutoPaste(); if (!success) { // 如果第一次失败,等待3秒后再试一次 setTimeout(() => { tryAutoPaste(); }, 3000); } } }, 3000); // 等待3秒确保页面完全加载 } } // 启动脚本 init(); })();