くらしのマーケット開発ブログ

「くらしのマーケット」を運営する、みんなのマーケット株式会社のテックブログ。積極採用中です。

TypeScriptを使ってDiscord botを作ってみよう!

はじめに

こんにちは!今年新卒入社しました、エンジニアのタナカです。

早速ですが、ここ最近リモートワークが推奨されている中、「Discord(ディスコード)」というツールを導入した、もしくは導入してはいないけれども単語は聞いたことがあるという方が増えているのではないでしょうか。

このツールは過去に 「ゲーマー向けチャットツール」 とよく言われてましたが、2020年7月にブランディングイメージが 「あらゆるコミュニティが使えるコミュニケーションツール」 に変更され、ゲームだけに留まらないツールとなりそうです。

音声も結構クリーンな感じなのもまたいいところですよね👍

さて、この「Discord」というツールなのですが…実はこれ、チャットや会話をするだけではなく、botだって作れるんです。

私も実際にbotを作ってみましたが、とても楽しく作れます!

「プログラミング一通り学んだけど次何したら良いか分かんない…」という方にもきっとオススメできます。

なので今回は、TypeScriptでのDiscord botの作り方(コード部分)について書いていきたいと思います!

※本記事は9/18(金)に協賛枠で参加したWebナイト宮崎 Vol.10 ~てげTypeScriptを学びたい~にて Tunakan という名義で発表した内容から一部を抜粋し、ブログ用に編集したものになります。

 

Discord bot 作ってみよう!

準備

事前に必要なものは以下になります。

…え?これだけ?

そうです。これだけで作れちゃいます!

24時間フル稼働させるような規模のものでなければ、たったの3つで出来ます!

ちなみに今回JavaScriptではなくTypeScriptを使う理由としては、くらしのマーケットで使われている言語の1つなので使い慣れているという点があります。

そして以下が作成したサーバーにbotを追加した状態になります。

f:id:curama-tech:20200924134126p:plain

右側に 「Tunakan bot というのがオフラインで存在してますね。こちらが今回準備したbotになります。

…おや?何やらDiscord画面の下の方に面白い言葉が書いてありますね?

f:id:curama-tech:20200924134221p:plain

「ご挨拶しな!」と半ば強引に挨拶を求められてますね…😅

ただこの状態だと(作成してすぐなので当然ですが)何も返してくれません。シカトされちゃいます。悲しいですね…

なので、まずは簡単に挨拶出来る機能を追加しちゃいましょう!💪

botが挨拶出来るようにしよう!

最初に、discord.jsというNode.jsモジュールをインストールします。MacLinux系ならターミナル、Windowsならコマンドプロンプト、その他お好きなコマンドラインツールがあればそちらを開き以下を実行しましょう!

npm install discord.js

次にテキストエディタを開いて以下のコードをサクッと書いて保存してください。

// ./bot.ts

import { Client, Message } from "discord.js";
import { config } from "./config";

const client = new Client();

interface ReplyInterface {
    messageReply(message: Message): Promise<void>;
}

class Reply implements ReplyInterface {
    public async messageReply(message: Message): Promise<void> {
        if (message.author.bot) {
            return;
        } else if (message.content === "こんにちは") {
            message.reply("こんにちは!");
        }
    }
}

const reply = new Reply();

client.on("message", (message) => reply.messageReply(message));

client.login(config.token);
// ./config.ts

export const config = {
    token: "{token}",   // Developer Portalからtokenをコピペする
};

ファイル構成は以下のようになるはずです。

f:id:curama-tech:20200924134244p:plain

このサクッと書いたコードですが、以下の流れになります

  1. client.loginbotがオンライン状態になる
  2. 何かしらのメッセージが送信されたらreply.messageReply(message)を実行
  3. if (message.author.bot) { return; }で、メッセージがbotからならreturn
  4. bot以外で"こんにちは"というメッセージが送信されたら"こんにちは!"と発言者に対してリプライをする

bot作るなら結構複雑なコード書かなきゃいけないんじゃ…?と思われそうですが(実際私もそう思っていました)、単純なものならこの通りたった数行で作れます!👍

では実際に動かしてみましょう!以下をコマンドラインツールで実行してみてください!

.tsファイルのコンパイルを行った後、botを起動します!

tsc bot.ts
node bot.js

f:id:curama-tech:20200924134306p:plain

f:id:curama-tech:20200924134320p:plain

うまく行けばこの通り、Tunakan botがオンライン状態になり、挨拶をしてくれます!

挨拶だけじゃ物足りない!もっと凝ったものを作る

さて、挨拶するだけじゃかなり物足りないですよね?

せっかく簡単に作れるんだから、何かもうちょっと凝ったものを作りたい…少し手軽にbotらしく動作させたい…うーん…

そうだ、じゃんけんゲームを作ろう!

というわけでじゃんけんのコードをサクッと書いちゃいましょう。

import { Collection } from "@discordjs/collection";
import { Client, Message } from "discord.js";
import { config } from "./config";

const client = new Client();

enum hands {
    "グー",
    "チョキ",
    "パー",
}

interface ReplyInterface {
    messageReply(message: Message): Promise<void>;
}

interface GameInterface {
    playRockPaperScissorsGame(message: Message): Promise<void>;
}

class Reply implements ReplyInterface {
    private game = new Game();

    public async messageReply(message: Message): Promise<void> {
        if (message.content === "じゃんけん") {
            await this.game.playRockPaperScissorsGame(message);
        }
    }
}

class Game implements GameInterface {
    public async playRockPaperScissorsGame(message: Message): Promise<void> {
        await message.reply("最初はグー!じゃんけん…!");
        const filter = (player) => {
            return ["グー", "チョキ", "パー"].includes(player.content);
        };

        let player: Collection<string, Message>;
        try {
            player = await message.channel.awaitMessages(filter, { max: 1, time: 10000, errors: ["time"] });
        } catch {
            message.reply("タイムオーバー!あなたの負けです");
            return;
        }

        if (!player) {
            // playerがundefinedならログアウトしてエラーを出す
            client.destroy();
            throw new Error("player is undefined");
        }

        // botの手を決める
        const botHand = Math.floor(Math.random() * 3);
        await message.reply(hands[botHand]);

        // 判定を行う
        const judge: number = (hands[player.first().content] - botHand + 3) % 3;
        switch (judge) {
            case 0:
                await message.reply("あいこ");
                break;
            case 1:
                await message.reply("あなたの負け");
                break;
            case 2:
                await message.reply("あなたの勝ち");
                break;
            default:
                await message.reply("Error!");
                break;
        }
    }
}

const reply = new Reply();
client.on("message", (message) => reply.messageReply(message));
client.login(config.token);

処理の流れは以下の通りです。サクッと作成したものなので、あいこでも再度勝負をしない一発勝負という点にご注意ください。

  1. ユーザーが「じゃんけん」とメッセージを送るとplayRockPaperScissorsGame()を実行
  2. 「最初はグー!じゃんけん…!」とreplyし、ユーザーからのメッセージをawaitMessagesで待つ
  3. filterで設定した文字列「グー」「チョキ」「パー」のメッセージが送信された時、bot側の手をランダムで決め、botの手をreplyする
  4. 計算してじゃんけんの判定を行い、それぞれの結果をreplyする

また、じゃんけんの判定ロジックは以下になります

  1. グー(0) チョキ(1) パー(2)とする
  2. ( {プレイヤーの手} - {botの手} + 3 ) % 3 をする
  3. 計算結果が0ならあいこ、1なら負け、2なら勝ちになる

コードが書けたらbotを起動してみましょう!tsc bot.tsコンパイルも忘れずに!

f:id:curama-tech:20200924134337p:plain

さいごに

Discordのbotは気軽に作れて、プログラミングの勉強にもなると思います!

Discordを使っている、サーバー持ってたりしててもうちょっといい感じにしたい!楽に運用したい!って時や、TypeScriptを勉強したい…!って時に作ってみるのもいいかもしれませんね👍