DiscordのBotを作る #2
— development — 8 min read
ボイスチャンネルへの参加を通知する
DiscordのBotを作る #1
の続きです.
コード書く
めでたく起動したのであとはWikiとかドキュメント参照してコード書きましょう.
イベントハンドラを追加するにはフィルタ属性のリストとブロックと共にそれぞれのメソッドを呼び出せばいいので,(Wikiより)
require 'discordrb'
bot = Discordrb::Commands::CommandBot.new token: 'B0T.T0KEN.here', client_id: 'BOT.CLIENT_ID.here', prefix: '!'
bot.message(with_text: "Hey Bot!") do |event| event.respond "Hi, #{event.user.name}!"end
bot.run
こんな感じで記述すると,"Hey Bot!"というテキストに対して"Hi, {ユーザ名}"と返すようになります.(exampleでは与えてませんでしたが,botインスタンス生成時にCLIENT_ID
も与えときましょう.)
最初はいまいち何してるかわかりませんでした.ライブラリ読んでみると,
message
メソッドはDiscordrb::Commands::CommandBot
のスーパークラスDiscordrb::Bot
がincludeしているDiscordrb::EventContainer
モジュールで定義されていて,フィルタ属性とブロックを渡してイベントを登録してるのがわかります.Rubyはメソッド呼び出しの時に引数と一緒にブロック渡せるんですね.discordrb/container.rb
def message(attributes = {}, &block)register_event(MessageEvent, attributes, block)end
コマンドの定義ならこんな感じ
bot.command :hello do |event| event.respond "Hello, #{event.user.name}"end
ボイスチャンネルへの参加をテキストチャンネルへ通知する
Discordは同じボイスチャンネルに参加していない限り誰かが新しく参加してきても何の通知もありません.ので新しくユーザがボイスチャンネルに参加した時,テキストチャンネルで通知するようにします.暇なときしれっとボイスチャンネルに接続しとけば誰かかまってくれるかもしれません.|ω' ) ヌッ
ボイスチャンネルへの参加は#voice_state_update
で取れるので,
bot.voice_state_update do |event|
end
このブロックを記述する.誰かがボイスチャンネルに接続した時発火するイベントはこれ
Class: Discordrb::Events::VoiceStateUpdateEvent
参加時のみ通知したいのでold_channel
がnil
のときのみ#send_message
します.
if event.old_channel.nil? # text notification text = "#{event.user.name} joined #{event.channel.name}" event.bot.send_message(default_text_channel, text)end
メッセージを送るテキストチャンネルIDが必要なので事前に取得しとかなきゃならないですね.デフォルトで存在する"general"があればそこに,なければ最初に取得できたチャンネルに.Discordはチャンネル一つもない状況が作れるみたいなのでテキストチャンネルがなければ警告くらいだしとけばいいかな.(自分の作ったもので初めて例外処理書いた気がします...)
begin default_text_channel = nil event.server.channels.each do |channel| if channel.type == 0 default_text_channel ||= channel.id default_text_channel = channel.id if channel.name == 'general' end end exit unless default_text_channelrescue SystemExit => err puts "[WARN] There is no text channel."end
最後に,Botアカウントには反応したくないのでuser.bot_account
がfalse
のときのみ処理するとして,こんな感じになりました.
bot.voice_state_update do |event| if !event.user.bot_account # get default text channel begin default_text_channel = nil event.server.channels.each do |channel| if channel.type == 0 default_text_channel ||= channel.id default_text_channel = channel.id if channel.name == 'general' end end exit unless default_text_channel rescue SystemExit => err puts "[WARN] There is no text channel." end
# notify only when joining any channel if event.old_channel.nil? # text notification text = "#{event.user.name} joined #{event.channel.name}" event.bot.send_message(default_text_channel, text) end endend
bot_account
の判定部分,早期returnで返してしまいたかったんですが,エラー吐いてしまうので断念.無駄にネストが深い.というかRubyはreturn
使うの邪道なんでしょうか.まだよくわかってない.
チャンネルへの参加を通知. 誰か相手して('ω')!!!
モジュールを使う
大まかな機能毎にコード管理したかったのでmodule使ってコード分割します.
ディレクトリ構成は大体こんな感じ(GitHubでRubyのリポジトリ漁ってパクり参考にしました.)
$ tree srcsrc├── bot.rb└── modules ├── commands │ └── general.rb ├── events │ ├── join_announcer.rb │ └── mention.rb └── helpers └── soundfile.rb
4 directories, 5 files
例えば,general.rb
にはコマンド類を記述したいので,CommandContainer
をextendしたモジュール作って内部に#command
使ってコマンドを定義しときます. #voice_state_update
の処理はコマンドではなくイベントが発生したら呼ぶ処理なのでjoin_announcer.rb
としてモジュール化しときます.
require 'discordrb'
module Bot::Commands module General extend Discordrb::Commands::CommandContainer command :hello do |event| event.respond "Hello, #{event.user.name}" end endend
これをbot.rb
内でCommandBot
のインスタンスBOTにincludeする.bot.rb
はモジュールの読み込みとBOT.run
のみ.
def self.load_modules(cls, path) new_module = Module.new const_set(cls.to_sym, new_module) Dir["src/modules/#{path}/*rb"].each { |file| load file } new_module.constants.each do |mod| BOT.include! new_module.const_get(mod) endend
load_modules(:Commands, 'commands')load_modules(:Events, 'events')
なるほど(˘ω˘)
#load_modules
は指定したシンボルと読み込んだモジュールを記述したファイルから新しくモジュール作ってBOT
にincludeしてるのかな.#include!
が調べてもいまいちわからないけど恐らくそんな感じ.
とにかくこれで今まで通り動作して機能追加もしやすくなりました.
まとめ
取り敢えずボイスチャンネルへの参加をテキストチャンネルに通知するDiscord Botが作れました.
なんだかんだMac上で動いたBotを契約したVPS上で動くようにするための環境構築に一番時間がかかった気がします.(Discordで音声を 扱う場合はさらに必要なパッケージがいくつかあって手間取る)
この環境構築の手順もそのうち自分のためにもまとめときたいですね.
あと,ボイスチャンネルに参加したユーザ名を音声通知する機能も実装したんですが,ボイスチャンネルに人がいなくなったとき謎の警告を吐くのが解決してない&記事が長くなりすぎるのでまた今度まとめます.(GitHub上のリポジトリは音声通知まで実装してます.)
Bot作成,ちょっと書けばすぐ動くし簡単に機能追加できそうなのでおすすめです.たのしい.昔PHP向けのライブラリ使って作ろうとしたときはリファレンス禄に読めなくてやめちゃったんですが今やってみると割とサクサク読めるようになって若干の成長を感じる.あと,人の実装みて理解しようとすると学ぶことが多くて結構勉強になったのでよかったですね.