/
SlashBotExample.java
196 lines (175 loc) · 8.57 KB
/
SlashBotExample.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
* Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.ButtonClickEvent;
import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.components.Button;
import net.dv8tion.jda.api.requests.GatewayIntent;
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
import javax.security.auth.login.LoginException;
import java.util.EnumSet;
import static net.dv8tion.jda.api.interactions.commands.OptionType.*;
public class SlashBotExample extends ListenerAdapter
{
public static void main(String[] args) throws LoginException
{
JDA jda = JDABuilder.createLight("BOT_TOKEN_HERE", EnumSet.noneOf(GatewayIntent.class)) // slash commands don't need any intents
.addEventListeners(new SlashBotExample())
.build();
// These commands take up to an hour to be activated after creation/update/delete
CommandListUpdateAction commands = jda.updateCommands();
// Moderation commands with required options
commands.addCommands(
new CommandData("ban", "Ban a user from this server. Requires permission to ban users.")
.addOptions(new OptionData(USER, "user", "The user to ban") // USER type allows to include members of the server or other users by id
.setRequired(true)) // This command requires a parameter
.addOptions(new OptionData(INTEGER, "del_days", "Delete messages from the past days.")) // This is optional
);
// Simple reply commands
commands.addCommands(
new CommandData("say", "Makes the bot say what you tell it to")
.addOptions(new OptionData(STRING, "content", "What the bot should say")
.setRequired(true))
);
// Commands without any inputs
commands.addCommands(
new CommandData("leave", "Make the bot leave the server")
);
commands.addCommands(
new CommandData("prune", "Prune messages from this channel")
.addOptions(new OptionData(INTEGER, "amount", "How many messages to prune (Default 100)"))
);
// Send the new set of commands to discord, this will override any existing global commands with the new set provided here
commands.queue();
}
@Override
public void onSlashCommand(SlashCommandEvent event)
{
// Only accept commands from guilds
if (event.getGuild() == null)
return;
switch (event.getName())
{
case "ban":
Member member = event.getOption("user").getAsMember(); // the "user" option is required so it doesn't need a null-check here
User user = event.getOption("user").getAsUser();
ban(event, user, member);
break;
case "say":
say(event, event.getOption("content").getAsString()); // content is required so no null-check here
break;
case "leave":
leave(event);
break;
case "prune": // 2 stage command with a button prompt
prune(event);
break;
default:
event.reply("I can't handle that command right now :(").setEphemeral(true).queue();
}
}
@Override
public void onButtonClick(ButtonClickEvent event)
{
// users can spoof this id so be careful what you do with this
String[] id = event.getComponentId().split(":"); // this is the custom id we specified in our button
String authorId = id[0];
String type = id[1];
// When storing state like this is it is highly recommended to do some kind of verification that it was generated by you, for instance a signature or local cache
if (!authorId.equals(event.getUser().getId()))
return;
event.deferEdit().queue(); // acknowledge the button was clicked, otherwise the interaction will fail
MessageChannel channel = event.getChannel();
switch (type)
{
case "prune":
int amount = Integer.parseInt(id[2]);
event.getChannel().getIterableHistory()
.skipTo(event.getMessageIdLong())
.takeAsync(amount)
.thenAccept(channel::purgeMessages);
// fallthrough delete the prompt message with our buttons
case "delete":
event.getHook().deleteOriginal().queue();
}
}
public void ban(SlashCommandEvent event, User user, Member member)
{
event.deferReply(true).queue(); // Let the user know we received the command before doing anything else
InteractionHook hook = event.getHook(); // This is a special webhook that allows you to send messages without having permissions in the channel and also allows ephemeral messages
hook.setEphemeral(true); // All messages here will now be ephemeral implicitly
if (!event.getMember().hasPermission(Permission.BAN_MEMBERS))
{
hook.sendMessage("You do not have the required permissions to ban users from this server.").queue();
return;
}
Member selfMember = event.getGuild().getSelfMember();
if (!selfMember.hasPermission(Permission.BAN_MEMBERS))
{
hook.sendMessage("I don't have the required permissions to ban users from this server.").queue();
return;
}
if (member != null && !selfMember.canInteract(member))
{
hook.sendMessage("This user is too powerful for me to ban.").queue();
return;
}
int delDays = 0;
OptionMapping option = event.getOption("del_days");
if (option != null) // null = not provided
delDays = (int) Math.max(0, Math.min(7, option.getAsLong()));
// Ban the user and send a success response
event.getGuild().ban(user, delDays)
.flatMap(v -> hook.sendMessage("Banned user " + user.getAsTag()))
.queue();
}
public void say(SlashCommandEvent event, String content)
{
event.reply(content).queue(); // This requires no permissions!
}
public void leave(SlashCommandEvent event)
{
if (!event.getMember().hasPermission(Permission.KICK_MEMBERS))
event.reply("You do not have permissions to kick me.").setEphemeral(true).queue();
else
event.reply("Leaving the server... :wave:") // Yep we received it
.flatMap(v -> event.getGuild().leave()) // Leave server after acknowledging the command
.queue();
}
public void prune(SlashCommandEvent event)
{
OptionMapping amountOption = event.getOption("amount"); // This is configured to be optional so check for null
int amount = amountOption == null
? 100 // default 100
: (int) Math.min(200, Math.max(2, amountOption.getAsLong())); // enforcement: must be between 2-200
String userId = event.getUser().getId();
event.reply("This will delete " + amount + " messages.\nAre you sure?") // prompt the user with a button menu
.addActionRow(// this means "<style>(<id>, <label>)" the id can be spoofed by the user so setup some kinda verification system
Button.secondary(userId + ":delete", "Nevermind!"),
Button.danger(userId + ":prune:" + amount, "Yes!")) // the first parameter is the component id we use in onButtonClick above
.queue();
}
}