From e127296207c78742a04774491e72514523d880a4 Mon Sep 17 00:00:00 2001 From: "Tomachi [ICHIGO]" Date: Wed, 30 Mar 2022 04:01:43 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=90=E3=82=B0=E3=83=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../javajaotan2/event/Event_BugReaction.java | 125 ++++++++++++++++++ .../com/jaoafa/javajaotan2/lib/Channels.java | 1 + .../javajaotan2/lib/JavajaotanConfig.java | 10 +- .../javajaotan2/lib/JavajaotanLibrary.java | 118 ++++++++++++++++- 4 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/jaoafa/javajaotan2/event/Event_BugReaction.java diff --git a/src/main/java/com/jaoafa/javajaotan2/event/Event_BugReaction.java b/src/main/java/com/jaoafa/javajaotan2/event/Event_BugReaction.java new file mode 100644 index 00000000..43120114 --- /dev/null +++ b/src/main/java/com/jaoafa/javajaotan2/event/Event_BugReaction.java @@ -0,0 +1,125 @@ +/* + * jaoLicense + * + * Copyright (c) 2022 jao Minecraft Server + * + * The following license applies to this project: jaoLicense + * + * Japanese: https://github.com/jaoafa/jao-Minecraft-Server/blob/master/jaoLICENSE.md + * English: https://github.com/jaoafa/jao-Minecraft-Server/blob/master/jaoLICENSE-en.md + */ + +package com.jaoafa.javajaotan2.event; + +import com.jaoafa.javajaotan2.Main; +import com.jaoafa.javajaotan2.lib.Channels; +import com.jaoafa.javajaotan2.lib.JavajaotanLibrary; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.NotNull; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class Event_BugReaction extends ListenerAdapter { + String targetReaction = "\uD83D\uDC1B"; // :bug: + String repo = "jaoafa/jao-Minecraft-Server"; + + @Override + public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { + if (Main.getConfig().getGuildId() != event.getGuild().getIdLong()) return; + + User user = event.getUser(); + + if (user == null) user = event.retrieveUser().complete(); + if (user.isBot()) return; + + MessageReaction.ReactionEmote emote = event.getReactionEmote(); + + if (!emote.isEmoji()) return; + if (!emote.getEmoji().equals(targetReaction)) return; + + Message message = event.retrieveMessage().complete(); + List users = message.retrieveReactionUsers(targetReaction).complete(); + + if (users.size() != 1) { + // 1人以外 = 0もしくは2人以上 = 既に報告済み + return; + } + TextChannel channel = event.getTextChannel(); + + // Issueを作成する + ZonedDateTime createdAt = message.getTimeCreated().atZoneSameInstant(ZoneId.of("Asia/Tokyo")); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + String title = user.getAsTag() + " による #" + message.getChannel().getName() + " での不具合報告"; + String body = """ + ## 不具合と思われるメッセージ (または報告) + + %s + + URL: %s + + ## 不具合報告者 + + %s + """.formatted( + createdAt.format(formatter) + " に送信された `" + message.getAuthor().getAsTag() + "` による `#" + message.getChannel().getName() + "` でのメッセージ", + message.getJumpUrl(), + "`" + user.getAsTag() + "`" + ); + + JavajaotanLibrary.CreateIssueResponse response = JavajaotanLibrary.createIssue(repo, title, body); + JavajaotanLibrary.IssueResponseType responseType = response.responseType(); + int issueNumber = response.issueNumber(); + + // スレッドを立てる + TextChannel developmentChannel = Channels.development.getChannel(); + if (developmentChannel == null) { + // チャンネルが見つからない + channel + .sendMessage(user.getAsMention() + " スレッドの作成に失敗しました。developmentチャンネルが見つかりません。") + .delay(1, TimeUnit.MINUTES, Main.getScheduler()) // delete 1 minute later + .flatMap(Message::delete) + .queue(); + return; + } + + String mainMessage = """ + ## 不具合と思われるメッセージ (または報告) + + %s + + URL: %s + + ## 不具合報告者 + + %s + """.formatted( + createdAt.format(formatter) + " に送信された " + message.getAuthor().getAsMention() + " による " + message.getChannel().getAsMention() + " でのメッセージ", + message.getJumpUrl(), + user.getAsMention() + ); + + List messages = new ArrayList<>(); + if (responseType == JavajaotanLibrary.IssueResponseType.SUCCESS) { + messages.add("[LINKED-ISSUE:jaoafa/jao-Minecraft-Server#" + issueNumber + "]"); + messages.add(""); + } + messages.add(":bug: リアクションにより、不具合の報告がなされました。"); + messages.add(mainMessage); + if (responseType == JavajaotanLibrary.IssueResponseType.SUCCESS) { + messages.add("Issue: https://github.com/jaoafa/jao-Minecraft-Server/issues/" + issueNumber); + } + + String threadTitle = (responseType == JavajaotanLibrary.IssueResponseType.SUCCESS ? "*" + issueNumber + " " : "") + title; + ThreadChannel thread = developmentChannel.createThreadChannel(threadTitle).complete(); + thread.sendMessage(String.join("\n", messages)).queue(); + + message.addReaction(targetReaction).queue(); + } +} diff --git a/src/main/java/com/jaoafa/javajaotan2/lib/Channels.java b/src/main/java/com/jaoafa/javajaotan2/lib/Channels.java index 6dc0fa7e..4bf91d08 100644 --- a/src/main/java/com/jaoafa/javajaotan2/lib/Channels.java +++ b/src/main/java/com/jaoafa/javajaotan2/lib/Channels.java @@ -22,6 +22,7 @@ public enum Channels { invite_checker(893166232772161577L), c659(621632815599190016L), // 最初を数字にできない… meeting_vote(597423974816808970L), + development(597765518501478401L), vc_speech_log(927666435336056862L); private long channel_id; diff --git a/src/main/java/com/jaoafa/javajaotan2/lib/JavajaotanConfig.java b/src/main/java/com/jaoafa/javajaotan2/lib/JavajaotanConfig.java index 146c43da..4c6c1158 100644 --- a/src/main/java/com/jaoafa/javajaotan2/lib/JavajaotanConfig.java +++ b/src/main/java/com/jaoafa/javajaotan2/lib/JavajaotanConfig.java @@ -1,7 +1,7 @@ /* * jaoLicense * - * Copyright (c) 2021 jao Minecraft Server + * Copyright (c) 2022 jao Minecraft Server * * The following license applies to this project: jaoLicense * @@ -29,6 +29,7 @@ public class JavajaotanConfig { String customSearchCX; String gasTranslateAPIUrl; String detectLanguageAPIToken; + String githubAPIToken; public JavajaotanConfig() throws RuntimeException { logger = Main.getLogger(); @@ -53,6 +54,7 @@ public JavajaotanConfig() throws RuntimeException { customSearchCX = config.optString("customSearchCX"); gasTranslateAPIUrl = config.optString("gasTranslateAPIUrl"); detectLanguageAPIToken = config.optString("detectLanguageAPIToken"); + githubAPIToken = config.optString("githubAPIToken"); // -- データベース関連 if (config.has("main_database")) { @@ -130,7 +132,13 @@ public String getGASTranslateAPIUrl() { return gasTranslateAPIUrl; } + @Nullable public String getDetectLanguageAPIToken() { return detectLanguageAPIToken; } + + @Nullable + public String getGitHubAPIToken() { + return githubAPIToken; + } } diff --git a/src/main/java/com/jaoafa/javajaotan2/lib/JavajaotanLibrary.java b/src/main/java/com/jaoafa/javajaotan2/lib/JavajaotanLibrary.java index af93a669..aa7b857b 100644 --- a/src/main/java/com/jaoafa/javajaotan2/lib/JavajaotanLibrary.java +++ b/src/main/java/com/jaoafa/javajaotan2/lib/JavajaotanLibrary.java @@ -1,7 +1,7 @@ /* * jaoLicense * - * Copyright (c) 2021 jao Minecraft Server + * Copyright (c) 2022 jao Minecraft Server * * The following license applies to this project: jaoLicense * @@ -11,10 +11,17 @@ package com.jaoafa.javajaotan2.lib; +import com.jaoafa.javajaotan2.Main; import net.dv8tion.jda.api.entities.*; +import okhttp3.*; +import org.json.JSONArray; +import org.json.JSONObject; +import javax.annotation.Nonnull; +import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -117,4 +124,113 @@ public static String getContentDisplay(Message message, String raw) { } return tmp; } + + @Nonnull + public static CreateIssueResponse createIssue(String repo, String title, String body) { + String githubToken = Main.getConfig().getGitHubAPIToken(); + if (githubToken == null || githubToken.isEmpty()) { + return new CreateIssueResponse( + IssueResponseType.FAILED, + "GitHub API Token が設定されていません。", + -1 + ); + } + String url = String.format("https://api.github.com/repos/%s/issues", repo); + JSONObject json = new JSONObject() + .put("title", title) + .put("body", body) + .put("labels", new JSONArray() + .put("\uD83D\uDC1Bbug")); + + try { + OkHttpClient client = new OkHttpClient(); + RequestBody requestBody = RequestBody.create(json.toString(), MediaType.parse("application/json; charset=UTF-8")); + Request request = new Request.Builder() + .url(url) + .header("Authorization", String.format("token %s", githubToken)) + .post(requestBody) + .build(); + JSONObject obj; + try (Response response = client.newCall(request).execute()) { + if (response.code() != 201) { + return new CreateIssueResponse( + IssueResponseType.FAILED, + "Issue の作成に失敗しました。", + -1 + ); + } + obj = new JSONObject(Objects.requireNonNull(response.body()).string()); + } + + int issueNum = obj.getInt("number"); + return new CreateIssueResponse( + IssueResponseType.SUCCESS, + "Issue の作成に成功しました。", + issueNum + ); + } catch (IOException e) { + e.printStackTrace(); + return new CreateIssueResponse( + IssueResponseType.FAILED, + "Issue の作成に失敗しました。" + e.getMessage(), + -1 + ); + } + } + + public record CreateIssueResponse(IssueResponseType responseType, String message, int issueNumber) { + } + + @Nonnull + public static CreateIssueCommentResponse createIssueComment(String repo, int issueNum, String body) { + String githubToken = Main.getConfig().getGitHubAPIToken(); + if (githubToken == null || githubToken.isEmpty()) { + return new CreateIssueCommentResponse( + IssueResponseType.FAILED, + "GitHub API Token が設定されていません。" + ); + } + String url = String.format("https://api.github.com/repos/%s/issues/%s/comments", repo, issueNum); + JSONObject json = new JSONObject() + .put("body", body); + + try { + OkHttpClient client = new OkHttpClient(); + RequestBody requestBody = RequestBody.create(json.toString(), MediaType.parse("application/json; charset=UTF-8")); + Request request = new Request.Builder() + .url(url) + .header("Authorization", String.format("token %s", githubToken)) + .post(requestBody) + .build(); + JSONObject obj; + try (Response response = client.newCall(request).execute()) { + if (response.code() != 201) { + return new CreateIssueCommentResponse( + IssueResponseType.FAILED, + "Issue コメントの作成に失敗しました。" + ); + } + obj = new JSONObject(Objects.requireNonNull(response.body()).string()); + } + + return new CreateIssueCommentResponse( + IssueResponseType.SUCCESS, + "Issue コメントの作成に成功しました。" + ); + } catch (IOException e) { + e.printStackTrace(); + return new CreateIssueCommentResponse( + IssueResponseType.FAILED, + "Issue コメントの作成に失敗しました。" + e.getMessage() + ); + } + } + + public record CreateIssueCommentResponse(IssueResponseType responseType, String message) { + } + + public enum IssueResponseType { + SUCCESS, + FAILED + } }