☆Twitterのタイムラインをマージする

Twitterのタイムラインをマージしたいと思って、下記のようなスクリプトを書きました。
毎度初心者レベルなものですいません。
作った環境はUbuntu Karmic Koala。
libdatetime-format-dateparse-perl パッケージを導入していることが前提です。
導入した覚えがない場合は下記のようにして導入してください。
これを使う理由は☆Twitter の投稿時刻をPerlでローカル時刻にするを見てください。

$ sudo apt-get install libdatetime-format-dateparse-perl

tw_merge.shtw_merge_rev.pl をダウンロードして、実行権限をつけ、下記のようにして使います。
~/tmp というディレクトリを作るか、tw_merge.sh の HTML_TMP_DIR の指定を適当に変えてください。
スクリプトで作成したファイルは残ったままになるので、ときどき手動で削除してください。

使用例
$ tw_merge.sh _hito_ mizuno_as pores_n ikunya seotch henrich
$ tw_merge.sh 'cc_jp?max_id=11149920499&per_page=20' 'henrich?max_id=11149420427&per_page=24' 'Say_no?max_id=11150167435&per_page=4'
$ tw_merge.sh 'HPLinuxJP?max_id=11248010182&per_page=7' 'henrich?max_id=11192408823&per_page=2' 'henrich?max_id=11247481244&per_page=3'

ブラウザで結果が表示されます。
結果はTwitterのWeb表示とは逆順になります。
通常と同じ順番が場合はtw_merge_rev.plの下から4行目
「sort keys %TWEET」の部分を「reverse sort keys %TWEET」に修正してください。

最初の例の場合、リストと違って発言の多い人と少ない人でズレがでてきてしまうのが難点です。
揃えたい場合はper_pageでTweetの数を調整します。
古い時期の発言を揃えて見たい場合はmax_idを使います。

max_idやper_pageはあらかじめFirefoxで見たいところを選択して選択部分のソースをみて調べておきます。
(これが一番大変なのですが、簡単な方法が思いつきませんでした。)

一応リストもマージ対象に出来ます。 通常 user_name のところに user_name/list_name としてやればOKです。
(ちょっと表示がおかしくなりますが、読めないことはないです。)
リストが対象に入っている場合は、リストを引数の先頭にしてください。

tw_merge.sh
#!/bin/bash
HTML_TMP_DIR=~/tmp
OUT_HTML=${HTML_TMP_DIR}/TwitRev-`date '+%F-%H%M%S'`-$$.html

i=0
for arg in $*
do
  html=${HTML_TMP_DIR}/`echo $arg | sed -e 's#[/?&]#_#g'`.html
#  HTMLS="$HTMLS $html"
  HTMLS[$i]=$html
  wget -O - http://twitter.com/$arg |sed -e 's#</li>#</li>\n#;' > ${html}
  i=$(($i+1))
done

tw_merge_rev.pl ${HTMLS[*]} > $OUT_HTML
x-www-browser $OUT_HTML &
tw_merge_rev.pl
#!/usr/bin/perl
use DateTime;
use DateTime::Format::DateParse;
my $tzhere = DateTime::TimeZone->new( name => 'local' );

my $thumb_span_tmplt = <<'EOF';
<span class="thumb vcard author">
<a href="http://twitter.com/%s" class="tweet-url profile-pic url">
<img alt="%s" class="photo fn" height="48" src="%s" width="48" />
</a>
</span>
EOF


sub repTime
{
  my $gmt = shift;
  $dt = DateTime::Format::DateParse->parse_datetime($gmt);
  $dt->set_time_zone($tzhere);
}

my $is_header_saved = 0;
my $is_footer_saved = 0;
my (@HEADER, @FOOTER);
my (%TWEET);

for $twhtml (@ARGV){
    open TWHTML, $twhtml or die "$!:$twhtml\n";

    my $screen_name;
    my $url_screen_name;
    my $thumb_span;

    # search start of tweets.
    while(<TWHTML>){
      if(not $is_header_saved){
        s#<head>#<head><base href="http://twitter.com/" />#;
        push @HEADER, $_;
      }
      last if m#<ol id='timeline' class='statuses'>#;

      # build screen_name span
      if(!$screen_name and m#<meta content="(.+)" name="page-user-screen_name" />#){
        $screen_name = $1;
        $url_screen_name = qq(<strong><a href="http://twitter.com/$1">$1</a></strong>);
      }

      # buid thumb span
      if(!$thumb_span and m#<a href="/account/profile_image/.*src="([^"]+)"#){
        my $src_url = $1;
        $src_url =~ s#_bigger\.(\w+)$#_normal.\1#;
        $thumb_span = sprintf $thumb_span_tmplt, $screen_name, $screen_name, $src_url;
      }
    }
    $is_header_saved = 1;

    # save tweets
    OUTER:
    while(1){
      my($id, $tweet);
      while(<TWHTML>){
        last OUTER if m#</ol>#;
        $id = $1 if /id="status_(\d+)"/;
        s#(<span class="published timestamp" data="{time:'([^']+)'}">)[^<]+</span>#$1 . repTime($2) . "</span>"#e;
        $tweet .= $_;
        last if m#</li>#;
      }

      # add $url_screen_name and $thumb_span if not contained.
      if( $tweet !~ m#class="tweet-url screen-name"#){
        $tweet =~ s#<span class="status-body">#$thumb_span <span class="status-body" style="padding-left:48px;">#;
        $tweet =~ s#<span class="status-content">#<span class="status-content"> $url_screen_name#;
      }

      $TWEET{$id} = $tweet;
    }

    if(not $is_footer_saved){
        @FOOTER = $_;
        # save remaining
        while(<TWHTML>){
          push @FOOTER, $_;
        }
        $is_footer_saved = 1;
    }

    close TWHTML;
}

print @HEADER;
for (sort keys %TWEET){
    print $TWEET{$_};
}
print @FOOTER;

☆軽量で操作性の良いTwitterクライアントPino

少し前に見つけたPinoというTwitterクライアントの紹介です。
TwitterのほかにIdenti.ca というのにも対応しているようです。
Valaとう言語で書かれています。
Version 0.21では日本語が入力できなかったのですが、0.24になって日本語入力できるようになりました。
残念ならが日本語化されていません。
本家サイトはこちら
pino-twitter - Project Hosting on Google Code

導入方法は本家サイトにも書いてある通りで、Karmic以上であれば下記のコマンドで導入できます。
Lucid Beta-1でも同じ操作で導入できることを確認しています。

$ sudo add-apt-repository ppa:vala-team/ppa 
$ sudo add-apt-repository ppa:troorl/pino
$ sudo apt-get update
$ sudo apt-get install pino

画面はLucidに入っているGwibberと似ています。検索機能や複数のTLを流す機能はありません。
気にいっている点は、
・軽快な動作
・Ctrl+1,Ctrl+2,Ctrl+3,Ctrl+4でHome、Mentions、Direct Message、User Infoを切替可能
・投稿はAlt+Enter
最後の投稿操作は何処にも書いていなかったので迷いました。
しかし私は漢字変換の確定にEnterを使うので、誤ってPostしてしまうことが少なくてよいと思っています。


メイン画面のスクリーンショット

User InfoはHome TimelineやMentionsでプロフィール画像をダブルクリックして表示する方法と、
ユーザ名を入力する表示する方法の2種類があります。
残念ながらダイレクトメッセージを頂いたことがないのでDirect Messagesはさびしいスクリーンショットになってしまいました。

Home Timeline (Ctrl+1) Mentions (Ctrl+2) Direct Messages (Ctrl+3) User Info (Ctrl+4) [Show User]押下後のUser Info

メニューの中身

これは見たまんまです。特に説明は不要だと思います。
複数のアカウントを登録して切り替えることが出来そうですが、一つしかアカウントを持っていないのでわかりません。

[Pino] [Edit] [View] [Help]

Preferences(設定画面)

デフォルトではフォントサイズが9ポイントなのですが、日本語は9ポイントではちょっときついと思うので大きくすることをお勧めします。
[Enable Spell checking]はオフの方がいいと思います。(これを書いていて気付きました。)

Main Desktop Account Appearance

gnome-sudoku bug evidence

initial state
Symbol definition
A:Cell(1,5)
B:Cell(1,7)
C:Cell(2,9)

Case1 and Case6 are wrong.
Last input Number dose not change to red.

Case1
A->B->C
Case2
A->C->B
Case3
B->A->C
Case4
B->C->A
Case5
C->A->B
Case6
C->B->A

☆KarmicにBlogtk2をいれるとGwibberが起動しなくなる件

KarmicでjayredingさんのPPAを追加してblogtkを導入すると、gwibberが起動しなくなることに気付きました。
blogtk2の起動でも出た"enchant error for language: ja_JP.UTF-8"です。
原因はgwui.pyとblogtk(2)につられて入るpython-gtkspellです。
(/usr/lib/python2.6/dist-packages/gwibber/gwui.py)
問題になるのは最初のほうにある

try:
  import gtkspell
except:
  gtkspell = None
と、後ろのほうにある
    if gtkspell:
      self.spell = gtkspell.Spell(self, None)

gwibberパッケージの依存性からはpython-gtkspellパッケージが導入されません。
したがって冒頭部分でgtkspellがNoneになるので、後ろのほうのコードが実行されません。

ここへpython-gtkspellパッケージが導入されると後ろのほうのコードでエラーが発生します。

もともとgwibberはpython-gtkspellなしで動作しますし、
gwibberがらみのソースをgrep -i -e spell しても使っている形跡がありません。
下記のように変更してしまえばgwibberが動作するようになります。

gwui.pyへのパッチ
@@ -15,10 +15,11 @@
 
 IMG_CACHE_DIR = os.path.join(resources.CACHE_BASE_DIR, "gwibber", "images")
 
-try:
-  import gtkspell
-except:
-  gtkspell = None
+# try:
+#   import gtkspell
+# except:
+#   gtkspell = None
+gtkspell = None
 
 class Color:
   def __init__(self, hex):

ついでにblogtk(2)の方も見てみたのですが、こちらもgtkspell.Spellから取得したものを使っている様子はありません。
なんなのでしょうかgtkspell。なぞです。

この時期なのでLucid Beta1でどうなったか気になったので調べてみました。
blogtkのバージョンは古い系統の1.1-2でpython-gtkspellとは無縁でした。
(依存性の指定が不足しているようで起動エラーになりましたが....)
gwibberは新しくなっていて、依存パッケージにpython-gtkspellが入っています。
さすがに標準アプリになっただけあって起動エラーにはなりません。
調べてみるとgtkspell.Spellの呼び出しがtry:、except: で囲まれてました。
grep -i -e spellしてみたのですが、相変わらず使われてなさそうです....。
なんなのでしょうかgtkspell。なぞです。

☆普段使わないパッケージコマンドやオプションをメモ

普段使わないパッケージ関連のコマンドやオプションをとりあえずメモ
・apt-file
  ファイルの検索
  --ignore-case(-i)
  --regexp(-x)

・apt-cache showsrc

・gep-dctrl,grep-status,grep-available
  sea also "/var/lib/dpkg/status","/var/lib/dpkg/available"

・apt-cache dumpavail
  利用可能なパッケージの表示

・apt-cache showpkg
  パッケージの情報をAPTの内部形式で表示
  sea also apt-show-versions


・apt-get --simulate
  実際には実行しない installと一緒に使う

・apt-get --print-uris
  debファイルの取得元を表示する。installと一緒に使う
  キャッシュにファイルが存在すると表示しない
  この場合はcleanするか--reinstallを指定。

・apt-listchanges
  パッケージの変更履歴を確認する
  同名のパッケージを導入する必要あり
  導入後 sudo dpkg-reconfigure apt-listchanges
  http://ascii.jp/elem/000/000/475/475007/index-3.html
  http://www.debian.org/doc/manuals/apt-howto/ch-search.ja.html

・apt-rdepends
  依存関係の表示。再帰する。
  --reverse,--build-depends とかある
  sea also apt-cache depends,apt-cache rdepends
  http://d.ma-aya.to/?date=20091216

・apt-mark showauto
  依存関係で自動的にインストールされたパッケージを表示
  
・dpkg --audit
  パッケージの状態チェック

・dpkg --configure --pending
  インストールがこけた場合の修復

・dpkg-reconfigure
  設定のやり直し

・dpkg-query
・dpkg --stasus(-s) パッケージ名
・dpkg --info(-i) パッケージファイル
・dpkg --field(-f) パッケージファイル
・dpkg --contents(-c) パッケージファイル

・dpkg-depcheck
  http://www.netfort.gr.jp/~ohura/diary/200501-1.html#07
  dpkg-depcheck(1) は特定のコマンドを strace 上で実行し、 そのコマンドが必要とするパッケージを表示する。 


・dpkg-source -x hoge.desc
  ソースを展開してパッチを適用
  (see also apt-get source)

・debfoster
  同名パッケージを導入する必要あり。

・apt-cache showsrc パッケージ名

・dpkg-buildpackage
・debuild
・dpkg-genbuilddeps
・debsign

・pkg-config
  gcc `pkg-config --cflags --libs` みたいに使う
  http://dolphin2005.blog.so-net.ne.jp/2008-02-17-2

・ディレクトリ、ファイル
/var/lib/dpkg/info

/var/log/installer/initial-status.gz
  インストール時のstatus

・推奨パッケージをインストールしない
sudo apt-get --no-install-recommends install devscripts

・パッチを当てたあとにパッケージのバージョンを上げる
dch -v "VERSION"

changelogの雛形が作成されるので、適当に入力してCtrl+Oで保存
このときファイル名から「.dch」を取り除く