Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions application/config.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,9 @@
"logErrorChannelWebhook": "<put_your_webhook_here>",
"openaiApiKey": "<check pins in #tjbot_discussion for the key>",
"sourceCodeBaseUrl": "<https://github.com/<your_account_here>/<your_repo_here>/blob/master/application/src/main/java/>"
"jshell": {
"baseUrl": "<put_jshell_rest_api_url_here>",
"rateLimitWindowSeconds": 10,
"rateLimitRequestsInWindow": 3
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public final class Config {
private final String logErrorChannelWebhook;
private final String openaiApiKey;
private final String sourceCodeBaseUrl;
private final JShellConfig jshell;

@SuppressWarnings("ConstructorWithTooManyParameters")
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
Expand Down Expand Up @@ -74,7 +75,8 @@ private Config(@JsonProperty(value = "token", required = true) String token,
@JsonProperty(value = "logErrorChannelWebhook",
required = true) String logErrorChannelWebhook,
@JsonProperty(value = "openaiApiKey", required = true) String openaiApiKey,
@JsonProperty(value = "sourceCodeBaseUrl", required = true) String sourceCodeBaseUrl) {
@JsonProperty(value = "sourceCodeBaseUrl", required = true) String sourceCodeBaseUrl,
@JsonProperty(value = "jshell", required = true) JShellConfig jshell) {
this.token = Objects.requireNonNull(token);
this.gistApiKey = Objects.requireNonNull(gistApiKey);
this.databasePath = Objects.requireNonNull(databasePath);
Expand All @@ -99,6 +101,7 @@ private Config(@JsonProperty(value = "token", required = true) String token,
this.logErrorChannelWebhook = Objects.requireNonNull(logErrorChannelWebhook);
this.openaiApiKey = Objects.requireNonNull(openaiApiKey);
this.sourceCodeBaseUrl = Objects.requireNonNull(sourceCodeBaseUrl);
this.jshell = Objects.requireNonNull(jshell);
}

/**
Expand Down Expand Up @@ -330,4 +333,13 @@ public String getOpenaiApiKey() {
public String getSourceCodeBaseUrl() {
return sourceCodeBaseUrl;
}

/**
* The configuration about jshell REST API and command/code action settings.
*
* @return the jshell configuration
*/
public JShellConfig getJshell() {
return jshell;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.togetherjava.tjbot.config;


import org.togetherjava.tjbot.features.utils.RateLimiter;

import java.util.Objects;

/**
* JShell config.
*
* @param baseUrl the base url of the JShell REST API
* @param rateLimitWindowSeconds the number of seconds of the {@link RateLimiter rate limiter} for
* jshell commands and code actions
* @param rateLimitRequestsInWindow the number of requests of the {@link RateLimiter rate limiter}
* for jshell commands and code actions
*/
public record JShellConfig(String baseUrl, int rateLimitWindowSeconds,
int rateLimitRequestsInWindow) {
/**
* Creates a JShell config.
*
* @param baseUrl the base url of the JShell REST API, must be not null
* @param rateLimitWindowSeconds the number of seconds of the {@link RateLimiter rate limiter}
* for jshell commands and code actions, must be higher than 0
* @param rateLimitRequestsInWindow the number of requests of the {@link RateLimiter rate
* limiter} for jshell commands and code actions, must be higher than 0
*/
public JShellConfig {
Objects.requireNonNull(baseUrl);
if (rateLimitWindowSeconds < 0) {
throw new IllegalArgumentException(
"Illegal rateLimitWindowSeconds : " + rateLimitWindowSeconds);
}
if (rateLimitRequestsInWindow < 0) {
throw new IllegalArgumentException(
"Illegal rateLimitRequestsInWindow : " + rateLimitRequestsInWindow);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import org.togetherjava.tjbot.features.code.CodeMessageManualDetection;
import org.togetherjava.tjbot.features.filesharing.FileSharingMessageListener;
import org.togetherjava.tjbot.features.help.*;
import org.togetherjava.tjbot.features.jshell.JShellCommand;
import org.togetherjava.tjbot.features.jshell.JShellEval;
import org.togetherjava.tjbot.features.mathcommands.TeXCommand;
import org.togetherjava.tjbot.features.mathcommands.wolframalpha.WolframAlphaCommand;
import org.togetherjava.tjbot.features.mediaonly.MediaOnlyChannelListener;
Expand Down Expand Up @@ -67,13 +69,15 @@ private Features() {
* @return a collection of all features
*/
public static Collection<Feature> createFeatures(JDA jda, Database database, Config config) {
JShellEval jshellEval = new JShellEval(config.getJshell());

TagSystem tagSystem = new TagSystem(database);
BookmarksSystem bookmarksSystem = new BookmarksSystem(config, database);
ModerationActionsStore actionsStore = new ModerationActionsStore(database);
ModAuditLogWriter modAuditLogWriter = new ModAuditLogWriter(config);
ScamHistoryStore scamHistoryStore = new ScamHistoryStore(database);
HelpSystemHelper helpSystemHelper = new HelpSystemHelper(config, database);
CodeMessageHandler codeMessageHandler = new CodeMessageHandler();
CodeMessageHandler codeMessageHandler = new CodeMessageHandler(jshellEval);
ChatGptService chatGptService = new ChatGptService(config);

// NOTE The system can add special system relevant commands also by itself,
Expand Down Expand Up @@ -143,6 +147,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
features.add(new ReportCommand(config));
features.add(new BookmarksCommand(bookmarksSystem));
features.add(new ChatGptCommand(chatGptService));
features.add(new JShellCommand(jshellEval));
return features;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.togetherjava.tjbot.features.UserInteractor;
import org.togetherjava.tjbot.features.componentids.ComponentIdGenerator;
import org.togetherjava.tjbot.features.componentids.ComponentIdInteractor;
import org.togetherjava.tjbot.features.jshell.JShellEval;
import org.togetherjava.tjbot.features.utils.CodeFence;
import org.togetherjava.tjbot.features.utils.MessageUtils;

Expand Down Expand Up @@ -62,11 +63,14 @@ public final class CodeMessageHandler extends MessageReceiverAdapter implements

/**
* Creates a new instance.
*
* @param jshellEval used to execute java code and build visual result
*/
public CodeMessageHandler() {
public CodeMessageHandler(JShellEval jshellEval) {
componentIdInteractor = new ComponentIdInteractor(getInteractionType(), getName());

List<CodeAction> codeActions = List.of(new FormatCodeCommand());
List<CodeAction> codeActions =
List.of(new FormatCodeCommand(), new EvalCodeCommand(jshellEval));

labelToCodeAction = codeActions.stream()
.collect(Collectors.toMap(CodeAction::getLabel, Function.identity(), (x, y) -> y,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.togetherjava.tjbot.features.code;

import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;

import org.togetherjava.tjbot.features.jshell.JShellEval;
import org.togetherjava.tjbot.features.utils.CodeFence;
import org.togetherjava.tjbot.features.utils.Colors;
import org.togetherjava.tjbot.features.utils.ConnectionFailedException;
import org.togetherjava.tjbot.features.utils.RequestFailedException;

/**
* Evaluates the given code with jshell.
* <p>
* It will not work of the code isn't valid java or jshell compatible code.
*/
final class EvalCodeCommand implements CodeAction {
private final JShellEval jshellEval;

EvalCodeCommand(JShellEval jshellEval) {
this.jshellEval = jshellEval;
}

@Override
public String getLabel() {
return "Run code";
}

@Override
public MessageEmbed apply(CodeFence codeFence) {
if (codeFence.code().isEmpty()) {
return new EmbedBuilder().setColor(Colors.ERROR_COLOR)
.setDescription("There is nothing to evaluate")
.build();
}
try {
return jshellEval.evaluateAndRespond(null, codeFence.code(), false, false);
} catch (RequestFailedException | ConnectionFailedException e) {
return new EmbedBuilder().setColor(Colors.ERROR_COLOR)
.setDescription("Request failed: " + e.getMessage())
.build();
}
}

}
Loading