Slackにバイク川崎バイクを導入する【Python/Slackbot/Mecab】

# はじめに

バイク川崎バイクとは

<iframe width="560" height="315" src="https://www.youtube.com/embed/DmQWGH8r3oE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

> 何でも略したらBKB(ベッドで急に爆睡など)になるネタなどがある。ネタの中では『BKB』というフレーズを多用し、身の回りの事を何でも『BKB』にして表現[11]し、最後に両腕を広げて「ヒィーーーアッ」と叫び、身体でBKB文字を作りながら「ススス！」と言う。他に主なセリフには「今日もアクセル全開でがんばります、バイクだけにブンブン」などがある。

# 目的

業務用Slackにバイク川崎バイクを召喚します。
チャネル投稿中の文章を勝手にみて、略したらBKBになる文章があればその人にリプライを飛ばすbotを実装します。

**イメージ（slack）**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591481/bf8973ab-270f-3e27-f276-e1509eef5bd2.png)


# 設計

大体こんな感じで作ろうと妄想します。

 - チャネルの投稿文を常時監視して、BKBを含んだ投稿をした人にリプライする。BKB自体にリプライが来たら適当なリプライを返す
 - BKBの判定ロジックは形態素解析と正規表現でいけそう　慣れているPythonと、MeCabで動かす
 - BotのロジックはPythonのライブラリslackbotを使う
 - Herokuにデプロイして恒常的に動くようにする

# 実装

## BKB判定ロジック

概ね、以下の順で文章をBKB文に変換します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591481/77a71fee-5e8d-3eec-7a1a-97e4337f6122.png)

**実装例(botmodule.py)**

```python:botmodule.py
# BKB文判定ロジック
@listen_to(r'.*')
def reply_bkb(message):
    # 1. 元文章を読み込み
    TEXT = message.body['text']
    m = MeCab.Tagger()
    # 2. 形態素分割して単語にインデックスを振る（リスト化する）
    node = m.parseToNode(TEXT)
    raws = []
    features = []
    # 3. 読みがなに変換
    while node:
        try:
            feature = node.feature.split(',')[9]
        except IndexError:
            feature = node.surface.split(',')[0]
        raw = node.surface.split(',')[0]
        if feature != "*":
            features.append(feature)
            raws.append(raw)

        node = node.next
　　
    # 4. 単語の頭文字の読みが、「Bから始まる」→B「Kから始まる」→K「それ以外」→O　に変換
    features2 = []
    for feature in features:
        if feature:
            if feature[0] in {"バ", "ビ", "ブ", "ベ", "ボ", "B", "b"}:
                tmp="B"
            elif feature[0] in {"カ", "キ", "ク", "ケ", "コ", "K", "k"}:
                tmp="K"
            else:
                tmp="O"
        else:
            tmp="O"

        features2.append(tmp)

    features2 = "".join(features2)
    # 5. B/K/Bが先頭になるように文字を区切りなおす
    p_bkb = re.compile(r'.*?B.{0,3}?K.{0,3}?B.*?')
    if p_bkb.match(features2):
        b1 = re.compile(r'.*?(B.{0,3}?)K.{0,3}?B.*?').match(features2).span(1)
        k1 = re.compile(r'.*?B.{0,3}?(K.{0,3}?)B.*?').match(features2).span(1)
        b2 = re.compile(r'.*?B.{0,3}?K.{0,3}?(B).*?').match(features2).span(1)
        b1_text = "".join(raws[b1[0]:b1[1]])
        k1_text = "".join(raws[k1[0]:k1[1]])
        b2_text = "".join(raws[b2[0]:b2[1]])

     # 6. 5.のB/K/Bのインデックスを参照して、BKB文を生成する
        message.reply("\n{}！\n{}！\n{}！\n\nBKB！ヒィア！！".format(b1_text, k1_text, b2_text))

    else:
        pass
```

5.の文字の区切り直しの手順が少し複雑です。下の4種類の正規表現のパターンを使用します。

| 変数名 | パターン | 用途 | 補足 |
|:-|:-|:-|:-|
| p_bkb | r'.*?B.{0,3}?K.{0,3}?B.*?'  | 文章がB,K,Bを含むか判定する  | 間の文字が長すぎると美しくないので、間は3単語以内とする  |
| b1  | r'.*?(B.{0,3}?)K.{0,3}?B.*?'  | 最初のB~Kの直前までを抽出する  |   |
| k1  | r'.*?B.{0,3}?(K.{0,3}?)B.*?'  | K~2回目のBの直前までを抽出する  |   |
| b2  | r'.*?B.{0,3}?K.{0,3}?(B).*?'  | 2回目のBを抽出する  | Bだけを抽出する（後ろの方まで取るか迷ったが、元ネタもこれに近い  |

## botの作成

下記の記事を参考に実装しました。
SlackのAPIキーなど大事な情報はHerokuの環境変数に記載して読み込むように実装します。

https://miyabikno-jobs.com/entrance-labotlatori/

```plaintext:ディレクトリ構成
│  .buildpacks
│  .cellar
│  .gitignore
│  Procfile # Herokuでの挙動の設定を記述する
│  pyvenv.cfg
│  README.md
│  requirements.txt
│  run.py # 初回起動時の処理を記載する
│  runtime.txt
│  slackbot_settings.py # APIのKeyを読み込む処理などを記述する
│
│
└─plugins
        botmodule.py # BKBロジック判定など主要な処理を書く
        __init__.py
```

# 完成品

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591481/52b46426-25bf-9435-2238-b108dd4f5501.png)

動いた！
メンションしなくても勝手に拾ってくるのが ~~鬱陶し~~ ユーモアあふれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591481/3de2a262-e92e-4998-ca73-b293f8c06731.png)

ちなみにメンションすると適当な返事を返してくれます。

# 改善点

 - 辞書は`ipadic-neologd`を使っていないので、今風の言葉の判定は弱い。とはいえ、Herokuにインストールする方法も分からないし、そもそも無料枠のストレージには乗り切らなそうなので断念。
 - Herokuには`Free dyno time`なるものが設定されており、無料枠だと月550時間≒22日ぐらいしか稼働しない。月末はお休みをいただいている状態である。ラズパイとかに乗せれば365日稼働するけどそこまでするほどのものでもない。

# 終わりに

TwitterのBotにすることも考えましたが、API申請が面倒だったり世に出すことへの諸々の懸念があったので、身内だけで楽しむ用に作りました。
本記事を参考にお仕事中のBKBを楽しんでもらえると幸いです。

**参考資料**

https://note.com/bkbbunbun/

https://www.amazon.co.jp/dp/484709946X/ref=cm_sw_r_cp_apa_fabc_J6L4FbP4CP4Q7