リモートワーク用にエアリプ探知&自動応答BOTを作ってみた

コロナの影響で万年n次請けな私にもリモートワークがやってきました。
家の静かな環境で仕事ができるとはかどりますね。
通勤電車も乗らないで済み、「今まで相当無駄なことをしていたな」と思い知っているところです。

さて、そんなリモートワークで私が困っていることは「チャット」です。
私は、自社や客先部署などかなりたくさんのチャンネルに入っているのですが、
そこで「自分の名前がメンション抜きで出てくる」というエアリプ事象がしばしば起こります。
リモートワークの人員の増加に伴いチャットの流量も増え、拾い出すだけでも一苦労です。

これ自体は「用があるならメンションをちゃんとつけさせる」「キーワード通知を設定する」
という運用側カバーでなんとかすべき話ではありますが、せっかくなので今回は
**「エアリプの内容を解析して、内容次第でBOTの応答を出しわける」**を実装してみます。

イメージとしてはこんな感じです。
![bot_flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211046/8bde6d78-9e6d-1a9b-d0f7-f58022942e74.png)

# チャットBOTの作成
何を使っても構いません。
Pythonなら、どのチャットのプラットフォーム向けでも何かしらあるはずですし、
Qiitaにも良い記事がたくさんあります。

自社Slackでの運用を考えてみましたが、その前に動作の確認をちゃんとしたいので、
今回はまず私のネットの知り合いがいっぱいいるTelegramで運用しようと思います。

また、今回使うCOTOHAは、開発者向け機能だけですと、利用規約の通り、
https://www.ntt.com/content/dam/nttcom/hq/jp/about-us/disclosure/tariff/pdf/c256.pdf
業務用に使うとライセンスの問題となるため、ご利用の際はご注意ください。


以前私が作ったこちらのBOTを使います。
https://qiita.com/Ovismeme/items/cc59a2de1cf537c977cf
https://github.com/ovismeme/telegram_autoreply_bot

これを作った当時「Pythonなんもわからん」でサンプルコードを見よう見まねに書いたのですが、
一年ぶりに見返したところ「なんだこのゴミは。誰が書いたんだ！」ってなったので、大幅に直しました。
可読性も多少上がったのですが、本記事のメインはそこではないので、割愛します。

作り方などは元記事をご参照いただければと思いますが、
BOTの要件としては、「発話されたものの拾い出し」「発話」の機能がついていればなんでも大丈夫です。
moduleも提供されているはずですが、発話を拾う仕組みが物によって違うのでドキュメントはよく読むようにしましょう。

# COTOHA APIを使う
## アカウント登録
「文章がネガティブなものかポジティブなものか」を判別するのに、
NTTコミュニケーションズ株式会社様が開発者向けに提供してくださっているCOTOHAを使います。

[COTOHA API](https://api.ce-cotoha.com/)

<details><summary>ごく簡単に登録方法を説明</summary><div>
![c99dba0e850d24459e47cb2889b10af6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211046/ee0f6b61-a23d-1a6e-c9a9-73126c277b37.png)
ここをクリックしてメールアドレスを入力し、帰ってきたメールのリンクを踏んで本登録したら登録完了です。

![86f90895a0c958336f53ca6a7e57dbd2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211046/90f6a889-a59f-da36-657a-e70eab48f616.png)

</div></details>

登録が終ると、アカウントポータルが見れるようになり、そこでClientIDとClientSecretが払い出されます。

## API呼び出し
[アクセストークンの取得方法](https://api.ce-cotoha.com/contents/reference/accesstoken.html)
[感情分析API](https://api.ce-cotoha.com/contents/reference/apireference.html#sentiment)
こちらに方法は書いてあります。

APIを叩くだけといえば叩くだけですが、実際に動かしているコードを一応貼っておきます。
アクセス情報やURLなどはconfigに突っ込んであるので、ご自分でお使いになるときは、
ハードコードするなりコンフィグを呼び出すなりして動かしてください。

<details><summary>API呼び出し</summary><div>

```python:cotoha.py
import requests
import configparser
import pprint

class CotohaController:
    def __init__(self):
        config_file = './settings/config.ini'
        self.config = configparser.ConfigParser()
        self.config.read(config_file, 'utf-8')
        self.base_url = str(self.config.get('cotoha_auth', 'base_url'))
        self.headers = {
            'Content-Type' : 'application/json;charset=UTF-8'
        }
        self.get_accesstoken()

    def get_accesstoken(self):
        request_body = {
            "grantType": "client_credentials",
            "clientId": str(self.config.get('cotoha_auth', 'client_id')),
            "clientSecret": str(self.config.get('cotoha_auth', 'client_secret'))
        }
        auth_url = str(self.config.get('cotoha_auth', 'auth_url'))
        responce = requests.post(auth_url, headers=self.headers, json=request_body)
        self.headers.update(Authorization = 'Bearer ' + responce.json()['access_token'])

    def emotion_analysis(self, text):
        request_body = {
            "sentence": str(text)
        }
        url = self.base_url + '/nlp/v1/sentiment'
        responce = requests.post(url, headers=self.headers, json=request_body)
        return responce.json()['result']['sentiment']


if __name__ == '__main__':
    cotoha = CotohaController()
    print(cotoha.emotion_analysis('今日も一日がんばるぞい！'))
```
</div></details>

`emotion_analysis()`にテキストを渡すと、APIが感情を分析して、`Positive` `Negative` `Neutral`が返ってきます。

```
PS D:\src\telegram_autoreply_bot> python .\cotoha.py
Positive
```
サンプルコードでいうとこんな感じです。

## BOT側設定
```python
try:
    cth = cotoha.CotohaController()
    emote = cth.emotion_analysis(rcv_text)
    reply_list = self.replyLists['cotoha'][emote]
    return_text = reply_list[random.randrange(len(reply_list))]
except Exception:
    return_text = "・・・"
```
トリガー発話があった場合、先ほど作ったAPI呼び出しモジュールを叩き、APIの感情分析に合わせて、定義ファイルからランダムに応答を返却します。

```json

 [
   "regex:.*めめたん.*",
   "regex:.*(羊|ひつじ|ヒツジ).*"
 ]
```
私の実装では、この正規表現にマッチすると発火するようになっています。

参考までに、応答の定義部分は、こんな感じになっています。
<details><summary>定義json</summary><div>

```json
  "cotoha" : {
    "Positive" : [
      "もっと褒めて！！",
      "そうだ！そうだ！！",
      "褒めると伸びます（鼻の下が）"
    ],
    "Negative" : [
      "聞こえてるぞ！",
      "何をかいわんや！！",
      "何をこそこそと！！",
      "悪口禁止！！"
    ],
    "Neutral" : [
      "エアリプ禁止！！",
      "呼んだ？呼んだよね！？呼んだよね！？",
      "御用の際は要件をお伝えの上、一万円をください",
      "何かいいましたか？"
    ]
  }
```
</div></details>

ちなみに「応答する」という実装ではなく
**「ネガティブワードが入ったら自分にチクりメンションをこっそり送る」**のような邪悪な実装は色々できます。
今回は色々と自粛していますが、各自面白い実装があったら教えてくださいませ。

# 動作
![29cf765e043a4d68331e75ebd750ea64.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211046/ba207051-66b1-ae0d-1a80-594e786a55f9.png)

ご好評をいただき何よりです。

# 気を付けることやよもやま話
## 呼び出しトリガーについて
https://api.ce-cotoha.com/contents/reference/apireference.html#parsing
この形態素解析をかけて、自分の名前を形態素から拾いだすほうがクールな実装です。

しかし日本語の自然言語処理は固有名詞に弱いです。
特にひらがなの意味の分からない名前はあんまり拾えませんし、何かと誤動作する原因にになります。
COTOHA APIというかNTT系のプロダクトは精度が高いほうだと私は思っていますが、
それでもあだ名のようなものについては正確に拾うのは難しいです。
私の「めめたん」というスクリーンネームも文脈によっては「め/めたん」のように分解される恐れがあります。
よって、トリガーについては形態素解析を使わず、正規表現で発火させる形のほうが間違いがないです。

## 感情分析について
ここまで読んでいただいて申し訳ないのですが、申し上げづらいことがあります。
正直悪口のバリエーションが少ないといいますか、口語の悪口を想定しないで作られているのか、
はたまた「悪口」と「Negative」は違うのか、ちょっとわからないのですが、
現状「Negative」のフラグがあまりたちません。
心を痛めながら自分への悪口を書きまくって実験をしていたのですが、ちょっとそこは残念でした。
![afc12487ec568fb355eec13a260ce6bb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211046/2f3150ef-8e73-fbce-b50c-fe4b64f84238.png)

その辺は今後に大きく期待するとして、
運用側のカバーとして、私のBOT側では「Neutral」でもそれっぽい回答を返すという実装にしています。
チクりBOTなどを作りたい方も、「Positive」以外で動作するようにしたほうがいいかもしれません。

## 商用で使えないじゃない！リモートワーク用ってなんだよ！
月13万円で音声入力なども含めてガンガン使えますよ！！安いじゃん！！（払うとは言ってない）
もちろんこの機能だけで入れるわけにもいかないと思いますが、他の機能などとセットで提案すると会社で通るかもしれません。
ご利用は自己責任でお願いします。

# 終わりに
チャットBOTは既成のものを使いましたが、今日の午前だけでAPI呼び出しを仕込めたので、
おそらくQiitaをご覧の皆さまでしたらそこまで苦労せずに使いこなせると思います。
自然言語処理は、くだらない使い道が色々あり、遊びで作るのにはとても楽しいので、皆様のアイディアも是非見せてください。

ご覧いただきありがとうございました。