LoginSignup
67
14

More than 1 year has passed since last update.

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

Posted at

はじめに

バイク川崎バイクとは

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

目的

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

イメージ(slack)

image.png

設計

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

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

実装

BKB判定ロジック

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

image.png

実装例(botmodule.py)

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の環境変数に記載して読み込むように実装します。

ディレクトリ構成
│  .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

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

image.png

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

改善点

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

終わりに

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

参考資料

67
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
67
14