// ==UserScript==
// @name Advanced Bing
// @version 1.0
// @description Link Bing search results directly to real URL, show Gemini search results on the right side, and highlight ad links in green.
// @author lanpod
// @match https://www.bing.com/search*
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @license MIT
// @namespace http://tampermonkey.net/
// @downloadURL none
// ==/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; }`);
/*** Gemini 검색 결과 박스 생성 및 API 호출 로직 ***/
let apiKey = localStorage.getItem('geminiApiKey') || prompt('Gemini API 키를 입력하세요:');
if (apiKey) localStorage.setItem('geminiApiKey', apiKey);
const markedParse = text => text
.replace(/^### (.*$)/gm, '
$1
')
.replace(/^## (.*$)/gm, '$1
')
.replace(/^# (.*$)/gm, '$1
')
.replace(/^\* (.*$)/gm, '$1')
.replace(/^- (.*$)/gm, '$1')
.replace(/``````/gs, '$1
')
.replace(/\*\*(.*?)\*\*/g, '$1')
.replace(/(?$1')
.replace(/\n/g, '
');
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 = `
Loading...
`;
return box;
};
GM_addStyle(`
#gemini-box { max-width:400px; background:#fff; border:1px solid #e0e0e0; padding:16px; margin-bottom:20px; font-family:sans-serif; }
#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; }
`);
let currentQuery;
let geminiResponseCache;
const fetchGeminiResult = query => {
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) }] }] }),
onload({ responseText }) {
if (currentQuery !== query) return;
try {
geminiResponseCache = JSON.parse(responseText)?.candidates?.[0]?.content?.parts?.[0]?.text || 'No response';
document.getElementById('gemini-content').innerHTML = markedParse(geminiResponseCache);
} catch {
document.getElementById('gemini-content').innerText = 'Error parsing response';
}
},
onerror() { document.getElementById('gemini-content').innerText = 'API request failed'; }
});
};
const ensureGeminiBox = () => {
const queryParam = new URLSearchParams(location.search).get('q');
if (!queryParam) return;
let contextEl = document.getElementById('b_context');
if (!contextEl) return;
let geminiBoxEl = document.getElementById('gemini-box');
if (!geminiBoxEl) contextEl.prepend(createGeminiBox());
if (queryParam === currentQuery && geminiResponseCache) {
// 캐시된 응답이 있으면 표시
document.getElementById('gemini-content').innerHTML = markedParse(geminiResponseCache);
} else {
// 새로운 쿼리일 경우 API 호출
currentQuery = queryParam;
fetchGeminiResult(queryParam);
}
};
// URL 변경 감지 및 Gemini 박스 유지
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);
ensureGeminiBox();
})();