20210728のJavaに関する記事は7件です。

【Java】DiscordBotの作り方 2021年版

はじめに 初投稿です。 この記事では2021年版のDiscordBotの作り方を解説していきます。 なお、DiscordBotをグループに招待する方法等はたくさん記事があるので、省略させていただきます。 目次 1.はじめに 2.使用するライブラリ 3.プロジェクトに依存関係を追加する 4.BOTにログインできるようにする 5.チャットを検知する 6.スラッシュコマンドを実装する 7.ユーザーID(Long)からユーザ名(String)を取得する 8.チャンネルID(Long)からテキストチャンネルを取得する 9.BOTがメンションされたことを検知する 10.BOTを正常に終了させる 11.最後に 使用するライブラリ ・JDA GitHub JavaDoc この記事で利用するバージョン 4.3.0_277 プロジェクトに依存関係を追加する プロジェクトを作成し、pom.xmlやbuild.gradleに記載を行います。 Mavenの場合 pom.xml <dependency> <groupId>net.dv8tion</groupId> <artifactId>JDA</artifactId> <version>4.3.0_277</version> </dependency> <repository> <id>dv8tion</id> <name>m2-dv8tion</name> <url>https://m2.dv8tion.net/releases</url> </repository> Gradleの場合 build.gradle dependencies { implementation("net.dv8tion:JDA:4.3.0_277") } repositories { mavenCentral() maven { name 'm2-dv8tion' url 'https://m2.dv8tion.net/releases' } } botにログインできるようにする BOT_TOKENという変数にご自身のDiscordBOTのトークンを入力します。 その後、Activity.playing("")という欄に好きな文字列を入れてください。 ※設定しないことも可能です。 ログインに失敗した場合はLoginExceptionが発生するので、例外処理も忘れないようにしましょう。 また、今回はグループ内でのBotの使用を目的としているので解説はしませんが、GatewayIntent.GUILD_MESSAGESを GatewayIntent.DIRECT_MESSAGESに変更することでダイレクトメッセージのイベントを監視することができます。 Main.java import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.requests.GatewayIntent; import javax.security.auth.login.LoginException; public class Main { private static JDA jda = null; private static final String BOT_TOKEN = "BOTのTokenを入力してください。"; public static void main(String[] args) { try { jda = JDABuilder.createDefault(BOT_TOKEN, GatewayIntent.GUILD_MESSAGES) .setRawEventsEnabled(true) .setActivity(Activity.playing("設定したいステータスを入力してください")) .build(); } catch (LoginException e) { e.printStackTrace(); } } } Activityの種類について Activityには種類があり、様々な文字列に変更することができます。 ・Activity.playing("好きな文字列") 〜〜をプレイ中 ・Activity.competing("好きな文字列") 〜〜に参戦中です ・Activity.listening("好きな文字列") 〜〜を再生中 ・Activity.streaming("好きな文字列","url") 〜〜を配信中(ユーザーをクリックすることで設定したURLに飛ぶようです) ・Activity.watching("好きな文字列") 〜〜を視聴中 チャットを検知する JDAのListenerAdapterを継承させ、イベントを記載するクラスのインスタンスを生成し、JDAに追加します。 ※extends ListenerAdapterと.addEventListeners(new Main())を追加しました。 Main.java import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.requests.GatewayIntent; import javax.security.auth.login.LoginException; public class Main extends ListenerAdapter { //追加部分 private static JDA jda = null; private static final String BOT_TOKEN = "BOTのTokenを入力してください。"; public static void main(String[] args) { try { jda = JDABuilder.createDefault(BOT_TOKEN, GatewayIntent.GUILD_MESSAGES) .setRawEventsEnabled(true) .addEventListeners(new Main()) //追加部分 .setActivity(Activity.playing("設定したい文字列")) .build(); } catch (LoginException e) { e.printStackTrace(); } } } メッセージを受け取るイベントの定義 下記記載のイベント(MessageReceivedEvent)を上で登録したクラスに記載すると、BOTがメッセージを送信されたことを検知することができるようになります。 また、e.getMessage().getContentRaw()を利用することで入力されたメッセージを取得できます。 Main.java @Override public void onMessageReceived(MessageReceivedEvent e) { String msg = e.getMessage().getContentRaw(); //入力されたメッセージを取得 } メッセージを送信する 通常メッセージを送信する 次は、Botから通常メッセージを送信します。 Botからメッセージを送信するには Main.java e.getTextChannel().sendMessage("送信したいメッセージ").queue(); を記載することでイベント発生後にメッセージを送信します。 ただ、これを記載するだけだと無限ループしてしまうので以下のようにします。 ※BOTのメッセージだった場合は、メッセージを送信しないようにしています。 Main.java String msg = e.getMessage().getContentRaw(); if (!e.getAuthor().isBot()) { e.getTextChannel().sendMessage("送信したいメッセージ").queue(); } 埋め込みメッセージを送信する 書いてある説明を参考にしてください。 また、タイトルや色、説明、フッター等はなくても動きます。 Main.java @Override public void onMessageReceived(MessageReceivedEvent e) { if (!e.getAuthor().isBot()) { EmbedBuilder eb = new EmbedBuilder(); eb.addField("名前1","説明1",false); eb.addField("名前2","説明2",false); eb.setTitle("タイトル"); eb.setDescription("埋め込みメッセージの説明"); eb.setThumbnail("サムネイル[リンク]"); eb.setFooter("フッター画像[リンク]"); eb.setColor(Color.WHITE); //埋め込みメッセージの横の色 eb.setImage("横に表示する画像[リンク]"); eb.setAuthor("名前","アイコン[リンク](任意)"); e.getTextChannel().sendMessage(eb.build()).queue(); //送信 } } メッセージを削除する メッセージを削除するには、過去のメッセージを取得し削除を行います。 今回は、/clearというメッセージを検知した際に、メッセージを3つListとして取得し、 取得したメッセージをdelete()で消しています。 ※メッセージを削除できる数に制限はないようですが、一度にやるとDiscordに怒られるので、大量に行う場合は遅延を入れましょう。 Main.java @Override public void onMessageReceived(MessageReceivedEvent e) { String msg = e.getMessage().getContentRaw(); if (msg.equalsIgnoreCase("/clear")) { //メッセージが/clearだったら過去のメッセージを3つ取得 List<Message> messages = e.getTextChannel().getHistory().retrievePast(3).complete(); for (Message getMsg : messages) { getMsg.delete().queue(); } } } スラッシュコマンドを実装する 最近Discordにスラッシュコマンドとやらが追加されましたね。 JavaのJDAを利用したスラッシュコマンドの追加方法(特にサブコマンド)はほぼ見つからなかったので、Docs等を読み漁りなんとか実装したものです。(ミスがあれば指摘の方お願いしますm(_ _)m) また、スラッシュコマンドはクライアント側で使えるようになるまで最大1時間程度かかることがあります。 ※スラッシュコマンドを追加するには、Botを招待する際に、OAuth2からapplications.commandsの権限を与える必要があります。 ※スラッシュコマンドはまだ不安定なようだったので、どうしてもうまく行かないものはメッセージの検知で実装を行うことをオススメします。 スラッシュコマンドをJDAに登録 OptionTypeについては入力を求める型名を入力してください。(今回は文字列のためSTRING) ※数字はINTEGER ※サブコマンドはいくつでも追加できます。 ※ヒントに表示させるで、使用できる文字が制限されているようです。 ※また、setRequired(false)で入力を任意にすることができます。 Main.java public static void main(String[] args) { try { jda = JDABuilder.createDefault(BOT_TOKEN, GatewayIntent.GUILD_MESSAGES) .setRawEventsEnabled(true) .addEventListeners(new Main()) .setActivity(Activity.playing("てすとBot")) .build(); } catch (LoginException e) { e.printStackTrace(); } //登録しているコマンドを最新のものに更新する(一度リセットを行う) //updateを行わないと過去のコマンド等が残り続けます。 jda.updateCommands().queue(); //コマンドの登録 // /testコマンドの実装 jda.upsertCommand("test1","テストコマンド2").queue(); //サブコマンドの実装 // /test2 [subCommand(必須)]コマンドの実装 jda.upsertCommand("test2","テストコマンド2") .addOptions(new OptionData(OptionType.STRING,"subCommand1","subCommand1の説明").setRequired(true)) .queue(); } また、このままだと登録だけしてコマンドが動作しないので動作について実装します。 コマンドの動作を書く 下記プログラムを実装することによってスラッシュコマンドの動作を実装することができます。 また、スラッシュコマンドは何も反応がなかった場合に、ユーザーにエラーを表示してしまうので、必ず反応を行うようにします。 ※今回の場合は、/test2 subCommand以外を受け付けないため、/test2 test2 等入力されたときのエラーを防ぐためにelseを記述 スラッシュコマンドは実行した人以外に結果を表示するかどうかの設定も可能 Main.java @Override public void onSlashCommand(SlashCommandEvent e) { if (e.getName().equalsIgnoreCase("test1")) { //setEphemeral()でコマンドを実行したユーザー以外にリプライを見えないようにするかを指定します。 e.reply("/test1を入力しました!").setEphemeral(false).queue(); } else if (e.getName().equalsIgnoreCase("test2") && e.getOptions().get(0).getAsString().equalsIgnoreCase("subCommand")) { e.reply("/test2 subCommand を入力しました!").setEphemeral(false).queue(); } else { e.reply("不明なコマンドです。").setEphemeral(false).queue(); } } ユーザーid(long)からユーザー名(string)を取得する ユーザーID(Long)からユーザー名(String)を取得する方法です。 以下の記述で取得することができます。 Main.java jda.retrieveUserById(/*ユーザーID(Long)*/).queue(userName -> { //userNameにユーザー名(String)が代入されます。(非同期処理なので注意!!) }); チャンネルid(long)からテキストチャンネルを取得する チャンネルID(Long)からテキストチャンネルを取得する方法です。 以下の記述で取得することができます。 Main.java TextChannel tc = jda.getTextChannelById(/*チャンネルID(Long)*/); botがメンションされたことを検知する 下記の処理を記述し、isMentionがtrueになったときの処理をif分等で実装すればよいです。 Main.java String message = e.getMessage().getContentDisplay(); boolean isMention = false; for (Object s : e.getMessage().getMentionedMembers()) { //メッセージを取得して、取得したメッセージに自分のIDが含まれていればisMentionをtrueにする isMention = s.toString().contains(jda.getSelfUser().getId()); } botを正常に終了させる 上記のコマンド等を利用してBotを正常に終了させるには Main.java jda.shutdownNow(); を実行するコマンド等を実装することで正常にBotを終了するようにできます。 最後に DiscordBotを作る際に、最新のものがなくてすごく大変な思いをしたので皆さんに共有できればと思いこの記事を書きました。 なにか間違い等があれば指摘の方をお願いしますm(_ _)m
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java再入門 第1回 Java7以降の歴史 機能追加とサポート期間

Java6で時間が止まった人(私のこと)のためのこれまでのあらすじ。 Java5以降の主な機能追加 「これは重要な変更だっただろ!」というものがあれば理解できていないだけなので教えてください。 リリース日 Version 主な変更点 2004年9月 5 ジェネリクス、アノテーション、拡張for文、列挙型、可変長引数、ExecutorService/Future 2006年12月 6 スクリプト言語サポート。ここで時間が止まる 2011年7月 7 switchで文字列、ダイアモンド演算子、リソース付きtry文、Fork/Join 2014年3月 8 LTS ラムダ式、メソッド参照、Stream、Optinal、日時時刻APIの変更 2017年9月 9 モジュールシステム。G1GCがデフォルトに。プリミティブのラッパークラスのコンストラクタが非推奨 2018年3月 10 ローカル変数型推論 2018年9月 11 LTS JavaEE由来のAPIが削除 2019年3月 12 プレビュー段階のもの以外は特に目立つ変更はない 2019年9月 13 プレビュー段階のもの以外は特に目立つ変更はない 2020年3月 14 switch式。jpackage 2020年9月 15 テキストブロック、ZGC、Shenandoah GC 2021年3月 16 record、パターンマッチングinstanceof Javaのライフサイクルと商用サポート期間 6ヶ月に1回、3月と9月に新バージョンがリリースされ、3年毎にLTSの長期サポートのバージョンがリリースされています。 サポートが終了するタイミングはJDKのベンダーごとに差があり、たまに変更(基本的に延長)されているので、定期的に公式を確認する必要があります。 8 2014年3月 11 2018年9月 17 2021年9月予定 Oracle 2022年3月, 2030年12月 2023年9月, 2026年9月 2026年9月, 2029年9月 Red Hat 2026年6月 2024年10月 未掲載 Azul Zulu 2030年12月 2026年9月 未掲載 MS 提供なし 2024年10月 2027年9月 AdoptOpenJDK 2026年3月 2024年10月 未掲載 Amazon Corretto 2026年6月 2027年9月 未掲載 各社のライフサイクル掲載ページです https://www.oracle.com/jp/java/technologies/java-se-support-roadmap.html https://docs.microsoft.com/ja-jp/java/openjdk/support https://www.azul.com/products/azul-support-roadmap/ https://adoptopenjdk.net/support.html https://access.redhat.com/articles/1299013 https://aws.amazon.com/jp/corretto/faqs/ 以下を主な情報源にしています。 【連載】イマドキのJava徹底入門 | TECH+ JJUGナイトセミナー OpenJDK祭り「OpenJ9+OpenJDK」 最適なOpenJDKディストリビューションの選び方 #codetokyo19B3 #ccc_l5 「Java8からJava11」で何が起きたのか、どう環境構築すればいいのか - Qiita Java新機能メモ(Hishidama's Java up-to-date) シリーズJava再入門 いまからJava8にしよう!だってJava11よりサポート長いし!という判断もあると思いますが、11に引っ越そうとしている方や、11の次のLTSになることを予定されている17への移行を目指している方向けのJava再入門です。 Java7以降の歴史 機能追加とサポート期間 モジュールシステムとクラスパス、Gradle - Qiita ~Java11 ジェネリクス、ダイアモンド演算子 ~Java11 日付時刻API ~Java11 リソース付きtry文 ~Java11 ラムダ式、メソッド参照、Stream、Optional、ローカル変数型推論 ~Java11 ExecutorService/Future、Fork/Join 今後:テキストブロック 今後:switch式、パターンマッチングinstanceof 今後:record
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

よくやるNullPointerExceptionまとめ

基本的すぎることですがよくやるのでメモ 定数.equals(変数)で変数がnull final String nullStr = null; // ぬるぽにならない System.out.println("a".equals(nullStr)); // ぬるぽになる System.out.println(nullStr.equals("a")); if文がnull final String nullStr = null; // ぬるぽ if (nullStr) {}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

java static 内部クラス

外部クラスは複数のオブジェクトをnewができますが、 static 内部クラスは一つしか作らないです。 複数の外部クラスオブジェクトは一つのstatic内部クラス領域を共有する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

継承ってどういう時に使うの?

はじめに 継承とはどのような関係性の中で行われるものなのかについて解説します。 継承の関係性 結論:継承はis-a関係の中で成立します。 具体的にいうと、子クラスは親クラスの一種であるという関係性において継承は成立します。 例えば、チワワについて考えてみます。チワワは犬の一種であるといえます。この場合、チワワクラスは犬クラスを継承しても問題ないということです。 汎化・特化の関係 is-a関係において継承は成立すると書きましたが、その関係に従っていくと、子クラスになるほど具体化(特化)され、親クラスになるほど抽象化(汎化)されます。 例えば、チワワ→犬→動物→生き物 という継承関係があるとします。この場合、子クラスになるほどチワワとしての具体的な要素を持ちますが、親クラスになるほどそれが持つ要素は、抽象的なものになっていきます。 まとめ 継承は、is-a関係にあるもので成立するもので、他の関係性にあるものの間では推奨されない。(できないことはないようです) 人間にわかりやすいようにオブジェクト指向という考え方にのっとりプログラムを作っていくわけなので、定義に従って作っていくことがよりわかりやすいプログラムにつながると考えます。 そのため、正しい継承関係のもとに書いていくことを意識することは大切です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

独習Java(3章)

3-1 連続する文字列連結にはString Builderクラスのappendメゾットを用いる var builder = new String Builder(); for(int i; i<1000; i++){ builder.append("いろは"); } 浮動小数点の演算をする際には内部的には2進数で処理されるので誤差が出る場合がある よってBig Decimalクラスを用いる import java.math.BigDicimal; 中略 var bd1 = new Big Decimal(0.2); var bd1 = new Big Decimal(0.4); var bd1 = new Big Decimal(0.5); System.out.println(bd1.add(bd2)multiply(bd3)); ここにはadd/subtract/multiply/divideを用いて演算 3-2 代入演算子 intは基本型だが、Stringは参照型なのでここはしっかり違いを見極める 基本型・・整数/浮動小数点/審議 参照型・・クラス型/インターフェイス型/配列型 定数は値を変更できな変数 3-3関係演算子 同一性・・オブジェクト参照同士が同じオブジェクトを持っている 同地性・・オブジェクトが同じ値 文字列は==で比較すると別で作られたオブジェクトと認識されるのでequalsメゾットを用いる 浮動小数点はBig Dicimalクラスのcompare toメゾットを用いる(同じならば0,大きければ1,小さいときは―1) 定数EPSILONは誤差の許容範囲を示す 配列の比較はjava.utilのequalsメゾット 3-4 論理演算 既知のためパス 3-5ビット演算 利用する頻度は多くないので1度本を終了して2週目に
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【デザインパターン①】Iteratorパターン

こんにちは。久しぶりすぎる。 なかなかアウトプットの習慣って身につかないですねぇ。 ということで、ちょっとハードルを下げた投稿をコンスタントにしていこうと思ってます。 最近GoFデザインパターンとSpringを勉強しているのでその投稿をしていこーと思ってます。 今日はIteratorパターンについて。 簡単なようで重要な概念。 Iteratorとは Wikipediaには下記のように書いてある。 イテレータ(英語: iterator)とは、プログラミング言語において配列やそれに類似する集合的データ構造(コレクションあるいはコンテナ)の各要素に対する繰り返し処理の抽象化である。 つまり、データが集まっている空間の一つ一つの要素を最初から順番に順番に走査すること。 今から紹介する、Iteratorパターンとは、何かがたくさん集まっているときに、それを順番に指し示していき、全体をスキャンしていく処理を一般化したものをいう。 登場オブジェクト Aggregateインターフェース 数え上げを行われる対象物「集合体」を表す。 Aggregate.java public interface Aggregate { public abstract Iterator iterator(); } Iteratorインターフェース 要素の数え上げを行うもの。 Iterator.java public interface Iterator { public abstract boolean hasNext(); public abstract Object next(); } Aggregate実装クラス(ConcreateAggregate) Iterator実装クラス (ConcreateIterator) 上記のパターンを具体的に落とし込むと下記のようなものがあるだろう。 Aggregateインターフェース Iteratorインターフェース Aggregate実装クラス(Parking) Iterator実装クラス (ParkingIterator) モデル(Car) 名前 解説 Aggregate 集合体を表すインタフェース Iterator 数え上げ、スキャンを行うインタフェース Parking 駐車場を表すAggregateの実装を行うクラス ParkingIterator 駐車場をスキャンするクラス Car 車を表すクラス サンプルコード Parking.java public class Parking implements Aggregate { private Car[] cars; private int last = 0; public Parking(int maxsize) { this.cars = new Car[maxsize]; } public Car getCarAt(int index) { return cars[index]; } public void appendCar(Car car) { this.cars[last] = car; last++; } public int getLength() { return last; } public Iterator iterator() { return new ParkingIterator(this); } } ParkingIterator.java public class ParkingIterator implements Iterator { private Parking parking; private int index; public ParkingIterator(Parking parking) { this.parking = parking; this.index = 0; } public boolean hasNext() { if(index < parking.getLength()) { return true; } else { return false; } } public Object next() { Car car = parking.getCarAt(index); index++; return car; } } Car.java public class Car { private String name; private String color; public Car(String name, String color) { this.name = name; this.color = color; } public String getName() { return name; } public String getColor() { return color; } 細かいメソッドの説明は省略するが、重要なメソッドは - iterator - hasNext - next の3つであろう。 iteratorメソッド このメソッドは、Parkingクラスに対応するIteratorとして、ParkingIteratorというクラスのインスタンスを生成してそれを返す。この駐車場の車を数え上げたいときに、このiteratorメソッドが呼び出される。 hasNextメソッド 「次の車」があるかどうかを調べ、あるならtrue, ないならfalseを返す。 nextメソッド 現在注目している車を返し、さらに次へ進めるためのメソッド。 Iteratorパターンを利用するメリット 実際、Iteratorパターンを利用せずとも for文やWhile文で同じ全体走査は可能である。 ではなぜわざわざインターフェースなどを用いて実装するのだろうか。 その理由は「実装とは切り離して、数え上げが行うことができる」からである。 while (it.hasNext()) { Car car = (Car) it.next(); System.out.println(car.getName()); } 上記の例を見ると、使われているのは"next"と"hasNext"というメソッドだけであり、Parkingの実装で使われているメソッドは呼び出されていない。つまり、このコードはParkingの実装には依存しない。 例えば、今Parkingの中にはCarsという配列を使われているが、これをVectorを使うようにプログラムを修正したとしよう。この場合でも、Parkingがiteratorメソッドを持っており、正しいIteratorを返してくれれば上記のコードを変更する必要はない。 Iteratorがなかったら・・・ int i = 0; while (i < parking.getLength()) { Car car = parking.getCarAt(i); System.out.println(car.getName()); } と書けると思うんですが、問題ないよなぁ。としみじみ。Iteratorいらんやん。。。 色々調べると納得したので下で記述 もし、Carsの長さがこの時点で決まっていたら、parking.getLength()が発動できるが、未定だった場合上限が決められない。 それに対して、Iteratorの場合は「次の要素があるかないか」ということを調べるのでコレクションのようなサイズが未定なものでも使うことができる。言い換えると、上記のコードはParkingに依存しているといえる。依存は嫌ぁぁぁ。 まとめ Iteratorパターンとは 集合体を順番に指し示していき、全体をスキャンしていく処理を一般化したもの。 目的は、クラスの再利用かを促進するためである。 デザインパターンって難しい。 何が難しいかって、一人でコーディングするときは意識しないよね。 ゴリゴリに書いてしまええええって思っていたので・・・。 そしてもっと短くわかりやすくコンパクトに書きたい・・・・。 次はその辺を挑戦する。 今日はこの辺で。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む