88import net .javadiscord .javabot .command .ResponseException ;
99import net .javadiscord .javabot .command .Responses ;
1010import net .javadiscord .javabot .command .interfaces .SlashCommand ;
11+ import net .javadiscord .javabot .data .config .GuildConfig ;
1112import net .javadiscord .javabot .systems .help .HelpChannelManager ;
13+ import net .javadiscord .javabot .systems .help .model .ChannelReservation ;
1214
1315import java .util .EnumSet ;
1416import java .util .Map ;
17+ import java .util .Optional ;
18+ import java .util .Set ;
1519import java .util .concurrent .ConcurrentHashMap ;
1620import java .util .concurrent .TimeUnit ;
1721
@@ -26,39 +30,44 @@ public class HelpPingCommand implements SlashCommand {
2630 private final Map <Member , Long > lastPingTimes ;
2731
2832 /**
29- * Constructor that initializes and handles the cooldown map.
33+ * Constructor that initializes a scheduled cache cleanup for this
34+ * command's built-in {@link HelpPingCommand#lastPingTimes} cache.
3035 */
3136 public HelpPingCommand () {
3237 lastPingTimes = new ConcurrentHashMap <>();
33- Bot .asyncPool .scheduleWithFixedDelay (() -> {
34- var membersToRemove = lastPingTimes .entrySet ().stream ().filter (entry -> {
35- var config = Bot .config .get (entry .getKey ().getGuild ()).getHelp ();
36- long timeoutMillis = config .getHelpPingTimeoutSeconds () * 1000L ;
37- return entry .getValue () + timeoutMillis < System .currentTimeMillis ();
38- }).map (Map .Entry ::getKey ).toList ();
39- for (var member : membersToRemove ) {
40- lastPingTimes .remove (member );
41- }
42- }, CACHE_CLEANUP_DELAY , CACHE_CLEANUP_DELAY , TimeUnit .SECONDS );
38+ Bot .asyncPool .scheduleWithFixedDelay (this ::cleanTimeoutCache , CACHE_CLEANUP_DELAY , CACHE_CLEANUP_DELAY , TimeUnit .SECONDS );
4339 }
4440
4541 @ Override
4642 public ReplyCallbackAction handleSlashCommandInteraction (SlashCommandInteractionEvent event ) throws ResponseException {
4743 Guild guild = event .getGuild ();
4844 if (guild == null ) return Responses .warning (event , WRONG_CHANNEL_MSG );
49- var channelManager = new HelpChannelManager (Bot .config .get (guild ).getHelp ());
45+ GuildConfig config = Bot .config .get (guild );
46+ var channelManager = new HelpChannelManager (config .getHelp ());
5047 if (channelManager .isReserved (event .getTextChannel ())) {
51- Long lastPing = lastPingTimes .get (event .getMember ());
52- if (lastPing != null && lastPing + channelManager .getConfig ().getHelpPingTimeoutSeconds () * 1000L > System .currentTimeMillis ()) {
48+ Optional <ChannelReservation > optionalReservation = channelManager .getReservationForChannel (event .getTextChannel ().getIdLong ());
49+ if (optionalReservation .isEmpty ()) {
50+ return Responses .warning (event , "Could not fetch the channel reservation." );
51+ }
52+ ChannelReservation reservation = optionalReservation .get ();
53+ Member member = event .getMember ();
54+ if (member == null ) {
55+ return Responses .warning (event , "No member information was available for this event." );
56+ }
57+ if (isHelpPingForbiddenForMember (reservation , member , config )) {
58+ return Responses .warning (event , "Sorry, but only the person who reserved this channel, or staff and helpers, may use this command." );
59+ }
60+ if (isHelpPingTimeoutElapsed (member , config )) {
61+ lastPingTimes .put (event .getMember (), System .currentTimeMillis ());
62+ Role role = channelManager .getConfig ().getHelpPingRole ();
63+ event .getChannel ().sendMessage (role .getAsMention ())
64+ .allowedMentions (EnumSet .of (Message .MentionType .ROLE ))
65+ .setEmbeds (this .buildAuthorEmbed (event .getUser ()))
66+ .queue ();
67+ return event .replyFormat ("Successfully pinged " + role .getAsMention ()).setEphemeral (true );
68+ } else {
5369 return Responses .warning (event , "Sorry, but you can only use this command occasionally. Please try again later." );
5470 }
55- lastPingTimes .put (event .getMember (), System .currentTimeMillis ());
56- Role role = channelManager .getConfig ().getHelpPingRole ();
57- event .getChannel ().sendMessage (role .getAsMention ())
58- .allowedMentions (EnumSet .of (Message .MentionType .ROLE ))
59- .setEmbeds (this .buildAuthorEmbed (event .getUser ()))
60- .queue ();
61- return event .replyFormat ("Successfully pinged " + role .getAsMention ()).setEphemeral (true );
6271 } else {
6372 return Responses .warning (event , WRONG_CHANNEL_MSG );
6473 }
@@ -69,4 +78,53 @@ private MessageEmbed buildAuthorEmbed(User author) {
6978 .setTitle ("Requested by " + author .getAsTag ())
7079 .build ();
7180 }
81+
82+ /**
83+ * Determines if a user is forbidden from sending a help-ping command due
84+ * to their status in the server.
85+ * @param reservation The channel reservation for the channel they're
86+ * trying to send the command in.
87+ * @param member The member.
88+ * @param config The guild config.
89+ * @return True if the user is forbidden from sending the command.
90+ */
91+ private boolean isHelpPingForbiddenForMember (ChannelReservation reservation , Member member , GuildConfig config ) {
92+ Set <Role > allowedRoles = Set .of (config .getModeration ().getStaffRole (), config .getHelp ().getHelperRole ());
93+ return !(
94+ reservation .getUserId () == member .getUser ().getIdLong () ||
95+ member .getRoles ().stream ().anyMatch (allowedRoles ::contains ) ||
96+ member .isOwner ()
97+ );
98+ }
99+
100+ /**
101+ * Determines if the user's timeout has elapsed (or doesn't exist), which
102+ * implies that it's fine for the user to send the command.
103+ * @param member The member.
104+ * @param config The guild config.
105+ * @return True if the user's timeout has elapsed or doesn't exist, or
106+ * false if the user should NOT send the command because of their timeout.
107+ */
108+ private boolean isHelpPingTimeoutElapsed (Member member , GuildConfig config ) {
109+ Long lastPing = lastPingTimes .get (member );
110+ return lastPing == null ||
111+ lastPing + config .getHelp ().getHelpPingTimeoutSeconds () * 1000L < System .currentTimeMillis ();
112+ }
113+
114+ /**
115+ * Method that cleans out any entries from the list of last ping times if
116+ * their timeout is no longer valid.
117+ */
118+ private void cleanTimeoutCache () {
119+ // Find the list of members whose last ping time was old enough that they should be removed from the cache.
120+ var membersToRemove = lastPingTimes .entrySet ().stream ().filter (entry -> {
121+ var config = Bot .config .get (entry .getKey ().getGuild ()).getHelp ();
122+ long timeoutMillis = config .getHelpPingTimeoutSeconds () * 1000L ;
123+ return entry .getValue () + timeoutMillis < System .currentTimeMillis ();
124+ }).map (Map .Entry ::getKey ).toList ();
125+ // Remove each member from the map.
126+ for (var member : membersToRemove ) {
127+ lastPingTimes .remove (member );
128+ }
129+ }
72130}
0 commit comments