チャットワーク」タグアーカイブ

GoogleChrome拡張でチャットワークを使いやすくする

概要

業務でもプライベートでも、GoogleChromeでチャットワークを使用しているが、微妙に使いづらい部分があったり、使わない機能のUIが気になったりしていたので、Chromeの拡張機能でどうにか改善できないかと調査し、改善したお話。

標準の問題点

  • 何か全体的にUIの主張がデカイ
  • メッセージ記法の入力が面倒というか忘れる
  • タスク機能はメモレベルでしか使ってないので不要なUIが多い

Chrome拡張の導入

Chatwork Extension

Chatwork Extensionは、Chatwork全体をいい感じに拡張してくれるが、拡張内容のオン/オフの切替や、任意のスクリプト/スタイルの適用を行えるので実質ユーザの望むままのカスタマイズを行うことができる。

導入後のデフォルト設定だけで、主に以下のような拡張が行われる

ルーム一覧のスマート表示

ルーム一覧の縦幅が狭まりスマートに表示され、検索窓を用いてルームを検索することができる。

コードのシンタックスハイライト化

コードにシンプルな装飾をしてくれるので、チャットワークでコードを共有するレベルでなら充分。

@でToの入力をアシスト

Twitterに近い感覚で、@を入力すると対象ユーザの一覧が表示され、To指定を容易に行えるようになるので、マウスレスな宛先指定ができるようになる。

その他の機能

その他の機能についてはこちらを参照

カスタムスタイル

Chatwork Extensionでは、拡張のオプションから、任意のスタイルを設定することができる。

現状、チャットワークのタスク機能はメモ程度にしか使っていないので、タスク機能周りUIを始めとした、不要な要素を片っ端から非表示にするスタイルを適用した。適用したスタイルは以下の通り

/* チャットルームごとのタスク数を非表示 */
li.roomListBadges__taskBadge {
  display: none !important;
}

/* ヘッダーメニューのタスク一覧ボタンを非表示 */
li#_openTaskWindow {
  display: none !important;
}

/* ヘッダーメニューのチャットワークアシスタントボタンを非表示 */
div.globalHeaderAssistant {
  display: none !important;
}

/* チャット一覧のメニューを非表示 */
div#_chatFilterMenu {
  height: 25px !important;
}
div#_chatFilterMenu > div {
  display: none !important;
}
div#_chatFilterMenu > ul {
  display: none !important;
}
div#_chatFilterMenu > input  {
  width: 97% !important;
  position: static !important;
}

/*タスクの期限を非表示に*/
ul#_subRoomTaskList .taskListItem__meta {
  display: none !important;
}

/* 自分のタスクのみ表示 を非表示 */
div#_subRoomMyTaskList {
  display: none !important;
}

適用した結果、チャット一覧はよりスマートに

タスク一覧はただのメモとして表示されるようになった

ChatWork Quick Info Input

Chatwork Extensionとは別にもう一種、ChatWork Quick Info Inputも導入した。

こちらは地味ながらかなり大きな役割をもっており、入力ウィンドウに、title/info/codeのメッセージ記法を入力するためのUIを追加してくれる。

所感

  • チャットワーク用のChrome拡張は本記事で紹介した2種類の他にも様々ある。個々人のチャットワークの使い方や拘りに応じて柔軟にカスタマイズすると良さそう
  • Chatwork Extensionは、任意のスクリプトを仕込むこともできるので、やりようによっては凄いことできそう
  • Slackはもっとカスタマイズがしやすいとの噂が気になる

Python3 + Selenium + Firefox でチャットワークの表示名を自動で切り替える

前提

以下の環境で実装、動作確認済み

要素 バージョン
debian 8.6
python 3.6.2

概要

非常にニッチな目的に感じる題だが、以下の流れでこうなった

  • 諸事情でチャットワークの表示名を自動で変更する手段を作りたい
  • チャットワークAPIを使えばいけそう
  • ユーザ情報を変更するAPIが存在しなかった(見落としているだけであったら教えてください)
  • 仕方ないのでスクレイピングで強引に変更しよう
  • チャットワークにログインする必要もあるのでSeleniumを使うのが良いのかな
  • せっかくだから最近勉強してるPython3を使おう

以上より、Seleniumを用いてFirefoxを自動操作し、チャットワークにログイン、表示名を任意のに変更するスクリプトを実装する。

なお、今回はGUIを持たない環境で実装するので、Xvfbを用いた仮想画面を使って動かす(この辺理解が怪しい)

下準備

まずFirefoxを入れる。画面無くて良いならPhantomJSとかヘッドレスブラウザ使えばいいじゃんと思い最初はそうしたのだが、どうしてもチャットワークが正しく表示できなかったので安定を取ってFirefoxにした。その分容量が大きいけど仕方ない。

$ apt-get install firefox

Firefoxのレンダリングエンジンであるgeckoをseleniumから利用するためのドライバーが別途必要。
こちらからダウンロードできるので、OSの種類に応じてダウンロードする。
今回は64ビット版のLinuxなので以下のように。パスが通ってるところで展開したほうが後々楽。

$ cd /usr/local/bin
$ wget https://github.com/mozilla/geckodriver/releases/download/v0.18.0/geckodriver-v0.18.0-linux64.tar.gz
$ tar -zxvf geckodriver-v0.18.0-linux64.tar.gz
$ rm geckodriver-v0.18.0-linux64.tar.gz

仮想モニタが必要になるのでインストール

$ apt-get install xvfb

必要なPythonモジュールをpipでインストール

$ pip install selenium
$ pip install PyVirtualDisplay

ログイン情報を定義

チャットワークにログインするためのメールアドレスをパスワードが必要になるが、ソースコード中にハードコーディングしてしまうとコードの共有ができなくなってしまうので、今回はJSONファイルに別途書き出すようにする。

以下のようなsecret.jsonを作成した。

{
  "email":    "hogehoge@fugafuga.com",
  "password": "qawsedrftgyhujikol"
}

実装

今回は一つのファイルで全て完結させるので、前項で作成したsecret.jsonと同じフォルダに、main.pyを作成する。

モジュールインポート

今回使うモジュールは以下の通りなのでimportする。

import time
import json
import sys
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By
from pyvirtualdisplay import Display

ログイン情報を取得する

secret.jsonを開いて、jsonモジュールでデシリアライズ。メールアドレスとパスワードを抜き出す

with open('secret.json', 'r') as file:
  secret   = json.load(file)
  email    = secret['email']
  password = secret['password']

Seleniumの初期設定

仮想モニタを立ち上げ、SeleniumDriverをFirefoxで初期化。画面サイズがやたらと大きいのはデバッグ時のスクリーンショットを見やすくするためなので最終的に意味はない。

display = Display(visible=0, size=(1920, 1080))
display.start()
driver = webdriver.Firefox()

チャットワークへのアクセス

SeleniumDriverのgetメソッドで簡単にWebサイトにアクセスできる。

driver.get('https://www.chatwork.com/login.php?lang=ja&args=')

メールアドレス/パスワードを入力

find_element_by_css_selectorメソッドで、CSSセレクターを用いてDOMを取得し、それに対してsend_keysメソッドを用いてキー入力を行うことができる。JSONファイルから取得しておいたメールアドレス/パスワードを入力する。今後他にも出てくるけど、SeleniumDriverって何かとメソッド名が冗長だなと思った。

driver.find_element_by_css_selector('form[name="login"] input[name="email"]').send_keys(email)
driver.find_element_by_css_selector('form[name="login"] input[name="password"]').send_keys(password)

ログインする

同じくfind_element_by_css_selectorメソッドを用いてログインボタンを取得し、clickメソッドでクリックすることでログインする。

driver.find_element_by_css_selector('form[name="login"] input[name="login"]').click()

ログイン処理には時間がかかる上、チャットワークはJavaScriptで画面を構築していくので、構築の完了まで待機する必要がある。
Seleniumでは、以下のようにWebDriverWaitを用いることで、「特定の何かが確認できるまで待機する」という記述を行うことができる。
以下では、IDが_myStatusNameのDOMが確認できるまで待機するという処理(10秒でタイムアウト)

wait = WebDriverWait(driver, 10)
wait.until(expected_conditions.visibility_of_element_located((By.ID, "_myStatusName")))

_myStatusNameは、この部分。これが表示されたら読み込み完了と捉える。

読み込み完了でようやくチャットワークの画面に到達する。

プロフィール画面を開く

ヘッダのプルダウンメニューを開いて、編集メニューをクリックしてプロフィール編集画面を表示する。
かなりまどろっこしい事してる気がする。可能であれば最初からプロフィール編集画面を表示するためのJavaScriptコードを実行させれば一発だと思うが、Seleniumの練習も含めて地道に行う。

0.5秒のスリープを毎回挟んでるのは、UIのアニメーションがあるのでスグに次のUIを操作できないため。これもっと良い方法あったら教えて下さい。

driver.find_element_by_css_selector('#_myStatusName').click()
time.sleep(0.5)
driver.find_element_by_css_selector('#_myProfile .myAccountMenu__anchor').click()
time.sleep(0.5)

表示名を編集する

編集ボタンをクリックし、テキストボックスの内容を書き換え、保存ボタンをクリックする。
変更後の表示名は、コマンドライン引数から取ることを想定し、sys.argvを参照する。

driver.find_element_by_css_selector('#_profileContent ._profileEdit').click()
time.sleep(0.5)
driver.find_element_by_id('_profileInputName').clear()
driver.find_element_by_id('_profileInputName').send_keys(sys.argv[1])
driver.find_element_by_css_selector("div[aria-label='保存する']").click()

実行

以下コマンドで実装できる。ネットワーク環境の影響を大きく受けるが、手元の環境で実行時間は10秒程度。APIがあれば1秒で終わるというのに。

$ python main.py "ふー ばーのすけ"

最新のソースコードはこちら

所感

  • Seleniumの入門としてはえらくニッチな使い方になったが、個人的にやりたかったことが実現できたので良かった
  • 全体的冗長なコードになりやすそう。ラッパーライブラリが多いのも頷ける
  • JavaScriptで動いてるページをうまく制御したければ関連ライブラリ/フレームワークを使うべきか
  • このやり方だとチャットワーク側がちょいとHTML変更したら動かなくなったりするのでやっぱりAPIが欲しい

Backlog + Chatwork + Sinatraで課題の更新通知を行う

前提

以下の環境で実装、動作確認

要素 バージョン
debian 8.6
ruby 2.2.2
sinatra 1.4.7

概要

BacklogのWebhook機能を用いて、Sinatraで実装したWebアプリケーションを経由し、チャットワークにリアルタイム更新通知を行ったお話。

メール通知より圧倒的に早いリアルタイム通知による、プロジェクト管理の効率化ができる可能性を探る。

今回は試験的実装のため、課題作成時に課題の作成者、タイトル、URLをチャットワークのマイチャットに投稿するだけの簡単なシステムを実装した。

なお、今回は非常に軽量なシステムとなるので、Rubyの軽量WebフレームワークであるSinatraを採用しているが、Sinatraについては本記事から脱線するので詳しい解説は割愛する。

Webhookとは

端的に言えばアプリケーションで何らかの更新アクションが発生した時、その内容を特定のURLに対してPOSTしてくれる機能。
Backlogの場合、課題が追加された、更新された、削除されたなどの情報を、指定したURLに対してJSONでPOSTさせることができる。
技術とかツールでなく、ただの機能の名称なので注意。

参考 Webhookとは? on @Qiita

Webhookの設定を行う

BacklogでのWebhookの設定はプロジェクト単位で行う。
プロジェクト設定に、Webhookの項目があるので、そこから設定する。

Webhook名と説明は適当に入力し、WebHookURLに更新通知をPOSTさせたいURLを入力する。当然、インターネットからアクセスできる場所でないといけないので、ローカル開発環境などを指定しても動作しない。

「通知するイベント」は、Backlogのどの更新アクションをWebhookで通知するかを指定できる。今回は課題の追加時にチャットワークで通知したいので、「課題の追加」にのみチェックを入れる

Webhookの受け口作成(Sinatra)

本記事では、Webhookの受け口に、Rubyの軽量WebアプリケーションフレームワークであるSinatraを用いる。といっても、POSTされたデータをチャットワークに流すだけなので、フレームワークも不要なレベルだが、導入が簡単なので今回はSinatraを用いることにした。

なお、Sinatra環境の構築には、手前味噌だが以下を用いた。
Sa2Knight/Sinatra-Skeleton: Sinatraアプリケーションを最短で構築する個人用リポジトリ

Backlogの更新内容を出力

Sinatraの詳細的な説明は割愛するが、以下がWebhookの受け口となるエンドポイントの実装である

require 'sinatra/base'
require 'json'
require 'pp'

class App < Sinatra::Base

  post '/' do
    pp JSON.parse request.body.read
    return true
  end

end

BacklogのWebhookでは、指定したURLに対して、リクエストボディがJSONのPOSTリクエストが飛んで来る。
上記コードでは、それを受け取ってJSONをparseし、標準出力している。

サーバを立ち上げてから、以下のような課題を作成すると

WebhookによってJSONがPOSTされ、以下のような標準出力が得られる

{"created"=>"2017-08-15T13:44:56Z",
 "project"=>
  {"archived"=>false,
   "projectKey"=>"DEV",
   "name"=>"個人開発",
   "id"=>38382,
   "subtaskingEnabled"=>false},
 "id"=>19571304,
 "type"=>1,
 "content"=>
  {"summary"=>"課題タイトル",
   "key_id"=>275,
   "customFields"=>[],
   "dueDate"=>"2017-08-09",
   "description"=>"課題詳細",
   "priority"=>{"name"=>"中", "id"=>3},
   "resolution"=>{"name"=>"", "id"=>nil},
   "actualHours"=>nil,
   "issueType"=>
    {"color"=>"#7ea800",
     "name"=>"タスク",
     "displayOrder"=>0,
     "id"=>172585,
     "projectId"=>38382},
   "milestone"=>[],
   "versions"=>[],
   "parentIssueId"=>nil,
   "estimatedHours"=>nil,
   "id"=>2889963,
   "assignee"=>
    {"name"=>"sa2knight",
     "id"=>85748,
     "roleType"=>255,
     "lang"=>"null",
     "userId"=>"sa2knight"},
   "category"=>[{"name"=>"開発関係", "displayOrder"=>0, "id"=>79173}],
   "startDate"=>"",
   "status"=>{"name"=>"未対応", "id"=>1}},
 "notifications"=>[],
 "createdUser"=>
  {"nulabAccount"=>nil,
   "name"=>"sa2knight",
   "mailAddress"=>nil,
   "id"=>85748,
   "roleType"=>1,
   "userId"=>nil}}

フォーマットはBacklog APIと概ね一緒なのでここでは割愛する。
BacklogAPIについては[Ruby] BacklogをCUIで操作してみる | QS-DEVSでも触れている。

通知に必要な情報のみ抜き出す

受け取ったデータのうち、今回必要なのは以下の3種類

  • 課題キー
  • 課題名
  • 課題作成者

以下のようにコードを修正して、必要な情報のみ抜き出す。

  post '/' do
    params = JSON.parse request.body.read
    @issue = {
      key:     "#{params['project']['projectKey']}-#{params['content']['key_id']}",
      summary: params['content']['summary'],
      creator: params['createdUser']['name'],
    }
    pp @issue
    return true
  end

これで出力は以下のようになる

{:id=>"DEV-137", :summary=>"課題タイトル", :creator=>"sa2knight"}

チャットワーク連携の準備

次に、チャットワーク連携用のロジックを用意する。
こちらについても手前味噌だが、RubyでチャットワークAPIを利用するクラスを以前作っていたので、それをベースにする。
Sa2Knight/chatwork-ruby: RubyでチャットワークAPI呼んで遊ぶ

チャットワークAPIについては、以下参照
チャットワークAPIをRubyで利用する | QS-DEVS

今回はAPIの認証と、メッセージの送信ができればいいので、大幅にコードを削って以下のようなクラスに仕上げた。

require 'net/http'
require 'uri'
require 'json'
require 'date'

class Chatwork

  @@API_BASE = 'https://api.chatwork.com/v2'
  @@ROOM_ID  = 'hogehogefugafuga'

  # tokenを指定してオブジェクトを生成
  # tokenを省略した場合、環境変数を参照する
  def initialize(token = nil)
    @token = token || ENV['CHATWORKAPI']
  end

  # ルームに新規メッセージを送信
  # room_id: 対象のroomID
  # body:    投稿する本文
  def sendMessage(body)
    url = '/rooms/' + @@ROOM_ID + '/messages'
    res = createHttpObject(url, :post, {:body => body})
    return res.body ? JSON.parse(res.body) : []
  end

  private
    # HTTPリクエストを送信する
    def createHttpObject(url, method, params = {})
      api_uri = URI.parse(@@API_BASE + url)
      https = Net::HTTP.new(api_uri.host, api_uri.port)
      https.use_ssl = true
      api_uri.query = URI.encode_www_form(params) if method == :get
      req = createRequestObject(method, api_uri)
      req["X-ChatWorkToken"] = @token
      req.set_form_data(params) unless method == :get
      https.request(req)
    end
    # リクエストオブジェクトを生成する
    def createRequestObject(method, uri)
      case method
        when :get
          return Net::HTTP::Get.new(uri.request_uri)
        when :post
          return Net::HTTP::Post.new(uri.request_uri)
        when :put
          return Net::HTTP::Put.new(uri.request_uri)
        when :delete
          return Net::HTTP::Delete.new(uri.request_uri)
      end
    end
end

環境変数”CHATWORKAPI”にAPIキーを設定し、このクラスのインスタンスを生成後、sendMessageメソッドを呼び出せばチャットワークにメッセージを送信できる。

なお、今回はメッセージの送信先として、マイチャットを指定している。

課題の更新を通知する

前項で実装したChatworkクラスを用いて、Webhookで送られた更新情報をチャットワークに通知する。
Sinatraの使い方としてはかなりお行儀が悪いが、プライベートメソッドmakeChatworkMessageを実装し、チャットワークに送信するメッセージを作成、それをチャットワークに送信するようにした。

require 'sinatra/base'
require 'json'
require 'pp'
require_relative 'chatwork'

class App < Sinatra::Base

  post '/' do
    params = JSON.parse request.body.read
    issue = {
      key:     "#{params['project']['projectKey']}-#{params['content']['key_id']}",
      summary: params['content']['summary'],
      creator: params['createdUser']['name'],
    }
    Chatwork.new.sendMessage(makeChatworkMessage(issue))
    return true
  end

  private
  def makeChatworkMessage(issue)
    message =  "[info][title]#{issue[:creator]}さんが課題を作成しました[/title]"
    message += "[#{issue[:key]}] #{issue[:summary]}\n"
    message += "https://saknight.backlog.jp/view/#{issue[:key]}[/info]"
  end

end

動作確認

サーバを起動し、課題の作成を行うと、チャットワークのマイチャットにリアルタイムで通知が届くことが確認できた。

所感

  • 初めてWebhookを使ったが、かなりリアルタイムに近い。APIでpull型で取得するよりもコードもスマートに書けて良い
  • メール通知より遥かに確実に、高速に通知できるので使いみちはあると思う
  • BacklogWebhookの仕様上、インターネットで繋がる場所にサーバを建てる必要があるため、セキュリティにちょっと気をつける必要ありそう
  • 以下のような工夫ができる余地がある
    — プロジェクトごとに通知するチャットルームを切り替える
    — 担当者に対応するユーザに対してToを付ける
    — 課題のクローズ時にも作成者にToを付けて通知する

チャットワークAPIをRubyで利用する

前提

以下の環境で実装及び動作確認を行った

要素 バージョン
debian 8.6
ruby 2.2.2

概要

チャットワークのAPIを使える状態にし、Rubyからチャットワークの各種操作を行う。今回の成果物についてはGithubから閲覧、ダウンロード可能。

チャットワークとは

ビジネス向けのチャットツールで以下の特徴がある

  • 基本無料
  • ルームを個別に作成し、複数人でチャット可能
  • ルームごとにタスクの管理が可能
  • ファイルの共有が可能
  • 音声通話/ビデオ通話も可能

チャットワークAPIとは

チャットワークの基本的な機能をプログラムから利用するためのRestFullなAPI。
現在(2017/05/20)は、プレビュー版のみの限定公開となっており、APIを利用するためには申請する必要がある

チャットワークAPIの利用準備

チャットワークAPIの利用申請

チャットワークにログインしているブラウザで、利用申請ページにアクセスし、APIの利用申請を行う。利用が可能な状態になると、チャットワークで登録しているメールアドレスに、以下のような通知メールが届くのでそれまで待機(1営業日程度?)

いつもお世話になっております。
チャットワーク サポートデスクです。

チャットワークAPI(プレビュー版)のご利用が可能となりましたので
お知らせいたします。

APIトークンの確認

メールが届いたら、ブラウザでチャットワークを開き、「動作設定」を開くと、以下のようにAPI発行メニューが追加されていることが確認できる。

パスワードを入力することで、以下のようにAPIトークンを確認することができる(画像のトークンはダミー)
トークンはこの画面で更新可能なので、必要に応じて更新する。

APIトークンはプログラムでAPIを利用するのに必須のため、環境変数に保存しておく。以下のコマンドをbashrcに追記するのがベスト

$ export CHATWORKAPI=hogehogehogehogehogehogehogehogehoge

APIの仕様確認

APIドキュメントを確認すれば、APIのシンプルな仕様を確認できるが、ここでは以下さえ抑えておけば問題ない

  • APIのエンドポイントは https://api.chatwork.com/v2
  • リクエストは必ずHTTPSを用いる
  • リクエストヘッダに”X-ChatWorkToken”というキーでAPIトークンを指定する必要がある
  • APIの制限は5分で100回程度
  • レスポンスは必ずJSONで返却される
  • ただし、APIの成否判定の多くはレスポンスヘッダに含まれている

APIの動作確認

さっそくプログラムを作っても良いが、API及びAPIトークンが正しく機能しているかを確認するため、curlコマンドで試しにAPIを呼び出してみる。

$ curl https://api.chatwork.com/v2/me GET -H "X-ChatWorkToken: hogehoge"

上記curlコマンドは、以下のHTTPリクエストを送信する

  • リクエスト先は https://api.chatwork.com/v2/me (自身のステータスを取得するAPI)
  • メソッドにGETを用いる
  • リクエストヘッダに”X-ChatWorkToken: hogehoge”を含める

すると、以下のレスポンスが返却される

{"account_id":2091543,"room_id":59255776,"name":"笹木信吾","chatwork_id":"","organization_id":1118988,"organization_name":"","department":"","title":"","url":"https://github.com/Sa2Knight","introduction":"","mail":"sasaki@91932.com","tel_organization":"","tel_extension":"","tel_mobile":"090-7067-7376","skype":"","facebook":"","twitter":"","avatar_image_url":"https://appdata.chatwork.com/avatar/1623/1623307.rsz.png"}

レスポンスを見る限り、無事にチャットワークAPIを用いて、自身のユーザ情報を取得できていることがわかる。

RubyからチャットワークAPIを利用する

Chatworkクラスの作成

とりあえずチャットワークAPIをRubyで叩くためのエンドポイントとなるChatworkクラスを作成する。

require 'net/http'
require 'uri'
require 'json'
require 'date'

class Chatwork
  @@API_BASE = 'https://api.chatwork.com/v2'

  # tokenを指定してオブジェクトを生成
  # tokenを省略した場合、環境変数を参照する
  def initialize(token = nil)
    @token = token || ENV['CHATWORKAPI']
  end

end

例によって、APIトークンを指定してオブジェクトを生成する。トークンの指定を省略した場合に、環境変数”CHATWORKAPI”を参照するようにしてあるので、今回はそれを前提にする。APIのエンドポイントは全てのAPIで共通なので、クラス定数@@API_BASEで定義しておく。

HTTP通信するメソッドの実装

どのAPIでも共通となる、HTTPリクエストを投げてレスポンスを戻すメソッドを作成する。

createRequestObjectは、そのヘルパーメソッドで、指定したHTTPメソッド(:get,:post,:put,:delete)に応じた、空のHTTPRequestオブジェクトを返却する。

createHttpObject、対象のURL,HTTPメソッド,パラメータを指定すると、以下のことをしてくれる。
1. APIのエンドポイントと各APIのURLを結合し、URIオブジェクトを生成する
2. URIを元にHTTPオブジェクトを生成し、SSLを有効にする
3. (メソッドがGETの場合) URIにパラメータを付与する
4. メソッドに応じたRequestオブエクトを取得
5. リクエストヘッダにAPIトークンを付与
6. (メソッドがGET以外の場合) リクエストボディにパラメータを付与する
7. リクエストを送信し、レスポンスを戻す

## HTTPリクエストを送信する
def createHttpObject(url, method, params = {})
  api_uri = URI.parse(@@API_BASE + url)
  https = Net::HTTP.new(api_uri.host, api_uri.port)
  https.use_ssl = true
  api_uri.query = URI.encode_www_form(params) if method == :get
  req = createRequestObject(method, api_uri)
  req["X-ChatWorkToken"] = @token
  req.set_form_data(params) unless method == :get
  https.request(req)
end
## リクエストオブジェクトを生成する
def createRequestObject(method, uri)
  case method
    when :get
      return Net::HTTP::Get.new(uri.request_uri)
    when :post
      return Net::HTTP::Post.new(uri.request_uri)
    when :put
      return Net::HTTP::Put.new(uri.request_uri)
    when :delete
      return Net::HTTP::Delete.new(uri.request_uri)
  end
end

上記のメソッドを用いて、各APIに対応したメソッドを実装する。

各APIごとのメソッドを実装する

あとはAPIドキュメントを参考に、各種APIを呼び出すためのメソッドを順に実装する。全て実装するのは疲れるので、とりあえず以下を実装した。残りもおいおい実装する予定

メソッド名 内容
me 自身のユーザ情報を取得
myStatus 自身の未読数、未読To数、未完了タスク数を取得する
myTasks 自身のタスク一覧を取得する
myContact 自身のコンタクト一覧を取得する
myRooms 自信が参加しているチャット一覧を取得する
getRoom チャットの情報を取得する
createRoom 新しいチャットを作成する
updateRoom チャットの情報を更新する
getRoomMembers チャットに参加しているユーザ一覧を取得する
getRoomMessages チャットのメッセージ一覧を取得する
sendMessage チャットに新規メッセージを投稿する
getRoomTask チャット内のタスク一覧を取得する
getTask タスク情報を取得する
createTask タスクを新規登録する
getRoomFiles アップロードされたファイル一覧を取得する
getFile アップロードされたファイルの情報を取得する

※ 何故か既存のタスクを操作するためのAPIが無い。タスクを作ることは出来るのに完了したり編集したりすることがでない謎。

実装例

どのメソッドも基本的に実装内容は同じ(URL、メソッド、パラメータの有無などが異なるだけ)なので、全てを記載しないが、ここでは以下の2種類について記載する。

myStatus(自身の未読数、未読To数、未完了タスク数を取得する)

myStatusは特定のURLに対して、パラメータなしでシンプルにGETするだけなので、以下のように実装した

def myStatus
  url = '/my/status'
  res = createHttpObject(url, :get)
  return JSON.parse(res.body)
end

createTask(タスクを新規登録する)

createTaskは少しだけ複雑で、タスクを新規登録するルーム(チャット)IDとタスクの内容、担当者のユーザID一覧を指定して登録する。また、オプションでタスクの期限を指定することができる。

APIが要求するパラメータでは、担当者のユーザID一覧はカンマ区切りで、期限はUNIXTIMEで指定する必要がある。これはそのままだと使いづらいので、メソッド側では担当者のユーザID一覧を配列で、期限をDateTimeオブジェクトで受け取るようにしている。メソッド内でそれをAPI用に変換して実行している。

def createTask(room_id, body, to_ids = [], params = {})
  url = '/rooms/' + room_id + '/tasks'
  params[:body] = body
  params[:to_ids] = to_ids.join(',')
  params[:limit] = params[:limit].to_i if params[:limit].class == Time
  res = createHttpObject(url, :post, params)
  return res.body ? JSON.parse(res.body) : []
end

動作確認

本項では、以下の手順で動作確認する

  1. 動作確認用の新しいチャットルームを作成する(自分のみ参加)
  2. チャット内で適当に発言する
  3. チャット内にタスクを追加する
  4. チャットルームの情報を取得する
  5. チャットルームの発言一覧を取得する
  6. チャットルームのタスク一覧を取得する

なお、動作確認中に使用するユーザのIDを’2091543′,及び作成するチャットルームのIDを’76097933’とし、以下のようにChatworkオブジェクトを生成済みとする。

cw = Chatwork.new

チャットルームを作成

動作確認用に、「API動作確認ルーム」というチャットルームを作成する。参加者は、自身のみを管理者ユーザとして設定する。

cw.createRoom('API動作確認ルーム', ['2091543'], :description => '動作確認用のルームです')

実行すると、以下のように新規チャットルームが作成されたことがわかる。

チャット内で適当に発言する

「こんにちは,APIからの投稿です」という発言を、先ほど作成したルームに対して送信する

cw.sendMessage('76097933', 'こんにちは、APIからの投稿です')

実行すると、発言が送信されていることがわかる

チャット内にタスクを追加する

「WordPressを更新する」というタスクを、本日(5/21)を締め切りにして登録する

cw.createTask('76097933', 'WordPressを更新する', ['2091543'], :limit => Time.now)

実行すると、タスクが登録されたことが確認できる

チャットの情報を取得する

先程作成したチャットルームの情報を取得する。

cw.getRoom('76097933')

実行すると、チャットルームの基本的な情報を確認することができる

{"room_id"=>76097933, "name"=>"API動作確認ルーム", "type"=>"group", "role"=>"admin", "sticky"=>false, "unread_num"=>0, "mention_num"=>0, "mytask_num"=>1, "message_num"=>3, "file_num"=>0, "task_num"=>1, "icon_path"=>"https://appdata.chatwork.com/icon/ico_group.png", "description"=>"動作確認用のルームです", "last_update_time"=>1495307674}

チャットルーム内のメッセージ一覧を取得する

メッセージ一覧(と言っても先程の1件のみだが)を取得する

cw.getRoomMessages('76097933')

メッセージ一覧及び個々のメッセージの発言者の情報が取得できることがわかる

[{"message_id"=>"2020162066", "account"=>{"account_id"=>2091543, "name"=>"笹木 信吾", "avatar_image_url"=>"https://appdata.chatwork.com/avatar/1623/1623307.rsz.png"}, "body"=>"[info][title][dtext:chatroom_groupchat_created][/title][dtext:chatroom_chatname_is]API動作確認ルーム[dtext:chatroom_set]\n\n[dtext:chatroom_description_is]動作確認用のルームです[dtext:chatroom_set]\n\n[dtext:chatroom_member_is][piconname:2091543][dtext:chatroom_added][/info]", "send_time"=>1495307261, "update_time"=>0}, {"message_id"=>"2020162308", "account"=>{"account_id"=>2091543, "name"=>"笹木 信吾", "avatar_image_url"=>"https://appdata.chatwork.com/avatar/1623/1623307.rsz.png"}, "body"=>"こんにちは、APIからの投稿です", "send_time"=>1495307439, "update_time"=>0}, {"message_id"=>"2020162647", "account"=>{"account_id"=>2091543, "name"=>"笹木 信吾", "avatar_image_url"=>"https://appdata.chatwork.com/avatar/1623/1623307.rsz.png"}, "body"=>"[info][title][dtext:task_added][/title][task aid=2091543 st=open lt=1495378799]WordPressを更新する[/task][/info]", "send_time"=>1495307674, "update_time"=>0}]

チャットルーム内のタスク一覧を取得

こちらも先程登録した1件のみだが、タスクの情報を取得する

p cw.getRoomTasks('76097933')

実行すると、タスク及びそのメタ情報が確認できる

[{"task_id"=>68592616, "account"=>{"account_id"=>2091543, "name"=>"笹木 信吾", "avatar_image_url"=>"https://appdata.chatwork.com/avatar/1623/1623307.rsz.png"}, "assigned_by_account"=>{"account_id"=>2091543, "name"=>"笹木 信吾", "avatar_image_url"=>"https://appdata.chatwork.com/avatar/1623/1623307.rsz.png"}, "message_id"=>"2020162647", "body"=>"WordPressを更新する", "limit_time"=>1495378799, "status"=>"open"}]

所感

  • チャットワークのAPIはそれは見事なRestFull設計で感動を覚えるほど使いやすかった
  • だがまだプレビュー版で、既存タスクの更新ができないなど、痒いところに手が届かないので正式リリースが待たれる
  • APIを叩くライブラリを作ったはいいが、問題はこれをどう活用するか。そこは全然考えてない。おそらくサーバを監視して報告をチャットワークに流すとか、プロジェクト管理ツールの更新をチャットワークでも通知するとか、読み込みより書き込みがメインになるとは思う

参考