// ==UserScript== // @name 剪贴板图片上传lsky图床-Nodeseek // @namespace http://tampermonkey.net/ // @version 1.6.4 // @description Upload clipboard images to a configurable image host, auto-fetch token if missing or expired // @author You // @match *://*.nodeseek.com/* // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_addStyle // @connect * // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/529474/%E5%89%AA%E8%B4%B4%E6%9D%BF%E5%9B%BE%E7%89%87%E4%B8%8A%E4%BC%A0lsky%E5%9B%BE%E5%BA%8A-Nodeseek.user.js // @updateURL https://update.greasyfork.icu/scripts/529474/%E5%89%AA%E8%B4%B4%E6%9D%BF%E5%9B%BE%E7%89%87%E4%B8%8A%E4%BC%A0lsky%E5%9B%BE%E5%BA%8A-Nodeseek.meta.js // ==/UserScript== (function () { 'use strict'; let config = { baseUrl: GM_getValue('baseUrl', 'http://'), token: GM_getValue('token', ''), email: GM_getValue('email', ''), password: GM_getValue('password', ''), strategyId: GM_getValue('strategyId', 1) }; function createSettingsPanel() { let panel = document.createElement('div'); panel.id = 'gm-settings-panel'; panel.innerHTML = `

lsky 图床 配置

`; document.body.appendChild(panel); document.getElementById('gm-save-settings').addEventListener('click', () => { let baseUrl = document.getElementById('gm-baseUrl').value; // Check if baseUrl doesn't start with "http://" or "https://" if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) { baseUrl = 'http://' + baseUrl; // Automatically add "http://" } // Save the baseUrl along with other values GM_setValue('baseUrl', baseUrl); GM_setValue('email', document.getElementById('gm-email').value); GM_setValue('password', document.getElementById('gm-password').value); GM_setValue('strategyId', Number(document.getElementById('gm-strategyId').value)); // Update the config object config.baseUrl = GM_getValue('baseUrl'); config.email = GM_getValue('email'); config.password = GM_getValue('password'); config.strategyId = GM_getValue('strategyId'); panel.remove(); showSuccessMessage("配置已保存!"); }); document.getElementById('gm-close-settings').addEventListener('click', () => { panel.remove(); }); GM_addStyle(` #gm-settings-panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); z-index: 10000; } .gm-panel h2 { margin: 0 0 10px; font-size: 18px; } .gm-panel label { display: block; margin-bottom: 10px; } .gm-panel input { width: 100%; padding: 5px; margin-top: 5px; border: 1px solid #ddd; border-radius: 4px; } .gm-panel button { margin-right: 10px; padding: 5px 10px; cursor: pointer; } `); } GM_registerMenuCommand("⚙️ 配置 lsky 图床", createSettingsPanel); async function getToken() { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: `${config.baseUrl}/api/v1/tokens`, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, data: JSON.stringify({ email: config.email, password: config.password }), onload: function (response) { try { let res = JSON.parse(response.responseText); if (res.status && res.data?.token) { GM_setValue('token', res.data.token); config.token = res.data.token; resolve(res.data.token); } else { reject(res.message || "获取 Token 失败"); } } catch (e) { reject("返回的不是有效的 JSON 响应:" + response.responseText); } }, onerror: () => reject("网络错误") }); }); } function showUploadModal() { let modal = document.createElement('div'); modal.id = 'upload-modal'; modal.innerHTML = ` `; document.body.appendChild(modal); GM_addStyle(` #upload-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: rgba(0, 0, 0, 0.5); padding: 20px; border-radius: 8px; z-index: 9999; display: flex; justify-content: center; align-items: center; } .modal-content { background-color: white; padding: 20px; border-radius: 8px; text-align: center; } .loader { border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 50px; height: 50px; animation: spin 1s linear infinite; margin: auto; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `); setTimeout(() => { let modal = document.getElementById('upload-modal'); if (modal) { modal.remove(); } }, 10000); } function hideUploadModal() { let modal = document.getElementById('upload-modal'); if (modal) { modal.remove(); } } function showSuccessMessage(message) { let successModal = document.createElement('div'); successModal.id = 'success-modal'; successModal.innerHTML = `

${message}

`; document.body.appendChild(successModal); GM_addStyle(` #success-modal { position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background-color: rgba(0, 128, 0, 0.8); padding: 10px 20px; border-radius: 8px; color: white; z-index: 10000; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); font-size: 16px; } #success-modal .success-modal-content { text-align: center; } `); setTimeout(() => { let modal = document.getElementById('success-modal'); if (modal) { modal.remove(); } }, 3000); } function showFailureMessage(message) { let failureModal = document.createElement('div'); failureModal.id = 'failure-modal'; failureModal.innerHTML = `

${message}

`; document.body.appendChild(failureModal); GM_addStyle(` #failure-modal { position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background-color: rgba(255, 0, 0, 0.8); padding: 10px 20px; border-radius: 8px; color: white; z-index: 10000; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); font-size: 16px; } #failure-modal .failure-modal-content { text-align: center; } `); setTimeout(() => { let modal = document.getElementById('failure-modal'); if (modal) { modal.remove(); } }, 3000); } document.addEventListener('paste', async (event) => { let items = (event.clipboardData || event.originalEvent.clipboardData).items; for (let item of items) { if (item.type.indexOf('image') !== -1) { // 弹出确认框,确认是否上传 let confirmUpload = window.confirm("确认上传图片吗?"); if (!confirmUpload) { return; // 如果用户选择取消,退出上传流程 } showUploadModal(); let file = item.getAsFile(); if (!config.token) { await getToken().catch((error) => { hideUploadModal(); console.error("获取 Token 失败"); showFailureMessage("获取 Token 失败"); return; // Early exit if token can't be fetched }); } // Proceed with image upload if the token is valid uploadImage(file).then(insertText) .catch((error) => { console.error(error); showFailureMessage("上传失败"); }) .finally(() => { hideUploadModal(); // Ensure the upload modal is hidden when upload finishes or fails }); } } }); async function uploadImage(file) { return new Promise((resolve, reject) => { let formData = new FormData(); formData.append('file', file); GM_xmlhttpRequest({ method: 'POST', url: `${config.baseUrl}/api/v1/upload`, headers: { 'Authorization': `Bearer ${config.token}` }, data: formData, onload: function (response) { try { let res = JSON.parse(response.responseText); if (res.status && res.data?.links?.markdown) { resolve(res.data.links.markdown); showSuccessMessage("上传成功!"); } else { reject(res.message || "上传失败"); } } catch (e) { reject("返回的不是有效的 JSON 响应:" + response.responseText); } }, onerror: () => reject("网络错误") }); }); } function insertText(text) { let activeElement = document.activeElement; if (activeElement && (activeElement.tagName === 'TEXTAREA' || activeElement.tagName === 'INPUT')) { let start = activeElement.selectionStart; let end = activeElement.selectionEnd; activeElement.value = activeElement.value.substring(0, start) + text + activeElement.value.substring(end); activeElement.selectionStart = activeElement.selectionEnd = start + text.length; } else if (activeElement?.isContentEditable) { document.execCommand('insertText', false, text); } } })();