kintone アプリのデータを Cisco Webex Messaging に投稿する方法

目次

caution
警告

記事内で利用しているライブラリ「 request (External link) 」「 request-promise (External link) 」は、非推奨(deprecated)になりました。
HTTP リクエストができる他のライブラリ( axios (External link) など)や、 https.requst (External link) に書き換えることをおすすめします。

はじめに

Cisco Webex Messaging と cybozu.com の連携シリーズ第 3 弾として、kintone と連携してみました。
kintone のタスク管理アプリに登録されている残タスクリストを、Cisco Webex Messaging に呼び出す方法を紹介します。
Cisco Webex Messaging はスマホアプリもありますので、出勤中や外出中の電車の中などで、すばやく自分のタスクを確認できます。

第 2 弾のユーザー連携を応用したり、Cisco Webex Messaging の Bot 機能を使います。
これまでの Cisco Webex Messaging と cybozu.com の連携シリーズはこちらです。

第 1 弾(Garoon との連携) Cisco Webex Messaging から Garoon スケジュールを予約する

第 2 弾(ユーザー連携) Cisco Webex Messaging と cybozu.comでユーザー連結を行う

連携イメージ

  • Cisco Webex Messaging(以下 Webex Messaging)のスペースで、タスク確認用の Bot にメンションを投げます。
  • Bot が kintone のタスク管理アプリに登録されている自分の未完了タスクを Webex Messaging に投稿します。

連携概要

これまでの Webex Messaging と cybozu.com の連携シリーズでは、AWS を使って連携する方法を紹介していましたが、AWS 以外でも動作する例として Azure を使った方法で紹介します。

Azure fuctions(Node.js)にて、以下の処理を行います。

  • Webex Messaging の Webhook から POST された投稿メッセージ、メールアドレスを取得
  • Webex Messaging と cybozu.com のユーザーをマッチング
  • kintone へのリクエストデータを作成
  • kintone REST API を実行し、タスク管理アプリのレコードを取得
  • Webex Messaging 投稿用のメッセージ(タスク一覧)を作成
  • Bot 経由でメッセージを Webex Messaging に投稿

下準備

kintone アプリ

担当者とステータスがわかるシンプルなタスク管理アプリを作成します。

フィールド名 フィールドタイプ フィールドコード
タスク 文字列(1行) task
ステータス ラジオボタン status
担当者 ユーザー選択 responsible
詳細 文字列(複数行) detail

ステータスの選択肢は、「未着手」「着手」「完了」で設定してください。

cybozu.com

cybozu.com の管理権を有するユーザーを用意します。
ユーザー情報を取得できる権限が必要になります。

Cisco Webex Messaging 用 Bot

  1. Webex for Developers (External link) から My Webex Apps (External link) を開く。

  2. 「Create a Bot」をクリックする。

  3. 以下を参考に設定し、「Create Bot」をクリックする。

    項目 設定例
    Name TaskList(bot)
    Bot Username 任意文字列@sparkbot.io
    Icon 任意(URL)
    Description 任意

Cisco Webex Messaging

  • 任意のスペース(タスクを表示させるスペース)のユーザーに作成した Bot を追加する。
  • 上記のスペースを利用するユーザーを 1 人以上登録する。
  • 登録するユーザーのメールアドレスは、cybozu.com に登録されているメールアドレスと一致させる。

Cisco Webex Messaging と cybozu.com のユーザー連携は、メールアドレスで一致させています。
ユーザー連携の詳細は Cisco Webex Messaging と cybozu.com でユーザー連結を行う を参照してください。

環境作成

Azure Functions

Azure Functions の準備に関しては、 「kintone と Microsoft Azure を連携してみよう (Azure Functions その1)」の「Azure Functions の準備」 を参考にしてください。

ここでは簡単にポイントのみ記述し、アカウント等の準備は省略します。

関数を作成
  1. Function App の画面からクイックスタート画面を開く。
  2. シナリオ選択は、「Webhook + API」を選択
  3. 言語の選択は、「JavaScript」を選択
パッケージのインストール
  1. 「Function App の設定」>「Kudu に移動」を選択
  2. 「site」から作成した関数のフォルダーに移動
  3. npm コマンドを実行し、package.json を生成
  4. 「request」「request-promise」をインストール
  5. package.json を開いて下図の赤枠部分にインストールしたパッケージが表示されていることを確認
1
2
3
4
5
6
7
8
// npmコマンド実行
D:\home\site\wwwroot\<function_name>> npm init

// requestのインストール
D:\home\site\wwwroot\<function_name>> npm install request --save-dev

// request-promiseのインストール
D:\home\site\wwwroot\<function_name>> npm install request-promise --save-dev

Node.js
  1. 次の JavaScrip プログラムをコピーし、作成した関数の「開発」のコード欄に貼付
  2. 14 行目、17 行目、20 行目、23 行目で記載されている各パラメーターをご自身の環境に合わせて記入
  3. 「保存」ボタンをクリック
  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
 * kinone x Cisco Webex Messaging sample program
 * Copyright (c) 2017 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
 */

(function() {
  'use strict';
  const rp = require('request-promise');

  // Cisco Webex Messaging の Bot の Access Token
  const BEARER = 'XXX';

  // cybozu.com の実行用のユーザー(「ログイン名:パスワード」をBASE64エンコードしたもの)
  const CYBOZU_AUTH = 'XXX';

  // kintone のドメイン
  const DOMAIN = 'https://{subdomain}.cybozu.com';

  // kintone のタスク管理のタスク管理のアプリ番号
  const APP_ID = 1;

  let context,
    err = null;

  // Cisco Webex Messaging にメッセージを send する
  function sendSpark(msg, markdown, roomid) {
    const body_post_spark = {}; // Cisco Webex Messaging に投稿する内容

    // markdown か否かで区分
    if (markdown) {
      body_post_spark.markdown = msg;
    } else {
      body_post_spark.text = msg;
    }

    body_post_spark.roomId = roomid;

    // Cisco Webex Messaging に投稿するためのオブジェクト
    const postspark = {
      url: 'https://api.ciscospark.com/v1/messages/',
      method: 'POST',
      auth: {bearer: BEARER},
      'Content-Type': 'application/json',
      json: body_post_spark
    };

    // 投稿を実行する
    return rp(postspark).then((res) => {
      context.log('投稿されました:' + msg);
      return res;
    });
  }

  // cybozu.com のユーザー情報を取得する
  function getUser(opt_offset, users) {
    const offset = opt_offset || 0;
    const objUser = users || {};

    const objGetUser = {
      url: DOMAIN + '/v1/users.json?offset=' + offset,
      headers: {'X-Cybozu-Authorization': CYBOZU_AUTH},
      method: 'GET',
      'Content-Type': 'application/json'
    };

    // ユーザー情報の取得を実行する
    return rp(objGetUser).then((res) => {
      const resUser = JSON.parse(res);

      // 取得したユーザー情報をオブジェクトに格納する
      for (let i = 0; i < resUser.users.length; i += 1) {
        objUser[resUser.users[i].email] = {code: resUser.users[i].code,
          name: resUser.users[i].name};
      }

      // 100 件取得してる場合は、offset を 100 ずらして再度ユーザー取得
      if (resUser.users.length === 100) {
        return getUser(offset + 100, objUser);
      }

      return objUser;
    });
  }

  // Cisco Webex Messaging の投稿者と e-mail が一致する cybozu.com のユーザー情報を取得する
  function getPostingUser(email, roomid) {
    context.log('roomid2= ' + roomid);

    // cybozu.com からユーザー情報を取得する
    return getUser().then((rtnUser) => {
      if (!(rtnUser[email])) {
        err = DOMAIN + ' 上に該当するユーザーが見つかりませんでした';
        return sendSpark(err, true, roomid);
      }
      // Cisco Webex Messaging の投稿者と e-mail が一致するユーザー情報を返す
      return rtnUser[email];
    }).catch((e) => {
      if (e) {
        throw Error(e);
      }
    });
  }

  // kintone から残タスクを取得する
  function getTaskRemain(email, objUser, roomid) {
    const code = objUser.code.replace(/(^\s+)|(\s+$)/g, ''),
      param = 'responsible in ("' + code + '") and status not in ("' + encodeURIComponent('完了') + '")';

    const objTaskRemain = {
      url: DOMAIN + '/k/v1/records.json?app=' + APP_ID + '&query=' + param,
      headers: {'X-Cybozu-Authorization': CYBOZU_AUTH},
      method: 'GET',
      'Content-Type': 'application/json'
    };

    // 検索を実行する
    return rp(objTaskRemain).then((body) => {
      let msgstr = '';
      const obj = JSON.parse(body);
      const rec = obj.records;
      context.log(obj);

      // 検索結果を表示する
      if (objUser) {
        msgstr += '<@personEmail:' + email + '|' +
                                             objUser.name + 'さん>\n\n';
      }

      msgstr += '**----------------------------**\n\n';
      msgstr += '**' + rec.length + '件**が検索されました\n\n';
      for (let i = 0; i < rec.length; i += 1) {
        msgstr += '\n**【 ' + rec[i].task.value + ' 】**\n\n';
        msgstr += '- ステータス: ' + rec[i].status.value + '\n';
        msgstr += '- 詳細: ' + rec[i].detail.value + '\n';
      }

      msgstr += '\n**----------------------------**\n\n';
      return sendSpark(msgstr, true, roomid);
    });
  }

  module.exports = function(ct, event) {
    let sendpemail, roomid;
    context = ct;

    if (event.body.data) {
      // 投稿したメッセージの詳細を取得する
      const getmessage = {
        url: 'https://api.ciscospark.com/v1/messages/' + event.body.data.id,
        method: 'GET',
        auth: {bearer: BEARER},
        'Content-Type': 'application/json'
      };

      // メッセージの詳細の取得を実行する
      return rp(getmessage).then((body) => {
        const objbody = JSON.parse(body);

        // 投稿者の投稿者の email の情報を保持しておく
        sendpemail = objbody.personEmail;

        // 投稿先のroomidを保持しておく
        roomid = objbody.roomId;

        // email の情報を元に、、cybozu.com のユーザー情報を取得する
        if (!objbody.html) {
          return null;
        }

        const cmd = objbody.html ? objbody.html.split('</spark-mention>') : [];

        // botを指定した直後に「get」と入力された場合にのみ実行する
        if (cmd.length > 1 && cmd[1].indexOf('get') > -1) {
          return getPostingUser(sendpemail, roomid);
        }

        return null;

      }).then((rtnUser) => {

        if (rtnUser) {
          return getTaskRemain(sendpemail, rtnUser, roomid);
        }

      }).catch((e) => {

        context.log(e);
      });
    }
  };
})();

Cisco Webex Messaging Webhook の設定

Bot のアクセストークンを確認する方法

Bot のアクセストークンは、Bot を作成した My Apps から該当する Bot を選択すとアクセストークンを取得できます。

Azure Functions の関数の URL を確認する方法

targetUrl には、Azure Functions の関数の URL を設定してください。
URL は、Azure Functions の「開発」画面の上部に記載があります。

filter に設定する値について

Authorization で Bot のアクセストークンを指定しているためフィルターは基本的に不要ですが、今回は Bot からの投稿を除外したいため、Bot がメンション先に指定された場合のみとしています。
me は Authorization で指定したトークンに対応するユーザでフィルターするという意味になります。

試してみよう

実際に Cisco Webex Messaging のスペースで TaskList(Bot)にメンションし、get を入力してみました。
この機能は TaskList(Bot)に続いて「get」というキーワードを入力した場合のみ動作するようにしています。

おわりに

今回のポイントは、以下の 2 点となります。

  • kintone のデータをチャット感覚で手軽に確認できること
  • 既存スペースにタスク確認用の Bot を追加するだけで使えること

特に 2 点目は、Bot を使うことで、タスク確認用のスペースを新たに作る必要がなくなります。

ここではシンプルなシナリオで紹介しましたが、当日期限のタスクだけを表示するとか、毎日朝 9 時に Bot から残タスクのお知らせが届くとか、応用することでより便利に使えます。

Cisco Webex Messaging はビジネスで使われることを前提としたコミュニケーションツールなので、セキュリティ面でも安心してお使いいただけます。
kintone とも組み合わせることで、バーチャル会議やチャットだけではない、使い方もできます。
アイディアひとつで、便利でユニークなアプリケーションにもなり得ますので、ぜひお試しください!

関連技術情報