All Projects → Discord4J → Discord4j

Discord4J / Discord4j

Licence: lgpl-3.0
Discord4J is a fast, powerful, unopinionated, reactive library to enable quick and easy development of Discord bots for Java, Kotlin, and other JVM languages using the official Discord Bot API.

Programming Languages

java
68154 projects - #9 most used programming language

Projects that are alternatives of or similar to Discord4j

Jda
Java wrapper for the popular chat & VOIP service: Discord https://discord.com
Stars: ✭ 2,598 (+167.01%)
Mutual labels:  api, rest-api, rest, bot-api, bot, discord, websocket
Javacord
An easy to use multithreaded library for creating Discord bots in Java.
Stars: ✭ 368 (-62.18%)
Mutual labels:  api, rest-api, rest, discord-api, bot, discord-bot, discord
Dsharpplus
A .NET Standard library for making bots using the Discord API.
Stars: ✭ 635 (-34.74%)
Mutual labels:  api, discord-api, bot, discord-bot, discord
Basicbot
A basic example of a Discord Bot written in Python. (discord.py)
Stars: ✭ 73 (-92.5%)
Mutual labels:  api, discord-api, bot, discord-bot, discord
Airdcpp Webclient
Communal peer-to-peer file sharing application for file servers/NAS devices
Stars: ✭ 106 (-89.11%)
Mutual labels:  api, rest-api, rest, websocket
Tiledesk Server
Tiledesk server. Tiledesk is an Open Source Live Chat platform written in NodeJs and MongoDB
Stars: ✭ 94 (-90.34%)
Mutual labels:  api, rest-api, rest, bot
Discord Bot
🤖 Our BIG help in things about moderation and many more useful stuff on our Discord server.
Stars: ✭ 30 (-96.92%)
Mutual labels:  api, bot, discord-bot, discord
Aegis.cpp
Discord C++ library for interfacing with the API. Join our server:
Stars: ✭ 198 (-79.65%)
Mutual labels:  api, discord-api, bot, discord
Discordeno
Discord API library for Deno
Stars: ✭ 254 (-73.9%)
Mutual labels:  api, discord-api, discord-bot, discord
Nostrum
Elixir Discord Library
Stars: ✭ 274 (-71.84%)
Mutual labels:  discord-api, bot, discord-bot, discord
Xiao
Xiao is a Discord bot coded in JavaScript with discord.js using the Commando command framework. With over 500 commands, she is one of the most feature-rich bots out there. Formerly XiaoBot.
Stars: ✭ 302 (-68.96%)
Mutual labels:  discord-api, bot, discord-bot, discord
Deku
Multi-purpose discord bot built with discord.js
Stars: ✭ 13 (-98.66%)
Mutual labels:  discord-api, bot, discord-bot, discord
Modmail
A feature rich discord Modmail bot
Stars: ✭ 957 (-1.64%)
Mutual labels:  discord-api, bot, discord-bot, discord
Poloniex Api Node
Poloniex API client for REST and WebSocket API
Stars: ✭ 138 (-85.82%)
Mutual labels:  api, rest-api, rest, websocket
Lenoxbot
🖥️ LenoxBot is a Discord bot that offers many cool new features to your Discord server!
Stars: ✭ 163 (-83.25%)
Mutual labels:  discord-api, bot, discord-bot, discord
Discord.js
discord.js is a powerful Node.js module that allows you to easily interact with the Discord API.
Stars: ✭ 16,432 (+1588.8%)
Mutual labels:  discord-api, bot, discord-bot, discord
Music Bot
Simple music bot with a full-blown queue system that is easy to understand
Stars: ✭ 102 (-89.52%)
Mutual labels:  discord-api, bot, discord-bot, discord
Discord Panel
📊 User friendly dashboard/tool for discord bot developpers to manage servers
Stars: ✭ 116 (-88.08%)
Mutual labels:  discord-api, bot, discord-bot, discord
Disgord
Go module for interacting with the documented Discord's bot interface; Gateway, REST requests and voice
Stars: ✭ 277 (-71.53%)
Mutual labels:  api, discord-api, bot, discord
Commando
Official command framework for discord.js
Stars: ✭ 434 (-55.4%)
Mutual labels:  discord-api, bot, discord-bot, discord

Discord4J

Support Server Invite Maven Central Javadocs GitHub Workflow Status (branch)

Discord4J is a fast, powerful, unopinionated, reactive library to enable quick and easy development of Discord bots for Java, Kotlin, and other JVM languages using the official Discord Bot API.

🏃 Quick Example

In this example for v3.1, whenever a user sends a !ping message the bot will immediately respond with Pong!.

public final class ExampleBot {

  public static void main(final String[] args) {
    final String token = args[0];
    final DiscordClient client = DiscordClient.create(token);
    final GatewayDiscordClient gateway = client.login().block();

    gateway.on(MessageCreateEvent.class).subscribe(event -> {
      final Message message = event.getMessage();
      if ("!ping".equals(message.getContent())) {
        final MessageChannel channel = message.getChannel().block();
        channel.createMessage("Pong!").block();
      }
    });

    gateway.onDisconnect().block();
  }
}

🔗 Quick Links

💎 Benefits

  • 🚀 Reactive - Discord4J follows the reactive-streams protocol to ensure Discord bots run smoothly and efficiently regardless of size.

  • 📜 Official - Automatic rate limiting, automatic reconnection strategies, and consistent naming conventions are among the many features Discord4J offers to ensure your Discord bots run up to Discord's specifications and to provide the least amount of surprises when interacting with our library.

  • 🛠️ Modular - Discord4J breaks itself into modules to allow advanced users to interact with our API at lower levels to build minimal and fast runtimes or even add their own abstractions.

  • ⚔️ Powerful - Discord4J can be used to develop any bot, big or small. We offer many tools for developing large-scale bots from custom distribution frameworks, off-heap caching, and its interaction with Reactor allows complete integration with frameworks such as Spring and Micronaut.

  • 🏫 Community - We pride ourselves on our inclusive community and are willing to help whenever challenges arise; or if you just want to chat! We offer help ranging from Discord4J specific problems, to general programming and web development help, and even Reactor-specific questions. Be sure to visit us on our Discord server!

📦 Installation

Gradle

repositories {
  mavenCentral()
}

dependencies {
  implementation 'com.discord4j:discord4j-core:3.1.3'
}

Gradle Kotlin DSL

repositories {
  mavenCentral()
}

dependencies {
  implementation("com.discord4j:discord4j-core:3.1.3")
}

Maven

<dependencies>
  <dependency>
    <groupId>com.discord4j</groupId>
    <artifactId>discord4j-core</artifactId>
    <version>3.1.3</version>
  </dependency>
</dependencies>

SBT

libraryDependencies ++= Seq(
  "com.discord4j" % "discord4j-core" % "3.1.3"
)

🔀 Discord4J Versions

Discord4J 3.1.x introduces performance and API enhancements, a plethora of new features, and dependency upgrades. A Migration Guide is provided to aide users and ensure a smooth and readily available transition.

Discord4J Support Gateway/API Intents
v3.2.x In development v8 Mandatory, non-privileged default
v3.1.x Current v6 Optional, no intent default
v3.0.x Maintenance only v6 No intents support

See this wiki page for more details about compatibility.

🎉 Sponsors

We would like to give a special thanks to all of our sponsors for providing us the funding to continue developing and hosting repository resources as well as driving forward initiatives for community programs. In particular, we would like to give a special shoutout to these wonderful individuals:

⛰️ Large Bots

Here are some real-world examples of large bots using Discord4J:

  • Groovy - An easy to use music bot used by more than 4 million servers.
  • ZeroTwo - An anime multi-purpose bot used in more than 250K servers.
  • DisCal - Implements Google Calendar into Discord as seamlessly and comprehensively as possible; serving nearly 14K servers.
  • Shadbot - A configurable multipurpose bot with music, gambling mini-games, video game stats, and more; serving almost 12K servers.

Do you own a large bot using Discord4J? Ask an admin in our Discord or submit a pull request to add your bot to the list!

⚛️ Reactive

Discord4J uses Project Reactor as the foundation for our asynchronous framework. Reactor provides a simple yet extremely powerful API that enables users to reduce resources and increase performance.

public final class ExampleBot {

  public static void main(final String[] args) {
    final String token = args[0];
    final DiscordClient client = DiscordClient.create(token);

    client.login().flatMapMany(gateway -> gateway.on(MessageCreateEvent.class))
      .map(MessageCreateEvent::getMessage)
      .filter(message -> "!ping".equals(message.getContent()))
      .flatMap(Message::getChannel)
      .flatMap(channel -> channel.createMessage("Pong!"))
      .blockLast();
  }
}

Discord4J also provides several methods to aide in better reactive chain compositions, such as GatewayDiscordClient#withGateway and EventDispatcher#on with an error handling overload.

final String token = args[0];
final DiscordClient client = DiscordClient.create(token);

client.withGateway(gateway -> {
  final Publisher<?> pingPong = gateway.on(MessageCreateEvent.class, event ->
    Mono.just(event.getMessage())
      .filter(message -> "!ping".equals(message.getContent()))
      .flatMap(Message::getChannel)
      .flatMap(channel -> channel.createMessage("Pong!")));
            
    final Publisher<?> onDisconnect = gateway.onDisconnect()
      .doOnTerminate(() -> System.out.println("Disconnected!"));

    return Mono.when(pingPong, onDisconnect);
  }).block();

🧵 Kotlin

By utilizing Reactor, Discord4J has native integration with Kotlin coroutines when paired with the kotlinx-coroutines-reactor library.

val token = args[0]
val client = DiscordClient.create(token)

client.withGateway {
  mono {
    it.on(MessageCreateEvent::class.java)
      .asFlow()
      .collect {
        val message = it.message
        if (message.content == "!ping") {
          val channel = message.channel.awaitSingle()
          channel.createMessage("Pong!").awaitSingle()
        }
      }
  }
}
.block()

📚 Examples

📑 Message Embeds

// IMAGE_URL = https://cdn.betterttv.net/emote/55028cd2135896936880fdd7/3x
// ANY_URL = https://www.youtube.com/watch?v=5zwY50-necw
final MessageChannel channel = ...
channel.createEmbed(spec -> 
  spec.setColor(Color.RED)
    .setAuthor("setAuthor", ANY_URL, IMAGE_URL)
    .setImage(IMAGE_URL)
    .setTitle("setTitle/setUrl")
    .setUrl(ANY_URL)
    .setDescription("setDescription\n" +
      "big D: is setImage\n" +
      "small D: is setThumbnail\n" +
      "<-- setColor")
    .addField("addField", "inline = true", true)
    .addField("addFIeld", "inline = true", true)
    .addField("addFile", "inline = false", false)
    .setThumbnail(IMAGE_URL)
    .setFooter("setFooter --> setTimestamp", IMAGE_URL)
    .setTimestamp(Instant.now())
).block();

🏷️ Find Members by Role Name

Users typically prefer working with names instead of IDs. This example will demonstrate how to search for all members that have a role with a specific name.

final Guild guild = ...
final Set<Member> roleMembers = new HashSet<>();

for (final Member member : guild.getMembers().toIterable()) {
  for (final Role role : member.getRoles().toIterable()) {
    if ("Developers".equalsIgnoreCase(role.getName())) {
      roleMembers.add(member);
      break;
    }
  }
}

return roleMembers;

Alternatively, using Reactor:

final Guild guild = ...
return guild.getMembers()
  .filterWhen(member -> member.getRoles()
    .map(Role::getName)
    .any("Developers"::equalsIgnoreCase));

🎵 Voice and Music

Discord4J provides full support for voice connections and the ability to send audio to other users connected to the same channel. Discord4J can accept any Opus audio source with LavaPlayer being the preferred solution for downloading and encoding audio from YouTube, SoundCloud, and other providers.

To get started, you will first need to instantiate and configure an, conventionally global, AudioPlayerManager.

public static final AudioPlayerManager PLAYER_MANAGER;

static {
  PLAYER_MANAGER = new DefaultAudioPlayerManager();
  // This is an optimization strategy that Discord4J can utilize to minimize allocations
  PLAYER_MANAGER.getConfiguration().setFrameBufferFactory(NonAllocatingAudioFrameBuffer::new);
  AudioSourceManagers.registerRemoteSources(PLAYER_MANAGER);
  AudioSourceManagers.registerLocalSource(PLAYER_MANAGER);
}

Next, we need to allow Discord4J to read from an AudioPlayer to an AudioProvider.

public final class LavaPlayerAudioProvider extends AudioProvider {

  private final AudioPlayer player;
  private final MutableAudioFrame frame;

  public LavaPlayerAudioProvider(final AudioPlayer player) {
    // Allocate a ByteBuffer for Discord4J's AudioProvider to hold audio data for Discord
    super(ByteBuffer.allocate(StandardAudioDataFormats.DISCORD_OPUS.maximumChunkSize()));
    // Set LavaPlayer's AudioFrame to use the same buffer as Discord4J's
    frame = new MutableAudioFrame();
    frame.setBuffer(getBuffer());
    this.player = player;
  }

  @Override
  public boolean provide() {
    // AudioPlayer writes audio data to the AudioFrame
    final boolean didProvide = player.provide(frame);

    if (didProvide) {
      getBuffer().flip();
    }

    return didProvide;
  }
}

Typically, audio players will have queues or internal playlists for users to be able to automatically cycle through songs as they are finished or requested to be skipped over. We can manage this queue externally and pass it to other areas of our code to allow tracks to be viewed, queued, or skipped over by creating an AudioTrackScheduler.

public final class AudioTrackScheduler extends AudioEventAdapter {

  private final List<AudioTrack> queue;
  private final AudioPlayer player;

  public AudioTrackScheduler(final AudioPlayer player) {
    // The queue may be modifed by different threads so guarantee memory safety
    // This does not, however, remove several race conditions currently present
    queue = Collections.synchronizedList(new LinkedList<>());
    this.player = player;
  }

  public List<AudioTrack> getQueue() {
    return queue;
  }

  public boolean play(final AudioTrack track) {
    return play(track, false);
  }

  public boolean play(final AudioTrack track, final boolean force) {
    final boolean playing = player.startTrack(track, !force);

    if (!playing) {
      queue.add(track);
    }

    return playing;
  }

  public boolean skip() {
    return !queue.isEmpty() && play(queue.remove(0), true);
  }

  @Override
  public void onTrackEnd(final AudioPlayer player, final AudioTrack track, final AudioTrackEndReason endReason) {
    // Advance the player if the track completed naturally (FINISHED) or if the track cannot play (LOAD_FAILED)
    if (endReason.mayStartNext) {
      skip();
    }
  }
}

Currently, Discord only allows 1 voice connection per server. Working within this limitation, it is logical to think of the 3 components we have worked with thus far (AudioPlayer, LavaPlayerAudioProvider, and AudioTrackScheduler) to be correlated to a specific Guild, naturally unique by some Snowflake. Logically, it makes sense to combine these objects into one, so that they can be put into a Map for easier retrieval when connecting to a voice channel or when working with commands.

public final class GuildAudioManager {

  private static final Map<Snowflake, GuildAudioManager> MANAGERS = new ConcurrentHashMap<>();

  public static GuildAudioManager of(final Snowflake id) {
    return MANAGERS.computeIfAbsent(id, ignored -> new GuildAudioManager());
  }

  private final AudioPlayer player;
  private final AudioTrackScheduler scheduler;
  private final LavaPlayerAudioProvider provider;

  private GuildAudioManager() {
    player = PLAYER_MANAGER.createPlayer();
    scheduler = new AudioTrackScheduler(player);
    provider = new LavaPlayerAudioProvider(player);

    player.addListener(scheduler);
  }

  // getters
}

Finally, we need to connect to the voice channel. After connecting you are given a VoiceConnection object where you can utilize it later to disconnect from the voice channel by calling VoiceConnection#disconnect.

final VoiceChannel channel = ...
final GuildAudioManager manager = GuildAudioManager.of(channel.getGuildId());
final AudioProvider provider = manager.getProvider();
final VoiceConnection connection = channel.join(spec -> spec.setProvider(provider)).block();

// In the AudioLoadResultHandler, add AudioTrack instances to the AudioTrackScheduler (and send notifications to users)
PLAYER_MANAGER.loadItemOrdered(manager, "https://www.youtube.com/watch?v=dQw4w9WgXcQ", new AudioLoadResultHandler() { /* overrides */ })

❌ Disconnecting from a Voice Channel Automatically

Typically, after everyone has left a voice channel, the bot should disconnect automatically as users typically forget to disconnect the bot manually. This problem can be solved rather elegantly using a reactive approach over an imperative one as the example below demonstrates.

final VoiceChannel channel = ...
final Mono<Void> onDisconnect = channel.join(spec -> { /* TODO Initialize */ })
  .flatMap(connection -> {
    // The bot itself has a VoiceState; 1 VoiceState signals bot is alone
    final Publisher<Boolean> voiceStateCounter = channel.getVoiceStates()
      .count()
      .map(count -> 1L == count);

    // After 10 seconds, check if the bot is alone. This is useful if
    // the bot joined alone, but no one else joined since connecting
    final Mono<Void> onDelay = Mono.delay(Duration.ofSeconds(10L))
      .filterWhen(ignored -> voiceStateCounter)
      .switchIfEmpty(Mono.never())
      .then();

    // As people join and leave `channel`, check if the bot is alone.
    // Note the first filter is not strictly necessary, but it does prevent many unnecessary cache calls
    final Mono<Void> onEvent = channel.getClient().getEventDispatcher().on(VoiceStateUpdateEvent.class)
      .filter(event -> event.getOld().flatMap(VoiceState::getChannelId).map(channel.getId()::equals).orElse(false))
      .filterWhen(ignored -> voiceStateCounter)
      .next()
      .then();

    // Disconnect the bot if either onDelay or onEvent are completed!
    return Mono.first(onDelay, onEvent).then(connection.disconnect());
  });
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].