DiscordのBotを作ってみよう！②メッセージとコマンドに反応するBot

# はじめに
こんにちは、とみーです。
前回の記事を見てくださった皆さん、ありがとうございました！
初めての投稿だったのに自分の想像以上の方が見てくださって、うれしかったです。ありがとうございました！
前回に開発環境を整えたため、今回からいよいよ実装を行っていきたいと思います。
興味のある方よろしければ最後までご覧ください。
# 注意すること
- これより先の記事では、Ruby開発環境、discordrbをインストールしていること前提でお話していきたいと思います。まだの方は、前記事準備編をご覧ください。
- 自分の解釈が間違ってる可能性もあります。ご了承ください。
- タグにもつけていますが、Rubyにて開発を行っていきます。
- フォルダの名前などは任意ですが、予期しないエラーや不具合を避けるため、すべて半角英数で入力してください。

# Rubyやプログラミング言語を触ったことがない方へ
今回から解説していく記事ですが、Rubyや他のプログラミング言語を全く触ったことがない方には意味が分からず難しいかもしれません。
自分もはじめたてのため、できるだけ優しく解説するつもりですが、基礎は飛ばしてしまうかもしれません。
プログラミング言語を全く触ったことがない方は、まずはこの記事を読む前に、オンライン教材などで基礎を習得することをお勧めします。
# はじめてのBotを起動してみよう！
まずは、Botを起動するためのコードを入力していきましょう。
フォルダを作成して(フォルダの名前はなんでも構いません。ここではexamplebotという名前で作成しました。)
次にコードエディタなどで`bot.rb`ファイルを作成します。(ファイルの名前はなんでも構いません。)
それでは、`bot.rb`を開いていきましょう！

```ruby:bot.rb
require 'discordrb'
bot = Discordrb::Commands::CommandBot.new token: 'ここにBotのトークンを入力', client_id: ここにBotのidを入力 , prefix: 'Botに設定したいPrefixを入力'
bot.run
```
まずはGemを使うために`require 'discordrb'`を入力します。
bot変数にインスタンスを作成します。 `token: 'トークン'`のところにトークンを入力して、
`client_id: BotのID`ここに、BotのIDを入力していきます。
`prefix: 'prefix'`ここにプレフィックスを入力します。ここで、Botのコマンド入力の際のプレフィックスが決まります。
プレフィックスとは、コマンドの前についてくる記号`!`とか`?`とか`/`とかの事です。
本記事では、`$`を使うことにします。
最後に`bot.run`で起動します。

書けたら、保存して、起動してみましょう！

```shell:コマンドプロンプト・ターミナル
ruby bot.rb
[INFO : websocket @ 2021-05-16 11:40:24.838] Discord using gateway protocol version: 6, requested: 6
```

## はじめまして！
![quita8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/34806e37-f763-270c-6084-1ec32b799c97.jpeg)
やった！、初めての起動がうまくいきました！
でも、起動するプログラム以外を書いていないのでまだ起動しただけ...
メッセージを入力しても何も返してくれません...

# メッセージに反応してくれるBotを作成する
これでは、何もできないので、メッセージを送信したら返してくれる機能を作りましょう！
いったん終了します。(現時点ではまだ終了するプログラムを作成していないので`Ctrl+Cキー`で終了してください。)
Discord側も少し遅れてBotがオフラインになります。

## 何かメッセージが送られてきたら実行する
まずは、どんなメッセージにも反応する機能を作ってみましょう
### 書き方

```ruby:bot.rb
# discordrbを使うために必要
require 'discordrb'
bot = Discordrb::Commands::CommandBot.new token: 'ここにBotのトークンを入力', client_id: ここにBotのidを入力 , prefix: 'Botに設定したいPrefixを入力'

# メッセージが送られてきたら実行
bot.message do |変数|
  #処理
end

bot.run
```
### Hello World!

```ruby:bot.rb
require 'discordrb'
bot = Discordrb::Commands::CommandBot.new token: 'ここにBotのトークンを入力', client_id: ここにBotのidを入力 , prefix: 'Botに設定したいPrefixを入力'

# 何かメッセージが送信されたら実行
bot.message do |event|
  event.respond 'Hello, world!'
end

bot.run
```

書けたら、保存して、起動してみましょう！

```shell:コマンドプロンプト・ターミナル
ruby bot.rb
[INFO : websocket @ 2021-05-16 11:58:16.945] Discord using gateway protocol version: 6, requested: 6
```

起動しましたか？
起動したら何かメッセージを送信してみましょう！
![quita9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/beaa5344-7337-3081-48bd-89d1bef7e47d.jpeg)
上で設定したメッセージを返してくれてますね！成功です！
## どんなことしたの？
先ほどのプログラムは

```ruby:bot.rb
require 'discordrb'
bot = Discordrb::Commands::CommandBot.new token: 'ここにBotのトークンを入力', client_id: ここにBotのidを入力 , prefix: 'Botに設定したいPrefixを入力'

# 何かメッセージが入力されたら実行
bot.message do |event|
  event.respond 'Hello, world!'
end

bot.run
```
と入力しました。これは何かというと、
まず

```ruby
bot.message do |event|
  event.respond 'Hello, world!'
end
```
さっき上で作成した`bot`の変数を使って、`なにか、メッセージが来たら実行する`というプログラムを作成しました。
`変数.respond`でメッセージを送信できます。(他の方法もありますが、今回は割愛します。)
ですのでHello, Worldのところを変えると、メッセージが変わります。(ダブルクオーテーションでも構いません。ダブルクオーテーションでくくると変数展開が使えます。)
## ちょっと応用編
ちょっとプログラムを変えてみましょう！

```ruby
bot.message do |event|
  event.respond event.user.name
end
```

`event.respond`の部分を`event.user.name`に変えました。実行してみましょう！
![quita10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/27860112-18e2-79a2-8328-c2e3adb4a16e.jpeg)
自分の名前が返ってきました！

もう一つ。

```ruby
bot.message do |event|
  event.respond "#{event.user.name}さん、#{event.server.name}へようこそ！"
end
```
変数展開を用いて、ようこその文章を作りました。実行してみましょう！
![quita11.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/734ad28e-64bf-7b69-6227-f007eff816c3.jpeg)
`名前`さん、`サーバ名`へようこそ！と返ってきました！
このように `変数.user.name`や`変数.server.name`などを使えば、送信したユーザに応じて名前を変えるコマンドの実行に成功しました！

## メッセージから値を色々とってみよう！
他にもいろんな値が取得することができます。

| 変数 | 取得できる値 | 備考 |
|:-:|:-:|:-:|
| 変数.user.name | メッセージを送信したユーザーの名前 |   |
| 変数.user.id | メッセージを送信したユーザのID |   |
| 変数.user.display_name | メッセージを送信したユーザのサーバ上のニックネーム | ニックネーム未設定時はユーザの名前で返ってくる |
| 変数.user.server.name | メッセージを送信したサーバの名前 |   |
| 変数.user.server.id | メッセージを送信したサーバのID |   |
| 変数.channel.name | メッセージを送信したサーバのチャンネル |   |
| 変数.channel.id | メッセージを送信したサーバのチャンネルID |   |
| 変数.user.voice_channel.name | メッセージを送信したユーザが入っているボイスチャンネル | ボイスチャンネルに入っていないときは NoMethodErrorが出ます。 |
| 変数.user.voice_channel.id | メッセージを送信したユーザが入っているボイスチャンネルのID | ボイスチャンネルに入っていないときはNoMethodErrorが出ます。 |
## 特定のメッセージに反応してくれる機能を作成する
さて、今の状態では、メッセージの内容に関係なく、実行してしまいます。
このままでは、どんなメッセージでも返信してしまうため、とてもうるさくなってしまいます。
そこで、特定のメッセージのみに反応するBotを作ってみましょう！

```ruby:bot.rb
# はじめましてと入っていれば実行
bot.message(containing: "はじめまして") do |event|
  event.respond "#{event.user.name}さん、#{event.server.name}へようこそ！"
end
```
保存して、実行してみましょう！
※ここから、ターミナルの実行画面省略します。`ruby [ファイル名]`で実行できます。
![quita12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/619073a5-e618-f693-d2f8-146eae153ef8.jpeg)
はじめまして、という文章が入っていれば実行されるようになりました！
ちなみに、

```ruby:bot.rb
# はじめましてか初めましてと入っていれば実行
bot.message(containing: ["はじめまして","初めまして"]) do |event|
  event.respond "#{event.user.name}さん、#{event.server.name}へようこそ！"
end
```
`containing:`を`["対象メッセージ1","対象メッセージ2"]`でくくって配列にすると複数条件の指定ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/30c33410-6abe-8b85-c08a-ca09e98a95e2.png)

# コマンドで動く機能を作ってみよう
さて、先ほどはメッセージに反応する機能を作成しました。
次は、コマンドを作成して、コマンドが入力されたら、実行するプログラムを作成していきましょう。

### 例
```ruby
bot.command :設定したいコマンド do |変数|
  # 処理
end
```

### $helloと入力されたら実行するコマンド
```ruby:bot.rb
bot.command :hello do |event|
  event.respond "#{event.user.name}さん、こんにちは"
end
```
実行してみます。
コマンドは、bot変数に入れたprefix引数がコマンドの前に入れる記号となります。
ここでは、`$`を設定したので
`$hello`になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/9081386c-2c7a-f941-a4eb-73b0518652fd.png)
### helpコマンドについて
コマンドBotを作る際によく、helpコマンドが作ってありますが、helpコマンドはすでに用意されています。
一度、$helpと入力してみましょう(prefixの値を変えている場合は頭文字もそれに変更してください。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/0f786960-5089-05c2-c908-c9b82d899e32.png)
List of commands:というものが出てきました、ここにプログラムしたコマンドの一覧が表示されます。(今回は、helloコマンドしか作っていないため、helloしか出ていませんが、他のコマンドも作成したらここに表示されます。)
helloの欄を見ると、No description availableと書かれています。つまり、helloコマンドの説明を書いてやる必要があります。

### helpに説明文が出てくるようにしよう。
```ruby:bot.rb
bot.command(:hello, description: "こんにちはメッセージを返します。", usage: "表示するだけです。引数はありません") do |event|
  event.respond "#{event.user.name}さん、こんにちは"
end
```
descriptionに表示したい内容を書きます。 usageには、help [コマンド]とか、間違えて使ったときに表示される内容を書きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/84e98cf8-1a46-10b1-634f-6c1c502c4250.png)
helpコマンドを使うと説明が出てくるようになりました！
### オプションハッシュいろいろ
| ハッシュ | 属性 | 説明 | 備考 |
|:-:|:-:|:-:|:-:|
| :permisson_level  | Integer  | このコマンドを使用するための最小の権限レベル  | 権限レベルは、Discordのロール権限とは異なります。詳しくは、[こちらの記事](https://qiita.com/denebola/items/d50a92c5e1b59d9de489#case1-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E5%AE%9F%E8%A1%8C%E6%A8%A9%E9%99%90%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%9F%E3%81%84)が参考になります。  |
| :permission_level  | String,false  | コマンドを実行する際に上記の:permission_levelの設定に対して、ユーザが十分な権限を持っていないときに表示されるメッセージです。  | 同上 |
| :required_permissons  | Array  | コマンドを使用するために、必要なDiscord権限  | おそらくDiscordの権限だと思いますが、自分もはっきりわかっていません。  |
| :required_roles  |  Array | このコマンドを使用するために必要なロール(記入したロールすべて必要です)  | 自分もはっきりわかっていません。  |
| :allowed_roles  | Array  | このコマンドを使用するために必要なロール(1つ以上必要です)  |  自分もはっきりわかっていません。 |
|:channels|Array|このコマンドを使用できるチャンネル||
|:chain_usable|true,false|コマンドチェーン?やサブチェーンでの可否を設定する|自分もはっきりわかっていません。|
|:help_available|true,false|helpコマンドを入力したときに、一覧に表示されるかどうか|デフォルトはtrue(表示する)|
|:description|String|helpコマンドを入力した際に、出てくる説明文||
|:usage|String|コマンドの詳細説明 `help [コマンド名]`などで表示される説明||
|:min_args|Integer|このコマンドを実行するために必要な最小の引数の数||
|:max_args|Integer|このコマンドが持つべき引数の最大数||
その他のハッシュは[こちら](https://www.rubydoc.info/github/meew0/discordrb/Discordrb/Commands/CommandContainer)
※属性について

- String 文字列型
- Integer 整数型
- Arrey 配列
- true,false 有効か無効です

# Botのステータスを変えてみよう！
さて、次にステータスを変更する方法です。
Botも状態をオンライン、退席中、取り込み中、オンライン状態を隠すなどプログラムで変更可能です！~~あまりつかわないけど...~~
## オンラインに変更
`変数.online`でステータスをオンラインにできます。
ステータスを変更する際に、だれでも変更できてしまうと、さすがにマズイので、コマンドを実行したユーザIDと、`configatron.ownerid`(ここに自分のIDが設定されています。後程説明します。)の一致を確認しています。

```ruby:bot.rb
# ステータスをオンラインに変更する
bot.command(:online, help_available: false) do |event|
  if event.user.id == configatron.ownerid
    bot.online
    event.respond "Botはオンラインになりました。"
  end
end
```
※`configatron.ownerid`は後程説明します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/c15b4a67-4870-2a95-c6fe-56dc6328a311.png)

## 退席中に変更
`変数.idle`でステータスを退席中に変更できます。

```ruby:bot.rb
# ステータスを退席中に変更する
bot.command(:idle, help_available: false) do |event|
  if event.user.id == configatron.ownerid
    bot.idle
    event.respond "Botは退席中になりました。"
  end
end
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/97b5e054-5b99-9ac8-46d4-f8610ac31bfd.png)

## 取り込み中に変更
`変数.dnd`でステータスを取り込み中に変更できます。

```ruby:bot.rb
# ステータスを取り込み中に変更する
bot.command(:dnd, help_available: false) do |event|
  if event.user.id == configatron.ownerid
    bot.dnd
    event.respond "Botは取り込み中になりました"
  end
end
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/3e9efc10-a453-5f59-03e1-db325fc18e0d.png)

## オンライン状態を隠す
`変数.invisible`でオンライン状態を隠せます。
※ちなみに`\n`をつかうと改行できます！

```ruby:bot.rb
# ステータスをオンライン状態を隠すに変更する
bot.command(:invisible, help_available: false) do |event|
  if event.user.id == configatron.ownerid
    bot.invisible
    event.respond "Botはオンライン状態を隠しています\nこのコマンドを入力しても実際にシャットダウンされたわけではありません。\n終了するときはshutdownコマンドを入力してください。"
  end
end
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/ca64eb76-329a-9976-7425-0252d9585e44.png)

# ステータスメッセージを変更してみよう
メンバーの欄に出てますよね...`*helpをプレイ中`とかのやつですよ...
あれもプログラムで自在に変更できるようにしてみましょう！

## 〇〇をプレイ中
`変数.game`で変更できます。
すこしややこしいので、解説。
`do |event,message|`のところです。
Discordでコマンドを送信する際、半角スペースを空けると引数となります。
したの画像を見ていただくと、`$set_game $help`と送信しています。
`$set_game`のスペースの後、`$help`は`message`変数が受け取っています。

```ruby:bot.rb
# Botのステータスメッセージを変更する
# ～～をプレイ中
bot.command(:set_game, help_available: false) do |event,message|
  if event.user.id == configatron.ownerid
    bot.game = message # message変数は(Discordで半角スペースを空けたところからの文字を受け取る[次の半角スペースまで])
    event.respond "Botステータスメッセージを#{message}に変更しました。"
  end
end
# ちなみに、変数.game に nilを入れると、ステータスメッセージを消去出来ます。
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/11761741-e9a9-b496-1593-47c754544761.png)
うまく変わりました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/e3d4c670-e95f-cac9-7d94-dfe965c7e224.png)

## 〇〇を視聴中
`変数.watching`で変更できます。

```ruby:bot.rb
# ～～を視聴中
bot.command(:set_watch, help_available: false) do |event,message|
  if event.user.id == configatron.ownerid
    bot.watching = message
    event.respond "Botステータスメッセージを#{message}に変更しました。"
  end
end
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/bf4b229e-f460-b29b-60b8-96a8dccd4c6d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/20e05889-7766-4263-45a8-4ce8d2a67dba.png)

## 〇〇を再生中
`変数.listening`で変更できます。

```ruby:bot.rb
# ～～を再生中
bot.command(:set_play, help_available: false) do |event,message|
  if event.user.id == configatron.ownerid
    bot.listening = message
    event.respond "Botステータスメッセージを#{message}に変更しました。"
  end
end
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/7019ae3d-0af5-153b-24d1-7bd46d776b05.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/2fe44cd6-bc01-65d7-a8b2-3e7c075f0f80.png)

# 終了するプログラム
最後にBotを終了してみましょう。
いままでは、`Ctrl`+`C`で終わっていましたが、今度は、終了するコマンドを作ってみましょう。

```ruby:bot.rb
# shutdownコマンドが入力されたとき
bot.command(:shutdown, help_available: false) do |event|
  # コマンド入力している人がオーナーかチェックします
  if event.user.id == configatron.ownerid
    # 終了しましたと送信(先に送信しておかないと切断されてしまう為)
    event.respond "終了しました"
    # botを切断
    bot.stop
  end
end
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/6c627bcf-41ce-9180-9cdb-e01e88a09e32.png)

Botがオフラインになり、プログラムが終了しました。

# ちょっと応用してみよう
## 時間に応じた挨拶をしてくれる機能を作ってみよう
ここまでの知識で、ちょっとだけ時間の表示と、時間によって違ったあいさつをしてくれるプログラムを作ってみましょう
`Time.now`で現在時刻を取得することができます。それをtime変数にインスタンスを作成します。
`変数.hour`で時だけ取得できます。
`変数.min`で分だけ取得できます。
取得した時で、朝、昼、夕、夜を判断、それによってif文でメッセージを条件分岐します。

```ruby:bot.rb
bot.command(:hello, description: "こんにちはメッセージを返します。", usage: "表示するだけです。引数はありません") do |event|
  # 現在時刻を取得
  time = Time.now
  message = ""
  # 時間を確認、メッセージ分岐
  if time.hour < 6
    message = "遅くまでおつかれさま"
  elsif time.hour < 12
    message = "おはよう！"
  elsif time.hour < 17
    message = "こんにちは！"
  else
    message = "こんばんは"
  end
  # Discordに送信
  event.respond "#{event.user.name}さん、#{message} 今の時間は#{time.hour}時#{time.min}分だよ"
end
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677279/71420909-d520-f323-f96a-3a6f46cf6a0a.png)
時間に応じて挨拶が変わり、現在時刻を表示されるようになりました。

# Tokenの安全な取り扱いかた
さいごにですが、今回のBotプログラムは直接、トークンを書いています。
前回も書きましたが、**トークンはBotにログインするための重要なコードです！**
したがって、トークンを直接記述する方法はあまりよくない上に、GitやGitHubにするときに色々不便です。
そこで、別に設定ファイルを作り、ついでに、IDやプレフィックス、オーナIDなどもそちらに設定していきましょう。
まず、Gemをインストールしていきましょう。

```shell:コマンドプロンプト・ターミナル
$ gem install configatron

Fetching configatron-4.5.1.gem
Successfully installed configatron-4.5.1
Parsing documentation for configatron-4.5.1
Installing ri documentation for configatron-4.5.1
Done installing documentation for configatron after 0 seconds
1 gem installed
```
$は不要です、gemから入力してください。
`gem install configatron`と入力し、最後に`1 gem installed`が出ればOKです。
次に、`config.rb`を作成します。
## config.rbの中身
```ruby:config.rb
configatron.token = 'YOUR_TOKEN'　# 'YOUR_TOKENのところに、自分のトークンを入力。
configatron.botid = #自分の使用するBotのIDをここに入力
configatron.prefix = 'prefix' # 'prefix'のところに、使いたいプレフィックスを入力する。
configatron.ownerid = # Botの管理するユーザのIDを入力
```
ここにそれぞれ設定するIDやトークン、プレフィックスを入力します。
ちなみに、ステータスメッセージのところで出てきた、`configatron.ownerid`はここから値をとっています。

## configatronを使う
さっきのBotを書いていたファイルに戻ります

```ruby:bot.rb
# gemの読み込み
require 'configatron'
# さっきのトークンを書き込んだファイルです
requere_relative 'config.rb'
# token:とclient_id:とprefix:をそれぞれ書き換えます。
bot = Discordrb::Commands::CommandBot.new token: configatron.token, client_id: configatron.botid , prefix: configatron.prefix
```
これで、Botの設定ファイルを別にすることに成功しました。
`configatron.メゾット`で、`config.rb`に書いた内容を呼び出すことができます。
あ、GitやGithubとかで保管したい方は、`.gitignore`に`config.rb`を例外設定するのをわすれないようにしてね！

```shell:.gitignore
# トークンとか書いてあるため、除外
config.rb
```
**ここまでお疲れさまでした！**

# おわりに
ここまで、長い記事を見てくださって本当にありがとうございました！
説明がうまくできているかわかりませんが、見てくださったみなさんの開発のヒントになればとてもうれしいです。
さて、まだ自分が説明しきれていない機能がもうちょっとあります。次回はそこのところを説明できればと思います。
よろしくお願いします。

# 今回作ったBotのプログラムはこちら
今回作ったBotのプログラムのソースコードです。よろしければご覧ください。
`bot.rb`の中に入っています。

https://github.com/tomysan/discordrb_samplebot

# 参考にしたもの
https://github.com/shardlab/discordrb

# DiscordのBotを作ってみよう　バックナンバー

- [1.準備編](https://qiita.com/tomynomint/items/4f35b281ec09eae3c1d2)(インストールまで)
- 2.メッセージとコマンドに反応するBot(この記事です)
- 3.(準備中)