DockerでSkypeを起動してSlackにメッセージを転送するまで


# 背景

オフィス内で一時的に作ったSkypeメッセージをSlackへ投げつけるプログラムが、（人為的な）停電の度に落ちるのでそろそろコンテナ化してオフィス外に出そうかと思う。

# 現状

Xvfbに対してSkypeが立ち上がりDBus経由でSkype2Pyのスクリプトがメッセージを抜き取ってSlack APIへ転送する仕組み。

# 準備

久々のboot2dockerなのでupgradeからスタート

```
$ boot2docker upgrade
Backing up existing docker binary...
Downloading new docker client binary...
Success: downloaded https://get.docker.com/builds/Darwin/x86_64/docker-latest
	to /usr/local/bin/docker
	The old version is backed up to ~/.boot2docker.
Backing up existing boot2docker binary...
Downloading new boot2docker client binary...
Success: downloaded https://github.com/boot2docker/boot2docker-cli/releases/download/v1.5.0/boot2docker-v1.5.0-darwin-amd64
	to /usr/local/bin/boot2docker
	The old version is backed up to ~/.boot2docker.
Latest release for boot2docker/boot2docker is v1.5.0
Downloading boot2docker ISO image...
Success: downloaded https://github.com/boot2docker/boot2docker/releases/download/v1.5.0/boot2docker.iso
	to /Users/hac/.boot2docker/boot2docker.iso
```

1.5になった。

Slack側にSkypeからのメッセージを受け付けるチャンネルの作成と、APIのトークン発行を行っておく。

# Dockerfileを書く

```
$ mkdir skype2slack
$ cd skype2slack
$ nano Dockerfile
```

極度のeeかnanoファンである

```Dockerfile.
FROM debian:stable
# debconfにnon-interactiveモードであることを教える
ENV DEBIAN_FRONTEND noninteractive
# i386アーキテクチャのものも追加する
RUN dpkg --add-architecture i386
# 必要なパッケージをまずは導入
RUN apt-get update
RUN apt-get -y install x11vnc xvfb fonts-takao openbox wget

# Skypeをダウンロード
RUN wget http://download.skype.com/linux/skype-debian_4.3.0.37-1_i386.deb
# 依存性を無視してSkypeをとりあえずインストール
RUN dpkg -i skype-debian_4.3.0.37-1_i386.deb || true
# 不足している依存パッケージのインストール
RUN apt-get -fy install

# Skype4Pyとpyslackを準備
RUN apt-get -y install python python-pip git
RUN pip install Skype4Py
RUN pip install requests
RUN pip install pyslack
# skype2slackスクリプトを配置
ADD skype2slack.py /
RUN mv /skype2slack.py /root

# x11vncにパスワードを設定
RUN mkdir /root/.vnc
RUN x11vnc -storepasswd [パスワード] /root/.vnc/passwd

# タイムゾーンとロケールを設定
RUN localedef -v -c -i ja_JP -f UTF-8 ja_JP.UTF-8 || :
RUN echo "Asia/Tokyo" > /etc/timezone
RUN dpkg-reconfigure -f noninteractive tzdata

# 起動スクリプトを押し込める
ADD startup.sh /
RUN mv startup.sh /root

ENTRYPOINT ["/bin/bash"]
CMD ["/root/startup.sh"]

EXPOSE 5900
```

起動スクリプトはこちら

```startup.sh
#/bin/bash
rm -f /tmp/.X1-lock
rm -fr /tmp/.X11*
rm -fr /tmp/*
rm -fr /root/.Xauthority
# なぜか自分のMacでは数秒待たないと消えない
sync; sync; sync;
sleep 3s
# 他の環境では問題なかった
Xvfb :1 -extension GLX -screen 0 1024x768x24 &
x11vnc -display :1 -bg -usepw -forever
DISPLAY=:1 /usr/bin/openbox-session &
DISPLAY=:1 XAUTHORITY=/root/.Xauthority python /root/skype2slack.py &
DISPLAY=:1 skype
```

Skypeは結構ウインドウをバカスカ開いてユーザーに確認をさせる場面があり枠がついてないとそのウインドウを消すことも移動することも出来なくなる。なのでウインドウマネージャとしてopenboxを入れてみることにした。

あとロケールについてはUTF-8のマップねえよ！と怒られるがみなさんなぜか入れているようなので真似しただけ。。。

もし利用する方いらっしゃれば x11vncのパスワードをお好きに決めて下さいな。

# skype2slack.pyを配置

そして、Dockerfileと同じディレクトリにskype2slack.pyを配置する

```skype2slack.py
#coding: utf-8
import os
import time
import requests
import Skype4Py
import slack.chat

def handler(msg, event):
    slack.api_token = os.environ["SLACK_TOKEN"]
    channel = os.environ["SLACK_CHANNEL"]
    print "Event %s: %s" % (event, msg)
    if (event == u"RECEIVED") or (event == u"SENT"):
            slack.chat.post_message(channel, msg.Body, username=msg.FromDisplayName)

def main():
    time.sleep(10)
    skype = Skype4Py.Skype(Transport='x11')
    skype.OnMessageStatus = handler
    skype.Attach()
    while True:
        time.sleep(1)

requests.packages.urllib3.disable_warnings()
if __name__ == "__main__":
    main()
```

まずはsleepで10秒待ちが入る。これはSkypeより先にこのスクリプトを起動させているから。
そののち"RECEIVED"と"SENT"イベントの場合にSlackにメッセージを投げつける。

Slack API用のトークン(SLACK_TOKEN)とチャンネル(SLACK_CHANNEL)はコンテナ起動時の環境変数として受け渡しを期待している。

> `requests.packages.urllib3.disable_warnings()`を呼んでいる箇所だが、pyslackモジュールが内部でrequestsを使っていて、通信中常時Warningを吐きまくるのを抑制している

# イメージをビルドする

```
$ docker build -t skype2slack:latest .
```

長い・・・長すぎるよ
タバコが３本とコーヒーもいける

# まずは1回目の起動

```
$ docker run -p 5900:5900 -e SLACK_TOKEN="xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXXXX" -e SLACK_CHANNEL="#skype-proxy" skype2slack:latest bash /root/startup.sh
```

トークンとチャンネルを環境変数で渡して起動する

```
$ boot2docker ip
192.168.59.103
```

早速、Macから vnc:://192.168.59.103 で接続すると先のDockerfileで指定したパスワードを求められるので入力すると？

![スクリーンショット 2015-02-15 21.58.50.png](https://qiita-image-store.s3.amazonaws.com/0/36701/11e37110-f53e-cb47-33aa-6457a6ab9667.png "スクリーンショット 2015-02-15 21.58.50.png")

うまく起動しているようだ。

![スクリーンショット 2015-02-15 21.59.51.png](https://qiita-image-store.s3.amazonaws.com/0/36701/b0a6e128-c3ee-532f-8b26-94fc318ab356.png "スクリーンショット 2015-02-15 21.59.51.png")

Skypeアカウントにサインインするにあたり、自動ログインをチェックするのを忘れずに。

![スクリーンショット 2015-02-15 22.02.22.png](https://qiita-image-store.s3.amazonaws.com/0/36701/df3e9016-86b3-1505-0f32-a778bb374716.png "スクリーンショット 2015-02-15 22.02.22.png")

この状態まで進めることが出来たら、まずコンテナを止めてイメージをコミットしておく。

Skypeの認証情報の保存が目的だ。

```
$ docker ps
CONTAINER ID        IMAGE                COMMAND                CREATED             STATUS              PORTS                    NAMES
930ce7c5f3d7        skype2slack:latest   "bash /root/startup.   4 minutes ago       Up 4 minutes        0.0.0.0:5900->5900/tcp   pensive_galileo
```

```
$ docker stop 930ce7c5f3d7
930ce7c5f3d7
```

```
$ docker commit 930ce7c5f3d7 skype2slack-run:latest
033784851a53c77f49d4ecffdebf7a087cd54c7f6c152f0f37d1c7661e0df493
```

これで skype2slack-run:latest という名前でコミット出来た。

# ２回目の起動

先ほどコミットした skype2slack-run:latest を起動する。

```
$ docker run -p 5900:5900 -e SLACK_TOKEN="xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXXXX" -e SLACK_CHANNEL="#skype-proxy" skype2slack-run:latest /bin/bash /root/startup.sh
```

この２回目の起動では Skypeに対してskype2slack.pyが接続を要求してくるはずだ。

「この選択を記憶する」にチェックを入れて「はい」を選択する。

![スクリーンショット 2015-02-15 23.55.20.png](https://qiita-image-store.s3.amazonaws.com/0/36701/cc697d1a-1824-4a36-bceb-743f01fe8b1c.png "スクリーンショット 2015-02-15 23.55.20.png")

以下の画面からSkype4PyがSkypeと繋がったことを確認できる。

![スクリーンショット 2015-02-15 23.55.48.png](https://qiita-image-store.s3.amazonaws.com/0/36701/e098cbd1-6cd8-a0d7-d771-9c373ce81032.png "スクリーンショット 2015-02-15 23.55.48.png")

さぁ、またここまでの状況をコミットしておこう。

```
$ docker ps
CONTAINER ID        IMAGE                    COMMAND                CREATED              STATUS              PORTS                    NAMES
5ceb3f3b96ab        skype2slack-run:latest   "/bin/bash /root/   About a minute ago   Up About a minute   0.0.0.0:5900->5900/tcp   trusting_mcclintock
```

```
$ docker stop 5ceb3f3b96ab
5ceb3f3b96ab
```

```
$ docker commit 5ceb3f3b96ab skype2slack-run:latest
101643e8d66983e2dd2c22481a10aca822fed9d74a105b9bf2b520f856e66809
```

# 本番稼働

```
$ docker run --rm -p 5900:5900 -e SLACK_TOKEN="xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXXXX" -e SLACK_CHANNEL="#skype-proxy" skype2slack-run:latest /bin/bash /root/startup.sh
```

長かった。
メッセージをSkypeアカウントに送りつけると自動的にSlackのチャンネルに転送されるようになった。
Slack側のアイコン等もSkype的にすると混乱しなくて宜しいかも。

ちなみに動いている最中のstatsはこんな感じ。

```
CONTAINER           CPU %               MEM USAGE/LIMIT       MEM %               NET I/O
93664c636361        11.19%              121.4 MiB/1.961 GiB   6.05%               359.3 KiB/718.5 KiB
```

思ったよりヘビーだなぁ。


# 最後に

今回、DockerでSkypeをやってみたけどもGUIでしか設定出来ない部分はどうしようも無さそうで運用対応としてしまったのは心残りだ。

あと理由は分からないが、起動スクリプトの先頭にあるrmコマンドがうまく動かない場合があるのか、起動したりしなかったりする。
ロックファイルが消えないとXが立ち上がらずエラーになってしまうのだがこれはboot2dockerだけの問題なのだろうか？
(多い時は20回くらい失敗して起動)
と思って社長のMacbook Air(Docker 1.3)でやってる分にはこの問題は出ないので自分の環境だけだと思われる。

そして、このskype2slackスクリプトでは全てのメッセージを拾いきれて無いらしい（社内談）
ログ的にどんなイベントが拾えてないのか分かってないのでこれもまた判明次第記事更新しようと思う。
hubot側にも流してみればメッセージの違いがわかったりするかもしれない。