Skip to content

Commit fa434fe

Browse files
Bookmark Command (#634)
* Added bookmark command The bookmark command is a command to add and view bookmarks for help threads. The bookmarks get cleaned after some time if they are not renewed. * Made bookmarks feature sonarlint compliant * Removed BookmarksCommand section comments * Sort bookmarks by latest activity * Made final embed creations in BookmarksCommand more readable Created genSimpleEmbed method in BookmarksCommand to make final embed creations more readable * Revert all changes for rewrite * Full rewrite of the bookmarks feature * Added length limit for note option * Added bookmarks limit to prevent spam * Created replyEmbedEphemeral method because all messages are ephemeral embeds * Changed remove-select-menu to display thread name if the thread was found * Added guild-leave bookmark-removal-scheduling * Fixed requested changes for version 1 that still applied to the rewrite * Added missing javadoc dots * Fixed issue with deleting all bookmarks * Converted unnecessary multiline Strings to normal Strings * Split createTinyEmbed into separate methods for every color * Rename BookmarkPaginatorInteractor to BookmarksPaginatorInteractor * Add javadoc for BookmarksPaginatorInteractor * Don't overwrite args in onSelectMenuSelection * Removed generatePageEmbed overloading for readability * Renamed highestPageIndex to lastPageIndex in generatePageEmbed * Removed newline to improve readability in generatePageEmbed * Removed unnecessary comments in generatePageEmbed * Renamed uninformative variable from b to bookmark when creating PageEntry * Added emptiness check to getLastPageIndex to avoid issues in the future * Fixed ComponentArguments and RemoveComponentArguments having fromStringArgs and toArray in reverse order * Improved the list subcommand description * Renamed LEAVE_REMOVAL_DELAY to LEAVE_BOOKMARKS_REMOVAL_DELAY * Changed requestListPagination and requestRemovePagination to use Objects#requireNonNull instead of a manual null check * Renamed SCHEDULED_REMOVAL_AT to DELETE_AT * Improve naming about deleting old bookmarks of users that left the guild * Improve naming of MAX_BOOKMARK_COUNT_USER to MAX_BOOKMARK_COUNT_PER_USER * Improve naming of LEAVE_BOOKMARKS_REMOVAL_DELAY to REMOVE_BOOKMARKS_AFTER_LEAVE_DELAY * Changed constants to be package private instead of public * Improve speed by using a Set for bookmarksToRemoveChannelIDs * Improved fetching the result of selectCount * Improved speed of checking if a user already bookmarked a channel * Created helper method for createXxxEmbed methods * Improved javadoc @param descriptions * Renamed replyEmbedEphemeral to sendResponse for improved readability * Delayed fetching of the users bookmark count to avoid unnecessary database reads * Removed subtracting 1 from WARN/MAX_BOOKMARK_COUNT_TOTAL to improve readability One bookmark more or less will not make a difference * Fix naming convention violation of delete_at to deleteAt * Improved bookmark limit warning and error log messages * Made error messages more friendly * Improved command descriptions * Changed to correct way of formatting log arguments * Revert "Improved fetching the result of selectCount" This reverts commit e3b08804610bd51a8c75c6b8bbb1c8bde62f403d. * Actually improved fetching the result of selectCount * Improved BookmarksSystem constructor javadoc * Improved the remove subcommand description * Added the users bookmarks limit to the BOOKMARK_LIMIT_USER_EMBED failure embed * Renamed ADD_OPTION_NOTE to ADD_BOOKMARK_NOTE_OPTION * Fixed onSelectMenuSelection not ensuring the page index * Improved BookmarksPaginatorInteractor javadoc * Added BookmarksCommand javadoc * Moved into proper package * overall flow adjustments * More improvements * CR Nxllpointer * Renamings from CR * BookmarksPaginationHandler -> BookmarksListRemoveHandler * RequestWithPagination -> Request * Fixed bookmark limit reached soon log message * Added command usage javadoc for BookmarksCommand * Improved BookmarksSystem javadoc * Spotless after resolving conflicts Co-authored-by: Nxllpointer <[email protected]> Co-authored-by: Zabuzard <[email protected]>
1 parent a7a0944 commit fa434fe

File tree

9 files changed

+777
-0
lines changed

9 files changed

+777
-0
lines changed

application/src/main/java/org/togetherjava/tjbot/commands/Features.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.togetherjava.tjbot.commands.basic.RoleSelectCommand;
77
import org.togetherjava.tjbot.commands.basic.SuggestionsUpDownVoter;
88
import org.togetherjava.tjbot.commands.basic.VcActivityCommand;
9+
import org.togetherjava.tjbot.commands.bookmarks.*;
910
import org.togetherjava.tjbot.commands.code.CodeMessageAutoDetection;
1011
import org.togetherjava.tjbot.commands.code.CodeMessageHandler;
1112
import org.togetherjava.tjbot.commands.code.CodeMessageManualDetection;
@@ -67,6 +68,7 @@ private Features() {
6768
*/
6869
public static Collection<Feature> createFeatures(JDA jda, Database database, Config config) {
6970
TagSystem tagSystem = new TagSystem(database);
71+
BookmarksSystem bookmarksSystem = new BookmarksSystem(config, database);
7072
ModerationActionsStore actionsStore = new ModerationActionsStore(database);
7173
ModAuditLogWriter modAuditLogWriter = new ModAuditLogWriter(config);
7274
ScamHistoryStore scamHistoryStore = new ScamHistoryStore(database);
@@ -90,6 +92,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
9092
features
9193
.add(new AutoPruneHelperRoutine(config, helpSystemHelper, modAuditLogWriter, database));
9294
features.add(new HelpThreadAutoArchiver(helpSystemHelper));
95+
features.add(new LeftoverBookmarksCleanupRoutine(bookmarksSystem));
9396

9497
// Message receivers
9598
features.add(new TopHelpersMessageListener(database, config));
@@ -107,6 +110,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
107110
features.add(new RejoinModerationRoleListener(actionsStore, config));
108111
features.add(new OnGuildLeaveCloseThreadListener(database));
109112
features.add(new UserBannedDeleteRecentThreadsListener(database));
113+
features.add(new LeftoverBookmarksListener(bookmarksSystem));
110114

111115
// Message context commands
112116

@@ -139,6 +143,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
139143
features.add(new ModMailCommand(jda, config));
140144
features.add(new HelpThreadCommand(config, helpSystemHelper));
141145
features.add(new ReportCommand(config));
146+
features.add(new BookmarksCommand(bookmarksSystem));
142147

143148
// Mixtures
144149
features.add(new HelpThreadOverviewUpdater(config, helpSystemHelper));
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package org.togetherjava.tjbot.commands.bookmarks;
2+
3+
import net.dv8tion.jda.api.entities.MessageEmbed;
4+
import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion;
5+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
6+
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
7+
import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent;
8+
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
9+
import net.dv8tion.jda.api.interactions.commands.OptionType;
10+
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
11+
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
15+
import org.togetherjava.tjbot.commands.CommandVisibility;
16+
import org.togetherjava.tjbot.commands.SlashCommandAdapter;
17+
18+
import java.util.List;
19+
import java.util.Objects;
20+
21+
/**
22+
* The bookmarks command is used for managing and viewing bookmarks. A bookmark is a link to a help
23+
* thread that can have a note so you can easily remember why you bookmarked a help thread. Writing
24+
* to the database and showing the list/remove messages is not done by this class, that is handled
25+
* by the {@link BookmarksSystem}. This class only checks if you are able to add a bookmark in the
26+
* current channel and tells the {@link BookmarksSystem} to do the rest.
27+
* <p>
28+
* Usage:
29+
*
30+
* <pre>
31+
* /bookmarks add [note]
32+
* /bookmarks list
33+
* /bookmarks remove
34+
* </pre>
35+
*/
36+
public final class BookmarksCommand extends SlashCommandAdapter {
37+
38+
private static final Logger logger = LoggerFactory.getLogger(BookmarksCommand.class);
39+
40+
public static final String COMMAND_NAME = "bookmarks";
41+
public static final String SUBCOMMAND_ADD = "add";
42+
public static final String SUBCOMMAND_LIST = "list";
43+
public static final String SUBCOMMAND_REMOVE = "remove";
44+
public static final String ADD_BOOKMARK_NOTE_OPTION = "note";
45+
46+
private static final MessageEmbed NOT_A_HELP_THREAD_EMBED =
47+
BookmarksSystem.createFailureEmbed("You can only bookmark help threads.");
48+
49+
private static final MessageEmbed ALREADY_BOOKMARKED_EMBED =
50+
BookmarksSystem.createFailureEmbed("You have already bookmarked this channel.");
51+
52+
private static final MessageEmbed BOOKMARK_ADDED_EMBED =
53+
BookmarksSystem.createSuccessEmbed("Your bookmark was added.");
54+
55+
private static final MessageEmbed BOOKMARK_LIMIT_USER_EMBED = BookmarksSystem
56+
.createFailureEmbed(
57+
"You have exceeded your bookmarks limit of `%d`. Please delete some of your other bookmarks."
58+
.formatted(BookmarksSystem.MAX_BOOKMARK_COUNT_PER_USER));
59+
60+
private static final MessageEmbed BOOKMARK_LIMIT_TOTAL_EMBED = BookmarksSystem
61+
.createWarningEmbed(
62+
"""
63+
You cannot add a bookmark right now because the total amount of bookmarks has exceeded its limit.
64+
Please wait a bit until some of them have been deleted or contact a moderator.
65+
Sorry for the inconvenience.
66+
""");
67+
68+
private final BookmarksSystem bookmarksSystem;
69+
private final BookmarksListRemoveHandler listRemoveHandler;
70+
71+
/**
72+
* Creates a new instance and registers every sub command.
73+
*
74+
* @param bookmarksSystem The {@link BookmarksSystem} to request pagination and manage bookmarks
75+
*/
76+
public BookmarksCommand(BookmarksSystem bookmarksSystem) {
77+
super(COMMAND_NAME, "Bookmark help threads so that you can easily look them up again",
78+
CommandVisibility.GLOBAL);
79+
this.bookmarksSystem = bookmarksSystem;
80+
listRemoveHandler =
81+
new BookmarksListRemoveHandler(bookmarksSystem, this::generateComponentId);
82+
83+
OptionData addNoteOption = new OptionData(OptionType.STRING, ADD_BOOKMARK_NOTE_OPTION,
84+
"Your personal comment on this bookmark")
85+
.setMaxLength(BookmarksSystem.MAX_NOTE_LENGTH)
86+
.setRequired(false);
87+
88+
SubcommandData addSubCommand = new SubcommandData(SUBCOMMAND_ADD,
89+
"Bookmark this help thread, so that you can easily look it up again")
90+
.addOptions(addNoteOption);
91+
92+
SubcommandData listSubCommand =
93+
new SubcommandData(SUBCOMMAND_LIST, "List all of your bookmarks");
94+
95+
SubcommandData removeSubCommand =
96+
new SubcommandData(SUBCOMMAND_REMOVE, "Remove some of your bookmarks");
97+
98+
getData().addSubcommands(addSubCommand, listSubCommand, removeSubCommand);
99+
}
100+
101+
@Override
102+
public void onSlashCommand(SlashCommandInteractionEvent event) {
103+
String subCommandName = Objects.requireNonNull(event.getSubcommandName());
104+
105+
switch (subCommandName) {
106+
case SUBCOMMAND_ADD -> addBookmark(event);
107+
case SUBCOMMAND_LIST -> listRemoveHandler.handleListRequest(event);
108+
case SUBCOMMAND_REMOVE -> listRemoveHandler.handleRemoveRequest(event);
109+
default -> throw new IllegalArgumentException("Unknown subcommand");
110+
}
111+
}
112+
113+
@Override
114+
public void onButtonClick(ButtonInteractionEvent event, List<String> args) {
115+
listRemoveHandler.onButtonClick(event, args);
116+
}
117+
118+
@Override
119+
public void onSelectMenuSelection(SelectMenuInteractionEvent event, List<String> args) {
120+
listRemoveHandler.onSelectMenuSelection(event, args);
121+
}
122+
123+
private void addBookmark(SlashCommandInteractionEvent event) {
124+
long userID = event.getUser().getIdLong();
125+
long channelID = event.getChannel().getIdLong();
126+
String note = event.getOption(ADD_BOOKMARK_NOTE_OPTION, OptionMapping::getAsString);
127+
128+
if (!handleCanAddBookmark(event)) {
129+
return;
130+
}
131+
132+
bookmarksSystem.addBookmark(userID, channelID, note);
133+
134+
sendResponse(event, BOOKMARK_ADDED_EMBED);
135+
}
136+
137+
private boolean handleCanAddBookmark(SlashCommandInteractionEvent event) {
138+
MessageChannelUnion channel = event.getChannel();
139+
long channelID = channel.getIdLong();
140+
long userID = event.getUser().getIdLong();
141+
142+
if (!bookmarksSystem.isHelpThread(channel)) {
143+
sendResponse(event, NOT_A_HELP_THREAD_EMBED);
144+
return false;
145+
}
146+
147+
if (bookmarksSystem.didUserBookmarkChannel(userID, channelID)) {
148+
sendResponse(event, ALREADY_BOOKMARKED_EMBED);
149+
return false;
150+
}
151+
152+
long bookmarkCountTotal = bookmarksSystem.getTotalBookmarkCount();
153+
if (bookmarkCountTotal == BookmarksSystem.WARN_BOOKMARK_COUNT_TOTAL) {
154+
logger.warn("""
155+
The bookmark limit will be reached soon (`{}/{}` bookmarks)!
156+
If the limit is reached no new bookmarks can be added!
157+
Please delete some bookmarks!
158+
""", BookmarksSystem.WARN_BOOKMARK_COUNT_TOTAL,
159+
BookmarksSystem.MAX_BOOKMARK_COUNT_TOTAL);
160+
}
161+
if (bookmarkCountTotal == BookmarksSystem.MAX_BOOKMARK_COUNT_TOTAL) {
162+
logger.error("""
163+
The bookmark limit of `{}` has been reached!
164+
No new bookmarks can be added anymore!
165+
Please delete some bookmarks!
166+
""", BookmarksSystem.MAX_BOOKMARK_COUNT_TOTAL);
167+
}
168+
if (bookmarkCountTotal > BookmarksSystem.MAX_BOOKMARK_COUNT_TOTAL) {
169+
sendResponse(event, BOOKMARK_LIMIT_TOTAL_EMBED);
170+
return false;
171+
}
172+
173+
long bookmarkCountUser = bookmarksSystem.getUserBookmarkCount(userID);
174+
if (bookmarkCountUser >= BookmarksSystem.MAX_BOOKMARK_COUNT_PER_USER) {
175+
sendResponse(event, BOOKMARK_LIMIT_USER_EMBED);
176+
return false;
177+
}
178+
179+
return true;
180+
}
181+
182+
private void sendResponse(SlashCommandInteractionEvent event, MessageEmbed embed) {
183+
event.replyEmbeds(embed).setEphemeral(true).queue();
184+
}
185+
}

0 commit comments

Comments
 (0)