はじめに
この記事はHamee Advent Calendar 2020の17日目の記事です。
私は趣味で某ヤバイ☆ゲームを遊んでおり、毎月月末にプレイヤー同士が30人でチームを組みランキングを競うイベントをメインに楽しんでいます。
ランキング上位のチームはDiscord上であれやこれや連絡を取りながらプレイするのですが、チャットでしかやりとりをしないので30人もプレイヤーがいると情報をまとめきれずイベントをスムーズに進めることができないことがあります。
それを解決するためにDiscordのチャット内容からGoogle Spreadsheetsに書き込むBotを作成しました。が、今月から使わなくなったためこの記事で供養します。
準備
DiscordBotの開発には、Botのtokenが必要です。
以前に記事を書いたのでこちらを参考にしてください(今回はRubyで動かします)。
Spreadsheetに書き込むために、SpreadsheetsAPIのcredentialも必要です。
こちらの記事の**[Google Sheets API の設定]**を参考にしてください。
Dockerコンテナ上で動作させますが、Dockerについては割愛します。
中身
2日くらいで作ったためイケてない箇所が多々あります。
用意したファイルと構成はこんな感じです。
.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
└── source
├── .env
├── bot.rb
└── credentials.json
スプレッドシート
シートは以下のようなものを用意して、発言した人の行のC列に書き込みをするbotとします
A | B | C | |
---|---|---|---|
1 | 252398827 | Aさん | |
2 | 261223322 | Bさん | |
3 | 348710907 | Cさん |
Dockerfile
FROM ruby:2.6.5
ENV APP_HOME /home/source
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile Gemfile
ADD Gemfile.lock Gemfile.lock
ADD ./source ./
RUN bundle install
CMD ["bundle", "exec", "ruby", "bot.rb"]
このDockerfileをbuildして、runします
Gemfile
source 'https://rubygems.org'
gem 'dotenv'
gem 'discordrb'
gem 'google_drive'
- dotenv
- DiscordBotのトークンなどを.envファイルで管理するために使用します
- discordrb
- Ruby版のDiscordAPIのライブラリです
- リファレンス
- google_drive
- Spreadsheets APIのために使用します
- 公式のライブラリはgoogle-api-clientというライブラリなのですがこちらを使うことにしました
- GitHub
credentials.json
前述の準備で用意したものをそのまま配置します。
bot.rb
全体を通してエラーに関しては何も考えていません。
Spreadsheets部分
require "google/apis/sheets_v4"
require "googleauth/stores/file_token_store"
require "fileutils"
Dotenv.load
class Spreadsheet
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'.freeze
APPLICATION_NAME = 'MyBot'.freeze
CREDENTIALS_PATH = 'credentials.json'.freeze
TOKEN_PATH = 'token.yaml'.freeze
SCOPE = Google::Apis::SheetsV4::AUTH_SPREADSHEETS
ID = ENV['SHEAT_ID']
attr_accessor :service
def initialize
@service = Google::Apis::SheetsV4::SheetsService.new
@service.client_options.application_name = APPLICATION_NAME
@service.authorization = authorize
end
def authorize
client_id = Google::Auth::ClientId.from_file CREDENTIALS_PATH
token_store = Google::Auth::Stores::FileTokenStore.new file: TOKEN_PATH
authorizer = Google::Auth::UserAuthorizer.new client_id, SCOPE, token_store
user_id = "default"
credentials = authorizer.get_credentials user_id
if credentials.nil?
url = authorizer.get_authorization_url base_url: OOB_URI
puts "Open the following URL in the browser and enter the " \
"resulting code after authorization:\n" + url
code = gets
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code, base_url: OOB_URI
)
end
credentials
end
def read(range)
response = service.get_spreadsheet_values ID, range
response.values
end
def write(range, value)
data = Google::Apis::SheetsV4::ValueRange.new
data.major_dimension = 'ROWS'
data.range = range
data.values = [[value]]
options = {
value_input_option: 'RAW'
}
response_write = service.update_spreadsheet_value ID, range, data, options
response_write.updated_cells
end
end
- authorize
- どこかからコピペしてきたものです
- 初回実行時にのみ認証作業を求められるので、表示されたURLにアクセスし表示されたコードを入力してください
- 認証が済むとtoken.yamlが生成されるので2回目以降は作業の必要はありません
Discordbot部分
require 'discordrb'
require 'dotenv'
Dotenv.load
class MyBot
attr_accessor :bot, :spreadsheet
def initialize
@bot = Discordrb::Commands::CommandBot.new client_id: ENV['CLIENT_ID'], token: ENV['BOT_TOKEN']
@spreadsheet = Spreadsheet.new
end
def start
settings
@bot.run
end
def get_user_row(user_id)
a = @spreadsheet.read('A:A')
(a.find_index [user_id.to_s]) + 1
end
def settings
@bot.message contains: /^[1-3]$/ do |event|
num = event.content
user_row = get_user_row event.user.id
@spreadsheet.write "C#{user_row}", num
end
end
end
bot = MyBot.new
bot.start
-
initialize
- CommandBotというクラスを使います
- 今回は使いませんが第三引数にprefixを設定すると、特定の文字から始まるメッセージにのみ反応させることができます
-
start
- bot.runを実行するとbotが起動します
-
setting
- 上記コードでは、どのチャンネルか区別なく1〜3のみの文字の発言に反応するようにしています
- メッセージを発言した人のIDとスプレッドシート上のIDを照らし合わせて、書き込みを行なっています
詰まりポイント
実はさきほどのDockerfileには不備があり、いざ起動してみると以下のようなエラーが表示されます。
/usr/local/bundle/gems/ffi-1.13.1/lib/ffi/library.rb:145:in `block in ffi_lib': Could not open library 'sodium': sodium: cannot open shared object file: No such file or directory. (LoadError)
Could not open library 'libsodium.so': libsodium.so: cannot open shared object file: No such file or directory
libsodiumというライブラリが足りないようなのですが、いざググってみても何者かあまりわからない。
結局discordrbのREADMEにちゃんと依存ファイルとして書いてあって、それを見逃していただけでした。
discordrbについて解説している他ブログなどではあまり明示していることがなかったため、基礎部分だけコピペで作ろうとすると詰まるという例でした。
Dockerfileに以下の記述を追加して解決。
RUN apt-get update -y && apt-get install -y libsodium-dev
おわりに
11月の月末は上記のBotが稼働してくれました。
今月はいろいろ理由があり、Botをnode.jsで作り直して機能を拡張する予定で、現在開発中です。
個人的にはRubyでBotを開発できて満足していますし、Botが稼働している様子は我が子が頑張っている感があって良いですね。