您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Link Bing search results directly to real URL, show Gemini search results on the right side (PC only), and highlight ad links in green.
当前为
// ==UserScript== // @name Bing Plus // @version 1.3 // @description Link Bing search results directly to real URL, show Gemini search results on the right side (PC only), and highlight ad links in green. // @author lanpod // @match https://www.bing.com/search* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @require https://cdnjs.cloudflare.com/ajax/libs/marked/15.0.7/marked.min.js // @license MIT // @namespace http://tampermonkey.net/ // ==/UserScript== (function () { 'use strict'; /*** 공통 유틸 함수 ***/ const getUrlParam = (url, key) => new URL(url).searchParams.get(key); const patterns = [ { pattern: /^https?:\/\/(.*\.)?bing\.com\/(ck\/a|aclick)/, key: 'u' }, { pattern: /^https?:\/\/e\.so\.com\/search\/eclk/, key: 'aurl' }, ]; const isRedirectUrl = url => patterns.find(p => p.pattern.test(url)); const decodeRedirectUrl = (url, key) => { let encodedUrl = getUrlParam(url, key)?.replace(/^a1/, ''); if (!encodedUrl) return null; try { let decodedUrl = decodeURIComponent(atob(encodedUrl.replace(/_/g, '/').replace(/-/g, '+'))); return decodedUrl.startsWith('/') ? window.location.origin + decodedUrl : decodedUrl; } catch { return null; } }; const resolveRealUrl = url => { let match; while ((match = isRedirectUrl(url))) { const realUrl = decodeRedirectUrl(url, match.key); if (!realUrl || realUrl === url) break; url = realUrl; } return url; }; /*** 링크 URL 변환 로직 ***/ const convertLinks = root => { root.querySelectorAll('a[href]').forEach(a => { const realUrl = resolveRealUrl(a.href); if (realUrl && realUrl !== a.href) a.href = realUrl; }); }; /*** 광고 링크 스타일 적용 (초록색) ***/ GM_addStyle(`#b_results > li.b_ad a { color: green !important; }`); /*** PC 환경 확인 함수 ***/ const isPCEnvironment = () => window.innerWidth > 768 && !/Mobi|Android|iPhone|iPad|iPod/.test(navigator.userAgent); /*** Gemini 검색 결과 박스 생성 및 API 호출 로직 ***/ let apiKey; if (isPCEnvironment()) { apiKey = localStorage.getItem('geminiApiKey') || prompt('Gemini API 키를 입력하세요:'); if (apiKey) localStorage.setItem('geminiApiKey', apiKey); } const markedParse = text => marked.parse(text); const getPromptQuery = query => { const lang = navigator.language; if (lang.includes('ko')) return `"${query}"에 대한 정보를 마크다운 형식으로 작성해줘`; if (lang.includes('zh')) return `请以标记格式填写有关"${query}"的信息。`; return `Please write information about "${query}" in markdown format`; }; const createGeminiBox = () => { const box = document.createElement('div'); box.id = 'gemini-box'; box.innerHTML = ` <div id="gemini-header"> <img id="gemini-logo" src="https://www.gstatic.com/lamda/images/gemini_sparkle_v002_d4735304ff6292a690345.svg" alt="Gemini Logo"> <h3>Gemini Search Results</h3> </div> <hr id="gemini-divider"> <div id="gemini-content">Loading...</div> `; return box; }; GM_addStyle(` #gemini-box { max-width:400px; background:#fff; border:1px solid #e0e0e0; padding:16px; margin-bottom:20px; font-family:sans-serif; overflow-x: auto; } #gemini-header { display:flex; align-items:center; margin-bottom:8px; } #gemini-logo { width:24px; height:24px; margin-right:8px; } #gemini-box h3 { margin:0; font-size:18px; color:#202124; } #gemini-divider { height:1px; background:#e0e0e0; margin:8px 0; } #gemini-content { font-size:14px; line-height:1.6; color:#333; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word; } #gemini-content pre { background:#f5f5f5; padding:10px; border-radius:5px; overflow-x: auto; } `); let currentQuery; let geminiResponseCache; const fetchGeminiResult = query => { if (!apiKey) { document.getElementById('gemini-content').innerText = 'Error: No API key provided'; return; } GM_xmlhttpRequest({ method: 'POST', url: `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`, headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ "contents": [{ "parts": [{"text": getPromptQuery(query)}] }] }), timeout: 10000, onload({ responseText }) { if (currentQuery !== query) return; try { const response = JSON.parse(responseText); console.log('Gemini API Response:', response); // 디버깅용 출력 if (!response || !response.candidates || response.candidates.length === 0) { document.getElementById('gemini-content').innerText = 'No content available: API returned empty response'; return; } geminiResponseCache = response.candidates[0]?.content?.parts?.[0]?.text; if (!geminiResponseCache) { document.getElementById('gemini-content').innerText = 'No content available: Response lacks valid text'; return; } document.getElementById('gemini-content').innerHTML = markedParse(geminiResponseCache); } catch (e) { document.getElementById('gemini-content').innerText = `Error parsing response: ${e.message}`; } }, onerror() { document.getElementById('gemini-content').innerText = 'API request failed: Network error'; }, ontimeout() { document.getElementById('gemini-content').innerText = 'API request failed: Timeout (response took too long)'; } }); }; const ensureGeminiBox = () => { if (!isPCEnvironment()) return; let contextEl = document.getElementById('b_context'); if (!contextEl) return; let geminiBoxEl = document.getElementById('gemini-box'); if (!geminiBoxEl) { geminiBoxEl = createGeminiBox(); contextEl.prepend(geminiBoxEl); } const queryParam = new URLSearchParams(location.search).get('q'); if (queryParam !== currentQuery) { currentQuery = queryParam; fetchGeminiResult(queryParam); } }; let lastHref = location.href; new MutationObserver(() => { if (location.href !== lastHref) { lastHref = location.href; ensureGeminiBox(); convertLinks(document); } }).observe(document.body, { childList: true, subtree: true }); convertLinks(document); if (isPCEnvironment()) ensureGeminiBox(); })();