Emacs
hipchat
xmpp
Jabber

これは、emacs を hipchat クライアント化するための設定方法の説明ドキュメントです。

hipchat はブラウザや専用クライアントを使って利用するのが一般的ですが、emacs フリークであれば hipchat も emacs でやりたくなるのが信条というものでしょう。

ただ、jabber のオーバーヘッドが大きいのか、メンバーの presence が変ったりすと emacs の反応が鈍くなります。メンバーが多い場合は hipchat 用に別プロセスの emacs を立ち上げた方が無難です。

忙しい人はこのセクションを飛して、「まとめ」 (See section 4)に進んでください。

hipchat

hipchat は XMPP をベースとしたチャットサービスです。

emacs には XMPP を扱うためのライブラリ emacs-jabber (jabber.el) が開発されています。

http://emacs-jabber.sourceforge.net/

最新は github で開発されていますが、github のものは一部の起動が動作しなかったためsourceforge 上の 0.8.92 の利用をオススメします。

最低限の設定

emacs-jabber で hipchat に接続する最低限の設定は次の通りです。

(setq jabber-account-list
      '(("1_1@hipchat.com" ;; <-- hipchat の JID
         (:network-server . "chat.hipchat.com")  ;; <-- hipchat のサーバ
         (:connection-type . ssl))))

あとは M-x jabber-connect-all で OK。

なお hipchat の JID は、ブラウザ等のクライアントから hipchat にログインし、hipchat account setting → 左下の XMPP/jabber info から確認できます。

デフォルト設定の使い勝手改善

上記設定だけだと次の問題があります。

  • チャットが折り返されて読み難い。
  • presence (away, online 等) のメッセージが mini-buffer に出て邪魔。
  • smiley が見慣れない
  • hipchat 接続時に、room チャットが常に「新規メッセージあり」状態になる。
  • offline 時の private チャットのログが残らない。
  • 新しい ROOM でチャットするときの ROOM の JID が分からない。

以降は、これらの問題に対応する設定方法について説明します。

チャットの折り返しを抑制

jabber は、デフォルト状態だとチャットが折り返されます。文の中途半端な箇所で折り返されるのは読み難いので、これを次の設定で折り返しを抑制します。

(setq jabber-chat-fill-long-lines nil)

presence (away, online 等) のメッセージ抑制

XMPP は、ユーザ状態(away, online 等)を presence として管理します。そして emacs-jabber のデフォルト設定だと、その状態に変化があったときに mini-buffer に通知します。

この通知が結構邪魔なので、次の設定でメッセージを抑制します。

(remove-hook 'jabber-alert-presence-hooks 'jabber-presence-echo)

smiley をアイコンで表示

英語圏のチャットでは smiley (こういうやつ :-D) を使って、日本の顔文字的な表現をします。

自分には smiley は見慣れないので、 smiley をアイコン表示させます。

(require 'autosmiley)
(add-hook 'jabber-chat-mode-hook 'autosmiley-mode)

この設定は、一部の smiley のみに対応しています。

hipchat は smiley 以外に thumbs up 等のアイコンにも対応していますが、それらはアイコン表示できません。

room チャットが常に「新規メッセージあり」状態になる。

hipchat は、サーバ接続時に room チャットの履歴を新規メッセージとして送ってきます。

これにより、 emacs-jabber は room チャットの履歴を新規メッセージとして判断してしまいます。これだと、本来の新規メッセージと、履歴のメッセージとが区別が付かないので、接続時に送られてくるメッセージを新規メッセージとして処理しないようにします。

(defun jabber-hipchat-jabber-activity-show-p (JID)
  nil
  )

(defadvice jabber-connect-all (around jabber-hipchat activate)
  ;; hipchat は、 connect 時に ROOM のメッセージ履歴が送られてくる。
  ;; メッセージ履歴は activity として表示しないように jabber-activity-show-p をセットする
  (setq jabber-activity-show-p 'jabber-hipchat-jabber-activity-show-p)
  ad-do-it
  (run-at-time 60 nil
               (lambda ()
                 (setq jabber-activity-show-p 'jabber-activity-show-p-default)
                 )))

jabber-emacs では、メッセージを受信した時に jabber-activity-show-p に登録されている関数を実行し、そのメッセージを新規メッセージとして通知するかどうかを判断します。

上記処理は、jabber-activity-show-p をダミー関数で上書きし、新規メッセージとしての通知を抑制するものです。一定時間 (60秒) 経過後に jabber-activity-show-p をデフォルトの関数に戻しています。

この処理はイマイチですが、とりあえず目的の動作をしてくれます。

offline 時の private チャットのログ取得

emacs-jabber は private チャットのログをファイルに記録する機能があります。

ログ機能は次の設定で有効になります。

(setq
  jabber-history-enabled t
  jabber-use-global-history nil
  jabber-backlog-number 100
  jabber-backlog-days 30
 )

しかしこのログは、online 状態のログであって offline 時のログは残りません。

hipchat は、offline のチャットを notification で email 通知します。email で通知されるので offline 時のチャット内容は分かるのですが、ログにないと会話の流れが見えづらくなってしまいます。

そこで、 emacs-jabber のログ機能を少し拡張します。

hipchat REST API

hipchat は XMPP ベースのチャットサービスですが、REST API にも対応しています。

この REST API にアクセスすることで、ユーザ情報やチャット情報にアクセスできます。

REST API を利用するには、次のドキュメントを見れば分かります。

https://www.hipchat.com/docs/apiv2

ですが少しだけ説明すると、hipchat の REST API は Web サービスに良くある ACCESS TOKEN をヘッダに付加けて規定の JSON コマンドを送信することで所望の操作を行なうものです。

ヘッダは次のヘッダを付けます。

Authorization: Bearer <TOKEN>

<TOKEN> 部分に、 ACCESS TOKEN を指定します。

なお hipchat は、REST API の種類ごとに scope と呼ばれる定義があり、scope ごとに ACCESS TOKEN を発行します。あるいは、一つの ACCES TOKEN に複数の scope を割り当てることもできます。

ACCESS TOKEN の取得

ACCESS TOKEN の取得方法は次の 2 つあります。
- ID/PASS から ACCESS TOKEN 自体を取得する Web API を実行する方法
- 別途専用クライアントからログインして ACCESS TOKEN を発行する方法

ここでは、処理を簡単にするために専用クライアントでACCESS TOKEN を発行する方法を使います。

専用クライアントで ACCESS TOKEN を発行するには、hipchat account setting → 左下の API access から操作できます。

private チャットのログを取得するために、view message 用の ACCESS TOKEN を発行します。

ここで取得した ACCESS TOKEN を emacs にします。

(setq jabber-hipchat-view-mess-token "<TOKEN>")

なお、 この方法で発行した ACCES SOKEN は一定期間(1年?)で expire します。expire した場合は再発行が必要です。

private チャットログ取得

REST API で private チャットログ取得するには、次の PATH にアクセスします。

/v2/user/%d/history

ここで %d は、ユーザ ID です。

jabber-emacs のログ取得処理を修正

jabber-emacs のログ取得処理は jabber-history-query で行なっています。

この処理を上書きして、 REST API から取得するように修正します。

(defun jabber-hipchat-get-history-json (buffer id)
  (call-process "curl" nil (list buffer nil) nil
                (format "https://%s/v2/user/%s/history"
                        (cdr (assoc :network-server (car jabber-account-list))) id)
                "--proxy" "" "-H"
                (concat "Authorization: Bearer " jabber-hipchat-token-view-mess))
  )

(defun jabber-hipchat-hist-2-jabber-log (item jid)
  (let ((my-jid (jabber-hipchat-my-jid))
        message date who)
    (if (assoc 'file item)
        (setq message (concat "File uploaded: "
                              (cdr (assoc 'url (cdr (assoc 'file item))))))
      (setq message (cdr (assoc 'message item))))
    (setq date (cdr (assoc 'date item)))
    (string-match "\\..+" date)
    (setq date (replace-match "" t nil date))
    (setq who (cdr (assoc 'id (cdr (assoc 'from item)))))
    (vector (concat date "Z")
            (if (equal who (jabber-hipchat-jid-2-user-id my-jid))
                "out" "in")
            "me" jid message )
    )
  )
(defun jabber-hipchat-get-history (jid buffer)
  (let ((id (jabber-hipchat-jid-2-user-id jid))
        json item links)
    (with-temp-buffer
      (jabber-hipchat-get-history-json (current-buffer) id)
      (setq json (json-read-from-string (buffer-string))))
    (setq item (cdr (assoc 'items json)))
    (with-current-buffer buffer
      (mapcar (lambda (X) (jabber-hipchat-hist-2-jabber-log X jid)) item))
    ))


(defadvice jabber-history-query (around jabber-hipchat activate)
  (let (id jid)
    (with-temp-buffer
      (string-match ".*/\\([0-9]+_[0-9]+@.*\\)$" history-file)
      (setq jid (replace-match "\\1" t nil history-file))
      (setq ad-return-value
            (jabber-hipchat-get-history jid (current-buffer)))
      )))

新しい ROOM でチャットするときの ROOM の JID が分からない。

jabber-emacs では、ROOM の JID が分からないようです。

チャットには ROOM の JID が必要なので、別途 JID を取得する必要があります。

M-x jabber-hipchat-start-room-chat することで、REST API から取得した ROOM の JID を補完リストから入力してチャットを開始できます。

github

上記の拡張処理は jabber-hipchat.el として github で公開してます。

https://github.com/ifritJP/jabber-hipchat

まとめ

まとめると、次の設定を行なうことで emacs から hipchat にアクセスできます。

(require 'jabber-hipchat)

(setq jabber-account-list
      '(("1_1@hipchat.com" ;; <-- hipchat の JID
         (:network-server . "hipchat.com")  ;; <-- hipchat のサーバ
         (:connection-type . ssl))))

(setq jabber-hipchat-token-view-mess "<TOKEN>") ;; <-- ACCESS TOKEN
(setq jabber-hipchat-token-view-room "<TOKEN>") ;; <-- ACCESS TOKEN
(setq jabber-hipchat-token-view-group "<TOKEN>") ;; <-- ACCESS TOKEN


(setq
  jabber-history-enabled t
  jabber-use-global-history nil
  jabber-backlog-number 100
  jabber-backlog-days 30
 )

;; チャットを折り返ししない
(setq jabber-chat-fill-long-lines nil)

(require 'autosmiley)
(add-hook 'jabber-chat-mode-hook 'autosmiley-mode)

(remove-hook 'jabber-alert-presence-hooks 'jabber-presence-echo)

トラブルシュート

  • private chat のログが表示されない, ROOM チャットの JID が表示されない

    • REST API が失敗している可能性があります。
    • 次を確認してください。

      • 指定した ACCESS TOKEN が間違っている
        • emacs に設定した TOKEN を確認してください。
      • ACCESS TOKEN の scope が間違っている
        • ACCESS TOKEN の scope を確認してください。
      • proxy 設定をしていない
      • サーバ証明書チェックでエラーしている

        • 証明書を更新する。
        • (setq jabber-hipchat-ignore-cert-p t) で証明書チェックを無視する。