Skip to content

Commit f0a93b5

Browse files
authored
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
1 parent 0de9992 commit f0a93b5

23 files changed

+797
-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": "http://localhost:8080/jshell/",
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.
339+
*
340+
* @return the jshell configuration
341+
*/
342+
public JShellConfig getJshell() {
343+
return jshell;
344+
}
333345
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.togetherjava.tjbot.config;
2+
3+
import com.linkedin.urls.Url;
4+
5+
import java.net.MalformedURLException;
6+
7+
public record JShellConfig(String baseUrl, int rateLimitWindowSeconds,
8+
int rateLimitRequestsInWindow) {
9+
public JShellConfig {
10+
try {
11+
Url.create(baseUrl);
12+
} catch (MalformedURLException e) {
13+
throw new IllegalArgumentException(e);
14+
}
15+
if (rateLimitWindowSeconds <= 0)
16+
throw new IllegalArgumentException(
17+
"Illegal rateLimitWindowSeconds : " + rateLimitWindowSeconds);
18+
if (rateLimitRequestsInWindow <= 0)
19+
throw new IllegalArgumentException(
20+
"Illegal rateLimitRequestsInWindow : " + rateLimitRequestsInWindow);
21+
}
22+
}

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: 4 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

@@ -63,10 +64,11 @@ public final class CodeMessageHandler extends MessageReceiverAdapter implements
6364
/**
6465
* Creates a new instance.
6566
*/
66-
public CodeMessageHandler() {
67+
public CodeMessageHandler(JShellEval jshellEval) {
6768
componentIdInteractor = new ComponentIdInteractor(getInteractionType(), getName());
6869

69-
List<CodeAction> codeActions = List.of(new FormatCodeCommand());
70+
List<CodeAction> codeActions =
71+
List.of(new FormatCodeCommand(), new EvalCodeCommand(jshellEval));
7072

7173
labelToCodeAction = codeActions.stream()
7274
.collect(Collectors.toMap(CodeAction::getLabel, Function.identity(), (x, y) -> y,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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.jshell.render.Colors;
8+
import org.togetherjava.tjbot.features.utils.CodeFence;
9+
import org.togetherjava.tjbot.features.utils.RequestFailedException;
10+
11+
/**
12+
* Evaluates the given code.
13+
*/
14+
final class EvalCodeCommand implements CodeAction {
15+
private final JShellEval jshellEval;
16+
17+
EvalCodeCommand(JShellEval jshellEval) {
18+
this.jshellEval = jshellEval;
19+
}
20+
21+
@Override
22+
public String getLabel() {
23+
return "Run code";
24+
}
25+
26+
@Override
27+
public MessageEmbed apply(CodeFence codeFence) {
28+
if (codeFence.code().isEmpty()) {
29+
return new EmbedBuilder().setColor(Colors.ERROR_COLOR)
30+
.setDescription("There is nothing to evaluate")
31+
.build();
32+
}
33+
try {
34+
return jshellEval.evaluateAndRespond(null, codeFence.code(), false);
35+
} catch (RequestFailedException e) {
36+
return new EmbedBuilder().setColor(Colors.ERROR_COLOR)
37+
.setDescription("Request failed: " + e.getMessage())
38+
.build();
39+
}
40+
}
41+
42+
}

0 commit comments

Comments
 (0)