Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
bb6ef83
Bump org.xerial:sqlite-jdbc from 3.41.0.0 to 3.42.0.0 (#838)
dependabot[bot] May 23, 2023
a37d1cc
Bump org.flywaydb:flyway-core from 9.18.0 to 9.19.0 (#841)
dependabot[bot] May 24, 2023
fc82bbf
Bump gradle.plugin.org.flywaydb:gradle-plugin-publishing (#842)
dependabot[bot] May 24, 2023
98f090e
Bump com.diffplug.spotless from 6.18.0 to 6.19.0 (#843)
dependabot[bot] May 25, 2023
d6694f3
Bump org.sonarqube from 4.0.0.2929 to 4.1.0.3113 (#844)
dependabot[bot] May 30, 2023
0ac8fe0
Bump org.sonarqube from 4.1.0.3113 to 4.2.0.3129 (#845)
dependabot[bot] Jun 1, 2023
7086528
Bump org.kohsuke:github-api from 1.314 to 1.315 (#846)
dependabot[bot] Jun 2, 2023
a777157
Bump chatGPTVersion from 0.12.0 to 0.13.0 (#848)
dependabot[bot] Jun 20, 2023
f41f6a9
Bump gradle.plugin.org.flywaydb:gradle-plugin-publishing (#849)
dependabot[bot] Jun 21, 2023
a153298
Bump org.flywaydb:flyway-core from 9.19.0 to 9.20.0 (#850)
dependabot[bot] Jun 21, 2023
d2d6487
Bump chatGPTVersion from 0.13.0 to 0.14.0 (#851)
dependabot[bot] Jun 23, 2023
6454646
Bump name.remal.sonarlint from 3.2.0 to 3.3.0 (#855)
dependabot[bot] Jul 14, 2023
a26588c
Bump com.diffplug.spotless from 6.19.0 to 6.20.0 (#859)
dependabot[bot] Jul 18, 2023
27b97d3
Bump org.sonarqube from 4.2.0.3129 to 4.3.0.3225 (#858)
dependabot[bot] Jul 18, 2023
2279bf2
Logging - links logs to source (#857)
SquidXTV Jul 19, 2023
2fceec5
Bump org.flywaydb:flyway-core from 9.20.0 to 9.21.0 (#861)
dependabot[bot] Jul 21, 2023
c83c515
Bump gradle.plugin.org.flywaydb:gradle-plugin-publishing (#860)
dependabot[bot] Jul 21, 2023
3e86ef0
Bump org.junit.jupiter:junit-jupiter-api from 5.9.0 to 5.10.0 (#864)
dependabot[bot] Jul 25, 2023
3e2aaaa
Bump org.junit.jupiter:junit-jupiter-engine from 5.9.0 to 5.10.0 (#863)
dependabot[bot] Jul 25, 2023
8195a30
Bump org.junit.jupiter:junit-jupiter-params from 5.9.0 to 5.10.0 (#862)
dependabot[bot] Jul 25, 2023
0de9992
Increase default ChatGPT response timeout from 10s to 90s (#852)
TheRealJoeFriel Jul 26, 2023
03b1112
Feature/jshell (#870)
Alathreon Aug 11, 2023
ae006d2
Chat gpt attempt questions (#873)
tmcdonnell2 Aug 27, 2023
b5730bc
Bump org.flywaydb:flyway-core from 9.21.0 to 9.22.0 (#880)
dependabot[bot] Sep 4, 2023
01ffaae
Bump gradle.plugin.org.flywaydb:gradle-plugin-publishing (#879)
dependabot[bot] Sep 4, 2023
5874437
Bump org.xerial:sqlite-jdbc from 3.42.0.0 to 3.43.0.0 (#878)
dependabot[bot] Sep 4, 2023
07292e6
Bump com.diffplug.spotless from 6.20.0 to 6.21.0 (#877)
dependabot[bot] Sep 4, 2023
b90fce6
Bump chatGPTVersion from 0.14.0 to 0.16.0 (#876)
dependabot[bot] Sep 4, 2023
d1da708
use - in channel names (#853)
Taz03 Sep 11, 2023
92e36aa
typo in config template
Zabuzard Sep 18, 2023
e9a7f32
Fixed some ChatGPT issues (#888)
Zabuzard Sep 18, 2023
69d4f78
added helper prune config (#889)
Zabuzard Sep 18, 2023
4823000
temporarily disable jshell
Zabuzard Sep 18, 2023
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
10 changes: 5 additions & 5 deletions application/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
buildscript {
dependencies {
classpath 'org.xerial:sqlite-jdbc:3.41.0.0'
classpath 'org.xerial:sqlite-jdbc:3.43.0.0'
}
}

Expand Down Expand Up @@ -74,12 +74,12 @@ dependencies {

implementation 'com.github.ben-manes.caffeine:caffeine:3.1.1'

implementation 'org.kohsuke:github-api:1.314'
implementation 'org.kohsuke:github-api:1.315'

testImplementation 'org.mockito:mockito-core:5.3.1'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'

implementation "com.theokanning.openai-gpt3-java:api:$chatGPTVersion"
implementation "com.theokanning.openai-gpt3-java:service:$chatGPTVersion"
Expand Down
21 changes: 17 additions & 4 deletions application/config.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
"databasePath": "local-database.db",
"projectWebsite": "https://github.com/Together-Java/TJ-Bot",
"discordGuildInvite": "https://discord.com/invite/XXFUXzK",
"modAuditLogChannelPattern": "mod_audit_log",
"modAuditLogChannelPattern": "mod-audit-log",
"modMailChannelPattern": "modmail",
"mutedRolePattern": "Muted",
"heavyModerationRolePattern": "Moderator",
"softModerationRolePattern": "Moderator|Community Ambassador",
"tagManageRolePattern": "Moderator|Community Ambassador|Top Helpers .+",
"excludeCodeAutoDetectionRolePattern": "Top Helpers .+|Moderator|Community Ambassador|Expert",
"suggestions": {
"channelPattern": "tj_suggestions",
"channelPattern": "tj-suggestions",
"upVoteEmoteName": "peepo_yes",
"downVoteEmoteName": "peepo_no"
},
Expand Down Expand Up @@ -87,6 +87,19 @@
"wsh"
],
"logInfoChannelWebhook": "<put_your_webhook_here>",
"logErrorChannelWebhook": "<put_your_webhook_here>"
"openaiApiKey": "<check pins in #tjbot_discussion for the key>"
"logErrorChannelWebhook": "<put_your_webhook_here>",
"openaiApiKey": "<check pins in #tjbot_discussion for the key>",
"sourceCodeBaseUrl": "https://github.com/Together-Java/TJ-Bot/blob/master/application/src/main/java/",
"jshell": {
"baseUrl": "<put_jshell_rest_api_url_here>",
"rateLimitWindowSeconds": 10,
"rateLimitRequestsInWindow": 3
},
"helperPruneConfig": {
"roleFullLimit": 100,
"roleFullThreshold": 95,
"pruneMemberAmount": 7,
"inactivateAfterDays": 90,
"recentlyJoinedDays": 4
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public final class Config {
private final String logInfoChannelWebhook;
private final String logErrorChannelWebhook;
private final String openaiApiKey;
private final String sourceCodeBaseUrl;
private final JShellConfig jshell;
private final HelperPruneConfig helperPruneConfig;

@SuppressWarnings("ConstructorWithTooManyParameters")
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
Expand Down Expand Up @@ -72,7 +75,11 @@ private Config(@JsonProperty(value = "token", required = true) String token,
required = true) String logInfoChannelWebhook,
@JsonProperty(value = "logErrorChannelWebhook",
required = true) String logErrorChannelWebhook,
@JsonProperty(value = "openaiApiKey", required = true) String openaiApiKey) {
@JsonProperty(value = "openaiApiKey", required = true) String openaiApiKey,
@JsonProperty(value = "sourceCodeBaseUrl", required = true) String sourceCodeBaseUrl,
@JsonProperty(value = "jshell", required = true) JShellConfig jshell,
@JsonProperty(value = "helperPruneConfig",
required = true) HelperPruneConfig helperPruneConfig) {
this.token = Objects.requireNonNull(token);
this.gistApiKey = Objects.requireNonNull(gistApiKey);
this.databasePath = Objects.requireNonNull(databasePath);
Expand All @@ -96,6 +103,9 @@ private Config(@JsonProperty(value = "token", required = true) String token,
this.logInfoChannelWebhook = Objects.requireNonNull(logInfoChannelWebhook);
this.logErrorChannelWebhook = Objects.requireNonNull(logErrorChannelWebhook);
this.openaiApiKey = Objects.requireNonNull(openaiApiKey);
this.sourceCodeBaseUrl = Objects.requireNonNull(sourceCodeBaseUrl);
this.jshell = Objects.requireNonNull(jshell);
this.helperPruneConfig = Objects.requireNonNull(helperPruneConfig);
}

/**
Expand Down Expand Up @@ -316,4 +326,33 @@ public String getLogErrorChannelWebhook() {
public String getOpenaiApiKey() {
return openaiApiKey;
}

/**
* The base URL of the source code of this bot. E.g.
* {@code getSourceCodeBaseUrl() + "/org/togetherjava/tjbot/config/Config.java"} would point to
* this file.
*
* @return the base url of the source code of this bot
*/
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;
}

/**
* Gets the config for automatic pruning of helper roles.
*
* @return the configuration
*/
public HelperPruneConfig getHelperPruneConfig() {
return helperPruneConfig;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.togetherjava.tjbot.config;


/**
* Config for automatic pruning of helper roles, see
* {@link org.togetherjava.tjbot.features.help.AutoPruneHelperRoutine}.
*
* @param roleFullLimit if a helper role contains that many users, it is considered full and pruning
* must occur
* @param roleFullThreshold if a helper role contains that many users, pruning will start to occur
* to prevent reaching the limit
* @param pruneMemberAmount amount of users to remove from helper roles during a prune
* @param inactivateAfterDays after how many days of inactivity a user is eligible for pruning
* @param recentlyJoinedDays if a user is with the server for just this amount of days, they are
* protected from pruning
*/
public record HelperPruneConfig(int roleFullLimit, int roleFullThreshold, int pruneMemberAmount,
int inactivateAfterDays, int recentlyJoinedDays) {
}
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 @@ -4,20 +4,26 @@

import org.togetherjava.tjbot.config.Config;
import org.togetherjava.tjbot.db.Database;
import org.togetherjava.tjbot.features.basic.*;
import org.togetherjava.tjbot.features.bookmarks.*;
import org.togetherjava.tjbot.features.chaptgpt.ChatGptCommand;
import org.togetherjava.tjbot.features.chaptgpt.ChatGptService;
import org.togetherjava.tjbot.features.basic.PingCommand;
import org.togetherjava.tjbot.features.basic.RoleSelectCommand;
import org.togetherjava.tjbot.features.basic.SlashCommandEducator;
import org.togetherjava.tjbot.features.basic.SuggestionsUpDownVoter;
import org.togetherjava.tjbot.features.bookmarks.BookmarksCommand;
import org.togetherjava.tjbot.features.bookmarks.BookmarksSystem;
import org.togetherjava.tjbot.features.bookmarks.LeftoverBookmarksCleanupRoutine;
import org.togetherjava.tjbot.features.bookmarks.LeftoverBookmarksListener;
import org.togetherjava.tjbot.features.chatgpt.ChatGptCommand;
import org.togetherjava.tjbot.features.chatgpt.ChatGptService;
import org.togetherjava.tjbot.features.code.CodeMessageAutoDetection;
import org.togetherjava.tjbot.features.code.CodeMessageHandler;
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.JShellEval;
import org.togetherjava.tjbot.features.mathcommands.TeXCommand;
import org.togetherjava.tjbot.features.mathcommands.wolframalpha.WolframAlphaCommand;
import org.togetherjava.tjbot.features.mediaonly.MediaOnlyChannelListener;
import org.togetherjava.tjbot.features.moderation.*;
import org.togetherjava.tjbot.features.moderation.ReportCommand;
import org.togetherjava.tjbot.features.moderation.attachment.BlacklistedAttachmentListener;
import org.togetherjava.tjbot.features.moderation.audit.AuditCommand;
import org.togetherjava.tjbot.features.moderation.audit.ModAuditLogRoutine;
Expand Down Expand Up @@ -67,14 +73,16 @@ 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);
HelpSystemHelper helpSystemHelper = new HelpSystemHelper(config, database, chatGptService);

// NOTE The system can add special system relevant commands also by itself,
// hence this list may not necessarily represent the full list of all commands actually
Expand Down Expand Up @@ -143,6 +151,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
@@ -0,0 +1,82 @@
package org.togetherjava.tjbot.features.chatgpt;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
* Represents a class to partition long text blocks into smaller blocks which work with Discord's
* API. Initially constructed to partition text from AI text generation APIs.
*/
public class AIResponseParser {
private AIResponseParser() {
throw new UnsupportedOperationException("Utility class, construction not supported");
}

private static final Logger logger = LoggerFactory.getLogger(AIResponseParser.class);
private static final int RESPONSE_LENGTH_LIMIT = 2_000;

/**
* Parses the response generated by AI. If response is longer than
* {@value RESPONSE_LENGTH_LIMIT}, then breaks apart the response into suitable lengths for
* Discords API.
*
* @param response The response from the AI which we want to send over Discord.
* @return An array potentially holding the original response split up into shorter than
* {@value RESPONSE_LENGTH_LIMIT} length pieces.
*/
public static String[] parse(String response) {
String[] partedResponse = new String[] {response};
if (response.length() > RESPONSE_LENGTH_LIMIT) {
logger.debug("Response to parse:\n{}", response);
partedResponse = partitionAiResponse(response);
}

return partedResponse;
}

private static String[] partitionAiResponse(String response) {
List<String> responseChunks = new ArrayList<>();
String[] splitResponseOnMarks = response.split("```");

for (int i = 0; i < splitResponseOnMarks.length; i++) {
String split = splitResponseOnMarks[i];
List<String> chunks = new ArrayList<>();
chunks.add(split);

// Check each chunk for correct length. If over the length, split in two and check
// again.
while (!chunks.stream().allMatch(s -> s.length() < RESPONSE_LENGTH_LIMIT)) {
for (int j = 0; j < chunks.size(); j++) {
String chunk = chunks.get(j);
if (chunk.length() > RESPONSE_LENGTH_LIMIT) {
int midpointNewline = chunk.lastIndexOf("\n", chunk.length() / 2);
chunks.set(j, chunk.substring(0, midpointNewline));
chunks.add(j + 1, chunk.substring(midpointNewline));
}
}
}

// Given the splitting on ```, the odd numbered entries need to have code marks
// restored.
if (i % 2 != 0) {
// We assume that everything after the ``` on the same line is the language
// declaration. Could be empty.
String lang = split.substring(0, split.indexOf(System.lineSeparator()));
chunks = chunks.stream()
.map(s -> ("```" + lang).concat(s).concat("```"))
// Handle case of doubling language declaration
.map(s -> s.replaceFirst("```" + lang + lang, "```" + lang))
.collect(Collectors.toList());
}

List<String> list = chunks.stream().filter(string -> !string.equals("")).toList();
responseChunks.addAll(list);
} // end of for loop.

return responseChunks.toArray(new String[0]);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.togetherjava.tjbot.features.chaptgpt;
package org.togetherjava.tjbot.features.chatgpt;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
Expand All @@ -22,6 +22,7 @@
* which it will respond with an AI generated answer.
*/
public final class ChatGptCommand extends SlashCommandAdapter {
public static final String COMMAND_NAME = "chatgpt";
private static final String QUESTION_INPUT = "question";
private static final int MAX_MESSAGE_INPUT_LENGTH = 200;
private static final int MIN_MESSAGE_INPUT_LENGTH = 4;
Expand All @@ -37,7 +38,7 @@ public final class ChatGptCommand extends SlashCommandAdapter {
* @param chatGptService ChatGptService - Needed to make calls to ChatGPT API
*/
public ChatGptCommand(ChatGptService chatGptService) {
super("chatgpt", "Ask the ChatGPT AI a question!", CommandVisibility.GUILD);
super(COMMAND_NAME, "Ask the ChatGPT AI a question!", CommandVisibility.GUILD);

this.chatGptService = chatGptService;
}
Expand Down Expand Up @@ -73,14 +74,20 @@ public void onSlashCommand(SlashCommandInteractionEvent event) {
public void onModalSubmitted(ModalInteractionEvent event, List<String> args) {
event.deferReply().queue();

Optional<String> optional =
Optional<String[]> optional =
chatGptService.ask(event.getValue(QUESTION_INPUT).getAsString());
if (optional.isPresent()) {
userIdToAskedAtCache.put(event.getMember().getId(), Instant.now());
}

String response = optional.orElse(
"An error has occurred while trying to communicate with ChatGPT. Please try again later");
event.getHook().sendMessage(response).queue();
String[] errorResponse = {"""
An error has occurred while trying to communicate with ChatGPT.
Please try again later.
"""};

String[] response = optional.orElse(errorResponse);
for (String message : response) {
event.getHook().sendMessage(message).queue();
}
}
}
Loading