|
24 | 24 | import net.dv8tion.jda.api.entities.channel.Channel; |
25 | 25 | import net.dv8tion.jda.api.entities.channel.ChannelType; |
26 | 26 | import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; |
| 27 | +import net.dv8tion.jda.api.exceptions.FirstAckException; |
27 | 28 | import net.dv8tion.jda.api.interactions.DiscordLocale; |
28 | 29 | import net.dv8tion.jda.api.interactions.IntegrationOwners; |
29 | 30 | import net.dv8tion.jda.api.interactions.Interaction; |
|
40 | 41 | import javax.annotation.Nonnull; |
41 | 42 | import javax.annotation.Nullable; |
42 | 43 | import java.util.List; |
| 44 | +import java.util.concurrent.ScheduledFuture; |
| 45 | +import java.util.concurrent.TimeUnit; |
43 | 46 |
|
44 | 47 | public class InteractionImpl implements Interaction |
45 | 48 | { |
| 49 | + // Enables recording where the first ack is done, |
| 50 | + // to help with debugging when an interaction is acknowledged twice |
| 51 | + private static boolean recordAckTraces = false; |
| 52 | + private static ScheduledFuture<?> scheduledRecordDeactivation; |
| 53 | + private Throwable firstAckTrace = null; |
| 54 | + |
46 | 55 | protected final long id; |
47 | 56 | protected final long channelId; |
48 | 57 | protected final int type; |
@@ -139,11 +148,41 @@ else if (guild instanceof DetachedGuildImpl) |
139 | 148 | public synchronized void releaseHook(boolean success) {} |
140 | 149 |
|
141 | 150 | // Ensures that one cannot acknowledge an interaction twice |
142 | | - public synchronized boolean ack() |
| 151 | + @Nullable |
| 152 | + public synchronized IllegalStateException tryAck() |
143 | 153 | { |
144 | | - boolean wasAck = isAck; |
145 | | - this.isAck = true; |
146 | | - return wasAck; |
| 154 | + // If not already acknowledged => no exception |
| 155 | + if (!isAck) |
| 156 | + { |
| 157 | + // Store where the first ack was made, so we can use show it on the 2nd ack |
| 158 | + if (recordAckTraces) |
| 159 | + firstAckTrace = new FirstAckException(); |
| 160 | + isAck = true; |
| 161 | + return null; |
| 162 | + } |
| 163 | + |
| 164 | + // Enable saving stack traces of acknowledgements. |
| 165 | + // On future acknowledgements, the stack trace of the first ack will be kept, and added to the second ack, |
| 166 | + // so the user doesn't have to figure out where the first ack was at. |
| 167 | + if (firstAckTrace == null) |
| 168 | + { |
| 169 | + recordAckTraces = true; |
| 170 | + |
| 171 | + // Stop recording after 15 minutes if the issue was not reproduced |
| 172 | + if (scheduledRecordDeactivation != null) |
| 173 | + scheduledRecordDeactivation.cancel(false); |
| 174 | + scheduledRecordDeactivation = api.getGatewayPool().schedule(() -> |
| 175 | + { |
| 176 | + recordAckTraces = false; |
| 177 | + }, 15, TimeUnit.MINUTES); |
| 178 | + return new IllegalStateException("This interaction has already been acknowledged or replied to. You can only reply or acknowledge an interaction once! Retry using this interaction for more details."); |
| 179 | + } |
| 180 | + else |
| 181 | + { |
| 182 | + recordAckTraces = false; |
| 183 | + scheduledRecordDeactivation.cancel(false); |
| 184 | + return new IllegalStateException("This interaction has already been acknowledged or replied to. You can only reply or acknowledge an interaction once!", firstAckTrace); |
| 185 | + } |
147 | 186 | } |
148 | 187 |
|
149 | 188 | @Override |
|
0 commit comments