// ==UserScript== // @name Muse2API Cookie 导入助手 // @namespace https://github.com/czg86389-hub/muse2api // @version 1.1.2 // @description 在 muse.ai 网页上一键导出并推送核心 Cookie 至 muse2api 账号池,支持现代化悬浮面板、状态指示与剪贴板备份 // @author MUSE2API Contributors // @match https://muse.ai/* // @match https://*.muse.ai/* // @updateURL https://raw.githubusercontent.com/czg86389-hub/muse2api/main/tools/muse2api_cookie_importer.user.js // @downloadURL https://raw.githubusercontent.com/czg86389-hub/muse2api/main/tools/muse2api_cookie_importer.user.js // @grant GM_cookie // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @connect * // @run-at document-end // ==/UserScript== (function () { 'use strict'; const STORE_KEY = '***'; const ESSENTIAL = ['hatch_sess']; function loadCfg() { const fallback = { base: '', key: '', label: '' }; if (typeof GM_getValue === 'function') { return GM_getValue(STORE_KEY, fallback); } try { return JSON.parse(localStorage.getItem(STORE_KEY)) || fallback; } catch (_) { return fallback; } } function saveCfg(cfg) { if (typeof GM_setValue === 'function') { GM_setValue(STORE_KEY, cfg); } else { localStorage.setItem(STORE_KEY, JSON.stringify(cfg)); } } function normBase(v) { let s = (v || '').trim(); if (!s) return ''; if (!/^https?:\/\//i.test(s)) { s = (/^(127\.|100\.|10\.|192\.168\.|localhost)/i.test(s) ? 'http://' : 'https://') + s; } s = s.replace(/\/+$/, '').replace(/\/v1$/i, ''); return s; } function showToast(msg, type = 'info') { const el = document.createElement('div'); el.textContent = msg; const bg = type === 'ok' ? '#059669' : type === 'err' ? '#dc2626' : '#2563eb'; el.style.cssText = `position:fixed;bottom:24px;left:50%;transform:translateX(-50%);z-index:9999999;padding:10px 18px;background:${bg};color:#fff;border-radius:10px;font-size:13px;font-weight:500;font-family:-apple-system,BlinkMacSystemFont,sans-serif;box-shadow:0 12px 30px rgba(0,0,0,0.5);transition:opacity 0.3s;max-width:420px;text-align:center;line-height:1.5;pointer-events:none;`; document.body.appendChild(el); setTimeout(() => { el.style.opacity = '0'; setTimeout(() => el.remove(), 300); }, 4000); } async function getCookies() { const out = {}, exp = {}; if (typeof GM_cookie !== 'undefined' && typeof GM_cookie.list === 'function') { try { const queries = [ { url: 'https://muse.ai/' }, { domain: '.muse.ai' }, { domain: 'muse.ai' }, ]; if (typeof window !== 'undefined' && window.location && window.location.href) { queries.unshift({ url: window.location.href }); } const all = []; for (const q of queries) { try { const list = await new Promise((resolve) => { GM_cookie.list(q, (cookies, error) => { resolve(error ? [] : (cookies || [])); }); }); if (Array.isArray(list)) all.push(...list); } catch (_) {} } for (const c of all) { if (!c || !c.name) continue; const dom = (c.domain || '').replace(/^\./, ''); if (!dom.endsWith('muse.ai')) continue; out[c.name] = c.value; if (c.expirationDate) exp[c.name] = Math.floor(c.expirationDate); } } catch (_) {} } try { const parts = document.cookie.split(';'); for (const p of parts) { const idx = p.indexOf('='); if (idx > 0) { const k = p.slice(0, idx).trim(); const v = p.slice(idx + 1).trim(); if (k && !(k in out)) out[k] = v; } } } catch (_) {} return { cookies: out, expires: exp }; } async function checkSessionStatus() { const { cookies } = await getCookies(); const missing = ESSENTIAL.filter(n => !(n in cookies)); const hasCore = missing.length === 0; return { hasCore, count: Object.keys(cookies).length, missing, cookies }; } async function executePush(btnEl = null) { const cfg = loadCfg(); const base = normBase(cfg.base); const key = (cfg.key || '').trim(); const label = (cfg.label || '').trim(); if (btnEl) { btnEl.disabled = true; btnEl.innerHTML = '⏳ 提取中…'; } try { const { cookies, expires } = await getCookies(); const names = Object.keys(cookies); if (!names.length) { showToast('未检测到 muse.ai Cookie,请确保已登录账号!', 'err'); return; } const missing = ESSENTIAL.filter(n => !(n in cookies)); if (missing.length) { showToast(`读到 ${names.length} 条 Cookie,但缺少核心会话令牌:${missing.join(', ')}。请确保已登录账号!`, 'err'); return; } const cookieStr = Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; '); if (typeof GM_setClipboard === 'function') { GM_setClipboard(cookieStr); } if (!base) { showToast('✓ 完整 Cookie 已复制到剪贴板!请在上方配置服务地址以启用一键入库。', 'ok'); return; } if (btnEl) btnEl.innerHTML = '🚀 推送至服务…'; const headers = { 'Content-Type': 'application/json' }; if (key) { headers['Authorization'] = 'Bearer ' + key; } const payload = JSON.stringify({ label, cookies, expires }); const doFetch = () => { if (typeof GM_xmlhttpRequest === 'function') { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: base + '/admin/accounts', headers: headers, data: payload, onload: (res) => resolve({ ok: res.status >= 200 && res.status < 300, status: res.status, text: () => Promise.resolve(res.responseText) }), onerror: reject, ontimeout: reject, }); }); } return fetch(base + '/admin/accounts', { method: 'POST', headers, body: payload }); }; const r = await doFetch(); const txt = await r.text(); let data = {}; try { data = JSON.parse(txt); } catch (_) {} if (r.status === 401) { showToast('推送失败:API Key 错误 (401)', 'err'); return; } if (!r.ok) { showToast(`推送失败 (HTTP ${r.status}):${txt.slice(0, 80)}。已复制 Cookie 到剪贴板!`, 'err'); return; } const a = (data.added && data.added[0]) || {}; const expDate = a.expires_at ? new Date(a.expires_at * 1000).toLocaleDateString() : '有效'; showToast(`✓ 账号成功导入 muse2api!\nID: ${a.id || '?'}\n有效期至: ${expDate}`, 'ok'); updateStatusBadge(); } catch (e) { showToast(`请求异常:${e.message || e}。已自动复制 Cookie 到剪贴板!`, 'err'); } finally { if (btnEl) { btnEl.disabled = false; btnEl.innerHTML = '⚡ 一键推送至账号池'; } } } async function updateStatusBadge() { const badge = document.getElementById('muse-status-tag'); if (!badge) return; const st = await checkSessionStatus(); if (st.hasCore) { badge.style.color = '#22c55e'; badge.textContent = `✓ 登录态完整 (${st.count} 项)`; } else { badge.style.color = '#f59e0b'; badge.textContent = `⚠ 登录态未就绪`; } } function togglePanel() { const existing = document.getElementById('muse2api-token-panel'); if (existing) { existing.remove(); return; } showPanel(); } function showPanel() { if (document.getElementById('muse2api-token-panel')) { document.getElementById('muse2api-token-panel').remove(); } const cfg = loadCfg(); const panel = document.createElement('div'); panel.id = 'muse2api-token-panel'; panel.innerHTML = `
muse2api 导入面板 v1.1.2
当前网页会话 检测中…
服务地址 (Base URL)
API Key(若服务未开鉴权可留空)
账号标签 (可选显示名称)
配置将自动保存于浏览器本地 按 Esc 或右上角关闭
`; document.body.appendChild(panel); updateStatusBadge(); // 绑定事件与自动保存 const baseIn = document.getElementById('muse-cfg-base'); const keyIn = document.getElementById('muse-cfg-key'); const labelIn = document.getElementById('muse-cfg-label'); function saveInputs() { const newCfg = { base: normBase(baseIn.value), key: keyIn.value.trim(), label: labelIn.value.trim(), }; saveCfg(newCfg); return newCfg; } baseIn.addEventListener('change', saveInputs); keyIn.addEventListener('change', saveInputs); labelIn.addEventListener('change', saveInputs); document.getElementById('muse-close-btn').onclick = () => panel.remove(); document.getElementById('muse-action-save').onclick = () => { saveInputs(); showToast('✓ 配置已保存!', 'ok'); }; document.getElementById('muse-action-copy').onclick = async () => { const { cookies } = await getCookies(); const str = Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; '); if (str) { if (typeof GM_setClipboard === 'function') GM_setClipboard(str); showToast('✓ 完整 Cookie 已复制到剪贴板!', 'ok'); } else { showToast('未检测到任何 Cookie!', 'err'); } }; document.getElementById('muse-action-push').onclick = function () { saveInputs(); executePush(this); }; } function mountFloatingTrigger() { if (document.getElementById('muse2api-floating-ball')) return; const ball = document.createElement('div'); ball.id = 'muse2api-floating-ball'; ball.innerHTML = `
⚡
`; ball.addEventListener('click', togglePanel); document.body.appendChild(ball); } if (typeof GM_registerMenuCommand === 'function') { GM_registerMenuCommand('🚀 打开 muse2api 导入面板', togglePanel); GM_registerMenuCommand('⚡ 直接一键推送当前账号', () => executePush()); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', mountFloatingTrigger); } else { mountFloatingTrigger(); } })();