Skip to content

Commit

Permalink
Merge pull request #1657 from DV8FromTheWorld/release-4.3.0
Browse files Browse the repository at this point in the history
Release 4.3.0
  • Loading branch information
MinnDevelopment authored Jun 11, 2021
2 parents fa8a40a + cd1134a commit 24aed48
Show file tree
Hide file tree
Showing 191 changed files with 18,157 additions and 1,793 deletions.
87 changes: 51 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public class MessageListener extends ListenerAdapter
//You can also add event listeners to the already built JDA instance
// Note that some events may not be received if the listener is added after calling build()
// This includes events such as the ReadyEvent
jda.addEventListeners(new MessageListener());
jda.addEventListener(new MessageListener());
}

@Override
Expand Down Expand Up @@ -229,6 +229,40 @@ public class Bot extends ListenerAdapter
}
```

**Slash-Commands**:

```java
public class Bot extends ListenerAdapter
{
public static void main(String[] args) throws LoginException
{
if (args.length < 1) {
System.out.println("You have to provide a token as first argument!");
System.exit(1);
}
// args[0] should be the token
// We don't need any intents for this bot. Slash commands work without any intents!
JDA jda = JDABuilder.createLight(args[0], Collections.emptyList())
.addEventListeners(new Bot())
.setActivity(Activity.playing("Type /ping"))
.build();

jda.upsertCommand("ping", "Calculate ping of the bot").queue(); // This can take up to 1 hour to show up in the client
}

@Override
public void onSlashCommand(SlashCommandEvent event)
{
if (!event.getName().equals("ping")) return; // make sure we handle the right command
long time = System.currentTimeMillis();
event.reply("Pong!").setEphemeral(true) // reply or acknowledge
.flatMap(v ->
event.getHook().editOriginalFormat("Pong: %d ms", System.currentTimeMillis() - time) // then edit original
).queue(); // Queue both reply and edit
}
}
```

### RestAction

Through [RestAction](https://ci.dv8tion.net/job/JDA/javadoc/net/dv8tion/jda/api/requests/RestAction.html) we provide request handling with
Expand Down Expand Up @@ -429,7 +463,8 @@ Be sure to replace the **VERSION** key below with the one of the versions shown
**Gradle**
```gradle
dependencies {
compile 'net.dv8tion:JDA:VERSION'
//Change 'implementation' to 'compile' in old Gradle versions
implementation("net.dv8tion:JDA:VERSION")
}
repositories {
Expand All @@ -444,7 +479,8 @@ repositories {
**Gradle without Audio**
```gradle
dependencies {
compile ('net.dv8tion:JDA:VERSION') {
//Change 'implementation' to 'compile' in old Gradle versions
implementation("net.dv8tion:JDA:VERSION") {
exclude module: 'opus-java'
}
}
Expand Down Expand Up @@ -539,28 +575,6 @@ as it is easier.

[Lavalink-Client](https://github.com/FredBoat/Lavalink-Client) is the official Lavalink client for JDA.

### [JDA-Utilities](https://github.com/JDA-Applications/JDA-Utilities)

Created and maintained by [jagrosh](https://github.com/jagrosh).
<br>JDA-Utilities provides a Command-Extension and several utilities to make using JDA very simple.

Features include:
- Paginated Message using Reactions
- EventWaiter allowing to wait for a response and other events


<!--
TODO: Ensure this is compatible with version 4
### [JDAction](https://github.com/sedmelluq/jdaction)
Created and maintained by [sedmelluq](https://github.com/sedmelluq)
<br>JDAction is a [Gradle](https://gradle.org/) plugin which makes sure that the return values of all methods which return a RestAction are used.
Since it is a common mistake to forget to `.queue()`/`.complete()`/`.submit()` RestActions,
and it is often only discovered after noticing that something doesn't work,
this plugin will help catch those cases quickly as it will cause a build failure in such case.
More info about RestAction: [Wiki](https://github.com/DV8FromTheWorld/JDA/wiki/7\)-Using-RestAction)
-->

### [jda-nas](https://github.com/sedmelluq/jda-nas)

Expand All @@ -575,27 +589,28 @@ JDABuilder builder = JDABuilder.createDefault(BOT_TOKEN)
.setAudioSendFactory(new NativeAudioSendFactory());
```

### [jda-reactor](https://github.com/MinnDevelopment/jda-reactor)
### [jda-ktx](https://github.com/MinnDevelopment/jda-ktx)

Created and maintained by [MinnDevelopment](https://github.com/MinnDevelopment).
<br>Provides [Kotlin](https://kotlinlang.org/) extensions for **RestAction** and events that provide a [reactive](http://reactivex.io/intro.html) alternative to common JDA interfaces.
<br>Provides [Kotlin](https://kotlinlang.org/) extensions for **RestAction** and events that provide a more idiomatic Kotlin experience.

```kotlin
fun main() {
val manager = ReactiveEventManager()
manager.on<ReadyEvent>()
.subscribe { println("Ready to serve!") }
manager.on<MessageReceivedEvent>()
.filter { it.message.contentRaw == "!ping" }
.subscribe { it.channel.sendMessage("Pong!").queue() }

val jda = JDABuilder.createDefault(BOT_TOKEN)
.setEventManager(manager)
.injectKTX()
.build()

jda.onCommand("ping") { event ->
val time = measureTime {
event.reply("Pong!").await()
}.inWholeMilliseconds

event.hook.editOriginal("Pong: $time ms").queue()
}
}
```

An example bot for this can be found at [Reactive JDA Bot](https://github.com/MinnDevelopment/reactive-jda-bot).
There is a number of examples available in the [README](https://github.com/MinnDevelopment/jda-ktx/#jda-ktx).

------

Expand Down
8 changes: 5 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ plugins {
id("com.github.johnrengelman.shadow") version "5.1.0"
}

val versionObj = Version(major = "4", minor = "2", revision = "1")
val versionObj = Version(major = "4", minor = "3", revision = "0")

project.group = "net.dv8tion"
project.version = "$versionObj"
Expand Down Expand Up @@ -91,8 +91,10 @@ dependencies {
implementation("com.fasterxml.jackson.core:jackson-databind:2.10.1")

//Sets the dependencies for the examples
configurations.asMap["examplesCompile"] = configurations["apiElements"]
configurations.asMap["examplesRuntime"] = configurations["implementation"]
configurations["examplesImplementation"].withDependencies {
addAll(configurations["api"].allDependencies)
addAll(configurations["implementation"].allDependencies)
}

testImplementation("org.junit.jupiter:junit-jupiter:5.4.0")
}
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
196 changes: 196 additions & 0 deletions src/examples/java/SlashBotExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,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();
}
}
14 changes: 1 addition & 13 deletions src/main/java/net/dv8tion/jda/api/AccountType.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,12 @@

package net.dv8tion.jda.api;

import net.dv8tion.jda.annotations.DeprecatedSince;
import net.dv8tion.jda.annotations.ForRemoval;

/**
* Represents the type of account that is logged in.
* <br>Used to differentiate between Bots and Client accounts.
*/
public enum AccountType
{
/** An OAuth2 Bot which was created by an application */
BOT,
/**
* A User-Account which can be used via the official Discord Client
*
* @deprecated This will be removed in a future version
*/
@ForRemoval
@Deprecated
@DeprecatedSince("4.2.0")
CLIENT
BOT
}
Loading

0 comments on commit 24aed48

Please sign in to comment.