Skip to content

Commit 03b1112

Browse files
authored
Feature/jshell (#870)
* Feature/jshell from fork (#869) * JShell feature added * Added option to get the snippets of another user * Refactored JShell eval into its own class * Changed RateLimiter so it's a global limiter and not a per user limiter + merged user and oneOffSession together * Added context action for running java code * Fixing sonar wranings for jshell * Added startupt scripts for jshell * Formating jshell classes * JShell javadoc and many minor code improvements * JShell more minor code improvements * Other minor code changes * JShell now allows wrong uri and dead server, will give correct response to the user and will log them as warns * Forgot one exception javadoc for jshell feature * JShell refactored snippets send conditions
1 parent 0de9992 commit 03b1112

26 files changed

+1229
-5
lines changed

application/config.json.template

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,9 @@
9090
"logErrorChannelWebhook": "<put_your_webhook_here>",
9191
"openaiApiKey": "<check pins in #tjbot_discussion for the key>",
9292
"sourceCodeBaseUrl": "<https://github.com/<your_account_here>/<your_repo_here>/blob/master/application/src/main/java/>"
93+
"jshell": {
94+
"baseUrl": "<put_jshell_rest_api_url_here>",
95+
"rateLimitWindowSeconds": 10,
96+
"rateLimitRequestsInWindow": 3
97+
}
9398
}

application/src/main/java/org/togetherjava/tjbot/config/Config.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public final class Config {
3838
private final String logErrorChannelWebhook;
3939
private final String openaiApiKey;
4040
private final String sourceCodeBaseUrl;
41+
private final JShellConfig jshell;
4142

4243
@SuppressWarnings("ConstructorWithTooManyParameters")
4344
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
@@ -74,7 +75,8 @@ private Config(@JsonProperty(value = "token", required = true) String token,
7475
@JsonProperty(value = "logErrorChannelWebhook",
7576
required = true) String logErrorChannelWebhook,
7677
@JsonProperty(value = "openaiApiKey", required = true) String openaiApiKey,
77-
@JsonProperty(value = "sourceCodeBaseUrl", required = true) String sourceCodeBaseUrl) {
78+
@JsonProperty(value = "sourceCodeBaseUrl", required = true) String sourceCodeBaseUrl,
79+
@JsonProperty(value = "jshell", required = true) JShellConfig jshell) {
7880
this.token = Objects.requireNonNull(token);
7981
this.gistApiKey = Objects.requireNonNull(gistApiKey);
8082
this.databasePath = Objects.requireNonNull(databasePath);
@@ -99,6 +101,7 @@ private Config(@JsonProperty(value = "token", required = true) String token,
99101
this.logErrorChannelWebhook = Objects.requireNonNull(logErrorChannelWebhook);
100102
this.openaiApiKey = Objects.requireNonNull(openaiApiKey);
101103
this.sourceCodeBaseUrl = Objects.requireNonNull(sourceCodeBaseUrl);
104+
this.jshell = Objects.requireNonNull(jshell);
102105
}
103106

104107
/**
@@ -330,4 +333,13 @@ public String getOpenaiApiKey() {
330333
public String getSourceCodeBaseUrl() {
331334
return sourceCodeBaseUrl;
332335
}
336+
337+
/**
338+
* The configuration about jshell REST API and command/code action settings.
339+
*
340+
* @return the jshell configuration
341+
*/
342+
public JShellConfig getJshell() {
343+
return jshell;
344+
}
333345
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.togetherjava.tjbot.config;
2+
3+
4+
import org.togetherjava.tjbot.features.utils.RateLimiter;
5+
6+
import java.util.Objects;
7+
8+
/**
9+
* JShell config.
10+
*
11+
* @param baseUrl the base url of the JShell REST API
12+
* @param rateLimitWindowSeconds the number of seconds of the {@link RateLimiter rate limiter} for
13+
* jshell commands and code actions
14+
* @param rateLimitRequestsInWindow the number of requests of the {@link RateLimiter rate limiter}
15+
* for jshell commands and code actions
16+
*/
17+
public record JShellConfig(String baseUrl, int rateLimitWindowSeconds,
18+
int rateLimitRequestsInWindow) {
19+
/**
20+
* Creates a JShell config.
21+
*
22+
* @param baseUrl the base url of the JShell REST API, must be not null
23+
* @param rateLimitWindowSeconds the number of seconds of the {@link RateLimiter rate limiter}
24+
* for jshell commands and code actions, must be higher than 0
25+
* @param rateLimitRequestsInWindow the number of requests of the {@link RateLimiter rate
26+
* limiter} for jshell commands and code actions, must be higher than 0
27+
*/
28+
public JShellConfig {
29+
Objects.requireNonNull(baseUrl);
30+
if (rateLimitWindowSeconds < 0) {
31+
throw new IllegalArgumentException(
32+
"Illegal rateLimitWindowSeconds : " + rateLimitWindowSeconds);
33+
}
34+
if (rateLimitRequestsInWindow < 0) {
35+
throw new IllegalArgumentException(
36+
"Illegal rateLimitRequestsInWindow : " + rateLimitRequestsInWindow);
37+
}
38+
}
39+
}

application/src/main/java/org/togetherjava/tjbot/features/Features.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import org.togetherjava.tjbot.features.code.CodeMessageManualDetection;
1414
import org.togetherjava.tjbot.features.filesharing.FileSharingMessageListener;
1515
import org.togetherjava.tjbot.features.help.*;
16+
import org.togetherjava.tjbot.features.jshell.JShellCommand;
17+
import org.togetherjava.tjbot.features.jshell.JShellEval;
1618
import org.togetherjava.tjbot.features.mathcommands.TeXCommand;
1719
import org.togetherjava.tjbot.features.mathcommands.wolframalpha.WolframAlphaCommand;
1820
import org.togetherjava.tjbot.features.mediaonly.MediaOnlyChannelListener;
@@ -67,13 +69,15 @@ private Features() {
6769
* @return a collection of all features
6870
*/
6971
public static Collection<Feature> createFeatures(JDA jda, Database database, Config config) {
72+
JShellEval jshellEval = new JShellEval(config.getJshell());
73+
7074
TagSystem tagSystem = new TagSystem(database);
7175
BookmarksSystem bookmarksSystem = new BookmarksSystem(config, database);
7276
ModerationActionsStore actionsStore = new ModerationActionsStore(database);
7377
ModAuditLogWriter modAuditLogWriter = new ModAuditLogWriter(config);
7478
ScamHistoryStore scamHistoryStore = new ScamHistoryStore(database);
7579
HelpSystemHelper helpSystemHelper = new HelpSystemHelper(config, database);
76-
CodeMessageHandler codeMessageHandler = new CodeMessageHandler();
80+
CodeMessageHandler codeMessageHandler = new CodeMessageHandler(jshellEval);
7781
ChatGptService chatGptService = new ChatGptService(config);
7882

7983
// NOTE The system can add special system relevant commands also by itself,
@@ -143,6 +147,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
143147
features.add(new ReportCommand(config));
144148
features.add(new BookmarksCommand(bookmarksSystem));
145149
features.add(new ChatGptCommand(chatGptService));
150+
features.add(new JShellCommand(jshellEval));
146151
return features;
147152
}
148153
}

application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.togetherjava.tjbot.features.UserInteractor;
2121
import org.togetherjava.tjbot.features.componentids.ComponentIdGenerator;
2222
import org.togetherjava.tjbot.features.componentids.ComponentIdInteractor;
23+
import org.togetherjava.tjbot.features.jshell.JShellEval;
2324
import org.togetherjava.tjbot.features.utils.CodeFence;
2425
import org.togetherjava.tjbot.features.utils.MessageUtils;
2526

@@ -62,11 +63,14 @@ public final class CodeMessageHandler extends MessageReceiverAdapter implements
6263

6364
/**
6465
* Creates a new instance.
66+
*
67+
* @param jshellEval used to execute java code and build visual result
6568
*/
66-
public CodeMessageHandler() {
69+
public CodeMessageHandler(JShellEval jshellEval) {
6770
componentIdInteractor = new ComponentIdInteractor(getInteractionType(), getName());
6871

69-
List<CodeAction> codeActions = List.of(new FormatCodeCommand());
72+
List<CodeAction> codeActions =
73+
List.of(new FormatCodeCommand(), new EvalCodeCommand(jshellEval));
7074

7175
labelToCodeAction = codeActions.stream()
7276
.collect(Collectors.toMap(CodeAction::getLabel, Function.identity(), (x, y) -> y,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.togetherjava.tjbot.features.code;
2+
3+
import net.dv8tion.jda.api.EmbedBuilder;
4+
import net.dv8tion.jda.api.entities.MessageEmbed;
5+
6+
import org.togetherjava.tjbot.features.jshell.JShellEval;
7+
import org.togetherjava.tjbot.features.utils.CodeFence;
8+
import org.togetherjava.tjbot.features.utils.Colors;
9+
import org.togetherjava.tjbot.features.utils.ConnectionFailedException;
10+
import org.togetherjava.tjbot.features.utils.RequestFailedException;
11+
12+
/**
13+
* Evaluates the given code with jshell.
14+
* <p>
15+
* It will not work of the code isn't valid java or jshell compatible code.
16+
*/
17+
final class EvalCodeCommand implements CodeAction {
18+
private final JShellEval jshellEval;
19+
20+
EvalCodeCommand(JShellEval jshellEval) {
21+
this.jshellEval = jshellEval;
22+
}
23+
24+
@Override
25+
public String getLabel() {
26+
return "Run code";
27+
}
28+
29+
@Override
30+
public MessageEmbed apply(CodeFence codeFence) {
31+
if (codeFence.code().isEmpty()) {
32+
return new EmbedBuilder().setColor(Colors.ERROR_COLOR)
33+
.setDescription("There is nothing to evaluate")
34+
.build();
35+
}
36+
try {
37+
return jshellEval.evaluateAndRespond(null, codeFence.code(), false, false);
38+
} catch (RequestFailedException | ConnectionFailedException e) {
39+
return new EmbedBuilder().setColor(Colors.ERROR_COLOR)
40+
.setDescription("Request failed: " + e.getMessage())
41+
.build();
42+
}
43+
}
44+
45+
}

0 commit comments

Comments
 (0)