您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
向水源图床上传图片,自动携带cookie
// ==UserScript== // @name 水源图床上传工具 // @namespace http://tampermonkey.net/ // @version 1.1 // @description 向水源图床上传图片,自动携带cookie // @author Labyrinth // @icon https://shuiyuan.s3.jcloud.sjtu.edu.cn/original/4X/f/2/3/f23eba8f728e684ad7c9fc3529083e03cc054fc2.svg // @match https://shuiyuan.sjtu.edu.cn/* // @match https://notes.sjtu.edu.cn/* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @license MIT // @connect notes.sjtu.edu.cn // ==/UserScript== (function() { 'use strict'; // Cookie处理函数 function getCookiesForDomain(domain) { const cookies = document.cookie.split(';'); const domainCookies = []; cookies.forEach(cookie => { const trimmedCookie = cookie.trim(); if (trimmedCookie) { domainCookies.push(trimmedCookie); } }); return domainCookies.join('; '); } // 检查是否在notes.sjtu.edu.cn域名下,如果是则保存cookie function saveCookiesIfNeeded() { if (window.location.hostname === 'notes.sjtu.edu.cn') { const cookies = getCookiesForDomain('notes.sjtu.edu.cn'); if (cookies) { GM_setValue('notes_sjtu_cookies', cookies); console.log('已保存水源笔记Cookie'); } } } // 获取保存的cookie function getSavedCookies() { return GM_getValue('notes_sjtu_cookies', ''); } // 创建上传按钮和界面 function createUploadInterface() { // 创建上传容器 const uploadContainer = document.createElement('div'); uploadContainer.id = 'shuiyuan-upload-container'; uploadContainer.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 300px; background: #fff; border: 2px solid #007bff; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 10000; font-family: Arial, sans-serif; display: none; `; // 创建标题栏 const titleBar = document.createElement('div'); titleBar.style.cssText = ` background: #007bff; color: white; padding: 10px; border-radius: 6px 6px 0 0; font-weight: bold; cursor: move; display: flex; justify-content: space-between; align-items: center; `; titleBar.innerHTML = ` <span>水源图床上传</span> <span id="close-upload-panel" style="cursor: pointer; font-size: 18px;">×</span> `; // 创建内容区域 const content = document.createElement('div'); content.style.padding = '15px'; const savedCookies = getSavedCookies(); const isNotesPage = window.location.hostname === 'notes.sjtu.edu.cn'; content.innerHTML = ` <div style="margin-bottom: 15px; padding: 8px; border-radius: 4px; ${savedCookies ? 'background: #d4edda; border: 1px solid #c3e6cb; color: #155724;' : 'background: #f8d7da; border: 1px solid #f5c6cb; color: #721c24;'}"> <div style="font-weight: bold; margin-bottom: 5px;">登录状态:</div> <div style="font-size: 12px;"> ${savedCookies ? '✅ 已检测到登录Cookie' : '❌ 未检测到登录Cookie'} </div> ${!isNotesPage && !savedCookies ? '<div style="font-size: 11px; margin-top: 5px;">请先访问 <a href="https://notes.sjtu.edu.cn" target="_blank" style="color: #721c24;">notes.sjtu.edu.cn</a> 登录</div>' : ''} ${isNotesPage ? '<button id="refresh-cookies-btn" style="width: 100%; margin-top: 5px; padding: 5px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 12px;">刷新登录状态</button>' : ''} </div> <div style="margin-bottom: 15px;"> <label style="display: block; margin-bottom: 5px; font-weight: bold;">选择图片:</label> <input type="file" id="image-input" accept="image/*" style="width: 100%; padding: 5px; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 8px;"> <div style="text-align: center; color: #666; font-size: 12px; margin-bottom: 8px;">或</div> <div id="paste-area" style="width: 100%; height: 80px; border: 2px dashed #ddd; border-radius: 4px; display: flex; align-items: center; justify-content: center; cursor: pointer; background: #f8f9fa; transition: all 0.3s ease;"> <div style="text-align: center; color: #666; font-size: 13px;"> <div>📋 点击此处并按 Ctrl+V 粘贴图片</div> <div style="font-size: 11px; margin-top: 3px;">或右键选择"粘贴"</div> </div> </div> <div id="paste-preview" style="margin-top: 8px; display: none;"> <div style="font-size: 12px; color: #28a745; margin-bottom: 5px;">✅ 已粘贴图片:</div> <img id="preview-image" style="max-width: 100%; max-height: 100px; border-radius: 4px; border: 1px solid #ddd;"> </div> </div> <div style="margin-bottom: 15px;"> <button id="upload-btn" style="width: 100%; padding: 10px; background: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;" ${!savedCookies ? 'disabled' : ''}>上传图片</button> </div> <div id="upload-status" style="padding: 8px; border-radius: 4px; display: none;"></div> <div id="upload-result" style="margin-top: 10px; display: none;"> <label style="display: block; margin-bottom: 5px; font-weight: bold;">图片链接:</label> <textarea id="result-url" readonly style="width: 100%; height: 40px; padding: 5px; border: 1px solid #ddd; border-radius: 4px; resize: none; margin-bottom: 5px;"></textarea> <label style="display: block; margin-bottom: 5px; font-weight: bold;">HTML格式:</label> <textarea id="result-html" readonly style="width: 100%; height: 40px; padding: 5px; border: 1px solid #ddd; border-radius: 4px; resize: none; margin-bottom: 5px;"></textarea> <label style="display: block; margin-bottom: 5px; font-weight: bold;">Markdown格式:</label> <textarea id="result-markdown" readonly style="width: 100%; height: 40px; padding: 5px; border: 1px solid #ddd; border-radius: 4px; resize: none; margin-bottom: 10px;"></textarea> <div style="display: flex; gap: 5px;"> <button id="copy-url-btn" style="flex: 1; padding: 8px; background: #17a2b8; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">复制链接</button> <button id="copy-html-btn" style="flex: 1; padding: 8px; background: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">复制HTML</button> <button id="copy-markdown-btn" style="flex: 1; padding: 8px; background: #fd7e14; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px;">复制MD</button> </div> </div> `; uploadContainer.appendChild(titleBar); uploadContainer.appendChild(content); document.body.appendChild(uploadContainer); // 创建触发按钮 const triggerBtn = document.createElement('button'); triggerBtn.id = 'show-upload-panel'; triggerBtn.innerHTML = '📷 上传图片'; triggerBtn.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 10px 15px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; z-index: 9998; font-size: 14px; box-shadow: 0 2px 8px rgba(0,0,0,0.2); `; document.body.appendChild(triggerBtn); return { uploadContainer, triggerBtn }; } // 粘贴处理相关函数 let pastedFile = null; // 处理粘贴事件 function handlePaste(e) { e.preventDefault(); const items = e.clipboardData.items; for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.type.indexOf('image') !== -1) { const file = item.getAsFile(); if (file) { setPastedImage(file); break; } } } } // 设置粘贴的图片 function setPastedImage(file) { pastedFile = file; // 创建预览 const reader = new FileReader(); reader.onload = function(e) { const previewDiv = document.getElementById('paste-preview'); const previewImg = document.getElementById('preview-image'); previewImg.src = e.target.result; previewDiv.style.display = 'block'; // 更新粘贴区域样式 const pasteArea = document.getElementById('paste-area'); pasteArea.style.borderColor = '#28a745'; pasteArea.style.background = '#d4edda'; }; reader.readAsDataURL(file); // 清空文件选择器 const fileInput = document.getElementById('image-input'); fileInput.value = ''; } // 获取当前选择的文件(文件选择器或粘贴) function getCurrentFile() { if (pastedFile) { return pastedFile; } const fileInput = document.getElementById('image-input'); return fileInput.files[0] || null; } // 清除粘贴的图片 function clearPastedImage() { pastedFile = null; const previewDiv = document.getElementById('paste-preview'); const pasteArea = document.getElementById('paste-area'); if (previewDiv) previewDiv.style.display = 'none'; if (pasteArea) { pasteArea.style.borderColor = '#ddd'; pasteArea.style.background = '#f8f9fa'; // 确保粘贴区域失去焦点 if (document.activeElement === pasteArea) { pasteArea.blur(); } } } // 上传图片到水源图床 async function uploadToShuiyuan(file) { return new Promise((resolve) => { try { // 创建FormData const formData = new FormData(); formData.append('image', file); // 获取保存的Cookie const savedCookies = getSavedCookies(); // 构建请求头 const headers = { "accept": "*/*", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", "cache-control": "no-cache", "pragma": "no-cache", "sec-ch-ua": '"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": '"Windows"', "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "cross-site" }; // 如果有保存的Cookie,则添加到请求头 if (savedCookies) { headers["Cookie"] = savedCookies; } // 使用GM_xmlhttpRequest来绕过CORS限制 GM_xmlhttpRequest({ method: "POST", url: "https://notes.sjtu.edu.cn/uploadimage", headers: headers, data: formData, onload: function(response) { if (response.status >= 200 && response.status < 300) { try { // 尝试解析JSON响应 const responseText = response.responseText.trim(); console.log('服务器响应:', responseText); // 调试日志 const jsonResponse = JSON.parse(responseText); let imageUrl; // 检查不同可能的响应格式 if (jsonResponse.link) { imageUrl = jsonResponse.link; } else if (jsonResponse.url) { imageUrl = jsonResponse.url; } else if (jsonResponse.data && jsonResponse.data.link) { imageUrl = jsonResponse.data.link; } else if (jsonResponse.data && jsonResponse.data.url) { imageUrl = jsonResponse.data.url; } else { // 如果找不到预期的字段,使用整个响应作为URL imageUrl = responseText; } console.log('提取的图片链接:', imageUrl); // 调试日志 resolve({ success: true, url: imageUrl, message: '上传成功!' }); } catch (e) { console.log('JSON解析失败,使用原始响应:', e); // 如果不是JSON格式,直接使用原始响应 resolve({ success: true, url: response.responseText.trim(), message: '上传成功!' }); } } else { resolve({ success: false, message: `上传失败: ${response.status} ${response.statusText}` }); } }, onerror: function(error) { resolve({ success: false, message: `上传失败: 网络错误` }); }, ontimeout: function() { resolve({ success: false, message: `上传失败: 请求超时` }); }, timeout: 30000 // 30秒超时 }); } catch (error) { resolve({ success: false, message: `上传失败: ${error.message}` }); } }); } // 显示状态信息 function showStatus(message, isError = false) { const statusDiv = document.getElementById('upload-status'); statusDiv.style.display = 'block'; statusDiv.style.background = isError ? '#f8d7da' : '#d4edda'; statusDiv.style.color = isError ? '#721c24' : '#155724'; statusDiv.style.border = `1px solid ${isError ? '#f5c6cb' : '#c3e6cb'}`; statusDiv.textContent = message; } // 隐藏状态信息 function hideStatus() { const statusDiv = document.getElementById('upload-status'); statusDiv.style.display = 'none'; } // 复制到剪贴板 async function copyToClipboard(text, type = '链接') { try { await navigator.clipboard.writeText(text); showStatus(`${type}已复制到剪贴板!`); setTimeout(hideStatus, 2000); } catch (err) { // 降级方案 const textArea = document.createElement('textarea'); textArea.value = text; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); showStatus(`${type}已复制到剪贴板!`); setTimeout(hideStatus, 2000); } } // 使面板可拖拽 function makeDraggable(element) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; const titleBar = element.querySelector('div'); titleBar.onmousedown = dragMouseDown; function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; element.style.top = (element.offsetTop - pos2) + "px"; element.style.left = (element.offsetLeft - pos1) + "px"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } } // 初始化插件 function init() { // 如果在notes.sjtu.edu.cn域名下,保存cookie saveCookiesIfNeeded(); // 如果不在notes.sjtu.edu.cn域名下且没有保存的cookie,显示提示 if (window.location.hostname !== 'notes.sjtu.edu.cn' && !getSavedCookies()) { console.warn('未找到水源笔记的Cookie,请先访问 https://notes.sjtu.edu.cn 登录后再使用上传功能'); } const { uploadContainer, triggerBtn } = createUploadInterface(); // 使面板可拖拽 makeDraggable(uploadContainer); // 绑定事件 triggerBtn.addEventListener('click', () => { uploadContainer.style.display = 'block'; triggerBtn.style.display = 'none'; }); document.getElementById('close-upload-panel').addEventListener('click', () => { uploadContainer.style.display = 'none'; triggerBtn.style.display = 'block'; // 关闭面板时将焦点回到页面主体 document.body.focus(); const mainContent = document.querySelector('main, #main, .main-content, body'); if (mainContent) { mainContent.focus(); } }); document.getElementById('upload-btn').addEventListener('click', async () => { // 检查是否有登录Cookie const savedCookies = getSavedCookies(); if (!savedCookies) { showStatus('请先登录水源笔记!访问 https://notes.sjtu.edu.cn 登录后再使用上传功能。', true); return; } const file = getCurrentFile(); if (!file) { showStatus('请先选择或粘贴图片文件!', true); return; } // 验证文件类型 if (!file.type.startsWith('image/')) { showStatus('请选择有效的图片文件!', true); return; } // 验证文件大小(限制为10MB) if (file.size > 10 * 1024 * 1024) { showStatus('图片文件不能超过10MB!', true); return; } showStatus('正在上传,请稍候...'); const result = await uploadToShuiyuan(file); if (result.success) { showStatus(result.message); const imageUrl = result.url; // 填充不同格式的文本框 document.getElementById('result-url').value = imageUrl; document.getElementById('result-html').value = `<img src="${imageUrl}" alt="uploaded image" />`; document.getElementById('result-markdown').value = ``; document.getElementById('upload-result').style.display = 'block'; // 清除粘贴的图片和文件选择器 clearPastedImage(); document.getElementById('image-input').value = ''; // 修复焦点问题:将焦点移回页面主体 document.body.focus(); // 如果页面有主要内容区域,也可以尝试聚焦到那里 const mainContent = document.querySelector('main, #main, .main-content, body'); if (mainContent) { mainContent.focus(); } } else { showStatus(result.message, true); document.getElementById('upload-result').style.display = 'none'; } }); document.getElementById('copy-url-btn').addEventListener('click', () => { const url = document.getElementById('result-url').value; if (url) { copyToClipboard(url, '链接'); } }); document.getElementById('copy-html-btn').addEventListener('click', () => { const html = document.getElementById('result-html').value; if (html) { copyToClipboard(html, 'HTML代码'); } }); document.getElementById('copy-markdown-btn').addEventListener('click', () => { const markdown = document.getElementById('result-markdown').value; if (markdown) { copyToClipboard(markdown, 'Markdown代码'); } }); // 刷新Cookie按钮事件(仅在notes.sjtu.edu.cn页面显示) const refreshCookiesBtn = document.getElementById('refresh-cookies-btn'); if (refreshCookiesBtn) { refreshCookiesBtn.addEventListener('click', () => { saveCookiesIfNeeded(); showStatus('已刷新登录状态'); setTimeout(() => { location.reload(); // 重新加载页面以更新界面 }, 1000); }); } // 粘贴区域相关事件 const pasteArea = document.getElementById('paste-area'); const fileInput = document.getElementById('image-input'); // 粘贴区域点击聚焦(为了接收粘贴事件) pasteArea.addEventListener('click', () => { pasteArea.focus(); // 短暂聚焦后自动失焦,避免长时间持有焦点 setTimeout(() => { if (document.activeElement === pasteArea) { pasteArea.blur(); } }, 100); }); // 设置粘贴区域可聚焦 pasteArea.tabIndex = 0; // 粘贴成功后自动失焦 const originalHandlePaste = handlePaste; const enhancedHandlePaste = function(e) { originalHandlePaste(e); // 粘贴完成后失焦 setTimeout(() => { if (document.activeElement === pasteArea) { pasteArea.blur(); } }, 50); }; // 监听粘贴事件 pasteArea.addEventListener('paste', enhancedHandlePaste); document.addEventListener('paste', (e) => { // 如果上传面板是可见的,处理全局粘贴 if (uploadContainer.style.display !== 'none') { enhancedHandlePaste(e); } }); // 文件选择器变化时清除粘贴的图片 fileInput.addEventListener('change', () => { if (fileInput.files.length > 0) { clearPastedImage(); } }); // 拖拽支持 pasteArea.addEventListener('dragover', (e) => { e.preventDefault(); pasteArea.style.borderColor = '#007bff'; pasteArea.style.background = '#e3f2fd'; }); pasteArea.addEventListener('dragleave', (e) => { e.preventDefault(); if (!pastedFile) { pasteArea.style.borderColor = '#ddd'; pasteArea.style.background = '#f8f9fa'; } }); pasteArea.addEventListener('drop', (e) => { e.preventDefault(); const files = e.dataTransfer.files; if (files.length > 0 && files[0].type.startsWith('image/')) { setPastedImage(files[0]); } else { pasteArea.style.borderColor = '#ddd'; pasteArea.style.background = '#f8f9fa'; } }); // 添加键盘快捷键支持 document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.shiftKey && e.key === 'U') { e.preventDefault(); if (uploadContainer.style.display === 'none') { uploadContainer.style.display = 'block'; triggerBtn.style.display = 'none'; } else { uploadContainer.style.display = 'none'; triggerBtn.style.display = 'block'; } } }); } // 等待页面加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();