// ==UserScript==
// @name Faster Bing
// @namespace CoderJiang
// @version 1.0.2
// @description 将 Bing 的重定向 url 转换为真实 url
// @author CoderJiang
// @match https://www.bing.com/*
// @match http://www.bing.com/*
// @match http://cn.bing.com/*
// @match https://cn.bing.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const Config = {
/**
* 日志配置
* log.enable 是否打印日志
* log.success.icon 成功的图标
* log.fail.icon 失败的图标
* log.success.originalLink.maxLength 原始链接最大长度,如果为 - 1 则不截断
* log.success.originalLink.frontChars 原始链接前面保留的字符数
* log.success.realLink.maxLength 真实链接最大长度,如果为 - 1 则不截断
* log.success.realLink.frontChars 真实链接前面保留的字符数
* log.fail.originalLink.maxLength 原始链接最大长度,如果为 - 1 则不截断
* log.fail.originalLink.frontChars 原始链接前面保留的字符数
* log.fail.realLink.display 失败时不显示真实链接,用这个字符串代替
*/
log: {
enable: true,
success: {
icon: '✅',
originalLink: {
maxLength: 30,
frontChars: 10,
},
realLink: {
maxLength: 30,
frontChars: 20,
}
},
fail: {
icon: '❎',
originalLink: {
maxLength: -1,
frontChars: 20,
},
realLink: {
display: '------',
}
}
}
}
const logo = `
________) ______
(, / (, / ) ,
/___, _ _ _/_ _ __ /---( __ _
) / (_(_/_)_(___(/_/ (_) /____)_(_/ (_(_/_
(_/ (_/ ( .-/
(_/`;
/**
* 获取 url 中的参数
*
* @param url url
* @param key 参数名
* @returns {string} 参数值,如果没有则返回 null
*/
function getUrlParameter (url, key) {
const parsedUrl = new URL (url);
const queryParams = parsedUrl.searchParams;
return queryParams.get (key);
}
/**
* 判断是否是 bing 的重定向 url
*
* @param url url
* @returns {boolean} 是否是 bing 的重定向 url
*/
function isBingRedirectUrl (url) {
const pattern = /^https?:\/\/(.*\.)?bing\.com\/ck\/a/;
return pattern.test (url);
}
/**
* 判断 url 是否有效
* @param urlString url 字符串
* @returns {boolean} 是否是有效的 url
*/
function isValidUrl (urlString) {
try {
new URL (urlString);
return true; // 没有抛出异常,URL 有效
} catch (error) {
return false; // 抛出异常,URL 无效
}
}
/**
* 将重定向 url 转换为真实 url
* @param url Bing 的重定向 url,必须包含参数 u
* @returns {string|null} 真实 url,转换失败则返回 null
*/
function redirect2RealUrl (url) {
let urlBase64 = getUrlParameter (url, 'u')
urlBase64 = urlBase64.replace (/^a1/, '');
let realUrl = ''
try {
realUrl = atob (urlBase64);
} catch (error) {
console.log ('url:', url)
console.log ('urlBase64:', urlBase64)
return null;
}
if (!isValidUrl (realUrl)) {
return null;
}
return realUrl;
}
/**
* 找到所有的链接并转换为真实 url
* @returns {{failedUrls: *[], urls: *[], count: number}}
*/
function convertBingRedirectUrls () {
const failedUrls = [];
const processedUrls = [];
const links = document.querySelectorAll ("a");
for (const link of links) {
const href = link.href;
if (isBingRedirectUrl (href)) {
const realUrl = redirect2RealUrl (href);
if (realUrl) {
link.href = realUrl;
processedUrls.push ({
realUrl,
originalUrl: href
});
} else {
failedUrls.push (href);
}
}
}
return {
processedUrls,
failedUrls
};
}
/**
* 可视化结果
*
* @param result 结果
*/
function log (result) {
function middleTruncate (str, maxLength, frontChars) {
if (maxLength < 0) return str;
if (str.length > maxLength) {
const backChars = maxLength - frontChars - 3; // 确保总长度不超过 maxLength
return str.substring (0, frontChars) + '...' + str.substring (str.length - backChars);
} else {
return str;
}
}
const overview = {
"Success": result.processedUrls.length,
"Failed": result.failedUrls.length,
}
const details = [];
const successSample = {
"Status": Config.log.success.icon,
"Original": "",
"Real": "",
}
const failedSample = {
"Status": Config.log.fail.icon,
"Original": "",
"Real": Config.log.fail.realLink.display,
}
for (const processedUrl of result.processedUrls) {
const success = {...successSample};
success.Original = middleTruncate (
processedUrl.originalUrl,
Config.log.success.originalLink.maxLength,
Config.log.success.originalLink.frontChars);
success.Real = middleTruncate (
processedUrl.realUrl,
Config.log.success.realLink.maxLength,
Config.log.success.realLink.frontChars);
details.push (success);
}
for (const failedUrl of result.failedUrls) {
const failed = {...failedSample};
failed.Original = middleTruncate (failedUrl,
Config.log.fail.originalLink.maxLength,
Config.log.fail.originalLink.frontChars);
details.push (failed);
}
console.table (overview);
console.table (details);
}
const result = convertBingRedirectUrls ();
console.log (logo);
if (Config.log.enable) {
log (result)
}
})();