ヨーヨーヨーヨーヨーヨー

ロックマンエグゼの構築とかオフレポとか便所の落書きとか

Discordのbot経由でChallongeのAPIを叩いた話

珍しく真面目(?)な記事です。
数ヶ月前にエグゼネット対戦の対戦募集や大会運営を行うプラットフォームをIRCからDiscordに移行するにあたって、大会運営を円滑化したいという意図でちょっとしたbotを作ったので、その時の備忘録です。

Discordのbotを作る

Discordの開発者向けページからNew Applicationをクリックしてbotを作成。
SETTINGBotタブにあるTOKENをコピーしてどっかに保存しておいてください。
悪用されるから第三者に教えちゃダメだぞマジで。
細かい設定とかは各自ググってくれ、僕が書くよりよっぽど丁寧に書いてあるサイトが無限にあるから。

ソースを書く

今回はJava+Mavenを使用します。(というか僕が満足に書ける言語がJavaぐらいしかない)
また、ライブラリはJDA(Java Discord API)を使用。

Mavenの設定

pom.xmlにこんな感じのを適当に書いてJDAを追加します。

<dependencies>
  <dependency>
    <groupId>net.dv8tion</groupId>
    <artifactId>JDA</artifactId>
    <version>1.7.25</version>
  </dependency>
</dependencies>

Javaでジャバジャバする

ListenerAdapterクラスを継承したクラスを適当に作って、さっき保存しておいたトークンを食わせる。
僕はpropertiesファイルに書いて読み込ませてるけど別にソースコードにベタ書きでもいいんじゃないかな!!!

public class Listener extends ListenerAdapter {

    public void startListener() throws Exception {
        String token = "ここにトークンを記載";
        JDA jda = new JDABuilder(token).build();
        jda.addEventListener(new Listener());
    }
}

なんかカラースキームがキモくね??

んでもって適当にmainメソッドの中でさっき作ったクラスを呼び出す。

public class Sample {

    public static void main(String[] args) {
        Listener listener = new Listener();
        try {
            listener.startListener();
        } catch (Exception e) {
            // エラーハンドリングは適当に
        }
    }
}

これでプログラムを実行すると作ったbotがDiscord上でオンラインになるぞ!!すごい!!!

メッセージを受け取る

ListenerAdapterクラスのonMessageReceivedメソッドをオーバーライドします。
こいつはbotがいるテキストチャンネルに何かしらのメッセージが送信されたときに呼ばれるメソッドで、引数のMessageReceivedEventにはチャットの内容とかなんかいろんなものが入ってます。
詳しいことはOverview見てくれ、全部書いてあっから。

@Override
public void onMessageReceived(MessageReceivedEvent event) {
    // 送信されたメッセージを取得
    String message = event.getMessage().getContentRaw();
    if (message.startWith("こんにちは")) {
        event.getTextChannel().sendMessage("うんこ").queue();
    }
}

「こんにちは」から始まる文字列を取得すると、sendMessageの部分でbotが返信してくれます。 f:id:exceed_strekoza:20200531221824p:plain
信じられないくらいバッドマナーですね。
何はともあれbotがメッセージを受け取って返信するという一連の流れはこんな感じで実装できます。

ChallongeのAPIを叩いてみる

Challonge API Documentationに超細かく書いてあります。
全部英語ですが全部Google翻訳にブチ込んだらある程度使えるレベルまでは理解できるので英語アレルギーの人でも大丈夫です、実際僕がそうだし。

APIキーの生成

API Documentationにも書いてありますが

All interactions with the API require a Challonge account with a verified email address and API key. You can generate one from your developer settings page.

要はAPI使いたかったらAPIキー生成済みのアカウントが必要だよって話ですね。
APIキーはdeveloper settings pageで生成できるのでポチポチクリックして生成しましょう。
こいつも悪用されるから第三者に教えちゃダメだぞ!!!

APIを叩いてみる

手始めにAPIを叩いてトーナメントを生成してみましょう。
トーナメント生成のAPIここ
APIキーだけ必須であとは任意項目なのねなるほどーってことで

{
  "api_key":"ここにAPIキーを記載"
}

こんな感じのjsonを作って

curl -X POST -H "Content-Type: application/json" https://api.challonge.com/v1/tournaments.json -d @challonge.json

それっぽくcurlコマンドを叩いてみると

{
  "errors": [
    "Name can't be blank",
    "URL can't be blank"
  ]
}

こんな感じのエラーが返ってくる、いやいやいや…

f:id:exceed_strekoza:20200602063348p:plain
トーナメント名とURLが必須なんてどこにも書いてねーじゃねーか嘘つき!!!!!!
API Documentationを書いた人は嘘をついちゃいけないって道徳の授業で習わなかったんですかね。
少々ムカつきますが、文句言ってても仕方無いのでjsonにnameとurlを追記します。

{
  "api_key":"ここにAPIキーを記載",
  "tournament": {
    "name":"test",
    "url":"exceed_test_20200602"
  }
}

ちなみにURLの部分はchallonge内でユニークとなる必要があるので、末尾にタイムスタンプとかを埋め込んでおくと上手いこと回避できる気がします。
修正したjsonでPOSTすると

{
  "tournament": {
    "id": 8527622,
    "name": "test",
    "url": "exceed_test_20200602",
    "description": "",
    "tournament_type": "single elimination",
    "started_at": null,
    "completed_at": null,
    "require_score_agreement": false,
    "notify_users_when_matches_open": true,
    "created_at": "2020-06-01T14:43:41.992-07:00",
    "updated_at": "2020-06-01T14:43:41.992-07:00",
    "state": "pending",
    "open_signup": false,
    "notify_users_when_the_tournament_ends": true,
    "progress_meter": 0,
    "quick_advance": false,
    "hold_third_place_match": false,
    "pts_for_game_win": "0.0",
    "pts_for_game_tie": "0.0",
    "pts_for_match_win": "1.0",
    "pts_for_match_tie": "0.5",
    "pts_for_bye": "1.0",
    "swiss_rounds": 0,
    "private": false,
    "ranked_by": null,
    "show_rounds": false,
    "hide_forum": false,
    "sequential_pairings": false,
    "accept_attachments": false,
    "rr_pts_for_game_win": "0.0",
    "rr_pts_for_game_tie": "0.0",
    "rr_pts_for_match_win": "1.0",
    "rr_pts_for_match_tie": "0.5",
    "created_by_api": true,
    "credit_capped": false,
    "category": null,
    "hide_seeds": false,
    "prediction_method": 0,
    "predictions_opened_at": null,
    "anonymous_voting": false,
    "max_predictions_per_user": 1,
    "signup_cap": null,
    "game_id": null,
    "participants_count": 0,
    "group_stages_enabled": false,
    "allow_participant_match_reporting": true,
    "teams": null,
    "check_in_duration": null,
    "start_at": null,
    "started_checking_in_at": null,
    "tie_breaks": null,
    "locked_at": null,
    "event_id": null,
    "public_predictions_before_start_time": null,
    "ranked": false,
    "grand_finals_modifier": null,
    "predict_the_losers_bracket": null,
    "spam": null,
    "ham": null,
    "rr_iterations": null,
    "tournament_registration_id": null,
    "donation_contest_enabled": null,
    "mandatory_donation": null,
    "non_elimination_tournament_data": {},
    "auto_assign_stations": null,
    "only_start_matches_with_stations": null,
    "registration_fee": "0.0",
    "registration_type": "free",
    "split_participants": false,
    "description_source": "",
    "subdomain": null,
    "full_challonge_url": "https://challonge.com/exceed_test_20200602",
    "live_image_url": "https://challonge.com/exceed_test_20200602.svg",
    "sign_up_url": null,
    "review_before_finalizing": true,
    "accepting_predictions": false,
    "participants_locked": false,
    "game_name": null,
    "participants_swappable": true,
    "team_convertable": false,
    "group_stages_were_started": false
  }
}

こんな感じで生成したトーナメント内容のjsonが返ってきます。
ちゃんとトーナメントが生成できているか確認するためにfull_challonge_urlのURLにアクセスしてみると
f:id:exceed_strekoza:20200602064751p:plain
こんな感じでトーナメントが生成できているのが確認できました!!

JavaAPIを叩く

適当に実装しちゃいましょう。
DTOに値を突っ込んでGson使ってjson形式にフォーマットして…

TournamentDto tournament = new TournamentDto();
tournament.setApi_key("ここにAPIキーを記載");
TournamentDetailDto detail = new TournamentDetailDto();
detail.setName("test");
detail.setUrl("exceed_test_20200602");
tournament.setTournament(detail);

Gson gson = new Gson();
String postJson = gson.toJson(tournament);

適当にPOSTするぞ!!!!
マジで適当に書きすぎた!!!もっと丁寧に書けねえかなこれ

URL url = new URL("https://api.challonge.com/v1/tournaments.json");
HttpsURLConnection uc = (HttpsURLConnection) url.openConnection();
uc.setRequestMethod("POST");
uc.setDoOutput(true);
uc.setRequestProperty("Content-Type", "application/json; charset=utf-8");

OutputStream os = uc.getOutputStream();

PrintStream ps = new PrintStream(os);
ps.print(postJson);
ps.close();
os.close();

bot経由でAPIを叩く

もうこの辺書く必要なくない???
上で説明したonMessageReceivedの中でPOSTするやつ書いてあげればそれっぽく動きます。
f:id:exceed_strekoza:20200602114709p:plain
参考程度に、こんな感じで運用してます。

おわりに

ソースが汚ねえ!とか、動かねえんだけど!とか言われても知りません、ぼくの備忘録なので。
初めて技術的な記事を書いたんですけど、技術を文章に落とし込むのは難しいですね。
ぼくなんかは技術力も文章力もカスゴミなので仕事以上に体力使った気がします。
また気が向いたらなんか書こうかな、多分しょーもないだろうけど。