// ==UserScript== // @name 【Sorry,Discord!】 DC Orbs Quests Video Skip Button (Beta) // @name:zh-TW 【對不起,Discord!】 DC Orbs 任務影片跳過按鈕 (Beta) // @name:zh-CN 【对不起,Discord!】 DC Orbs 任务视频跳过按钮 (Beta) // @name:ja 【すみません,Discord!】 DC Orbs クエスト動画スキップボタン (Beta) // @name:ko 【죄송합니다,Discord!】 DC Orbs 퀘스트 동영상 건너뛰기 버튼 (Beta) // @namespace https://github.com/NiktoBlox/Tampermonkey-Scripts/tree/60ae32ddf7b8d2e6cfdae065bf1ce5f702550180/%F0%9F%9F%A2%E2%94%82Deploy%20Scripts/Discord%20Orbs%20Quests%20Video%20Skip%20Button // @version 1.51-Beta // @description 🔒 99% Safe & Account Ban-Free Script — Floating draggable skip button for Discord Orbs Quests Videos // @description:zh-TW 🔒 99% 安全且不會封禁帳號的腳本 — 可拖曳的懸浮按鈕,用於跳過 Discord Orbs 任務影片 // @description:zh-CN 🔒 99% 安全且不会封禁账号的脚本 — 可拖拽的悬浮按钮,用于跳过 Discord Orbs 任务视频 // @description:ja 🔒 99% 安全・アカウントBANリスクなしスクリプト — Discord Orbs クエスト動画をスキップするためのドラッグ可能なフローティングボタン // @description:ko 🔒 99% 안전 & 계정 차단 없는 스크립트 — Discord Orbs 퀘스트 동영상을 건너뛰기 위한 드래그 가능한 플로팅 버튼 // @author NiktoBlox // @license MIT // @match https://discord.com/quest-home // @match https://discord.com/quests/* // @match https://discord.com/channels/* // @match https://discord.com/* // @icon https://files.catbox.moe/kdzneo.png // @grant GM_getValue // @grant GM_setValue // @downloadURL https://update.greasyfork.icu/scripts/574006/%E3%80%90Sorry%2CDiscord%21%E3%80%91%20DC%20Orbs%20Quests%20Video%20Skip%20Button%20%28Beta%29.user.js // @updateURL https://update.greasyfork.icu/scripts/574006/%E3%80%90Sorry%2CDiscord%21%E3%80%91%20DC%20Orbs%20Quests%20Video%20Skip%20Button%20%28Beta%29.meta.js // ==/UserScript== (function () { 'use strict'; // ── 多國語系 ── const I18N = { en: { skip: '⏭️ Skip', skipped: '✅ Skipped!', notFound: '❌ Not Found', settings: '⚙️ Settings', theme: '🎨 Theme', customColor: '🖌️ Custom Color', btnColor: 'Button', textColor: 'Text', borderRadius: '⬜ Border Radius', opacity: '👁️ Opacity', autoHide: '🔁 Auto-hide', autoHideDesc: 'Fade button when inactive', language: '🌐 Language', about: 'ℹ️ About', version: 'Version', author: 'Author', license: 'License', github: 'GitHub ↗', autoSkip: '🤖 Auto Skip', autoSkipDesc: 'Auto skip when video detected', autoSkipDelay: '⏱️ Auto Skip Delay', seconds: 's', notifyDuration: '🔔 Notify Duration', btnSize: '📐 Button Size', small: 'S', medium: 'M', large: 'L', btnPosition: '📌 Quick Position', customText: '✏️ Custom Button Text', resetAll: '🔄 Reset All Settings', resetConfirm: 'Reset all settings?', exportSettings: '📤 Export', importSettings: '📥 Import', importSuccess: '✅ Imported!', importFail: '❌ Import Failed', guiTheme: '🖥️ Panel Theme', guiLight: 'Light', guiDark: 'Dark', guiBlack: 'Pure Black', corners: ['↖ Top Left', '↗ Top Right', '↙ Bottom Left', '↘ Bottom Right'], dragDelay: '🖱️ Long-press Drag Delay', ms: 'ms', tabPanel: 'Panel', tabBtn: 'Button Style', tabOther: 'Other', aboutSection: 'ℹ️ About', aboutAuthor: '👨‍💻 Author', aboutVersion: '🏷️ Version', aboutBug: '🐛 Report a Bug', aboutBugLink: 'Click here to report →', aboutLang: '🌐 Add a Language', aboutLangLink: 'Request a new language →', }, 'zh-TW': { skip: '⏭️ 跳過', skipped: '✅ 已跳過!', notFound: '❌ 找不到', settings: '⚙️ 設定', theme: '🎨 主題', customColor: '🖌️ 自訂顏色', btnColor: '按鈕', textColor: '文字', borderRadius: '⬜ 圓角大小', opacity: '👁️ 透明度', autoHide: '🔁 自動隱藏', autoHideDesc: '閒置時淡化按鈕', language: '🌐 語言', about: 'ℹ️ 關於', version: '版本', author: '作者', license: '授權', github: 'GitHub ↗', autoSkip: '🤖 自動跳過', autoSkipDesc: '偵測到影片時自動跳過', autoSkipDelay: '⏱️ 自動跳過延遲', seconds: '秒', notifyDuration: '🔔 通知顯示時間', btnSize: '📐 按鈕大小', small: '小', medium: '中', large: '大', btnPosition: '📌 快速定位', customText: '✏️ 自訂按鈕文字', resetAll: '🔄 重設所有設定', resetConfirm: '確定重設所有設定?', exportSettings: '📤 匯出', importSettings: '📥 匯入', importSuccess: '✅ 匯入成功!', importFail: '❌ 匯入失敗', guiTheme: '🖥️ 面板主題', guiLight: '淺色', guiDark: '深色', guiBlack: '極黑', corners: ['↖ 左上', '↗ 右上', '↙ 左下', '↘ 右下'], dragDelay: '🖱️ 長按拖曳延遲', ms: '毫秒', tabPanel: '面板外觀', tabBtn: '按鈕外觀', tabOther: '其他', aboutSection: 'ℹ️ 關於', aboutAuthor: '👨‍💻 製作者', aboutVersion: '🏷️ 版本', aboutBug: '🐛 回報 Bug', aboutBugLink: '點此回報問題 →', aboutLang: '🌐 新增語言', aboutLangLink: '申請新增語言 →', }, 'zh-CN': { skip: '⏭️ 跳过', skipped: '✅ 已跳过!', notFound: '❌ 未找到', settings: '⚙️ 设置', theme: '🎨 主题', customColor: '🖌️ 自定义颜色', btnColor: '按钮', textColor: '文字', borderRadius: '⬜ 圆角大小', opacity: '👁️ 透明度', autoHide: '🔁 自动隐藏', autoHideDesc: '闲置时淡化按钮', language: '🌐 语言', about: 'ℹ️ 关于', version: '版本', author: '作者', license: '许可证', github: 'GitHub ↗', autoSkip: '🤖 自动跳过', autoSkipDesc: '检测到视频时自动跳过', autoSkipDelay: '⏱️ 自动跳过延迟', seconds: '秒', notifyDuration: '🔔 通知显示时间', btnSize: '📐 按钮大小', small: '小', medium: '中', large: '大', btnPosition: '📌 快速定位', customText: '✏️ 自定义按钮文字', resetAll: '🔄 重置所有设置', resetConfirm: '确定重置所有设置?', exportSettings: '📤 导出', importSettings: '📥 导入', importSuccess: '✅ 导入成功!', importFail: '❌ 导入失败', guiTheme: '🖥️ 面板主题', guiLight: '浅色', guiDark: '深色', guiBlack: '极黑', corners: ['↖ 左上', '↗ 右上', '↙ 左下', '↘ 右下'], dragDelay: '🖱️ 长按拖拽延迟', ms: '毫秒', tabPanel: '面板外观', tabBtn: '按钮外观', tabOther: '其他', aboutSection: 'ℹ️ 关于', aboutAuthor: '👨‍💻 制作者', aboutVersion: '🏷️ 版本', aboutBug: '🐛 反馈 Bug', aboutBugLink: '点此反馈问题 →', aboutLang: '🌐 新增语言', aboutLangLink: '申请新增语言 →', }, ja: { skip: '⏭️ スキップ', skipped: '✅ スキップ完了!', notFound: '❌ 見つかりません', settings: '⚙️ 設定', theme: '🎨 テーマ', customColor: '🖌️ カスタムカラー', btnColor: 'ボタン', textColor: 'テキスト', borderRadius: '⬜ 角丸サイズ', opacity: '👁️ 透明度', autoHide: '🔁 自動非表示', autoHideDesc: '非アクティブ時にフェード', language: '🌐 言語', about: 'ℹ️ このスクリプトについて', version: 'バージョン', author: '作者', license: 'ライセンス', github: 'GitHub ↗', autoSkip: '🤖 自動スキップ', autoSkipDesc: '動画検出時に自動スキップ', autoSkipDelay: '⏱️ 自動スキップ遅延', seconds: '秒', notifyDuration: '🔔 通知表示時間', btnSize: '📐 ボタンサイズ', small: '小', medium: '中', large: '大', btnPosition: '📌 クイック配置', customText: '✏️ カスタムボタンテキスト', resetAll: '🔄 設定をリセット', resetConfirm: '全ての設定をリセットしますか?', exportSettings: '📤 エクスポート', importSettings: '📥 インポート', importSuccess: '✅ インポート完了!', importFail: '❌ インポート失敗', guiTheme: '🖥️ パネルテーマ', guiLight: 'ライト', guiDark: 'ダーク', guiBlack: '漆黒', corners: ['↖ 左上', '↗ 右上', '↙ 左下', '↘ 右下'], dragDelay: '🖱️ 長押しドラッグ遅延', ms: 'ms', tabPanel: 'パネル外観', tabBtn: 'ボタン外観', tabOther: 'その他', aboutSection: 'ℹ️ このスクリプトについて', aboutAuthor: '👨‍💻 作者', aboutVersion: '🏷️ バージョン', aboutBug: '🐛 バグを報告', aboutBugLink: 'こちらから報告 →', aboutLang: '🌐 言語を追加', aboutLangLink: '新しい言語をリクエスト →', }, ko: { skip: '⏭️ 건너뛰기', skipped: '✅ 건너뛰기 완료!', notFound: '❌ 찾을 수 없음', 设置: '⚙️ 설정', theme: '🎨 테마', customColor: '🖌️ 커스텀 색상', btnColor: '버튼', textColor: '텍스트', borderRadius: '⬜ 테두리 반경', opacity: '👁️ 투명도', autoHide: '🔁 자동 숨김', autoHideDesc: '비활성 시 페이드 처리', language: '🌐 언어', about: 'ℹ️ 정보', version: '버전', author: '작성자', license: '라이선스', github: 'GitHub ↗', autoSkip: '🤖 자동 건너뛰기', autoSkipDesc: '영상 감지 시 자동 건너뛰기', autoSkipDelay: '⏱️ 자동 건너뛰기 지연', seconds: '초', notifyDuration: '🔔 알림 표시 시간', btnSize: '📐 버튼 크기', small: '소', medium: '중', large: '대', btnPosition: '📌 빠른 위치', customText: '✏️ 버튼 텍스트 커스텀', resetAll: '🔄 모든 설정 초기화', resetConfirm: '모든 설정을 초기화할까요?', exportSettings: '📤 내보내기', importSettings: '📥 가져오기', importSuccess: '✅ 가져오기 완료!', importFail: '❌ 가져오기 실패', guiTheme: '🖥️ 패널 테마', guiLight: '라이트', guiDark: '다크', guiBlack: '순수 블랙', corners: ['↖ 왼쪽 위', '↗ 오른쪽 위', '↙ 왼쪽 아래', '↘ 오른쪽 아래'], dragDelay: '🖱️ 길게 누르기 지연', ms: 'ms', tabPanel: '패널 외관', tabBtn: '버튼 스타일', tabOther: '기타', aboutSection: 'ℹ️ 정보', aboutAuthor: '👨‍💻 제작자', aboutVersion: '🏷️ 버전', aboutBug: '🐛 버그 신고', aboutBugLink: '여기서 신고하기 →', aboutLang: '🌐 언어 추가', aboutLangLink: '새 언어 요청하기 →', }, }; const LANG_NAMES = { en: 'English', 'zh-TW': '繁體中文', 'zh-CN': '简体中文', ja: '日本語', ko: '한국어' }; const detectLang = () => { const nav = (navigator.languages?.[0] || navigator.language || 'en').toLowerCase(); if (nav.startsWith('zh-tw') || nav.startsWith('zh-hk')) return 'zh-TW'; if (nav.startsWith('zh')) return 'zh-CN'; if (nav.startsWith('ja')) return 'ja'; if (nav.startsWith('ko')) return 'ko'; return 'en'; }; const DEFAULTS = { theme: 'discord', btnColor: '#5865F2', btnTextColor: '#ffffff', btnRadius: '20', btnOpacity: '100', autoHide: false, lang: 'en', autoSkip: false, autoSkipDelay: '3', notifyDuration: '2', btnSize: 'M', customText: '', dragDelay: '200', guiTheme: 'dark', }; const load = (key) => { try { return GM_getValue(key, DEFAULTS[key]); } catch { return DEFAULTS[key]; } }; const save = (key, val) => { try { GM_setValue(key, val); } catch {} }; let cfg = Object.fromEntries(Object.keys(DEFAULTS).map(k => [k, load(k)])); const t = () => I18N[cfg.lang] || I18N.en; const THEMES = { discord: { bg: '#5865F2', text: '#ffffff' }, dark: { bg: '#1e1e1e', text: '#ffffff' }, light: { bg: '#e0e0e0', text: '#000000' }, green: { bg: '#43a047', text: '#ffffff' }, red: { bg: '#e53935', text: '#ffffff' }, purple: { bg: '#8e24aa', text: '#ffffff' }, }; const BTN_SIZES = { S: '8px 14px', M: '10px 20px', L: '14px 28px' }; const BTN_FONT = { S: '12px', M: '14px', L: '16px' }; // ── 面板主題定義 ── const PANEL_THEMES = { light: { panel: '#f2f3f5', header: '#e3e5e8', text: '#060607', subtext: '#747f8d', input: '#e3e5e8', inputText: '#060607', border: '#d4d7dc', divider: '#d4d7dc', accent: '#5865F2', btnBg: '#e3e5e8', btnText: '#060607', btnActiveText: '#ffffff', resetBg: '#fde8e8', resetText: '#d32f2f', }, dark: { panel: '#2b2d31', header: '#1e1f22', text: '#dbdee1', subtext: '#80848e', input: '#1e1f22', inputText: '#dbdee1', border: '#1e1f22', divider: '#3f4147', accent: '#ebebeb', btnBg: '#1e1f22', btnText: '#b0b3b8', btnActiveText: '#111111', resetBg: '#3a1e1e', resetText: '#e53935', }, black: { panel: '#0d0d0d', header: '#000000', text: '#e0e0e0', subtext: '#666666', input: '#1a1a1a', inputText: '#e0e0e0', border: '#000000', divider: '#222222', accent: '#ffffff', btnBg: '#1a1a1a', btnText: '#cccccc', btnActiveText: '#000000', resetBg: '#2a0a0a', resetText: '#ff5252', }, }; const pt = () => PANEL_THEMES[cfg.guiTheme] || PANEL_THEMES.dark; // ── 主按鈕 【修復】── const btn = document.createElement('button'); const getBtnText = () => cfg.customText.trim() || t().skip; btn.innerText = getBtnText(); btn.id = 'sq-skip-btn'; const applyBtn = () => { btn.style.cssText = ` position: fixed !important; bottom: 30px !important; right: 30px !important; z-index: 9999999 !important; background: ${cfg.btnColor} !important; color: ${cfg.btnTextColor} !important; border: none !important; border-radius: ${cfg.btnRadius}px !important; padding: ${BTN_SIZES[cfg.btnSize] || BTN_SIZES.M} !important; font-size: ${BTN_FONT[cfg.btnSize] || BTN_FONT.M} !important; font-weight: bold !important; cursor: grab !important; box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important; user-select: none !important; transition: background 0.2s, opacity 0.2s !important; opacity: ${cfg.autoHide ? '0.2' : Number(cfg.btnOpacity) / 100} !important; visibility: visible !important; display: block !important; pointer-events: auto !important; `; }; applyBtn(); // ── 跳過核心 ── const doSkip = () => { const i = t(); let found = false; document.querySelectorAll('video').forEach(el => { el.currentTime = el.duration; found = true; }); ['[class*="skip"]', '[class*="close"]', '[class*="dismiss"]'].forEach(sel => { document.querySelectorAll(sel).forEach(el => { if (el.offsetParent !== null) { el.click(); found = true; } }); }); btn.innerText = found ? i.skipped : i.notFound; setTimeout(() => { btn.innerText = getBtnText(); }, Number(cfg.notifyDuration) * 1000); }; // ── 自動跳過 ── let autoSkipTimer = null; const setupAutoSkip = () => { if (autoSkipTimer) clearInterval(autoSkipTimer); if (!cfg.autoSkip) return; autoSkipTimer = setInterval(() => { const videos = document.querySelectorAll('video'); videos.forEach(v => { if (!v.paused && !v.ended && v.duration && v.currentTime < v.duration) { setTimeout(() => { v.currentTime = v.duration; }, Number(cfg.autoSkipDelay) * 1000); } }); }, 2000); }; setupAutoSkip(); // ── 設定面板 ── const panel = document.createElement('div'); const applyPanelTheme = () => { const p = pt(); const wasVisible = panel.style.display === 'block'; const savedLeft = panel.style.left; const savedTop = panel.style.top; panel.style.cssText = ` display:${wasVisible ? 'block' : 'none'} !important; position:fixed !important; z-index:9999999 !important; width:310px !important; background:${p.panel} !important; color:${p.text} !important; border-radius:12px !important; box-shadow:0 8px 32px rgba(0,0,0,0.6) !important; font-family:'gg sans',sans-serif !important; font-size:14px !important; overflow:hidden !important; border:1px solid ${p.border} !important; max-height:85vh !important; overflow-y:auto !important; `; if (savedLeft) panel.style.left = savedLeft; if (savedTop) panel.style.top = savedTop; }; applyPanelTheme(); const row = (label, content) => `
${label}
${content}
`; const toggle = (id, checked, label, desc = '') => `
${label}
${desc ? `
${desc}
` : ''}
`; const buildPanel = () => { const i = t(); panel.innerHTML = `
${i.settings}
${i.tabPanel}
${row(i.language, ` `)} ${row(i.guiTheme, `
${[['light', i.guiLight], ['dark', i.guiDark], ['black', i.guiBlack]].map(([k, lbl]) => ` `).join('')}
`)}
${i.tabBtn}
${row(i.theme, `
${Object.entries(THEMES).map(([k,v]) => `
`).join('')}
`)} ${row(i.customColor, `
${i.btnColor} / ${i.textColor}
`)} ${row(i.btnSize, `
${['S','M','L'].map(s => ` `).join('')}
`)} ${row(i.btnPosition, `
${[['TL','top:10px;left:10px'],['TR','top:10px;right:10px'],['BL','bottom:10px;left:10px'],['BR','bottom:10px;right:10px']].map(([pos, s], idx) => ` `).join('')}
`)} ${row(`${i.borderRadius}: ${cfg.btnRadius}px`, ` `)} ${row(`${i.opacity}: ${cfg.btnOpacity}%`, ` `)} ${toggle('sq-autohide', cfg.autoHide, i.autoHide, i.autoHideDesc)} ${row(i.customText, ` `)}
${i.tabOther}
${toggle('sq-autoskip', cfg.autoSkip, i.autoSkip, i.autoSkipDesc)} ${row(`${i.autoSkipDelay}: ${cfg.autoSkipDelay}${i.seconds}`, ` `)} ${row(`${i.notifyDuration}: ${cfg.notifyDuration}${i.seconds}`, ` `)} ${row(`${i.dragDelay}: ${cfg.dragDelay}${i.ms}`, `
0${i.ms}500${i.ms}1000${i.ms}
`)}
${i.aboutSection}
${i.aboutAuthor} NiktoBlox
${i.aboutVersion} v1.0-Beta
${i.aboutBug}
${i.aboutBugLink}
${i.aboutLang}
${i.aboutLangLink}
`; // 關閉 panel.querySelector('#sq-close').onclick = () => panel.style.display = 'none'; // 語言 panel.querySelector('#sq-lang').onchange = (e) => { cfg.lang = e.target.value; save('lang', cfg.lang); buildPanel(); // 同步更新主按鈕文字跟隨語言 if (!cfg.customText.trim()) { btn.innerText = getBtnText(); } }; // GUI主題 panel.querySelectorAll('[data-guitheme]').forEach(guiBtn => { guiBtn.onclick = (e) => { e.stopPropagation(); cfg.guiTheme = guiBtn.dataset.guitheme; save('guiTheme', cfg.guiTheme); applyPanelTheme(); buildPanel(); }; }); // 主題色 panel.querySelectorAll('[data-theme]').forEach(div => { div.onclick = (e) => { e.stopPropagation(); cfg.theme = div.dataset.theme; cfg.btnColor = THEMES[cfg.theme].bg; cfg.btnTextColor = THEMES[cfg.theme].text; save('theme', cfg.theme); save('btnColor', cfg.btnColor); save('btnTextColor', cfg.btnTextColor); applyBtn(); buildPanel(); }; }); // 自訂顏色 const btnColor = panel.querySelector('#sq-btncolor'); const txtColor = panel.querySelector('#sq-txtcolor'); btnColor.onchange = (e) => { cfg.btnColor = e.target.value; save('btnColor', cfg.btnColor); applyBtn(); }; txtColor.onchange = (e) => { cfg.btnTextColor = e.target.value; save('btnTextColor', cfg.btnTextColor); applyBtn(); }; // 按鈕大小 panel.querySelectorAll('[data-size]').forEach(sizeBtn => { sizeBtn.onclick = (e) => { e.stopPropagation(); cfg.btnSize = sizeBtn.dataset.size; save('btnSize', cfg.btnSize); applyBtn(); buildPanel(); }; }); // 快速定位 panel.querySelectorAll('[data-pos]').forEach(posBtn => { posBtn.onclick = (e) => { e.stopPropagation(); posBtn.dataset.pos.split(';').forEach(p => { const [k, v] = p.split(':'); if (k && v) { btn.style[k] = v; } }); btn.style.right = 'auto'; btn.style.bottom = 'auto'; }; }); // 圓角 panel.querySelector('#sq-radius').oninput = (e) => { cfg.btnRadius = e.target.value; panel.querySelector('#sq-radius-val').innerText = e.target.value; applyBtn(); }; panel.querySelector('#sq-radius').onchange = (e) => { cfg.btnRadius = e.target.value; save('btnRadius', cfg.btnRadius); }; // 透明度 panel.querySelector('#sq-opacity').oninput = (e) => { cfg.btnOpacity = e.target.value; panel.querySelector('#sq-opacity-val').innerText = e.target.value; applyBtn(); }; panel.querySelector('#sq-opacity').onchange = (e) => { cfg.btnOpacity = e.target.value; save('btnOpacity', cfg.btnOpacity); }; // 自動隱藏 panel.querySelector('#sq-autohide').onchange = (e) => { cfg.autoHide = e.target.checked; save('autoHide', cfg.autoHide); applyBtn(); }; // 自訂文字 panel.querySelector('#sq-customtext').onchange = (e) => { cfg.customText = e.target.value; save('customText', cfg.customText); btn.innerText = getBtnText(); }; // 自動跳過 panel.querySelector('#sq-autoskip').onchange = (e) => { cfg.autoSkip = e.target.checked; save('autoSkip', cfg.autoSkip); setupAutoSkip(); }; // 自動跳過延遲 panel.querySelector('#sq-delay').oninput = (e) => { panel.querySelector('#sq-delay-val').innerText = e.target.value; }; panel.querySelector('#sq-delay').onchange = (e) => { cfg.autoSkipDelay = e.target.value; save('autoSkipDelay', cfg.autoSkipDelay); }; // 通知時間 panel.querySelector('#sq-notify').oninput = (e) => { panel.querySelector('#sq-notify-val').innerText = e.target.value; }; panel.querySelector('#sq-notify').onchange = (e) => { cfg.notifyDuration = e.target.value; save('notifyDuration', cfg.notifyDuration); }; // 拖曳延遲 panel.querySelector('#sq-dragdelay').oninput = (e) => { panel.querySelector('#sq-dragdelay-val').innerText = e.target.value; }; panel.querySelector('#sq-dragdelay').onchange = (e) => { cfg.dragDelay = e.target.value; save('dragDelay', cfg.dragDelay); }; // 匯出 panel.querySelector('#sq-export').onclick = () => { const data = JSON.stringify(cfg); const blob = new Blob([data], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `discord-orbs-skip-settings-${Date.now()}.json`; a.click(); }; // 匯入 panel.querySelector('#sq-import').onclick = () => { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = (evFile) => { const file = evFile.target.files[0]; const reader = new FileReader(); reader.onload = (evRead) => { try { const imported = JSON.parse(evRead.target.result); Object.keys(DEFAULTS).forEach(k => { if (k in imported) { cfg[k] = imported[k]; save(k, cfg[k]); } }); applyBtn(); applyPanelTheme(); buildPanel(); panel.style.display = 'block'; btn.innerText = t().importSuccess; setTimeout(() => { btn.innerText = getBtnText(); }, 2000); } catch { btn.innerText = t().importFail; setTimeout(() => { btn.innerText = getBtnText(); }, 2000); } }; reader.readAsText(file); }; input.click(); }; // 重設 panel.querySelector('#sq-reset').onclick = () => { if (!confirm(t().resetConfirm)) return; Object.keys(DEFAULTS).forEach(k => { cfg[k] = DEFAULTS[k]; save(k, DEFAULTS[k]); }); applyBtn(); applyPanelTheme(); buildPanel(); panel.style.display = 'block'; btn.innerText = getBtnText(); }; }; buildPanel(); // ── 面板定位 ── const positionPanel = () => { panel.style.display = 'block'; const rect = btn.getBoundingClientRect(); const pw = Math.min(310, window.innerWidth - 20); const ph = Math.min(panel.scrollHeight || 500, window.innerHeight * 0.85); panel.style.width = pw + 'px'; let left = rect.left - pw + btn.offsetWidth; let top = rect.top - ph - 10; if (left < 10) left = 10; if (top < 10) top = rect.bottom + 10; if (left + pw > window.innerWidth - 10) left = window.innerWidth - pw - 10; if (top + ph > window.innerHeight - 10) top = window.innerHeight - ph - 10; panel.style.left = left + 'px'; panel.style.top = top + 'px'; }; // ── 長按拖曳模式 ── let isDragging = false; let dragReady = false; let longPressTimer = null; let startX, startY, startLeft, startTop; const ring = document.createElement('div'); ring.style.cssText = ` position: fixed; z-index: 9999998; pointer-events: none; width: 0; height: 0; border-radius: 50%; border: 3px solid transparent; transition: none; display: none; `; document.body.appendChild(ring); const showRing = (x, y) => { const size = 54; ring.style.display = 'block'; ring.style.left = (x - size / 2) + 'px'; ring.style.top = (y - size / 2) + 'px'; ring.style.width = size + 'px'; ring.style.height = size + 'px'; ring.style.border = `3px solid ${cfg.btnColor}`; ring.style.boxShadow = `0 0 0 0 ${cfg.btnColor}44`; ring.style.animation = 'sq-ring-spin 1s linear forwards'; }; const hideRing = () => { ring.style.display = 'none'; ring.style.animation = 'none'; }; const styleEl = document.createElement('style'); styleEl.textContent = ` @keyframes sq-ring-spin { 0% { clip-path: inset(0 100% 0 0); opacity: 1; } 100% { clip-path: inset(0 0% 0 0); opacity: 1; } } @keyframes sq-ring-pulse { 0% { box-shadow: 0 0 0 0px rgba(88,101,242,0.5); } 100% { box-shadow: 0 0 0 10px rgba(88,101,242,0); } } `; document.head.appendChild(styleEl); btn.addEventListener('mousedown', (e) => { if (e.button !== 0) return; isDragging = false; dragReady = false; const rect = btn.getBoundingClientRect(); startX = e.clientX; startY = e.clientY; startLeft = rect.left; startTop = rect.top; showRing(e.clientX, e.clientY); btn.style.transition = 'transform 0.1s'; btn.style.transform = 'scale(0.92)'; longPressTimer = setTimeout(() => { dragReady = true; hideRing(); btn.style.transform = 'scale(1.1)'; btn.style.cursor = 'grabbing'; btn.style.boxShadow = `0 0 0 4px ${cfg.btnColor}66, 0 6px 20px rgba(0,0,0,0.4)`; btn.style.right = 'auto'; btn.style.bottom = 'auto'; btn.style.left = startLeft + 'px'; btn.style.top = startTop + 'px'; }, Number(cfg.dragDelay)); const onMouseMove = (e) => { if (!dragReady) return; isDragging = true; const dx = e.clientX - startX, dy = e.clientY - startY; btn.style.left = Math.max(10, Math.min(startLeft + dx, window.innerWidth - btn.offsetWidth - 10)) + 'px'; btn.style.top = Math.max(10, Math.min(startTop + dy, window.innerHeight - btn.offsetHeight - 10)) + 'px'; }; const onMouseUp = () => { clearTimeout(longPressTimer); hideRing(); btn.style.cursor = 'grab'; btn.style.transform = 'scale(1)'; btn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)'; dragReady = false; document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); }; document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); e.preventDefault(); }); btn.addEventListener('click', () => { if (isDragging) { isDragging = false; return; } doSkip(); }); btn.addEventListener('contextmenu', (e) => { e.preventDefault(); if (panel.style.display === 'block') { panel.style.display = 'none'; } else { buildPanel(); positionPanel(); } }); btn.addEventListener('mouseenter', () => { btn.style.opacity = '1'; btn.style.background = adjustColor(cfg.btnColor, -20); }); btn.addEventListener('mouseleave', () => { btn.style.background = cfg.btnColor; btn.style.opacity = cfg.autoHide ? '0.2' : String(Number(cfg.btnOpacity) / 100); }); document.addEventListener('click', (e) => { if (!panel.contains(e.target) && e.target !== btn) panel.style.display = 'none'; }); function adjustColor(hex, amount) { try { const n = parseInt(hex.slice(1), 16); const r = Math.max(0, Math.min(255, (n >> 16) + amount)); const g = Math.max(0, Math.min(255, ((n >> 8) & 0xff) + amount)); const b = Math.max(0, Math.min(255, (n & 0xff) + amount)); return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); } catch { return hex; } } document.body.appendChild(btn); document.body.appendChild(panel); // 【修復】最後確認一次按鈕可見 setTimeout(() => { btn.style.cssText += 'z-index: 9999999 !important; visibility: visible !important; display: block !important;'; console.log('✅ Skip Button Initialized Successfully!'); }, 500); })();