記事内に広告が含まれています

サルでもわかる Discord Botの作り方⑥ ~コマンドを作ろう~

テクノロジー
この記事は約17分で読めます。

こんにちは!今日は、サルでもわかる Discord Botの作り方のパート⑥です。
今回は、主流のDiscord Botでよくあるコマンド機能を作ってみようと思います。

最初に言っておきます。今回からコマンドを扱うにあたって、少し難しい内容となっています。
出来るだけわかりやすく説明していきます。

まだ、パート1・2・3・4・5を見ていない場合は先に1・2・3・4・5を見ることをオススメします!

サルでもわかる Discord Botの作り方

サルでもわかる Discord Botの作り方をゼロから説明していくシリーズです。

スポンサーリンク

今回の記事で作れるBot

今回の記事では、コマンド機能の基本と、応用としてじゃんけん機能を搭載してみようと思います。

じゃんけんは、出した手に応じて結果が出るタイプです。

簡単に作れるコマンド機能

まず最初に、以前のパート4で紹介したMessageCreateイベントでコマンドを簡単に作ることが出来ます。

例えば、「!hello」というコマンドを送信したら以前と同じように「こんにちは!Discord Botです。」と返してくれるBotを作りたい場合は、以下のようなコードになります。

client.on(Events.MessageCreate, message => {
    if (message.author.bot) return; // Botには反応しないようにする
    if (message.guild.id !== "利用するサーバーId") return; // 指定のサーバー以外では動作しないようにする
    if (message.content === "!hello") { // !hello と送信された場合
        message.channel.send("こんにちは!Discord Botです。");
    }
});

これは、前回の紹介したこと同じですね。

でもコマンドといえば、以下のようにコマンド引数で値(文字や数字)をプログラムに渡したいですよね。

なにを言ってるんだ、こいつ」と思う方も多いかもしれないので、先にコードを見てみましょう。
これを実装したコードが以下になります。

const prefix = "!";

client.on(Events.MessageCreate, message => {
    if (message.author.bot) return; // Botには反応しないようにする
    if (message.guild.id !== "利用するサーバーId") return; // 指定のサーバー以外では動作しないようにする
    if (!message.content.startsWith(prefix)) return; // prefix の文字列から始まるもの以外には反応しないようにする
    const args = message.content.split(" "); // メッセージコンテンツを スペース で配列にする
    if (args[0] === prefix + "name") { // prefix + "name" と送信された場合
        const name = args[1];
        message.channel.send("あなたの名前は " + name + " です。");
    }
});
const { Client, Events, GatewayIntentBits } = require('discord.js');

const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessages] });

client.once(Events.ClientReady, c => {
	console.log(`Ready! (${c.user.tag})`); // 起動した時に"Ready!"とBotの名前をコンソールに出力する
});

const prefix = "!";

client.on(Events.MessageCreate, message => {
    if (message.author.bot) return; // Botには反応しないようにする
    if (message.guild.id !== "利用するサーバーId") return; // 指定のサーバー以外では動作しないようにする
    if (!message.content.startsWith(prefix)) return; // prefix の文字列から始まるもの以外には反応しないようにする
    const args = message.content.split(" "); // メッセージコンテンツを スペース で配列にする
    if (args[0] === prefix + "name") { // prefix + "name" と送信された場合
        const name = args[1];
        message.channel.send("あなたの名前は " + name + " です。");
    }
});

client.login("Discord BotのToken");

実際に動かしてみます。
実行結果は、以下の画像のようになります。お分かりいただけましたか?

そうです、コマンド文字列の後に入力した値を使って文字列を出力することが出来ます

コードの解説

コードを見てみるとややこしいと感じる方も非常に多いでしょう。確かにややこしいです。
ただし、わかってしまうと非常にシンプルです。

まず最初のコードは、接頭辞(prefix)を定義するコードです。

const prefix = "!";

ここの文字を変えることで、コマンドの先頭の文字を変えることが出来ます

constというのは、定数を宣言する魔法の言葉です。JavaScriptでは、基本的に let か const を使います
letは、変数で自由に値を変えれるものを入れます。constは今回のように事前に決めておく変えることが出来ない値を入れておくのに使います。

簡単に言うと、let は書き換えが出来るけど const は書き換えが出来ないということですね。

次に6行目の if 文です。コメント文で「prefix の文字列から始まるもの以外には反応しないようにする」と書いているのでそのままです。

// prefix の文字列から始まるもの以外には反応しないようにする
if (!message.content.startsWith(prefix)) return;

以前から何気に if 文の中で ! (ビックリマーク) を書いていましたが、これは「否定」を意味します。

次の7行目では、args という定数を宣言しています。ここからややこしくなります。

// メッセージコンテンツを スペース で配列にする
const args = message.content.split(" ");

配列というのは、簡単に言うと値を入れておくことが出来る箱の塊です。

この「0,1,2,3」を配列に表すと以下のようなコードになります。

const array = [0, 1, 2, 3];

例えば、以下のような「りんご、ばなな、れもん」という配列を作りたい場合はこのようなコードになります。

const array = ["りんご", "ばなな", "れもん"];

配列には、最初から順番に1ずつ増える数字が割り振られます。
気をつけないといけないのは、最初が0番目から始まることです。

そして、それぞれの配列の値を取り出したい場合は、以下のように取り出すことが出来ます。

りんご = array[0]
ばなな = array[1]
れもん = array[2]

プログラミングでは、配列をあらゆるところで使うので大切です。

今回のコード内で出てくるsplit("␣")というのは文字列を指定の区切り文字で分割するものです。難しい言葉で言うと、split メソッドといいます。

今回の場合は、スペースで区切っています。
コマンドを実行した際のイメージとしては以下のようになります。

そして、8行目~11行目のコードでは、今までのコードを合わせて if 文を作っています。

// prefix + "name" と送信された場合
if (args[0] === prefix + "name") {
     const name = args[1];
     message.channel.send("あなたの名前は " + name + " です。");
}

コマンドを複数個作りたいとき

では、次にコマンドを複数個作りたいときにどうすればいいのか。という話です。
単純に「MessageCreate イベントを増やせばいいのでは?」と思う方が多いですが、それは間違いです。動くことには動きますが、様々な問題が発生します。

ではどうすればいいのか、if 文には if else 文というものもあります。それを使います。

コマンドを増やして !name コマンドと !age コマンドを作った場合のコードが以下のようになります。

client.on(Events.MessageCreate, message => {
    if (message.author.bot) return; // Botには反応しないようにする
    if (message.guild.id !== "利用するサーバーId") return; // 指定のサーバー以外では動作しないようにする
    if (!message.content.startsWith(prefix)) return; // prefix の文字列から始まるもの以外には反応しないようにする
    const args = message.content.split(" "); // メッセージコンテンツを スペース で配列にする
    if (args[0] === prefix + "name") { // prefix + "name" と送信された場合
        const name = args[1];
        message.channel.send("あなたの名前は " + name + " です。");
    } else if (args[0] == prefix + "age") { // prefix + "age" と送信された場合
        const age = args[1];
        message.channel.send("あなたの年齢は " + age + " です。");
    }
});

if else 文というのは最初の条件文に当てはまらない場合に次の条件文に行くという形です。
以下のイメージを見てもらってなんとなくわかれば問題ないです。

3つ以上にしたい場合は、else if 文を増やすだけです。以下のようなコードです。

if (args[0] === prefix + "name") {
    // prefix + "name"の処理
} else if (args[0] == prefix + "age") {
    // prefix + "age"の処理
} else if (args[0] == prefix + "banana") {
    // prefix + "banana"の処理
}

【応用】じゃんけんを作ろう

ここまで学んだことを生かしてじゃんけんコマンドを作ってみようと思います。

まずじゃんけんをするには、プログラム側が何の手を出すかをランダムに決める必要があります。そのランダムを作るにも配列を使います

まず最初に「グー、チョキ、パー」の3つの配列を作っておきます。

const hands_list = ["グー", "チョキ", "パー"];

そして、次にこの3つの中からランダムでどの手を出すかを計算します。

今回は、配列なので0~2の3個の数字をランダムに計算で出したいです。次に指定範囲の数字をランダムに出す方法を紹介します。

Math.floor()

JavaScriptでは、Math.floor()という小数点以下を切り捨てるものがあります。
例えば、以下のように使うことが出来ます。

Math.floor(3.46) // 結果 : 3

Math.floor(2.05) // 結果 : 2

Math.floor(1.95) // 結果 : 1

Math.floor(0.52) // 結果 : 0

Math.random()

次に、Math.random()といものを紹介します。これは、0 以上 1 未満の乱数を作るものです。
例えば、以下のように使うことが出来ます。

Math.random() // 結果 : 0.9595965608315749

Math.random() // 結果 : 0.22940039490095732

Math.random() // 結果 : 0.016294164816429202

Math.floor() と Math.random()を組み合わせる

そして、 Math.floor() と Math.random() を組み合わせることで、指定の数以内のランダムな数字を作ることが出来ます

例えば、以下のコードでは 0~4 までのランダムな整数を出してくれます。

Math.floor( Math.random() * 5 )

なぜ0~4のランダムな数字が生まれるかを詳しく説明すると、以下のようになります。

まず、Math.random() * 5 の計算をすると以下のようになります。

そして、計算した値をMath.floor()にかけるとこのようになります。

イメージ出来ましたでしょうか?つまり、3つの値がある配列からランダムの値を出したい場合は、以下のコードのように出来ます。

const hands_list = ["グー", "チョキ", "パー"];

hands_list[Math.floor( Math.random() * 3 ) ] // グー,チョキ,パーのいずれか

配列の要素数を取得 [].length

先ほどは、コードに直接 配列の要素数を指定しました。でもこれでは、配列の要素の数を変えた場合に使いにくい状態です。そこで、配列の要素の数を簡単に取得するプロパティがあります。

それが、lengthプロパティです。使い方は、至って簡単です。
配列名.lengthで出すことが出来ます。

const hands_list = ["グー", "チョキ", "パー"];

hands_list.length // 結果 : 3

これを踏まえて、じゃんけんの手を出すコードは以下のコードになります。これで完成です。

const hands_list = ["グー", "チョキ", "パー"];

hands_list[Math.floor( Math.random() * hands_list.length ) ]

すべてを組み合わせる

では、これまでのことを再度すべて組み合わせてじゃんけんを完成させます。

じゃんけんのコードが以下のようになります。

if (args[0] === prefix + "janken") {
    const hands_list = ["グー", "チョキ", "パー"];
    // プログラム側の手
    const cpu_hand = hands_list[Math.floor( Math.random() * hands_list.length ) ];
    // プレイヤー側の手
    const player_hand = args[1];
    // 結果
    let result = "";
    // 結果の判定
    if (cpu_hand == player_hand) {
        result = "あいこ";
    } else if (cpu_hand == "グー" && player_hand == "チョキ") {
        result = "あなたの勝ち";
    } else if (cpu_hand == "チョキ" && player_hand == "パー") {
        result = "あなたの勝ち";
    } else if (cpu_hand == "パー" && player_hand == "グー") {
        result = "あなたの勝ち";
    } else {
        result = "あなたの負け";
    }
    message.channel.send("コンピュータ: " + cpu_hand + "\nあなた: " + player_hand + "\n結果: " + result);
}
const { Client, Events, GatewayIntentBits } = require('discord.js');

const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessages] });

client.once(Events.ClientReady, c => {
	console.log(`Ready! (${c.user.tag})`); // 起動した時に"Ready!"とBotの名前をコンソールに出力する
});

const prefix = "!";

client.on(Events.MessageCreate, message => {
    if (message.author.bot) return; // Botには反応しないようにする
    if (message.guild.id !== "利用するサーバーId") return; // 指定のサーバー以外では動作しないようにする
    if (!message.content.startsWith(prefix)) return; // prefix の文字列から始まるもの以外には反応しないようにする
    const args = message.content.split(" "); // メッセージコンテンツを スペース で配列にする
    if (args[0] === prefix + "janken") {
        const hands_list = ["グー", "チョキ", "パー"];
        // プログラム側の手
        const cpu_hand = hands_list[Math.floor( Math.random() * hands_list.length ) ];
        // プレイヤー側の手
        const player_hand = args[1];
        // 結果
        let result = "";
        // 結果の判定
        if (cpu_hand == player_hand) {
            result = "あいこ";
        } else if (cpu_hand == "グー" && player_hand == "チョキ") {
            result = "あなたの勝ち";
        } else if (cpu_hand == "チョキ" && player_hand == "パー") {
            result = "あなたの勝ち";
        } else if (cpu_hand == "パー" && player_hand == "グー") {
            result = "あなたの勝ち";
        } else {
            result = "あなたの負け";
        }
        message.channel.send("コンピュータ: " + cpu_hand + "\nあなた: " + player_hand + "\n結果: " + result);
    }
});

client.login("Discord BotのToken");

実行した結果は以下のようになります。

最後に

これでようやくDiscord Botとしての一つの機能が完成しました。

次のパートでは、まだ何を書こうか悩んでますので良ければコメント欄で最終的に何が作りたいか教えてほしいです!

では、次の記事をお楽しみに!最後までお読みいただきありがとうございました!

サルでもわかる Discord Botシリーズ記事一覧

サルでもわかる Discord Botの作り方

サルでもわかる Discord Botの作り方をゼロから説明していくシリーズです。

困ったことがあったら、、
この記事に関して、困ったことがありましたらなりかくんのブログ公式Discordサーバーにて質問をすることが可能です!
ぜひ入ってみましょう!

Discordサーバー「Narikakun Network」に参加しよう!
このサーバーでは、プログラミングの質問などや気になったことなどなんでも話し合える雑談Discordです。 | 980人のメンバー

コメント

タイトルとURLをコピーしました