Skip to content

Commit 1bb2758

Browse files
committed
Added naming strategies and complete implementation.
1 parent 8828212 commit 1bb2758

File tree

11 files changed

+312
-54
lines changed

11 files changed

+312
-54
lines changed

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.javadiscord.javabot.data.H2DataSource;
44
import com.javadiscord.javabot.events.*;
55
import com.javadiscord.javabot.help.HelpChannelListener;
6-
import com.javadiscord.javabot.help.HelpChannelUpdater;
76
import com.javadiscord.javabot.properties.config.BotConfig;
87
import net.dv8tion.jda.api.JDA;
98
import net.dv8tion.jda.api.JDABuilder;
@@ -18,7 +17,6 @@
1817
import java.util.TimeZone;
1918
import java.util.concurrent.Executors;
2019
import java.util.concurrent.ScheduledExecutorService;
21-
import java.util.concurrent.TimeUnit;
2220

2321
/**
2422
* The main class where the bot is initialized.
@@ -77,7 +75,6 @@ public static void main(String[] args) throws Exception {
7775
.addEventListeners(slashCommands)
7876
.build();
7977
addEventListeners(jda);
80-
asyncPool.scheduleAtFixedRate(new HelpChannelUpdater(jda), 1, 1, TimeUnit.SECONDS);
8178
}
8279

8380
/**

src/main/java/com/javadiscord/javabot/events/InteractionListener.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
import com.google.gson.JsonObject;
44
import com.google.gson.JsonParser;
5+
import com.javadiscord.javabot.Bot;
6+
import com.javadiscord.javabot.help.HelpChannelManager;
57
import com.mongodb.client.MongoCollection;
68
import com.mongodb.client.MongoDatabase;
7-
import net.dv8tion.jda.api.entities.Guild;
8-
import net.dv8tion.jda.api.entities.Member;
9-
import net.dv8tion.jda.api.entities.Role;
9+
import lombok.extern.slf4j.Slf4j;
10+
import net.dv8tion.jda.api.entities.*;
1011
import net.dv8tion.jda.api.events.interaction.ButtonClickEvent;
1112
import net.dv8tion.jda.api.hooks.ListenerAdapter;
1213
import org.bson.Document;
@@ -15,6 +16,7 @@
1516
import static com.javadiscord.javabot.events.Startup.preferredGuild;
1617
import static com.mongodb.client.model.Filters.eq;
1718

19+
@Slf4j
1820
public class InteractionListener extends ListenerAdapter {
1921

2022
// TODO: add Context-Menu Commands (once they're available in JDA)
@@ -31,6 +33,7 @@ public void onButtonClick(ButtonClickEvent event) {
3133
case "dm-submission" -> this.handleDmSubmission(database, guild, event);
3234
case "submission" -> this.handleSubmission(database, guild, event);
3335
case "reaction-role" -> this.handleReactionRoles(event);
36+
case "help-channel" -> this.handleHelpChannel(event, id[1]);
3437
}
3538
}
3639

@@ -81,4 +84,30 @@ private void handleReactionRoles(ButtonClickEvent event) {
8184
event.getHook().sendMessage("Added Role: " + role.getAsMention()).setEphemeral(true).queue();
8285
}
8386
}
87+
88+
private void handleHelpChannel(ButtonClickEvent event, String action) {
89+
var config = Bot.config.get(event.getGuild()).getHelp();
90+
var channelManager = new HelpChannelManager(config);
91+
TextChannel channel = event.getTextChannel();
92+
User owner = channelManager.getReservedChannelOwner(channel);
93+
if (owner == null) {
94+
return; // This channel will be pruned automatically.
95+
}
96+
97+
if (
98+
event.getUser().equals(owner) ||
99+
(event.getMember() != null && event.getMember().getRoles().contains(Bot.config.get(event.getGuild()).getModeration().getStaffRole()))
100+
) {
101+
if (action.equals("done")) {
102+
log.info("Removing reserved channel {} because it was marked as done.", channel.getAsMention());
103+
channel.delete().queue();
104+
} else if (action.equals("not-done")) {
105+
if (event.getMessage() != null) {
106+
log.info("Removing timeout check message in {} because it was marked as not-done.", channel.getAsMention());
107+
event.getMessage().delete().queue();
108+
channel.sendMessage("Okay, we'll keep this channel reserved for you, and check again in **" + config.getInactivityTimeoutMinutes() + "** minutes.").queue();
109+
}
110+
}
111+
}
112+
}
84113
}

src/main/java/com/javadiscord/javabot/events/Startup.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import ch.qos.logback.classic.LoggerContext;
66
import com.javadiscord.javabot.Bot;
77
import com.javadiscord.javabot.commands.other.Version;
8+
import com.javadiscord.javabot.help.HelpChannelUpdater;
89
import com.javadiscord.javabot.other.Database;
910
import com.javadiscord.javabot.other.Misc;
1011
import com.mongodb.MongoClient;
@@ -92,6 +93,10 @@ public void onReady(ReadyEvent event) {
9293
new Database().deleteOpenSubmissions(guild);
9394
new StarboardListener().updateAllSBM(guild);
9495
Bot.slashCommands.registerSlashCommands(guild);
96+
97+
// Schedule the help channel updater to run periodically for each guild.
98+
var helpConfig = Bot.config.get(guild).getHelp();
99+
Bot.asyncPool.scheduleAtFixedRate(new HelpChannelUpdater(event.getJDA(), helpConfig), 5, helpConfig.getUpdateIntervalSeconds(), TimeUnit.SECONDS);
95100
}
96101

97102

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.javadiscord.javabot.help;
2+
3+
import com.javadiscord.javabot.properties.config.guild.HelpConfig;
4+
import net.dv8tion.jda.api.entities.TextChannel;
5+
6+
import java.util.List;
7+
import java.util.concurrent.ThreadLocalRandom;
8+
9+
/**
10+
* Naming strategy that names help channels with a random letter. Note that the
11+
* chance for duplicates is quite high!
12+
*/
13+
public class AlphabetNamingStrategy implements ChannelNamingStrategy {
14+
private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
15+
16+
@Override
17+
public String getName(List<TextChannel> channels, HelpConfig config) {
18+
return config.getOpenChannelPrefix() + "help-" + ALPHABET.charAt(ThreadLocalRandom.current().nextInt(ALPHABET.length()));
19+
}
20+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.javadiscord.javabot.help;
2+
3+
import com.javadiscord.javabot.properties.config.guild.HelpConfig;
4+
import net.dv8tion.jda.api.entities.TextChannel;
5+
6+
import java.util.List;
7+
import java.util.concurrent.ThreadLocalRandom;
8+
9+
/**
10+
* A naming strategy that names channels with a random animal name.
11+
*/
12+
public class AnimalNamingStrategy implements ChannelNamingStrategy {
13+
private static final String[] ANIMALS = {
14+
"bear", "tiger", "lion", "snake", "cheetah", "panther", "bat", "mosquito", "opossum", "raccoon", "beaver",
15+
"walrus", "seal", "dolphin", "shark", "narwhal", "orca", "whale", "squid", "tuna", "nautilus", "jellyfish",
16+
"seagull", "eagle", "hawk", "flamingo", "spoonbill", "puffin", "condor", "albatross", "parrot", "parakeet",
17+
"rabbit", "sloth", "deer", "boar", "ferret", "dog", "cat", "marmoset", "mole", "lizard", "kangaroo"
18+
};
19+
20+
@Override
21+
public String getName(List<TextChannel> channels, HelpConfig config) {
22+
String name = ANIMALS[ThreadLocalRandom.current().nextInt(ANIMALS.length)];
23+
return config.getOpenChannelPrefix() + "help-" + name;
24+
}
25+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.javadiscord.javabot.help;
2+
3+
import com.javadiscord.javabot.properties.config.guild.HelpConfig;
4+
import net.dv8tion.jda.api.entities.TextChannel;
5+
6+
import java.util.List;
7+
8+
/**
9+
* A strategy to use to generate names for new help channels as they're needed.
10+
*/
11+
public interface ChannelNamingStrategy {
12+
String getName(List<TextChannel> channels, HelpConfig config);
13+
}

src/main/java/com/javadiscord/javabot/help/HelpChannelListener.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@
1212
* more designated help channels.
1313
*/
1414
public class HelpChannelListener extends ListenerAdapter {
15+
1516
@Override
1617
public void onGuildMessageReceived(@NotNull GuildMessageReceivedEvent event) {
1718
if (event.getAuthor().isBot() || event.getAuthor().isSystem()) return;
1819

1920
var config = Bot.config.get(event.getGuild()).getHelp();
2021
TextChannel channel = event.getChannel();
2122
Category category = channel.getParent();
22-
if (category == null) return;
23+
if (category == null || !category.equals(config.getHelpChannelCategory())) return;
24+
var channelManager = new HelpChannelManager(config);
25+
26+
// If a message was sent in an open text channel, reserve it.
2327
if (channel.getName().startsWith(config.getOpenChannelPrefix())) {
24-
String rawChannelName = channel.getName().substring(config.getOpenChannelPrefix().length());
25-
channel.getManager().setName(config.getReservedChannelPrefix() + rawChannelName).queue();
26-
channel.getManager().setPosition(category.getTextChannels().size()).queue();
28+
channelManager.reserve(channel, event.getAuthor());
2729
}
2830
}
2931
}
Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,79 @@
11
package com.javadiscord.javabot.help;
22

33
import com.javadiscord.javabot.properties.config.guild.HelpConfig;
4-
import net.dv8tion.jda.api.entities.Message;
4+
import lombok.extern.slf4j.Slf4j;
55
import net.dv8tion.jda.api.entities.TextChannel;
6+
import net.dv8tion.jda.api.entities.User;
67

7-
import java.util.List;
8+
import java.util.Objects;
9+
import java.util.regex.Pattern;
810

11+
/**
12+
* This manager is responsible for all the main interactions that affect the
13+
* help system's channels.
14+
*/
15+
@Slf4j
916
public class HelpChannelManager {
17+
private final HelpConfig config;
18+
19+
public HelpChannelManager(HelpConfig config) {
20+
this.config = config;
21+
}
22+
23+
public boolean isOpen(TextChannel channel) {
24+
return channel.getName().startsWith(config.getOpenChannelPrefix());
25+
}
26+
27+
public boolean isReserved(TextChannel channel) {
28+
return channel.getName().startsWith(config.getReservedChannelPrefix());
29+
}
30+
1031
/**
1132
* Opens a text channel so that it is ready for a new question.
12-
* @param channel The channel to open.
13-
* @param config The configuration properties.
1433
*/
15-
public void open(TextChannel channel, HelpConfig config) {
16-
String rawName = channel.getName().substring(config.getReservedChannelPrefix().length());
17-
channel.getManager().setName(config.getOpenChannelPrefix() + rawName).queue();
18-
channel.getManager().setPosition(0).queue();
19-
removeAllMessages(channel);
34+
public void openNew() {
35+
var category = config.getHelpChannelCategory();
36+
if (category == null) throw new IllegalStateException("Missing help channel category. Cannot open a new help channel.");
37+
String name = this.config.getChannelNamingStrategy().getName(category.getTextChannels(), config);
38+
category.createTextChannel(name).queue(channel -> {
39+
channel.getManager().setPosition(0).setTopic("Ask a question here!").queue();
40+
log.info("Created new help channel {}.", channel.getAsMention());
41+
});
42+
}
43+
44+
/**
45+
* Reserves a text channel for a user.
46+
* @param channel The channel to reserve.
47+
* @param reservingUser The user who is reserving the channel.
48+
*/
49+
public void reserve(TextChannel channel, User reservingUser) {
50+
String rawChannelName = channel.getName().substring(config.getOpenChannelPrefix().length());
51+
channel.getManager()
52+
.setName(config.getReservedChannelPrefix() + rawChannelName)
53+
.setPosition(Objects.requireNonNull(channel.getParent()).getTextChannels().size())
54+
.setTopic(String.format(
55+
"Reserved for %s\n(_id=%s_)",
56+
reservingUser.getAsTag(),
57+
reservingUser.getId()
58+
)).queue();
59+
log.info("Reserved channel {} for {}.", channel.getAsMention(), reservingUser.getAsTag());
60+
openNew(); // Open a new channel immediately, to keep things balanced.
2061
}
2162

2263
/**
23-
* Utility method to remove all messages from a channel.
24-
* @param channel The channel to remove messages from.
64+
* Gets the owner of a reserved channel.
65+
* @param channel The channel to get the owner of.
66+
* @return The user who reserved the channel, or null.
2567
*/
26-
private void removeAllMessages(TextChannel channel) {
27-
List<Message> messages;
28-
do {
29-
messages = channel.getHistory().retrievePast(50).complete();
30-
if (messages.isEmpty()) break;
31-
if (messages.size() == 1) {
32-
channel.deleteMessageById(messages.get(0).getIdLong()).complete();
33-
} else {
34-
channel.deleteMessages(messages).complete();
68+
public User getReservedChannelOwner(TextChannel channel) {
69+
var pattern = Pattern.compile("\\(_id=(\\d+)_\\)");
70+
if (channel.getTopic() != null) {
71+
var matcher = pattern.matcher(channel.getTopic());
72+
if (matcher.find()) {
73+
String id = matcher.group(1);
74+
return channel.getJDA().retrieveUserById(id).complete();
3575
}
36-
} while (!messages.isEmpty());
76+
}
77+
return null;
3778
}
3879
}

0 commit comments

Comments
 (0)