• ペルソナ3 ペルソナ合体検索

    突然こんなものを書いてみた。
    • 合体の結果、入手したいペルソナを起点として、どのペルソナを合体させればよいか候補を列挙する。
    • 合体の結果、継承して欲しいスキルを条件に加えることができる。
    • javascriptでローカルに検索しているが、PCスペックによっては結構重いかも。(総当りで何が出来るか調べているのが原因だけども。先に全部の組み合わせを計算しておいて、アクセスパスを設ければ軽くなる??)
    • ノーマルスプレッドだけ対応。今のままだとトライアングルはまともな検索速度にならんか。
    ペルソナ合体検索

    リンク先の注意事項をよく読んだうえで使用してください。

  • tracからやって来るメール その2

    tracから送られてくるメールのDateフィールドの値は、タイムゾーンがGMTになっている。
    Date: Sun, 16 Jul 2006 14:29:21 -0000
    タイムゾーンを無視しているメーラーがある。メーラーというかwebメールなのだが。あきらかにメーラーが悪いが、かといって9時間ずれたメールが並んでいるのはなんだか嫌だ。ということで、送信メールのDateフィールドにlocaltimeを適用するパッチを追加。
    Date: Sun, 16 Jul 2006 23:32:00 +0900
    ダウンロード→trac.patch.txt

    元のソースは、インタアクト株式会社が日本語化したtrac-0.9.5-ja-1。

    > 元のソースを展開する
    > パッチをDLしてどこかに置く
    > cd trac-0.9.5-ja-1/ (展開したソースディレクトリにcd)
    > patch -p1 < DLしたパッチ
    # python setup.py install
    (で、いけるはず)

    設定例

    [notification]
    always_notify_owner = true
    smtp_always_cc =
    smtp_password =
    smtp_enabled = true
    smtp_replyto = xxxxxxx@example.com
    smtp_port = 25
    always_notify_reporter = true
    smtp_server = SMTPサーバ
    smtp_from = xxxxxxx@example.com
    smtp_user =
    use_localtime = true
  • C#でFelicaからIDを読む

    以前、Felicaを使ったシステムを企画してみたいなぁとふいに考えた。で、Felicaを使った(Felicaにアクセスする)開発には「SDK for Felica」という製品が必要だ。SDKにはLiteからEnterpriseまで出来ることの違う製品がいくつかある。
    当時、いいタイミングで「IC CARD WORLD 2005」というのがビッグサイトでやっていたので、これに出かけていってソニーのブースでSDKの値段やら色々聞いてみた。Liteを使えばフリー領域のみではあるが読み書きが出来るというので、さっそく購入しようと代理店2社に電話した。そしたら「個人の方にはお売りできません」との返事。がっかりだ。
    ※ホントにシェアとりたいんだったらSDKは個人にも開放して、どんどんアプリやシステムを書かせればいいと思うんだけどね。

    それから1.5年ほどたって、ふとオープンソースマガジンを手にとると、自前で解析して読み取りを成功させている人がいるじゃないか。恐ろしいことよ。(「SFCardPeeper」のことは以前から知っていたが、技術情報を整理して雑誌に載せた、というのがすごいと思う)さっそくプロジェクトのページからダウンロードしてSuicaを読ませてみる。自分が持っていたR/WはS310だったので見事に読めず。

    やむを得ずビックカメラでS320をゲットしてきた。携帯にもFelicaが載るようになったせいか、S320の読み取り部分はフラットになった。なので、自分のように机の上が雑然としていて、水平にR/Wを置けない環境では、載せたカードがすべり落ちてしまう。この辺はS310の方がよかったなぁ、などと思いつつチャレンジ。S320に変えたらあっさり読めた。

    じゃあ、早速何か書いてみよう、とlibpasoriを眺めたのだが、unixとソース共用を意識しているせいか開発環境がcygwinやMinGWなんだな。それに最近はC#が好きだし、C#からアクセスできるようにしたいなぁ、ということでlibpasori.NETを書いてみた。

    注意:要するにラッパーを書いただけなので、Felicaおよびlibpasoriの技術的な情報は何もありません。libpasoriの情報はlibpasori - RC-S320操作コードを参照

    方針

    • 目的:libpasoriをC#から使いたい。
      • まずVisual C++を使ってlibpasoriとインタフェースするためのDLLを作る。
      • このDLLをP/InvokeでC#から呼び出す。
    • libpasoriには読み書きのための関数がいくつかあるが、率直な話、プロトコルの解析には興味がないし、セキュアなR/W(特にW)はSDKがないと無理そうだ、と考えた。となれば「カードのIDを読み取る」機能だけがあればいいので、以下の関数だけ呼び出せるようにすることにした。
      • pasori_open()
      • pasori_init()
      • felica_polling()
      • pasori_close()
    • libpasoriはまだこれから変化していくはずなので、出来れば元のソースはいじらないで済ませる。

    準備

    • LibUsb-Win32
      • Windows版のlibpasoriを動かすのに必要。事前にインストールしておく。
      • ちなみに僕のところではこれをインストールしたら、Windowsがシャットダウンしなくなった。(「シャットダウンしています」のままになる。)ので、自己責任で。USBsnoopも使ってるのでこれが競合しているのかもしれない。
      • 今回使ったのは「libusb-win32-filter-bin-20060518.exe」。リリース版で上記のようにシャットダウンしなくなったのでsnapshot版ならイケるかな、と思ったらそうでもなかった。
      • 上記のファイルをインストールすると、ソースとヘッダファイルもインストールされる。うれしいことにVC用のインポートライブラリも同梱されているのでありがたく利用する。
      • 上記のヘッダをインクルードできるよう、上記ライブラリをリンクできるようにVisualStudioのディレクトリ設定を追加する。
    • libpasori
      • 上記ページからソースをダウンロード。今回使ったのはlibpasori02.tar.bz2

    libpasori.NET

    • cc.breeze.libpasori.Pasori
      • libpasoriのpasori型に相当します。
    • cc.breeze.libpasori.Felica
      • libpasoriのfelica型に相当
    • cc.breeze.libpasori.PasoriNotifier
      • 別スレッドを用いて、一定間隔でパソリをポーリングし、完了後にコールバックする。

    簡単なサンプル

            static void Main(string[] args)
            {
                Pasori ps = null;
    
                try
                {
                    ps = Pasori.Open();
    
                }
                catch (LibPasoriException ex)
                {
                    Console.Error.WriteLine("パソリの初期化でエラー");
                    return;
                }
    
                try
                {
                    Felica fc = ps.Polling(SystemCode.ANY);
                    Console.Out.WriteLine("IDm:" + fc.IDm_text);
                    fc.Close();
                }
                catch (LibPasoriException ex)
                {
                    /*
                     * 現在のところエラーなのか、何も読めなかったのかは区別がないので
                     * 例外を無視
                     */
                }
                
                ps.Close();
            }
    

    で、サンプルがこれ。パソリをUSBポートに挿して、Felica(僕のところで試したのはSuicaとauのお財布ケータイ)をかざせば、カード固有のIDを読み取ることができる。

    libpasori.NET(ソースとサンプルexe:約330KB)
    • Visual Studio 2005 on XP Proでコンパイルしたもの。他の環境で動くかどうかわかりません。
    • この配布物の使用は自己責任で。何が起こっても作者は責任を負いかねます。
    • NOD32アンチウイルス パターン1.1650(20060707)でチェック済み。
    • ただのラッパーにライセンスを謳うのも気が引けるけど、ソースの改変、配布は自由です。
      • libusb、libpasoriについては、上述の両プロジェクトのページを参照してください。
  • tracパッチまとめ

    以下のエントリで書いたパッチをひとくくりにしたもの。
    ダウンロード→trac.patch.txt

    元のソースは、インタアクト株式会社が日本語化したtrac-0.9.5-ja-1。

    > 元のソースを展開する
    > パッチをDLしてどこかに置く
    > cd trac-0.9.5-ja-1/ (展開したソースディレクトリにcd)
    > patch -p1 < DLしたパッチ
    # python setup.py install
    (で、いけるはず)
  • tracからやって来るメール

    trac.iniでsmtp_enabledをtrueにsmtp_fromかsmtp_replytoにアドレスを設定すれば、でチケットを登録したり更新した際に関係者にメールが送られる。
    このメールについて、自分の趣味とは合わないな~と思っていること。

    その1 - From:の表示名とアドレス

    tracからのメールは以下のように「プロジェクトさん」から送られてきたことになる。
    From: [trac]セクションのname値 <[notification]セクションのsmtp_from値>
    一人でTODOやアイデア管理に使っている分には特に気にならないが、複数の利用者が1つのチケットに対してあれこれ変更や追記をすると、受信したメールの発信者が全て「プロジェクトさん」のため、メーラで整理しにくい。
    そもそもSubjectにプロジェクト名が入っているので、メールの一覧表示における情報量に無駄があるなあ、と。
    ※ちなみに、tracから来たメールはプロジェクトごとに特定のフォルダに振り分けて、スレッド表示にしている。
    • From:フィールドの表示名を報告者の名前にする
      • といっても、on/offの制御はしたいところなので、trac.iniの[notification]セクションにuse_reporter_name=trueを設定すると、表示名が報告者の名前になるようにする。falseか未設定なら元の通りプロジェクト名が表示名になる。
      • 「報告者の名前」は、以下の優先順位で適用される
        1. 「ユーザ設定」で「名前」に記入した文字列
        2. 認証名(ログイン状態で「xxxxとしてログイン中」と出るアレ)
    • From:フィールドのメールアドレスを報告者のアドレスにする
      • 表示名と同様に、trac.iniの[notification]セクションにuse_reporter_email=trueを設定すると、Fromアドレスが報告者のアドレスになるようにする。falseか未設定なら元の通りプロジェクト名が表示名になる。
      • 結果的にFromの詐称を行うことになるので、smtpサーバによってはメール送信を蹴ってしまうかもしれない。(自ドメイン以外のメールアドレスがFrom:にあると蹴るケースなど)
      • smtp_fromに指定したアドレスはMAIL FROMとしてsmtpセッションで使われ、結果的にSender:の値になる。ウチで使っている環境に関して言うと、smtp_fromにsmtpサーバにとってローカルなメールアドレスを指定しておけば、From:が他ドメインでも送信してくれた。

    その2 - From:の表示名とあて先の表示名をMIMEエンコードしない

    特にanonymousで使っていて、「ユーザ設定」で日本語の名前を設定している場合、表示名部分をMIMEエンコードしないので文字化けしてしまう。先のエントリにも書いたようにウチの環境はanonymousでは使っておらず、認証名も英字のみとしているので、あまり問題ないのかも。
    • 送信先が「表示名 <アドレス>」の場合、表示名部分をMIMEエンコードする。判定はかなり適当。
    • 文字セットはSubjectを踏襲してUTF-8として、手抜きした。pythonでiso-2022-jpに変換するのって簡単なのかな。

    その3 - Message-ID:の生成の仕方

    プロジェクトのURLやチケットのIDを用い、ハッシュをとったもの+αにsmtp_fromの@以降を付与している。smtpサーバに外部のサーバを使用している場合にどうも気持ち悪い。
    smtp_fromがxxxxxxx@example.comだと、
     Message-ID: <071.56dd0b824d3625a8ce2b492402b465db@example.com>
    となる。
     Message-ID: <071.56dd0b824d3625a8ce2b492402b465db@tracサーバのFQDN>
    としたい。
    • trac.iniの[notification]セクションで、use_fqdn4message_id=trueに設定した場合、Message-ID:の@以降をtracをホストしているサーバのFQDNにする。
    • falseか未設定なら従来通りsmtp_fromの@以降。

    今回のパッチ

    --- trac-0.9.5-ja-1/trac/Notify.py 2006-02-16 10:47:21.000000000 +0900
    +++ trac-0.9.5-ja-1kai/trac/Notify.py 2006-06-25 00:07:56.000000000 +0900
    @@ -20,9 +20,12 @@
     from trac.web.clearsilver import HDFWrapper
     from trac.web.main import populate_hdf
     
    +import re
    +import socket
     import md5
     import time
     import smtplib
    +from email.Header import Header
     
     
     class Notify:
    @@ -74,6 +77,8 @@
         server = None
         email_map = None
         template_name = None
    +    reporter_name = ''
    +    reporter_email = ''
     
         def __init__(self, env):
             Notify.__init__(self, env)
    @@ -117,6 +122,17 @@
             if self.user_name:
                 self.server.login(self.user_name, self.password)
     
    +    def modifyNotification(self , authname , reporter_name_ , reporter_email):
    +        if self.env.config.getbool('notification', 'use_reporter_name'):
    +            if reporter_name_:
    +                self.reporter_name = reporter_name_
    +            elif authname:
    +                self.reporter_name = authname
    +
    +        if self.env.config.getbool('notification', 'use_reporter_email'):
    +            if reporter_email:
    +                self.reporter_email = reporter_email
    +
         def send(self, rcpt, mime_headers={}):
             from email.MIMEMultipart import MIMEMultipart
             from email.MIMEText import MIMEText
    @@ -132,7 +148,18 @@
             msg['X-Trac-Project'] =  projname
             msg['X-URL'] =  self.config.get('project','url')
             msg['Subject'] = Header(self.subject, 'utf-8')
    -        msg['From'] = '%s <%s>' % (projname, self.from_email)
    +
    +        display_name = projname
    +        from_email_ = self.from_email
    +        if self.env.config.getbool('notification', 'use_reporter_name') and \
    +            self.reporter_name:
    +            display_name = self.reporter_name
    +
    +        if self.env.config.getbool('notification', 'use_reporter_email') and \
    +            self.reporter_email:
    +            from_email_ = self.reporter_email
    +        
    +        msg['From'] = '%s <%s>' % (Header(display_name, 'utf-8'), from_email_)
             msg['Sender'] = self.from_email
             msg['Reply-To'] = self.replyto_email
             msg['To'] = rcpt
    @@ -301,6 +328,11 @@
             emails = []
             for recipient in recipients:
                 if recipient.find('@') >= 0:
    +                p = re.compile('([^<]*)\s*(<.+>)')
    +                m = p.match(recipient)
    +                if m:
    +                    recipient = '%s %s' % \
    +                        (Header(m.group(1), 'utf-8'), m.group(2))
                     emails.append(recipient)
                 elif self.email_map.has_key(recipient):
                     emails.append(self.email_map[recipient])
    @@ -312,12 +344,20 @@
                     result.append(e)
             return result
     
    +    def get_message_id_host(self):
    +        if self.config.getbool('notification', 'use_fqdn4message_id'):
    +            fqdn = socket.getfqdn()
    +            if fqdn:
    +                return fqdn
    +
    +        return self.from_email[self.from_email.find('@') + 1:]
    +
         def get_message_id(self, rcpt, modtime=0):
             """Generate a predictable, but sufficiently unique message ID."""
             s = '%s.%08d.%d.%s' % (self.config.get('project', 'url'),
                                    int(self.ticket.id), modtime, rcpt)
             dig = md5.new(s).hexdigest()
    -        host = self.from_email[self.from_email.find('@') + 1:]
    +        host = self.get_message_id_host()
             msgid = '<%03d.%s@%s>' % (len(s), dig, host)
             return msgid
     
    url: archives/2006/06/trac-060625.html --- trac-0.9.5-ja-1/trac/ticket/web_ui.py 2006-02-16 10:59:33.000000000 +0900
    +++ trac-0.9.5-ja-1kai/trac/ticket/web_ui.py 2006-06-24 22:28:52.000000000 +0900
    @@ -139,6 +139,8 @@
             # Notify
             try:
                 tn = TicketNotifyEmail(self.env)
    +            tn.modifyNotification(req.authname, \
    +                req.session.get('name', None), req.session.get('email', None))
                 tn.notify(ticket, newticket=True)
             except Exception, e:
                 self.log.exception("Failure sending notification on creation of "
    @@ -347,6 +349,8 @@
     
             try:
                 tn = TicketNotifyEmail(self.env)
    +            tn.modifyNotification(req.authname, \
    +                req.session.get('name', None), req.session.get('email', None))
                 tn.notify(ticket, newticket=False, modtime=now)
             except Exception, e:
                 self.log.exception("Failure sending notification on change to "

    設定例

    [notification]
    always_notify_owner = true
    smtp_always_cc =
    smtp_password =
    smtp_enabled = true
    smtp_replyto = xxxxxxx@example.com
    smtp_port = 25
    always_notify_reporter = true
    smtp_server = SMTPサーバ
    smtp_from = xxxxxxx@example.com
    smtp_user =
    use_reporter_name = true
    use_reporter_email = true
    use_fqdn4message_id = true
    あとは、SubjectかどこかでチケットのStatusがわかるといいんだけど、と思っているがとりあえずここまで。