読者です 読者をやめる 読者になる 読者になる

GoogleAppEngine/Pythonから、HipChat API v1/v2を使ってみた

GoogleAppEngine Python

GoogleAppEngine/Python(以下、GAE/py)から、HipChat API v1/v2を使ってみた時のメモ。

 

環境

 

HipChat APIについて

今のところHipChat APIはv1/v2の2つがあります(ただし、v2はBETA)。

APIに関する公式ドキュメントは以下にありました。

 
また、APIを使うためにはtokenを事前に取得する必要があります。

 
なお、v1向けのTokenのTypeはAdminとNotificationの2つがありましたが、RoomIDを取得する都合上、Adminを使いました。RoomIDが事前にわかっていれば、Notificationで十分かもしれません。

 

ライブラリ探し

HipChat公式では、API v1/v2それぞれ向けのライブラリが紹介されています。

 
GAE/pyでライブラリを使う場合、通常、

  1. ローカルではなく、GAEアプリのlibなどのディレクトリにライブラリを入れる 用意されているのを使ったり、python setup.py buildで、buildディレクトリ以下にできたものを使ったりする

  2. 以下を参考に、appengine_config.pyを作成
    GAE/Pyでインポートのパスを追加する

を行うかと思います。

HipChatライブラリでも同じことをやってみましたが、開発環境でのセットアップがうまくできませんでした。

ただ、今回は「GAE/pyからHipChatのRoomに通知」だけをやりたかったため、ライブラリを使うのをやめて直接HipChat APIを叩くことにしました。

 
なお、Slackに対して同じようなことをやっていたコードがGitHubにあり、とても参考になりました。
albertpadin/slack-gae

 

GAE/pyからHipChat APIを使う

HipChatのRoomに通知するにはRoomIDが必要になるため、

  1. HipChatのRoomIDを取得 (API: /rooms/list, Get all rooms)
  2. RoomIDに対して通知 (API: /rooms/message, Send room notification)

という流れを試しましたが、GAE/pyのurlfetch.fetch()メソッドにより、HipChatAPI v1/v2 のどちらも同じように実行できました。

 
なお、手軽な方法で実装したため、API v1の通知者名は任意の名前でしたが、v2の通知者名は自身の名前となりました。

そのため、手軽に通知者名を使いたい場合には、API v1の方が良いかもしれません。

 
また、パラメータの説明は公式ドキュメントの他、以下が参考になりました。
執事のセバスチャンが東京ドームのイベントをHipChatで教えてくれる | feedforce Engineers' blog

 

HipChat API v1向けの通知コード
API_SEND_BASE_URL = 'https://api.hipchat.com/v1/rooms/message?format=json&auth_token={token}'
API_SEND_BASE_PARAMETER = 'room_id={roomId}&from={user}&message={message}&color={color}'
SENDER_NAME = 'GAE'

url = API_SEND_BASE_URL.format(token=token)
parameter = API_SEND_BASE_PARAMETER.format(roomId=roomId,
                                           user=SENDER_NAME,
                                           message=message,
                                           color='purple')

urlfetch.fetch(url, payload=parameter, method=urlfetch.POST)

 

HipChat API v2向けの通知コード
API_SEND_BASE_URL = 'https://api.hipchat.com/v2/room/{id_or_name}/notification?auth_token={token}'

url = API_SEND_BASE_URL.format(id_or_name=roomId,
                               token=token)
parameter = { 'color': 'yellow',
              'message': message,
              'message_format': 'text'
            }
json_body = json.dumps(parameter)

urlfetch.fetch(url, 
               payload=json_body, 
               method=urlfetch.POST, 
               headers={'Content-Type': 'application/json'})

 

GAE/pyまわりで調べたところ

HipChat APIとはあまり関係ないのですが、忘れないように残しておきます。

 

非同期実行のdeferredライブラリ

slack-gaeで使っていたdeferredライブラリが気になり、調べてみました。

deferredはGAE/pyに含まれるライブラリで、Task Queue Python APIより手軽に使えそうなことが分かりました。
Background work with the deferred library - Google App Engine — Google Developers

そこで、今回メッセージ通知を非同期で行うこともあり、初めて試してみました。

 
ただ、webapp2.RequestHandlerを継承したクラス内ではdeferred.defer()を使えなかったため、以下を参考に別ファイルに切り出して使うようにしました。
遅延ライブラリを使用したバックグラウンド作業の補足(GAE/python): 鈴木くにのブログ

 
また、deferred.defer()にself.requestの値をそのまま渡した場合、

(Can't pickle : it's not the same object as threading._RLock)

のようなエラーが発生したため、事前にself.requestのうちで必要なパラメータを取得してから、それらを渡すようにしました。

 
他に、リトライ回数を制限するため、以下を参考に webapp2.get_request().headers.get('X-AppEngine-TaskRetryCount')を使ってリトライ回数を取得しました。
python - Retry count in deferred.defer in GAE - Stack Overflow

 
リトライ回数を超過した場合は、

Raise a deferred.PermanentTaskFailure exception. This is a special exception, and is treated as a permanent failure. It is logged as an exception, so it will show up in your admin console as one, but causes the task to not be retried again. Background work with the deferred library - Dealing with task failures - Google App Engine — Google Developers

と公式ドキュメントに記載のある通り、raise deferred.PermanentTaskFailureで例外を発生させました。

 

JSONのPOST

API v2ではJSON BodyをPOSTする必要があるため、urlfetch.fetchのheadersパラメータを使いました。

この部分のコードの再掲です。

urlfetch.fetch(url, 
               payload=json_body, 
               method=urlfetch.POST, 
               headers={'Content-Type': 'application/json'})

 

テンプレートの継承

HipChat API v1/v2では、レスポンスのJSON構造が少し異なっています。

そのため、以下を参考に、{% block %}{% extends "base.html" %}を使ってテンプレートの継承を行い、HTML表示を行いました。
GAE アプリのテンプレートを DRY - present

 

Google App Engine Launcher起動時にエラー

あるとき、Google App Engine LauncherでRunしたものの、長い間起動中になってしまいました。

とりあえず強制終了したところ、次回起動時に

See the logfile 'C:\Program Files (x86)\Google\google_appengine\launcher\GoogleAppEngineLauncher.exe.log' for details

というエラーメッセージが出てLauncherが起動できなくなりました。

エラーメッセージに従いGoogleAppEngineLauncher.exe.logを探しましたが見当たりませんでした。仕方なくSDKを再インストールしたものの、同じエラーが出続けました。

 
そこで、エラーメッセージ自体を調べてみたところ、以下の記事がありました。
Google App Engine Launcherの起動エラー - RENAISSANCE

同じようにgoogle_appengine_projects.iniファイルを修正したところ、無事に起動するようになりました。ありがとうございました。

 

ソースコード

GitHubに上げてあります。
thinkAmi-sandbox/GAEHipChat-Sample