|
1 | 1 | package org.togetherjava.tjbot.features.messages; |
2 | 2 |
|
3 | | -import net.dv8tion.jda.api.entities.MessageEmbed; |
4 | 3 | import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; |
| 4 | +import net.dv8tion.jda.api.interactions.commands.OptionMapping; |
5 | 5 | import net.dv8tion.jda.api.interactions.commands.OptionType; |
6 | 6 | import net.dv8tion.jda.api.interactions.commands.build.OptionData; |
| 7 | +import org.jetbrains.annotations.Nullable; |
7 | 8 | import org.slf4j.Logger; |
8 | 9 | import org.slf4j.LoggerFactory; |
9 | 10 |
|
10 | 11 | import org.togetherjava.tjbot.features.CommandVisibility; |
11 | 12 | import org.togetherjava.tjbot.features.SlashCommandAdapter; |
| 13 | +import org.togetherjava.tjbot.features.chatgpt.ChatGptModel; |
| 14 | +import org.togetherjava.tjbot.features.chatgpt.ChatGptService; |
12 | 15 |
|
13 | 16 | import java.util.Arrays; |
| 17 | +import java.util.Objects; |
14 | 18 | import java.util.Optional; |
15 | 19 |
|
16 | 20 | /** |
17 | | - * The implemented command is {@code /rewrite}, which allows users to have their message rewritten |
18 | | - * in a clearer, more professional, or better structured form using ChatGPT AI. |
| 21 | + * The implemented command is {@code /rewrite-msg}, which allows users to have their message |
| 22 | + * rewritten in a clearer, more professional, or better structured form using ChatGPT AI. |
19 | 23 | * <p> |
20 | 24 | * The rewritten message is shown as an ephemeral message visible only to the user who triggered the |
21 | 25 | * command, making it perfect for getting quick writing improvements without cluttering the channel. |
|
24 | 28 | */ |
25 | 29 | public final class RewriteMsgCommand extends SlashCommandAdapter { |
26 | 30 | private static final Logger logger = LoggerFactory.getLogger(RewriteMsgCommand.class); |
27 | | - public static final String COMMAND_NAME = "rewrite"; |
| 31 | + public static final String COMMAND_NAME = "rewrite-msg"; |
28 | 32 | private static final String MESSAGE_OPTION = "message"; |
29 | 33 | private static final String TONE_OPTION = "tone"; |
30 | 34 | private static final int MAX_MESSAGE_LENGTH = 500; |
31 | 35 | private static final int MIN_MESSAGE_LENGTH = 3; |
| 36 | + private static final ChatGptModel CHAT_GPT_MODEL = ChatGptModel.HIGH_QUALITY; |
32 | 37 |
|
33 | | - private final RewriteMsgService rewriteMsgService; |
| 38 | + private final ChatGptService chatGptService; |
34 | 39 |
|
| 40 | + private static String buildResponse(String userMessage, String rewrittenMessage, MsgTone tone) { |
| 41 | + final String toneLabel = tone.displayName; |
35 | 42 |
|
36 | | - public RewriteMsgCommand(RewriteMsgService rewriteMsgService) { |
37 | | - super(COMMAND_NAME, "Rewrite your message in a clearer, more professional form", |
38 | | - CommandVisibility.GUILD); |
| 43 | + return """ |
| 44 | + **Rewritten message (%s)** |
39 | 45 |
|
40 | | - this.rewriteMsgService = rewriteMsgService; |
| 46 | + **Original:** |
| 47 | + %s |
41 | 48 |
|
42 | | - logger.debug("Initializing RewriteMsgCommand with ChatGptService and HelpSystemHelper"); |
| 49 | + **Rewritten:** |
| 50 | + %s""".formatted(toneLabel, userMessage, rewrittenMessage); |
| 51 | + } |
43 | 52 |
|
44 | | - final OptionData toneOption = new OptionData(OptionType.STRING, TONE_OPTION, |
45 | | - "The tone/style for the rewritten message (default: " |
46 | | - + RewriteMsgTone.CLEAR.getDisplayName() + ")", |
47 | | - false); |
| 53 | + private static String buildChatGptPrompt(String userMessage, MsgTone tone) { |
| 54 | + return """ |
| 55 | + Please rewrite the following message to make it clearer, more professional, \ |
| 56 | + and better structured. Maintain the original meaning while improving the quality \ |
| 57 | + of the writing. Do NOT use em-dashes (—). %s |
| 58 | +
|
| 59 | + If the message is already well-written, provide minor improvements. |
48 | 60 |
|
49 | | - logger.debug("Adding tone choices to command options"); |
50 | | - Arrays.stream(RewriteMsgTone.values()).forEach(tone -> { |
51 | | - toneOption.addChoice(tone.getDisplayName(), tone.name()); |
52 | | - logger.debug("Added tone choice: {} ({})", tone.getDisplayName(), tone.name()); |
53 | | - }); |
| 61 | + Original message: |
| 62 | + %s""".formatted(tone.description, userMessage); |
| 63 | + } |
| 64 | + |
| 65 | + /** |
| 66 | + * Creates the slash command definition and configures available options for rewriting messages. |
| 67 | + * |
| 68 | + * @param chatGptService service for interacting with ChatGPT |
| 69 | + */ |
| 70 | + public RewriteMsgCommand(ChatGptService chatGptService) { |
| 71 | + super(COMMAND_NAME, "Let AI rephrase and improve your message", CommandVisibility.GUILD); |
| 72 | + |
| 73 | + this.chatGptService = chatGptService; |
54 | 74 |
|
55 | 75 | final OptionData messageOption = |
56 | 76 | new OptionData(OptionType.STRING, MESSAGE_OPTION, "The message you want to rewrite", |
57 | 77 | true) |
58 | 78 | .setMinLength(MIN_MESSAGE_LENGTH) |
59 | 79 | .setMaxLength(MAX_MESSAGE_LENGTH); |
60 | 80 |
|
61 | | - logger.debug("Configured message option: min={}, max={}", MIN_MESSAGE_LENGTH, |
62 | | - MAX_MESSAGE_LENGTH); |
| 81 | + final OptionData toneOption = new OptionData(OptionType.STRING, TONE_OPTION, |
| 82 | + "The tone/style for the rewritten message (default: " + MsgTone.CLEAR.displayName |
| 83 | + + ")", |
| 84 | + false); |
| 85 | + |
| 86 | + Arrays.stream(MsgTone.values()) |
| 87 | + .forEach(tone -> toneOption.addChoice(tone.displayName, tone.name())); |
63 | 88 |
|
64 | | - super.getData().addOptions(messageOption, toneOption); |
65 | | - logger.debug("RewriteMsgCommand initialization complete"); |
| 89 | + getData().addOptions(messageOption, toneOption); |
66 | 90 | } |
67 | 91 |
|
68 | 92 | @Override |
69 | 93 | public void onSlashCommand(SlashCommandInteractionEvent event) { |
70 | | - logger.debug("onSlashCommand method invoked"); |
| 94 | + final String userMessage = |
| 95 | + Objects.requireNonNull(event.getOption(MESSAGE_OPTION)).getAsString(); |
| 96 | + |
| 97 | + final MsgTone tone = parseTone(event.getOption(TONE_OPTION), event.getUser().getId()); |
| 98 | + |
71 | 99 | event.deferReply(true).queue(); |
72 | | - logger.debug("Reply deferred as ephemeral"); |
73 | 100 |
|
74 | | - final String userId = event.getUser().getId(); |
| 101 | + final Optional<String> rewrittenMessage = this.rewrite(userMessage, tone); |
75 | 102 |
|
76 | | - logger.info("Rewrite command triggered by user: {}", userId); |
| 103 | + if (rewrittenMessage.isEmpty()) { |
| 104 | + logger.debug("Failed to obtain a response for /rewrite-msg, original message: '{}'", |
| 105 | + userMessage); |
| 106 | + event.getHook() |
| 107 | + .editOriginal( |
| 108 | + "An error occurred while processing your request. Please try again later.") |
| 109 | + .queue(); |
| 110 | + return; |
| 111 | + } |
77 | 112 |
|
78 | | - final String userMessage = |
79 | | - this.rewriteMsgService.validateMsg(event.getOption(MESSAGE_OPTION), userId); |
| 113 | + final String response = buildResponse(userMessage, rewrittenMessage.orElseThrow(), tone); |
80 | 114 |
|
81 | | - final RewriteMsgTone tone = |
82 | | - this.rewriteMsgService.parseTone(event.getOption(TONE_OPTION), userId); |
| 115 | + event.getHook().editOriginal(response).queue(); |
| 116 | + } |
83 | 117 |
|
84 | | - final Optional<String> rewrittenMessage = |
85 | | - this.rewriteMsgService.rewrite(userMessage, tone, userId); |
| 118 | + private MsgTone parseTone(@Nullable OptionMapping toneOption, String userId) |
| 119 | + throws IllegalArgumentException { |
| 120 | + if (toneOption == null) { |
| 121 | + logger.debug("Tone option not provided for user: {}, using default CLEAR", userId); |
| 122 | + return MsgTone.CLEAR; |
| 123 | + } |
86 | 124 |
|
87 | | - final Optional<MessageEmbed> responseEmbed = |
88 | | - this.rewriteMsgService.buildResponse(userMessage, rewrittenMessage.orElse(null), |
89 | | - tone, userId, event.getJDA().getSelfUser()); |
| 125 | + final String toneValue = toneOption.getAsString(); |
| 126 | + final MsgTone tone = MsgTone.valueOf(toneValue); |
90 | 127 |
|
91 | | - logger.debug("Sending embed response to user: {}", userId); |
| 128 | + logger.debug("Parsed tone '{}' for user: {}", tone.displayName, userId); |
92 | 129 |
|
93 | | - if (responseEmbed.isPresent()) { |
94 | | - event.getHook() |
95 | | - .sendMessageEmbeds(responseEmbed.get()) |
96 | | - .queue(_ -> logger.info("Rewrite response sent successfully to user: {}", userId), |
97 | | - error -> logger.error("Failed to send rewrite response to user: {}", userId, |
98 | | - error)); |
99 | | - } else { |
100 | | - logger.error("Failed to build response embed for user: {}", userId); |
101 | | - event.getHook() |
102 | | - .sendMessage( |
103 | | - "An error occurred while processing your request. Please try again later.") |
104 | | - .queue(); |
| 130 | + return tone; |
| 131 | + } |
| 132 | + |
| 133 | + private Optional<String> rewrite(String userMessage, MsgTone tone) { |
| 134 | + final String rewritePrompt = buildChatGptPrompt(userMessage, tone); |
| 135 | + |
| 136 | + return chatGptService.ask(rewritePrompt, tone.displayName, CHAT_GPT_MODEL); |
| 137 | + } |
| 138 | + |
| 139 | + private enum MsgTone { |
| 140 | + CLEAR("Clear", "Make it clear and easy to understand."), |
| 141 | + PRO("Pro", "Use a professional and polished tone."), |
| 142 | + DETAILED("Detailed", "Expand with more detail and explanation."), |
| 143 | + TECHNICAL("Technical", "Use technical and specialized language where appropriate."); |
| 144 | + |
| 145 | + private final String displayName; |
| 146 | + private final String description; |
| 147 | + |
| 148 | + MsgTone(String displayName, String description) { |
| 149 | + this.displayName = displayName; |
| 150 | + this.description = description; |
105 | 151 | } |
106 | 152 | } |
107 | 153 | } |
0 commit comments