kintoneとChatworkの連携 レコード詳細画面からタスクを作成しよう

著者名:mura-(Alphasta)

目次

はじめに

kintoneのレコード詳細画面からボタンを押すだけで、Chatworkにタスクを作成できたら便利だと思いませんか?
今回はkintone UI Componentを利用してドロップダウンやボタンを作成し、Chatwork APIを活用してチャットやメンバーを動的に取得・選択し、タスクを作成する方法を紹介します。

注意事項

今回の例はあくまでサンプルで、Chatworkにタスクを作成するところまでのカスタマイズです。

完成イメージ

カスタマイズを適用すると、kintoneのレコード詳細画面の上部にドロップダウンとボタンが表示されます。
ドロップダウンを選択しボタンをクリックすると、レコードに登録されているデータを、タスクとしてChatworkに連携できます。

kintoneのアプリの作成

簡略化のために、次の2つのフィールドだけを設定します。
また、フィールド名とフィールドコードは同一とします。

フィールド名/フィールドコード フィールドタイプ
タイトル 文字列(1行)
詳細 文字列(複数行)

Chatwork の設定

Chatworkのアカウント設定ページからAPIトークンを取得します。
詳細は Chatworkのヘルプページ (External link) を確認してください。

caution
警告

APIトークンは秘密情報なので、絶対に公開しないでください。
APIトークンの情報が漏洩するとAPIを自由に実行できてしまうため、kintoneのJavaScriptの開発においても慎重に扱ってください。

cybozu develper networkでは、認証情報の秘匿にプラグインを利用する方法を推奨しています。
詳しくは以下の記事を確認してください。

サンプルコード

以下のJavaScriptファイルとCSSファイルを設定画面より指定してください。
指定方法はヘルプの JavaScriptやCSSでアプリをカスタマイズする (External link) を参考してください。

  • JavaScriptファイル
    • https://unpkg.com/kintone-ui-component@1.16.0/umd/kuc.min.js(kintone UI Component)
    • chatwork.js
      chatwork.js をJavaScriptファイルとして保存し、アップロードしてください。
  • CSSファイル
    • chatwork.css
      chatwork.css をCSSファイルとして保存し、アップロードしてください。

chatwork.js

  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
/*
 * Chatwork sample program
 * Copyright (c) 2024 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
*/
(() => {
  'use strict';

  const chatworkApiToken = 'YOUR_CHATWORK_API_TOKEN';

  // ドロップダウンの初期値
  const defaultRoomOption = {label: 'チャットを選択', value: ''};
  const defaultUserOption = {label: 'ユーザーを選択', value: ''};

  // Chatwork APIからチャットリストを取得
  const getChatworkRooms = async () => {
    const response = await kintone.proxy('https://api.chatwork.com/v2/rooms', 'GET', {
      'X-ChatWorkToken': chatworkApiToken
    }, {});

    if (response[1] !== 200) {
      throw new Error('チャットリストの取得に失敗しました');
    }

    return JSON.parse(response[0]);
  };

  // Chatwork APIからメンバーリストを取得
  const getChatworkMembers = async (roomId) => {
    const response = await kintone.proxy(`https://api.chatwork.com/v2/rooms/${roomId}/members`, 'GET', {
      'X-ChatWorkToken': chatworkApiToken
    }, {});

    if (response[1] !== 200) {
      throw new Error('メンバーリストの取得に失敗しました');
    }

    return JSON.parse(response[0]);
  };

  // レコード詳細画面のイベント
  kintone.events.on('app.record.detail.show', async (event) => {
    if (document.getElementById('chatworkButton')) {
      return;
    }

    // チャットリストのドロップダウンの作成
    const roomSelect = new Kuc.Dropdown({
      items: [defaultRoomOption],
      id: 'chatworkRoom',
      requiredIcon: true
    });

    // ユーザーリストのドロップダウンの作成
    const userSelect = new Kuc.Dropdown({
      items: [defaultUserOption],
      id: 'chatworkUser',
      requiredIcon: true,
    });

    // タスク作成ボタンの作成
    const button = new Kuc.Button({
      text: 'Chatworkにタスクを作成',
      id: 'chatworkButton',
      type: 'submit'
    });

    // チャットが変更されたら、そのチャットのメンバーを取得
    roomSelect.addEventListener('change', async () => {
      if (roomSelect.value === '') {
        userSelect.items = [defaultUserOption];
      } else {
        const members = await getChatworkMembers(roomSelect.value);
        userSelect.items = [defaultUserOption, ...members.map(member => ({label: member.name, value: `${member.account_id}`}))];
      }
      userSelect.value = '';
    });

    // ボタンがクリックされたら、タスクを作成
    button.addEventListener('click', async () => {
      const roomId = roomSelect.value;
      const userId = userSelect.value;
      if (roomId === '' || userId === '') {
        alert('チャット、ユーザーを指定してください');
        return;
      }
      const record = event.record;
      const chatworkApiUrl = `https://api.chatwork.com/v2/rooms/${roomId}/tasks`;

      const recordUrl = `${location.origin}/k/${kintone.app.getId()}/show#record=${kintone.app.record.getId()}`;

      const taskData = {
        body: `新しいタスク: ${record.タイトル.value} \n ${record.詳細.value} \n kintoneレコードURL: ${recordUrl}`, // レコード名を使用
        limit: 0, // 期限が必要ならUNIXタイムスタンプで設定
        to_ids: userId // 担当者のID
      };

      const urlEncodedData = new URLSearchParams(taskData).toString();

      try {
        const response = await kintone.proxy(chatworkApiUrl, 'POST', {
          'X-ChatWorkToken': chatworkApiToken,
          'Content-Type': 'application/x-www-form-urlencoded'
        }, urlEncodedData);

        if (response[1] !== 200) {
          throw new Error('タスクの作成に失敗しました');
        }
        alert('タスクの作成に成功しました');
      } catch (error) {
        alert('タスクの作成に失敗しました: ' + error.message);
      }
    });

    // チャットリストの取得と表示
    try {
      const rooms = await getChatworkRooms();
      roomSelect.items = [defaultRoomOption, ...rooms.map(room => ({label: room.name, value: `${room.room_id}`}))];
    } catch (error) {
      console.error('チャットリストの取得に失敗しました:', error);
    }

    // ボタンやドロップダウン表示用のラッパーエレメントを作成
    const wrapperElement = document.createElement('div');
    wrapperElement.id = 'chatwork-wrapper';
    wrapperElement.appendChild(roomSelect);
    wrapperElement.appendChild(userSelect);
    wrapperElement.appendChild(button);

    // レコード詳細画面上部のスペースに表示
    const spaceField = kintone.app.record.getHeaderMenuSpaceElement();
    if (spaceField) {
      spaceField.appendChild(wrapperElement);
    }
  });
})();

chatwork.css

1
2
3
4
5
6
7
8
9
@charset "UTF-8";

#chatwork-wrapper {
  display: grid;
  gap: 10px;
  grid-auto-flow: column;
  padding: 10px;
  grid-auto-columns: max-content;
}

サンプルコードの解説

以下、コードを具体的に解説します。

チャットリスト・ユーザーリストを取得する関数の定義

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
// Chatwork APIからチャットリストを取得
const getChatworkRooms = async () => {
  const response = await kintone.proxy('https://api.chatwork.com/v2/rooms', 'GET', {
    'X-ChatWorkToken': chatworkApiToken
  }, {});

  if (response[1] !== 200) {
    throw new Error('チャットリストの取得に失敗しました');
  }

  return JSON.parse(response[0]);
};

// Chatwork APIからメンバーリストを取得
const getChatworkMembers = async (roomId) => {
  const response = await kintone.proxy(`https://api.chatwork.com/v2/rooms/${roomId}/members`, 'GET', {
    'X-ChatWorkToken': chatworkApiToken
  }, {});

  if (response[1] !== 200) {
    throw new Error('メンバーリストの取得に失敗しました');
  }

  return JSON.parse(response[0]);
};

ここではChatwork APIに対して、kintone.proxy()を利用してチャットリストとユーザーリストを取得するための関数を定義しています。

kintoneイベントハンドラーの定義

レコード詳細画面にドロップダウンやボタンを表示するため、 レコード詳細画面を表示した後のイベント を利用します。

// レコード詳細画面のイベント
kintone.events.on('app.record.detail.show', async (event) => {
  // 中略
});

レコード詳細画面のイベントの中で、前述の関数やkintone UI Componentを利用していますので、それぞれみていきます。
kintone UI Componentの詳細については、 kintone UI Component v1 を確認してください。

kintone UI Componentの利用

kintone UI Componentを利用して、ドロップダウンやボタンを作成します。

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// チャットリストのドロップダウンの作成
const roomSelect = new Kuc.Dropdown({
  items: [defaultRoomOption],
  id: 'chatworkRoom',
  requiredIcon: true
});

// ユーザーリストのドロップダウンの作成
const userSelect = new Kuc.Dropdown({
  items: [defaultUserOption],
  id: 'chatworkUser',
  requiredIcon: true,
});

// タスク作成ボタンの作成
const button = new Kuc.Button({
  text: 'Chatworkにタスクを作成',
  id: 'chatworkButton',
  type: 'submit'
});

チャットリストの取得

順番が前後しますが、先にチャットリストを取得する箇所について説明します。

117
118
119
120
121
122
123
// チャットリストの取得と表示
try {
  const rooms = await getChatworkRooms();
  roomSelect.items = [defaultRoomOption, ...rooms.map(room => ({label: room.name, value: `${room.room_id}`}))];
} catch (error) {
  console.error('チャットリストの取得に失敗しました:', error);
}

前述したgetChatworkRooms()を利用することでチャットリストを取得し、チャットリストのためのドロップダウンを更新します。
レコード詳細画面を表示した後のイベント内の定義ですので、レコード詳細画面が表示されたときに自動で取得します。

チャットリストを選択したときのイベント

チャットリストのドロップダウンの変更イベントがあるとき、選んだチャットに応じてメンバーリストを取得する必要があるためその定義をします。

70
71
72
73
74
75
76
77
78
79
// チャットが変更されたら、そのチャットのメンバーを取得
roomSelect.addEventListener('change', async () => {
  if (roomSelect.value === '') {
    userSelect.items = [defaultUserOption];
  } else {
    const members = await getChatworkMembers(roomSelect.value);
    userSelect.items = [defaultUserOption, ...members.map(member => ({label: member.name, value: `${member.account_id}`}))];
  }
  userSelect.value = '';
});

チャットが選ばれていないときはデフォルトの表示、選ばれている場合は前述のgetChatworkMembers()を呼び出しメンバーリストを取得して、メンバーリストのドロップダウンの項目を変更します。

ボタンをクリックしたときのイベント

 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
// ボタンがクリックされたら、タスクを作成
button.addEventListener('click', async () => {
  const roomId = roomSelect.value;
  const userId = userSelect.value;
  if (roomId === '' || userId === '') {
    alert('チャット、ユーザーを指定してください');
    return;
  }
  const record = event.record;
  const chatworkApiUrl = `https://api.chatwork.com/v2/rooms/${roomId}/tasks`;

  const recordUrl = `${location.origin}/k/${kintone.app.getId()}/show#record=${kintone.app.record.getId()}`;

  const taskData = {
    body: `新しいタスク: ${record.タイトル.value} \n ${record.詳細.value} \n kintoneレコードURL: ${recordUrl}`, // レコード名を使用
    limit: 0, // 期限が必要ならUNIXタイムスタンプで設定
    to_ids: userId // 担当者のID
  };

  const urlEncodedData = new URLSearchParams(taskData).toString();

  try {
    const response = await kintone.proxy(chatworkApiUrl, 'POST', {
      'X-ChatWorkToken': chatworkApiToken,
      'Content-Type': 'application/x-www-form-urlencoded'
    }, urlEncodedData);

    if (response[1] !== 200) {
      throw new Error('タスクの作成に失敗しました');
    }
    alert('タスクの作成に成功しました');
  } catch (error) {
    alert('タスクの作成に失敗しました: ' + error.message);
  }
});

タスクを作るために、レコードのタイトル詳細を利用しています。
また、レコードのURLもタスクの内容として送りたいため、それも含むような記述にしています。

おわりに

本記事では、kintoneとChatworkを連携させる手法について紹介しました。
このように、kintoneでは外部のコミュニケーションツールなどと連携ができるのも強みです。
この記事が、さまざまなツールとの連携、業務効率化の一例としてご参考になると幸いです。

information

このTipsは、2024年07月版kintoneで動作を確認しています。