MattermostとPython+Flaskでシンプルなbotを作る

# Mattermostでシンプルなbotを作る

大人の事情で職場でSlackが使えないときがあります。
その代替手段としてOSSのMattermostがあります。
これで社内での情報共有が捗るわけです！

![](https://i.imgur.com/VdSYwrd.png)

Slackと同様にチャンネルを作成できます。
任意のチャンネルに入って、おしゃべりしたり煽り画像のドッジボールを仕掛けたりできます。

## やりたいこと

僕にかまってくれるbotがほしい。

というわけで、チャンネル上で私が特定のコメントを投げると、それに応じて良い感じに返答してくれるようなシステム、すなわちbotを作成します。

言語はPythonを使ってFlaskでサーバを立ててbotを作ります。

## Mattermostの設定

### インストール

Dockerがあればコマンド1つでインストール＆起動ができます。

```shell
docker run --name mattermost-preview -d --publish 8065:8065 --add-host dockerhost:127.0.0.1 mattermost/mattermost-preview
```

次にMattermostの設定として、以下の2つのことについて行います。

1. Mattermost上での私のコメントをbotのプログラムに送信する
2. botのプログラムからMattermostに返事を返す

### 1. Mattermost上での私のコメントをbotのプログラムに送信する

ここでは、外向きのウェブフックを設定します。

参考URL : [CentOS7.3 環境(Vagrant)に Mattermost いれたので php で bot を追加してみる（Outgoing WebHooks）　その１](https://qiita.com/murachi1208/items/f06c614910b717026d3e)

メインメニューからintergrationsを選択し、「外向きのウェブフック(Outgoing webhook)」を選びます。

![](https://i.imgur.com/r5lVqL6.png)

「外向きのウェブフック(Outgoing webhook)」をクリックすると、設定した一覧が見えます。最初は何も設定していないので、何もないと思います。
右上に「Add Outgoing webhook」があるので、そこから新規作成します。

すると、ずらずらと設定するものがたくさんあります。いくつか説明すると、

- Content Type : Mattermostから送られてきたデータをどの形式で受け取るか
- Channels : どのチャンネルのコメントに注目するか
- Trigger Words : 外向きのウェブフックが機能するためのトリガーとなるワード
- Trigger When : いつトリガーされるのか
- Callback URLs : トリガーされたときにどこにコールバックするか。（POSTするか）

です。設定例は以下の通りです。
受け取るデータの形式はJSON形式で、私がコメントした内容の最初にechoという文字列があれば```http://192.168.11.5:8888/matter```にコメントをPOSTで送信します。
ここには書かれていないですが、チャンネルはOff-Topicに設定しました。
トークンは、どの外向きのフェブフックによるものかをbot側のプログラムで検知するために用いることができます。トークンは自動生成されるものです。

![](https://i.imgur.com/JvlEuh9.png)

あとは、サーバを立てて、```http://192.168.11.5:8888/matter```に何かしらPOSTしたら、送られてきたコメントを読み取って適切な処理をして、その返事をMattermostに返信するようなプログラムを作成すればOKです。

受け取る側（サーバ側）のプログラムはこんな感じに書きます。
dataがターミナル上でprintされれば受信できてるってことですね。

```python
@app.route('/matter', methods=['POST'])
def post():
    data = request.json
    print(data)
    return json.dumps(dict())
```

上手くいかないときは、ログをみることで解決の糸口を探ることができます。

[Outgoing webhookのトラブルシューティング（公式サイト）](https://docs.mattermost.com/developer/webhooks-outgoing.html#troubleshooting)

私が開発していたときは以下のようなログが出て、結構悩んだのでその解決策だけ載せておきます。

### エラーの内容

```
{"level":"error","ts":1540490281.712668,"caller":"app/webhook.go:111","msg":"Event POST failed, err=Post http://192.168.11.5:5353/matter: address forbidden, you may need to set AllowedUntrustedInternalConnections to allow an integration access to your internal network"}
```

セキュリティが強化されているらしく、POSTが禁止されているようです。AllowedUntrustedInternalConnectionsとやらで許可を与えてあげる必要があるみたいですね。

### 解決法

ここで許可するURLを指定します。今回は実験用なので、全部許可しちゃいます。

![](https://i.imgur.com/AUCOCqE.png)


### 2. botのプログラムからMattermostに返事を返す

ここでは、内向きのウェブフック(Incoming webhook)を設定します。

追加画面にてusernameや画像を選択できるかと思います。ここでbotの名前とアイコンを指定します。

設定すると、以下のように新しいURLが生成されます。このURLにコメントを送信すれば、今回設定したbotの名前とアイコンで、コメントがMattermostに反映されます。

![](https://i.imgur.com/sfi3jLj.png)

試しにMattermostにコメントを送ってみましょう。
Slack用のライブラリでslackwebというのがありますが、これがMattermostにも対応しているので使わせていただきました。

```python
import slackweb
mattermost = slackweb.Slack(url="http://192.168.11.10:8065/hooks/y986nywodtf7zjynf9kqn6s9qe")
mattermost.notify(text="Hello!")
```

簡単すぎてビビります。

## モッツアレラチーズゲームをしてくれるbotを作る

本当は業務時間の隙間にある休憩時間でササッと作りたかったのですが、意外と時間がないものですね。

```python
from flask import Flask, request
import requests
import json
import slackweb
import copy
import random

# コメントアウトがとても頭が弱い感じです。

app = Flask(__name__)
mattermost = slackweb.Slack(url="http://192.168.11.10:8065/hooks/y986nywodtf7zjynf9kqn6s9qe")

@app.route('/')
def welcome():
    html = '<html><title>welcome</title>'
    html = html + '<body>Moooooooooooozzarrrrrrrrrrrellaaaaaa</body></html>'
    return html

@app.route('/matter', methods=['POST'])
def post():
    data = request.json
    text = data['text']
    _, mozzarella = text.split(' ')　# トリガー部分の文字列を除去する
    splited_mozzarella = split_mozzarella(mozzarella)
    new_mozzarella = add_hogehoge(splited_mozzarella)
    text = ''.join(new_mozzarella)
    mattermost.notify(text=text)　# botからのコメントを送る
    return json.dumps(dict())


'''
 input: ["モ", "ッッッッッッ", "ツ", "ア", "レ", "ラ", "チ", "ーーーーーー", "ズ", "！！！！"]
output: ["モ", "ッッッッッッッッ", "ツ", "アア", "レ", "ラ", "チ", "ーーーーーーーー", "ズ", "！！！！！！"]
'''
def add_hogehoge(splited_mozzarella):
    new_mozzarella = copy.deepcopy(splited_mozzarella)
    for i, elem in enumerate(splited_mozzarella):
        moji = elem[0]
        if moji in ["ァ", "ィ", "ェ","ッ", "ア", "イ", "エ", "ー", "!", "！"]:
            moji = elem[0]
            n_add = random.choice(range(1, 5))
            new_mozzarella[i] += (moji * n_add)

    return new_mozzarella


'''
 input: "モッッッッッッツアレラチーーーーーーズ！！！！"
output: ["モ", "ッッッッッッ", "ツ", "ア", "レ", "ラ", "チ", "ーーーーーー", "ズ", "！！！！"]
'''
def split_mozzarella(word):
    splited = list()
    last_i = -1
    for w in word:
        if last_i == -1 or splited[last_i][0] != w:
            splited.append(w)
            last_i += 1
        else:
            splited[last_i] += w

    return splited


if __name__ == '__main__':
    app.debug = True
    app.run(host='0.0.0.0', port=8888)
```

人に見せることは意識してないです。
なお、私はリアルでモッツアレラチーズゲームをしたことがないです。あれ、どんな状況でやるんだろう・・・？

基本は以下のサイトにあるコードを膨らませた感じです。

[SLACK/MATTERMOSTのBOTをPYTHONのFLASKで作ろう](http://april.fool.jp/blogs/2016/06/slackmattermost%E3%81%AEbot%E3%82%92python%E3%81%AEflask%E3%81%A7%E4%BD%9C%E3%82%8D%E3%81%86/)

こっちのほうがわかりやすいかもですね。

## さいごに

私はモッツアレラチーズは叫ぶ派ではなく食べる派です。