Skip to content

Commit 02492ce

Browse files
committed
Port over lj-discord-bot into examples
1 parent 770d25c commit 02492ce

26 files changed

+2173
-0
lines changed

example/lj-discord-bot/build.gradle

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
plugins {
2+
id 'java'
3+
id 'application'
4+
id 'com.github.johnrengelman.shadow' version '8.1.1'
5+
}
6+
7+
application {
8+
mainClass = 'com.javadiscord.bot.Main'
9+
}
10+
11+
dependencies {
12+
implementation 'com.github.docker-java:docker-java:3.3.6'
13+
implementation 'com.theokanning.openai-gpt3-java:service:0.18.2'
14+
implementation 'com.rometools:rome:2.1.0'
15+
}
16+
17+
shadowJar {
18+
archiveBaseName.set('lj-discord-bot')
19+
archiveClassifier.set('')
20+
archiveVersion.set('')
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.javadiscord.bot;
2+
3+
import com.javadiscord.jdi.core.Discord;
4+
5+
public class Main {
6+
public static void main(String[] args) {
7+
Discord discord = new Discord(System.getenv("BOT_TOKEN"));
8+
discord.start();
9+
}
10+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.javadiscord.bot.commands.slash;
2+
3+
import java.awt.*;
4+
5+
import com.javadiscord.bot.utils.chatgpt.ChatGPT;
6+
import com.javadiscord.jdi.core.CommandOptionType;
7+
import com.javadiscord.jdi.core.annotations.CommandOption;
8+
import com.javadiscord.jdi.core.annotations.SlashCommand;
9+
import com.javadiscord.jdi.core.interaction.SlashCommandEvent;
10+
import com.javadiscord.jdi.core.models.message.embed.Embed;
11+
import com.javadiscord.jdi.core.models.message.embed.EmbedAuthor;
12+
13+
public class ChatGPTCommand {
14+
private final ChatGPT chatGPT = new ChatGPT();
15+
16+
@SlashCommand(
17+
name = "chatgpt", description = "Ask ChatGPT a question", options = {
18+
@CommandOption(
19+
name = "message", description = "What would you like to ask?", type = CommandOptionType.STRING
20+
)
21+
}
22+
)
23+
public void handle(SlashCommandEvent event) {
24+
event.deferReply();
25+
Thread.ofVirtual().start(() -> handleCommand(event));
26+
}
27+
28+
private void handleCommand(SlashCommandEvent event) {
29+
event.option("message").ifPresent(msg -> {
30+
StringBuilder answer = new StringBuilder();
31+
answer.append(event.user().asMention());
32+
answer.append(" asked:\n");
33+
answer.append(msg.valueAsString());
34+
answer.append("\n");
35+
answer.append("───────────────\n");
36+
37+
chatGPT.ask(msg.valueAsString())
38+
.ifPresentOrElse(
39+
strings -> {
40+
for (String string : strings) {
41+
answer.append(string).append("\n");
42+
}
43+
},
44+
() -> sendChatGptUnavailableMessage(event)
45+
);
46+
47+
Embed embed =
48+
new Embed.Builder().color(Color.CYAN)
49+
.description(answer.toString())
50+
.author(
51+
new EmbedAuthor("", "https://chat.openai.com/favicon-32x32.png", null, null)
52+
).build();
53+
event.reply(embed);
54+
});
55+
}
56+
57+
private void sendChatGptUnavailableMessage(SlashCommandEvent event) {
58+
event.reply("ChatGPT is currently unavailable.");
59+
}
60+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.javadiscord.bot.commands.slash;
2+
3+
import com.javadiscord.jdi.core.annotations.SlashCommand;
4+
import com.javadiscord.jdi.core.interaction.SlashCommandEvent;
5+
6+
public class PingSlashCommand {
7+
8+
@SlashCommand(
9+
name = "ping", description = "Pong!"
10+
)
11+
public void ping(SlashCommandEvent event) {
12+
event.reply("Pong!");
13+
}
14+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package com.javadiscord.bot.commands.slash.jshell;
2+
3+
import java.awt.*;
4+
5+
import com.javadiscord.jdi.core.CommandOptionType;
6+
import com.javadiscord.jdi.core.annotations.CommandOption;
7+
import com.javadiscord.jdi.core.annotations.SlashCommand;
8+
import com.javadiscord.jdi.core.interaction.SlashCommandEvent;
9+
import com.javadiscord.jdi.core.models.message.embed.Embed;
10+
import com.javadiscord.jdi.core.models.message.embed.EmbedAuthor;
11+
import com.javadiscord.jdi.core.models.user.User;
12+
13+
public class JShellCommand {
14+
private final JShellService jShellService = new JShellService();
15+
16+
@SlashCommand(
17+
name = "jshell", description = "Run Java code using JShell", options = {
18+
@CommandOption(
19+
name = "code", description = "The code you would like to execute", type = CommandOptionType.STRING
20+
)
21+
}
22+
)
23+
public void handle(SlashCommandEvent event) {
24+
event.deferReply();
25+
Thread.ofVirtual().start(() -> handleJShell(event));
26+
}
27+
28+
private void handleJShell(SlashCommandEvent event) {
29+
User user = event.user();
30+
31+
long start = System.currentTimeMillis();
32+
33+
event.option("code").ifPresent(msg -> {
34+
JShellResponse response = jShellService.sendRequest(msg.valueAsString());
35+
if (response == null) {
36+
String reply = "Failed to execute the provided code, was it bad?";
37+
Embed embed =
38+
new Embed.Builder()
39+
.author(new EmbedAuthor(user.asMention(), user.avatar(), null, null))
40+
.description(reply)
41+
.color(Color.ORANGE)
42+
.build();
43+
event.reply(embed);
44+
return;
45+
}
46+
47+
if (response.error() != null && !response.error().isEmpty()) {
48+
String reply =
49+
"""
50+
An error occurred while executing command:
51+
52+
```java
53+
%s
54+
```
55+
56+
%s
57+
"""
58+
.formatted(msg.valueAsString(), response.error());
59+
Embed embed =
60+
new Embed.Builder()
61+
.author(new EmbedAuthor(user.asMention(), user.avatar(), null, null))
62+
.description(reply)
63+
.color(Color.RED)
64+
.build();
65+
event.reply(embed);
66+
return;
67+
}
68+
69+
StringBuilder sb = new StringBuilder();
70+
sb.append("## Snippets\n");
71+
for (JShellSnippet snippet : response.events()) {
72+
sb.append("`");
73+
sb.append(snippet.statement());
74+
sb.append("`\n\n");
75+
sb.append("**Status**: ");
76+
sb.append(snippet.status());
77+
sb.append("\n");
78+
79+
if (snippet.value() != null && !snippet.value().isEmpty()) {
80+
sb.append("**Output**\n");
81+
sb.append("```java\n");
82+
sb.append(snippet.value());
83+
sb.append("```\n");
84+
}
85+
}
86+
87+
if (!response.outputStream().isEmpty()) {
88+
sb.append("## Console Output\n");
89+
sb.append("```java\n");
90+
sb.append(response.outputStream());
91+
sb.append("```\n");
92+
}
93+
94+
if (response.errorStream() != null && !response.errorStream().isEmpty()) {
95+
sb.append("## Error Output\n");
96+
sb.append("```java\n");
97+
sb.append(response.errorStream());
98+
sb.append("```\n");
99+
}
100+
101+
Embed.Builder embed =
102+
new Embed.Builder()
103+
.author(new EmbedAuthor(user.asMention(), null, null, null));
104+
105+
if (sb.length() > 4000) {
106+
embed.description(sb.substring(0, 4000));
107+
} else {
108+
embed.description(sb.toString());
109+
}
110+
111+
embed.color(Color.GREEN);
112+
embed.footer("Time taken: " + (System.currentTimeMillis() - start) + "ms");
113+
event.reply(embed.build()).onError(System.err::println);
114+
});
115+
}
116+
117+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.javadiscord.bot.commands.slash.jshell;
2+
3+
import java.util.List;
4+
5+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
6+
import com.fasterxml.jackson.annotation.JsonProperty;
7+
8+
@JsonIgnoreProperties(ignoreUnknown = true)
9+
public record JShellResponse(
10+
@JsonProperty("errorStream") String errorStream,
11+
@JsonProperty("outputStream") String outputStream,
12+
@JsonProperty("events") List<JShellSnippet> events,
13+
@JsonProperty("error") String error
14+
) {}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.javadiscord.bot.commands.slash.jshell;
2+
3+
import java.io.IOException;
4+
import java.net.URI;
5+
import java.net.http.HttpClient;
6+
import java.net.http.HttpRequest;
7+
import java.net.http.HttpResponse;
8+
import java.util.ArrayList;
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
12+
13+
import com.fasterxml.jackson.core.JsonProcessingException;
14+
import com.fasterxml.jackson.databind.ObjectMapper;
15+
import org.apache.logging.log4j.LogManager;
16+
import org.apache.logging.log4j.Logger;
17+
18+
public class JShellService {
19+
private static final Logger LOGGER = LogManager.getLogger(JShellService.class);
20+
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
21+
private static final String API_URL = System.getenv("JSHELL_API_URL");
22+
23+
private final Map<Long, List<String>> history = new HashMap<>();
24+
25+
public JShellService() {}
26+
27+
public JShellResponse sendRequest(String code) {
28+
record Request(String code) {}
29+
try (HttpClient client = HttpClient.newHttpClient()) {
30+
HttpRequest request =
31+
HttpRequest.newBuilder()
32+
.uri(URI.create(API_URL))
33+
.setHeader("Content-Type", "application/json")
34+
.POST(
35+
HttpRequest.BodyPublishers.ofString(
36+
OBJECT_MAPPER.writeValueAsString(new Request(code))
37+
)
38+
)
39+
.build();
40+
HttpResponse<String> response =
41+
client.send(request, HttpResponse.BodyHandlers.ofString());
42+
return OBJECT_MAPPER.readValue(response.body(), JShellResponse.class);
43+
} catch (JsonProcessingException e) {
44+
LOGGER.error("Failed to parse data received from JShell API", e);
45+
} catch (IOException | InterruptedException e) {
46+
LOGGER.error("Failed to send request to JShell API", e);
47+
}
48+
return null;
49+
}
50+
51+
public void updateHistory(long userId, String snippet) {
52+
if (history.containsKey(userId)) {
53+
history.get(userId).add(snippet);
54+
} else {
55+
history.put(userId, new ArrayList<>());
56+
}
57+
}
58+
59+
public List<String> getHistory(long userId) {
60+
if (history.containsKey(userId)) {
61+
return history.get(userId);
62+
}
63+
return new ArrayList<>();
64+
}
65+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.javadiscord.bot.commands.slash.jshell;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
6+
@JsonIgnoreProperties(ignoreUnknown = true)
7+
public record JShellSnippet(
8+
@JsonProperty("statement") String statement,
9+
@JsonProperty("value") String value,
10+
@JsonProperty("status") String status
11+
) {}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.javadiscord.bot.commands.text;
2+
3+
import com.javadiscord.jdi.core.Guild;
4+
import com.javadiscord.jdi.core.models.message.Message;
5+
6+
public interface TextCommand {
7+
void handle(Guild guild, Message message, String input);
8+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.javadiscord.bot.commands.text;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import com.javadiscord.bot.commands.text.impl.*;
7+
8+
public class TextCommandRepository {
9+
private static final Map<String, TextCommand> COMMANDS = new HashMap<>();
10+
11+
static {
12+
COMMANDS.put("clear", new ClearChannelCommand());
13+
COMMANDS.put("mute", new MuteCommand());
14+
COMMANDS.put("unmute", new UnmuteCommand());
15+
COMMANDS.put("say", new SayCommand());
16+
COMMANDS.put("embed", new SayEmbedCommand());
17+
}
18+
19+
public static TextCommand get(String key) {
20+
return COMMANDS.get(key);
21+
}
22+
}

0 commit comments

Comments
 (0)