TinyURL Decoder


これなんか、どうも全部の短縮URLサービスの API を叩きにいってるらしい。力技はキライじゃないけど、こりゃあ、絶対に肉食っている人に違いない、間違いない! と思ったら、じつは日本の方らしい、というオチ。

// ==UserScript==
// @name           TinyURL Decoder
// @namespace      http://matatabi.homeip.net
// @description    decode shortened URLs to original ones
// @include        http://twitter.com/*
// @include        http://search.twitter.com/*
// @include        http://userscripts.org/scripts/show/40582
// @exclude        http://0rz.tw/*
// @exclude        http://55c.cc/*
// @exclude        http://6url.com/*
// @exclude        http://alturl.com/*
// @exclude        http://arm.in/*
// @exclude        http://awe.sm/*
// @exclude        http://b23.ru/*
// @exclude        http://bacn.me/*
// @exclude        http://bit.ly/*
// @exclude        http://bitly.com/*
// @exclude        http://bloat.me/*
// @exclude        http://buttn.me/*
// @exclude        http://budurl.com/*
// @exclude        http://burnurl.com/*
// @exclude        http://chilp.it/*
// @exclude        http://cli.gs/*
// @exclude        http://cuthut.com/*
// @exclude        http://digg.com/*
// @exclude        http://doiop.com/*
// @exclude        http://elfurl.com/*
// @exclude        http://ff.im/*
// @exclude        http://fff.to/*
// @exclude        http://good.ly/*
// @exclude        http://gol.ly/*
// @exclude        http://href.in/*
// @exclude        http://hub.tm/*
// @exclude        http://hurl.me/*
// @exclude        http://i5.be/*
// @exclude        http://icanhaz.com/*
// @exclude        http://idek.net/*
// @exclude        http://is.gd/*
// @exclude        http://justlink.us/*
// @exclude        http://kissa.be/*
// @exclude        http://kl.am/*
// @exclude        http://korta.nu/*
// @exclude        http://lin.cr/*
// @exclude        http://linkbee.com/*
// @exclude        http://liurl.cn/*
// @exclude        http://ln-s.net/*
// @exclude        http://lnk.by/*
// @exclude        http://lnk.in/*
// @exclude        http://lnkd.in/*
// @exclude        http://m2lb.info/*
// @exclude        http://merky.de/*
// @exclude        http://migre.me/*
// @exclude        http://minify.me/*
// @exclude        http://minurl.fr/*
// @exclude        http://minurl.org/*
// @exclude        http://moourl.com/*
// @exclude        http://myurl.in/*
// @exclude        http://ninjalink.com/*
// @exclude        http://nsfw.in/*
// @exclude        http://ow.ly/*
// @exclude        http://p.zurl.ws/*
// @exclude        http://peaurl.com/*
// @exclude        http://ping.fm/*
// @exclude        http://piurl.com/*
// @exclude        http://pnt.me/*
// @exclude        http://ponyurl.com/*
// @exclude        http://poourl.com/*
// @exclude        http://poprl.com/*
// @exclude        http://qurlyq.com/*
// @exclude        http://r.im/*
// @exclude        http://reallytinyurl.com/*
// @exclude        http://ri.ms/*
// @exclude        http://rt.nu/*
// @exclude        http://rubyurl.com/*
// @exclude        http://short.ie/*
// @exclude        http://short.la/*
// @exclude        http://short.to/*
// @exclude        http://shortener.net/*
// @exclude        http://shorturl.com/*
// @exclude        http://shuurl.com/*
// @exclude        http://simurl.com/*
// @exclude        http://slnk.me/*
// @exclude        http://smallr.com/*
// @exclude        http://sn.im/*
// @exclude        http://snipr.com/*
// @exclude        http://snipurl.com/*
// @exclude        http://snurl.com/*
// @exclude        http://so-smart.be/*
// @exclude        http://tighturl.com/*
// @exclude        http://tcrn.ch/*
// @exclude        http://tii.li/*
// @exclude        http://tini.us/*
// @exclude        http://tiny.cc/*
// @exclude        http://tinyurl.com/*
// @exclude        http://to.ly/*
// @exclude        http://togotor.us/*
// @exclude        http://tr.im/*
// @exclude        http://tra.kz/*
// @exclude        http://traceurl.com/*
// @exclude        http://tweetburner.com/*
// @exclude        http://twurl.nl/*
// @exclude        http://u.mavrev.com/*
// @exclude        http://u.nu/*
// @exclude        http://unhub.com/*
// @exclude        http://ur1.ca/*
// @exclude        http://url.az/*
// @exclude        http://url.ie/*
// @exclude        http://url.so-smart.be/*
// @exclude        http://urlcut.com/*
// @exclude        http://urlenco.de/*
// @exclude        http://viigo.im/*
// @exclude        http://w3t.org/*
// @exclude        http://wurl.ws/*
// @exclude        http://www.cloakreferer.com/*
// @exclude        http://www.dwarfurl.com/*
// @exclude        http://www.urlxp.com/*
// @exclude        http://www.x.se/*
// @exclude        http://xii.li/*
// @exclude        http://xr.com/*
// @exclude        http://xrl.us/*
// @exclude        http://yatuc.com/*
// @exclude        http://yep.it/*
// @exclude        http://zurl.ws/*
// @exclude        http://zz.gd/*
// ==/UserScript==

(function(d){
    function reset_cache() {
	var _cache = {};
	GM_setValue('cache', _cache.toSource());
	cache = _cache;
	show_cache();
    }
    GM_registerMenuCommand("Reset cache of TinyURL Decoder", reset_cache);

    function show_cache() {
	var _counter = 0;
	GM_log('==== Cached URLs are shown below: ====');
	for (key in cache) {
	    GM_log(key + ' --> ' + cache[key]);
	    _counter++;
	}
	GM_log('====  These ' + _counter + ' URLs are cached. ====');
    }
    GM_registerMenuCommand("Show cache of TinyURL Decoder", show_cache);

    function init_cache() {
	var _cache = GM_getValue('cache');
	if (typeof(_cache) === 'undefined')
	    _cache = {};
	else {
	    _cache = eval(_cache);
	    
	    var _counter = 0;
	    for (c in _cache) _counter++;
	    
	    var maxlength = GM_getValue('maxlength'); 
	    if (typeof(maxlength) === 'undefined') {
		maxlength = 100;
		GM_setValue('maxlength', maxlength);
	    }
	    
	    var oversize =  _counter - maxlength;
	    if (oversize > 0) {
		var __cache = {};
		for (key in _cache)
		    if (oversize > 0)
			oversize--;
		    else
			__cache[key] = _cache[key];
		_cache = __cache;
	    }
	}
	return _cache;
    }

    var cache = init_cache();
    var services = {
	'0rz.tw': true, '55c.cc': true, '6url.com': true,
	'alturl.com': true, 'arm.in': true, 'awe.sm': true,
	'b23.ru': true, 'bacn.me': true, 'bit.ly': true, 'bitly.com': true,
	'bloat.me': true, 'budurl.com': true, 'burnurl.com': true,
	'buttn.me': true,
	'chilp.it': true, 'cli.gs': true, 'cuthut.com': true,
	'digg.com': true, 'doiop.com': true, 'dwarfurl.com': true,
	/*'elfurl.com': true,*/
	'ff.im': true, /*'fff.to': true,*/
	'good.ly': true, 'gol.ly': true,
	'href.in': true, 'htxt.it': true, 'hub.tm': true, 'hurl.me': true,
	'i5.be': true, 'icanhaz.com': true, 'idek.net': true,
	'is.gd': true,
	'justlink.us': true,
	'kissa.be': true, 'kl.am': true, 'korta.nu': true,
	'linkbee.com': true, 'link.gs': true, 'lin.cr': true, 'liurl.cn': true,
	'lnkd.in': true, 'lnk.by': true, 'lnk.in': true, 'ln-s.net': true,
	'm2lb.info': true, 'merky.de': true, 'migre.me': true,
	'minify.me': true, 'minurl.fr': true, 'minurl.org': true,
	'moourl.com': true, 'moveto.ws': true, 'myurl.in': true,
	'ninjalink.com': true, 'nsfw.in': true,
	'oneclip.jp': true, 'ow.ly': true,
	'peaurl.com': true,'ping.fm': true, 'piurl.com': true,
	'pnt.me': true, 'poourl.com': true, 'poprl.com': true,
	'ponyurl.com': true, /*'p.zurl.ws': true,*/
	'qurlyq.com': true,
	'r.im': true, 'reallytinyurl.com': true, 'ri.ms': true,
	'rt.nu': true, 'rubyurl.com': true,
	'shorturl.com': true, 'short.ie': true,	'short.la': true,
	'short.to': true, 'shuurl.com': true, 'simurl.com': true,
	'slnk.me': true, 'snurl.com': true, 'smallr.com': true, 'sn.im': true,
	'snipr.com': true, 'snipurl.com': true, 'so-smart.be': true,
	'tcrn.ch': true, 'tighturl.com': true, 'tii.li': true, 'tini.us': true,
	'tinyurl.com': true, 'tiny.cc': true, 'togoto.us': true, 'to.ly': true,
	'traceurl.com': true, 'tra.kz': true, 'tr.im': true, 'twurl.nl': true,
	'u.nu': true, 'unhub.com': true, 'ur1.ca': true, 'urlcut.com': true,
	'urlenco.de': true, 'urlxp.com': true,'url.az': true, 'url.ie': true,
	'u.mavrev.com': true,
	'viigo.im': true,
	'w3t.org': true, 'wurl.ws': true, 'www.cloakreferer.com': true,
	'www.shortener.net': true, 'www.x.se': true,
	'xii.li': true, 'xrl.us': true, 'xr.com': true,
	'yatuc.com': true, 'yep.it': true,
	/*'zurl.ws': true,*/ 'zz.gd': true
    };

    function request_for(a) {
	if (a.hostname in {'bit.ly': true, 'bitly.com': true})
	    return 'http://api.bit.ly/expand?version=2.0.1&format=xml' +
	    '&login=tinyurldecoder&apiKey=R_42e55650000a21fa8cf2caa2778b5aaf' +
	    '&shortUrl=' + a.href;
	else if (a.hostname === 'chilp.it')
	    return 'http://p.chilp.it/api.php' + a.search;
	else if (a.hostname === 'cli.gs' && a.pathname.indexOf('/api') !== 0)
	    return 'http://cli.gs/api/v1/cligs/expand?clig=' + a.href;
	else if (a.hostname === 'digg.com' && a.search.length > 0)
	    return null;
	else if (a.hostname === 'good.ly' && a.pathname.indexOf('/-') === 0)
	    return null;
	else if (a.hostname === 'idek.net' && a.search.indexOf('?idek-api') !== 0)
	    return a.href + '?idek-api=true';
	else if (a.hostname === 'kissa.be' && a.pathname.indexOf('/api') !== 0)
	    return 'http://kissa.be/api/expander' + a.pathname;
	else if (a.hostname === 'migre.me' && a.pathname.indexOf('/api') !== 0)
	    return 'http://migre.me/api_redirect.xml?url=' + a.href;
	else if (a.hostname === 'poprl.com' && a.pathname.indexOf('/api') !== 0)
	    return 'http://poprl.com/api/lookup/alias' + a.pathname;
	else if (a.hostname === 'r.im' && a.pathname.indexOf('/api') !== 0)
	    return 'http://r.im/api/index.cfm?short_url=' + a.href;
	else if (a.hostname === 'togoto.us' && a.pathname.indexOf('l-api') !== 0)
	    return 'http://togoto.us/l-api.php?t=' + a.pathname.substr(1);
	else if (a.hostname === 'tr.im')
	    return 'http://api.tr.im/api/trim_destination.xml' +
	    '?trimpath=' + a.pathname.substr(1);
	else if (a.hostname === 'u.nu' && a.href.indexOf('?') > 0)
	    return 'http://u.nu' + a.pathname;
	else if (a.hostname === 'urlenco.de' &&
		 a.pathname.indexOf('PostJSON') !== 0)
	    return 'http://urlenco.de/PostJSON.aspx?decode=' + a.href;
	else if (a.hostname === 'zz.gd' && a.pathname.indexOf('/api') !== 0)
	    return 'http://zz.gd/api-decrypt.php?url=' + a.href;
	else
	    return a.href;
    }

    function extract_from_response(resp) {
	var pat = null;
	var a = document.createElement('a');
	a.href = resp.finalUrl;

	if (a.hostname === '6url.com')
	    pat = '<title>Redirects\\s+to\\s+(.*?)</title>';
	else if (a.hostname === 'arm.in')
	    pat = 'noresize><frame\\s+src="(.*?)"\\s+name="ContentWindow"';
	else if (a.href.indexOf('http://api.bit.ly/expand') === 0)
	    pat = '<longUrl>(.*?)</longUrl>';
	else if (a.href.indexOf('http://api.tr.im/api/trim_destination.xml') === 0)
	    pat = '<destination>(.*?)</destination>';
	else if (a.hostname === 'burnurl.com')
	    pat = '<link\\s+rel="canonical"\\s+href="(.*?)"\\s*/>';
	else if (a.hostname === 'digg.com')
	    pat = 'id="diggiFrame"\\s+name="diggiFrame"\\s+noresize="noresize"\\s+src="(.*?)"';
	else if (a.hostname === 'gol.ly' && a.pathname === '/tinyurl.php')
	    pat = '<iframe\\s+id="tree"\\s+name="tree"\\s+src="(.*?)"';
	else if (a.hostname === 'link.gs')
	    pat = '<frame\\s+src="(.*?)"\\s+name="Body">'
	else if (a.hostname === 'lin.cr')
	    pat = 'content="0;url=(.*?)"';
	else if (a.hostname === 'lnk.by')
	    pat = '<meta\\s+HTTP-EQUIV="REFRESH"\\s+content="0; url=(.*?)">'
	else if (a.hostname === 'lnk.in')
	    pat = "redirect\\('(.*?)'\\)";
	else if (a.hostname === 'm2lb.info')
	    pat = '<meta\\s+http-equiv="REFRESH"\\s+content="10;url=(.*?)">';
	else if (a.href.indexOf('http://migre.me/api_redirect.xml') === 0)
	    pat = '<url>(.*?)</url>';
	else if (a.hostname === 'minurl.org')
	    pat = 'getTwitterSearchResults\\("(.*?)"\\);';
	else if (a.hostname === 'myurl.in')
	    pat = 'window.location.href=\\("(.*?)"\\);';
	else if (a.hostname === 'nsfw.in')
	    pat = 'name="query"\\s+value="(.*?)"';
	else if (a.hostname === 'oneclip.jp')
	    pat = '<input\\s+type="hidden"\\s+name="data\\[Source\\]\\[url\\]"\\s+value="(.*?)"\\s+id="SourceUrl"\\s+/>';
	else if (a.hostname === 'ow.ly')
	    pat = 'Click\\s+<a\\s+href="(.*?)">here</a>';
	else if (a.hostname === 'p.zurl.ws')
	    pat = '<blockquote><strong>(.*?)</strong></blockquote>';
	else if (a.hostname === 'qurlyq.com')
	    pat = 'redirect\\(\\\'(.*?)\\\'\\);';
	else if (a.hostname === 'rt.nu')
	    pat = '<title>(.*?)</title>';
	else if (a.hostname === 'www.shortener.net')
	    pat = 'window.location="(.*?)";';
	else if (a.hostname === 'shuurl.com')
	    pat = 'directed\\s+to:\\s+<a\\s+href="(.*?)"';
	else if (a.hostname === 'simurl.com')
	    pat = 'content="0;\\s+URL=(.*?)"';
	else if (a.href.indexOf('http://tinyurl.com/preview.php') === 0)
	    pat = 'id="redirecturl"\\s+href="(.*?)"';
	else if (a.hostname === 'unhub.com')
	    pat = '<link\\s+href="(.*?)"\\s+rel="canonical"/>'
	else if (a.href.indexOf('http://urlenco.de/PostJSON.aspx') === 0)
	    pat = '"url"\\s+:\\s+"(.*?)"';
	else if (a.hostname === 'viigo.im')
	    pat = '<a\\s+href="(.*?)"\\s+title="Close">';
	else if (a.hostname === 'w3t.org')
	    pat = "window.location='(.*?)'";
	else if (a.hostname === 'xr.com')
	    pat = '<a\\s+href="(.*?)"\\s+target=parent>';
	else if (a.href.indexOf('http://cli.gs/api/v1/cligs/expand') === 0 ||
		 (a.hostname === 'idek.net' && a.search === '?idek-api=true') ||
		 a.href.indexOf('http://kissa.be/api/expander/') === 0 ||
		 a.href.indexOf('http://poprl.com/api/lookup/alias') === 0 ||
		 a.href.indexOf('http://p.chilp.it/api.php') === 0 ||
		 a.href.indexOf('http://r.im/api/index.cfm?short_url=') === 0 ||
		 a.href.indexOf('http://togoto.us/l-api.php') === 0 ||
		 a.href.indexOf('http://zz.gd/api-decrypt.php') === 0)
	    return resp.responseText.replace(/[\n\r]/g, '');

	if (pat) 
	    try {
		theUrl = resp.responseText.match(pat, 'i')[1];
	    } catch (err) {
		theUrl = null;
	    }
	else
	    theUrl = resp.finalUrl;
	
	return theUrl;
    }

    var working = {};
    function decode_shortened_url(_node) {
	if (cache[_node.href]) {
	    _node.title = 'was ' + _node.href;
	    _node.innerHTML = cache[_node.href];
	    _node.href = cache[_node.href];
	} else {
	    if (working[_node.href])
		return;
	    else
		working[_node.href] = true;

	    var _url = request_for(_node);
	    if (_url)
		GM_xmlhttpRequest({
		    method: "GET",
		    url: _url,
		    onload: function(resp) {
			finalUrl = extract_from_response(resp);
			if (finalUrl) {
			    cache[_node.href] = finalUrl;
			    working[_node.href] = false;
			}
		    },
		    onerror: function(resp) {
			GM_log('Oops! Access error to ' + _node.href);
		    }
		});
	}
    }

    function main() {
	var counter = 0;
	var anchors = d.getElementsByTagName('a');
	var anchors_length = anchors.length;
	for (var i=0; i<anchors_length; i++) {
	    if (anchors[i].pathname.length === 1 && anchors[i].search.length < 2)
		continue;
	    else if (anchors[i].hostname in services) {
		decode_shortened_url(anchors[i]);
		counter++;
	    }
	}
	GM_setValue('cache', cache.toSource());

	if (counter === 0) {
	    setTimeout(main, 60000);
	    working = {};
	} else {
	    setTimeout(main, 10000 / counter);
	}
    }

    main();
}(document));

こういうスタイルでやるヤツは、きっと wedata とか使えばいいんだと思われる。