Skip to content

Commit 5a77c6b

Browse files
Merge pull request #335 from Java-Discord/dynxsty/user_preferences
User Preferences & QOTW Reminder
2 parents 2a9630c + 0832875 commit 5a77c6b

File tree

14 files changed

+352
-2
lines changed

14 files changed

+352
-2
lines changed

src/main/java/net/javadiscord/javabot/Bot.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public static void main(String[] args) throws Exception {
109109
AllowedMentions.setDefaultMentions(EnumSet.of(Message.MentionType.ROLE, Message.MentionType.CHANNEL, Message.MentionType.USER, Message.MentionType.EMOJI));
110110
dih4jda = DIH4JDABuilder.setJDA(jda)
111111
.setCommandsPackage("net.javadiscord.javabot")
112-
.setDefaultCommandType(RegistrationType.GUILD)
112+
.setDefaultCommandType(RegistrationType.GLOBAL)
113113
.build();
114114
customTagManager = new CustomTagManager(jda, dataSource);
115115
messageCache = new MessageCache();
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package net.javadiscord.javabot.systems.qotw;
2+
3+
import net.dv8tion.jda.api.JDA;
4+
import net.dv8tion.jda.api.entities.Guild;
5+
import net.dv8tion.jda.api.entities.TextChannel;
6+
import net.javadiscord.javabot.Bot;
7+
import net.javadiscord.javabot.data.config.guild.QOTWConfig;
8+
import net.javadiscord.javabot.systems.qotw.submissions.SubmissionManager;
9+
import net.javadiscord.javabot.systems.qotw.submissions.model.QOTWSubmission;
10+
import net.javadiscord.javabot.systems.user_preferences.UserPreferenceManager;
11+
import net.javadiscord.javabot.systems.user_preferences.model.Preference;
12+
import net.javadiscord.javabot.systems.user_preferences.model.UserPreference;
13+
import net.javadiscord.javabot.tasks.jobs.DiscordApiJob;
14+
import org.jetbrains.annotations.NotNull;
15+
import org.quartz.JobExecutionContext;
16+
import org.quartz.JobExecutionException;
17+
18+
import java.util.List;
19+
20+
/**
21+
* Checks that there's a question in the QOTW queue ready for posting soon.
22+
*/
23+
public class QOTWUserReminderJob extends DiscordApiJob {
24+
@Override
25+
protected void execute(JobExecutionContext context, @NotNull JDA jda) throws JobExecutionException {
26+
for (Guild guild : jda.getGuilds()) {
27+
QOTWConfig config = Bot.getConfig().get(guild).getQotwConfig();
28+
List<QOTWSubmission> submissions = new SubmissionManager(config).getActiveSubmissionThreads();
29+
for (QOTWSubmission submission : submissions) {
30+
UserPreferenceManager manager = new UserPreferenceManager(Bot.getDataSource());
31+
UserPreference preference = manager.getOrCreate(submission.getAuthorId(), Preference.QOTW_REMINDER);
32+
if (preference.isEnabled()) {
33+
TextChannel channel = config.getSubmissionChannel();
34+
channel.getThreadChannels().stream().filter(t -> t.getIdLong() == submission.getThreadId()).forEach(t -> {
35+
if (t.getMessageCount() <= 1) {
36+
t.sendMessageFormat("**Question of the Week Reminder**\nHey <@%s>! You still have some time left to submit your answer!", submission.getAuthorId())
37+
.queue();
38+
}
39+
});
40+
}
41+
}
42+
}
43+
}
44+
}

src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionManager.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.sql.Connection;
2626
import java.sql.SQLException;
2727
import java.time.Instant;
28+
import java.util.List;
2829
import java.util.Optional;
2930

3031
/**
@@ -129,6 +130,21 @@ public boolean hasActiveSubmissionThreads(long authorId) {
129130
}
130131
}
131132

133+
/**
134+
* Gets all active submission threads.
135+
*
136+
* @return An immutable {@link List} of {@link QOTWSubmission}s.
137+
*/
138+
public List<QOTWSubmission> getActiveSubmissionThreads() {
139+
try (Connection con = Bot.getDataSource().getConnection()) {
140+
QOTWSubmissionRepository repo = new QOTWSubmissionRepository(con);
141+
return repo.getSubmissionByQuestionNumber(repo.getCurrentQuestionNumber());
142+
} catch (SQLException e) {
143+
ExceptionLogger.capture(e, getClass().getSimpleName());
144+
return List.of();
145+
}
146+
}
147+
132148
private @NotNull MessageEmbed buildSubmissionThreadEmbed(@NotNull User createdBy, @NotNull QOTWQuestion question, @NotNull QOTWConfig config) {
133149
return new EmbedBuilder()
134150
.setColor(Responses.Type.DEFAULT.getColor())

src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/TagsCommand.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ public class TagsCommand extends SlashCommand {
1212
* adds the corresponding {@link net.dv8tion.jda.api.interactions.commands.Command.Subcommand}s.
1313
*/
1414
public TagsCommand() {
15-
setSlashCommandData(Commands.slash("tag", "Commands for interacting with Custom Tags."));
15+
setSlashCommandData(Commands.slash("tag", "Commands for interacting with Custom Tags.")
16+
.setGuildOnly(true)
17+
);
1618
addSubcommands(new TagViewSubcommand(), new TagListSubcommand());
1719
}
1820
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package net.javadiscord.javabot.systems.user_preferences;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import net.javadiscord.javabot.systems.user_preferences.dao.UserPreferenceRepository;
5+
import net.javadiscord.javabot.systems.user_preferences.model.Preference;
6+
import net.javadiscord.javabot.systems.user_preferences.model.UserPreference;
7+
import net.javadiscord.javabot.util.ExceptionLogger;
8+
9+
import javax.sql.DataSource;
10+
import java.sql.Connection;
11+
import java.sql.SQLException;
12+
import java.util.Optional;
13+
14+
/**
15+
* Handles & manages user preferences.
16+
*/
17+
@RequiredArgsConstructor
18+
public class UserPreferenceManager {
19+
private final DataSource dataSource;
20+
21+
/**
22+
* Simply sets the state of the specified {@link Preference} for the specified user.
23+
* If no entry for that user is found, this will simply create a new one.
24+
*
25+
* @param userId The users' id.
26+
* @param preference The {@link Preference} to change the state for.
27+
* @param enabled The preferences' state.
28+
* @return Whether the operation was successful.
29+
*/
30+
public boolean setOrCreate(long userId, Preference preference, boolean enabled) {
31+
try (Connection con = dataSource.getConnection()) {
32+
UserPreferenceRepository repo = new UserPreferenceRepository(con);
33+
Optional<UserPreference> preferenceOptional = repo.getById(userId, preference);
34+
if (preferenceOptional.isPresent()) {
35+
return repo.updateState(userId, preference, enabled);
36+
} else {
37+
UserPreference userPreference = new UserPreference();
38+
userPreference.setUserId(userId);
39+
userPreference.setPreference(preference);
40+
userPreference.setEnabled(enabled);
41+
repo.insert(userPreference, false);
42+
return true;
43+
}
44+
} catch (SQLException e) {
45+
ExceptionLogger.capture(e, getClass().getSimpleName());
46+
return false;
47+
}
48+
}
49+
50+
/**
51+
* Gets a single {@link UserPreference} (or creates a new one if it doesn't exist yet).
52+
*
53+
* @param userId The users' id.
54+
* @param preference The {@link Preference} to get.
55+
* @return The {@link UserPreference}.
56+
*/
57+
public UserPreference getOrCreate(long userId, Preference preference) {
58+
try (Connection con = dataSource.getConnection()) {
59+
UserPreferenceRepository repo = new UserPreferenceRepository(con);
60+
Optional<UserPreference> preferenceOptional = repo.getById(userId, preference);
61+
if (preferenceOptional.isPresent()) {
62+
return preferenceOptional.get();
63+
} else {
64+
UserPreference userPreference = new UserPreference();
65+
userPreference.setUserId(userId);
66+
userPreference.setPreference(preference);
67+
userPreference.setEnabled(preference.getDefaultState());
68+
repo.insert(userPreference, false);
69+
return userPreference;
70+
}
71+
} catch (SQLException e) {
72+
ExceptionLogger.capture(e, getClass().getSimpleName());
73+
return null;
74+
}
75+
}
76+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package net.javadiscord.javabot.systems.user_preferences.commands;
2+
3+
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
4+
import net.dv8tion.jda.api.interactions.commands.build.Commands;
5+
6+
/**
7+
* <h3>This class represents the /preferences command.</h3>
8+
*/
9+
public class PreferencesCommand extends SlashCommand {
10+
/**
11+
* The constructor of this class, which sets the corresponding {@link net.dv8tion.jda.api.interactions.commands.build.SlashCommandData}.
12+
*/
13+
public PreferencesCommand() {
14+
setSlashCommandData(Commands.slash("preferences", "Contains commands for managing user preferences.")
15+
.setGuildOnly(true)
16+
);
17+
addSubcommands(new PreferencesListSubcommand(), new PreferencesSetSubcommand());
18+
}
19+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package net.javadiscord.javabot.systems.user_preferences.commands;
2+
3+
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
4+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
5+
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
6+
import net.javadiscord.javabot.Bot;
7+
import net.javadiscord.javabot.systems.user_preferences.UserPreferenceManager;
8+
import net.javadiscord.javabot.systems.user_preferences.model.Preference;
9+
import net.javadiscord.javabot.util.Responses;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
import java.util.Arrays;
13+
import java.util.stream.Collectors;
14+
15+
/**
16+
* <h3>This class represents the /preferences list command.</h3>
17+
*/
18+
public class PreferencesListSubcommand extends SlashCommand.Subcommand {
19+
/**
20+
* The constructor of this class, which sets the corresponding {@link net.dv8tion.jda.api.interactions.commands.build.SlashCommandData}.
21+
*/
22+
public PreferencesListSubcommand() {
23+
setSubcommandData(new SubcommandData("list", "Shows all your preferences."));
24+
}
25+
26+
@Override
27+
public void execute(@NotNull SlashCommandInteractionEvent event) {
28+
UserPreferenceManager manager = new UserPreferenceManager(Bot.getDataSource());
29+
String preferences = Arrays.stream(Preference.values())
30+
.map(p -> String.format("`%s` %s", manager.getOrCreate(event.getUser().getIdLong(), p).isEnabled() ? "\uD83D\uDFE2" : "\uD83D\uDD34", p))
31+
.collect(Collectors.joining("\n"));
32+
Responses.info(event, String.format("%s's Preferences", event.getUser().getName()), preferences).queue();
33+
}
34+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package net.javadiscord.javabot.systems.user_preferences.commands;
2+
3+
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
4+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
5+
import net.dv8tion.jda.api.interactions.commands.Command;
6+
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
7+
import net.dv8tion.jda.api.interactions.commands.OptionType;
8+
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
9+
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
10+
import net.javadiscord.javabot.Bot;
11+
import net.javadiscord.javabot.systems.user_preferences.UserPreferenceManager;
12+
import net.javadiscord.javabot.systems.user_preferences.model.Preference;
13+
import net.javadiscord.javabot.util.Responses;
14+
import org.jetbrains.annotations.Contract;
15+
import org.jetbrains.annotations.NotNull;
16+
17+
import java.util.Arrays;
18+
19+
/**
20+
* <h3>This class represents the /preferences set command.</h3>
21+
*/
22+
public class PreferencesSetSubcommand extends SlashCommand.Subcommand {
23+
/**
24+
* The constructor of this class, which sets the corresponding {@link net.dv8tion.jda.api.interactions.commands.build.SlashCommandData}.
25+
*/
26+
public PreferencesSetSubcommand() {
27+
setSubcommandData(new SubcommandData("set", "Allows you to set your preferences!")
28+
.addOptions(
29+
new OptionData(OptionType.INTEGER, "preference", "The preference to set.", true)
30+
.addChoices(Arrays.stream(Preference.values()).map(this::toChoice).toList()),
31+
new OptionData(OptionType.BOOLEAN, "state", "The state of the specified preference.", true)
32+
)
33+
);
34+
}
35+
36+
@Override
37+
public void execute(@NotNull SlashCommandInteractionEvent event) {
38+
OptionMapping preferenceMapping = event.getOption("preference");
39+
OptionMapping stateMapping = event.getOption("state");
40+
if (preferenceMapping == null || stateMapping == null) {
41+
Responses.replyMissingArguments(event).queue();
42+
return;
43+
}
44+
Preference preference = Preference.values()[preferenceMapping.getAsInt()];
45+
boolean state = stateMapping.getAsBoolean();
46+
UserPreferenceManager manager = new UserPreferenceManager(Bot.getDataSource());
47+
if (manager.setOrCreate(event.getUser().getIdLong(), preference, state)) {
48+
Responses.info(event, "Preference Updated", "Successfully %s `%s`!", state ? "enabled" : "disabled", preference).queue();
49+
} else {
50+
Responses.error(event, "Could not %s `%s`.", state ? "enable" : "disable", preference).queue();
51+
}
52+
}
53+
54+
@Contract("_ -> new")
55+
private Command.@NotNull Choice toChoice(@NotNull Preference preference) {
56+
return new Command.Choice(preference.toString(), String.valueOf(preference.ordinal()));
57+
}
58+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package net.javadiscord.javabot.systems.user_preferences.dao;
2+
3+
import net.javadiscord.javabot.data.h2db.DatabaseRepository;
4+
import net.javadiscord.javabot.data.h2db.TableProperty;
5+
import net.javadiscord.javabot.systems.user_preferences.model.Preference;
6+
import net.javadiscord.javabot.systems.user_preferences.model.UserPreference;
7+
import org.h2.api.H2Type;
8+
import org.jetbrains.annotations.NotNull;
9+
10+
import java.sql.Connection;
11+
import java.sql.SQLException;
12+
import java.util.List;
13+
import java.util.Optional;
14+
15+
/**
16+
* Dao class that represents the USER_PREFERENCES SQL Table.
17+
*/
18+
public class UserPreferenceRepository extends DatabaseRepository<UserPreference> {
19+
/**
20+
* The constructor of this {@link DatabaseRepository} class which defines all important information
21+
* about the USER_PREFERENCES database table.
22+
*
23+
* @param con The {@link Connection} to use.
24+
*/
25+
public UserPreferenceRepository(Connection con) {
26+
super(con, UserPreference.class, "USER_PREFERENCES", List.of(
27+
TableProperty.of("user_id", H2Type.BIGINT, (x, y) -> x.setUserId((Long) y), UserPreference::getUserId),
28+
TableProperty.of("ordinal", H2Type.INTEGER, (x, y) -> x.setPreference(Preference.values()[(Integer) y]), p -> p.getPreference().ordinal()),
29+
TableProperty.of("enabled", H2Type.BOOLEAN, (x, y) -> x.setEnabled((Boolean) y), UserPreference::isEnabled)
30+
));
31+
}
32+
33+
public Optional<UserPreference> getById(long userId, @NotNull Preference preference) throws SQLException {
34+
return querySingle("WHERE user_id = ? AND ordinal = ?", userId, preference.ordinal());
35+
}
36+
37+
public boolean updateState(long userId, @NotNull Preference preference, boolean enabled) throws SQLException {
38+
return update("UPDATE user_preferences SET enabled = ? WHERE user_id = ? AND ordinal = ?", enabled, userId, preference.ordinal()) > 0;
39+
}
40+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package net.javadiscord.javabot.systems.user_preferences.model;
2+
3+
/**
4+
* Contains all preferences users can set.
5+
*/
6+
public enum Preference {
7+
/**
8+
* Enables/Disables QOTW reminders.
9+
*/
10+
QOTW_REMINDER("Question of the Week Reminder", false);
11+
12+
private final String name;
13+
private final boolean defaultState;
14+
15+
Preference(String name, boolean defaultState) {
16+
this.name = name;
17+
this.defaultState = defaultState;
18+
}
19+
20+
@Override
21+
public String toString() {
22+
return name;
23+
}
24+
25+
public boolean getDefaultState() {
26+
return defaultState;
27+
}
28+
}

0 commit comments

Comments
 (0)