どうも,暴言厨イルカです
・・・・・・ごめんなさいふざけました(罪悪感に耐えきれなくなった顔) 54代のみゅーです.どうやらこの人,会計とかいう大役を担っているらしいです.大変そうですね,温かい目で見守ってあげましょう.
いつも僕はMISのブログで映像関連の話しかしてなかったんですけど,せっかく ♰ 情 報 理 工 学 科 特 別 幼 稚 園 組 ♰ (情報理工学幼稚園とか言ってたけど正式名称はこれらしい)にいるのでたまにはプログラミングの話でもします.
Slackに突如出現した謎の刺客…
あれは2か月ほど前のことだろうか… 突如Slackにこんな奴が現れた!!!
「お前どこから来たんだ?!?!?」
ってわけで,こんな感じのリマインドbotを作ったんですよね~.え?Slackには標準で /remind
コマンドがあるって??そんなことは知らん!
まあぶっちゃけ標準のリマインダーでもいいんですが,手打ちで設定するのがめんどくさいのと,設定時に打ったものが確か見えちゃうんですよね.それでこいつを作るに至りました.
設計(適当)
僕は業務でプログラムを書いたことはない(VBA()は??)ので,設計とかから考えて物を作ったことが無く,こういう細かいことができないんですよ… とりあえずこんなメモだけ発掘しました.
…………なんですかこれ?ゴミですか? 書く手間を省こうとしたらこれだよ…(お前が悪い)
てことで一から説明しますね.
結局実装した機能
- 活動時間をリマインドするメッセージの送信(Slack & Discord)
- 上記メッセージを定期的に設定する機能 & その他の通知も送れるように
- 設定できる人間を幹部陣に絞る(誰でも変えられたら困るので)
- 特定のチャンネルをDiscordに垂れ流す
単純に指定の日時に送るだけなら適当なプログラムを適当なPCで常時稼働させておけばいいんですが,時間変更等にフレキシブルに対応するにはそうはいかないですね.
それに通知の設定をどこからやるかも問題になります.最初はブラウザからやろうかと思いましたが,せっかくSlackにはSlashコマンドとInteractive Componentとかいうものがあるので,こいつの恩恵を受けることにしました.
このSlashコマンドとかを使うには,まずWebhookのエンドポイントが必要になります.簡単に言えば,適当なサーバーを用意してHTTPリクエストを受け付けろ!ってことです.また,設定した日時等を保存しておくにはデータベースも要りますね.
使ったもの
これらを踏まえて,以下のものを採用しました.
- Heroku
- フリーで用意できる環境としては定番
- MySQL (ClearDB)
- な ん と な く Heroku標準がPostgreなのを忘れてMySQLで docker-compose.yml 書いちゃったんで止む無く(書き直せよ)…
- Ruby on Rails
- Heroku Scheduler
- Railsで常時起動プログラムはキツいので,Heroku Schedulerに定期的にTaskを投げさせる.
- Docker
- デプロイが楽?になるのかな…(にわかなのでよくわかってません)あと複数台のPCを所有してるので,環境差が出ないように開発したかったってのもあります.
最終的な構成
簡単に描くと,こんな感じで動いてます.
さあ開発だぁ!!!
の前に開発環境だけメモメモ
OSはManjaro Linuxでやってました.
開発に使うソフトは?
とりあえず適当なテキストエディタがあればいいです.僕は全力でNeoVimを推します.Vimはいいゾ
手懐けていくとVimはこんなにカラフルになります.
ぶっちゃけた話,とりあえずVSCodeを使っとくのが安牌です.
Docker環境の構築
一番詰まったんでここだけ細かく書いときます.なんか情報が錯綜してて何が正解かわからなかった.
以下の4ファイルを作ります.
[docker-compose.yml]
version: '3' services: web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/remind-bot-api ports: - "3000:3000" depends_on: - db stdin_open: true tty: true db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: password ports: - '3316:3306' command: --default-authentication-plugin=mysql_native_password volumes: - mysql_vol:/var/lib/mysql volumes: mysql_vol: driver: local
[Dockerfile]
FROM ruby:2.7.2 ENV LANG C.UTF-8 RUN apt update -qq && apt install -y build-essential mariadb-client WORKDIR /remind-bot-api COPY Gemfile /remind-bot-api/Gemfile COPY Gemfile.lock /remind-bot-api/Gemfile.lock RUN bundle install COPY . /remind-bot-api COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 CMD ["rails", "server", "-b", "0.0.0.0"]
[entrypoint.sh]
#!/bin/bash set -e rm -f /myapp/tmp/pids/server.pid exec "$@"
[Gemfile]
source 'https://rubygems.org' gem 'rails', '6.1.1'
docker-compose.yml
のdb
のports
なんですが,ローカルで使用してるポートと被るとダメなんで,適宜変えてください.ローカルでMariaDBとか動かしてると高確率で3306は被ります.
空のGemfile.lock
も生成しておきます.
$ touch Gemfile.lock
ここまで来たら,rails new
します.Docker使ってるのでコマンドはクソ長です.(記憶があいまいすぎて引数があってる気がしない…)
$ docker-compose run app rails new --api . --force --no-deps --database=mysql
今回,MVCでいうViewがいらないので,--api
でAPIモードにしてます.これで余計なファイルが減ります.またデータベースはMySQLを使うので--database=mysql
としてます.
そうすると,大量のRailsファイルが生成されます.ここまで来たらDockerイメージをビルドしたいんですが,その前に./config/database.yml
を修正します.
default: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password host: <%= ENV.fetch("DB_HOSTS") { 'db' } %> development: <<: *default database: remind_bot_api_development test: <<: *default database: remind_bot_api_test production: <<: *default url: <%= ENV['DATABASE_URL'] %>
たしか変更したのはproduction
の部分だけだった気がします.
さあラストスパートです.一気に終わらせましょう.
$ docker-compose build $ docker-compose up -d $ docker-compose run web rails db:create
え?どこで詰まったかって?データベースのポートが競合したり,なんかアクセス拒否されまくったり,もうめちゃくちゃだよ! よくわからんけどこれで動いたからヨシ!(よくない)
Web開発…嫌い…わかんにゃいよぉ~…
これでやっと本題に行けます.
ファイル構成
最終的なファイル構成はこんな感じになりました.主処理をservice層にとりあえずぶち込んだけれども,最近はこれをやらないとかなんとかいう話も聞いたりで… お作法的な奴全く分からん.直接手を加えたりしてないものは省略してます.
root/ └ app/ ├ controller/ │ └ slack_controller.rb ├ services/ │ ├ client/ │ │ ├ slack_client.rb │ │ ├ discord_client.rb │ │ └ portal_client.rb │ ├ command_service.rb │ ├ interact_service.rb │ ├ remind_service.rb │ ├ weekly_service.rb │ └ message_event.rb └ lib/ └ tasks/ ├ reminder.task └ weekly.task
また,データベースですが,以下の4つのテーブルを持っています.
reminders
- リマインドの日時,メッセージをカラムにもつ.weeklies
- 毎週設定するリマインドの曜日,時刻,メッセージをカラムにもつ.(活動リマインド用なんで,より固有の仕様になってます)channels
- 投稿の種類を示す文字列と,投稿先チャンネルのIDを示す文字列をカラムにもつ.messages
- ユーザーIDを示す文字列と,そのユーザーに対し最後に送信したDMのタイムスタンプを示す文字列をカラムにもつ.
このファイルたち,全部のソース載せると死ぬほど長くなっちゃうので,主要な説明だけしますね.気になった人はGitHub行ってください.僕の汚く香ばしいコードが見れます.
Controller
これはRailsの基本みたいなもんですが,config/routes.rb
で指定したものが,指定のリクエストを受けたときに呼び出されます.
Client
勝手に名前つけただけですが,こいつらにリクエストを方々に送らせてます.
Services
Slashコマンドとか,リマインダーの各種処理をやらせてます.
Tasks
リマインド機能用の定期的に呼び出すタスク
動いてるところを見よう!
なんかそれっぽい感じになりました.
あー長かった…
果たして最後まで見てくれた人はいるんでしょうか… 閲覧者0人!にならないことを願う…
これ,その場の思いつきで始めたんですけど割と形にできました.
コード総量は気づいたら1400行超えてました.
おわり
というわけで,botを作ったよーっていう事後報告でした.Slack Botはdiscord Botよりもちょっと面倒くさかったりしますが,いろいろ面白い機能があるんで触ってみるといいですよ!これでは使ってないですが,もっと楽にSlack Botが作れるライブラリもあったりします.
え?なんで使わなかったのかって?ライブラリの仕様書読みたくなかったからです(カス)
ぶっちゃけもうRuby on Railsなんてやりたくない…(意欲あるそこの後輩くん,これのメンテナでもやってくれませんか・・・?)
あとこのbot動かして遊んでみたいよーとかあったら言ってください.テスト用ワークスペースにぶち込みます.(MISのワークスペースでは幹部陣しか動かせないので)
明日は期待の星!国峰ユズキ君が書くようですよ!