JAMADAM.COM

RSS Subscribe to my RSS feed

Stripe Calendar

Sep, 2010
Aug 1213141516171819202122232425262728293031 Sep 123456

Entry: ついったーのTLを60件に保つグリモンを作った

ついったーのTLを60件に保つグリモンを作った

Initial post: 2010.02.24 | Last modified: 2010.02.24

ついったーのタイムラインが放っておくと超長くなってブラウザが重くなるので、タイムラインのサイズを一定に保つグリモンスクリプトを作った。60件より古いtweetをjQueryでremoveしている。内部的にデータがどうなってるのか分からないので効果は不明だけどDOMが減るので軽くなるだろうという憶測。

これをインストール

// ==UserScript==
// @name           tweetReduce
// @namespace      http://jamadam.com/
// @description    Avoid Twitter Timeline size get too long
// @include        http://twitter.com/*
// ==/UserScript==

(function() {

    var $;
    var jversion = '1.2.6';
    var jexist = (typeof unsafeWindow.jQuery != 'undefined');
    var conflict = (jexist && unsafeWindow.jQuery.fn.jquery != jversion);
    // Add jQuery if not loaded
    if (! jexist || conflict) {
        var GM_JQ = document.createElement('script');
        GM_JQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/' + jversion + '/jquery.js';
        GM_JQ.type = 'text/javascript';
        document.getElementsByTagName('body')[0].appendChild(GM_JQ);
    }

    GM_wait();

    // Check if jQuery's loaded
    function GM_wait() {
        if (typeof unsafeWindow.jQuery == 'undefined' || unsafeWindow.jQuery.fn.jquery != jversion) {
            window.setTimeout(GM_wait,100);
        } else {
            if (conflict) {
                $ = unsafeWindow.jQuery.noConflict(true);
            } else {
                $ = unsafeWindow.jQuery;
            }
            letsJQuery();
        }
    }

    // All your GM code must be inside this function
    function letsJQuery() {
        var topid = $('#timeline li').eq(0).attr('id');
        window.setInterval(reduce, 5000);
        function reduce() {
            var newid = $('#timeline li').eq(0).attr('id');
            if (topid != newid) {
                topid = newid;
                console.log($('#timeline li').length);
                while ($('#timeline li').length > 60) {
                    $('#timeline li').eq(60).remove();
                }
            }
        }
    }
})();

Entry: commons markerの件数をgoogle検索結果に表示するグリモンをつくった

commons markerの件数をgoogle検索結果に表示するグリモンをつくった

Initial post: 2010.01.14 | Last modified: 2010.01.15

[2010.01.15追記] 無駄な処理が多かったのでソースを少し整理した。

恥ずかしながら、commons markerというサービスを最近まで知らなかった。前々から出来たらいいなと思っていたことにすごく近いので使い始めてみた。

ところで、はてなブックマークのFirefoxアドオンはとても便利で、とくにGoogle検索の結果にブクマ件数が表示されるのは、クリックすべきリンクのアタリをつけるのに大変便利なのですが、同じ発想でcommons markerの件数も参考データとして意味があるかもしれないと思い、早速グリモンスクリプトで実現してみた。

googleResultWithMarker.user.jsをインストール

image

// ==UserScript==
// @name           googleResultWithMarker
// @namespace      http://jamadam.com/blog/
// @description    googleResultWithMarker
// @include        http://*.google.co.jp/search*
// ==/UserScript==


(function() {

    var $;
    var jversion = '1.3.2';
    var jexist = (typeof unsafeWindow.jQuery != 'undefined');
    var conflict = (jexist && unsafeWindow.jQuery.fn.jquery != jversion);
    // Add jQuery if not loaded
    if (! jexist || conflict) {
        var GM_JQ = document.createElement('script');
        GM_JQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/' + jversion + '/jquery.js';
        GM_JQ.type = 'text/javascript';
        document.getElementsByTagName('body')[0].appendChild(GM_JQ);
    }

    GM_wait();

    // Check if jQuery's loaded
    function GM_wait() {
        if (typeof unsafeWindow.jQuery == 'undefined' || unsafeWindow.jQuery.fn.jquery != jversion) {
            window.setTimeout(GM_wait,100);
        } else {
            if (conflict) {
                $ = unsafeWindow.jQuery.noConflict(true);
            } else {
                $ = unsafeWindow.jQuery;
            }
            letsJQuery();
        }
    }

    // All your GM code must be inside this function
    function letsJQuery() {

        var array = $('#res li.g');
        var procExist = false;
        var i = 0;
        var loop = function() {
            if (i < array.length) {
                if (procExist) {return;}
                procExist = true;
                var href = array.eq(i).find('h3 a').attr("href");
                var pos = array.eq(i).find('span.gl');
                var url = encodeURIComponent(href);
                var jsurl = "http://commonsmarker.com/tools/latest_info?url=" + url;
                unsafeWindow["__jsonp__aD3jHjaf27mZQxt"] = function(a) {
                    if (! a || ! a.marks || ! a.marks.length) {
                        return;
                    }
                    $("<a>" + a.marks.length + " markers</a>")
                        .attr('href', "http://commonsmarker.com/page/" + href)
                        .css({
                            backgroundColor:"#f5d0d0",
                            color:"#ff0000",
                            fontFamily:"arial,sans-serif",
                            fontWeight:"bold",
                            marginLeft:"4px",
                            padding:"1px",
                            fontSize:"85%"
                        })
                        .insertAfter(pos);
                }
                $.ajax({
                    type: "GET",
                    url: jsurl,
                    dataType: "script",
                    complete: function() {
                        i++;
                        procExist = false;
                        loop();
                    }
                });
            }
        }
        loop();
    }
})();

commons markerの公式のインターフェースはまだ機能不足なため他のユーザーの動向がよく掴めなかったんだけど、こうしてスクリプトで簡単に件数を把握できるようにしてみて分かったことは、「全然利用されてない」ということでした。予想はしていたけど。

なお、commons markerはこの手の外部スクリプトへのインターフェースや仕様を一切公開していません。今回のスクリプトはとても強引な処理と憶測で実装しているので、サーバーの仕様が変わって動かなくなることが大いに予想されます。

Entry: グリモンでjQueryのコンフリクトを避けるテンプレート

グリモンでjQueryのコンフリクトを避けるテンプレート

Initial post: 2009.11.10 | Last modified: 2009.11.10

昨日のつづき。

グリモンスクリプトでjQueryを使うとき、他のスクリプトでロードされたjQueryの別バージョンとのコンフリクトを避けるためのテンプレート。

(function() {
        
    var $;
    var jversion = '1.3';
    var jexist = (typeof unsafeWindow.jQuery != 'undefined');
    var conflict = (jexist && unsafeWindow.jQuery.fn.jquery != jversion);
    
    // Add jQuery if not loaded
    if (! jexist || conflict) {
        var GM_JQ = document.createElement('script');
        GM_JQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/' + jversion + '/jquery.js';
        GM_JQ.type = 'text/javascript';
        document.getElementsByTagName('body')[0].appendChild(GM_JQ);
    }
    
    GM_wait();
    
    // Check if jQuery's loaded
    function GM_wait() {
        if (typeof unsafeWindow.jQuery == 'undefined' || unsafeWindow.jQuery.fn.jquery != jversion) {
            window.setTimeout(GM_wait,100);
        } else {
            if (conflict) {
                $ = unsafeWindow.jQuery.noConflict(true);
            } else {
                $ = unsafeWindow.jQuery;
            }
            letsJQuery();
        }
    }
    
    // All your GM code must be inside this function
    function letsJQuery() {
    
    }
})();

a

Entry: グリモンでjQuery使ったらTwitterがバグった件が解決した

グリモンでjQuery使ったらTwitterがバグった件が解決した

Initial post: 2009.11.08 | Last modified: 2009.11.08

jQuery & Greasemonkey

Twitterの公式ページに適用するためのGreasemonkeyスクリプトにjQueryを使おうと思ったら、しばらくうまく行かなかった。原因は、TwitterページがすでにjQueryの旧バージョンをロードしていたというだけ。今回はたまたま自分のスクリプトがバージョンに依存しない内容だったので、if文でjQueryの存在確認をしてからロードすることで解決した。

(function() {

    // Add jQuery if not loaded
    if (typeof unsafeWindow.jQuery == 'undefined') {
        var GM_JQ = document.createElement('script');
        GM_JQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js';
        GM_JQ.type = 'text/javascript';
        document.getElementsByTagName('body')[0].appendChild(GM_JQ);
    }
    
    // Check if jQuery's loaded
    function GM_wait() {
        if (typeof unsafeWindow.jQuery == 'undefined') {
            window.setTimeout(GM_wait,100);
        } else {
            jQuery = unsafeWindow.jQuery;
            letsJQuery();
        }
    }
    GM_wait();

    // All your GM code must be inside this function
    function letsJQuery() {
        
    }
})();

Entry: ついったらータグs API(仮)

ついったらータグs API(仮)

Initial post: 2009.11.03 | Last modified: 2009.11.05

「ついったらータグs API(仮)」は、ついったらータグs(仮)のデータベースへの問い合わせをアプリケーションから利用するためのインターフェースを提供します。ついったらータグs(仮)のデータベースにはTwitterの各ユーザーページに対する、はてなブックマークタグの情報を格納しています。本APIを利用することで、任意のユーザーに対するタグ付けの情報はもちろんのこと、任意のタグがどのユーザーに適用されているかという情報を取得することができます。また、対象となるデータベースとして、独自のルールでタグ文字列の正規化を行ったテーブルを対象にすることもできます(任意)。

 全てのAPIは下記のURLで提供されます。

http://jamadam.com/th/

 現在サポートされている操作は、タグ-ユーザー検索(パラメータt=api/tag.json)とユーザー-タグ検索(パラメータt=api/user.json)です。いずれもレスポンスとしてJSONを返します。また、クエリーとしてcallbackを渡すことでJSONPを受け取ることもできます。

 

タグ-ユーザー検索

任意のタグ文字列を指定することで、このタグがつけられたユーザーのスクリーンネームのリストを得ます。

パラメータ

  • t (必須)
    "api/tag.json"を指定します。
  • str (必須)
    任意のタグを指定します。
  • nn (オプション)
    正規化されたテーブルを使用しないためのパラメータです。1を指定すると、はてなブックマークに登録された通りのタグ文字列が対象となります。デフォルトは0です。

 リクエスト例

/th/?t=api/tag.json&str=it

レスポンス例

{"result":[
{"user":"takapon_jp","occurance":"2"},
{"user":"m_kumagai","occurance":"1"},
{"user":"huehara88","occurance":"1"}
]}

 ※実際はレスポンスに改行は含まれません。

 

ユーザー-タグ検索

任意のスクリーンネームを指定することで、このユーザーに付与されたタグの一覧を得ます。

パラメータ

  • t (必須)
    "api/user.json"を指定します。
  • str (必須)
    任意のスクリーンネームを指定します。
  • nn (オプション)
    正規化されたテーブルを使用しないためのパラメータです。1を指定すると、はてなブックマークに登録された通りのタグ文字列が対象となります。デフォルトは0です。

リクエスト例

/th/?t=api/user.json&str=jamadam

レスポンス例

{"result":[
{"tag":"twitter","occurance":"38"},
{"tag":"音楽","occurance":"9"},
{"tag":"有名人","occurance":"4"},
{"tag":"webサービス","occurance":"4"}
]}

※実際はレスポンスに改行は含まれません。

 

実装例

ユーザー-タグ検索を利用してタグ一覧を取得するPerlコードは以下のようになります。

use LWP::UserAgent;
use JSON::XS;

my $tags = _twitterer_tags('jamadam');
foreach my $entry (@$tags) {
    print($entry->{tag});
    print(":");
    print($entry->{occurance}."回");
    print("¥n");
}

sub _twitterer_tags {
    my $user = shift;
    my $ua = LWP::UserAgent->new;
    my $res = $ua->get('http://jamadam.com/th/?t=api/user.json&str='. $user);
    my $obj = decode_json $res->content();
    return $obj->{result};
}

 

Entry: 縮.jpに外国人向けの機能をいくつかつけた

縮.jpに外国人向けの機能をいくつかつけた

Initial post: 2009.10.28 | Last modified: 2009.10.28

縮.jpに、IDとして使われた漢字の英訳を表示する機能を付けた。また、カスタムIDに外国語の単語などを入力した場合はエラーが出つつも、その単語の日本語訳から抽出した漢字を次候補として提案する機能をつけた。Google AJAX Language APIを使ってみたかっただけ。

Entry: 縮.jpに待望の「縮AGAIN」ボタンを搭載

縮.jpに待望の「縮AGAIN」ボタンを搭載

Initial post: 2009.10.08 | Last modified: 2009.10.16

縮.jp。7月に作って放置してたら10月になって話題になった。人生で一番集客した。そんな訳で、ネタを必要以上に膨らませるべく、システムを改修しました。見た目的にはあまり変化ありませんが、中身はほぼ丸々変わってます。特筆すべき変化は。。

待望の「縮AGAIN」ボタンを搭載

生成された短縮URLが気に入らなかった場合、このボタンを押すと別のURLを生成してくれます。なお、縮AGAINした場合、古いURLはしばらくして解放されます。

既設の短縮URLがあれば、短い順にご提案

今までは湯水のように新規作成してましたが、縮ボタンを押した際、まずは既設の短縮URLを提示するようになりました。縮AGEINすると短い順に既設URLが表示され、なくなれば新規作成されます。

転送時、末尾のゴミを可能な限り取り除く

Twitterでhttp://縮.jp/上これすげーみたいなつぶやきが多発していたので、こういう場合はhttp://縮.jp/上を検知して転送します。Perlでいうと、

$id =~ s/¥P{Han}.*//; 

となっています。漢字を表す正規表現なんて初めて知りました。

ちなみに、ファイルベースからPostgresqlに移行

パフォーマンスいいかなと思って、1件1ファイルなどというデータ管理をしてたんですが、IDがシーケンシャルに固定されるとか、逆引きできないとか、色々問題あったのでPostgresqlにしました。なお、ファイルベースのキャッシュの仕組みを導入したので転送時のパフォーマンスは以前と変わらない。はず。

APIにも変更あり

offsetというパラメータが新設されました。これは前述の既設URLの再利用と関連するもので、任意の既設URLを取り出すためのパラメータです。offsetを十分に大きくすると自動的に新規作成されます。通常は指定しないでください。

 

既知の問題

  • 元URLがbit.ly等ですでに短縮済みだった場合、iPhoneで転送されない。確かソフトバンクの仕様でiPhoneで多段リダイレクトできない。
  • Twitter用のいくつかのGreaseMonkeyスクリプトやアドオンで縮.jpへ飛べない。FirefoxのJavascriptのバグっぽい挙動が原因と思われる。対処法はこちら
  • Twitter周辺サービスで日本語ドメインが誤ってパーセントエンコードされているケースも見かけます。サーバサイドでの処理に問題があるのではないか(憶測)。
  • iPhoneの多くのアプリで開けない。おそらくアプリからwebkitだかのAPI的なものに渡すURLをpunycode変換してないのではないか(憶測)。対処法はこちらの12月15日の記事くらいしか見当たらなかった。

 

今後の予定

  1. 任意のIDを指定可能に
  2. 不人気文字を避ける仕組み
  3. 元URLがすでに短縮URLだった場合に展開してから短縮
  4. スパム対策

 

Entry: 縮 REST API

縮 REST API

Initial post: 2009.07.25 | Last modified: 2009.10.10

「縮 REST API」は、URL短縮/転送サービス「縮.jp」の機能を、アプリケーションから利用するためのインターフェースを提供します。

全てのAPIは下記のURL以下で提供されます。

http://api.xn--jj0a.jp/

現在サポートされている操作は、短縮URLの生成(generate.jsonへのPOST)と短縮URLの展開(lookup.jsonへのGET)です。いずれもレスポンスとしてJSONを返します。また、クエリーとしてcallbackを渡すことでJSONPを受け取ることもできるかもしれません。

 

GENERATE 

任意のURLを渡すことで、新規に短縮URLを生成します。

パラメータ

  • fUrl(必須)
    短縮対象のURL。http://かhttps://で始まる必要があります。
  • customId(オプション)
    任意の漢字の組み合わせで短縮URLの識別子を指定します。utf8でパーセントエンコードしてください。
  • offset(オプション)
    fUrlで指定したURLに対する既設の短縮URLがひとつ以上存在する場合、このパラメータに0以上の数値を指定することで任意の候補を選択することができます。既設URLは文字数の短い順に並んでいます。offsetが既設URLの数より大きいとき、新しいURLが生成されます。このパラメータは特殊な目的がない場合は指定しないでください。

 

リクエスト例

POST /generate.json

fUrl=http://example.com&id=%E4%B8%83

レスポンス例

{"result":{"fUrl":"http://example.com/","generated":"http://縮.jp/七"}}

 

LOOKUP

既存の短縮URLの識別子を受け取り、元のURLを返します。識別子はUTF-8でパーセントエンコードしてください。

パラメータ

  • id(必須)
    URLの識別子を指定します。
  • hash
    後方互換性のための、idの別名です。(廃止の予定)

リクエスト

GET /lookup.json?id=%E4%B8%83

レスポンス

{"result":{"fUrl":"http://example.com"}}

 

共通

HTTPレスポンスコード

正常/エラーに関わらず「200 OK」を返します。

エラー時のレスポンスボディ

下記の書式でJSONデータを返します。

{"error":"[エラーコード] [説明]"}

 

 エラーコード一覧

  • 400: generateの際、fUrlの書式が正しくない、customIdに不正な文字が含まれる、など。
  • 409: generateの際、customIdがすでに使用されている、など。
  • 404: lookupの際、指定のidが存在しない、など。

 

実装例

generate.jsonを利用して短縮URLを取得するPerlコードは以下のようになります。

use LWP::UserAgent;
use JSON::XS;

my $generated = _shuku('http://example.com');

sub _shuku {
    my $ua = LWP::UserAgent->new;
    my $res = $ua->post('http://api.xn--jj0a.jp/generate.json', {'fUrl' => $_[0]});
    my $obj = decode_json $res->content();
    my $decoded = $obj->{result}->{generated};
    utf8::decode($decoded);
    
    return $decoded;
}

 

Entry: はてブ窓にコメント表示した

はてブ窓にコメント表示した

Initial post: 2009.07.29 | Last modified: 2009.07.29

連日、日付越えの激務のため趣味の開発などには全く身が入らない、なんていいつつ、ささやかな成果物。はてブ窓にコメントを表示するようにした。これのためにどうでもいいコメントもつけた。

関係ないけど、はてブatomfeedのofパラメータってなんであんな回りくどい仕様なんだろう。このブログでは必要なデータ取得するのに下記みたいなことをやってみたけど、使い方間違ってる?

var hatebu_epp = 20; // API固定の取得件数
var disp_epp = 5; // 例えば5件表示
var page = 5; // 例えば5ページ目を取得
var ppr = hatebu_epp / disp_epp; // 1リクエストで4ページ分
var request_offset = Math.floor((page - 1) / ppr) * hatebu_epp; // ofパラメータは20
var local_offset = Math.floor((page - 1) % ppr) * disp_epp; // 取得したデータ中のオフセットは0

 

Entry: ブログにはてなブックマーク窓をつけた

ブログにはてなブックマーク窓をつけた

Initial post: 2009.07.14 | Last modified: 2009.07.14

つけた。自分のはてブをブログ上で公開しても意味ないんだけどね。露出狂なの。

ついったー窓のスクリプトを流用すべく、はてブのatomfeedで取得したRSSをjsonに変換する必要があったのでGoogleのfeed APIに丸投げ。最初、全部自前でやろうと思って、クロスドメイン対策用のproxyも作ってみたけど、今回は使用を見送った。別の機会に役に立ちそう。