2007年1月アーカイブ

とりあえず、第2章 SIMDプログラミングの基礎を見ながらちょっと触ってみる。
2.3.2の例題プログラムをコンパイルしようとすると、
$ gcc -maltivec -mabi=altivec ex2-1.c -o ex2-1.elf
ex2-1.c: In function main:
ex2-1.c:14: error: incompatible types in assignment
なんて怒られる。(あ、ソースファイル名は変えてあります)
ソースを読んで、文脈的にvcの型が合わないんだろうと思って*をつけたらうまく動いた。
14     *vc = vec_add(*va, *vb);    // 1 + 2, 3 + 4, 5 + 6, 7 + 8
$ !gcc
gcc -maltivec -mabi=altivec ex2-1.c -o ex2-1.elf
$ ./ex2-1.elf
c[0]=3, c[1]=7, c[2]=11, c[3]=15
調子に乗って演習問題もやってみる。
演習問題 (2-1)
#include <stdio.h>
#include <altivec.h>

#define MAX_NUM 1024

int a[MAX_NUM] __attribute__((aligned(16)));
int b[4] __attribute__((aligned(16))) = {0, 0, 0, 0};

int main(int argc, char *argv[])
{
  int i;
  vector signed int *va;
  vector signed int *vb = (vector signed int *) b;

  for (i = 0; i < MAX_NUM; i++) {
    a[i] = i + 1;
  }

  for (i = 0; i < MAX_NUM; i += 4) {
    va = (vector signed int *) (a + i);
    *vb = vec_add(*va, *vb);
  }

  printf("%d\n", b[0] + b[1] + b[2] + b[3]);

  return 0;
}
模範解答が載っていないのであっているかわからない。ひょっとしたら、出題者の意図としては、1024の配列ではなく4要素の配列を作って、ループの中で4つづつ中身を書き換えさせたいのかも知れないが、これでもまあ問題はないだろう。
1024の総和だと、SIMDを使わない場合と差が出なかったので、102400で試してみたところ、SIMDを使った方が遅かった(笑)。まあ、これは使い方を覚えるためのサンプルなので、そんなものだろう。
不思議なことに、SIMDを使ったほうはほぼuser time だけを消費し、SIMDを使わない方はほぼ sys time だけを消費した。

演習問題 (2-2)
#include <stdio.h>
#include <altivec.h>

#define SIZE    (16)

unsigned short in[SIZE] __attribute__((aligned(16))) = {
  1,  2,  3,  4,  5,  6,  7,  8,
  9, 10, 11, 12, 13, 14, 15, 16
};
static unsigned short dummy[SIZE] __attribute__((aligned(16)));

float out[SIZE] __attribute__((aligned(16)));

int main(int argc, char *argv[])
{
  int i;
  vector unsigned short *va;
  vector unsigned short *vb = (vector unsigned short *)dummy;
  vector unsigned char vpat1 = (vector unsigned char) {
    0x10, 0x10, 0x00, 0x01, 0x10, 0x10, 0x02, 0x03,
    0x10, 0x10, 0x04, 0x05, 0x10, 0x10, 0x06, 0x07,
  };
  vector unsigned char vpat2 = (vector unsigned char) {
    0x10, 0x10, 0x08, 0x09, 0x10, 0x10, 0x0a, 0x0b,
    0x10, 0x10, 0x0c, 0x0d, 0x10, 0x10, 0x0e, 0x0f,
  };

  vector unsigned int vc;
  vector float *vd;

  va = (vector unsigned short *)in;

  vc = vec_perm(*va, *vb, vpat1);
  vd = (vector float *)out;
  *vd = vec_ctf(vc, 1);
  vc = vec_perm(*va, *vb, vpat2);
  vd = (vector float *)(out + 4);
  *vd = vec_ctf(vc, 1);

  va = (vector unsigned short *)(in + 8);
  vc = vec_perm(*va, *vb, vpat1);
  vd = (vector float *)(out + 8);
  *vd = vec_ctf(vc, 1);
  vc = vec_perm(*va, *vb, vpat2);
  vd = (vector float *)(out + 12);
  *vd = vec_ctf(vc, 1);

  for (i = 0; i < SIZE; i++) {
    printf("out[%02d]=%0.1f\n", i, out[i]);
  }

  return 0;
}
2-2は、最初出題プログラム2-2をコピーして始めたら、inとoutが16バイトアラインに並んでいなくてはまった。
後は、vector short と vector int の関係、メモリイメージ、vec_permの使い方が頭の中でリンクしなくて、結構はまってしまった。
長くなるので続きは次のエントリで。
演習問題 (2-3)
#include <stdio.h>
#include <altivec.h>

#define SIZE    (16)

float in[SIZE] __attribute__((aligned(16))) = {  1,  -2,  3,  -4,
                    5,  -6,  7,  -8,
                    9, -10, 11, -12,
                    13, -14, 15, -16 };
float out[SIZE] __attribute__((aligned(16)));

static float zero[4] __attribute__((aligned(16)));

int main(int argc, char *argv[])
{
  int i;
  vector float *va = (vector float *)in;
  vector float *vc = (vector float *)out;
  vector float *vz = (vector float *)zero;
  vector unsigned int vpat;
  vector float vc_true, vc_false;

  for (i = 0; i < SIZE / sizeof(float); i++) {
    vpat = vec_cmpgt(*vz, va[i]);
    vc_true  = vec_sub(*vz, va[i]);
    vc_false = va[i];
    vc[i] = vec_sel(vc_false, vc_true, vpat);
  }

  for (i = 0; i < SIZE; i++) {
    printf("out[%02d]=%0.0f\n", i, out[i]);
  }

  return 0;
}
例題プログラム2-3を見ていたら、元の配列からvectorにキャストしたときに、vectorの配列のように扱えることができることがわかったので、2-2よりすっきりしている。
同じように書き換えれば、2-2ももう少し綺麗になるはず。

演習2-4は面倒なのでパス。今までのまとめなので、同じようにやればできるはず&いかにループや演算回数を減らすかで頭を使えそうではあるんだけど・・。
仕事ではこう言うパズルのような頭を使うことはないんだけど、たまにはこんな感じのも悪くないかなあ。脳の老化を感じます(笑)
演習問題とは言え、あまりにひどい内容で出すのもどうかと思い、ちょっとだけリファクタリング。
PS3のFC5は、デフォルトではLANGがen_US.UTF-8なので、自分の.bashrcでja_JP.eucJPにしたら、manとかで日本語が見れるようになった。(ひょっとしたらインストーラが聞いてきていたのかな?)
で、毎回gccのコマンドラインを打つのは面倒だし、オプションも覚えていられないので、以下の内容でMakefileを作成。
CC=gcc
CFLAGS=-maltivec -mabi=altivec
これで、たとえばp2-1.cがある状態で、
$ make p2-1
とかやればp2-1がコンパイルできる。コンパイルするものがあらかじめ決まっている場合は、Makefileに以下のような行を追加してやればmake一発で良い。
all: p2-1o p2-1 p2-2o p2-2 p2-3o p2-3 p2-4o p2-4
と、言うわけで演習問題の解答を見直してみる。

演習問題 (2-1)
#include <stdio.h>
#include <altivec.h>

/**
 * 整数の合計を計算する。
 * オーバーフローは考慮しない。
 * @param size 合計を計算する要素の数(4の倍数)
 * @param data 合計を計算する要素(16バイトアラインにそろっていること)
 * @return 合計
 */
int calc_sum(int size, int data[]) {
  int sum[4] __attribute__((aligned(16))) = {0, 0, 0, 0};
  int i;
  vector signed int *vdata = (vector signed int *) data;
  vector signed int *vsum = (vector signed int *) sum;

  for (i = 0; i < size / 4; i++) {
    *vsum = vec_add(*vsum, vdata[i]);
  }

  return sum[0] + sum[1] + sum[2] + sum[3];
}

#define MAX_NUM 1024

int main(int argc, char *argv[])
{
  int i;
  int data[MAX_NUM] __attribute__((aligned(16)));

  for (i = 0; i < MAX_NUM; i++) {
    data[i] = i + 1;
  }

  printf("%d\n", calc_sum(MAX_NUM, data));

  return 0;
}

演習問題 (2-2)
#include <stdio.h>
#include <altivec.h>

/**
 * in で渡されたsize個のデータを、0.5倍してoutに格納する。
 * @param size データの個数(8の倍数)
 * @param in データ
 * @param out in を0.5倍した結果
 */
void calc_half(int size, unsigned short in[], float out[]) {
  static vector unsigned short vzero;
  static vector unsigned char vpat[2] = {
    (vector unsigned char) {
      0x10, 0x10, 0x00, 0x01, 0x10, 0x10, 0x02, 0x03,
      0x10, 0x10, 0x04, 0x05, 0x10, 0x10, 0x06, 0x07,
    },
    (vector unsigned char) {
      0x10, 0x10, 0x08, 0x09, 0x10, 0x10, 0x0a, 0x0b,
      0x10, 0x10, 0x0c, 0x0d, 0x10, 0x10, 0x0e, 0x0f,
    }
  };
  vector unsigned short *vdata = (vector unsigned short *) in;
  vector float *vout = (vector float *) out;
  vector unsigned int vtmp;
  int i;

  for (i = 0; i < size / 4; i++) {
    vtmp = vec_perm(vdata[i / 2], vzero, vpat[i % 2]);
    vout[i] = vec_ctf(vtmp, 1);
  }
}

#define SIZE    (16)

int main(int argc, char *argv[])
{
  unsigned short in[SIZE] __attribute__((aligned(16))) = {
    1,  2,  3,  4,  5,  6,  7,  8,
    9, 10, 11, 12, 13, 14, 15, 16
  };
  float out[SIZE] __attribute__((aligned(16)));
  int i;

  calc_half(SIZE, in, out);

  for (i = 0; i < SIZE; i++) {
    printf("out[%02d]=%0.1f\n", i, out[i]);
  }

  return 0;
}
こいつはちょっと自信作。だいぶすっきりしたと思う。

演習問題 (2-3)は、もともとそれなりにすっきりしていたのでリファクタリングなし。

演習問題 (2-4)
#include <stdio.h>
#include <altivec.h>

/**
 * グレースケール変換
 * @param src 変換元データ(16バイトアライン)
 * @param dst 変換後データ(16バイトアライン)
 * @param num データ数(4の倍数)
 */
void rgb2y(unsigned char *src, unsigned char *dst, int num) {
  static vector float vzero_f;
  static vector unsigned int vzero_i;
  static vector unsigned char vpat_r = (vector unsigned char) {
    0x10, 0x10, 0x10, 0x00, 0x10, 0x10, 0x10, 0x04,
    0x10, 0x10, 0x10, 0x08, 0x10, 0x10, 0x10, 0x0c,
  };
  static vector unsigned char vpat_g = (vector unsigned char) {
    0x10, 0x10, 0x10, 0x01, 0x10, 0x10, 0x10, 0x05,
    0x10, 0x10, 0x10, 0x09, 0x10, 0x10, 0x10, 0x0d,
  };
  static vector unsigned char vpat_b = (vector unsigned char) {
    0x10, 0x10, 0x10, 0x02, 0x10, 0x10, 0x10, 0x06,
    0x10, 0x10, 0x10, 0x0a, 0x10, 0x10, 0x10, 0x0e,
  };
  static vector unsigned char vpat_y = (vector unsigned char) {
    0x03, 0x03, 0x03, 0x10, 0x07, 0x07, 0x07, 0x10,
    0x0b, 0x0b, 0x0b, 0x10, 0x0f, 0x0f, 0x0f, 0x10,
  };
  static vector float vrc = (vector float) {
    0.29891, 0.29891, 0.29891, 0.29891
  };
  static vector float vgc = (vector float) {
    0.58661, 0.58661, 0.58661, 0.58661
  };
  static vector float vbc = (vector float) {
    0.11448, 0.11448, 0.11448, 0.11448
  };
  vector unsigned int *vsrc = (vector unsigned int *) src;
  vector unsigned char *vdst = (vector unsigned char *) dst;
  vector unsigned int vint;
  vector unsigned int vmax = (vector unsigned int) {255, 255, 255, 255};
  vector unsigned int vpat_max;
  vector float vr, vg, vb, vy;
  int i;

  for (i = 0; i < num / 4; i++) {
    vint = vec_perm(vsrc[i], vzero_i, vpat_r);
    vr = vec_ctf(vint, 0);
    vint = vec_perm(vsrc[i], vzero_i, vpat_g);
    vg = vec_ctf(vint, 0);
    vint = vec_perm(vsrc[i], vzero_i, vpat_b);
    vb = vec_ctf(vint, 0);
    vy = vec_madd(vr, vrc, vzero_f);
    vy = vec_madd(vg, vgc, vy);
    vy = vec_madd(vb, vbc, vy);
    vint = vec_ctu(vy, 0);
    vpat_max = vec_cmpgt(vint, vmax);
    vint = vec_sel(vint, vpat_max, vpat_max);
    vdst[i] = vec_perm(vint, vzero_i, vpat_y);
  }
}
こいつは、コンパイルエラーだけは出ないことを確認したけど、テストプログラムを書いていないのであっているかどうか全然わからない。
テストプログラムは、次のエントリで。
と、言うわけで、演習問題(2-4)のテストプログラム。
やる気が削げてきたので、またまた手抜きプログラム。
#include <stdio.h>

int main(int argc, char *argv[]) {
  char buf[256];
  FILE *fin, *fout;
  int height, width, num, i;
  unsigned char *src, *dst;

  fin = fopen(argv[1], "r");
  fout = fopen(argv[2], "w");
  // P6
  fgets(buf, 256, fin);
  fprintf(fout, "%s", buf);
  // height width
  fgets(buf, 256, fin);
  fprintf(fout, "%s", buf);
  sscanf(buf, "%d%d", &height, &width);
  num = height * width;
  // depth
  fgets(buf, 256, fin);
  fprintf(fout, "%s", buf);


  printf("height = %d, width = %d, num = %d, num * 4 = %d\n", height, width, num, num * 4);
  src = (unsigned char *) malloc(((size_t)num) * 4);
  dst = (unsigned char *) malloc(((size_t)num) * 4);

  printf("src = %x, dst = %x\n", src, dst);

  for (i = 0; i < num; i++) {
    fread(src + i * 4, 1, 3, fin);
  }
  fclose(fin);

  rgb2y(src, dst, num);
  for (i = 0; i < num; i++) {
    fwrite(dst + ((size_t)i) * 4, 1, 3, fout);
  }
  fclose(fout);
}
使い方は、まず変換元のファイルをjpegtopnm等でpnmファイルに変換して、上のプログラムに食わせ、出来上がったファイルをpnmtojpeg等に食わせる。
エラーチェックとか何もしていないので、そのつもりで。
プログラムのパラメータは、第1引数が変換元のpnmファイル名で、第2引数が変換後のpnmファイル名。
一応手元の画像を変換してみたところ、ちゃんと動いているように見える。
VMWare player に VMWare tools をインストールする。
VMWare ServerからLinux版のtar.gz をダウンロードしてきて、中からvmware-server-distrib/lib/isoimages/linux.isoを取り出す。
VMWare Player上のFC5からisoイメージをマウントして、中に入っているrpmをインストールする。
続いて、vmware-config-tools.pl を実行。(kernel-develパッケージが入っている必要があった) 後は、質問に答えていくのだが、vmhgfs module のコンパイルで失敗してしまう。
それ以外の機能をインストールする?と聞かれるので、そのままインストール。
これで、vmw をcronで動かさなくても時刻が同期するようになるのかな?
しかし、相変わらずクリップボードは共有できていないようで、まだ何か足りない部分があるのかな・・・。

→Xを再起動したら、grab/ungrabは効くようになりました。これだけでもかなり快適。でも、何故かクリップボードの共有はうまくいきません。な~んでか~。
VMware Player 上の FC5 に、x86用の CELL SDK 2.0 を入れてみる。
手順は、X86 PCへのCell SDK 2.0のインストールの通り。
インストーラを起動してから、中でいろいろダウンロードするんだけど、そこでえらい時間がかかりました。
インストールが終わったら、ためしにGUI版のシミュレータを起動してみる。これも結構時間がかかります。
最初、一般ユーザで起動して、ramdiskが作れないみたいなエラーが出てめっさ嵌ってしまいましたが、rootで起動したら問題なく起動しました。
果てしなく遅いですが、エミュレータだからこんなもんなんですかね。
とりあえず、自前のプログラムでも動かしてみようかと思いましたが、使い方が全然わからないので、もう少し調べる必要がありそうです。
と、言うわけでちょっと調べてみました。

  • コンパイル手順
    1. /opt/cell/toolchain-3.3/bin/ppu-cc を使ってコンパイルする。コンパイルオプションはPS3上と同じでいけた。
  • 実行手順
    1. gui版は遅いので、以下の手順をrootで実行(遅いのは変わらないかも)
      # cd /opt/ibm/systemsim-cell/run/cell/linux
      # ../run_cmdline
      systemsim % mysim go
      
      ここまでで、mysimと言う名前のウィンドウでシミュレータの端末が起動する。
    2. mysim上で以下を実行
      # callthru source /home/test/a.out > a.out
      # chmod +x a.out
      # ./a.out
      
      ※ このコマンドラインで、ホスト上の/home/test/a.out をシミュレータ上にコピーして、実行する。

あまりおおっぴらにはしたくないんだけど、Mixiはじめました。
で、あまりのユーザインターフェイスの使えなさにぶっとびつつ、それでも情報収集には良いので、しばらく使ってみることにします。

しかし、全体的に未読管理や検索が弱く、参加しているコミュニティが増えると未読管理ができないのは致命的に思えます。
そこで、今まで食わず嫌いで避けていたPlaggerに手を出してみることにしました。
インストールは ports から textproc/p5-Plagger を入れるだけ。最初にどんなオプションで入れるか聞かれるので、 CUSFEEDMIXI を選択しておきました。
CustomFeed::Mixiだと、コミュニティに対応していません。調べてみたところ、CustomFeed::Mixiをコミュニティ最新書き込み対応にと言うのを発見。早速入れ替えてみます。
で、以下のような config.yaml を書いて、plagger -c config.yaml で実行します。
global:
  timezone: Asia/Tokyo
  assets_path: /usr/local/share/Plagger/assets

plugins:
  - module: Publish::Gmail
    rule:
      - module: Rating
        rate:   '>= 0'
      - module: Fresh
        mtime:
            path: /tmp/foo.tmp
            autoupdate: 1
    config:
      mailto:   gmailのアドレス@gmail.com
      mailfrom: 差出人のアドレス
      mailroute:
        via: smtp
        host: localhost

  - module: CustomFeed::Mixi
    config:
      email: mixiに登録したメールアドレス
      password: パスワード(一回起動すると、base64に置き換わる
      fetch_body: 1
      fetch_body_interval: 1.5
      show_icon: 0
      feed_type:
         - Bbs
が、何故かうちの環境では何も取ってきてくれません。
そこで、CustomFeed/Mixi.pmを元に戻して、feed_type をMessageとかにしてみると、ちゃんと持ってきてくれました。
で、二つのファイルを見比べながら手で修正してみたら、コミュニティの新着も取ってきてくれるようになりました。
しかし、トピックまでしか取ってきてくれないので私にとってはあまり意味がありません。

あと、元々のCustomFeed::Mixiの話ですが、fetch_icon = 1 にしておくと足跡等でイメージを取ってきてくれるのですが、そのときに相手のプロフィールページにアクセスするので、相手に足跡が残ります。
相手に足跡が残ることを気にする人は注意が必要です。

ここまでやってしばらく呆けていたのですが、ふと思い立ってCustomFeed::Mixiが呼び出しているWWW::Mixiのソースを読んでみました。
すると、WWW::Mixiではちゃんとコメントまで読んでいます。
そこで、CustomFeed::Mixiに手を入れて、コメントもfeedするようにしてみました。この際、WWW::Mixiはコメントの番号を読んでくれていなかったので、こちらにも手を入れて番号も取れるようにしました。

CustomFeed/Mixi.pmのdiff
*** Mixi.pm.ORIG        Fri Jan  5 23:14:14 2007
--- Mixi.pm     Sat Jan  6 22:11:05 2007
***************
*** 48,53 ****
--- 48,62 ----
          get_list   => 'parse_show_calendar',
          get_detail => 'get_view_event',
      },
+     Bbs => {
+         start_url  => 'http://mixi.jp/new_bbs.pl',
+         title      => 'コミュニティ最新書き込み',
+         get_list   => 'parse_new_bbs',
+         get_detail => 'dummy',
+         get_detail_bbs => 'get_view_bbs',
+         get_detail_event => 'get_view_event',
+         get_detail_enquete => 'get_view_enquete',
+     },
  };

  sub plugin_id {
***************
*** 134,139 ****
--- 143,149 ----

      my $i = 0;
      my $blocked = 0;
+     my $item;
      for my $msg (@msgs) {
          next if $type eq 'FriendDiary' and not $msg->{image}; # external blog
          last if $i++ >= $items;
***************
*** 170,182 ****
              }
          }

!         if ($self->conf->{fetch_body} && !$blocked && $msg->{link} =~ /view_/ && defined $MAP->{$type}->{get_detail}) {
              $context->log(info => "Fetch body from $msg->{link}");
!             my $item = $self->cache->get_callback(
                  "item-$msg->{link}",
                  sub {
                      Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
!                     my $meth = $MAP->{$type}->{get_detail};
                      my($item) = $self->{mixi}->$meth($msg->{link});

                      if ($meth eq 'get_view_diary') {
--- 180,196 ----
              }
          }

!       $item = 0;
!
!         if ($self->conf->{fetch_body} && !$blocked && $msg->{link} !~ /view_enquete/ &&$msg->{link} =~ /view_/ && defined $MAP->{$type}->{get_detail}) {
              $context->log(info => "Fetch body from $msg->{link}");
!             $item = $self->cache->get_callback(
                  "item-$msg->{link}",
                  sub {
                      Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
! #                    my $meth = $MAP->{$type}->{get_detail};
!                     my $meth = get_meth($MAP->{$type}, $msg->{link});
!
                      my($item) = $self->{mixi}->$meth($msg->{link});

                      if ($meth eq 'get_view_diary') {
***************
*** 207,212 ****
--- 221,229 ----
          }

          $feed->add_entry($entry);
+       if ($item) {
+           add_comments($feed, $item);
+       }
      }

      $context->update->add($feed);
***************
*** 221,226 ****
--- 238,274 ----
      }

      return \@images;
+ }
+ sub get_meth {
+     my($type, $link) = @_;
+
+     my $meth = "";
+     if ($link =~ /view_bbs/) {
+         $meth = $type->{get_detail_bbs};
+     } elsif ($link =~ /view_event/) {
+         $meth = $type->{get_detail_event};
+     } elsif ($link =~ /view_enquete/) {
+         $meth = $type->{get_detail_enquete};
+     } else {
+         $meth = $type->{get_detail};
+     }
+     return $meth;
+ }
+
+ sub add_comments {
+     my ($feed, $item) = @_;
+     my $format = DateTime::Format::Strptime->new(pattern => '%Y/%m/%d %H:%M');
+     if ($item->{'comments'}) {
+       for my $comment (@{$item->{'comments'}}) {
+           my $entry = Plagger::Entry->new;
+           $entry->title($comment->{'number'});
+           $entry->link($comment->{'link'});
+           $entry->author( decode('euc-jp', $comment->{'name'}) );
+           $entry->date( Plagger::Date->parse($format, $comment->{'time'}) );
+           $entry->body( decode('euc-jp', $comment->{'description'}) );
+           $feed->add_entry($entry);
+       }
+     }
  }

  1;
WWW/Mixi.pmのdiff
*** Mixi.pm.ORIG        Sat Jan  6 21:54:02 2007
--- Mixi.pm     Sat Jan  6 22:09:23 2007
***************
*** 1426,1431 ****
--- 1426,1432 ----
        my $re_subj = '<td bgcolor="#fff4e0">&nbsp;(.+?)</td>';
        my $re_desc = '</table>(.+?)</td>';
        my $re_c_date = '<td rowspan="2" width="110" bgcolor="#f2ddb7" align="center" nowrap>\n(\d{4})年(\d{2})月(\d{2})日<br>\n(\d{1,2}):(\d{2})';
+       my $re_c_no = '<b>(.*?[0-9]+?)</b>';
        my $re_c_desc = '<td class="h120">(.+?)\n</td>';
        my $re_link   = '<a href="?(.+?)"?>(.*?)<\/a>';
        if ($content =~ s/<!-- TOPIC: start -->.*?${re_date}.*?${re_subj}.*?${re_link}(.*?)${re_desc}(.*?)$//is) {
***************
*** 1436,1445 ****
                        next unless ($image =~ /<a [^<>]*'show_picture.pl\?img_src=(.*?)'[^<>]*><img src=([^ ]*) border=0>/);
                        push(@{$item->{'images'}}, {'link' => $self->absolute_url($1, $base), 'thumb_link' => $self->absolute_url($2, $base)});
                }
!               while ($comm =~ s/.*?${re_c_date}.*?${re_link}.*?${re_c_desc}.*?<\/table>//is){
!                       my ($time, $link, $name, $desc) = (sprintf('%04d/%02d/%02d %02d:%02d', $1,$2,$3,$4,$5), $6, $7, $8);
                        ($name, $desc) = map { s/[\r\n]+//g; s/<br>/\n/g; $_ = $self->rewrite($_); } ($name, $desc);
!                       push(@{$item->{'comments'}}, {'time' => $time, 'link' => $self->absolute_url($link, $base), 'name' => $name, 'description' => $desc});
                }
                push(@items, $item);
        }
--- 1437,1446 ----
                        next unless ($image =~ /<a [^<>]*'show_picture.pl\?img_src=(.*?)'[^<>]*><img src=([^ ]*) border=0>/);
                        push(@{$item->{'images'}}, {'link' => $self->absolute_url($1, $base), 'thumb_link' => $self->absolute_url($2, $base)});
                }
!               while ($comm =~ s/.*?${re_c_date}.*?${re_c_no}.*?${re_link}.*?${re_c_desc}.*?<\/table>//is){
!                       my ($time, $number, $link, $name, $desc) = (sprintf('%04d/%02d/%02d %02d:%02d', $1,$2,$3,$4,$5), $6, $7, $8, $9);
                        ($name, $desc) = map { s/[\r\n]+//g; s/<br>/\n/g; $_ = $self->rewrite($_); } ($name, $desc);
!                       push(@{$item->{'comments'}}, {'time' => $time, 'link' => $self->absolute_url($link, $base), 'name' => $name, 'description' => $desc, 'number' => $number});
                }
                push(@items, $item);
        }

これで、コミュニティの新着のコメントまでをgmailに送ることができるようになりました。
後は、トピック毎にコメントの番号を覚えておいて、新しいものだけfeedするようにすれば当初の目的は果たせそうです。
コメントの番号を覚えるには、自前でファイルを読み書きすれば実現できますが、ひょっとしたらPlaggerにそう言う作法があるかも知れないので、もう少し調べてみます。
# なんて書いておくと、Plaggerの偉い人がヒントをくれないかしらん。
昨日までで、トピックのコメントまでは取得できるようになったが、後は未読管理をしないといけない。
そこで、Filterのソースでも参考にしようと思って、Filterディレクトリの下を眺めていたら、Rule.pmって言うのがあって、そこのサンプルを見ると、Freshって言うモジュールで日時ベースでの未読管理は実現できていそうだ。
config.yamlを見直して、Publish::Gmailのrule: に書いていた部分を、Filter::Ruleに持ってきてみたら、ちゃんと一度メールしたものは送られないようになった。
  - module: Filter::Rule
    rule:
      - module: Fresh
        mtime:
            path: /tmp/foo.tmp
            autoupdate: 1
Publish::Gmailのソースを読んでも、rule:も効きそうに思えるんだけど、良くわからないけどFileter::Ruleで効くので良いことにする。
後は、上のルールを有効にすると、コメントだけが送られてトピックが送られないことが多いので、昨日の改造だとコメント番号をtitleにしているので読みにくい。そこで、最終的にCustomFeed::Mixiを以下のようにした。
*** Mixi.pm.ORIG        Fri Jan  5 23:14:14 2007
--- Mixi.pm     Sat Jan  6 22:11:05 2007
***************
*** 48,53 ****
--- 48,62 ----
          get_list   => 'parse_show_calendar',
          get_detail => 'get_view_event',
      },
+     Bbs => {
+         start_url  => 'http://mixi.jp/new_bbs.pl',
+         title      => 'コミュニティ最新書き込み',
+         get_list   => 'parse_new_bbs',
+         get_detail => 'dummy',
+         get_detail_bbs => 'get_view_bbs',
+         get_detail_event => 'get_view_event',
+         get_detail_enquete => 'get_view_enquete',
+     },
  };

  sub plugin_id {
***************
*** 134,139 ****
--- 143,149 ----

      my $i = 0;
      my $blocked = 0;
+     my $item;
      for my $msg (@msgs) {
          next if $type eq 'FriendDiary' and not $msg->{image}; # external blog
          last if $i++ >= $items;
***************
*** 170,182 ****
              }
          }

!         if ($self->conf->{fetch_body} && !$blocked && $msg->{link} =~ /view_/ && defined $MAP->{$type}->{get_detail}) {
              $context->log(info => "Fetch body from $msg->{link}");
!             my $item = $self->cache->get_callback(
                  "item-$msg->{link}",
                  sub {
                      Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
!                     my $meth = $MAP->{$type}->{get_detail};
                      my($item) = $self->{mixi}->$meth($msg->{link});

                      if ($meth eq 'get_view_diary') {
--- 180,196 ----
              }
          }

!       $item = 0;
!
!         if ($self->conf->{fetch_body} && !$blocked && $msg->{link} !~ /view_enquete/ &&$msg->{link} =~ /view_/ && defined $MAP->{$type}->{get_detail}) {
              $context->log(info => "Fetch body from $msg->{link}");
!             $item = $self->cache->get_callback(
                  "item-$msg->{link}",
                  sub {
                      Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
! #                    my $meth = $MAP->{$type}->{get_detail};
!                     my $meth = get_meth($MAP->{$type}, $msg->{link});
!
                      my($item) = $self->{mixi}->$meth($msg->{link});

                      if ($meth eq 'get_view_diary') {
***************
*** 207,212 ****
--- 221,229 ----
          }

          $feed->add_entry($entry);
+       if ($item) {
+           add_comments($feed, $item);
+       }
      }

      $context->update->add($feed);
***************
*** 221,226 ****
--- 238,274 ----
      }

      return \@images;
+ }
+ sub get_meth {
+     my($type, $link) = @_;
+
+     my $meth = "";
+     if ($link =~ /view_bbs/) {
+         $meth = $type->{get_detail_bbs};
+     } elsif ($link =~ /view_event/) {
+         $meth = $type->{get_detail_event};
+     } elsif ($link =~ /view_enquete/) {
+         $meth = $type->{get_detail_enquete};
+     } else {
+         $meth = $type->{get_detail};
+     }
+     return $meth;
+ }
+
+ sub add_comments {
+     my ($feed, $item) = @_;
+     my $format = DateTime::Format::Strptime->new(pattern => '%Y/%m/%d %H:%M');
+     if ($item->{'comments'}) {
+       for my $comment (@{$item->{'comments'}}) {
+           my $entry = Plagger::Entry->new;
+           $entry->title(decode('euc-jp', $item->{'subject'}) . ": " . $comment->{'number'});
+           $entry->link($comment->{'link'});
+           $entry->author( decode('euc-jp', $comment->{'name'}) );
+           $entry->date( Plagger::Date->parse($format, $comment->{'time'}) );
+           $entry->body( decode('euc-jp', $comment->{'description'}) );
+           $feed->add_entry($entry);
+       }
+     }
  }

  1;
これで、1時間置きにでもcronに仕込めば、gmailで未読が読めるようになりました。
いろいろ試して、なんとか当面の自分の目的には使えそうになったので、公開。(前回最終的に、とか書いたけど、まだまだ最終になりそうにないので、今回は最終版とか言わない)
まずは、CustomFeed::Mixi
*** /usr/local/lib/perl5/site_perl/5.8.8/Plagger/Plugin/CustomFeed/Mixi.pm.ORIG	Fri Jan  5 23:14:14 2007
--- /usr/local/lib/perl5/site_perl/5.8.8/Plagger/Plugin/CustomFeed/Mixi.pm	Sun Jan  7 20:40:08 2007
***************
*** 48,53 ****
--- 48,62 ----
          get_list   => 'parse_show_calendar',
          get_detail => 'get_view_event',
      },
+     Bbs => {
+         start_url  => 'http://mixi.jp/new_bbs.pl',
+         title      => 'コミュニティ最新書き込み',
+         get_list   => 'parse_new_bbs',
+         get_detail => 'dummy',
+         get_detail_bbs => 'get_view_bbs',
+         get_detail_event => 'get_view_event',
+         get_detail_enquete => 'get_view_enquete',
+     },
  };
  
  sub plugin_id {
***************
*** 134,139 ****
--- 143,149 ----
  
      my $i = 0;
      my $blocked = 0;
+     my $item;
      for my $msg (@msgs) {
          next if $type eq 'FriendDiary' and not $msg->{image}; # external blog
          last if $i++ >= $items;
***************
*** 143,148 ****
--- 153,161 ----
          $entry->link($msg->{link});
          $entry->author( decode('euc-jp', $msg->{name}) );
          $entry->date( Plagger::Date->parse($format, $msg->{time}) );
+ 	if ($entry->date) {
+ 	    $entry->date->set_time_zone('Asia/Tokyo');
+ 	}
  
          if ($self->conf->{show_icon} && !$blocked && defined $MAP->{$type}->{icon_re}) {
              my $owner_id = ($msg->{link} =~ $MAP->{$type}->{icon_re})[0];
***************
*** 170,182 ****
              }
          }
  
!         if ($self->conf->{fetch_body} && !$blocked && $msg->{link} =~ /view_/ && defined $MAP->{$type}->{get_detail}) {
              $context->log(info => "Fetch body from $msg->{link}");
!             my $item = $self->cache->get_callback(
                  "item-$msg->{link}",
                  sub {
                      Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
!                     my $meth = $MAP->{$type}->{get_detail};
                      my($item) = $self->{mixi}->$meth($msg->{link});
  
                      if ($meth eq 'get_view_diary') {
--- 183,199 ----
              }
          }
  
! 	$item = 0;
! 
!         if ($self->conf->{fetch_body} && !$blocked && $msg->{link} !~ /view_enquete/ &&$msg->{link} =~ /view_/ && defined $MAP->{$type}->{get_detail}) {
              $context->log(info => "Fetch body from $msg->{link}");
!             $item = $self->cache->get_callback(
                  "item-$msg->{link}",
                  sub {
                      Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
! #                    my $meth = $MAP->{$type}->{get_detail};
!                     my $meth = get_meth($MAP->{$type}, $msg->{link});
! 
                      my($item) = $self->{mixi}->$meth($msg->{link});
  
                      if ($meth eq 'get_view_diary') {
***************
*** 189,195 ****
              if ($item) {
                  my $body = decode('euc-jp', $item->{description});
                     $body =~ s!(\r\n?|\n)!<br />!g;
!                 for my $image (@{ $item->{images} }) {
                      $body .= qq(<div><a href="$image->{link}"><img src="$image->{thumb_link}" style="border:0" /></a></div>);
                      my $enclosure = Plagger::Enclosure->new;
                      $enclosure->url( URI->new($image->{thumb_link}) );
--- 206,213 ----
              if ($item) {
                  my $body = decode('euc-jp', $item->{description});
                     $body =~ s!(\r\n?|\n)!<br />!g;
! 		my $image;
!                 for $image (@{ $item->{images} }) {
                      $body .= qq(<div><a href="$image->{link}"><img src="$image->{thumb_link}" style="border:0" /></a></div>);
                      my $enclosure = Plagger::Enclosure->new;
                      $enclosure->url( URI->new($image->{thumb_link}) );
***************
*** 197,205 ****
--- 215,234 ----
                      $enclosure->is_inline(1);
                      $entry->add_enclosure($enclosure);
                  }
+ 		if ($image = $item->{image}) {
+                     $body .= qq(<div><a href="$image"><img src="$image" style="border:0" /></a></div>);
+                     my $enclosure = Plagger::Enclosure->new;
+                     $enclosure->url( URI->new($image) );
+                     $enclosure->auto_set_type;
+                     $enclosure->is_inline(1);
+                     $entry->add_enclosure($enclosure);
+ 		}
                  $entry->body($body);
  
                  $entry->date( Plagger::Date->parse($format, $item->{time}) );
+ 		if ($entry->date) {
+ 		    $entry->date->set_time_zone('Asia/Tokyo');
+ 		}
              } else {
                  $context->log(warn => "Fetch body failed. You might be blocked?");
                  $blocked++;
***************
*** 207,212 ****
--- 236,244 ----
          }
  
          $feed->add_entry($entry);
+ 	if ($self->conf->{fetch_body} && $item) {
+ 	    add_comments($feed, $item);
+ 	}
      }
  
      $context->update->add($feed);
***************
*** 221,226 ****
--- 253,301 ----
      }
  
      return \@images;
+ }
+ sub get_meth {
+     my($type, $link) = @_;
+     
+     my $meth = "";
+     if ($link =~ /view_bbs/) {
+         $meth = $type->{get_detail_bbs};
+     } elsif ($link =~ /view_event/) {
+         $meth = $type->{get_detail_event};
+     } elsif ($link =~ /view_enquete/) {
+         $meth = $type->{get_detail_enquete};
+     } else {
+         $meth = $type->{get_detail};
+     }
+     return $meth;
+ }
+ 
+ sub add_comments {
+     my ($feed, $item) = @_;
+     my $format = DateTime::Format::Strptime->new(pattern => '%Y/%m/%d %H:%M');
+     if ($item->{'comments'}) {
+ 	for my $comment (@{$item->{'comments'}}) {
+ 	    my $entry = Plagger::Entry->new;
+ 	    $entry->title(decode('euc-jp', $item->{'subject'}) . ": " . $comment->{'number'});
+ 	    $entry->link($item->{'link'});
+ 	    $entry->author( decode('euc-jp', $comment->{'name'}) );
+ 	    $entry->date( Plagger::Date->parse($format, $comment->{'time'}) );
+ 	    if ($entry->date) {
+ 		$entry->date->set_time_zone('Asia/Tokyo');
+ 	    }
+ 	    my $body = decode('euc-jp', $comment->{'description'});
+ 	    for my $image (@{ $comment->{images} }) {
+ 		$body .= qq(<div><a href="$image->{link}"><img src="$image->{thumb_link}" style="border:0" /></a></div>);
+ 		my $enclosure = Plagger::Enclosure->new;
+ 		$enclosure->url( URI->new($image->{thumb_link}) );
+ 		$enclosure->auto_set_type;
+ 		$enclosure->is_inline(1);
+ 		$entry->add_enclosure($enclosure);
+ 	    }
+ 	    $entry->body( $body );
+ 	    $feed->add_entry($entry);
+ 	}
+     }
  }
  
  1;
変更箇所は、以下の通り。
  • feed_type: Bbs の追加
  • Plagger::Entry の date に、timezoneを設定。(これをやらないと、module: Freshのruleがずれてしまう)
  • feed_type: Messageでのimageの取得(未確認)
続いて、WWW::Mixi
*** /usr/local/lib/perl5/site_perl/5.8.8/WWW/Mixi.pm.ORIG	Sat Jan  6 21:54:02 2007
--- /usr/local/lib/perl5/site_perl/5.8.8/WWW/Mixi.pm	Sun Jan  7 21:52:17 2007
***************
*** 1426,1431 ****
--- 1426,1432 ----
  	my $re_subj = '<td bgcolor="#fff4e0">&nbsp;(.+?)</td>';
  	my $re_desc = '</table>(.+?)</td>';
  	my $re_c_date = '<td rowspan="2" width="110" bgcolor="#f2ddb7" align="center" nowrap>\n(\d{4})年(\d{2})月(\d{2})日<br>\n(\d{1,2}):(\d{2})';
+ 	my $re_c_no = '<b>(.*?[0-9]+?)</b>';
  	my $re_c_desc = '<td class="h120">(.+?)\n</td>';
  	my $re_link   = '<a href="?(.+?)"?>(.*?)<\/a>';
  	if ($content =~ s/<!-- TOPIC: start -->.*?${re_date}.*?${re_subj}.*?${re_link}(.*?)${re_desc}(.*?)$//is) {
***************
*** 1436,1445 ****
  			next unless ($image =~ /<a [^<>]*'show_picture.pl\?img_src=(.*?)'[^<>]*><img src=([^ ]*) border=0>/);
  			push(@{$item->{'images'}}, {'link' => $self->absolute_url($1, $base), 'thumb_link' => $self->absolute_url($2, $base)});
  		}
! 		while ($comm =~ s/.*?${re_c_date}.*?${re_link}.*?${re_c_desc}.*?<\/table>//is){
! 			my ($time, $link, $name, $desc) = (sprintf('%04d/%02d/%02d %02d:%02d', $1,$2,$3,$4,$5), $6, $7, $8);
! 			($name, $desc) = map { s/[\r\n]+//g; s/<br>/\n/g; $_ = $self->rewrite($_); } ($name, $desc);
! 			push(@{$item->{'comments'}}, {'time' => $time, 'link' => $self->absolute_url($link, $base), 'name' => $name, 'description' => $desc});
  		}
  		push(@items, $item);
  	}
--- 1437,1450 ----
  			next unless ($image =~ /<a [^<>]*'show_picture.pl\?img_src=(.*?)'[^<>]*><img src=([^ ]*) border=0>/);
  			push(@{$item->{'images'}}, {'link' => $self->absolute_url($1, $base), 'thumb_link' => $self->absolute_url($2, $base)});
  		}
! 		while ($comm =~ s/.*?${re_c_date}.*?${re_c_no}.*?${re_link}.*?${re_c_desc}.*?<\/table>//is){
! 			my ($time, $number, $link, $name, $desc) = (sprintf('%04d/%02d/%02d %02d:%02d', $1,$2,$3,$4,$5), $6, $7, $8, $9);
! #			($name, $desc) = map { s/[\r\n]+//g; s/<br>/\n/g; $_ = $self->rewrite($_); } ($name, $desc);
! 			push(@{$item->{'comments'}}, {'time' => $time, 'link' => $self->absolute_url($link, $base), 'name' => $name, 'description' => $desc, 'number' => $number, 'images' => []});
! 			foreach my $image ($desc =~ /<td width=130[^<>]*>(.*?)<\/td>/g) {
! 			    next unless ($image =~ /<img src=([^ ]*) border=0>/);
! 			    push(@{$item->{'comments'}->{'images'}}, {'link' => $self->absolute_url($1, $base), 'thumb_link' => $self->absolute_url($1, $base)});
! 			}
  		}
  		push(@items, $item);
  	}
***************
*** 1582,1588 ****
  	my $label_time = "(?:\Q日 付\E|\Q日&nbsp;付\E)";
  	my $label_name = "(?:\Q差出人\E|\Q宛&nbsp;先\E)";
  	my $label_subj = "(?:\Q件 名\E|\Q件&nbsp;名\E)";
! 	my $time  = sprintf('%04d/%02d/%02d %02d:%02d', $1, $2, $3, $4, $5) if ($content =~ /<$td>$s<font(?:$attr)*>$label_time<\/font>$s:$s(\d{4})年(\d{2})月(\d{2})日$s(\d{2}):(\d{2})<\/td>/is);
  	my $subj  = $self->rewrite($1) if ($content =~ /<$td>$s<font(?:$attr)*>$label_subj<\/font>$s:$s($str)<\/td>/is);
  	my $desc  = $self->rewrite($1) if ($content =~ /<td(?:$attr)*CLASS=h120(?:$attr)*>$s($str)<\/td>/is);
  	my $image = $self->absolute_url($1, $base) if ($content =~ /<$td><a(?:$attr)*><img(?:$attr)*src=["']?([^"'\s<>]+)["'](?:$attr)*><\/a><\/td>/is);
--- 1587,1593 ----
  	my $label_time = "(?:\Q日 付\E|\Q日&nbsp;付\E)";
  	my $label_name = "(?:\Q差出人\E|\Q宛&nbsp;先\E)";
  	my $label_subj = "(?:\Q件 名\E|\Q件&nbsp;名\E)";
! 	my $time  = sprintf('%04d/%02d/%02d %02d:%02d', $1, $2, $3, $4, $5) if ($content =~ /<$td>$s<font(?:$attr)*>$label_time<\/font>$s:$s(\d{4})年(\d{2})月(\d{2})日$s(\d{2})時(\d{2})分$s<\/td>/is);
  	my $subj  = $self->rewrite($1) if ($content =~ /<$td>$s<font(?:$attr)*>$label_subj<\/font>$s:$s($str)<\/td>/is);
  	my $desc  = $self->rewrite($1) if ($content =~ /<td(?:$attr)*CLASS=h120(?:$attr)*>$s($str)<\/td>/is);
  	my $image = $self->absolute_url($1, $base) if ($content =~ /<$td><a(?:$attr)*><img(?:$attr)*src=["']?([^"'\s<>]+)["'](?:$attr)*><\/a><\/td>/is);
変更点は、
  • parse_view_bbs で、コメント番号を取るように
  • parse_view_bbs で、コメントのimagesを取るように
  • parse_view_bbs で、コメントのbrタグを改行に変えないように
最後に、config.yaml
global:
  timezone: Asia/Tokyo
  assets_path: /usr/local/share/Plagger/assets

plugins:
  - module: Publish::Gmail
    rule:
      - module: Rating
        rate:   '>= 0'
    config:
      mailto:   gmailのアドレス@gmail.com
      mailfrom: 差出人のアドレス
      mailroute:
        via: smtp
        host: localhost
      attach_enclosures: 1

  - module: Filter::Rule
    rule:
      - module: Fresh
        mtime:
            path: ホームディレクトリ/.plagger/tstamp
            autoupdate: 1

  - module: Filter::FetchEnclosure
    config:
      dir: ホームディレクトリ/.plagger/enclosures

  - module: CustomFeed::Mixi
    config:
      email: mixiに登録したのメールアドレス
      password: mixiのパスワード
      fetch_body: 1
      fetch_body_interval: 1.5
      fetch_items: 25
      show_icon: 0
      feed_type:
        - Message
        - RecentComment
        - Log
        - FriendDiary
        - Bbs
  • Filter::FetchEnclosureを追加
  • Publish::Gmailのconfigに、attach_enclosures: 1 を追加
で、毎時5分にplaggerを実行するようにcronに設定しました。
便利便利!?
いい加減しつこいですが、もうちょっとだけ改善しました。
今回の変更点は、以下です。
  • コメントのイメージの扱いが間違っていたので修正
  • コミュニティのアンケート(のコメント)に対応(アンケートに答えていない場合の挙動は未確認)
  • コミュニティのトピックのコメントに、コミュニティ名を追加
しかし、アンケートのページには何故か日付が表示されないんですね。これだと、module: Freshがうまく機能しないのではないかしらん。

今回から、パッチを添付します。Mixi.patch-20070108.tar.gz

WWW::Mixiのパッチ
*** /usr/local/lib/perl5/site_perl/5.8.8/WWW/Mixi.pm.ORIG	Sat Jan  6 21:54:02 2007
--- /usr/local/lib/perl5/site_perl/5.8.8/WWW/Mixi.pm	Mon Jan  8 15:56:01 2007
***************
*** 1422,1451 ****
  	my $base    = $res->base->as_string;
  	my $content = $res->content;
  	my @items   = ();
  	my $re_date = '<td rowspan="3" width="110" bgcolor="#ffd8b0" align="center" valign="top" nowrap>(\d{4})年(\d{2})月(\d{2})日<br>(\d{1,2}):(\d{2})</td>';
  	my $re_subj = '<td bgcolor="#fff4e0">&nbsp;(.+?)</td>';
  	my $re_desc = '</table>(.+?)</td>';
  	my $re_c_date = '<td rowspan="2" width="110" bgcolor="#f2ddb7" align="center" nowrap>\n(\d{4})年(\d{2})月(\d{2})日<br>\n(\d{1,2}):(\d{2})';
  	my $re_c_desc = '<td class="h120">(.+?)\n</td>';
  	my $re_link   = '<a href="?(.+?)"?>(.*?)<\/a>';
! 	if ($content =~ s/<!-- TOPIC: start -->.*?${re_date}.*?${re_subj}.*?${re_link}(.*?)${re_desc}(.*?)$//is) {
! 		my ($time, $subj, $link, $name, $imgs, $desc, $comm) = (sprintf('%04d/%02d/%02d %02d:%02d', $1,$2,$3,$4,$5), $6, $7, $8, $9, $10, $11);
  		($desc, $subj) = map { s/[\r\n]+//g; s/<br>/\n/g; $_ = $self->rewrite($_); } ($desc, $subj);
! 		my $item = { 'time' => $time, 'description' => $desc, 'subject' => $subj, 'link' => $res->request->uri->as_string, 'images' => [], 'comments' => [] , 'name' => $name, 'name_link' => $self->absolute_url($link, $base)};
  		foreach my $image ($imgs =~ /<td width=130[^<>]*>(.*?)<\/td>/g) {
  			next unless ($image =~ /<a [^<>]*'show_picture.pl\?img_src=(.*?)'[^<>]*><img src=([^ ]*) border=0>/);
  			push(@{$item->{'images'}}, {'link' => $self->absolute_url($1, $base), 'thumb_link' => $self->absolute_url($2, $base)});
  		}
! 		while ($comm =~ s/.*?${re_c_date}.*?${re_link}.*?${re_c_desc}.*?<\/table>//is){
! 			my ($time, $link, $name, $desc) = (sprintf('%04d/%02d/%02d %02d:%02d', $1,$2,$3,$4,$5), $6, $7, $8);
! 			($name, $desc) = map { s/[\r\n]+//g; s/<br>/\n/g; $_ = $self->rewrite($_); } ($name, $desc);
! 			push(@{$item->{'comments'}}, {'time' => $time, 'link' => $self->absolute_url($link, $base), 'name' => $name, 'description' => $desc});
  		}
  		push(@items, $item);
  	}
  	return @items;
  }
  
  sub parse_view_diary {
  	my $self    = shift;
  	my $res     = (@_) ? shift : $self->response();
--- 1422,1491 ----
  	my $base    = $res->base->as_string;
  	my $content = $res->content;
  	my @items   = ();
+ 	my $re_comm = '<b>(\[.*?\]) トピック</b>';
  	my $re_date = '<td rowspan="3" width="110" bgcolor="#ffd8b0" align="center" valign="top" nowrap>(\d{4})年(\d{2})月(\d{2})日<br>(\d{1,2}):(\d{2})</td>';
  	my $re_subj = '<td bgcolor="#fff4e0">&nbsp;(.+?)</td>';
  	my $re_desc = '</table>(.+?)</td>';
  	my $re_c_date = '<td rowspan="2" width="110" bgcolor="#f2ddb7" align="center" nowrap>\n(\d{4})年(\d{2})月(\d{2})日<br>\n(\d{1,2}):(\d{2})';
+ 	my $re_c_no = '<b>(.*?[0-9]+?)</b>';
  	my $re_c_desc = '<td class="h120">(.+?)\n</td>';
  	my $re_link   = '<a href="?(.+?)"?>(.*?)<\/a>';
! 	if ($content =~ s/<!-- TOPIC: start -->.*?${re_comm}.*?${re_date}.*?${re_subj}.*?${re_link}(.*?)${re_desc}(.*?)$//is) {
! 		my ($community, $time, $subj, $link, $name, $imgs, $desc, $comm) = ($1, sprintf('%04d/%02d/%02d %02d:%02d', $2,$3,$4,$5,$6), $7, $8, $9, $10, $11, $12);
  		($desc, $subj) = map { s/[\r\n]+//g; s/<br>/\n/g; $_ = $self->rewrite($_); } ($desc, $subj);
! 		my $item = { 'time' => $time, 'description' => $desc, 'subject' => $subj, 'link' => $res->request->uri->as_string, 'images' => [], 'comments' => [] , 'name' => $name, 'name_link' => $self->absolute_url($link, $base), 'community_name' => $community};
  		foreach my $image ($imgs =~ /<td width=130[^<>]*>(.*?)<\/td>/g) {
  			next unless ($image =~ /<a [^<>]*'show_picture.pl\?img_src=(.*?)'[^<>]*><img src=([^ ]*) border=0>/);
  			push(@{$item->{'images'}}, {'link' => $self->absolute_url($1, $base), 'thumb_link' => $self->absolute_url($2, $base)});
  		}
! 		while ($comm =~ s/.*?${re_c_date}.*?${re_c_no}.*?${re_link}.*?${re_c_desc}.*?<\/table>//is){
! 			my ($time, $number, $link, $name, $desc) = (sprintf('%04d/%02d/%02d %02d:%02d', $1,$2,$3,$4,$5), $6, $7, $8, $9);
! #			($name, $desc) = map { s/[\r\n]+//g; s/<br>/\n/g; $_ = $self->rewrite($_); } ($name, $desc);
! 			my $comment = {'time' => $time, 'link' => $self->absolute_url($link, $base), 'name' => $name, 'description' => $desc, 'number' => $number, 'images' => []};
! 			push(@{$item->{'comments'}}, $comment);
! 			foreach my $image ($desc =~ /<td width="?130"?[^<>]*>(.*?)<\/td>/g) {
! 			    next unless ($image =~ /<img src=([^ ]*) border="?0"?>/);
! 			    push(@{$comment->{'images'}}, {'link' => $self->absolute_url($1, $base), 'thumb_link' => $self->absolute_url($1, $base)});
! 			}
  		}
  		push(@items, $item);
  	}
  	return @items;
  }
  
+ sub parse_view_enquete {
+ 	my $self    = shift;
+ 	my $res     = (@_) ? shift : $self->response();
+ 	return unless ($res and $res->is_success);
+ 	my $base    = $res->base->as_string;
+ 	my $content = $res->content;
+ 	my @items   = ();
+ 	my $re_comm = '<b>(\[.*?\]).*?</b>';
+ 	my $re_subj = '<td bgcolor=#ffffff width=530>(.+?)</td>';
+ 	my $re_desc = '</table>(.+?)</td>';
+ 	my $re_c_date = '<td rowspan="2" width="110" bgcolor="#f2ddb7" align="center" nowrap>\n(\d{4})年(\d{2})月(\d{2})日<br>(\d{1,2}):(\d{2})';
+ 	my $re_c_no = '<b>(.*?[0-9]+?)</b>';
+ 	my $re_c_desc = '<td class="h120">(.+?)</td>';
+ 	my $re_link   = '<a href="?(.+?)"?>(.*?)</a>';
+ 	if ($content =~ s/<td align=center colspan=2>.*?${re_comm}.*?${re_subj}.*?${re_desc}.*?${re_link}(.*?)$//is) {
+ 		my ($community, $subj, $desc, $link, $name, $comm) = ($1, $2, $3, $4, $5, $6);
+ #		($desc, $subj) = map { s/[\r\n]+//g; s/<br>/\n/g; $_ = $self->rewrite($_); } ($desc, $subj);
+ 		my $item = { 'description' => $desc, 'subject' => $subj, 'link' => $res->request->uri->as_string, 'comments' => [] , 'name' => $name, 'name_link' => $self->absolute_url($link, $base), 'community_name' => $community};
+ 		while ($comm =~ s/.*?${re_c_date}.*?${re_c_no}.*?${re_link}.*?${re_c_desc}.*?<\/table>//is){
+ 			my ($time, $number, $link, $name, $desc) = (sprintf('%04d/%02d/%02d %02d:%02d', $1,$2,$3,$4,$5), $6, $7, $8, $9);
+ #			($name, $desc) = map { s/[\r\n]+//g; s/<br>/\n/g; $_ = $self->rewrite($_); } ($name, $desc);
+ 			my $comment = {'time' => $time, 'link' => $self->absolute_url($link, $base), 'name' => $name, 'description' => $desc, 'number' => $number, 'images' => []};
+ 			push(@{$item->{'comments'}}, $comment);
+ 			foreach my $image ($desc =~ /<td width="?130"?[^<>]*>(.*?)<\/td>/g) {
+ 			    next unless ($image =~ /<img src=([^ ]*) border="?0"?>/);
+ 			    push(@{$comment->{'images'}}, {'link' => $self->absolute_url($1, $base), 'thumb_link' => $self->absolute_url($1, $base)});
+ 			}
+ 		}
+ 		push(@items, $item);
+         }
+ 	return @items;
+ }
+ 
  sub parse_view_diary {
  	my $self    = shift;
  	my $res     = (@_) ? shift : $self->response();
***************
*** 1473,1479 ****
  			push(@{$item->{'comments'}}, {'time' => $time, 'link' => $self->absolute_url($link, $base), 'name' => $name, 'description' => $desc});
  		}
  		push(@items, $item);
! 	}
  	return @items;
  }
  
--- 1513,1519 ----
  			push(@{$item->{'comments'}}, {'time' => $time, 'link' => $self->absolute_url($link, $base), 'name' => $name, 'description' => $desc});
  		}
  		push(@items, $item);
!         }
  	return @items;
  }
  
***************
*** 1582,1588 ****
  	my $label_time = "(?:\Q日 付\E|\Q日&nbsp;付\E)";
  	my $label_name = "(?:\Q差出人\E|\Q宛&nbsp;先\E)";
  	my $label_subj = "(?:\Q件 名\E|\Q件&nbsp;名\E)";
! 	my $time  = sprintf('%04d/%02d/%02d %02d:%02d', $1, $2, $3, $4, $5) if ($content =~ /<$td>$s<font(?:$attr)*>$label_time<\/font>$s:$s(\d{4})年(\d{2})月(\d{2})日$s(\d{2}):(\d{2})<\/td>/is);
  	my $subj  = $self->rewrite($1) if ($content =~ /<$td>$s<font(?:$attr)*>$label_subj<\/font>$s:$s($str)<\/td>/is);
  	my $desc  = $self->rewrite($1) if ($content =~ /<td(?:$attr)*CLASS=h120(?:$attr)*>$s($str)<\/td>/is);
  	my $image = $self->absolute_url($1, $base) if ($content =~ /<$td><a(?:$attr)*><img(?:$attr)*src=["']?([^"'\s<>]+)["'](?:$attr)*><\/a><\/td>/is);
--- 1622,1628 ----
  	my $label_time = "(?:\Q日 付\E|\Q日&nbsp;付\E)";
  	my $label_name = "(?:\Q差出人\E|\Q宛&nbsp;先\E)";
  	my $label_subj = "(?:\Q件 名\E|\Q件&nbsp;名\E)";
! 	my $time  = sprintf('%04d/%02d/%02d %02d:%02d', $1, $2, $3, $4, $5) if ($content =~ /<$td>$s<font(?:$attr)*>$label_time<\/font>$s:$s(\d{4})年(\d{2})月(\d{2})日$s(\d{2})時(\d{2})分$s<\/td>/is);
  	my $subj  = $self->rewrite($1) if ($content =~ /<$td>$s<font(?:$attr)*>$label_subj<\/font>$s:$s($str)<\/td>/is);
  	my $desc  = $self->rewrite($1) if ($content =~ /<td(?:$attr)*CLASS=h120(?:$attr)*>$s($str)<\/td>/is);
  	my $image = $self->absolute_url($1, $base) if ($content =~ /<$td><a(?:$attr)*><img(?:$attr)*src=["']?([^"'\s<>]+)["'](?:$attr)*><\/a><\/td>/is);
***************
*** 2246,2251 ****
--- 2286,2298 ----
  	my $url  = shift or return;
  	$self->set_response($url, @_) or return undef;
  	return $self->parse_view_bbs();
+ }
+ 
+ sub get_view_enquete {
+ 	my $self = shift;
+ 	my $url  = shift or return;
+ 	$self->set_response($url, @_) or return undef;
+ 	return $self->parse_view_enquete();
  }
  
  sub get_view_community {
CustomFeed::Mixiのパッチ
*** /usr/local/lib/perl5/site_perl/5.8.8/Plagger/Plugin/CustomFeed/Mixi.pm.ORIG	Fri Jan  5 23:14:14 2007
--- /usr/local/lib/perl5/site_perl/5.8.8/Plagger/Plugin/CustomFeed/Mixi.pm	Mon Jan  8 13:13:19 2007
***************
*** 48,53 ****
--- 48,62 ----
          get_list   => 'parse_show_calendar',
          get_detail => 'get_view_event',
      },
+     Bbs => {
+         start_url  => 'http://mixi.jp/new_bbs.pl',
+         title      => 'コミュニティ最新書き込み',
+         get_list   => 'parse_new_bbs',
+         get_detail => 'dummy',
+         get_detail_bbs => 'get_view_bbs',
+         get_detail_event => 'get_view_event',
+         get_detail_enquete => 'get_view_enquete',
+     },
  };
  
  sub plugin_id {
***************
*** 134,148 ****
  
      my $i = 0;
      my $blocked = 0;
      for my $msg (@msgs) {
          next if $type eq 'FriendDiary' and not $msg->{image}; # external blog
          last if $i++ >= $items;
  
          my $entry = Plagger::Entry->new;
!         $entry->title( decode('euc-jp', $msg->{subject}) );
          $entry->link($msg->{link});
          $entry->author( decode('euc-jp', $msg->{name}) );
          $entry->date( Plagger::Date->parse($format, $msg->{time}) );
  
          if ($self->conf->{show_icon} && !$blocked && defined $MAP->{$type}->{icon_re}) {
              my $owner_id = ($msg->{link} =~ $MAP->{$type}->{icon_re})[0];
--- 143,165 ----
  
      my $i = 0;
      my $blocked = 0;
+     my $item;
      for my $msg (@msgs) {
          next if $type eq 'FriendDiary' and not $msg->{image}; # external blog
          last if $i++ >= $items;
  
          my $entry = Plagger::Entry->new;
! 	my $title = "";
! 	if ($msg->{community_name}) {
! 	    $title = decode('euc-jp', $msg->{subject}) . " ";
! 	}
!         $entry->title( $title . decode('euc-jp', $msg->{subject}) );
          $entry->link($msg->{link});
          $entry->author( decode('euc-jp', $msg->{name}) );
          $entry->date( Plagger::Date->parse($format, $msg->{time}) );
+ 	if ($entry->date) {
+ 	    $entry->date->set_time_zone('Asia/Tokyo');
+ 	}
  
          if ($self->conf->{show_icon} && !$blocked && defined $MAP->{$type}->{icon_re}) {
              my $owner_id = ($msg->{link} =~ $MAP->{$type}->{icon_re})[0];
***************
*** 170,182 ****
              }
          }
  
          if ($self->conf->{fetch_body} && !$blocked && $msg->{link} =~ /view_/ && defined $MAP->{$type}->{get_detail}) {
              $context->log(info => "Fetch body from $msg->{link}");
!             my $item = $self->cache->get_callback(
                  "item-$msg->{link}",
                  sub {
                      Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
!                     my $meth = $MAP->{$type}->{get_detail};
                      my($item) = $self->{mixi}->$meth($msg->{link});
  
                      if ($meth eq 'get_view_diary') {
--- 187,203 ----
              }
          }
  
+ 	$item = 0;
+ 
          if ($self->conf->{fetch_body} && !$blocked && $msg->{link} =~ /view_/ && defined $MAP->{$type}->{get_detail}) {
              $context->log(info => "Fetch body from $msg->{link}");
!             $item = $self->cache->get_callback(
                  "item-$msg->{link}",
                  sub {
                      Time::HiRes::sleep( $self->conf->{fetch_body_interval} || 1.5 );
! #                    my $meth = $MAP->{$type}->{get_detail};
!                     my $meth = get_meth($MAP->{$type}, $msg->{link});
! 
                      my($item) = $self->{mixi}->$meth($msg->{link});
  
                      if ($meth eq 'get_view_diary') {
***************
*** 189,195 ****
              if ($item) {
                  my $body = decode('euc-jp', $item->{description});
                     $body =~ s!(\r\n?|\n)!<br />!g;
!                 for my $image (@{ $item->{images} }) {
                      $body .= qq(<div><a href="$image->{link}"><img src="$image->{thumb_link}" style="border:0" /></a></div>);
                      my $enclosure = Plagger::Enclosure->new;
                      $enclosure->url( URI->new($image->{thumb_link}) );
--- 210,217 ----
              if ($item) {
                  my $body = decode('euc-jp', $item->{description});
                     $body =~ s!(\r\n?|\n)!<br />!g;
! 		my $image;
!                 for $image (@{ $item->{images} }) {
                      $body .= qq(<div><a href="$image->{link}"><img src="$image->{thumb_link}" style="border:0" /></a></div>);
                      my $enclosure = Plagger::Enclosure->new;
                      $enclosure->url( URI->new($image->{thumb_link}) );
***************
*** 197,205 ****
--- 219,238 ----
                      $enclosure->is_inline(1);
                      $entry->add_enclosure($enclosure);
                  }
+ 		if ($image = $item->{image}) {
+                     $body .= qq(<div><a href="$image"><img src="$image" style="border:0" /></a></div>);
+                     my $enclosure = Plagger::Enclosure->new;
+                     $enclosure->url( URI->new($image) );
+                     $enclosure->auto_set_type;
+                     $enclosure->is_inline(1);
+                     $entry->add_enclosure($enclosure);
+ 		}
                  $entry->body($body);
  
                  $entry->date( Plagger::Date->parse($format, $item->{time}) );
+ 		if ($entry->date) {
+ 		    $entry->date->set_time_zone('Asia/Tokyo');
+ 		}
              } else {
                  $context->log(warn => "Fetch body failed. You might be blocked?");
                  $blocked++;
***************
*** 207,212 ****
--- 240,248 ----
          }
  
          $feed->add_entry($entry);
+ 	if ($self->conf->{fetch_body} && $item) {
+ 	    add_comments($feed, $item);
+ 	}
      }
  
      $context->update->add($feed);
***************
*** 221,226 ****
--- 257,305 ----
      }
  
      return \@images;
+ }
+ sub get_meth {
+     my($type, $link) = @_;
+     
+     my $meth = "";
+     if ($link =~ /view_bbs/) {
+         $meth = $type->{get_detail_bbs};
+     } elsif ($link =~ /view_event/) {
+         $meth = $type->{get_detail_event};
+     } elsif ($link =~ /view_enquete/) {
+         $meth = $type->{get_detail_enquete};
+     } else {
+         $meth = $type->{get_detail};
+     }
+     return $meth;
+ }
+ 
+ sub add_comments {
+     my ($feed, $item) = @_;
+     my $format = DateTime::Format::Strptime->new(pattern => '%Y/%m/%d %H:%M');
+     if ($item->{'comments'}) {
+ 	for my $comment (@{$item->{'comments'}}) {
+ 	    my $entry = Plagger::Entry->new;
+ 	    $entry->title(decode('euc-jp', $item->{'community_name'}) . " " . decode('euc-jp', $item->{'subject'}) . ": " . $comment->{'number'});
+ 	    $entry->link($item->{'link'});
+ 	    $entry->author( decode('euc-jp', $comment->{'name'}) );
+ 	    $entry->date( Plagger::Date->parse($format, $comment->{'time'}) );
+ 	    if ($entry->date) {
+ 		$entry->date->set_time_zone('Asia/Tokyo');
+ 	    }
+ 	    my $body = decode('euc-jp', $comment->{'description'});
+ 	    for my $image (@{ $comment->{images} }) {
+ 		$body .= qq(<div><a href="$image->{link}"><img src="$image->{thumb_link}" style="border:0" /></a></div>);
+ 		my $enclosure = Plagger::Enclosure->new;
+ 		$enclosure->url( URI->new($image->{thumb_link}) );
+ 		$enclosure->auto_set_type;
+ 		$enclosure->is_inline(1);
+ 		$entry->add_enclosure($enclosure);
+ 	    }
+ 	    $entry->body( $body );
+ 	    $feed->add_entry($entry);
+ 	}
+     }
  }
  
  1;
さらに改善。(いい加減どこを直したか忘れた・・・)
ごめんなさい!前回添付したパッチは、webに上げるように & とか < とかを実体参照に変換したものでした。
今回新しいパッチを付け直しますので、こちらをご利用ください。
Mixi.patch-20070111.tar.gz
PlaggerのMixiのパッチを tar.gz で公開しているのだけれど、ダウンロードしてもうまく使えないとの指摘をもらった。
なんで~?と思って自分でもブラウザでダウンロードしてみたところ、Windows上でうまく展開できない。(使っているのは、Lhaplus)
で、FreeBSDにコピーしてみたところ、tar xzf では展開できず、zcat | tar xf では展開できた。
ファイルサイズを見てみると、アップロードしたものと違っている。
で、思い当たったのが apache2 の DEFLATEフィルタ。
以下の設定がされている。
SetOutputFilter DEFLATE
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|mov|avi|wmv)$ no-gzip dont-vary
Header append Vary User-Agent env=!dont-vary
これによって、gzipの圧縮がやり直されている??
いまいち原理はわからないのだけれど、圧縮しない拡張子にgzを追加してみる。
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|mov|avi|wmv|gz|tgz)$ no-gzip dont-vary
これで、会社からダウンロードしてみたら、ファイルサイズが変わらないことを確認。
しかし、相変わらず私の環境ではLhaplusでは展開できない。
gunzip と tar の使用をオススメします。
結城浩さんが、新しい本を出すそうですが、7名に無料プレゼントだそうです。

すごい倍率なので当選は難しそうですが、応募してみることにしました。
VMWare Player で遊んでいるのだけれど、Red Hat Linux 9で dhcp クライアントの設定をしてもうまくIPが取れない。
/etc/sysconfig/network-scripts/ifcfg-eth0 に以下を書いてあって、
DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes
起動時にも、ifup eth0 時にも
Determining IP information for eth0... failed; no link present. Check cable?
と言うエラーが出てIPが取れない。ちなみに、手動でdhclientを実行すると取れる。
調べてみた結果、Getting a DHCP Address in a Red Hat Linux 9.0 or Advanced Server Virtual Machineが該当した。
で、ifcfg-eth0 を書いてある通りに変更。
DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes
check_link_down () {
    return 1;
}
cactiにセキュリティホールがあったらしいのでバージョンアップ。
0.8.6h→0.8.6i
  • 今までは、データをvarに入れたくて、/usr/local/share/cacti/のlogとstaticとrraの3つのディレクトリを、cactiインストール後にリンク張ってたんだけど、今回のバージョンアップではリンクが消されなかったので、そのまま行けた。
  • 今までは、/usr/local/share/cacti/include/config.php で$config["url_path"]にURLの相対を入れてたんだけど、この変数がなくなった模様。なので、デフォルトのconfig.phpがそのまま使えるようになった。
  • /usr/local/share/cacti/include/db-settings.phpは特に変更なしっぽい。
  • Graph Exportで静的なグラフを生成してるんだけど、今まではExport Directory で指定した場所にhtmlもpngもできてたのに、pngの場所がExport Directory で指定した場所のgraphs/に移動した。
最近はあまり切れることはないんだけど、昔はfletsのpppoeが切れて、切れたことを検知できないことがたまにあった。
で、その対策がFreeBSD Q&Aに載っていたのでメモ。
しかし、OSの起動時にpppの接続が終わらないうちに */rc.d/ のスクリプトが実行されてしまい、結果的にapache等が起動できない問題はずっと解決しない。
他に同じ症状の人いないのかなあ・・・。
今朝1時14分から、1時27分にかけて、81.95.148.50から152回のトラックバックspam攻撃を受けました。
# 向こうはDOSのつもりはないのでしょうが、うちのサーバに取ってはDOSでした。

普通にwebを見ていたら、やたら遅くなって、おかしいな~と思ってログインしてみたら、perlがいっぱい立ち上がって、他のことができない状態になっていました。
すぐにapacheを止めて、上記IPをアクセス禁止にして、apacheを立ち上げました。
とりあえず、MT-Blacklistの善戦むなしく、日々送られてくるトラックバックspamに悩まされています。
で、MT2.661からの機能で、同一IPからの連続トラックバックをはじく設定があるので、それを入れてみました。
mt.cfgに、以下を追加してみました。
ThrottleSeconds 180
これで、180秒以内の連続アクセスをはじくようになるそうです。(デフォルトは20秒)

しかし、今回のような連続アクセスに対しては、上記対策ではトラックバックをはじくのみで、perlのプロセスが上がるのは防げません。
apacheの設定は、preforkで以下のようになっています。
StartServers         2
MinSpareServers      2
MaxSpareServers      5
MaxClients         150
MaxRequestsPerChild  0
MaxSpareServersの意味を今まで誤解していて、上記設定だと最大でapacheのプロセスが5個上がるんだと思っていたのですが、MaxClients + MaxSpareServers上がるようです。
cgiだけに150もアクセスされたら何もできなくなってしまうみたいです。
これをなんとかする設定はないのかな~?と思って調べたところ、RLimitNPROCと言うディレクティブに行き当たったのですが、説明を読んでもいまいちピンと来ません。
cgiが起動するサブプロセス?だとすると意味がないので、今回は設定しませんでした。

ん~、いい加減あきらめてMT3のライセンスを購入するべきなのかなあ・・・。でも移行が面倒だよなあ・・・。
自宅のPCだと、Windows MessengerとYahoo!メッセンジャー、Skypeが常駐している。
で、いつも休止状態にしているんだけれど、復帰したときにWindows Messengerだけ接続状態にならない。
MSN Messengerを入れたりしていた時期もあるんだけど、OSの起動時にWindows Messengerと両方起動したりしていまいち感があった。
で、今回Windows Live Messengerを入れてみました。
相変わらず、派手になった画面、消せない広告等は気に入りませんが、以下が良くなった点です。
  • 表示メッセージ(Skypeで言うムードメッセージ)が設定できるようになった。(しかし、Windows Messengerの相手には表示されないと思う)
  • hotmailの新着がわかる(今は30日アクセスしないと失効するらしい。今回も、無効になってた)
  • メンバーのログイン、ログアウトが通知される(Yahoo!メッセンジャーには昔からある機能)
  • 休止状態から復帰したときに、ちゃんと接続する。
いくつかは、MSN Messengerにはあった機能だけど、あると便利。
後は、OSを再起動したときにどんな感じで起動するかだな。
別のマシンに入れて、MT-3.34にして問題がないかをチェックした。
結論として、そこそこ手はかかるものの、3系に上げても問題なさそうだ。

  1. DBのバックアップとリストア
    ダンプ
    % pg_dump -b -E utf8 -F c -f mt.dump
    
    リストア
    % setenv PGCLIENTENCODING utf8
    % createdb mt
    % pg_restore -d mt mt.dump
    
    なぜか、pg_restoreには-Eオプションがない&-Cオプションでdatabaseを作らせようとしても駄目だったので、createdbしてからpg_restoreした。
    この手順で、別のマシンにDBをコピーした。
  2. MTのインストール
    zipを展開し、cgi-bin/mt (mt-static以外)と DocumentRoot/mt(mt-static)に必要なファイルをコピー。
  3. 一応、mt-check.cgiで必要なライブラリがインストールされていることを確認
  4. mt.cgiにアクセスして、ログインするとDBがバージョンアップされる。それなりに時間がかかった。
  5. ホストが変わっているので、適当に設定を変更してサイトの再構築。
  6. インデックスと連動しない設定にしていたスタイルシートやjavascriptは手動で再構築する。
  7. ここまでで、ほぼ元通りの見た目になった。
  8. 続いて、プラグインの移行。使っていたのは、aws.pl, processtags.pl, AdminLinks.pl, Blacklist.pl, patch-20050124-mail-spam.pl。後ろの二つは、MT3では必要ないと思うので割愛。
    • aws.plとprocesstags.pl
      aws.plはそのまま入った。processtags.plは、Amazon Web Service - playground | blogを参考にuse bradchoate::postproc;の行をコメントアウト。
    • AdminLinks.pl
      そのまま入った?
これで、うまく行きそうな見通しが立ったので、時間が取れるときに本サイトのバージョンを上げようと思う。
# メンテナンス情報サイトのMTを何か他のものにしなくっちゃ。Drupalにでもするかなあ。
あ、mail2entryもチェックしてないなあ。でもこいつは別サイトでは確認できないので・・・。
と、言うわけでさくっとバージョンアップしてみました。
で、やってみてわかったことですが、新しい機能のうちの一部は、テンプレートを新しくしないと有効になりません。
たとえば、コメントの投稿にTypeKeyの認証が要求できたりするんですが、そのためにはテンプレートが新しくなっている必要があります。
Template Backup and Refreshと言うpluginがついているのですが、こいつがやってくれるのは本当に「バックアップ」と「置き換え」だけです。
これだと、今までやってきたサイトのスタイルと言うものが新しいものに「置き換え」られてしまうので嬉しくありません。
またまた暇を見つけて、テンプレートをマージしていく必要がありそうです。
テンプレート周りをしこしこいじる。
  • アップロード周りを修正していたので、mt/tmpl/cms/upload.tmplとmt/tmpl/cms/upload_complete.tmplを修正。
  • atom/rss周りは新しいものに置き換え。
  • mt-site.jsは新しいものを追加し、古いscripts.jsはそのまま残す。重複する関数はscripts.jsから削除。
  • Stylesheet(styles-site.css) マージ?すると泣きそうなので、デザインが狂うところだけ後から持ってくることにする。
  • コメント周りは、popupしないように修正が入っているらしいので、ちょっと悩む。とりあえず、TypeKeyまわりの修正をちょこっとだけいれて、放置。
    小粋空間:ポップアップ画面を用いたコメント投稿が参考になりそうだ。
MovableType3.3のpluginで、Widget Managerと言うのがあるらしい。
一度でもWidget Managerを起動すると、テンプレートのモジュールのところに、サイドバーに貼れるWidgetのテンプレートが増える。
こいつをサイト用にカスタマイズして、Widget Managerでblog毎に組み合わせて、Main Indexのテンプレートに<$MTWidgetManager name="Widget Managerの名前"$>なんて書いてやれば、ばっちりらしい。
今まで、微妙にMain Indexに並べたいパーツが違ったので、テンプレートが共有できなかったんだけど、こいつを使えばテンプレートが統一できる。
さらに、分割統治するからテンプレートのソースの見通しも良くなるよね。
これは良いんじゃないですか?考えた人偉い!

でも、・・・でもね?移行が面倒なんだよ~(泣)
はあ、やる気と時間が欲しい。
3系に上げてから、エントリーの検索でエラーをやたら見るようになった。
「現在検索中です。検索が終わるまでお待ちください。」と言うエラー。
検索中?じゃあ、しばらくするとページが切り替わるのかな?と思ってエラーページのソースを見ても特にリフレッシュの指定とかはない。手動でリロードしても駄目。
で、googleさんに頼んだらまたもや小粋空間: Movable Type の検索でエラーになる問題についてに辿りつきました。
エエェ~!なんと、つい最近spam対策で入れたThrottleSeconds がいけないようです。
今の設定だと3分に一回しか検索できない(泣)。
いや、それはどうなのかと。コメントやトラックバックと、検索は同じ扱いではいかんのじゃないかと。悲しくて悲しくて、とてもやりきれないじゃないかと。
と歌っていてもしょうがないので、今度ソースをいじることにしよう。幸い、上記リンク先には、ソースをいじる方法と、エラーメッセージを変える方法が載っている。ありがたやありがたや。
暇を見つけて、小粋空間さんには全部目を通しておいた方が良いかも知れないな。
2007年1月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      

このアーカイブについて

このページには、2007年1月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2006年12月です。

次のアーカイブは2007年2月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Powered by Movable Type 6.1.1