8 posts tagged “twitter”
きっかけは MyMiniCity というサービスで,アクセス数に応じて自分の街が発展するという性質から Twitter 上にやたらと MyMiniCity へのリンクが貼られるようになった.個人的には興味がないのでスルーしていたが,そのうち巧妙な釣り文句と共に TinyURL を噛ませたリンクが貼られるようになって,迂闊に踏んでしまうとどうも悔しい.悔しいので対策することにした.
TinyURL のリンクを自動 decode している人は多いと思うが,自分も 自作 decoder を利用しているので,別途ページ中のリンクを走査して MyMiniCity らしきリンクをどうにかする Greasemonkey script を書けば解決するだろう.サクっと書いた.
// myminicity link detector
// (毎回 RegExp をコンパイルしているのは見逃して><)
var anchors = node.getElementsByTagName('a');
for (var n = 0, len = anchors.length; n < len; ++n) {
var anchor = anchors[n];
if (anchor.href.match(/^http:\/\/[^\.]+\.myminicity\.com\b/))
anchor.textContent = '<MyMiniCity Link>';
}
こいつを decoder script の後に動くようにしておけば偽装 MyMiniCity リンクもイチコロだぜ!! と思いきや,どうもうまく動かない.それもそのはずで,TinyURL decoder は tinyurl.com に対して decode 要求を行う際に GM_xmlhttpRequest による非同期通信を利用しており,decoder script が終了して更に myminicity link detector が終了してから実際の decode が行われる可能性もある.後続の処理を確実に動かすためには GM_xmlhttpRequest の onload ハンドラ内で行うしかないが,TinyURL decoder 内部に MyMiniCity 依存の処理を加えるのは気が引ける.
ところで,TinyURL 以外の URL 短縮サービスで MyMiniCity リンクが貼られることもあって,できればこれらも対応したい.最近は xrl.us という URL をよく見かける.他にも urltea.com, qurl.com, z.la など数えるとキリがないが,これらの中には明示的な decode 手段を用意していないものが多い.encode 用 API は用意されているが,decode はブラウザ任せとういか,実際に短縮 URL にリクエストしてみてリダイレクト時の location レスポンスヘッダを参照するしかない場合がある.GM_xmlhttpRequest は (自分の知る限り) リダイレクトに自動追随してしまうのでリダイレクト時の location レスポンスヘッダを参照することは恐らくできない.困ったものだ.
というか,decode API はともかく URL 短縮サービスは TinyURL のような Preview Mode を必ず備えているべきだと思うのだがどうよ.危なくね?
閑話休題,他の URL 短縮サービスに対応するとしても,要はどれもページ内のリンクを走査して何かを処理するというところは同じなので,その部分をうまく plugin 化できないものだろうか.AutoPagerize.addFilter のようにハンドラを登録して順次処理してくれる感じで.問題は前述の通り非同期処理を挟む可能性があるので単純にループでハンドラを回すだけでは目的は達成できない.
ハンドラが 3 つある場合は,このような処理をしたいわけだ:
if (should_do_1) {
GM_xmlhttpRequest({
onload: function(r) {
do_1();
if (should_do_2) {
GM_xmlhttpRequest({
onload: function(r) {
do_2();
if (should_do_3) {
GM_xmlhttpRequest({
onload: function(r) {
do_3();
else の場合も同様に次のハンドラへ処理を渡す.
というわけで,できた.
ページ内のリンクを走査し,登録されたハンドラ群に処理を渡すだけの Link Manipulator と,Link Manipulator の plugin として動作する TinyURL decoder と xrl.us decoder (これらは一つにまとめた),そして MyMiniCity のリンクを検出してアンカーテキストを置き換える detector を作った.detector は Twitter/Favotter 上で動作し,アンカーテキストを置き換えずに post そのものを非表示にすることもできる (今のところ要ソース修正 ;-).decoder は xrl.us → TinyURL といった二重 decode にも一応対応した.もちろん AutoPagerize 対応.
- Link Manipulator (管理用 script. 必須)
- Shorten URL Expander (各種短縮 URL 展開 plugin)
- Anti MyMiniCity (MyMiniCity リンク検出 plugin)
非同期処理が前提なので,スクリプトの実行順序に対して sensitive なのが玉に瑕.AutoPagerize → Link Manipulator → plugin scripts の順で並んでいないと意図通りに動かない (間に別のスクリプトが入るのは問題ないが).MyMiniCity のリンクをどうにかする場合は,事前に decoders を動かしておく必要がある.
TODO:
- decode 結果のキャッシュとクリア
- plugin 毎に処理するノードをカスタマイズできないか
- TinyURL, xrl.us 以外の短縮 URL 展開対応 → z.la に対応した (12/23)
というか,いまさら TinyURL ネタかよ :-(
install すると,favotter 上の各 post の下に星が出てきてクリックすると fav れる.既 fav かどうかは調べていないので,とりあえず全て白い星が表示される.fav った後は unfav もできる.
ついでに Twitter 上への permalink も設置する.
install: favotter-favor.user.js
前回の続き.「初心者向け Javascript 勉強会」の Greasemonkey に関する資料が公開されているブログエントリのコメント欄にて補足的なやりとりが見られる.どうやら Greasemonkey から提供された window オブジェクトはスクリプト間で共有されるらしい.確認してみよう.ようやく Firebug の "console" を覚えたのでソレで.
console.log('script a');
a = 10
window.b = 11;
if (typeof a != 'undefined') console.log(a);
if (typeof b != 'undefined') console.log(b);
if (typeof c != 'undefined') console.log(c);
if (typeof d != 'undefined') console.log(d);
もう一つ.
console.log('script b');
c = 20;
window.d = 21;
if (typeof a != 'undefined') console.log(a);
if (typeof b != 'undefined') console.log(b);
if (typeof c != 'undefined') console.log(c);
if (typeof d != 'undefined') console.log(d);
コンソールには "script 1" → "10" → "11" → "script 2" → "11" → "20" → "21" の順で表示された.window オブジェクトに定義したプロパティが引き継がれていることが分かる.script 2 側で "10" が表示されていないことから,Global オブジェクト != window オブジェクトであることも分かる (スコープチェイン中で a が未定義であることが前提).
で,ひとつ重要な事柄が.どうやら Opera の UserJS というもので Greasemonkey と似たようなことができるらしく,こちらでは機能が設けられた目的の違いから Greasemonkey のような保護が効かないようだ.つまり,スクリプトを Opera 対応させたい場合は匿名関数で wrap した方が良い,ということになる (その他,様々な cross browser hack も必要になるのだろう).恐らく,スピーカの方もこの件を伝えたかったのだと思う.
以上,今朝 Twitter で「グリモンスクリプト中の匿名関数は時代遅れのバッドノウハウ」と,やや刺激的に紹介されてしまったのでビビって補足している次第 (まぁでも間違ってないよな).ぶはは ;D
去る 7/14 に Twitter 発の「初心者向け Javascript 勉強会」が株式会社ノッキングオンにて開催された (こういった勉強会などを対象に会議室を提供して頂けるようだ.感謝!!).長文を書くのが苦手なので,あまり「まとめ」染みたことはできないのだが,印象に残っている範囲で一点だけ (一点かよ!!).
実は先週初めて Greasemonkey に触れて色々と遊んでいたのだが,学習用に目にした殆どのスクリプトにおいてコード本体を匿名関数で wrap しているのが気になった.
(function(){
// こんな感じ
})();
このケースで匿名関数を使う利点として,パッと思いつくのはこれくらい.
- 現行変数オブジェクトのプロパティを汚染しない
- 途中で return できる
- 自分自身を呼び出せる
2 は場合によっては使えるかもしれないが必須というわけではないし,3 が必要になることはそうないだろう.やはり 1 を目的にしているような気がする.script 要素から呼び出された外部スクリプトの実行コンテキストは呼び出し元と同一であることは周知の事実だが,別ファイルとして管理される greasemonkey script もそれと同様ということだろうか.
以前,Twitter 上の誰かの Favorite 経由で「gm を function で囲むのって unsafeWindow がなかったころのバッドノウハウだよね。なんとなく今でもラップしるけど、いらなんだよね。」 という発言を見てからは「なんだ,要らないじゃないか」と盲目的に思っていたのだが,Javascript 勉強会での Greasemonkey の話題でもやはり匿名関数による wrapping されたコードが雛型として挙がっていた.自分にとってこの点は非常にタイムリーだったので質問しようと思ったのだが,矢継ぎ早に以下の捕捉があった.
- これ (wrapping) がなくても不用意に元スクリプト環境を壊すことは無い.元スクリプト環境に触るには unsafeWindow を経由する必要がある.
- しかし,同一ページ上で動作する複数の greasemonkey script 同士で干渉が起きることを防ぐため,これ (wrapping) は入れなければならない.
うむ~,なるほど.前者は理解していたが,後者が本当であれば wrapping は必要ということになる.帰ってから試そう.という経緯で書いているのがこのエントリである.
恐らく事実上 official だと思われる "Dive Into Greasemonkey" の一節 を確認してみると「スクリプト間に悪影響を及ぼさないように取り計らってくれる (意訳)」,「自動的に匿名関数で wrap される (意訳)」等の記述が見られるので,やっぱり問題ないような気もする.でも実際に試してみた方がいいのだろう.
まずは "Dive Into Greasemonkey" にあった「自動的に匿名関数で云々」に少々引っかかりを感じるのだが,単に匿名関数で wrap されるだけであればスコープチェイン上に呼び出し元の変数オブジェクトが存在し,かつそれを触れることになってしまう.
例えば,こんな page script があったとして
var a = 10;
該当ページで以下の greasemonkey script が動作したとする.
a = 20;
動作後,a の値は (期待通り) 10 であるので,やはり単なる匿名関数ではないのだろう.一安心.
ん,まてよ.greasemonkey script が自動的に匿名関数内で動作するのであれば,先に挙げた
- 途中で return できる
- 自分自身を呼び出せる
も動くのでは!?
alert('a');
return;
alert('b');
うむ,return できる ("b" は表示されないし,return 時にエラーも起こらない).
更に自己再帰.
var i = arguments[0] || 0;
alert('i = ' + i);
if (i < 3) arguments.callee(i + 1);
おお,呼び出せる.これは知らなかった :-D
で,次.複数の greasemonkey script を動かした場合.
// greasemonkey script 1
alert('script 1');
var a = 11;
b = 12;
if (typeof a != 'undefined') alert(a);
if (typeof b != 'undefined') alert(b);
if (typeof c != 'undefined') alert(c);
if (typeof d != 'undefined') alert(d);
そして別ファイル.
// greasemonkey script 2
alert('script 2');
var c = 21;
d = 22;
if (typeof a != 'undefined') alert(a);
if (typeof b != 'undefined') alert(b);
if (typeof c != 'undefined') alert(c);
if (typeof d != 'undefined') alert(d);
alert された順番は "script 1" → "11" → "12" → "script 2" → "21" → "22" となり,やはり相互のスクリプト間で環境は独立しているように見える.
というわけで,やはり明示的に匿名関数で wrap する必要はないと改めて思ったのだが,まぁ単に「昔は必要だった (未確認) 」のと「コピペによる伝播」の影響が大きいんだろうなぁ.
当方,素人につき突っ込み歓迎 (免罪符).
2007-07-22 追記
補足エントリを書いた.
Twitter 上でアカウントが Ban される騒ぎが起きている.過密に投稿していると狙われるのだろうか.基準はよく分からないらしい.
Ban されたアカウント名を変更すれば新たに同名のアカウントを取得できるようなのだが,履歴は当然消えてしまうし Friends の復元も大変らしい.自分の Friends は数十人なので何とかならないこともないが,数百の人はザラにいるし,千を超える人も時折見掛ける.うひゃぁ.
というわけで,Friends 復元の手助けになればと思い,任意のアカウントの Friends を自分のアカウントに Copy する Web アプリを作ってみた.
http://phoneme.homelinux.org/twitter/repairer/
まず,Copy 先アカウントの username/password を入力し,更に Copy 元としたいアカウント名を入力すると,Copy 元アカウントの Friends 一覧が表示される.人数が多いとかなり待たされるかも知れない (入力された認証情報は一切保管されず,ログにも残らないので安心されたい).
一覧が表示された後は,任意のユーザを個別に Add したり,チェックされているユーザをまとめて Add したりできる.発展させれば復元のみならず,汎用 Friends 管理に使えるのではないだろうか.
ちなみに自分は Ban されるほどの頻度で投稿していないとは思うのでしばらくは安心だと思うのだが,このアプリのテストで頻繁に Add/Remove アクセスを繰り返してしまった.大丈夫だろうか XD.
また,念のため連続 Add 時の wait を調整できるようにしてはいるが,これまた Ban 対象行為になってしまうかもしれないという諸刃の剣.
てか,先駆者がいらしゃった.
http://d.hatena.ne.jp/shun262/20070602/twitter_autoadd
2007-06-05 追記
どうも Twitter の Add API 実装に問題があるようだ.いや,API だけではなく Web I/F 実装もだろうか (上記 URL の追記参照のこと).
そーいや一ヶ月くらい前から,自分が使っているテスト用サブアカウントを Add できなくなったんだよな….まさにこの現象か.Twitter にメールしたんだけど,定型文が帰ってきてオシマイだったとさ :P
Twitter をしばらく利用していて思ったのは,URL の扱いがナニであるということだ.Twitter は,文字列中の URL らしき文字列を自動的にリンクしてくれるのだが,どうも具合が悪い.
典型的なのが "(http://www.yahoo.co.jp)" という,URL を丸括弧で囲んだもので,Twitter 上では "http://www.yahoo.co.jp" がリンクとして扱われるのだが,丸括弧 "(", ")" は URL の一部として許されている文字なので,本来は閉じ括弧を含めた "http://www.yahoo.co.jp)" がリンクとして扱われるべきなのだが,よく見られる「発言内容 (補足 URL)」という形式は日本語 (?) としては自然で,かつ Twitter が書き手の意図通りにリンクしてしまうのでかなり氾濫している.
また,Google Maps の 等の,カンマ (",") が含まれる URL を正しく認識しないため,カンマ直前までが tinyurl に渡されて "http://maps.google.co.jp/maps?f=q&q=+38.694465,+139.538206" という入力が "http://tinyurl.com/26obzv,+139.538206" というリンクになってしまう.おや,前述の理屈で言えば "http://tinyurl.com/26obzv" までがリンクとして扱われるべきなのだが,どうやら path に含まれるカンマは URL の一部として扱われるようで,
- "http://maps.google.co.jp/maps?f=q&q=+38.694465,+139.538206" が渡された
- query 部のカンマ直前まで ("http://maps.google.co.jp/maps?f=q&q=+38.694465") が URL として認識され,tinyurl 圧縮される
- 結果として "http://tinyurl.com/26obzv,+139.538206" という文字列が生成されるが,ここに現れるカンマは path の一部として解釈されるので,再 parse されて結果として全体が URL として認識され,リンクされる
という仕組みになっているのだと思う.
さて,これらの挙動の何が問題なのか.前者は「丸括弧が含まれる URL が正しく認識されない」,「API を利用した別アプリケーションが (正しく) URL をリンクする際に投稿者の意図通りにリンクされない」といった問題がある.また,後者は Twitter 上でも投稿者の意図通りに扱われていないし,Twitter 以外のアプリケーションも正しく解釈できない.
よくよく考えると,カンマ記号は reserved な記号ので Google Maps の URL こそ Google の意図通りに扱われない可能性があるのではないか? と思ったが,よく分からない :-(
何より,API ベースのアプリケーションは正しく URL を検出・リンクしているのに,ユーザから「間違っている!! (Twitter と挙動が違う)」と言われるのは心苦しい.とはいえ,この事象に対してでどうやって「人に優しく自分に厳しく」するのか? と問われると返答に窮する.とりあえず Twitter にメールしてみた.
ちなみに,どちらが悪いのか分からないのだが,Twitter の投稿を Google Talk で受信した際に,"http://www.yahoo.co.jp[tag]" のようにブラケット記号を含んだ文字列が Twitter 側では "http://www.yahoo.co.jp" を URL として認識するのに Google Talk では "http://www.yahoo.cojp[tag" が URL として認識される.ううむ….
で,結論というか,conservative な人は RFC 2396 で (逆説的に) 推奨されていると読み取れる <http://www.yahoo.co.jp> や "http://www.yahoo.co.jp" という表記を用いるのが良いのではないか,と.
果たして海外で URL を丸括弧で囲む習慣があるのか不明だが,まぁ少なくとも自分にとっても合理的に思えるし,かつてのチルダ記号のように数の力で de facto にしてしまうのもアリかなぁとは思う (なので,基本的に conservative な自分としては微妙に歯切れが悪い).
それにしても,Vox ですらこのエントリ中の URL を意図通りに認識してくれなかったりして,URL 認識は各アプリケーションの都合によって随分と異なるので利用者は振り回される一方だなぁと改めて感じる.
Twitter に status を入力する際に & や < 等のメタ文字系を含めると困るという話.
まず,status に "&" を入力すると Web 経由での XHTML 生出力は "&" となる."&" ではないので,こいつを XML parser に食わせるとコケるのだが,Twitter の XHTML 出力はあくまで Web ブラウザ向けの出力なので XML parser に食わせる方が悪い,ということにしておく.幸い,XML API では "&" を,JSON API では "&" を (期待通りに) 返す.これはユーザ入力そのものなので問題なく再利用できる.
続いて,status に "<" を入力してみると XHTML 生出力は "<" となる."&" の時と挙動は異なるが,"<" をそのまま出力してしまうのは大問題なので納得しよう……と思ったのだが,なぜかコレが XML API 経由では "&lt;" が,JSON API では "<" が返される.なんだこりゃ.もしかして,Twitter は入力時に "<" を "<" に変換して保存しているのでは!? サニタイズ脳!?(ぷげら).ここでのユーザ入力はあくまで "<" なので,XML API では "<" が,JSON API では "<" が返らないと困るだろう.
さて,振り返って,なぜ "&" がサニタイズ (笑) されないのかを考えてみる.試しに,status に "&" と入力してみると,XHTML 出力は "&" となる."&amp;" ではない.ユーザ入力が (なぜか) そのまま出力されている.API の方はは問題くユーザ入力が (期待通り) そのまま出力されている.更に status に "@" と入力してみても,XHTML, API ともにユーザ入力がそのまま出力される.なるほど.文字実体参照や数値文字参照を特別扱いしているのだ.だから "&" が特別扱いされ,冒頭のような現象が起きるのだろう.
なぜ,このような謎仕様になっているのだろうか.
一つは,Web (XHTML) 表示時の便宜.大多数の一般のユーザは Web 経由の XHTML を通してしか Twitter と接しない.HTML を知っている人間が © と入力すれば Web 上で © と表示される,というのは悪くないアイデアのようにも思える.また,ブラウザによってはマルチバイト系の文字を数値文字参照表現で submit してしまう場合があるようだ (未確認).ユーザ入力は "ス" なのに "ス" が表示されてしまうのは期待に反する.
しかし,Twitter がブラウザ向け interface のみで完結しているシステムであればこれで問題はないが,API が存在するので事情は変わってくる.Web では "<" が表示されているのに API 経由では "<" が返ってくるのでは困る.API クライアントは Web ベースとは限らないので情報の再利用が困難になる.
VOX 上で複雑な表を描くのが困難なため,この問題に関する入出力対応表を別サイトに用意したので参照されたい.
さて,どうすればこの問題を改善できるのだろうか.無論,ユーザ入力に対する文字実体参照や数値文字参照表現の特別扱いを止め escape を出力時にのみ行う,という対処だけで良い
だが,対応表の下 3 行を眺めていると何となく特別視問題に関しても丸く治められるような気がしてくる.これらは全てブラウザの表示結果は同一 ("©") である.つまり,文字実体参照や数値文字参照と思しき文字列を可能な限り decode して Unicode 文字表現として (おそらく内部ストレージは Unicode 系だろう) 保持しておけばサニタイズ問題も解決するし,Web と API で結果が異なることも防げるのではないか.残念ながら,ユーザ入力と表示結果が異なる ("&" と入力すると "&" と表示されてしまう) 問題は残ったままになるが,表現の幅は広がるし,前述の文字参照 submit 問題が (存在するのであれば) 解決するので,悪くないトレードオフのように思える.
Twitter のサポート陣は割とレスポンスが良い (今のところ一日待たずに回答が返ってきている) ので,問題解決に関心のある方は連絡してみてはいかがだろうか.一応,この件に関しても連絡したのだが,うまく伝わっているか自信がない :-(
Twitter が流行っている.
自分も ID を取ってみたが,こーゆーものはケータイから更新したいと思うのが人情だ (そうか?).やはりというか,Twitter が提供する API を利用して,強者たちがケータイ版を数々と世に送り出し既に群雄割拠の体を成していたので,これらを利用すれば万々歳だ.
はて,本当にそうか!?
人様のサーバに別サービスの認証情報を預けるのって恐くね!?
あー,そういえば昔 mixi のケータイ版が提供されてなかったころに似たような話があったような (記憶不明瞭).ケータイの場合,アプリ版ブラウザとかみたいに通信を肩代わりせざるを得ない場合もあって,実はみんなそんな文化に慣れきっているとか?
まぁ,すぐにパスワード変えればいいやと思い,幾つか試しているうちに,「はー,へー,ほう…」と思い,気が付いたら自分で作っていた (意味不明).
迂闊にも PHP5 で書いてしまったらレンサバで動かなかったので,自宅サーバで公開してみるテスト.
http://phoneme.homelinux.org/twitter/
「人様のサーバに別サービスの認証情報を預けるのって恐くね!?」とか言った舌の根も乾かぬうちにナニなのだが,作ったら公開してみたくなるのが人情なので,きっと既存サイトもそういうメンタリティの元に放たれているのだろう.
一応,セッション ID の Referer 漏れや認証情報の扱いには気を遣っているので,そういう文化に慣れきっている人は利用されたい.
あー,なんか常体だと不自然になるな,こーゆー内容の場合….