daab 仕様 #
daab は GitHub社の Hubot を拡張したものです。 基本的なスクリプトの書き方は公式ドキュメントを参考にしてください。 以下では、direct 用に Hubot を拡張した点について記述しています。
メッセージの送信 #
以降のコード例は、以下のコードブロックの内側から呼び出すことを前提とします。
module.exports = (robot) => {
  robot.hear(/.../i, (res) => {
    // この部分
  });
}なお、上記のコードブロック以外から送信したい場合は、以下のように robot.send を使い、ルーム ID を明示的に指定する必要があります。ルーム ID
については後述の「トークルームの識別」を参照してください。
module.exports = (robot) => {
  robot.send({ room: '...id...' }, '...message...');
  // もしくは
  robot.send({ room: '...id...' }, { text: '...message...' });
}テキスト (Hubot) #
res.send('This message is text.');もしくは、以下も同等です。
res.send({ text: 'This message is text.' });スタンプ #
res.send({
  stamp_set: '3',
  stamp_index: '1152921507291203198',
  text: 'おはよう'  // (Option) テキスト付きスタンプの場合のみ
});※ stamp_set と stamp_index は、ブラウザの「要素の検証」等で確認してください。
<img src='./images/stamp/3/1152921507291203198.png'>オリジナルスタンプ #
デフォルトスタンプとはフィールド名が異なる点に注意してください。
res.send({
  stampset_id: '1234567_8901234',
  stamp_id: '1234567_8901234',
  text: 'こんにちは'  // (Option) テキスト付きにしたい場合に指定する
});stampset_id と stamp_id に指定する値は「メッセージの受信」の「オリジナルスタンプ」で取得してください。
Yes/No スタンプ #
res.send({
  question: '質問内容',
  closing_type: 1  // (Option) 誰かが回答:0, 全員が回答:1
});※ Yes/No スタンプの closing_type のデフォルト値は 1 です
送信したYes/Noスタンプは締め切ることができます。
res.send({
  close_yesno: sent.message.id
});※ sent.message.id については「メッセージの送信完了」を参照してください。
セレクトスタンプ #
res.send({
  question: '質問内容',
  options: ['選択肢1', '選択肢2', '選択肢3'],
  closing_type: 1  // (Option) 誰かが回答:0, 全員が回答:1
});※ セレクトスタンプの closing_type のデフォルト値は 1 です
送信したセレクトスタンプは締め切ることができます。
res.send({
  close_select: sent.message.id
});※ sent.message.id については「メッセージの送信完了」を参照してください。
タスクスタンプ #
res.send({
  title: 'すること',
  closing_type: 0  // (Option) 誰かが回答:0, 全員が回答:1
});※ タスクスタンプの closing_type のデフォルト値は 1 です
送信したタスクスタンプは締め切ることができます。
res.send({
  close_task: sent.message.id
});※ sent.message.id については「メッセージの送信完了」を参照してください。
ファイル #
res.send({
  path: 'your/file/name.png',
  name: 'name.png',    // (Option) アップロード名
  type: 'image/png',   // (Option) MIME
  text: 'send file',   // (Option) ファイルと同時に送信するテキスト
});※ 複数ファイルを送信するときは、path や name に配列を渡してください。
res.send({
  path: ['your/file1.png', 'your/file2.png'],
  name: ['name1.png', 'name2.png']   // (Option)
});
typeフィールドに関する注意事項:
typeフィールドの指定が無い場合は、SDK 内部で推測された値がサーバーに送信されます。 しかしこの値は正しくないことがあるため、組織の設定で「ファイル添付制限」を有効化していると問題を起こす場合があります。「ファイル添付制限」を有効化している組織で利用するボットを開発する場合は、必ず
typeフィールドを指定してください。
メッセージの受信 #
以降のコード例は、以下のコードブロックの内側で呼び出すことを前提とします。
module.exports = (robot) => {
  // この部分
}テキスト (Hubot) #
robot.hear(/(.*)/, (res) => {
  res.send(`Your message is ${res.match[1]}`);
}メッセージ本文は res.message.text で参照できます。
デフォルトスタンプ #
robot.hear('stamp', (res) => {
  res.send(`${res.json.stamp_set} - ${res.json.stamp_index}`);
}オリジナルスタンプ #
robot.hear('original_stamp', (res) => {
  const { stampset_id, stamp_id } = res.json;
  res.send(`${stampset_id} - ${stamp_id}`);
});Yes/No スタンプ #
robot.hear('yesno', (res) => {
  if (res.json.response === null) {
    res.send(`Your question is ${res.json.question}.`);
  } else {
    res.send(`Your answer is ${res.json.response}.`);
  }
});受信したYes/Noスタンプに返信できます。
robot.hear('yesno', (res) => {
  res.send({
    in_reply_to: res.message.id,
    response: true
  });
});セレクトスタンプ #
robot.hear('select', (res) => {
  if (res.json.response === null) {
    res.send(`Your question is ${res.json.question}.`);
  } else {
    res.send(`Your answer is ${res.json.options[res.json.response]}.`);
  }
});受信したセレクトスタンプに返信できます。
robot.hear('select', (res) => {
  res.send({
    in_reply_to: res.message.id,
    response: 0
  });
});タスクスタンプ #
robot.hear('task', (res) => {
  if (res.json.done === null) {
    res.send(`Your task is ${res.json.title}.`);
  } else {
    res.send(`Your task is ${ res.json.done ? 'done' : 'undone' }.`);
  }
});受信したタスクスタンプに返信できます。
robot.hear('task', (res) => {
  res.send({
    in_reply_to: res.message.id,
    done: true
  });
});ファイル #
const onfile = (res, file) => {
  res.send([
    'File received.',
    `name: ${file.name}`,
    `type: ${file.content_type}`,
    `size: ${file.content_size}bytes`,
  ].join('\n'));
  res.download(file, (path) => {
    res.send(`downloaded to ${path}`);
  });
};
// ファイルが1つだけの場合
robot.hear('file', (res) => {
  onfile(res, res.json);
});
// ファイルが複数の場合
robot.hear('files', (res) => {
  for (const file of res.json.files) {
    onfile(res, file);
    if (res.json.text) {
      res.send(`with text: ${res.json.text}`);
    }
  }
});なお、download メソッドのコールバックを (path, err) => { ... } のようにすると、ダウンロード中に発生したエラー情報を取得することができます。(要 direct-js >= 1.110.0)
res.download(file, (path, err) => {
  if (err) {
    console.error("failed:", err);
  } else {
    console.log("downloaded:", path);
  }
});位置情報 #
robot.hear('map', (res) => {
  res.send(`Your location is ${res.json.place} at ${res.json.lat}, ${res.json.lng}`);
});ノート #
「ノートメッセージの受信」をご覧ください。
トークルーム情報 #
以下のコードブロックの内側についてのものとします。
module.exports = (robot) => {
  robot.hear(/room/i, (res) => {
    // この部分
  });
}トークルームの識別 (Hubot) #
res.send(`This room id is ${res.message.room}`);※ direct特有のトークルーム情報は以下のようにして取得できます。
console.log(res.message.rooms[res.message.room]);トークルームの種類 #
res.send(`This room type is ${['unknown', 'pair', 'group'][res.message.roomType]}`);トークルーム名 #
if (res.message.roomType === 2) {
  // Group talk
  res.send(`Group name is ${res.message.roomTopic}`);
}トークルーム名の変更 (Hubot) #
res.topic('BotGroup');もしくは
robot.roomTopic({ room }, 'BotGroup');引数に指定する room は res.message.room の値を保存して使用するか、robot.brain.rooms() からトークルーム ID を取得して使用してください。
トークルームの参加者情報 #
const text = res.message.roomUsers.map(user => `${user.name} ${user.email} ${user.profile_url}`).join('\n\n');
res.send(text);トークルームの一覧 #
const text = Object.entries(res.message.rooms)
  .map(([id, talk]) => `name:${talk.topic} type:${talk.type} users:${talk.users.map(user => user.name).join(',')}`)
  .join('\n\n');
res.send(text);※ res オブジェクトが利用できない場合は、res.message.rooms の代わりに robot.brain.rooms() も利用できます (注:関数呼出しになります)。
連絡先情報の取得 #
以下のコードブロックの内側についてのものとします。
module.exports = (robot) => {
  robot.hear(/users/i, (res) => {
    // この部分
  });
}メッセージ送信者の連絡先 (hubot) #
res.send(`Sender is ${res.message.user.name}.`);連絡先の一覧 (hubot) #
const users = robot.brain.users();
console.log(users);  // { id0:user0, id1:user1, ... }
IDによる連絡先の検索 (hubot) #
const userId = Object.keys(users)[0];
console.log(robot.brain.userForId(userId));名前による連絡先の検索 (hubot) #
const user = users[userId];
console.log(robot.brain.userForName(user.name));※ その他にも、usersForRawFuzzyName (先頭一致)、usersForFuzzyName (先頭一致、ただし、完全一致を優先) も利用できます。
組織情報の取得 #
以下のコードブロックの内側についてのものとします。
module.exports = (robot) => {
  robot.hear(/domains/i, () => {
    // この部分
  });所属する組織の一覧 #
const domains = robot.brain.domains();
console.log(domains);アクションスタンプの回答情報 #
以下のコードブロックの内側についてのものとします。
module.exports = (robot) => {
  robot.hear(/answers/i, (res) => {
    res.send({
      // ここに各スタンプの内容
      onsend: (sent) => {
        setTimeout(() => {
          // この部分
        }, 5000);
      }
    });
  });
}※ 例として、5秒後の回答状況を取得しています。
Yes/No スタンプ #
sent.answer((trueUsers, falseUsers) => {
  console.log('YES', trueUsers);
  console.log('NO', falseUsers);
});セレクトスタンプ #
sent.answer((optionUsers) => {
  optionUsers.forEach((users, i) => {
    console.log(sent.message.content.options[i], users);
  });
});タスクスタンプ #
sent.answer((doneUsers, undoneUsers) => {
  console.log('DONE', doneUsers);
  console.log('UNDONE', undoneUsers);
});イベント #
以下のコードブロックの内側についてのものとします。
module.exports = (robot) => {
  // この部分
}ペアトークのメッセージや HUBOT_NAME を付けたメッセージのみの受信 (Hubot)
#
ペアトークだったり、グループトーク上で HUBOT_NAME を付けたボットへの直接的なメッセージのみを受信したい場合は respond を使います。
robot.respond(/.../, (res) => {
  res.send('');
});上記コールバックは、ペアトーク上のメッセージに対しては hear を使った場合と同様に呼び出されますが、グループトーク上のメッセージに対してはメッセージの最初に環境変数 HUBOT_NAME (デフォルト値は hubot) で指定された文字列を付けた (例えば hubot こんにちは) 場合にのみ呼び出されます。
もし厳密にペアトークのみに反応させたい場合は、コールバック内においてトークの種別を判定する必要があります。ペアトークの roomType 値は 1 です。
robot.respond(/こんにちは$/, (res) => {
  if (res.message.roomType !== 1) {
    return;
  }
  res.send("ペアトークから呼び出されました。");
});トークルーム名の変更 (Hubot) #
robot.topic((res) => {
  res.send(`Topic is changed: ${res.message.text}`);
});トークルームへのユーザーの参加 (Hubot) #
robot.enter((res) => {
  res.send(`Hi! ${res.message.user.name}`);
});トークルームからのユーザーの退出 (Hubot) #
トークルームからユーザーが退出したときに発生するイベントは以下のように検出します。
robot.leave((res) => {
  res.send(`Good bye! ${res.message.user.name}`);
});警告: ボット自身がトークルームの最後の1人になった場合はそのトークから自動的に退出します。
招待による自分自身のトークルームへの参加 #
robot.join((res) => {
  res.send('Nice to meet you!');
});メッセージの送信完了 #
送信メッセージに onsend を付与することで、そのメッセージの送信完了後に処理を行うことができるようになります。
res.send({
  text: 'Now sending...',
  onsend: (sent) => {
    res.send(`completed. messageId: ${sent.message.id}`);
  }
});メッセージの未読・既読 #
送信メッセージに onread を付与することで、そのメッセージの未読・既読情報を取得できるようになります。
robot.hear(/read after/, (res) => {
  res.send({
    text: 'Read this message, please!',
    onread: () => true,
    onsend: (sent) => {
      setTimeout(() => {
        const text = [
          ...sent.readUsers.map(user => `${user.name} read after 5sec.`),
          ...sent.unreadUsers.map(user => `${user.name} did't read after 5sec.`),
        ].join('\n');
        res.send(text);
      }, 5000);
    }
  });未読・既読の変更を監視したい場合は、以下のようにします。 (24時間たっても変更がなければ監視は終了します。)
robot.hear(/read now/, (res) => {
  res.send({
    text: 'Read this message, please!',
    onread: (readNowUsers, readUsers, unreadUsers) => {
      const text = readNowUsers.map(user => `${user.name} read now.`).join('\n');
      res.send(text);
    }
  });
});ノートの作成 #
「ノート機能」をご覧ください。
その他 #
トークルームからの退出 #
res.leave();指定したユーザをトークルームから退出 #
robot.hear(/banned word/, (res) => {
  res.leave(res.message.user);
});一斉連絡の送信 #
// メッセージを受けとった組織に送信
res.announce('THIS IS AN ANNOUNCEMENT!');
// 任意の組織に送信 (domain = { id: domain.id })
robot.announce(domain, 'ANNOUNCEMENT!');※ アカウントに管理者権限が必要です。