Skip to content

Improved User Preferences by using Strings instead of Booleans #356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 24, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protected void execute(JobExecutionContext context, @NotNull JDA jda) throws Job
for (QOTWSubmission submission : submissions) {
UserPreferenceService manager = new UserPreferenceService(Bot.getDataSource());
UserPreference preference = manager.getOrCreate(submission.getAuthorId(), Preference.QOTW_REMINDER);
if (preference.isEnabled()) {
if (Boolean.parseBoolean(preference.getState())) {
TextChannel channel = config.getSubmissionChannel();
channel.getThreadChannels().stream().filter(t -> t.getIdLong() == submission.getThreadId()).forEach(t -> {
if (t.getMessageCount() <= 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ public class UserPreferenceService {
*
* @param userId The users' id.
* @param preference The {@link Preference} to change the state for.
* @param enabled The preferences' state.
* @param state The preferences' state.
* @return Whether the operation was successful.
*/
public boolean setOrCreate(long userId, Preference preference, boolean enabled) {
public boolean setOrCreate(long userId, Preference preference, String state) {
try (Connection con = dataSource.getConnection()) {
UserPreferenceRepository repo = new UserPreferenceRepository(con);
Optional<UserPreference> preferenceOptional = repo.getById(userId, preference);
if (preferenceOptional.isPresent()) {
return repo.updateState(userId, preference, enabled);
return repo.updateState(userId, preference, state);
} else {
UserPreference userPreference = new UserPreference();
userPreference.setUserId(userId);
userPreference.setPreference(preference);
userPreference.setEnabled(enabled);
userPreference.setState(state);
repo.insert(userPreference, false);
return true;
}
Expand All @@ -64,7 +64,7 @@ public UserPreference getOrCreate(long userId, Preference preference) {
UserPreference userPreference = new UserPreference();
userPreference.setUserId(userId);
userPreference.setPreference(preference);
userPreference.setEnabled(preference.getDefaultState());
userPreference.setState(preference.getDefaultState());
repo.insert(userPreference, false);
return userPreference;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package net.javadiscord.javabot.systems.user_preferences.commands;

import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import net.javadiscord.javabot.Bot;
Expand All @@ -9,9 +12,6 @@
import net.javadiscord.javabot.util.Responses;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.stream.Collectors;

/**
* <h3>This class represents the /preferences list command.</h3>
*/
Expand All @@ -25,10 +25,24 @@ public PreferencesListSubcommand() {

@Override
public void execute(@NotNull SlashCommandInteractionEvent event) {
UserPreferenceService manager = new UserPreferenceService(Bot.getDataSource());
String preferences = Arrays.stream(Preference.values())
.map(p -> String.format("`%s` %s", manager.getOrCreate(event.getUser().getIdLong(), p).isEnabled() ? "\uD83D\uDFE2" : "\uD83D\uDD34", p))
.collect(Collectors.joining("\n"));
Responses.info(event, String.format("%s's Preferences", event.getUser().getName()), preferences).queue();
event.replyEmbeds(buildPreferencesEmbed(new UserPreferenceService(Bot.getDataSource()), event.getUser()))
.setEphemeral(true)
.queue();
}

private @NotNull MessageEmbed buildPreferencesEmbed(UserPreferenceService service, @NotNull User user) {
EmbedBuilder builder = new EmbedBuilder()
.setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl())
.setTitle(user.getName() + "'s Preferences")
.setColor(Responses.Type.INFO.getColor());
for (Preference p : Preference.values()) {
builder.addField(buildPreferenceField(user, service, p));
}
return builder.build();
}

private MessageEmbed.@NotNull Field buildPreferenceField(@NotNull User user, @NotNull UserPreferenceService service, Preference preference) {
String state = service.getOrCreate(user.getIdLong(), preference).getState();
return new MessageEmbed.Field(preference.toString(), state.isEmpty() ? String.format("`%s` has not yet been set.", preference) : state, true);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package net.javadiscord.javabot.systems.user_preferences.commands;

import com.dynxsty.dih4jda.interactions.commands.AutoCompletable;
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
import com.dynxsty.dih4jda.util.AutoCompleteUtils;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.AutoCompleteQuery;
import net.dv8tion.jda.api.interactions.commands.Command;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
Expand All @@ -15,11 +19,12 @@
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.List;

/**
* <h3>This class represents the /preferences set command.</h3>
*/
public class PreferencesSetSubcommand extends SlashCommand.Subcommand {
public class PreferencesSetSubcommand extends SlashCommand.Subcommand implements AutoCompletable {
/**
* The constructor of this class, which sets the corresponding {@link net.dv8tion.jda.api.interactions.commands.build.SlashCommandData}.
*/
Expand All @@ -28,7 +33,7 @@ public PreferencesSetSubcommand() {
.addOptions(
new OptionData(OptionType.INTEGER, "preference", "The preference to set.", true)
.addChoices(Arrays.stream(Preference.values()).map(this::toChoice).toList()),
new OptionData(OptionType.BOOLEAN, "state", "The state of the specified preference.", true)
new OptionData(OptionType.STRING, "state", "The state/value of the specified preference.", true, true)
)
);
}
Expand All @@ -42,17 +47,34 @@ public void execute(@NotNull SlashCommandInteractionEvent event) {
return;
}
Preference preference = Preference.values()[preferenceMapping.getAsInt()];
boolean state = stateMapping.getAsBoolean();
String state = stateMapping.getAsString();
if (Arrays.stream(preference.getType().getAllowedChoices()).noneMatch(s -> s.equals(state))) {
Responses.error(event, "`%s` is not allowed for this preference! Expected one of the following values:\n%s",
state, String.join(", ", preference.getType().getAllowedChoices())
).queue();
return;
}
UserPreferenceService manager = new UserPreferenceService(Bot.getDataSource());
if (manager.setOrCreate(event.getUser().getIdLong(), preference, state)) {
Responses.info(event, "Preference Updated", "Successfully %s `%s`!", state ? "enabled" : "disabled", preference).queue();
Responses.info(event, "Preference Updated", "Successfully set %s to `%s`!", preference, state).queue();
} else {
Responses.error(event, "Could not %s `%s`.", state ? "enable" : "disable", preference).queue();
Responses.error(event, "Could not set %s to `%s`.", preference, state).queue();
}
}

@Contract("_ -> new")
private Command.@NotNull Choice toChoice(@NotNull Preference preference) {
return new Command.Choice(preference.toString(), String.valueOf(preference.ordinal()));
}

@Override
public void handleAutoComplete(@NotNull CommandAutoCompleteInteractionEvent event, @NotNull AutoCompleteQuery target) {
String preferenceString = event.getOption("preference", OptionMapping::getAsString);
if (preferenceString != null && Arrays.stream(Preference.values()).map(Preference::name).anyMatch(c -> c.equals(preferenceString))) {
Preference preference = Preference.valueOf(preferenceString);
if (preference.getType().getDefaultChoices() != null && preference.getType().getDefaultChoices().length > 0) {
event.replyChoices(AutoCompleteUtils.filterChoices(event, List.of(preference.getType().getDefaultChoices()))).queue();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ public UserPreferenceRepository(Connection con) {
super(con, UserPreference.class, "USER_PREFERENCES", List.of(
TableProperty.of("user_id", H2Type.BIGINT, (x, y) -> x.setUserId((Long) y), UserPreference::getUserId),
TableProperty.of("ordinal", H2Type.INTEGER, (x, y) -> x.setPreference(Preference.values()[(Integer) y]), p -> p.getPreference().ordinal()),
TableProperty.of("enabled", H2Type.BOOLEAN, (x, y) -> x.setEnabled((Boolean) y), UserPreference::isEnabled)
TableProperty.of("state", H2Type.VARCHAR, (x, y) -> x.setState((String) y), UserPreference::getState)
));
}

public Optional<UserPreference> getById(long userId, @NotNull Preference preference) throws SQLException {
return querySingle("WHERE user_id = ? AND ordinal = ?", userId, preference.ordinal());
}

public boolean updateState(long userId, @NotNull Preference preference, boolean enabled) throws SQLException {
return update("UPDATE user_preferences SET enabled = ? WHERE user_id = ? AND ordinal = ?", enabled, userId, preference.ordinal()) > 0;
public boolean updateState(long userId, @NotNull Preference preference, String state) throws SQLException {
return update("UPDATE user_preferences SET state = ? WHERE user_id = ? AND ordinal = ?", state, userId, preference.ordinal()) > 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.javadiscord.javabot.systems.user_preferences.model;

import net.dv8tion.jda.api.interactions.commands.Command;

/**
* Represents a {@link Preference} of the {@link Boolean} type.
*/
public final class BooleanPreference implements PreferenceType {
@Override
public String[] getAllowedChoices() {
return new String[]{
"true", "false"
};
}

@Override
public Command.Choice[] getDefaultChoices() {
return new Command.Choice[]{
new Command.Choice("Enable", "true"),
new Command.Choice("Disable", "false")
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,28 @@ public enum Preference {
/**
* Enables/Disables QOTW reminders.
*/
QOTW_REMINDER("Question of the Week Reminder", false);
QOTW_REMINDER("Question of the Week Reminder", "false", new BooleanPreference());

private final String name;
private final boolean defaultState;
private final String defaultState;
private final PreferenceType type;

Preference(String name, boolean defaultState) {
Preference(String name, String defaultState, PreferenceType type) {
this.name = name;
this.defaultState = defaultState;
this.type = type;
}

@Override
public String toString() {
return name;
}

public boolean getDefaultState() {
public String getDefaultState() {
return defaultState;
}

public PreferenceType getType() {
return type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package net.javadiscord.javabot.systems.user_preferences.model;

import net.dv8tion.jda.api.interactions.commands.Command;

/**
* Interface used by different Preference Types. This holds the default and allowed choices.
*/
public interface PreferenceType {
default String[] getAllowedChoices() {
return new String[0];
}

default Command.Choice[] getDefaultChoices() {
return new Command.Choice[0];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package net.javadiscord.javabot.systems.user_preferences.model;

/**
* Represents a {@link Preference} of the {@link String} type.
*/
public final class StringPreference implements PreferenceType {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
public class UserPreference {
private long userId;
private Preference preference;
private boolean enabled;
private String state;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ CREATE TABLE user_preferences
(
user_id BIGINT NOT NULL,
ordinal INTEGER NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
state VARCHAR NOT NULL DEFAULT '',
PRIMARY KEY (user_id, ordinal)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
TRUNCATE TABLE user_preferences;

ALTER TABLE user_preferences DROP COLUMN enabled;

ALTER TABLE user_preferences ADD COLUMN state VARCHAR NOT NULL DEFAULT ''
2 changes: 1 addition & 1 deletion src/main/resources/database/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,6 @@ CREATE TABLE user_preferences
(
user_id BIGINT NOT NULL,
ordinal INTEGER NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
state VARCHAR NOT NULL DEFAULT '',
PRIMARY KEY (user_id, ordinal)
)