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とも組み合わせることで、バーチャル会議やチャットだけではない、使い方もできます。
アイディアひとつで、便利でユニークなアプリケーションにもなり得ますので、ぜひお試しください!

関連技術情報