MIS.W 公式ブログ

早稲田大学公認、情報系創作サークル「早稲田大学経営情報学会」(MIS.W)の公式ブログです!

Slack Interactive Bot のススメ 〜Ruby on Railsをそえて〜【新歓ブログリレー2021 6日目】

どうも,暴言厨イルカです

・・・・・・ごめんなさいふざけました(罪悪感に耐えきれなくなった顔) 54代のみゅーです.どうやらこの人,会計とかいう大役を担っているらしいです.大変そうですね,温かい目で見守ってあげましょう.

いつも僕はMISのブログで映像関連の話しかしてなかったんですけど,せっかく ♰ 情 報 理 工 学 科 特 別 幼 稚 園 組 ♰ (情報理工学幼稚園とか言ってたけど正式名称はこれらしい)にいるのでたまにはプログラミングの話でもします.

Slackに突如出現した謎の刺客…

あれは2か月ほど前のことだろうか… 突如Slackにこんな奴が現れた!!!

f:id:kokoromyuu:20210406233051j:plain

「お前どこから来たんだ?!?!?」

ってわけで,こんな感じのリマインドbotを作ったんですよね~.え?Slackには標準で /remind コマンドがあるって??そんなことは知らん!

まあぶっちゃけ標準のリマインダーでもいいんですが,手打ちで設定するのがめんどくさいのと,設定時に打ったものが確か見えちゃうんですよね.それでこいつを作るに至りました.

設計(適当)

僕は業務でプログラムを書いたことはない(VBA()は??)ので,設計とかから考えて物を作ったことが無く,こういう細かいことができないんですよ… とりあえずこんなメモだけ発掘しました.

f:id:kokoromyuu:20210406234719j:plain

…………なんですかこれ?ゴミですか? 書く手間を省こうとしたらこれだよ…(お前が悪い)

てことで一から説明しますね.

結局実装した機能

  • 活動時間をリマインドするメッセージの送信(Slack & Discord)
  • 上記メッセージを定期的に設定する機能 & その他の通知も送れるように
  • 設定できる人間を幹部陣に絞る(誰でも変えられたら困るので)
  • 特定のチャンネルをDiscordに垂れ流す

単純に指定の日時に送るだけなら適当なプログラムを適当なPCで常時稼働させておけばいいんですが,時間変更等にフレキシブルに対応するにはそうはいかないですね.

それに通知の設定をどこからやるかも問題になります.最初はブラウザからやろうかと思いましたが,せっかくSlackにはSlashコマンドとInteractive Componentとかいうものがあるので,こいつの恩恵を受けることにしました.

このSlashコマンドとかを使うには,まずWebhookのエンドポイントが必要になります.簡単に言えば,適当なサーバーを用意してHTTPリクエストを受け付けろ!ってことです.また,設定した日時等を保存しておくにはデータベースも要りますね.

使ったもの

これらを踏まえて,以下のものを採用しました.

  • Heroku
    • フリーで用意できる環境としては定番
  • MySQL (ClearDB)
    • な ん と な く Heroku標準がPostgreなのを忘れてMySQLで docker-compose.yml 書いちゃったんで止む無く(書き直せよ)…
  • Ruby on Rails
    • 陽キャ御用達フレームワーク すいません嘘です.なんか最近は廃れてる気もしますが,僕がまともに書けるWeb系のものがこれしかなかっただけです.
  • Heroku Scheduler
    • Railsで常時起動プログラムはキツいので,Heroku Schedulerに定期的にTaskを投げさせる.
  • Docker
    • デプロイが楽?になるのかな…(にわかなのでよくわかってません)あと複数台のPCを所有してるので,環境差が出ないように開発したかったってのもあります.

最終的な構成

簡単に描くと,こんな感じで動いてます.

f:id:kokoromyuu:20210407004355p:plain

さあ開発だぁ!!!

の前に開発環境だけメモメモ

  • Docker 20.10.2
  • Ruby 2.7.2
  • bundler 2.1.2
  • Rails 6.1.1

OSはManjaro Linuxでやってました.

開発に使うソフトは?

とりあえず適当なテキストエディタがあればいいです.僕は全力でNeoVimを推します.Vimはいいゾ

f:id:kokoromyuu:20210410020631p:plain

手懐けていくと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.ymldbportsなんですが,ローカルで使用してるポートと被るとダメなんで,適宜変えてください.ローカルでMariaDBとか動かしてると高確率で3306は被ります.

空のGemfile.lockも生成しておきます.

$ touch Gemfile.lock

ここまで来たら,rails newします.Docker使ってるのでコマンドはクソ長です.(記憶があいまいすぎて引数があってる気がしない…)

$ docker-compose run app rails new --api . --force --no-deps --database=mysql

今回,MVCでいうViewがいらないので,--apiAPIモードにしてます.これで余計なファイルが減ります.またデータベースは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行ってください.僕の汚く香ばしいコードが見れます.

github.com

Controller

これはRailsの基本みたいなもんですが,config/routes.rbで指定したものが,指定のリクエストを受けたときに呼び出されます.

Client

勝手に名前つけただけですが,こいつらにリクエストを方々に送らせてます.

Services

Slashコマンドとか,リマインダーの各種処理をやらせてます.

Tasks

リマインド機能用の定期的に呼び出すタスク

動いてるところを見よう!

f:id:kokoromyuu:20210407032209j:plain f:id:kokoromyuu:20210407035000j:plain f:id:kokoromyuu:20210407035532j:plain f:id:kokoromyuu:20210410025425p:plain

なんかそれっぽい感じになりました.

あー長かった…

果たして最後まで見てくれた人はいるんでしょうか… 閲覧者0人!にならないことを願う…

これ,その場の思いつきで始めたんですけど割と形にできました.

コード総量は気づいたら1400行超えてました.

おわり

というわけで,botを作ったよーっていう事後報告でした.Slack Botはdiscord Botよりもちょっと面倒くさかったりしますが,いろいろ面白い機能があるんで触ってみるといいですよ!これでは使ってないですが,もっと楽にSlack Botが作れるライブラリもあったりします.

え?なんで使わなかったのかって?ライブラリの仕様書読みたくなかったからです(カス)

ぶっちゃけもうRuby on Railsなんてやりたくない…(意欲あるそこの後輩くん,これのメンテナでもやってくれませんか・・・?)

あとこのbot動かして遊んでみたいよーとかあったら言ってください.テスト用ワークスペースにぶち込みます.(MISのワークスペースでは幹部陣しか動かせないので)

明日は期待の星!国峰ユズキ君が書くようですよ!