この記事は Willgate Advent Calendar 2019 の15日目の記事です。
昨日は @ryota_hnk さんの OKE(Oracle Container Engine for Kubernetes) を Rancher で監視する
でした。
本日は、ウィルゲートでPHPやJavaScript、CSSで開発している@N06ARです!
よろしくお願いします!
はじめに
私は普段の業務でSwiftを書くことがありませんが、最近で昔作ったiOSアプリをSwift1
からSwift5
にジャンプアップデートする機会がありました。
久しぶりのSwiftが楽しかったので、SlackKitを使いSlackBotを作ってみました。
そこでこの記事では、SlackKitを使ったSlackBotの導入をご紹介します!
ここで書かれていること
この記事では次の3点について書いています。
- SlackKitの導入方法
- SlackKitを使ったSlackでのメッセージの送受信
- SlackKitで作ったSlackBotを使ってみての感想x
この3点で、私が失敗や上手く行かなかったところは()を書いています。
もし、この記事を読んで作ろう!と思った方は、参考にしてみてください!
開発環境
開発環境は、次のバージョンで開発しました。
バージョン | |
---|---|
OS | macOS Catalina |
Swift | 5.1 |
SlackKit | 4.5.0 |
Xcode | 11 |
0.事前準備
コードを書き始める前に、事前に必要なものを準備します。
0-1.SlackでAPI Tokenの発行
はじめにSlackからAPI Tokenを取得してきます。
このURLからAPI Tokenを発行します。
Botの名前を入力し、botを追加するボタンを押すと次の画像が一部として表示される画面が表示されます。
画像のところにAPI Tokenがあるのでメモしておいてください。
(画像のTokenはダミーです)
0-2.(オプション)LibreSSLのインストール
SlackKitの依存パッケージの中にSwiftNIOがあり、LibreSSLが必要になります。
インストールしていなければ1-5.ビルドでビルドに失敗するのでインストールします。()
$ brew install libressl
1.SwiftPMの設定
SwiftPMを使いSwiftパッケージディレクトリ(プロジェクトフォルダ)を作成し、SlackKit
を使えるようにします。
1-1.SlackKitの実行パッケージディレクトリの作成
次のコマンドを実行して、SlackBotを作るディレクトリを作成し、実行パッケージディレクトリを作成します。
swift package init --type executable
を実行した時に特に入力を求められませんが、問題ありません。
$ mkdir 【プロジェクト名】
$ cd 【プロジェクト名】
$ swift package init --type executable
1-2.(オプション)実行確認
次のコマンドを実行した後 Hello, world!
と表示されたら準備完了です!
$ swift build
$ ./.build/debug/【プロジェクト名】
1-3.SwiftPMで使用するパッケージにSlackKitを追加
次にPackage.swift
を編集します。
Package.swift
をエディタで開きdependencies
の箇所に今回使用するパッケージのSlackKit
を追加します。
import PackageDescription
let package = Package(
name: "【プロジェクト名】",
dependencies: [
.package(url: "https://github.com/pvzig/SlackKit.git", .upToNextMinor(from: "4.5.0"))
]
)
##1-4. (オプション)テストもしたい場合
テストもしたい場合は、targets
にもSlackKitのパッケージを追加する必要があります。
SlackKitの追加を忘れると1-5.ビルドでビルドに失敗します。()
import PackageDescription
let package = Package(
name: "【プロジェクト名】",
dependencies: [
.package(url: "https://github.com/pvzig/SlackKit.git", .upToNextMinor(from: "4.5.0"))
],
targets: [
.target(
name: "【プロジェクト名】",
dependencies: ["SlackKit"]),
.testTarget(
name: "【プロジェクト名】Tests",
dependencies: ["【プロジェクト名】","SlackKit"]),
]
)
1-5.ビルド
Package.swiftを編集した後に、 swift build
をするとSlackKitを取得し使えるようになります。
$ swift build
1-6.(オプション)XcodeやAppCodeを使う場合
XcodeやAppCodeなどのエディタを使いたくなると思います。その時は次のコマンドを実行して.xcodeproj
を生成してください。
$ swift package generate-xcodeproj
ただし、Package.swiftにパッケージを追加変更する度に、.xcodeproj
を再生成する必要があります。()
AppCodeで発生しましたが、プロジェクト内のファイル名を変更したときビルドエラーになりました。
その時も再生成してあげると良さそうです。()
2.SlackBotのコードを書く
ここから実際にSlackKitを使ってSlackbotのコードを書いていきます。
2-1.main.swiftの編集
まずはじめに、今回SwiftPMで作成した のエントリーポイントのmain.swift
を編集します。
今回は、SlackBotの機能をmain.swift
に書かずSlackBot
クラスを作成し分離します。
SlackBot
のインスタンス生成時に、[0-1.SlackでAPI Tokenの発行](#0-1.SlackでAPI Tokenの発行) で準備したAPI Tokenを引数に入れます。
import Foundation
SlackBot(apiToken: "xoxb-XXXXXXXXXXXXXXXXXXXX")
RunLoop.main.run()
2-3.SlackBotのクラスを作成
次に、SlackBot
クラス用のファイルを作成し、コードを作成していきます。
コンストラクタ(init
)で、API Tokenを受け取れるようにして、
SlackKitでSlack Web API
とSlack Real Time Messaging API
使えるようにするためにAPI Token
を渡します。
API Token
を渡すには、addWebAPIAccessWithToken
とaddRTMBotWithAPIToken
を使用します。
import Foundation
import SlackKit
class SlackBot {
/// SlackBotのKit
let slackkit = SlackKit()
init(apiToken: String) {
slackkit.addWebAPIAccessWithToken(apiToken)
slackkit.addRTMBotWithAPIToken(apiToken)
}
}
2-4.メッセージを受け取る
次にSlackからメッセージを受け取ります。
メッセージは、SlackKitのnotificationForEvent
を使い受け取ります。
受け取ったメッセージの内容は、クロージャのevent.message
に入っており、
event.message
には発言したユーザIDやチャンネルID、スレッドID、メッセージの文章等が入っています。
import Foundation
import SlackKit
class Slackbot {
/// SlackBotのKit
let slackkit = SlackKit()
init(apiToken: String) {
slackkit.addWebAPIAccessWithToken(apiToken)
slackkit.addRTMBotWithAPIToken(apiToken)
slackkit.notificationForEvent(.message) { [weak self] (event, client) in
// ここに聞いたメッセージを受信したときのメッセージを書く!
}
}
}
【余談】SlackKitのサンプルコードのように書くときの注意
[サンプルコードのこの箇所]
(https://github.com/pvzig/SlackKit/blob/master/Examples/Leaderboard/Leaderboard/Sources/Leaderbot.swift#L70)のように、実装した場合でAPI Tokenを渡すと
listen
メソッド実行時に、client
クラスが生成されないためかnil
のためguard
構文でreturn
されるので何もせずに終わります。()
import Foundation
import SlackKit
class SlackBot {
/// SlackBotのKit
let slackkit = SlackKit()
init(apiToken: String) {
slackkit.addWebAPIAccessWithToken(apiToken)
slackkit.addRTMBotWithAPIToken(apiToken)
slackkit.notificationForEvent(.message) { [weak self] (event, client) in
self?.listen(client?.client, message: event.message)
}
}
private func listen(_ client: Client?, message: Message?) {
guard let client = client,
let message = message,
let text = message.text,
let channel = message.channel
else {
return
}
}
}
2-5.メッセージを送信する
メッセージを受け取れるようになったので、今度はメッセージを送れるようにします。
今回は、全てのメッセージに大してこんにちは
と返事をするメッセージを送信する処理を書きます。
メッセージの送信には、SlackKit
のWebAPI
クラスのsendMessage
メソッドを使用します。
sendMessage
メソッドの引数には、送信するチャンネル
、表示するユーザネーム
、アイコン画像のURL
、メッセージ
を入れてください。
import Foundation
import SlackKit
class SlackBot {
/// SlackBotのKit
let slackkit = SlackKit()
init(apiToken: String) {
slackkit.addWebAPIAccessWithToken(apiToken)
slackkit.addRTMBotWithAPIToken(apiToken)
slackkit.notificationForEvent(.message) { [weak self] (event, client) in
// ここに聞いたメッセージを受信したときのメッセージを書く!
slackkit.webAPI?.sendMessage(
channel: message.channel,
text: "こんにちは",
username: "【botの名前】",
iconURL: "【アイコン画像のURL】",
success: nil,
failure: { (error) in
print("メッセージ送信失敗:\(error)")
}
)
}
}
}
これでbotが入ったチャンネルか、DMでメッセージを送った時にこんにちは
と返事するようになりました!
メッセージ以外にもAttachmentクラスを使いAttachmentをつけることもできます。
【余談】アイコン画像とBotの名前がない場合
実のところ表示するユーザネーム
とアイコン画像のURL
は、必須ではないので引数に入れず送信することができます。
仮に引数なしで送信をした場合は、次の画像の結果になります。
アイコン画像がbotの画像
になり、名前がbot
になります。()
そのため、名前と画像のURLを渡たしてあげる必要があります。
2-6.起動!!
最後ビルドして起動します。
起動には、デバックモードの起動とリリースモードの起動があります。
2-6-1.デバックモードで起動!
ビルドは今まで通り次のコマンドを入力してください。
$ swift build
最後に次のコマンドで起動します!
コマンド実行するとシェルの入力はできなくなるので、nohup
を使いバックグラウンド実行すると良いです。
.build/debug/【プロジェクト名】
2-6-2.リリースモードで起動!
ビルドコマンドは今までとは少し異なります。
$ swift build -c release
最後に次のコマンドで起動します!
.build/release/【プロジェクト名】
3.macOS以外で起動したい!
SlackBotを動かすのにLinux
やサービスを使って動かしたいことがあると思います。
そこで、Heroku
とDocker
で動作させる方法をご紹介します。
3-1.Herokuにデプロイしたい場合
Herokuにデプロイする場合は、HerokuCLI
を使用します。
そのため、次のコマンドでHerokuCLIをインストールしてください。
brew tap heroku/brew && brew install heroku
3-1-1.Heroku用設定ファイルの追加
はじめに、Heroku用の設定ファイルを追加します。
次のコマンドを実行して、.swift-version
とProcfile
を作成してください。
$ echo 5.1 > .swift-version
$ echo slackbot: .swift-bin/【プロジェクト名】 > Procfile
Herokuでデプロイするために、heroku-buildpack-swiftを使用します。
heroku-buildpack-swiftでは、ビルドしたときのディレクトリ名は.build
ではなく.swift-bin
なので気をつけてください。()
3-1-2.Herokuの設定
次にHerokuCLIでアカウントにログインし、Herokuにアプリケーションを作成します。
アプリケーション作成時に、heroku-buildpack-swiftをbuildpackとして追加します。
$ heroku login
$ heroku create --buildpack kyle/swift 【プロジェクト名】
3-1-3.デプロイ
Herokuに、コードをpush
すればheroku側でデプロイが開始されます。
$ git push heroku master
起動するには、Herokuの画面からslackbot
のDynos
をオンにします。
3-2.Dockerのコンテナにデプロイしたい場合
次は、Docker
でSlackBotを起動する方法をご紹介します。
3-2-1.Dockerfileを書く
まず、プロジェクトのディレクトリに次のDockerfile
を追加してください。
(他のディレクトリにDockerfile
を置く場合はCOPYの第1引数を修正してください。)
libssl-dev
をインストールしていますが、これはSwiftNIOで必要となるためです。
libssl-dev
をインストールしないとbuildできません。()
FROM swift:5.1
RUN chmod -R o+r /usr/lib/swift/CoreFoundation && \
apt-get update && \
apt-get install -y libssl-dev
COPY . /【プロジェクト名】
WORKDIR /【プロジェクト名】
RUN swift build -c release
CMD [ "/【プロジェクト名】/.build/release/【プロジェクト名】"]
3-2-2.コンテナのビルド
次のコマンドを実行し、Dockerfileを元にビルドします。
docker build . -t slackbot
3-2-3.起動!
最後に、次のコマンドを実行してコンテナを立ち上げてください。
docker run --detach -t slackbot
これで、DockerでSlackBotの起動できます。
4.SwiftとSwiftPM、SlackKitのよかったところ、困ったところ
今回SwiftでSlackBot作りました、作っていて次のよかったところと困ったところがありました。
良かったところ
- macOSなら開発環境がすぐにできる
- sendMessegeメソッド
macOSならSwiftの開発環境が簡単にできる
さすが開発元がAppleなだけあり、さえXcodeをインストールすればSwiftの開発環境はおおよそ揃えられました。
SlackKitでは追加でライブラリを一つインストールする必要がありましたが
また、Xcode11からはSwiftPMのパッケージを管理できるようになりました。
sendMessegeメソッド
余談でも書きましたが、SlackKitのWebAPIを使うとSlackBotの名前とアイコン画像を変更することができます。
細かい仕様を調ベていないので詳細は分かりませんが、昔Node.js
のBotKit
でSlackBotを作った時はなかった仕様でした。
これを逆手にとると、画像のようにメッセージごとに名前とアイコン画像を切り替えることができます。
これが不具合なのか、いつ修正されるか分かりませんが、アイコンを切り替えて感情豊かなBotを作れそうです。
困ったところ
- Swiftのパッケージの大半がiOSのパッケージ
- Herokuやbitbucket-pipelineなどがSwiftに公式で対応していない
- 記事などの知見が少なく古い
Swiftのパッケージの大半がiOSのパッケージ
Googleカレンダーを使った機能を実装したいときに、使えそうなパッケージがないか調べるとiOSでしか使えないパッケージしかない等のことが多かったです。
使ったことがないので正確なことは言えませんが、Vaporのパッケージは色々揃っているので使うと楽になりそうです。
Herokuやbitbucket-pipelineなどがSwiftに公式で対応していない
AttrasianやHerokuなどのWebサイトを見てみるとわかりますが、自動デプロイに公式で対応してません。
そのため、Docker
で対処するか、すでにあるライブラリやパッケージ等を頼りに対応する必要がありました。
記事などの知見が少なく古い
良くありがちなことですが、今では使用できないものが書かれている記事が多かったです。
主にSlackKitを導入するにあたり、資料が古くSwiftやSwiftPMのバージョンアップにより上手く動作しないことがありました。
特にlibressl
関連とHeroku
、Docker
は、問題が解決するより、SlackBotの作成時間の方が短かったです。
今回作ったSlackBot
今回作ったSlackBot私の家にいる なな(猫)を模したBotをつくりました。
ななちゃんBotは、私の分報チャンネルに生息しています。
Botができること
ななちゃんbotには、次の機能を実装しました。
- メッセージにあるキーワード(ななちゃん)があるとリアクションする
- 勤怠の打刻をリマインドとリンクの表示
- GoogleMapでランチまたは、居酒屋を検索したURLの表示
- ユーザを祝ったり応援する機能
残念ながら二つ目の勤怠の打刻の通知以外はあまり使われていませんが、要望や個人的に欲しかった機能を実装しました。
SlackBotを分報に入れて気がついたこと
よかったこと
Botにキャラクターを持たせること
今回たまたま思いつきでしたが、家のなな(猫)をBotにしたことは正解でした。
ななちゃんBotの最初期は、個人のSlackチームで作っていたことや、SlackKitが手探りだったこと、環境が出来上がっていなかったこともありバグが多かったです。
それに、確認できる環境がSlack上のため、本番で動作確認をしている状態でした。
この時はバグを発見したら極力すぐに直すようにしていました。
ですが、このバグに関してSlackでは次のような反応があり、バグがあっても良いのではという意見がありました
この件について社内の同僚から、ご紹介いただいたこの記事に次のことが書かれていました。
バグは極力直さない
バカな子ほど可愛いという諺がある
怒られにくくするには愛着がわきやすい Bot を作るのが大切ということです
読んでみて確かにと思いました。
記事の通り愛着が沸きやすいマスコットとして作った方が周囲から可愛がられ、今後社内で広く使うには良い方法だと感じました。
また、Botで遊んでいるのを見ていると開発のモチベーションも上がるため、偶然でしたが猫のBotとして作ったのは正解だったと思いました。
失敗したこと
デプロイ方法を見つけるまでローカルで起動していた
今回HerokuとDockerでの起動方法をご紹介しましたが、先月ではまだできないでいて、
最初は私のローカル(MacBook Pro)で動作させていていました。
ただ、ローカルで動作していると、Sleepするとき
や会議の移動でネットワークが一時的に切断されたとき
に、オフラインになり使えなくなる状態ができ不便に思うことが増えました。
また、その度に再起動していたのですが、何度もやっていると流石に面倒でした。
そのためMac以外で常時起動できる方法を探しHerokuとDockerに行き着きました。
最後に
今回SlackBotをSwiftで作ることで、Swiftのverison1から久しぶりにがっつりコードをかきました。
Version1のころと比較して、結構文法が変わっていましたが変わらないところは変わっていなかったのですんなり使えるようになりました。
また今回初めて技術記事を書きましたが、こんなに文章を書くのが難しいとは思いませんでした。
普段から情報を発信している方々の凄さに驚きました。
最後になりますが、もしこの記事の内容で誤り等ありましたら、お教えいただけますと幸いです。
参考・参照文献
- 【SwiftPM】SwiftでもSlack Botを作りたい
- SlackKit
- Building Slack Bots In Swift
- heroku-buildpack-swift
- Update to Swift 5.0.1 breaks builds on Ubuntu 18/04
- Slack で役に立たない Bot を運用するときの知見とか
明日は@8845musignさんです!
よろしくお願いします!