Skip to content

Commit 8528af1

Browse files
committed
Revert "Revert "Merge branch 'main' into springify""
This reverts commit 8a53ae9.
1 parent 8a53ae9 commit 8528af1

File tree

10 files changed

+197
-36
lines changed

10 files changed

+197
-36
lines changed

build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ repositories {
2626
dependencies {
2727
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0")
2828
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0")
29+
compileOnly("com.google.code.findbugs:jsr305:3.0.2")
30+
compileOnly("org.jetbrains:annotations:23.0.0")
2931

3032
// DIH4JDA (Interaction Framework) & JDA
3133
implementation("com.github.DynxstyGIT:DIH4JDA:c8f7928efc")
32-
implementation("net.dv8tion:JDA:5.0.0-alpha.20") {
34+
implementation("com.github.DV8FromTheWorld:JDA:86af73d377") {
3335
exclude(module = "opus-java")
3436
}
3537

src/main/java/net/javadiscord/javabot/data/config/guild/HelpForumConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
@EqualsAndHashCode(callSuper = true)
1414
public class HelpForumConfig extends GuildConfigItem {
1515
private long helpForumChannelId = 0;
16+
private String closeReminderText = "Hey, %s!\nPlease remember to `/close` this post once your question has been answered!";
17+
private String helpThanksText = "Before your post will be closed, would you like to express your gratitude to any of the people who helped you? When you're done, click **I'm done here. Close this post!**.";
1618

1719
public ForumChannel getHelpForumChannel() {
1820
return getGuild().getForumChannelById(helpForumChannelId);

src/main/java/net/javadiscord/javabot/systems/help/HelpChannelManager.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,6 @@ public void reserve(TextChannel channel, User reservingUser, Message message) th
173173
TextChannel targetChannel = dormantChannels.get(0);
174174
targetChannel.getManager().setParent(targetCategory).sync(targetCategory).queue();
175175
targetChannel.sendMessage(config.getReopenedChannelMessage()).queue();
176-
} else {
177-
logChannel.sendMessage("Warning: No dormant channels were available to replenish an open channel that was just reserved.").queue();
178176
}
179177
} else {
180178
this.openNew();

src/main/java/net/javadiscord/javabot/systems/help/HelpChannelUpdater.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
import net.dv8tion.jda.api.JDA;
66
import net.dv8tion.jda.api.entities.*;
77
import net.dv8tion.jda.api.entities.channel.concrete.Category;
8+
import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel;
89
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
910
import net.dv8tion.jda.api.entities.emoji.Emoji;
11+
import net.dv8tion.jda.api.interactions.components.ActionRow;
1012
import net.dv8tion.jda.api.interactions.components.buttons.Button;
1113
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
1214
import net.dv8tion.jda.api.requests.RestAction;
@@ -16,6 +18,7 @@
1618
import net.javadiscord.javabot.data.config.BotConfig;
1719
import net.javadiscord.javabot.data.config.guild.HelpConfig;
1820
import net.javadiscord.javabot.data.h2db.DbActions;
21+
import net.javadiscord.javabot.data.config.guild.HelpForumConfig;
1922
import net.javadiscord.javabot.systems.help.model.ChannelReservation;
2023
import net.javadiscord.javabot.util.ExceptionLogger;
2124
import net.javadiscord.javabot.util.Responses;
@@ -379,20 +382,27 @@ private RestAction<?> semanticMessageCheck(TextChannel channel, User owner, List
379382
}
380383

381384
private void updateHelpOverview() {
382-
helpConfig.getHelpOverviewMessageIds().forEach((channelId, messageId) -> {
383-
TextChannel channel = helpConfig.getGuild().getTextChannelById(channelId);
385+
HelpForumConfig forumConfig = Bot.getConfig().get(config.getGuild()).getHelpForumConfig();
386+
config.getHelpOverviewMessageIds().forEach((channelId, messageId) -> {
387+
TextChannel channel = config.getGuild().getTextChannelById(channelId);
384388
if (channel == null) {
385389
log.error("Could not find Help Overview Channel with id '{}'", channelId);
386390
return;
387391
}
388392
List<TextChannel> availableChannels = helpConfig.getOpenChannelCategory().getTextChannels();
389393
List<Button> buttons = new ArrayList<>(2);
390394
if (!availableChannels.isEmpty()) {
391-
buttons.add(Button.link(availableChannels.get(0).getJumpUrl(), "Show me an available Help Channel!"));
395+
buttons.add(Button.link(availableChannels.get(0).getJumpUrl(), "Show me an Available Help Channel!"));
392396
}
393397
buttons.add(Button.link(StringResourceCache.load("/help_overview/overview_image_url.txt"), "How does this work?"));
394398
channel.retrieveMessageById(messageId).queue(
395-
m -> m.editMessageEmbeds(buildHelpOverviewEmbed()).setActionRow(buttons).queue(),
399+
m -> m.editMessageEmbeds(buildHelpOverviewEmbed()).setComponents(
400+
ActionRow.of(
401+
// Temporary Forum Channel Upsell
402+
Button.link(forumConfig.getHelpForumChannel().getJumpUrl(), "Try our new Help Forum!"),
403+
Button.link("https://discord.com/blog/forum-channels-space-for-organized-conversation", "What are Forums?")
404+
),
405+
ActionRow.of(buttons)).queue(),
396406
err -> channel.sendMessageEmbeds(buildHelpOverviewEmbed()).queue(m -> {
397407
helpConfig.getHelpOverviewMessageIds().put(channelId, m.getIdLong());
398408
botConfig.flush();
@@ -426,10 +436,14 @@ private void updateHelpOverview() {
426436
e -> ExceptionLogger.capture(e, getClass().getSimpleName())
427437
);
428438
}
439+
ForumChannel forum = Bot.getConfig().get(config.getGuild()).getHelpForumConfig().getHelpForumChannel();
429440
EmbedBuilder builder = new EmbedBuilder()
430441
.setTitle("Help Overview")
431442
.setColor(Responses.Type.DEFAULT.getColor())
432-
.setDescription(availableHelpChannels + " are __**available**__ to claim!")
443+
.setDescription(availableHelpChannels.isEmpty() ?
444+
String.format("There are no help channels available to claim.%nHow about using our new **[Help Forum](%s)** (%s) then?",
445+
forum.getJumpUrl(), forum.getAsMention()) :
446+
availableHelpChannels + " are __**available**__ to claim!")
433447
.setFooter("Last refreshed: ")
434448
.setTimestamp(Instant.now());
435449
if (!reservedHelpChannels.isEmpty()) {

src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.dynxsty.dih4jda.interactions.commands.SlashCommand;
44
import net.dv8tion.jda.api.EmbedBuilder;
55
import net.dv8tion.jda.api.entities.*;
6+
import net.dv8tion.jda.api.entities.channel.ChannelType;
7+
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
68
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
79
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
810
import net.javadiscord.javabot.data.config.BotConfig;
@@ -26,7 +28,7 @@
2628
* helpers.
2729
*/
2830
public class HelpPingSubcommand extends SlashCommand.Subcommand {
29-
private static final String WRONG_CHANNEL_MSG = "This command can only be used in **reserved help channels**.";
31+
private static final String WRONG_CHANNEL_MSG = "This command can only be used in **reserved help channels** OR **help forum posts.**.";
3032
private static final long CACHE_CLEANUP_DELAY = 60L;
3133

3234
private final Map<Long, Pair<Long, Guild>> lastPingTimes;
@@ -59,8 +61,45 @@ public void execute(@NotNull SlashCommandInteractionEvent event) {
5961
Responses.warning(event, WRONG_CHANNEL_MSG).queue();
6062
return;
6163
}
62-
GuildConfig config = botConfig.get(guild);
63-
HelpChannelManager channelManager=new HelpChannelManager(botConfig, guild, dbActions, asyncPool, helpExperienceService);
64+
GuildConfig config = Bot.getConfig().get(guild);
65+
if (event.getChannelType() == ChannelType.TEXT) {
66+
handleTextBasedHelpPing(event, config);
67+
} else if (event.getChannelType() == ChannelType.GUILD_PUBLIC_THREAD) {
68+
handleForumBasedHelpPing(event, config, event.getChannel().asThreadChannel());
69+
}
70+
}
71+
72+
private void handleForumBasedHelpPing(@NotNull SlashCommandInteractionEvent event, @NotNull GuildConfig config, @NotNull ThreadChannel post) {
73+
if (post.getParentChannel().getType() != ChannelType.FORUM ||
74+
post.getParentChannel().getIdLong() != config.getHelpForumConfig().getHelpForumChannelId()
75+
) {
76+
Responses.error(event, WRONG_CHANNEL_MSG).queue();
77+
return;
78+
}
79+
Member member = event.getMember();
80+
if (member == null) {
81+
Responses.warning(event, "No member information was available for this event.").queue();
82+
return;
83+
}
84+
if (isHelpPingForbiddenForMember(post.getOwnerIdLong(), member, config)) {
85+
Responses.warning(event, "Sorry, but only the person who reserved this channel, or staff and helpers, may use this command.").queue();
86+
return;
87+
}
88+
if (isHelpPingTimeoutElapsed(member.getIdLong(), config)) {
89+
lastPingTimes.put(event.getMember().getIdLong(), new Pair<>(System.currentTimeMillis(), config.getGuild()));
90+
Role role = config.getHelpConfig().getHelpPingRole();
91+
event.getChannel().sendMessage(role.getAsMention())
92+
.setAllowedMentions(EnumSet.of(Message.MentionType.ROLE))
93+
.setEmbeds(buildAuthorEmbed(event.getUser()))
94+
.queue();
95+
event.replyFormat("Successfully pinged " + role.getAsMention()).setEphemeral(true).queue();
96+
} else {
97+
Responses.warning(event, "Sorry, but you can only use this command occasionally. Please try again later.").queue();
98+
}
99+
}
100+
101+
private void handleTextBasedHelpPing(@NotNull SlashCommandInteractionEvent event, @NotNull GuildConfig config) {
102+
HelpChannelManager channelManager = new HelpChannelManager(config.getHelpConfig());
64103
if (channelManager.isReserved(event.getChannel().asTextChannel())) {
65104
Optional<ChannelReservation> optionalReservation = channelManager.getReservationForChannel(event.getChannel().getIdLong());
66105
if (optionalReservation.isEmpty()) {
@@ -78,7 +117,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) {
78117
return;
79118
}
80119
if (isHelpPingTimeoutElapsed(member.getIdLong(), config)) {
81-
lastPingTimes.put(event.getMember().getIdLong(), new Pair<>(System.currentTimeMillis(), guild));
120+
lastPingTimes.put(event.getMember().getIdLong(), new Pair<>(System.currentTimeMillis(), config.getGuild()));
82121
Role role = channelManager.getConfig().getHelpPingRole();
83122
event.getChannel().sendMessage(role.getAsMention())
84123
.setAllowedMentions(EnumSet.of(Message.MentionType.ROLE))
@@ -118,6 +157,24 @@ private boolean isHelpPingForbiddenForMember(@NotNull ChannelReservation reserva
118157
);
119158
}
120159

160+
/**
161+
* Determines if a user is forbidden from sending a help-ping command due
162+
* to their status in the server.
163+
*
164+
* @param postOwnerId The posts' owner id.
165+
* @param member The member.
166+
* @param config The guild config.
167+
* @return True if the user is forbidden from sending the command.
168+
*/
169+
private boolean isHelpPingForbiddenForMember(long postOwnerId, @NotNull Member member, @NotNull GuildConfig config) {
170+
Set<Role> allowedRoles = Set.of(config.getModerationConfig().getStaffRole(), config.getHelpConfig().getHelperRole());
171+
return !(
172+
postOwnerId == member.getUser().getIdLong() ||
173+
member.getRoles().stream().anyMatch(allowedRoles::contains) ||
174+
member.isOwner()
175+
);
176+
}
177+
121178
/**
122179
* Determines if the user's timeout has elapsed (or doesn't exist), which
123180
* implies that it's fine for the user to send the command.

src/main/java/net/javadiscord/javabot/systems/help/commands/UnreserveCommand.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,6 @@ private void replyInvalidChannel(CommandInteraction interaction) {
107107
.queue();
108108
}
109109

110-
private boolean isForumEligibleToBeUnreserved(@NotNull SlashCommandInteractionEvent event, @NotNull ThreadChannel postThread) {
111-
return event.getUser().getIdLong() == postThread.getOwnerIdLong() || memberHasHelperRole(event) || memberHasStaffRole(event);
112-
}
113-
114110
private boolean isTextEligibleToBeUnreserved(SlashCommandInteractionEvent event, TextChannel channel, HelpConfig config, User owner) {
115111
return channelIsInReservedCategory(channel, config) &&
116112
(isUserWhoReservedChannel(event, owner) || memberHasHelperRole(event) || memberHasStaffRole(event));

src/main/java/net/javadiscord/javabot/systems/help/forum/ForumHelpListener.java

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,36 @@
44
import com.dynxsty.dih4jda.interactions.components.ButtonHandler;
55

66
import lombok.RequiredArgsConstructor;
7+
import net.dv8tion.jda.api.EmbedBuilder;
78
import net.dv8tion.jda.api.entities.Message;
9+
import net.dv8tion.jda.api.entities.UserSnowflake;
10+
import net.dv8tion.jda.api.entities.channel.Channel;
811
import net.dv8tion.jda.api.entities.channel.ChannelType;
912
import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel;
1013
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
14+
import net.dv8tion.jda.api.events.channel.ChannelCreateEvent;
1115
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
1216
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
1317
import net.dv8tion.jda.api.hooks.ListenerAdapter;
18+
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
1419
import net.dv8tion.jda.api.interactions.components.ActionComponent;
20+
import net.dv8tion.jda.api.interactions.components.ActionRow;
1521
import net.dv8tion.jda.api.interactions.components.buttons.Button;
1622
import net.javadiscord.javabot.data.config.BotConfig;
1723
import net.javadiscord.javabot.data.config.GuildConfig;
1824
import net.javadiscord.javabot.data.config.guild.HelpConfig;
1925
import net.javadiscord.javabot.data.h2db.DbActions;
26+
import net.javadiscord.javabot.Bot;
27+
import net.javadiscord.javabot.data.config.guild.HelpConfig;
28+
import net.javadiscord.javabot.data.config.guild.HelpForumConfig;
2029
import net.javadiscord.javabot.systems.help.HelpChannelManager;
2130
import net.javadiscord.javabot.systems.help.HelpExperienceService;
2231
import net.javadiscord.javabot.systems.help.dao.HelpAccountRepository;
2332
import net.javadiscord.javabot.systems.help.dao.HelpTransactionRepository;
2433
import net.javadiscord.javabot.systems.help.model.HelpTransactionMessage;
34+
import net.javadiscord.javabot.systems.user_preferences.UserPreferenceService;
35+
import net.javadiscord.javabot.systems.user_preferences.model.Preference;
36+
import net.javadiscord.javabot.systems.user_preferences.model.UserPreference;
2537
import net.javadiscord.javabot.util.ExceptionLogger;
2638
import net.javadiscord.javabot.util.Responses;
2739
import org.jetbrains.annotations.NotNull;
@@ -53,19 +65,12 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) {
5365
if (event.getMessage().getAuthor().isSystem() || event.getMessage().getAuthor().isBot()) {
5466
return;
5567
}
56-
// check for guild & channel type
57-
if (!event.isFromGuild() || event.getChannelType() != ChannelType.GUILD_PUBLIC_THREAD) {
68+
// check for forum post
69+
if (isInvalidForumPost(event.getChannel())) {
5870
return;
5971
}
60-
// get post & check parent channel
6172
ThreadChannel post = event.getChannel().asThreadChannel();
62-
if (post.getParentChannel().getType() != ChannelType.FORUM) {
63-
return;
64-
}
65-
ForumChannel forum = post.getParentChannel().asForumChannel();
66-
GuildConfig config = botConfig.get(event.getGuild());
67-
// check for channel id
68-
if (forum.getIdLong() != config.getHelpForumConfig().getHelpForumChannelId()) {
73+
if (isInvalidHelpForumChannel(post.getParentChannel().asForumChannel())) {
6974
return;
7075
}
7176
// cache messages
@@ -77,20 +82,59 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) {
7782
HELP_POST_MESSAGES.put(post.getIdLong(), messages);
7883
}
7984

85+
@Override
86+
public void onChannelCreate(@NotNull ChannelCreateEvent event) {
87+
if (event.getGuild() == null || isInvalidForumPost(event.getChannel())) {
88+
return;
89+
}
90+
HelpForumConfig config = Bot.getConfig().get(event.getGuild()).getHelpForumConfig();
91+
ThreadChannel post = event.getChannel().asThreadChannel();
92+
if (isInvalidHelpForumChannel(post.getParentChannel().asForumChannel())) {
93+
return;
94+
}
95+
// send post buttons
96+
post.sendMessageComponents(ActionRow.of(
97+
Button.primary(ComponentIdBuilder.build(ForumHelpManager.HELP_CLOSE_IDENTIFIER, post.getIdLong()), "Close Post"),
98+
Button.secondary(ComponentIdBuilder.build(ForumHelpManager.HELP_GUIDELINES_IDENTIFIER), "View Help Guidelines")
99+
)).queue(success -> {
100+
// send /close reminder (if enabled)
101+
UserPreferenceService service = new UserPreferenceService(Bot.getDataSource());
102+
UserPreference preference = service.getOrCreate(post.getOwnerIdLong(), Preference.FORUM_CLOSE_REMINDER);
103+
if (Boolean.parseBoolean(preference.getState())) {
104+
post.sendMessageFormat(config.getCloseReminderText(), UserSnowflake.fromId(post.getOwnerIdLong()).getAsMention()).queue();
105+
}
106+
});
107+
}
108+
80109
@Override
81110
public void handleButton(@NotNull ButtonInteractionEvent event, @NotNull Button button) {
82111
String[] id = ComponentIdBuilder.split(event.getComponentId());
83-
if (event.getChannelType() != ChannelType.GUILD_PUBLIC_THREAD
84-
|| event.getChannel().asThreadChannel().getParentChannel().getType() != ChannelType.FORUM) {
112+
if (isInvalidForumPost(event.getChannel()) ||
113+
isInvalidHelpForumChannel(event.getChannel().asThreadChannel().getParentChannel().asForumChannel())
114+
) {
85115
Responses.error(event, "This button may only be used inside help forum threads.").queue();
86116
return;
87117
}
88118
ForumHelpManager manager = new ForumHelpManager(event.getChannel().asThreadChannel(), dbActions, botConfig, dataSource, helpAccountRepository, helpTransactionRepository);
119+
ThreadChannel post = event.getChannel().asThreadChannel();
120+
ForumHelpManager manager = new ForumHelpManager(post);
89121
switch (id[0]) {
90122
case ForumHelpManager.HELP_THANKS_IDENTIFIER -> handleHelpThanksInteraction(event, manager, id);
123+
case ForumHelpManager.HELP_GUIDELINES_IDENTIFIER -> handleReplyGuidelines(event, post.getParentChannel().asForumChannel());
124+
case ForumHelpManager.HELP_CLOSE_IDENTIFIER -> handlePostClose(event, manager);
91125
}
92126
}
93127

128+
private boolean isInvalidForumPost(@NotNull Channel channel) {
129+
return channel.getType() != ChannelType.GUILD_PUBLIC_THREAD ||
130+
((ThreadChannel) channel).getParentChannel().getType() != ChannelType.FORUM;
131+
}
132+
133+
private boolean isInvalidHelpForumChannel(@NotNull ForumChannel forum) {
134+
HelpForumConfig config = Bot.getConfig().get(forum.getGuild()).getHelpForumConfig();
135+
return config.getHelpForumChannelId() != forum.getIdLong();
136+
}
137+
94138
private void handleHelpThanksInteraction(@NotNull ButtonInteractionEvent event, @NotNull ForumHelpManager manager, String @NotNull [] id) {
95139
ThreadChannel post = manager.getPostThread();
96140
HelpConfig config = botConfig.get(event.getGuild()).getHelpConfig();
@@ -122,4 +166,21 @@ private void handleHelpThanksInteraction(@NotNull ButtonInteractionEvent event,
122166
default -> event.editButton(event.getButton().asDisabled()).queue();
123167
}
124168
}
169+
170+
private void handleReplyGuidelines(@NotNull IReplyCallback callback, @NotNull ForumChannel channel) {
171+
callback.replyEmbeds(new EmbedBuilder()
172+
.setTitle("Help Guidelines")
173+
.setDescription(channel.getTopic())
174+
.build()
175+
).setEphemeral(true)
176+
.queue();
177+
}
178+
179+
private void handlePostClose(ButtonInteractionEvent event, @NotNull ForumHelpManager manager) {
180+
if (manager.isForumEligibleToBeUnreserved(event)) {
181+
manager.close(event, event.getUser().getIdLong() == manager.postThread().getOwnerIdLong(), null);
182+
} else {
183+
Responses.warning(event, "Could not close this post", "You're not allowed to close this post.").queue();
184+
}
185+
}
125186
}

0 commit comments

Comments
 (0)