Coder Social home page Coder Social logo

discord4j / discord4j Goto Github PK

View Code? Open in Web Editor NEW
1.7K 42.0 264.0 22.88 MB

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.

Home Page: https://discord4j.com

License: GNU Lesser General Public License v3.0

Java 100.00%
discord bot rest-api rest websocket javadoc bot-api discord4j d4j java

discord4j's Introduction

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.2, whenever a user sends a !ping message the bot will immediately respond with Pong!.

Make sure your bot has the Message Content intent enabled in your developer portal.

public class ExampleBot {

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

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

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

For a full project example, check out our example projects repository here.

๐Ÿ”— 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.2.5'
}

Gradle Kotlin DSL

repositories {
  mavenCentral()
}

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

Maven

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

SBT

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

๐Ÿ”€ Discord4J Versions

Discord4J 3.2.x includes simpler and more powerful APIs to build requests, a new entity cache and performance improvements from dependency upgrades. Check our Migration Guide for more details.

Discord4J Support Gateway/API Intents Interactions
v3.3.x In development v9 Mandatory, non-privileged default Fully supported
v3.2.x Current v8 Mandatory, non-privileged default Fully supported
v3.1.x Maintenance only v6 Optional, no intent default Maintenance only

See our docs 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 - Was the second-largest bot on Discord, serving music to over 4 million servers before its shutdown in August 2021.
  • ZeroTwo - An anime multi-purpose bot used in over 1 million servers.
  • DisCal - Implements Google Calendar into Discord as seamlessly and comprehensively as possible; serving over 21k servers.
  • Shadbot - A configurable multipurpose bot with music, gambling mini-games, video game stats, and more; serving nearly 12K servers before its shutdown in August 2021.

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 class ExampleBot {

  public static void main(String[] args) {
    String token = args[0];
    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 aid in better reactive chain compositions, such as GatewayDiscordClient#withGateway and EventDispatcher#on with an error handling overload.

public class ExampleBot {

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

        client.withGateway(gateway -> {
            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!")));

            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
MessageChannel channel = ...
EmbedCreateSpec.Builder builder = EmbedCreateSpec.builder();
builder.author("setAuthor", ANY_URL, IMAGE_URL);
builder.image(IMAGE_URL);
builder.title("setTitle/setUrl");
builder.url(ANY_URL);
builder.description("setDescription\n" +
      "big D: is setImage\n" +
      "small D: is setThumbnail\n" +
      "<-- setColor");
builder.addField("addField", "inline = true", true);
builder.addField("addFIeld", "inline = true", true);
builder.addField("addFile", "inline = false", false);
builder.thumbnail(IMAGE_URL);
builder.footer("setFooter --> setTimestamp", IMAGE_URL);
builder.timestamp(Instant.now());
channel.createMessage(builder.build()).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.

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

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

return roleMembers;

Alternatively, using Reactor:

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 class LavaPlayerAudioProvider extends AudioProvider {

  private final AudioPlayer player;
  private final MutableAudioFrame frame;

  public LavaPlayerAudioProvider(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
    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 class AudioTrackScheduler extends AudioEventAdapter {

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

  public AudioTrackScheduler(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(AudioTrack track) {
    return play(track, false);
  }

  public boolean play(AudioTrack track, boolean force) {
    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(AudioPlayer player, AudioTrack track, 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 class GuildAudioManager {

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

  public static GuildAudioManager of(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.

VoiceChannel channel = ...
AudioProvider provider = GuildAudioManager.of(channel.getGuildId()).getProvider();
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.loadItem("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.

VoiceChannel channel = ...
Mono<Void> onDisconnect = channel.join(spec -> { /* TODO Initialize */ })
  .flatMap(connection -> {
    // The bot itself has a VoiceState; 1 VoiceState signals bot is alone
    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
    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
    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());
  });

discord4j's People

Contributors

alex1304 avatar arsenarsen avatar austinv11 avatar azn9 avatar bytealex avatar chrislo27 avatar d-g-n avatar danthonywalker avatar darichey avatar doc94 avatar grandpanda avatar gregory-widmer avatar j0rdanit0 avatar jamesgallicchio avatar kashike avatar krakenied avatar nerd avatar nija123098 avatar novafox161 avatar phantamanta44 avatar quanticc avatar randomname8 avatar schnapster avatar shadorc avatar sizableshrimp avatar skykatik avatar theiglooo avatar undermybrella avatar winteryfox avatar xaanit avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

discord4j's Issues

onReady event getGuild size is inconsistant

Prerequisites

Description

getting amount of Guilds a bot is connected with within onReadyEvent is inconsistent and often returns a 0 even if the bot has been confirmed to be connected to a Guild.

Steps to Reproduce

  1. Make sure that the bot is connected to a Guild and shows up in the User list.
  2. Print out event.getClient().getGuilds().size();
  3. Run Bot, Repeat about 10 times

...

Expected behavior: Consistantly return the amount of Guilds the bot is connected to

Actual behavior: bot is connected to 2 Guild's, performing the steps above can return 0 on many occasions and 1 on some, never 2.

Stacktrace (if applicable): N/a

Version affected: dev-SNAPSHOT (31aef0e)

Additional Information

Server 1:
image
Server 2:
image

Discord4J loses Websocket connection over time, especially on more idle servers

Prerequisites

Description

Discord4J intermittently loses connection to the Discord websocket with an "UNKNOWN" reason. It does not attempt reconnection but simply terminates itself.

Steps to Reproduce

  1. Connect Discord4J to the Discord API, ideally in a generally idle server.
  2. Wait for the timeout.

Expected behavior: Discord4J should be able to maintain a Websocket connection over a long period of time in accordance with heartbeats as defined in the Discord API documentation.
Actual behavior: Discord4J, especially on idle servers, cannot maintain a Websocket connection over a long period of time (a few hours).

Stacktrace (if applicable): http://pastebin.com/z1eGtJFr

Version affected: 2.4.7

404 NPE on attempting to edit own role

Prerequisites

Description

Attempting to edit a role the client has results in a NullPointerException (and 404 message) instead of a DiscordException or MissingPermissionsException.

Steps to Reproduce

  1. Edit a Role (through changePermissions or another changeX method).
  2. A 404 error message will appear, along with a NullPointerException pointing to DiscordUtils.java:437.

Expected behavior: A different exception, such as DiscordException or MissingPermissionsException should be thrown.

Actual behavior: An unchecked NullPointerException was thrown instead.

Stacktrace: http://hastebin.com/emobebibox.avrasm

Version affected: dev, commit 7934555, potentially earlier

Additional Information

I'm sorry for breaking D4J when you were away

Queued audio files are locked indefinitely

Prerequisites

Description

Queuing a File for an AudioChannel will lock the referenced file indefinitely due to the used *InputStreams never being closed. Manually creating the stream and closing it after queuing (which obviously will make it unable to play) will unlock the files.

Steps to Reproduce

  1. Use AudioChannel#queueFile() to queue an audio file
  2. Let the file play till the end
  3. Leave the bot process running and try to delete the audio file you just queued. Either through the system file explorer or Java.

...

Expected behavior: File can be deleted

Actual behavior: File can't be deleted. Files#delete() will throw an IOException saying the file is in use by another process.

Stacktrace (if applicable): None, no Discord4J exceptions here

Version affected: I tested on 2.4.7

Additional Information

I assume the used stream(s) could/should be closed after getAudioData() is done with it.

Cannot login using ClientBuilder

Prerequisites

Description

On attempting to login with following code

ClientBuilder builder = new ClientBuilder();
builder.withToken("mytoken");
IDiscordClient client = builder.login();

where mytoken is my account token it gives me a java.lang.NoSuchFieldError on version 2.4.0

Steps to Reproduce

  1. Run above code using 2.4.0 in a new project

...

Expected behavior:
Login and allow me to access the client

Actual behavior:
Exception thrown, program does not start

Stacktrace (if applicable):

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" java.lang.NoSuchFieldError: INSTANCE
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.<clinit>(SSLConnectionSocketFactory.java:144)
    at org.apache.http.impl.client.HttpClientBuilder.build(HttpClientBuilder.java:955)
    at sx.blah.discord.api.internal.Requests.<clinit>(Requests.java:53)
    at sx.blah.discord.api.internal.DiscordClientImpl.validateToken(DiscordClientImpl.java:231)
    at sx.blah.discord.api.internal.DiscordClientImpl.login(DiscordClientImpl.java:217)
    at sx.blah.discord.api.ClientBuilder.login(ClientBuilder.java:104)
    at BaseBot.login(BaseBot.java:33)
    at BaseBot.main(BaseBot.java:20)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Please note that even though it's calling the methods in BaseBot, there is nothing in that java file other than the code mentioned above in the main method

Version affected: [The version of Discord4J]
2.4.0

Additional Information

I don't know if this issue is just me being an idiot or what but any help would be greatly appreciated

Request Buffer Timer Thread - Negative delay

Prerequisites

Description

During runtime an IllegalArgumentException got thrown in the "Request Buffer Timer" thread.
This occured when the following code was executed

RequestBuffer.request(() -> {
            try {
                new MessageBuilder(getClient()).appendContent(message).withChannel(channel).build();
            } catch (DiscordException ignored) {
            } catch (MissingPermissionsException e) {
                plugin.getLogger().severe("Your Bot is missing required permissions to perform this action");
            }
            return null;
        });

Steps to Reproduce

Unable to reproduce, yet.

...

Expected behavior: Message gets delivered without any exceptions thrown.

Actual behavior: Message got sent, along with the exception above.

Stacktrace (if applicable): http://hastebin.com/fekibeyijo.avrasm

Version affected: dev, build with commit 31aef0e

Guild / server avaibility

Prerequisites

Description

Bot is unable to interact with server, probably related to release of new official API

Steps to Reproduce

  1. Use a bot Account
  2. Add bot to your server with official oAuth url : https://discordapp.com/oauth2/authorize?&client_id=&scope=bot&permissions=0
  3. Login with token using clientBuilder

Expected behavior: Retry check server availabity

Actual behavior: Nothing

Stacktrace (if applicable): [WebSocketClient@106374177-19] WARN sx.blah.discord.Discord4J - Guild with id is unavailable, ignoring it. Is there an outage?
Version affected: 2.4.2

Additional Information

Unavailability on official api docs :
https://discordapp.com/developers/docs/topics/gateway#guild-unavailability

Bad request on changing guild regions (moved from #java_discord4j)

Prerequisites

Description

Regions cannot be changed.

Steps to Reproduce

  1. Call IGuild#changeRegion()

Expected behavior: Region was changed.

Actual behavior: Region is unchanged and Discord4J reports a bad request.

Stacktrace (if applicable): http://pastebin.com/NhtYzp7j

Version affected: 2.4.x and probably 2.5.x

Additional Information

Originally reported by @Neraz

IChannel#getModifiedPermissions for private channel throws uncaught exception

Prerequisites

Description

Trying to get the bot permissions for private channel results in an exception.

Steps to Reproduce

  1. Send bot a private message.
  2. React with bot and try to get permissions for IMessage#getChannel
  3. Exception

Expected behavior:
The permissions for this private channel. For example the Permissions.SEND_MESSAGES.

Actual behavior:
The method breaks with an exception.

Stacktrace (if applicable):
http://pastebin.com/8j1SQcbj
results in
http://pastebin.com/RF4FUN8L

Version affected:
f8144af

Delete PM message of other user should throw Exception

Prerequisites

Description

When a bot tries to delete another user's pm message, instead of throwing a DiscordException or a MissingPermissionsException, it throws nothing and then Discord4J throws a 403 forbidden error.

Steps to Reproduce

  1. Send a PM message to a bot
  2. Have the bot try to delete it

...

Expected behavior: Throw a missing permissions exception

Actual behavior: No exception thrown and a 403 forbidden error

Stacktrace (if applicable):
18:54:42.979 [Event Dispatch Thread] ERROR s.b.d.Discord4J - Received 403 forbidden error for url https://discordapp.com/api/channels/214883700708605952/messages/216344222054350848. If you believe this is a Discord4J error, report this!
(It is one line so I didn't think hastebin was needed)

Version affected: 2.5.4

Additional Information

n/a

Threads not properly released

Hello,
I've noticed that ClientBuilder.login() creates 3 different threads, but those threads are never stopped when calling logout() can you look into it?

Are you still on the project

Hi, this has nothnig to do with "discord4J" but idk how to find a way to speak to you so I use the last topic you were on to ask this, but because of new mobs like bunnies, shulker, etc.. your "lootplus" plugin don't work, I mean it work for drops on normal mobs, but if a player kill new mobs, you get this error : http://prntscr.com/bk8cut . Is there a way to update it with new mobs please ?

Unable to leave voice channels

Prerequisites

Description

A bot user is unable to leave its connected voice channel(s).

Steps to Reproduce

  1. Join a voice channel
  2. Try to leave the channel

Expected behavior: The bot leaves the channel.

Actual behavior: Nothing.

Stacktrace (if applicable): N/A

Version affected: 2.4.x and 2.5.x

Additional Information

This has been confirmed on other libraries like Discord.NET, discord.py, discordgo, and discordrb so it appears to be an external Discord issue.

Role color

Prerequisites

Description

When setting a roles color it either does nothing at all or goes to the default color.

Steps to Reproduce

  1. Get a role and set its color.

Expected behavior: The role color was supposed to be set to specified value.

Actual behavior: Nothing or the color went to default.

Stacktrace (if applicable):

Version affected: 2.4.4

Additional Information

Implement Audio Receiving

Audio receiving would be nice to have, however this has low priority for me. It is a stretch goal for 2.6 but it is entirely possible that it won't be implemented by then.

This feature would need:

  • Per-user audio
  • A utility to combine audio
  • It should utilize AudioManager in order to allow for one to subscribe an IAudioReceiver to receive data in real time as well as providing a method which returns an AudioInputStream
  • It would be useful to have the ability to determine the format of the received audio (i.e. opus, pcm, etc)

Role caches not properly updated

Prerequisites

Description

A role's cached properties are not properly updated when the websocket receives GUILD_ROLE_UPDATE. As seen here, the "updated" role toUpdate is not actually ever updated. The cached proerties such as name, permissions, etc. will continue to remain the default until restart. This has the adverse effect of RoleUpdateEvent always sending identical role objects.

Steps to Reproduce

  1. Edit a role while the bot is running

Expected behavior: The cached properties of the edited role will be updated to match Discord.

Actual behavior: They are not.

Version affected: 2.5

Don't use Optional as a class field

Description

Currently the game-Field in the User-Class is an Optional - this is not the intended Optional usage. For more info you should read Brian Goetz' comment about Optional here: Should Java 8 getters return optional type?
In short: Optional is a limited tool for APIs to declare a "value not present" return type.

Also, Optionals are not serializable, which might be a problem in the future.

The better approach would be to store the game still as a String, but update the setter methods to only accept Strings and not an Optional. The getter methods can still return Optional in the format of:
return Optional.ofNullable(game);

Version affected: 2.4.4 and below

This is by no means a groundbreaking issue, just something "nice" to have since it follows java guidelines more closely. I can write a patch for it if you don't have the time to take on this task.

voice connections are not closed

Prerequisites

Description

Voice connections are not closed upon abnormal disconnects (anything other than using .logout)

Steps to Reproduce

  1. connect to a voice channel
  2. get disconnected abnormally

...

Expected behavior: disconnect from voice
Actual behavior: voice not disconnected

Stacktrace (if applicable): http://hastebin.com/ujekokoves.avrasm

Version affected: 09dd675

Additional Information

NullPointerException during reconnects

Prerequisites

Description

A NPE occurs during reconnects. The cause is from an attempt to check for permissions when the bot hasn't signed in yet.

Steps to Reproduce

  1. Have D4J attempt to reconnect.
  2. Look at the error.

I should note that there's a low chance it succeeds without the error, but in my experience it errors most of the time.

...

Expected behavior: A reconnect should happen.

Actual behavior: The reconnect fails with the null pointer exception.

Stacktrace (if applicable):
PrivateChannel error: http://hastebin.com/uzegetuqib.avrasm
Channel error: http://hastebin.com/himiwirese.avrasm
Longer stacktraces with both errors in the same launch: http://hastebin.com/ladocageba.avrasm

Version affected: dev 5e89125, occurred earlier as well

Additional Information

java.util.ConcurrentModificationException

I'm using the 2.2.6 version (from Maven), in this project (a Spigot plugin), and I'm getting this exception on the console. I don't know if this has been fixed on the current snapshot, but looking at the sources (Channel.java:169), that function has not been changed. It's not harmful to the plugin, but it's kinda annoying seeing this on the console.

[15:17:52 WARN]: java.util.ConcurrentModificationException
[15:17:52 WARN]:        at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
[15:17:52 WARN]:        at java.util.ArrayList$Itr.next(ArrayList.java:851)
[15:17:52 WARN]:        at sx.blah.discord.handle.impl.obj.Channel.getMessageByID(Channel.java:169)
[15:17:52 WARN]:        at sx.blah.discord.api.internal.DiscordUtils.getMessageFromJSON(DiscordUtils.java:316)
[15:17:52 WARN]:        at sx.blah.discord.api.internal.DiscordWS.messageCreate(DiscordWS.java:303)
[15:17:52 WARN]:        at sx.blah.discord.api.internal.DiscordWS.onMessage(DiscordWS.java:144)
[15:17:52 WARN]:        at org.java_websocket.client.WebSocketClient.onWebsocketMessage(WebSocketClient.java:312)
[15:17:52 WARN]:        at org.java_websocket.WebSocketImpl.decodeFrames(WebSocketImpl.java:368)
[15:17:52 WARN]:        at org.java_websocket.WebSocketImpl.decode(WebSocketImpl.java:157)
[15:17:52 WARN]:        at org.java_websocket.client.WebSocketClient.interruptableRun(WebSocketClient.java:230)
[15:17:52 WARN]:        at org.java_websocket.client.WebSocketClient.run(WebSocketClient.java:188)
[15:17:52 WARN]:        at java.lang.Thread.run(Thread.java:745)

Current main classes that have relation with this function are src/main/java/com/makzk/spigot/discordchat/DiscordListener.java and src/main/java/com/makzk/spigot/discordchat/MinecraftListener.java.

Reconnection Attempt Timed Out

Prerequisites

Description

The reconnect times out and doesn't reattempt afterwards.

Steps to Reproduce

  1. Let D4J attempt to reconnect

...

Expected behavior: It reconnects or fails and tries again.

Actual behavior: It times out and never tries again

Stacktrace (if applicable): http://hastebin.com/imekagafer.avrasm

Version affected: 2.5.4 / dev as of writing this

Additional Information

Seemingly random, instead of reconnecting it just times out, process isn't stopped either.

This is popping up more and more so an issue will make it easier to keep track of.

Implement Sharding

I have been meaning to do this for awhile.

Some requirements of this feature would have to include:

  • a ClientBuilder#withShards() method
  • Some way to find out which shard an event was dispatched from (Event#getShard()?)
  • Possibly a way hotswap shard size (which means reconnecting the websocket). It may be necessary to keep the old connection alive until the new gateway is ready to make sure events aren't missed if the session ends up being invalid.

Inconsistent timezones between getXDate methods

Prerequisites

Description

The LocalDateTimes retrieved from IDiscordClient#getCreationDate and IGuild#getJoinTimeForUser have differing timezones. The former has it in GMT (the zone Discord returns it as), but the latter has it in the system's timezone. @theIglooo has a screenshot of the example: https://u.pomf.is/hekspx.png

Steps to Reproduce

  1. Get the two times, attach them with the same timezone (GMT or otherwise) and print them out somewhere.
  2. Note that they differ by the timezone offset of your system.

Expected behavior: Both times should be in the same timezone.

Actual behavior: One time is in GMT, the other is in your system timezone.

Version affected: dev 7934555

Additional Information

Consider using ZonedDateTime, or standardizing the timezone, and writing it in the docs?

VoiceChannel should throw UnsupportedOperationException when .mention is called

Prerequisites

Description

When calling IVoiceChannel.mention() a #VoiceChannel is successfully placed into chat and it is clickable, but it brings you to a text chat room which doesn't truly exist.

Steps to Reproduce

  1. Create a voice channel object
  2. Do VoiceChannel.mention() in a sent message
  3. Click on the mentioned channel

...

Expected behavior: This shouldn't be possible

Actual behavior: You are brought to a ghost chat room

Version affected: 2.5.0

Additional Information

image

Parsing through IGuild#getRoles does not provide correct name in certain cases

Prerequisites

Description

Creating a new role (through API or through server GUI), setting its name, then iterating through IGuild#getRoles for names prints "new role". The correct name is printed if you iterate IGuild#getRoles from the same IGuild reference used to create the role. If you iterate through a different reference, it only prints "new role"

Steps to Reproduce

  1. Connect to server with bot
  2. Either programmatically create a new role or create one through the Discord GUI.
  3. Iterate through IGuild#getRoles from a separate reference

...

Expected behavior: Print the name set by the bot and/or server owner

Actual behavior: Prints "new role" for each role created

Version affected: 2.4.9

Additional Information

Example code:

GuildCreateListener

IRole newRole = guild.createRole();
newRole.changeName("Example name");
for(IRole role : guild.getRoles())
    System.out.println(role.getName());

will print the correct name, "Example name"

MessageReceivedListener event

for(IRole role : event.getMessage().getGuild().getRoles())
    System.out.println(role.getName());

will print "new name"

Detecting if a user is currently streaming

It is perfectly possible that right now this functionality is not yet in the official API or that it is in Discord4J and I couldnt find it.
http://puu.sh/oMl7B/61c2d32d7b.png
However if not, it would be great if one could detect this in the future.

In specific: I want to automatically add users that are currently streaming to a role, which is displayed on top of the list, so that users may see it and check out their stream; and that role is removed from them when not streaming anymore.

Change sendFile parameter to InputStream

Prerequisites

Description

It would be nice if the sendFile function accepted an InputStream as parameter instead of a File. File classes are difficult to use inside of a jar file (best solution I found on google was to copy the file from the jar to a temporary location) and it would also give you the possibility of sending images from an url or any other resource

Login Error : NoClassDefFoundError org/apache/http/client/methods/HttpUriRequest

Prerequisites

Description

Hello,

I'm getting the following error :

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/client/methods/HttpUriRequest
at sx.blah.discord.api.internal.DiscordClientImpl.validateToken(DiscordClientImpl.java:224)
at sx.blah.discord.api.internal.DiscordClientImpl.login(DiscordClientImpl.java:209)
at sx.blah.discord.api.ClientBuilder.login(ClientBuilder.java:144)
at com.darichey.tutorial_bot.TutorialBot.main(TutorialBot.java:11)
Caused by: java.lang.ClassNotFoundException: org.apache.http.client.methods.HttpUriRequest
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 4 more

while trying to login with a ClientBuilder. I'm using this serie of tutorials : http://blog.darichey.com/

Steps to Reproduce

  1. Login a bot using client = new ClientBuilder().withToken("TOKEN").login();
  2. Error

Expected behavior: Login the bot

Actual behavior: Error at login()

Stacktrace (if applicable):

Version affected: 2.5.2

Additional Information

I'm using Eclipse Neon with Maven and I'm under Windows 10.
It worked well under Windows 7 with Eclipse Luna.
I also tried with Eclipse Luna, IntelliJ and tried to make a new project from scratch. It doesn't work as well.

Multi-Guild Audio Poor Quality

Prerequisites

Description

Attempting to play audio into multiple voice channels in different guilds results in extremely distorted audio in both channels.

Steps to Reproduce

  1. Join a voice channel and begin playing audio with the AudioPlayer for that guild.
  2. Join another voice channel and do the same.

Expected behavior: Audio plays with normal drops in quality due to ping and other factors.

Actual behavior: The audio is distorted to an unrecognizable level. While perfect audio fidelity can't be expected from streaming to Discord, the amount of distortion leads me to believe it is an issue with Discord4J.

Version affected: 2.5

Additional Information

A video demonstrating the issue: http://y.zxq.co/ceftxl.webm

Discord4J Dev Build Login Exception

Prerequisites

Description

Logging in a bot using the ClientBuilder and.withToken("TOKEN").login(); throws an exception:
java.lang.NoClassDefFoundError: sx/blah/discord/api/IListener

Steps to Reproduce

  1. Login a bot using client = new ClientBuilder().withToken("TOKEN").login();
  2. Get exception.

...

Expected behavior: Login the bot and proceed to listen for commands.

Actual behavior: Uncaught exception thrown at login.

Stacktrace (if applicable): http://pastebin.com/JDRnrR2b

Version affected: 2.5.0-SNAPSHOT (Latest dev build)

Additional Information

None.

Make Discord4J source consistent and more readable with an official style guide.

Prerequisites

Description

Discord4J has inconsistent code style and no official style guide beyond a few basic directives. A full style guide should be chosen and utilized throughout the project.

I propose Google's Java Style Guide as it is more or less standard with most other style guides and is well thought out. This is the style guide used throughout all of Google's projects, many found on their GItHub org. Additionally, Google has a tool specifically written to maintain the style per that guide.

Making this change would make reading the source code on GitHub and in any editor easier and provide for a more consistent guideline for contributions to follow.

Additional Information

I would be willing to implement this change across the entire project to bring it in line with the style guide if desired.

DiscordDisconnectedEvent is never fired

Prerequisites

Description

DiscordDisconnectedEvent never fires because isReady() returns false which halts the dispatcher. I tried fixing it but I ended up with more errors and problems so here we are. ๐Ÿ˜ข

Steps to Reproduce

  1. Listen for DiscordDisconnectedEvent (or use trace instead of debug for logging)
  2. Disconnect
  3. Event is never dispatched

Expected behavior: DiscordDisconnectedEvent is dispatched upon disconnecting

Actual behavior: It is not

Stacktrace (if applicable): N/A

Version affected: 7934555

Additional Information

Re-order roles doesn't check all the necessary permissions

Prerequisites

Description

Attempting to move a role higher than the user's current role will result in a DiscordException rather than a MissingPermissionsException.

Steps to Reproduce

  1. Re order a role to a position higher than the bot's

Expected behavior: A MissingPermissionsException is thrown

Actual behavior: A DiscordException is thrown

Stacktrace (if applicable): https://puu.sh/qaPm1/cf45449a0d.png

Version affected: [The version of Discord4J]

Connecting to Voice Channel Creates Memory Leaks

Prerequisites

  • If this is a question/suggestion, I have already considered talking about it on the Discord4J server
  • This issue specifically has something to do with Discord4J
  • I have attempted to look for similar issues
    already

Description

When you connect to a voice channel it creates 9 threads that are never cleaned up.

Steps to Reproduce

  1. Connect to a voice channel
  2. Disconnect
  3. Measure the ammount of threads
  4. Repeat

TheIgloo has confirmed he is able to reproduce this

...

Expected behavior: When disconnecting from a voice channel the connection's threads are cleaned up.

Actual behavior: The threads are never cleaned up, not even when calling the Garbage Collector.

Stacktrace (if applicable): [PASTEBIN or HASTEBIN link to the stacktrace]
I made a bot to test this. It is a blank bot that simply connects to a voiceChannel, disconnects, and measures the amount of threads present 10 x, then measures the amount of threads every 15 seconds.
Here is the output: http://hastebin.com/ogixonadet.py
Version affected: Current Dev build

Additional Information

TheIgloo tested this as well and he found the same thing. I have also looked at the threads with VisualVM and they are infact at the levels that are being output.

AudioReceivedEvent not received

Prerequisites

Description

Listener does not receive AudioReceiveEvent event.

Steps to Reproduce

  1. (Have method to receive audio event (i.e

@EventSubscriber
public void onAudioReceiveEvent(AudioReceiveEvent event) {
System.out.println("Received audio");
}
))

  1. Send audio to bot

...

**Expected behavior: Receive AudioReceiveEvent when user is outputing audio while bot is in channel (i.e me talking with bot in channel)

Actual behavior: No AudioReceiveEvent received

**Stacktrace (if applicable): N/A

Version affected: 2.4.9

Additional Information

Other @EventSubscriber methods receive events correctly, this is the only one I've tried that doesn't seem to work.

Method used is :
@EventSubscriber
public void onAudioReceiveEvent(AudioReceiveEvent event) {
System.out.println("Received audio");
}

Ratelimit handling is boned

Prerequisites

Description

Buckets aren't a thing anymore so it causes an NPE in the RequestBuffer.

Steps to Reproduce

  1. Hit a ratelimit

Expected behavior: The RequestBuffer doesn't error

Actual behavior: The RequestBuffer errors

Stacktrace (if applicable): http://i.imgur.com/cILriLp.png

Version affected: All of them :(

Additional Information

https://github.com/hammerandchisel/discord-api-docs/pull/120/files

Report-kinda | withReconnects() throws exception

Hash: 82c2bb4
After I made my bot login. I disabled my network adapter for test purposes and that happened on the console:

[main] INFO sx.blah.discord.Discord4J - Discord4J v2.5.0-SNAPSHOT
[main] INFO sx.blah.discord.Discord4J - A Java binding for the unofficial Discord API, forked from https://github.com/nerd/Discord4J. Copyright (c) 2016, Licensed under GNU GPLv2
[AWT-EventQueue-0] INFO org.eclipse.jetty.util.log - Logging initialized @3791ms
[WebSocketClient@682010040-26] INFO sx.blah.discord.Discord4J - Connected to the Discord Websocket v5
[Request Builder Async Executor] INFO sx.blah.discord.Discord4J - Connected to 1 guilds.
[WebSocketClient@682010040-30] INFO sx.blah.discord.Discord4J - Attempting to reconnect...
[Websocket Reconnect Timer] ERROR sx.blah.discord.Discord4J - Reconnect attempt timed out after 10 seconds
[WebSocketClient@682010040-32] ERROR sx.blah.discord.Discord4J - Websocket error, disconnecting...
java.nio.channels.UnresolvedAddressException
    at sun.nio.ch.Net.checkAddress(Net.java:121)
    at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:617)
    at org.eclipse.jetty.websocket.client.io.ConnectionManager$PhysicalConnect.run(ConnectionManager.java:70)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:654)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:572)
    at java.lang.Thread.run(Thread.java:745)

Tried to reconnect once, then threw exception.

Getting audio data from a voicechannel

Hello.
I can't seem to get audio right. I have managed to play music and stuff correctly queueing a file.
Now I would like to make something like an echo bot by getting the received audio and sending it back through the bot. However I can't get this to work properly.

I'm using the VoiceUserSpeakingEvent.
When it triggers I get the client's audio channel and take It's audiodata.
I then try to put the audio data in an inputstream to send it to the audiochannel's queue method.
However it seems to already break at the getAudioData() method.

Could anyone shed some light on this particular problem?
Thanks in advance!

IDiscordClient#getConnectedVoiceChannels not returning the full list

Prerequisites

Description

Calling IDiscordClient#getConnectedVoiceChannels returns a list with only one voice channel, even if your bot is in multiple.

Steps to Reproduce

  1. Have a bot join two or more voice channels
  2. Check the contents of #getConnectedVoiceChannels

Expected behavior: Returns a list of all the voice channels the bot is in.

Actual behavior: Returns a list with only the most recently joined voice channel.

Stacktrace (if applicable): N/A

Version affected: dev-31aef0e

Additional Information

Don't know what else to put, I guess a screenshot showing what I mean might help?
Image of Wonky getConnectedVoiceChannels()

ConcurrentModificationException

Prerequisites

Description

[Description of the issue]
ConcurrentModificationException when trying to send a private message to a user.

Steps to Reproduce

  1. Try to send a private message
  2. {Unknown}
  3. Exception?
    ...

Expected behavior: Send the PM properly.

Actual behavior: ConcurrentModificationException - And probably did not send the message.

Stacktrace (if applicable): [PASTEBIN or HASTEBIN link to the stacktrace]
http://pastebin.com/bkKhmjxK

Version affected: Latest commit on dev

Additional Information

[Any other information that may be able to help me with the problem]
This bug ticket is poorly written as I do not have much knowledge of how this happened, just popped up when someone tried to use my bot.
(Don't look at my github repo for my bot, I haven't commited in ages.)

Append 502 error message to DiscordException if not caused by CloudFlare

Prerequisites

  • If this is a question/suggestion, I have already considered talking about it on the #java_discord4j channel on
    the Discord API Server
  • This issue specifically has something to do with Discord4J
  • I have attempted to look for similar issues already

Description

After my fix splitting all CloudFlare induced 502s into its own DiscordException Message, I discussed with @austinv11 on the Discord channel about adding the error message to the other Exception (non-CF), and it's fine by him.

Expected behavior: The exception message should have "502 error on request to [URI]. Message: [msg]"

Actual behavior: The exception message will have "502 error on request to [URI]."

Stacktrace (if applicable): N/A

Version affected: 2.5.1

Additional Information

L151, Requests.java

Maven don't recognized version 2.5.2

Description

The version 2.5.2 isn't recognized with Maven

Expected behavior: No error

Actual behavior: Error

Stacktrace (if applicable): Missing artifact com.github.austinv11:Discord4j:jar:2.5.2

Version affected: 2.5.2

Additional Information

I use Maven with eclipse

failed to load class "org.slf4j.impl.StaticLoggerBinder"

Prerequisites

  • This issue specifically has something to do with Discord4J

Description

My bot is failing to load class "org.slf4j.impl.StaticLoggerBinder".
I used Gradle to get Discord4J and got all the dependencies.

Im aware that Slf4j.Simple should be downloaded through Gradle. But it isnt.
Im using commit: 7a0b1fb.

A way to fix it

Get Slf4j-nop-1.7.9 through Gradle / Maven.

repositories { ... maven { url "http://repo2.maven.org/maven2/"; } }

dependencies { ... compile "org.slf4j:slf4j-simple:1.7.9"; }

Additional information

As of SLF4J version 1.6, in the absence of a binding, SLF4J will default to a no-operation (NOP) logger implementation.
You can download SLF4J bindings from the project download link

Source: http://www.slf4j.org/codes.html#StaticLoggerBinder

Unknown Message

Received the following console output while leaving my bot Idle on a private server. Not sure what significance it might have, but it asked to be reported here. This is a Gist containing the error message. Session IDs and user IDs have been omitted intentionally.

Pinning messages is not possible.

Prerequisites

Description

Pinning messages is not possible.

Steps to Reproduce

IMessage msg = /* ... get a valid message somewhere */ ;
msg.getChannel().pin(msg);

...

Expected behavior: The message would be pinned.

Actual behavior: It is not.

Stacktrace (if applicable): http://pastebin.com/pZSsAM6T

Version affected: Discord4J/dev at commit 9ac0ce294d0a7a81f298a3e2d7d9282d7cf5b2e2

Discord4J#disableChannelWarnings() should disable 404 Errors

Prerequisites

  • If this is a question/suggestion, I have already considered talking about it on the Discord4J server
  • This issue specifically has something to do with Discord4J
  • I have attempted to look for similar issues
    already

Description

When I delete a message with my bot, I get a 404 error even if I don't access it anymore, and it leads to this maddness: http://hastebin.com/hadasoqexu

Steps to Reproduce

  1. Delete a message with Discord4J
  2. Wait

...

Expected behavior: Silence 404 Errors

Actual behavior: Tons of 404 Errors flood the logs

Stacktrace (if applicable): http://hastebin.com/hadasoqexu

Version affected: Current Dev Build (dev-2.5.4-g2f1495c-91)

Additional Information

I think the fact that it doesn't realize the Message has been deleted and trys to access it could be a bug as well, but I don't know. Yes I have tripple checked that my code does not access the IMessage object after deleting it. All references to the object are removed after deleting it.

Implement Message embed support

Currently embeds trigger a MessageUpdateEvent. There should be a separate event for this (maybe called MessageEmbedEvent?) which will contain the information regarding the embed for the message. Additionally, I should provide a POJO for embed objects

Deleting messages doesn't require the client's permissions to be superior to the author of the message

If the title wasn't clear enough, DiscordUtils' checkPermissions method seems to check the client's permissions be equal to the permissions given. Thing is, deleting messages doesn't require this and only the client to have sufficient permissions to delete the message. Therefore, that method wouldn't exactly work.

P.S.: You seem to also check for mentioning everyone and embedding permissions, even though the client should still be able to send that message with everyone and embedding and without permissions.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.