ElixirでDiscordのbot作るの楽しい~って思ったので書きました。
Elixir自体のインストールはこちらからどうぞ。
Discord botアカウントの作成等はこちらの記事が参考になります。
作るもの
-
!dice 2d6
と送信するとダイスを振ってくれる - 結果は
[(,区切りの各ダイス値)] 合計:(ダイスの合計値)
のような形式で返ってくる
こんな感じのダイスロールbotを作ります。
作る
mix new discord_bot --sup
で新しいプロジェクトを作成します。
discord_bot
の部分は適宜変更してください。
ライブラリを導入する
今回はdiscord_alchemyを使用します。
Discord APIのラッパーライブラリです。
mix.exs
を開き、depsを以下のように変更してください。
defp deps do
[
{:alchemy, "~> 0.6.1", hex: :discord_alchemy}
]
end
変更後、
mix deps.get
を実行することで、パッケージが取り込まれます。
pingされたら返事する
とりあえずbot界のHello, world!(?)を作りましょう。
最初に、config.exs
にbotのトークンを書きます。
config :discord_bot,
discord_token: "bot token"
次に、botの処理を書きます。
lib/discord_bot/application.ex
を書き換えます。
defmodule DiscordBot.Application do
use Application
defmodule Commands do
use Alchemy.Cogs
Cogs.def ping do
Cogs.say "pong!"
end
end
def start(_, _) do
token = Application.get_env(:discord_bot, :discord_token)
run = Alchemy.Client.start(token)
use Commands
run
end
end
これで!ping
を送信したらpong!
と返ってくるbotが完成しました!
mix run --no-halt
を実行することで、botが動作します。
ダイスを振る
さて、ついにダイスを振るコマンドを実装していきます。
Commands
モジュールで
Cogs.def コマンド名(引数) do
# 処理
end
とすることでコマンドを定義できます。
まずは6面ダイスを2個振った結果を返すコマンド
をCommands
内に定義してみます。
Cogs.def dice do
dices = 1..2 |> Enum.map(fn _ -> 1..6 |> Enum.random end)
result = dices |> Enum.sum
Cogs.say("[#{dices |> Enum.join(",")}] 合計:#{result}")
end
できました!
やっていることは単純で、リスト[1, 2]
のそれぞれにリスト[1, 2, ..., 6]
からランダムに選ばれた値を突っ込んでるだけです。
Elixirのパイプライン演算子|>
は、左の式の結果を右の式の第一引数に渡すものです。とてもべんりです。
m面ダイスをn個振る
最後に、!dice ndm
の形式でダイスの個数、面数を指定できるようにします。
ついでに
- 個数は1~100
- 面数は1~1000
という制限もつけます。
Cogs.def コマンド名(引数)
の引数
にはコマンド後の文字列が入ってくるので、それを利用してダイスの個数と面数を取得します。
後は、固定値だった2
と6
を個数
と面数
に置き換えるだけです。
Cogs.def dice(roll) do
case Regex.run(~r/\A(\d|[1-9]\d+)d(\d|[1-9]\d+)\z/, roll) do
[_, n, m] ->
num = n |> String.to_integer
face = m |> String.to_integer
cond do
num <= 0 || num > 100 ->
Cogs.say("個数は1以上、100以下にしてください")
face <= 0 || face > 1000 ->
Cogs.say("面数は1以上、1000以下にしてください")
true ->
dices = 1..num |> Enum.map(fn _ -> 1..face |> Enum.random end)
result = dices |> Enum.sum
Cogs.say("[#{dices |> Enum.join(",")}] 合計:#{result}")
end
_ ->
Cogs.say("ndmの形式で入力してください")
end
end
これでダイスロールbotのできあがりです!
引数なしの!dice
コマンドを残しておくと、!dice
した時は2d6を振り、!dice ndm
した時はm面ダイスをn個振ってくれるのでべんりかもしれません。
さいごに
Elixirたのしい~~~!!!
文章を書くのは苦手なのでとてもつかれた。
おまけ
完成したlib/discord_bot/application.ex
の全文です。
defmodule DiscordBot.Application do
use Application
defmodule Commands do
use Alchemy.Cogs
Cogs.def dice do
dices = 1..2 |> Enum.map(fn _ -> 1..6 |> Enum.random end)
result = dices |> Enum.sum
Cogs.say("[#{dices |> Enum.join(",")}] 合計:#{result}")
end
Cogs.def dice(roll) do
case Regex.run(~r/\A(\d|[1-9]\d+)d(\d|[1-9]\d+)\z/, roll) do
[_, n, m] ->
num = n |> String.to_integer
face = m |> String.to_integer
cond do
num <= 0 || num > 100 ->
Cogs.say("個数は1以上、100以下にしてください")
face <= 0 || face > 1000 ->
Cogs.say("面数は1以上、1000以下にしてください")
true ->
dices = 1..num |> Enum.map(fn _ -> 1..face |> Enum.random end)
result = dices |> Enum.sum
Cogs.say("[#{dices |> Enum.join(",")}] 合計:#{result}")
end
_ ->
Cogs.say("ndmの形式で入力してください")
end
end
end
def start(_, _) do
token = Application.get_env(:discord_bot, :discord_token)
run = Alchemy.Client.start(token)
use Commands
run
end
end