【Java / kotlin】改・DiscordのBotの作り方 ～あいさつをするコマンドを実装してみよう～

結構前に「DiscordのBotの作り方（Java）」と題してDiscord Botの作り方を紹介しました。しかし、ライブラリのサポートが切れてしまって現在はその記事を参考にしてはBotを作れなくなってしまっているようです。

自宅学習やオンライン授業を強いられている今日この頃、Discordの需要がじわじわと高まっているようですが、それをさらに便利にするための「Bot」の作り方を紹介していきます。

この記事ではJavaとkotlin、二通りでの書き方を紹介していこうと思います！

# 環境
* Javaバージョン：Java 8（JDK 1.8.0_181）
* kotlinバージョン：kotlin 1.3.71
* プロジェクト管理：Maven

# ライブラリを調達
以下の内容をpom.xmlの```<dependencies></dependencies>```の中に書き入れてください。


```xml
        <!-- メインのライブラリ（必須） -->
        <dependency>
            <groupId>net.dv8tion</groupId>
            <artifactId>JDA</artifactId>
            <version>4.1.1_101</version>
        </dependency>

        <!-- コマンド処理用のライブラリ（必須） -->
        <dependency>
            <groupId>com.jagrosh</groupId>
            <artifactId>jda-utilities</artifactId>
            <version>3.0.3</version>
            <scope>compile</scope>
            <type>pom</type>
        </dependency>

        <!-- kotlinを使うならこの二つも -->
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
            <version>1.3.72</version>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-test</artifactId>
            <version>1.3.72</version>
            <scope>test</scope>
        </dependency>
```

# Botアカウントを作成・サーバーに招待
説明が長くなってしまうので、自分の記事（[こちら](https://qiita.com/itsu_dev/items/825e4ed32bf32e4b64b2#番外編-アプリ登録トークンの取得)）を参考にトークンを取得し、サーバーに招待してください。

# ログイン・認証部分
とにかくBotとしてログインしないと何も始まらないので、先ほど取得したトークンを使ってDiscordにログインしてみましょう！

以下のコードを実行して、問題がなければBotがオンラインになります。

## Java
```Main.java
package dev.itsu.discordbot;

import com.jagrosh.jdautilities.command.CommandClientBuilder;
import com.jagrosh.jdautilities.command.CommandClient;
import net.dv8tion.jda.api.AccountType;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity;

class Main {
    
    private static JDA jda;
    private static final String TOKEN = "MY_TOKEN"; // 取得したBotのトークン
    private static final String COMMAND_PREFIX = "!"; // コマンドの接頭辞

    public static void main(String args[]) {
        // コマンドを扱うイベントリスナを生成
        CommandClient commandClient = new CommandClientBuilder()
            .setPrefix(COMMAND_PREFIX) // コマンドの接頭辞
            .setStatus(OnlineStatus.ONLINE) // オンラインステータスの設定
            .setActivity(Activity.watching("YouTube")) // ステータスの設定（視聴中、プレイ中など）
            .build();
        
        jda = new JDABuilder(AccountType.BOT)
            .setToken(TOKEN) // トークンを設定
            .addEventListeners(commandClient) // commandClientを設定
            .build();
    }

}

```

## kotlin
```Main.kt
package dev.itsu.discordbot

import com.jagrosh.jdautilities.command.CommandClientBuilder
import net.dv8tion.jda.api.AccountType
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.OnlineStatus
import net.dv8tion.jda.api.entities.Activity

private lateinit var jda: JDA
private const val TOKEN = "MY_TOKEN" // 取得したBotのトークン
private const val COMMAND_PREFIX = "!" // コマンドの接頭辞

fun main() {
        // コマンドを扱うイベントリスナを生成
        val commandClient = CommandClientBuilder()
                .setPrefix(COMMAND_PREFIX) // コマンドの接頭辞
                .setStatus(OnlineStatus.ONLINE) // オンラインステータスの設定
                .setActivity(Activity.watching("YouTube")) // ステータスの設定（視聴中、プレイ中など）
                .build()

        jda = JDABuilder(AccountType.BOT)
                .setToken(TOKEN) // トークンを設定
                .addEventListeners(commandClient) // commandClientを設定
                .build()
}
```

# ステータスの変更
## オンラインステータスに設定できる値
```java
OnlineStatus.ONLINE // オンライン
OnlineStatus.OFFLINE // オフライン
OnlineStatus.IDLE // 退席中
OnlineStatus.DO_NOT_DISTURB // 取り込み中
OnlineStatus.INVISIBLE // オンライン状態を隠す
```

## ステータスに設定できる値
```java
Activity.watching("name") // nameを視聴中
Activity.listening("name") // nameを再生中
Activity.streaming("name", "url") // nameを配信中
Activity.playing("name") // nameをプレイ中
```

# コマンドを使ってみる
今回は試しに、「コマンドを実行した人にあいさつをする」コマンドを実装してみようと思います。

## コマンドを実装する
コマンドを実装するには、```om.jagrosh.jdautilities.command.Command``` を継承したクラスを作る必要があります。

### Java
```HelloCommand.java
package dev.itsu.discordbot

import com.jagrosh.jdautilities.command.Command;
import com.jagrosh.jdautilities.command.CommandEvent;

class HelloCommand extends Command {

    public HelloCommand() {
        this.name = "hello"; // コマンド名を設定（!helloで実行可能）
        this.help = "あいさつするだけ"; // コマンドの説明（!helpと実行したときに表示される説明）
    }

    // コマンドを実行したときに呼ばれるメソッド
    @Override
    public void execute(CommandEvent event) {
        event.reply("こんにちは、" + event.getAuthor().getName() + "さん！"); // 返信
    }

}
```
### kotlin
```HelloCommand.kt
package dev.itsu.discordbot

import com.jagrosh.jdautilities.command.Command
import com.jagrosh.jdautilities.command.CommandEvent

class HelloCommand : Command() {

    init {
        name = "hello" // コマンド名を設定（!helloで実行可能）
        help = "あいさつするだけ" // コマンドの説明（!helpと実行したときに表示される説明）
    }

    // コマンドを実行したときに呼ばれる関数
    override fun execute(event: CommandEvent) {
        event.reply("こんにちは、${event.author.name}さん！") // 返信
    }

}
```

## コマンドを登録する
クラスを作っただけではまだコマンドを実行できません。
コマンドを実行できるようにするためには、実装したコマンドのクラスをCommandClientに登録してあげる必要があります。
先ほどのMain.java（Main.kt）に戻って、以下のように変更してください。

これを実行すれば、Discord内で「!hello」と実行したときに「こんにちは、（ユーザー名）さん！」とBotが返してくれます！

### Java
```Main.java
package dev.itsu.discordbot;

import com.jagrosh.jdautilities.command.CommandClientBuilder;
import com.jagrosh.jdautilities.command.CommandClient;
import net.dv8tion.jda.api.AccountType;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity;

class Main {
    
    private static JDA jda;
    private static final String TOKEN = "MY_TOKEN";
    private static final String COMMAND_PREFIX = "!";

    public static void main(String args[]) {
        CommandClient commandClient = new CommandClientBuilder()
            .setPrefix(COMMAND_PREFIX)
            .setStatus(OnlineStatus.ONLINE)
            .setActivity(Activity.watching("YouTube")
            .addCommands(new HelloCommand()) // ここでコマンドを登録！
            .build();
        
        jda = new JDABuilder(AccountType.BOT)
            .setToken(TOKEN)
            .addEventListeners(commandClient)
            .build();
    }

}

```

### kotlin
```Main.kt
package dev.itsu.discordbot

import com.jagrosh.jdautilities.command.CommandClientBuilder
import net.dv8tion.jda.api.AccountType
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.OnlineStatus
import net.dv8tion.jda.api.entities.Activity

private lateinit var jda: JDA
private const val TOKEN = "MY_TOKEN"
private const val COMMAND_PREFIX = "!"

fun main() {
        val commandClient = CommandClientBuilder()
                .setPrefix(COMMAND_PREFIX)
                .setStatus(OnlineStatus.ONLINE)
                .setActivity(Activity.watching("YouTube"))
                .addCommands(HelloCommand()) // ここでコマンドを登録！
                .build()

        jda = JDABuilder(AccountType.BOT)
                .setToken(TOKEN)
                .addEventListeners(commandClient)
                .build()
}
```

実はaddCommands()メソッド（関数）は可変長引数を取るので、以下のようにほかに実装したコマンドをいくつでも登録することができます。

```java
addCommands(HelloCommand(), StatusCommand())
```

ほかに紹介してほしい機能があったり、ご指摘等がある場合はコメントにお願いします。