This article is the 17th day article of Hamee Advent Calendar 2020.
As a hobby, I play a certain dangerous ☆ game, and at the end of every month, I mainly enjoy the event where 30 players form a team and compete for ranking. The top ranking teams play on Discord while keeping in touch with each other, but since they only interact by chat, if there are as many as 30 players, the information cannot be gathered and the event cannot proceed smoothly. there is. To solve that, I created a bot that writes to Google Spreadsheets from Discord chat content. However, since it is no longer used from this month, it will be a memorial service in this article.
Bot token is required for DiscordBot development. I wrote an article before, so please refer to this (this time it will work with Ruby).
-Try running discord bot with python
You also need the Spreadsheets API credentials to write to Spreadsheet. Please refer to ** [Google Sheets API Settings] ** in this article.
-Manipulate Sheets Using Google Sheets API
It runs on the Docker container, but I will omit Docker.
Since I made it in about 2 days, there are many parts that are not cool.
The prepared file and structure looks like this.
tree
.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
└── source
    ├── .env
    ├── bot.rb
    └── credentials.json
Prepare the following sheet as a bot that writes in column C of the row of the person who spoke
| A | B | C | |
|---|---|---|---|
| 1 | 252398827 | Mr. A | |
| 2 | 261223322 | Mr. B | |
| 3 | 348710907 | Mr. C | 
Dockerfile
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"]
Build and run this Dockerfile
Gemfile
Gemfile
source 'https://rubygems.org'
gem 'dotenv'
gem 'discordrb'
gem 'google_drive'
credentials.json
Place the one prepared in the above preparation as it is.
bot.rb
I'm not thinking about errors throughout.
bot.rb
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
bot.rb
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
Use a class called CommandBot
I will not use it this time, but if you set prefix as the third argument, you can react only to messages that start with a specific character.
start
Run bot.run to start the bot
setting
In the above code, it responds to the remarks of only 1 to 3 letters regardless of which channel it is.
Writing is done by comparing the ID of the person who said the message with the ID on the spreadsheet.
Actually, there is a defect in the Dockerfile mentioned earlier, and when I start it, the following error is displayed.
/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
It seems that the library called libsodium is not enough, but even if I try to google it, I do not know who it is.
After all, it was properly written as a dependent file in discordrb README, and I just missed it. Since it wasn't explicitly stated in other blogs that explain discordrb, it was an example of getting stuck when trying to make only the basic part with copy and paste. Solved by adding the following description to the Dockerfile.
Dockerfile
RUN apt-get update -y && apt-get install -y libsodium-dev
At the end of November, the above bot was in operation. For various reasons this month, we plan to recreate the bot with node.js to expand its functionality and are currently under development. Personally, I'm happy to be able to develop a bot with Ruby, and it's good that my child is working hard to see the bot running.
Recommended Posts