もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

チャットワークだってslackに負けないぞ!(Botの話です)

はじめに

こんばんは。

連続投稿です。

眠いけど頑張ります。

短いけど許してください。

チャットサービスってみなさん何使ってますか?

会社に依存するとは思いますが、エンジニア主体な会社ではSlackが多いんじゃないでしょうか?

コチラとか見ても分かるように、各社Botとか作って、業務改善・コミュニケーション促進につかってますよね。

自社でも導入したいなーとか思っても、結局会社で導入されているのはチャットワークで、ずーんとかなっている人もいるかもです。

ただ、去年の11月にチャットワークでもwebhookが使えるようになり、Botが導入出来るようになりました。

自分が今お世話になっている会社もチャットワークなので、「個人的」にこっそりBotを導入しておきました。

PHPコード

今回はメンションをつけて話しかけられた時に返答するBotを作成しました。

1. public/my_mention.php

リクエストを受けるファイルです。

<?php

require_once realpath(__DIR__) . "/../vendor/autoload.php";

$dotenv = new Dotenv\Dotenv(__DIR__ . '/../');
$dotenv->load();

$request_body   = file_get_contents('php://input');
$chatwork_token = getenv('CHATWORK_WEBHOOK_MY_MENTION_TOKEN');
if (\App\Chatwork\ChatworkSecurityChecker::isValid($chatwork_token, $request_body) === false) {
    \App\Util\Logger::out("NG");
    die();
}

$request_body = json_decode($request_body, true);
$driver       = \App\Domain\MessageManager::createDriver($request_body);

$chatwork_api_key = getenv("CHATWORK_API_KEY");
$chatwork_room_id = $request_body['webhook_event']['room_id'];

$notifier = new \App\Chatwork\ChatworkNotifier($chatwork_api_key, $chatwork_room_id);
$notifier->send($driver->getMessageBody());

2. App/Chatwork/ChatworkSecurityChecker.php

リクエストの正当性をチェックします。

<?php
namespace App\Chatwork;

class ChatworkSecurityChecker
{
    const HASH_ALGORITHM = "sha256";

    /**
     * Chatworkセキュリティチェック
     *
     * @param string $token
     * @param string $request_body
     * @return bool
     */
    public static function isValid($token, $request_body)
    {
        // Signatureの存在確認
        if (!isset($_REQUEST['chatwork_webhook_signature'])) {
            return false;
        }

        $request_signature  = $_REQUEST['chatwork_webhook_signature'];
        $digest             = hash_hmac(self::HASH_ALGORITHM, $request_body, base64_decode($token), true);
        $expected_signature = base64_encode($digest);

        return $request_signature === $expected_signature;
    }
}

3. App/Domain/MessageManager.php

どういった返答を返すかを管理します。

<?php
namespace App\Domain;

use App\Api\DocomoApi;
use App\Api\ZipCloud;
use App\Domain\MessageDriver\FreeTalk;
use App\Domain\MessageDriver\PrefCode;
use App\Domain\MessageDriver\Random;

class MessageManager
{
    // メッセージのprefix
    const MESSAGE_PREFIX_RANDOM = '選んで';
    const MESSAGE_PREFIX_PREF   = '住所教えて';

    /**
     * ドライバーを作成する
     *
     * @param array $request_data
     */
    public static function createDriver(array $request_data)
    {
        // 改行で分割
        $message_data = explode("\n", $request_data['webhook_event']['body']);

        // MessageDriverを取得
        $message_driver = self::getMessageDriver($message_data[1], $request_data);
        $message_driver->log($request_data);

        // メッセージデータをセットする
        // 0番目は自分に対してのメンションなのでいらない
        unset($message_data[0]);
        $message = implode("\n", $message_data);
        $message_driver->setMessage($message);

        return $message_driver;
    }

    /**
     * MessageDriverを取得
     *
     * @param string $message_type
     * @param array  $from_account_id
     * @return \App\Domain\MessageDriver\DriverIn
     */
    private static function getMessageDriver($message_type, $request_data)
    {
        $message_driver = null;
        switch ($message_type) {
            case self::MESSAGE_PREFIX_RANDOM:
                // ランダム
                $message_driver = new Random();
                break;
            case self::MESSAGE_PREFIX_PREF:
                // 郵便番号検索
                $api_class      = new ZipCloud();
                $message_driver = new PrefCode($api_class);
                break;
            default:
                // 基本的にはdocomo雑談API
                $api_key        = getenv('DOCOMO_API_KEY');
                $api_class      = new DocomoApi($api_key);
                $message_driver = new FreeTalk($api_class);
        }

        return $message_driver;
    }
}

4. App/Domain/MessageDriver/Random.php

指定の文字の中からランダムで一つを選択してくれます。

<?php
namespace App\Domain\MessageDriver;

use App\Domain\MessageIn;

class Random implements DriverIn, MessageIn
{
    use DriverTrait;

    /**
     * メッセージ本文を取得
     *
     * @return string
     */
    public function getMessageBody()
    {
        // メッセージを配列へ変換
        $message_data = explode("\n", $this->message);
        
        // 0番目はドライバを選択するための「選んで」という文字のためいらない
        unset($message_data[0]);

        // ランダムでデータを取得
        $key     = array_rand($message_data);
        $message = $message_data[$key];

        return "{$message}』がいいね!";
    }
}

5. App/Chatwork/ChatworkNotifier.php

チャットワークに返信をします。

<?php

namespace App\Chatwork;

class ChatworkNotifier
{
    private $api_key = null;
    private $room_id = null;

    /**
     * ChatworkNotifier constructor.
     *
     * @param string $api_key
     * @param string $room_id
     */
    public function __construct($api_key, $room_id)
    {
        $this->api_key = $api_key;
        $this->room_id = $room_id;
    }

    /**
     * チャットワーク通知実行
     *
     * @param string $body
     */
    public function send($body)
    {
        $client                 = new \Polidog\Chatwork\Client($this->api_key);
        $messageEntity          = new \Polidog\Chatwork\Entity\Message();
        $messageEntity->account = $client->api('me')->show();
        $messageEntity->body    = $body;
        $client->api('rooms')->messages($this->room_id)->create($messageEntity);
    }
}

こんな感じで、my_mention.phpにアクセスが来ると、ChatworkSecurityCheckerで正しいリクエストかを判断する。

その後、MessageManagerで メンションの次の行で指定の文字で来ているかどうかを判断してドライバーを切り替えています。

たとえば「選んで」という文字できた場合はRandomドライバが選択されて、次の行から行区切りできた文字をランダム選択してくれます。

To Botくん
選んで
カレー
寿司
ラーメン
食べない

>> 『ラーメン』がいいね!

みたいな感じです。

終わりに

これは勝手に俺が作ってるんで、別に会社の資産というわけではないので、公開しても良いかなと思いましたw

他にも、先ほどのせたSlackのBot活用例でも紹介されていた、いいね太郎のような、社員同士でいいねを送りあえるBotや、毎日励ましてくれるBot

もちろん業務的な、KPIを教えてくれるBotなども作っています。

今後もどんどん作っていきたいなと思います。

あ、そうだ。githubに自分が作ったコードだけ移して公開しておこう。

こんな感じで今週2記事目の投稿でした。

大した内容ではなくて申し訳ない。