您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Manage chats across different platforms (GitHub Copilot, Flomo, Doubao)
当前为
// ==UserScript== // @name Multi-site Chat Manager // @namespace http://tampermonkey.net/ // @version 0.2 // @description Manage chats across different platforms (GitHub Copilot, Flomo, Doubao) // @author Your name // @match https://github.com/copilot* // @match https://v.flomoapp.com/mine // @match https://www.doubao.com/chat/thread/list* // @icon https://v.flomoapp.com/favicon.ico // @grant none // ==/UserScript== (function () { 'use strict'; // Common styles for buttons const buttonStyles = { base: { padding: '8px 16px', border: 'none', borderRadius: '6px', cursor: 'pointer', color: 'white', fontSize: '14px', fontWeight: '500', boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)', transition: 'all 0.3s ease', margin: '5px' }, green: { backgroundColor: '#2ea44f', '&:hover': { backgroundColor: '#2c974b' } }, red: { backgroundColor: '#d73a49', '&:hover': { backgroundColor: '#cb2431' } } }; // Apply styles to button function applyButtonStyles(button, type = 'base') { Object.assign(button.style, buttonStyles.base); if (type === 'green') { Object.assign(button.style, buttonStyles.green); } else if (type === 'red') { Object.assign(button.style, buttonStyles.red); } // Add hover effect button.addEventListener('mouseenter', () => { button.style.transform = 'translateY(-1px)'; button.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)'; }); button.addEventListener('mouseleave', () => { button.style.transform = 'translateY(0)'; button.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)'; }); } // Site configurations const siteConfigs = { 'github.com': { init: function () { this.waitForChatList(); }, waitForChatList: function () { const observer = new MutationObserver((mutations, obs) => { if (document.querySelector('.ConversationList-module__ConversationList__item--dD6z4')) { this.addButtons(); obs.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); }, addButtons: function () { const buttonContainer = document.createElement('div'); buttonContainer.style.position = 'fixed'; buttonContainer.style.bottom = '20px'; buttonContainer.style.left = '20px'; buttonContainer.style.zIndex = '9999'; buttonContainer.style.display = 'flex'; buttonContainer.style.flexDirection = 'column'; buttonContainer.style.gap = '10px'; const openChatsButton = document.createElement('button'); openChatsButton.textContent = '打开chat'; applyButtonStyles(openChatsButton, 'green'); const clearChatsButton = document.createElement('button'); clearChatsButton.textContent = '清空chat'; applyButtonStyles(clearChatsButton, 'red'); buttonContainer.appendChild(openChatsButton); buttonContainer.appendChild(clearChatsButton); document.body.appendChild(buttonContainer); openChatsButton.addEventListener('click', this.openAllChats); clearChatsButton.addEventListener('click', this.clearAllChats); }, openAllChats: function () { const chatLinks = document.querySelectorAll('.ConversationList-module__ConversationList__link--Byc2c'); chatLinks.forEach(link => { const newWindow = window.open(link.href, '_blank'); if (newWindow) { newWindow.addEventListener('load', () => { newWindow.scrollTo(0, 0); }); } }); }, clearAllChats: async function () { const kebabButtons = document.querySelectorAll('button[data-component="IconButton"]'); for (const button of kebabButtons) { if (button.closest('.ConversationList-module__ConversationList__item--dD6z4')) { button.click(); await new Promise(resolve => setTimeout(resolve, 1000)); const deleteButton = Array.from(document.querySelectorAll('li[role="menuitem"]')) .find(item => item.textContent.includes('Delete')); if (deleteButton) { deleteButton.click(); await new Promise(resolve => setTimeout(resolve, 1000)); } } } } }, 'flomoapp.com': { init: function () { this.addClearButton(); }, addClearButton: function () { const button = document.createElement('button'); button.textContent = '清空笔记'; button.style.position = 'fixed'; button.style.bottom = '20px'; button.style.left = '20px'; button.style.zIndex = '9999'; applyButtonStyles(button, 'red'); button.onclick = () => { if (confirm('确定要清空笔记吗?')) { this.scrollAndCheck(); } }; document.body.appendChild(button); }, scrollToBottom: function () { const element = document.querySelector('.memos'); if (element) { element.scrollTop = element.scrollHeight; } }, isScrolledToBottom: function () { const element = document.querySelector('.end'); return element ? element.getBoundingClientRect().bottom <= window.innerHeight : false; }, scrollAndCheck: function () { this.scrollToBottom(); if (!this.isScrolledToBottom()) { console.log('No element with class "end" was found, continue scrolling...'); setTimeout(() => this.scrollAndCheck(), 1000); } else { console.log('页面已下滑到最底部!'); const elements = document.querySelectorAll('.item.danger'); elements.forEach(element => { if (element.textContent.includes('删除')) { element.click(); } }); } } }, 'doubao.com': { init: function () { this.addButtons(); }, addButtons: function () { const buttonContainer = document.createElement('div'); buttonContainer.style.position = 'fixed'; buttonContainer.style.bottom = '20px'; buttonContainer.style.left = '20px'; buttonContainer.style.zIndex = '9999'; buttonContainer.style.display = 'flex'; buttonContainer.style.flexDirection = 'column'; buttonContainer.style.gap = '10px'; const openChatsButton = document.createElement('button'); openChatsButton.textContent = '打开chat'; applyButtonStyles(openChatsButton, 'green'); const clearChatsButton = document.createElement('button'); clearChatsButton.textContent = '清空chat'; applyButtonStyles(clearChatsButton, 'red'); buttonContainer.appendChild(openChatsButton); buttonContainer.appendChild(clearChatsButton); document.body.appendChild(buttonContainer); openChatsButton.addEventListener('click', this.openAllChats); clearChatsButton.addEventListener('click', this.deleteAllChatItems); }, openAllChats: async function () { const menuButtons = document.querySelectorAll('.chat-item-menu-button-outline-Ic2b7D'); for (const menuButton of menuButtons) { try { // 点击菜单按钮 menuButton.querySelector('div').click(); await new Promise(resolve => setTimeout(resolve, 1000)); // 查找并点击分享按钮 const shareButton = document.querySelector('[data-testid="chat_item_menu_thread_share_icon"]')?.closest('.semi-dropdown-item'); if (shareButton) { shareButton.click(); await new Promise(resolve => setTimeout(resolve, 1000)); // 获取当前URL并在新标签页打开 window.open(window.location.href, '_blank'); // 等待一下再处理下一个 await new Promise(resolve => setTimeout(resolve, 1000)); } } catch (error) { console.error('Error processing chat item:', error); } } }, deleteAllChatItems: async function () { // 工具函数:检查元素是否存在且可见 const isElementVisible = (element) => { return element && element.offsetParent !== null; }; // 工具函数:等待元素出现 const waitForElement = async (selector, maxAttempts = 20, interval = 50) => { for (let i = 0; i < maxAttempts; i++) { const element = document.querySelector(selector); if (isElementVisible(element)) { return element; } await new Promise(resolve => setTimeout(resolve, interval)); } return null; }; // 工具函数:模拟真实点击 const simulateClick = (element) => { if (!element) return false; try { ['mouseenter', 'mousedown', 'mouseup', 'click'].forEach(eventName => { element.dispatchEvent(new MouseEvent(eventName, { view: window, bubbles: true, cancelable: true, buttons: eventName === 'mousedown' ? 1 : 0 })); }); return true; } catch (e) { console.log('[Error] 点击模拟失败:', e); return false; } }; // 工具函数:处理确认按钮 const handleConfirmButton = async (maxAttempts = 3) => { for (let i = 0; i < maxAttempts; i++) { const confirmButton = await waitForElement('.semi-modal-content button.semi-button-danger', 10, 50); if (confirmButton) { console.log('[Confirm] 找到确认按钮,尝试点击'); if (simulateClick(confirmButton)) { return true; } } await new Promise(resolve => setTimeout(resolve, 50)); } return false; }; const menuButtons = document.querySelectorAll('[class*="chat-item-menu-button-outline"]'); let successCount = 0; let skipCount = 0; let failCount = 0; for (const menuButton of menuButtons) { try { // 检查是否有未关闭的modal,如果有,优先处理 const existingModal = document.querySelector('.semi-modal-content button.semi-button-danger'); if (isElementVisible(existingModal)) { console.log('[Modal] 发现未关闭的确认框,优先处理'); if (await handleConfirmButton()) { successCount++; await new Promise(resolve => setTimeout(resolve, 100)); continue; } } // 点击菜单按钮 if (!simulateClick(menuButton.querySelector('div'))) { console.log('[Skip] 菜单按钮点击失败'); skipCount++; continue; } // 等待并查找删除按钮 await new Promise(resolve => setTimeout(resolve, 50)); const deleteButton = Array.from(document.querySelectorAll('.semi-dropdown-item')) .find(item => item.querySelector('[data-testid="chat_item_menu_remove_icon"]')); if (!deleteButton) { console.log('[Skip] 没有找到删除按钮'); skipCount++; continue; } console.log('[Delete] 找到删除按钮,准备点击'); // 尝试点击删除按钮,带重试机制 let deleteSuccess = false; for (let attempt = 0; attempt < 3 && !deleteSuccess; attempt++) { if (simulateClick(deleteButton)) { deleteSuccess = true; break; } await new Promise(resolve => setTimeout(resolve, 50)); } if (!deleteSuccess) { console.log('[Error] 删除按钮点击失败'); failCount++; continue; } // 处理确认按钮 if (await handleConfirmButton()) { console.log('[Success] 删除操作执行成功'); successCount++; } else { console.log('[Error] 确认按钮处理失败'); failCount++; } // 短暂等待确保操作完成 await new Promise(resolve => setTimeout(resolve, 1000)); } catch (error) { console.error('[Error] 操作出错:', error); failCount++; } } console.log(`[Complete] 操作完成统计: 成功=${successCount}, 跳过=${skipCount}, 失败=${failCount}`); } } }; // Get current domain and execute corresponding code const domain = window.location.hostname.replace('www.', '').split('.').slice(-2).join('.'); const config = siteConfigs[domain]; if (config) { config.init(); } })();