やりたかったこと
飲みすぎた・・・明日もう会社やすみたい・・・
でも、その手の連絡は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()