v0.1.13 Updated OpenAI features

This commit is contained in:
2023-04-15 00:33:02 -04:00
parent a8e4f0f99d
commit fbc9ac9dde
11 changed files with 359 additions and 140 deletions

View File

@@ -9,7 +9,7 @@ services:
dockerfile: ./Dockerfile dockerfile: ./Dockerfile
args: args:
- JAVA_VERSION=17 - JAVA_VERSION=17
- VERSION=0.1.12 - VERSION=0.1.13
volumes: volumes:
- ./data:/app - ./data:/app
restart: unless-stopped restart: unless-stopped

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.bensherriff</groupId> <groupId>com.bensherriff</groupId>
<artifactId>siren</artifactId> <artifactId>siren</artifactId>
<version>0.1.12</version> <version>0.1.13</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<repositories> <repositories>

View File

@@ -52,5 +52,6 @@ public class Bot {
.setBulkDeleteSplittingEnabled(true) .setBulkDeleteSplittingEnabled(true)
.build(); .build();
listener.setJDA(jda); listener.setJDA(jda);
listener.initialize();
} }
} }

View File

@@ -4,20 +4,13 @@ import com.bensherriff.siren.audio.AudioHandler;
import com.bensherriff.siren.audio.PlayerManager; import com.bensherriff.siren.audio.PlayerManager;
import com.bensherriff.siren.commands.*; import com.bensherriff.siren.commands.*;
import com.bensherriff.siren.exceptions.EmptyVoiceChannelException; import com.bensherriff.siren.exceptions.EmptyVoiceChannelException;
import com.bensherriff.siren.openai.OpenAIManager;
import com.bensherriff.siren.settings.GuildSettings; import com.bensherriff.siren.settings.GuildSettings;
import com.bensherriff.siren.settings.Settings; import com.bensherriff.siren.settings.Settings;
import com.bensherriff.siren.settings.SettingsManager; import com.bensherriff.siren.settings.SettingsManager;
import com.theokanning.openai.completion.CompletionRequest;
import com.theokanning.openai.completion.CompletionResult;
import com.theokanning.openai.completion.chat.ChatCompletionRequest;
import com.theokanning.openai.completion.chat.ChatCompletionResult;
import com.theokanning.openai.completion.chat.ChatMessage;
import com.theokanning.openai.service.OpenAiService;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageType;
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
@@ -29,37 +22,29 @@ import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.IOException; import java.io.IOException;
import java.time.Duration;
import java.util.*; import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class Listener extends ListenerAdapter { public class Listener extends ListenerAdapter {
private static final Logger LOGGER = LogManager.getLogger(Listener.class); private static final Logger LOGGER = LogManager.getLogger(Listener.class);
private final ScheduledExecutorService executor;
private final PlayerManager playerManager;
private final Settings settings; private final Settings settings;
private final Map<String, Command> commands = new HashMap<>(); private final Map<String, Command> commands = new HashMap<>();
private OpenAiService openAiService; private PlayerManager playerManager;
private OpenAIManager openAIManager;
private JDA jda; private JDA jda;
public Listener(Settings settings) { public Listener(Settings settings) {
this.settings = settings; this.settings = settings;
if (settings.getOpenAISettings().getToken().isEmpty()) { this.executor = Executors.newScheduledThreadPool(this.settings.getThreadPool());
LOGGER.warn("No OpenAI token; OpenAI functionality is disabled");
} else {
openAiService = new OpenAiService(settings.getOpenAISettings().getToken(), Duration.ofMillis(settings.getOpenAISettings().getTimeout()));
} }
this.playerManager = new PlayerManager(this); public ScheduledExecutorService getExecutor() {
this.playerManager.initialize(); return executor;
commands.put("play", new PlayCommand(this));
commands.put("stop", new StopCommand(this));
commands.put("skip", new SkipCommand(this));
commands.put("volume", new VolumeCommand(this));
commands.put("pause", new PauseCommand(this));
commands.put("resume", new ResumeCommand(this));
} }
public PlayerManager getPlayerManager() { public PlayerManager getPlayerManager() {
@@ -78,6 +63,20 @@ public class Listener extends ListenerAdapter {
this.jda = jda; this.jda = jda;
} }
public void initialize() {
this.playerManager = new PlayerManager(this);
this.playerManager.initialize();
this.openAIManager = new OpenAIManager(this);
commands.put("play", new PlayCommand(this));
commands.put("stop", new StopCommand(this));
commands.put("skip", new SkipCommand(this));
commands.put("volume", new VolumeCommand(this));
commands.put("pause", new PauseCommand(this));
commands.put("resume", new ResumeCommand(this));
commands.put("help", new PauseCommand(this));
}
public void closeAudioConnection(long guildID) { public void closeAudioConnection(long guildID) {
Guild guild = jda.getGuildById(guildID); Guild guild = jda.getGuildById(guildID);
if (guild != null) { if (guild != null) {
@@ -122,12 +121,13 @@ public class Listener extends ListenerAdapter {
@Override @Override
public void onReady(@NotNull ReadyEvent event) { public void onReady(@NotNull ReadyEvent event) {
super.onReady(event); super.onReady(event);
jda.getGuilds().forEach(guild -> { jda.getGuilds().forEach(guild -> executor.execute(() -> {
LOGGER.debug("Updating commands for {}", guild.getId()); LOGGER.debug("Updating commands for {}", guild.getId());
guild.updateCommands().addCommands(commands.values().stream().map(Command::getSlashCommandData).collect(Collectors.toList())).queue(); guild.updateCommands().addCommands(
}); commands.values().stream().map(Command::getSlashCommandData).collect(Collectors.toList())
).queue();
}));
super.onReady(event); super.onReady(event);
LOGGER.info("Ready!");
} }
@Override @Override
@@ -135,106 +135,23 @@ public class Listener extends ListenerAdapter {
String command = event.getName(); String command = event.getName();
event.deferReply().queue(); event.deferReply().queue();
try {
if (commands.containsKey(command)) { if (commands.containsKey(command)) {
executor.execute(() -> {
try {
commands.get(command).execute(event); commands.get(command).execute(event);
} else {
event.getHook().sendMessage("Unexpected command received.").queue();
}
} catch (Exception ex) { } catch (Exception ex) {
LOGGER.error(ex.getMessage()); LOGGER.error(ex.getMessage());
event.getHook().sendMessage("An error occurred while processing your command. Please contact your administrator.").queue(); event.getHook().sendMessage("An error occurred while processing your command. Please contact your administrator.").queue();
} }
});
} else {
event.getHook().sendMessage("Unexpected command received.").queue();
}
super.onSlashCommandInteraction(event); super.onSlashCommandInteraction(event);
} }
@Override @Override
public void onMessageReceived(@NotNull MessageReceivedEvent event) { public void onMessageReceived(@NotNull MessageReceivedEvent event) {
String message = parseMessage(event.getMessage().getContentRaw()); openAIManager.handleEvent(event);
long guildId = event.getGuild().getIdLong();
GuildSettings guildSettings = settings.getGuildSettings().get(guildId);
String model = settings.getGuildSettings().get(guildId).getModel();
if (shouldReply(event)) {
if (openAiService != null) {
LOGGER.trace("{} Sending message: {}", guildId, message);
try {
StringBuilder stringBuilder = new StringBuilder();
if (model.equals("text-ada-001")) {
CompletionRequest request = CompletionRequest.builder()
.model(guildSettings.getModel())
.maxTokens(guildSettings.getMaxTokens())
.user(event.getAuthor().getId())
.prompt(message)
.build();
CompletionResult result = openAiService.createCompletion(request);
result.getChoices().forEach(choice -> stringBuilder.append(choice.getText().trim()));
} else if (model.equals("gpt-3.5-turbo")){
ChatMessage chatMessage = new ChatMessage();
chatMessage.setContent(message);
chatMessage.setRole(settings.getOpenAISettings().getDefaultRole());
ChatCompletionRequest request = ChatCompletionRequest.builder()
.model(guildSettings.getModel())
.maxTokens(guildSettings.getMaxTokens())
.user(event.getAuthor().getId())
.messages(List.of(chatMessage))
.build();
ChatCompletionResult result = openAiService.createChatCompletion(request);
result.getChoices().forEach(choice -> stringBuilder.append(choice.getMessage().getContent().trim()));
}
if (event.isFromThread()) {
ThreadChannel channel = event.getChannel().asThreadChannel();
channel.sendMessage(stringBuilder.toString()).queue();
} else {
String threadTitle = message;
if (message.length() > 20) {
threadTitle = message.substring(0, 20);
}
event.getMessage().createThreadChannel(threadTitle).queue(threadChannel ->
threadChannel.sendMessage(stringBuilder.toString()).queue());
}
} catch (Exception ex) {
LOGGER.error(ex.getMessage());
event.getMessage().reply("An error occurred while processing your message. Please contact your administrator.").queue();
}
} else {
event.getMessage().reply("OpenAI functionality is not enabled. Please contact an administrator").queue();
}
}
}
private String parseMessage(String input) {
return input.replaceAll("<@.*?>", "").replaceAll(" +", " ").trim();
}
/**
* @param event Message event received
* @return true if the message should be replied to by the bot, otherwise false
*/
private boolean shouldReply(MessageReceivedEvent event) {
boolean shouldReply = false;
if (!event.getAuthor().isBot()) {
// Check if message mentions bot
shouldReply = event.getMessage().getMentions().getMembers().stream().anyMatch(m -> m.getId().equals(jda.getSelfUser().getId()));
// Check if message is a reply
if (!shouldReply) {
shouldReply = event.getMessage().getType().equals(MessageType.INLINE_REPLY) &&
event.getMessage().getReferencedMessage() != null &&
event.getMessage().getReferencedMessage().getAuthor().getId().equals(jda.getSelfUser().getId());
}
// Check if message is from a bot thread
if (!shouldReply) {
shouldReply = event.isFromThread() &&
event.getChannel().asThreadChannel().getOwner() != null &&
Objects.requireNonNull(event.getChannel().asThreadChannel().getOwner()).getId().equals(jda.getSelfUser().getId());
}
}
return shouldReply;
} }
} }

View File

@@ -0,0 +1,20 @@
package com.bensherriff.siren.commands;
import com.bensherriff.siren.Listener;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
import java.io.IOException;
public class HelpCommand extends Command {
public HelpCommand(Listener listener) {
super(listener);
slashCommandData = Commands.slash("help", "Provide help information about the bot");
}
@Override
public void execute(SlashCommandInteractionEvent event) throws IOException {
event.getHook().sendMessage("TODO").queue();
}
}

View File

@@ -0,0 +1,25 @@
package com.bensherriff.siren.openai;
public enum Model {
DAVINCI_3("text-davinci-003"),
DAVINCI_2("text-davinci-002"),
CURIE_1("text-curie-001"),
BABBAGE_1("text-babbage-001"),
ADA_1("text-ada-001"),
GPT_4("gpt-4"),
GPT_4_0314("gpt-4-0314"),
GPT_4_32K("gpt-4-32k"),
GPT_4_32K_0314("gpt-4-32k-0314"),
GPT_35_TURBO("gpt-3.5-turbo"),
GPT_35_TURBO_0301("gpt-3.5-turbo-0301"),
;
private final String name;
private Model(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,193 @@
package com.bensherriff.siren.openai;
import com.bensherriff.siren.Listener;
import com.bensherriff.siren.settings.GuildSettings;
import com.bensherriff.siren.settings.Settings;
import com.theokanning.openai.completion.CompletionRequest;
import com.theokanning.openai.completion.CompletionResult;
import com.theokanning.openai.completion.chat.ChatCompletionRequest;
import com.theokanning.openai.completion.chat.ChatCompletionResult;
import com.theokanning.openai.completion.chat.ChatMessage;
import com.theokanning.openai.service.OpenAiService;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageType;
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
public class OpenAIManager {
private static final Logger LOGGER = LogManager.getLogger(OpenAIManager.class);
private final OpenAiService openAiService;
private final Settings settings;
private final JDA jda;
private final ScheduledExecutorService executor;
private final Map<String, List<ChatMessage>> threadMessages = new HashMap<>();
public OpenAIManager(Listener listener) {
this.settings = listener.getSettings();
this.jda = listener.getJDA();
this.executor = listener.getExecutor();
if (settings.getOpenAISettings().getToken().isEmpty()) {
LOGGER.warn("No OpenAI token; OpenAI functionality is disabled");
openAiService = null;
} else {
openAiService = new OpenAiService(settings.getOpenAISettings().getToken(),
Duration.ofMillis(listener.getSettings().getOpenAISettings().getTimeout()));
}
}
/**
* Handle responses when talking to the bot.
* @param event The message event received
*/
public void handleEvent(MessageReceivedEvent event) {
String message = parseMessage(event.getMessage().getContentRaw());
long guildId = event.getGuild().getIdLong();
GuildSettings guildSettings = settings.getGuildSettings().get(guildId);
Model model = settings.getGuildSettings().get(guildId).getModel();
if (event.getAuthor().isBot()) {
return;
}
if (message.isEmpty() || message.isBlank()) {
event.getMessage().reply("Your message is empty. Please try again").queue();
}
if (shouldReply(event)) {
if (openAiService != null) {
LOGGER.trace("Guild: <{}> User: <{}> Message: <{}>", guildId, event.getAuthor().getId(), message);
executor.execute(() -> {
try {
StringBuilder stringBuilder = new StringBuilder();
List<ChatMessage> chatMessages = new ArrayList<>();
ChatMessage chatMessage = createChatMessage(message, event);
// Send OpenAI Message and get response
switch (model) {
case DAVINCI_3, DAVINCI_2, CURIE_1, BABBAGE_1, ADA_1 -> {
CompletionRequest completionRequest = CompletionRequest.builder()
.model(guildSettings.getModel().getName())
.maxTokens(guildSettings.getMaxTokens())
.user(event.getAuthor().getId())
.temperature(settings.getOpenAISettings().getTemperature())
.topP(settings.getOpenAISettings().getTopP())
.frequencyPenalty(settings.getOpenAISettings().getFrequencyPenalty())
.presencePenalty(settings.getOpenAISettings().getPresencePenalty())
.prompt(message)
.build();
CompletionResult completionResult = openAiService.createCompletion(completionRequest);
completionResult.getChoices().forEach(choice -> stringBuilder.append(choice.getText().trim()));
}
case GPT_4, GPT_4_0314, GPT_4_32K, GPT_4_32K_0314, GPT_35_TURBO, GPT_35_TURBO_0301 -> {
//TODO check logic here, something is broken
// if (event.isFromThread()) {
// String channelId = event.getChannel().asThreadChannel().getId();
// // Update ThreadMessages with the new message, and add previous messages to be sent out
// if (threadMessages.containsKey(channelId)) {
// chatMessages.addAll(threadMessages.get(channelId));
// threadMessages.get(channelId).add(chatMessage);
// } else {
// threadMessages.put(channelId, List.of(chatMessage));
// }
// }
chatMessages.add(chatMessage);
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(guildSettings.getModel().getName())
.maxTokens(guildSettings.getMaxTokens())
.user(event.getAuthor().getId())
.temperature(settings.getOpenAISettings().getTemperature())
.topP(settings.getOpenAISettings().getTopP())
.frequencyPenalty(settings.getOpenAISettings().getFrequencyPenalty())
.presencePenalty(settings.getOpenAISettings().getPresencePenalty())
.messages(chatMessages)
.build();
ChatCompletionResult chatCompletionResult = openAiService.createChatCompletion(chatCompletionRequest);
chatCompletionResult.getChoices().forEach(choice -> stringBuilder.append(choice.getMessage().getContent().trim()));
}
default -> {
event.getMessage().reply("Unexpected model in settings. Please contact an administrator.").queue();
LOGGER.warn("Unexpected model in settings for guild {}: {}. Expected one of {}", guildId,
model, Arrays.toString(Model.values()));
return;
}
}
// Respond to user
if (event.isFromThread()) {
ThreadChannel channel = event.getChannel().asThreadChannel();
channel.sendMessage(stringBuilder.toString()).queue();
} else {
String threadTitle = message;
if (message.length() > 20) {
threadTitle = message.substring(0, 20);
}
event.getMessage().createThreadChannel(threadTitle).queue(channel -> {
channel.sendMessage(stringBuilder.toString()).queue();
threadMessages.put(channel.getId(), List.of(chatMessage));
});
}
} catch (Exception ex) {
LOGGER.error(ex.getMessage());
event.getMessage().reply("An error occurred while processing your message. Please contact your administrator.").queue();
}
});
} else {
event.getMessage().reply("OpenAI functionality is disabled. Please contact an administrator").queue();
}
}
}
private ChatMessage createChatMessage(String message, MessageReceivedEvent event) {
ChatMessage chatMessage = new ChatMessage();
chatMessage.setContent(message);
if (event.getAuthor().getId().equals(settings.getOwner())) {
chatMessage.setRole(Role.ASSISTANT.getName());
} else {
chatMessage.setRole(settings.getOpenAISettings().getDefaultRole().getName());
}
return chatMessage;
}
private String parseMessage(String input) {
return input.replaceAll("<@.*?>", "").replaceAll(" +", " ").trim();
}
/**
* @param event Message event received
* @return true if the message should be replied to by the bot, otherwise false
*/
private boolean shouldReply(MessageReceivedEvent event) {
boolean shouldReply = false;
try {
if (!event.getAuthor().isBot()) {
// Check if message mentions the bot
shouldReply = event.getMessage().getMentions().getMembers().stream().anyMatch(m -> m.getId().equals(jda.getSelfUser().getId()));
// Check if message is a reply
if (!shouldReply) {
Message message = event.getMessage().getReferencedMessage();
shouldReply = event.getMessage().getType().equals(MessageType.INLINE_REPLY) && message != null && message.getAuthor().getId().equals(jda.getSelfUser().getId());
}
// Check if message is from a bot thread
if (!shouldReply && event.isFromThread()) {
ThreadChannel channel = event.getChannel().asThreadChannel();
shouldReply = event.isFromThread() && channel.getOwner() != null && channel.getOwner().getId().equals(jda.getSelfUser().getId());
}
}
} catch (Exception ex) {
LOGGER.error(ex.getMessage());
return false;
}
return shouldReply;
}
}

View File

@@ -0,0 +1,17 @@
package com.bensherriff.siren.openai;
public enum Role {
SYSTEM("system"),
ASSISTANT("assistant"),
USER("user"),
;
private final String name;
private Role(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

View File

@@ -1,5 +1,7 @@
package com.bensherriff.siren.settings; package com.bensherriff.siren.settings;
import com.bensherriff.siren.openai.Model;
import java.io.IOException; import java.io.IOException;
public class GuildSettings { public class GuildSettings {
@@ -7,7 +9,13 @@ public class GuildSettings {
private String prefix = "!"; private String prefix = "!";
private int volume = 100; private int volume = 100;
private String model = "text-ada-001"; /**
* CompletionRequest Models:
* text-davinci-003, text-davinci-002, text-curie-001, text-babbage-001, text-ada-001
* ChatCompletionRequest Models:
* gpt-4, gpt-4-0314, gpt-4-32k, gpt-4-32k-0314, gpt-3.5-turbo, gpt-3.5-turbo-0301
*/
private Model model = Model.ADA_1;
private int maxTokens = 100; private int maxTokens = 100;
public String getPrefix() { public String getPrefix() {
@@ -26,11 +34,11 @@ public class GuildSettings {
this.volume = volume; this.volume = volume;
} }
public String getModel() { public Model getModel() {
return model; return model;
} }
public void setModel(String model) { public void setModel(Model model) {
this.model = model; this.model = model;
} }

View File

@@ -1,7 +1,6 @@
package com.bensherriff.siren.settings; package com.bensherriff.siren.settings;
import java.util.Arrays; import com.bensherriff.siren.openai.Role;
import java.util.List;
public class OpenAISettings { public class OpenAISettings {
@@ -13,14 +12,16 @@ public class OpenAISettings {
* Assistant: The assistant role is designed for use with virtual assistants or chatbots that interact with users. An API key with the assistant role can perform actions related to natural language processing, such as generating text, answering questions, or completing tasks. The assistant role is more limited than the system role, but it still provides access to powerful language models such as GPT-3. * Assistant: The assistant role is designed for use with virtual assistants or chatbots that interact with users. An API key with the assistant role can perform actions related to natural language processing, such as generating text, answering questions, or completing tasks. The assistant role is more limited than the system role, but it still provides access to powerful language models such as GPT-3.
* User: The user role provides limited access to specific OpenAI APIs and resources. An API key with the user role can only perform actions related to specific use cases, such as accessing a particular language model or dataset. The user role is more restricted than the assistant or system roles, but it is suitable for many common use cases. * User: The user role provides limited access to specific OpenAI APIs and resources. An API key with the user role can only perform actions related to specific use cases, such as accessing a particular language model or dataset. The user role is more restricted than the assistant or system roles, but it is suitable for many common use cases.
*/ */
private String defaultRole = "user"; private Role defaultRole = Role.USER;
/** /**
* In milliseconds * In milliseconds
*/ */
private long timeout = 10000; private long timeout = 10000;
private double temperature = 0.5;
private final List<String> availableModels = Arrays.asList("gpt-3.5-turbo", "gpt-3.5-turbo-0301", "text-ada-001", "text-davinci-001", "text-davinci-002", "text-davinci-003"); private double topP = 1.0;
private double frequencyPenalty = 0.0;
private double presencePenalty = 0.0;
public String getToken() { public String getToken() {
return token; return token;
@@ -30,15 +31,11 @@ public class OpenAISettings {
this.token = token; this.token = token;
} }
public List<String> getAvailableModels() { public Role getDefaultRole() {
return availableModels;
}
public String getDefaultRole() {
return defaultRole; return defaultRole;
} }
public void setDefaultRole(String defaultRole) { public void setDefaultRole(Role defaultRole) {
this.defaultRole = defaultRole; this.defaultRole = defaultRole;
} }
@@ -49,4 +46,36 @@ public class OpenAISettings {
public void setTimeout(long timeout) { public void setTimeout(long timeout) {
this.timeout = timeout; this.timeout = timeout;
} }
public double getTemperature() {
return temperature;
}
public void setTemperature(double temperature) {
this.temperature = temperature;
}
public double getTopP() {
return topP;
}
public void setTopP(double topP) {
this.topP = topP;
}
public double getFrequencyPenalty() {
return frequencyPenalty;
}
public void setFrequencyPenalty(double frequencyPenalty) {
this.frequencyPenalty = frequencyPenalty;
}
public double getPresencePenalty() {
return presencePenalty;
}
public void setPresencePenalty(double presencePenalty) {
this.presencePenalty = presencePenalty;
}
} }

View File

@@ -7,6 +7,7 @@ public class Settings {
private String token = ""; private String token = "";
private String owner = ""; private String owner = "";
private int threadPool = 2;
private Map<Long, GuildSettings> guildSettings = new HashMap<>(); private Map<Long, GuildSettings> guildSettings = new HashMap<>();
private OpenAISettings openAISettings = new OpenAISettings(); private OpenAISettings openAISettings = new OpenAISettings();
@@ -26,6 +27,14 @@ public class Settings {
this.owner = owner; this.owner = owner;
} }
public int getThreadPool() {
return threadPool;
}
public void setThreadPool(int threadPool) {
this.threadPool = threadPool;
}
public Map<Long, GuildSettings> getGuildSettings() { public Map<Long, GuildSettings> getGuildSettings() {
return guildSettings; return guildSettings;
} }