やりたかったこと
飲みすぎた・・・明日もう会社やすみたい・・・
でも、その手の連絡はmattermostでやることになっていて、家から連絡しようにもF/Wで閉じられてるしなぁ・・・
VPNを通せばいいのだけれど、それも面倒だし・・・
っていうかスマホからさくっと連絡すませたい・・・
ということで、slackとmattermostを両方使っているという謎運用を利用して、
slackの特定チャンネルに投げるとmattermostにも流れるようにできないか、とチャレンジしてみた。
使用言語はpythonだが、超初心者なのでその辺はご勘弁ください。
構成(イメージ)
- slack
- 無料範囲で利用
- mattermost連携用の専用チャンネルを作る
- botを登録し、専用チャンネルに紐付ける
 
- mattermost
- 5.1.0 on CentOS7(たぶんどのバージョンでも動く)
- slackから投げられる専用チャンネルを作る
- Incomming-Webhookを登録
 
- mattermostにhookできるIPを持ったサーバ上のツール
- Python3.6.4 on CentOS7(python2系使うならurllib3まわりを修正する必要アリ)
- 面倒なのでmattermostサーバに同居させた
- INは絞られていてもかまわないが、OUTはslackへの到達性が必要なので、注意
 
- slackに情報を取りに行く
- その情報をmattermostに投げる
- 常時起動しておきたいので、systemdに登録
 
- Python3.6.4 on CentOS7(python2系使うならurllib3まわりを修正する必要アリ)
slackにBotを登録 & mattermostにwebhookを登録する
webでもqiitaでもいい記事がいっぱいあるので、ここでは、割愛。
slackからとってくるClass
zukaさんのブログを参考に、python-slackclientを利用。
ちょっと古いバージョン(1.2.1)でよければ、githubからとって来なくとも普通にpip install slackclientでとってこれた。
import time
from slackclient import SlackClient
slack_token        = "slackで発行したBotのtoken"
class SlackReader:
    def __init__(self):
        self.slack = SlackClient(slack_token)
        if self.slack.rtm_connect():
            while True:
                data = self.slack.rtm_read()
                if len(data) > 0:
                    for item in data:
                        if item["type"] == "message":
                            ### mattermostに投げる処理
                    continue
                else:
                    time.sleep(30)
        else:
            print("Connection Failed")
古いバージョンなら普通にインストール可能、と先に書いておきながら、
実は古いバージョンにはrtm_readした際にメッセージを1個しかとってきてくれないバグがある。
そのため、メッセージ取得に成功した場合は、sleepせずにcontinueで再度取得している。
mattermostに投げるClass
これは、以前に自分で適当に作ったclassを少しいじって流用。
urllib3で所定のフォーマットで投げるだけ。
import json
import requests
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
mattermost_url     = "mattermost-hookのURL"
mattermost_channel = "投げたいチャンネル名"
mattermost_iconurl = "/static/emoji/1f43c.png" # パンダ
class MattermostSender:
    def __init__(self, message_data):
        self.post_data = {
            "channel"  : mattermost_channel,
            "icon_url" : mattermost_iconurl,
            "username" : message_data["user"],
            "text"     : message_data["text"]
        }
    
    def send(self):
        headers = {'content-type': 'application/json;charset=UTF-8'}
        try:
            r = requests.post(mattermost_url, headers=headers, data=json.dumps(self.post_data), verify=False)
        except Exception as curl_error:
            print(str(curl_error))
            sys.exit()
InsecureRequestWarningしているのは、検証環境がオレオレのため。
正規証明書の環境でだけ動かすなら、もちろん消す方が良い。
systemdで起動
上記を組み合わせた一枚モノのpythonコード(slack-feed.py:仮称)を、あとはsystemdで起動すればOK
以下はとりあえず動かす用でだいぶ適当な例
[Unit]
Description=slack-feed
After=syslog.target network.target
[Service]
Type=simple
User=root
Group=root
ExecStart=/path/to/slack-feed.py
PrivateTmp=yes
Restart=no
RestartSec=30
[Install]
WantedBy=multi-user.target
一応、動きはした
改善点はあるものの、一応到達はした。
これで自分用のスマホからでも、会社に「ちょっと体調悪いことにして休みます〜」とメッセージが投げれるようになった!
よし、休もう!
- 改善点
- slackのuserはよくわからんIDになっているので、見てわかるユーザー名に変えてやる必要がある
- そのついでに、アイコンがパンダ固定なのもなんとかしたいところ
- slackサーバ側の負荷を考慮して同期間隔を30秒にしたが、もう少し短くても大丈夫かも
- そもそもslackをメインの運用にしてればこんなことしなくて済んだのに。
 
- slackの
slack-feed.py全貌
説明の都合上小分けにしたので、最後にあらためてpythonファイルの全貌を。
# !/path/to/python
# -*- coding: utf-8 -*-
import sys,os
import re
### slackからとってくるのに使用
import time
from slackclient import SlackClient
### mattermostに送るのに使用
import json
import requests
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
### 設定
mattermost_url     = "https://mattermost/hooks/hogehoge"
mattermost_channel = "slack-channel"
mattermost_iconurl = "/static/emoji/1f43c.png"
slack_token        = "hogepiyo"
### mattermostにメッセージ送信するクラス
class MattermostSender:
    ### init:送信データを辞書形式で作成
    def __init__(self, message_data):
        self.post_data = {
            "channel"  : mattermost_channel,
            "icon_url" : mattermost_iconurl,
            "username" : message_data["user"],
            "text"     : message_data["text"]
        }
    
    ### データをmattermostに送信する
    def send(self):
        headers = {'content-type': 'application/json;charset=UTF-8'}
        try:
            r = requests.post(mattermost_url, headers=headers, data=json.dumps(self.post_data), verify=False)
        except Exception as curl_error:
            print(str(curl_error))
            sys.exit()
        
class SlackReader:
    def __init__(self):
        self.slack = SlackClient(slack_token)
        if self.slack.rtm_connect():
            while True:
                data = self.slack.rtm_read()
                if len(data) > 0:
                    for item in data:
                        if item["type"] == "message":
                            MattermostSender(item).send()
                    continue # slackclientのバグで一個しかとってこない(issue #231)ので、再度投げる
                else:
                    time.sleep(30)
        else:
            print("Connection Failed")
 
###### MAIN SCRIPT ######
if __name__ == '__main__':
    SlackReader()