Giter VIP home page Giter VIP logo

discord-webhooks's Introduction

version license

Discord-Webhooks

Originally part of JDA, this library provides easy to use bindings for the Discord Webhook API.

Introduction

Here we will give a small overview of the proper usage and applicability of the resources provided by this library.

Documentation is available via the GitHub pages on this repository: Javadoc

Limitations

Webhooks on discord are only capable of sending messages, nothing more. For anything else you either have to use OAuth2 or a bot account. This library does not provide any functionality for creating or modifying webhooks.

Getting Started

The first thing to do is to create either a WebhookClient or a WebhookCluster. The WebhookClient provides functionality to send messages to one webhook based on either a webhook URL or the ID and token of a webhook. It implements automatic rate-limit handling and can be configured to use a shared thread-pool.

Creating a WebhookClient

// Using the builder
WebhookClientBuilder builder = new WebhookClientBuilder(url); // or id, token
builder.setThreadFactory((job) -> {
    Thread thread = new Thread(job);
    thread.setName("Hello");
    thread.setDaemon(true);
    return thread;
});
builder.setWait(true);
WebhookClient client = builder.build();
// Using the factory methods
WebhookClient client = WebhookClient.withUrl(url); // or withId(id, token)

Creating a WebhookCluster

// Create and initialize the cluster
WebhookCluster cluster = new WebhookCluster(5); // create an initial 5 slots (dynamic like lists)
cluster.setDefaultHttpClient(new OkHttpClient());
cluster.setDefaultDaemon(true);

// Create a webhook client
cluster.buildWebhook(id, token);

// Add an existing webhook client
cluster.addWebhook(client);

Sending Messages

Sending messages happens in a background thread (configured through the pool/factory) and thus is async by default. To access the message you have to enable the wait mechanic (enabled by default). With this you can use the callbacks provided by CompletableFuture<ReadonlyMessage>.

// Send and forget
client.send("Hello World");

// Send and log (using embed)
WebhookEmbed embed = new WebhookEmbedBuilder()
        .setColor(0xFF00EE)
        .setDescription("Hello World")
        .build();

client.send(embed)
      .thenAccept((message) -> System.out.printf("Message with embed has been sent [%s]%n", message.getId()));

// Change appearance of webhook message
WebhookMessage message = new WebhookMessageBuilder()
        .setUsername("Minn") // use this username
        .setAvatarUrl(avatarUrl) // use this avatar
        .setContent("Hello World")
        .build();
client.send(message);

Threads

You can use the webhook clients provided by this library to send messages in threads. There are two ways to accomplish this.

Set a thread id in the client builder to send all messages in that client to the thread:

WebhookClient client = new WebhookClientBuilder(url)
        .setThreadId(threadId)
        .build();

client.send("Hello"); // appears in the thread

Use onThread to create a client with a thread id and all other settings inherited:

try (WebhookClient client = WebhookClient.withUrl(url)) {
    WebhookClient thread = client.onThread(123L);
    thread.send("Hello"); // appears only in the thread with id 123
    client.send("Friend"); // appears in the channel instead
} // calls client.close() which automatically also closes all onThread clients as well.

All WebhookClient instances created with onThread will share the same thread pool used by the original client. This means that shutting down or closing any of the clients will also close all other clients associated with that underlying thread pool.

WebhookClient thread = null;
try (WebhookClient client = WebhookClient.withUrl(url)) {
    thread = client.onThread(id);
} // closes client
thread.send("Hello"); // <- throws rejected execution due to pool being shutdown by client.close() above ^

WebhookClient client = WebhookClient.withUrl(url);
try (WebhookClient thread = client.onThread(id)) {
    thread.send("...");
} // closes thread
client.send("Hello");  // <- throws rejected execution due to pool being shutdown by thread.close() above ^

Shutdown

Since the clients use threads for sending messages you should close the client to end the threads. This can be ignored if a shared thread-pool is used between multiple clients but that pool has to be shutdown by the user accordingly.

try (WebhookClient client = WebhookClient.withUrl(url)) {
    client.send("Hello World");
} // client.close() automated

webhookCluster.close(); // closes each client and can be used again

Error Handling

By default, this library will log every exception encountered when sending a message using the SLF4J logger implementation. This can be configured using WebhookClient#setErrorHandler to custom behavior per client or WebhookClient#setDefaultErrorHandler for all clients.

Example

WebhookClient.setDefaultErrorHandler((client, message, throwable) -> {
    System.err.printf("[%s] %s%n", client.getId(), message);
    if (throwable != null)
        throwable.printStackTrace();
    // Shutdown the webhook client when you get 404 response (may also trigger for client#edit calls, be careful)
    if (throwable instanceof HttpException ex && ex.getCode() == 404) {
        client.close();
    }
});

External Libraries

This library also supports sending webhook messages with integration from other libraries such as

Example JDA

public void sendWebhook(Webhook webhook) {
    MessageCreateData message = new MessageCreateBuilder().setContent("Hello World").build();
    try (JDAWebhookClient client = JDAWebhookClient.from(webhook)) { // create a client instance from the JDA webhook
        client.send(message); // send a JDA message instance
    }
}

Example Discord4J

public void sendWebhook(Webhook webhook) {
    try (D4JWebhookClient client = D4JWebhookClient.from(webhook)) {
        client.send(MessageCreateSpec.create()
            .withContent("Hello World")
            .addFile("cat.png", new FileInputStream("cat.png"))
        );
    }
}

Download

version

Note: Replace %VERSION% below with the desired version.

Gradle

repositories {
    mavenCentral()
}
dependencies {
    implementation("club.minnced:discord-webhooks:%VERSION%")
}

Maven

<dependency>
    <groupId>club.minnced</groupId>
    <artifactId>discord-webhooks</artifactId>
    <version>%VERSION%</version>
</dependency>

Compile Yourself

  1. Clone repository
  2. Run gradlew shadowJar
  3. Use jar suffixed with -all.jar in build/libs

Example

class MyAppender extends AppenderBase<LoggingEvent> {
    private final WebhookClient client;

    @Override
    protected void append(LoggingEvent eventObject) {
        if (client == null)
            return;
        WebhookEmbedBuilder builder = new WebhookEmbedBuilder();
        builder.setDescription(eventObject.getFormattedMessage());
        int color = -1;
        switch (eventObject.getLevel().toInt()) {
            case ERROR_INT:
                color = 0xFF0000;
                break;
            case INFO_INT:
                color = 0xF8F8FF;
                break;
        }
        if (color > 0)
            builder.setColor(color);
        builder.setTimestamp(Instant.ofEpochMilli(eventObject.getTimeStamp()));
        client.send(builder.build());
    }
}

This is an example implementation of an Appender for logback-classic

discord-webhooks's People

Contributors

almighty-satan avatar atakku avatar balam314 avatar bungeefan avatar dan-dr avatar fratik avatar github-actions[bot] avatar jroy avatar justpyrrha avatar kantenkugel avatar minndevelopment avatar mrletsplay2003 avatar nea89o avatar xirado avatar zavarov avatar zbutwialypiernik 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

discord-webhooks's Issues

CloudFlare Ratelimits

I was just wondering if you handle cloudflare bans, because yesterday I hit a cloudflare ban and it seemed the library kept accumulating requests and continuosly tried to send them instead of falling back and waiting for the ban to end which meant I was just guaranteed to get banned for the next hour especially with the amount of requests it was accumulating. So not sure if this is due to the library not handling cloudflare ratelimits or if it has something to do with something else.
image

Issues with accents

Hi !

Here in france we use characters likes "รฉ", "ร ", "รน"

And it doesn't works with DiscordWebhooks, idk if its me bc I use webhooks with an Habbo Emulator, is it this?

Thanks

[BUG] File upload issue

When I try to upload a file to Discord using a webhook, which includes an embed, I get this error:

Could not pass event AFRenderEvent to ActionFoto v2.7.3
java.lang.NoSuchMethodError: okhttp3.MultipartBody.appendQuotedString(Ljava/lang/StringBuilder;Ljava/lang/String;)V
    at okhttp3.MultipartBody$Part.createFormData(MultipartBody.java:251) ~[?:?]
    at okhttp3.MultipartBody$Builder.addFormDataPart(MultipartBody.java:327) ~[?:?]
    at club.minnced.discord.webhook.send.WebhookMessage.getBody(WebhookMessage.java:328) ~[?:?]
    at club.minnced.discord.webhook.WebhookClient.send(WebhookClient.java:217) ~[?:?]
    at nl.sbdeveloper.actionfoto.listeners.ActionFotoTakeListener.onActionFotoTake(ActionFotoTakeListener.java:68) ~[?:?]
    at com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor294.execute(Unknown Source) ~[?:?]
    at org.bukkit.plugin.EventExecutor.lambda$create$1(EventExecutor.java:69) ~[patched_1.16.5.jar:git-Paper-457]
    at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:76) ~[patched_1.16.5.jar:git-Paper-457]
    at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70) ~[patched_1.16.5.jar:git-Paper-457]
    at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:607) ~[patched_1.16.5.jar:git-Paper-457]
    at nl.sbdeveloper.actionfoto.api.FrameAPI.lambda$renderAFFrame$2(FrameAPI.java:415) ~[?:?]
    at org.bukkit.craftbukkit.v1_16_R3.scheduler.CraftTask.run(CraftTask.java:99) ~[patched_1.16.5.jar:git-Paper-457]
    at org.bukkit.craftbukkit.v1_16_R3.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:54) ~[patched_1.16.5.jar:git-Paper-457]
    at com.destroystokyo.paper.ServerSchedulerReportingWrapper.run(ServerSchedulerReportingWrapper.java:22) ~[patched_1.16.5.jar:git-Paper-457]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_201]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_201]
    at java.lang.Thread.run(Thread.java:748) [?:1.8.0_201]

I'm on version 0.5.5-rc of Discord-Webhooks. This is my code:

// These values come from a config file or API. I've put them there to show the reference.
String nameOrID = ...;
RenderedImage img = ...;

String title = ...;
String description = ...;
String copy = ...;
String copyimg = ...;
Integer color = ...;

WebhookEmbed embed = new WebhookEmbedBuilder()
        .setTitle(new WebhookEmbed.EmbedTitle(title, ""))
        .setDescription(description)
        .setFooter(new WebhookEmbed.EmbedFooter(copy, copyimg))
        .setColor(color)
        .build();

WebhookMessage message = new WebhookMessageBuilder()
        .addEmbeds(embed)
        .addFile("af-picture-" + nameOrID + ".png", RenderAPI.imgToByteArray(img, "png"))
        .build();

Main.getWebhookClient().send(message);

RenderAPI.imgToByteArray returns the byte[] of my image.

Am I doing anything wrong, or is this an issue in Discord-webhooks?

Support for sending messages with timeouts

The following code is being used to send with a 30 second timeout.

    void sendMessagesWithTimeout(Notification change, Optional<DiscordGuild> guild, List<DiscordGuild> helperGuilds) {
        //message created by some method
        WebhookMessage message = createRemoteHelpMessage(change);
        log.info("Sending {} discord raid request for {} to {} servers", language, change.getId(), helperGuilds.size());
        for (DiscordGuild helperGuild: helperGuilds) {
            try {
                getClient(helperGuild.getId()).send(message, helperGuild).orTimeout(30000, TimeUnit.MILLISECONDS).whenCompleteAsync((result,e) -> {
                    if (e != null) {
                        if (e instanceof TimeoutException) {
                            log.warn("Time out after {}ms sending {} to {}", globalNotificationTimeoutMs ,change.getRaidPartyId(), helperGuild);
                        }
                    }
                });
            } catch (ExecutionException | RuntimeException e) {
                log.error("Not retrying " + change.getId() + ": Could not send discord raid request for " + helperGuild, e);
            }
        }
    }

Unfortunately, this does not currently cancel the request

The following change is required to support timeouts

    private boolean executePair(@Async.Execute Request req) {
        if (req.future.isCancelled()) {

should be

    private boolean executePair(@Async.Execute Request req) {
        if (req.future.isCompletedExceptionally()) {

as TimeoutException is not a CancellationException. Per javadocs, isCompletedExceptionally is a superset of isCancelled.

Trying to send a file along with a message

This is my bit of code

long id = 779418282280091669L;
WebhookClient client = WebhookClient.withId(id, "[REDACTED]");
client.send("Hello, lmao test");
WebhookEmbed embed = new WebhookEmbedBuilder()
    .setColor(0x00b3ff)
    .setDescription("test")
    .build();
client.send(embed);
System.out.println("Message Sent!");
client.send(new File("/storage/emulated/0/Downloads/file.txt"));
System.out.println("File sent!");
client.close();

Its using the Android sdk lvl 30
The message sends fine, just not the embed neither the file

image

Ive tried other library's as well, but they dont work either. This library is the nicest one yet so here i am posing my issue.
Im quite a begginer in java so please dont roast me, thank you ๐Ÿ˜€

Thread limit reached.

It seems like this libary creates for each Webhook a thread:

return Executors.newSingleThreadScheduledExecutor(factory == null ? new DefaultWebhookThreadFactory(id, isDaemon) : factory);

This causes the thread limit to be reached when e.g. several webhooks are sent per minute.
"java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached"

I profiled it with YourKit, the threads will not be shutdowned.

Solution:
Shutdown the thread or use a Pool of threads. Or make possible to use own thread pool.

Failed to parse webhook URL

I have:

private val shardHook = WebhookClientBuilder("https://canary.discord.com/api/webhooks/{MY_CHANNEL_ID}/{MY_WEBHOOK_TOKEN}").build()

But it fails to parse the webhook url I copied it from discord in channel settings, I've tried the webhook id and token, but all of three of don't work

Error:

Caused by: java.lang.IllegalArgumentException: Failed to parse webhook URL
        at club.minnced.discord.webhook.WebhookClientBuilder.<init>(WebhookClientBuilder.java:84)
        at bot.hibikibot.utils.WebhookManager.<clinit>(WebhookManager.kt:13)
        ... 26 common frames omitted

Webhook fails to edit due to missing author field

I currently get this error message when trying to edit any message with a webhook, I've tried this in 2 different servers and get the same result, just in case it's a discord issue and I still believe it might be. I've used the most basic way to edit a message to make sure it's not on my end

java.lang.NumberFormatException: null
	at java.base/java.lang.Integer.parseInt(Integer.java:620)
	at java.base/java.lang.Integer.parseInt(Integer.java:776)
	at club.minnced.discord.webhook.WebhookClient$Bucket.update0(WebhookClient.java:678)
	at club.minnced.discord.webhook.WebhookClient$Bucket.update(WebhookClient.java:692)
	at club.minnced.discord.webhook.WebhookClient.executePair(WebhookClient.java:585)
	at club.minnced.discord.webhook.WebhookClient.drainQueue(WebhookClient.java:560)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:835)
51116 [pool-11-thread-1] ERROR club.minnced.discord.webhook.WebhookClient - There was some error while sending a webhook message
org.json.JSONException: JSONObject["author"] not found.
	at org.json.JSONObject.get(JSONObject.java:566)
	at org.json.JSONObject.getJSONObject(JSONObject.java:778)
	at club.minnced.discord.webhook.receive.EntityFactory.makeMessage(EntityFactory.java:248)
	at club.minnced.discord.webhook.WebhookClient.executePair(WebhookClient.java:600)
	at club.minnced.discord.webhook.WebhookClient.drainQueue(WebhookClient.java:560)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:835)

footer and author icon url not working - output shows it as iconUrl instead of icon_url

// add author
embedBuilder.setAuthor(new EmbedAuthor("Author", "https://example.com/png.png", "https://example.com"));

// ... later after creating a message
webhookMessage.getBody().writeTo(buffer);
LOGGER.debug("getBody: " + buffer.readUtf8());

//in the output i see
"author":{"name":"Author","iconUrl":"https://example.com/png.png","url":"https://example.com"}

I believe that's what's sent to discord. it should be "icon_url", and so the icon isn't visible on my embed as a result.
Maybe a json library conflict?

[Question] How does the lib handle rate limits

I'm curious to know how exactly this library handles rate limits. By that I mean the following questions:

  • How does it set/obtain rate limit information. Is it a hard-coded value it gets from somewhere in the lib itself? Is it retrieved from something like a response header from discord?
  • How exactly does it handle new requests when the current rate limit is used up? Simply queue them for future sending? Print warns?

I'm asking this because a software I use (A plugin) uses this library. And whenever there is a large number of text to be send through a webhook is the WebhookClient encountering 429 errors and prints those in the console:

errors

According to the dev, and from my own checks, is the library used as it should. A single WebhookClient instance is created and used for a single webhook without recreating it during the entire use.

What could be possible causes for this random issue?

Question about your queue

I was reading through WebhookClient and I was wondering after you send the first initial 5 webhooks will it wait to send the 6ith?

I made some mock code

WebhookClient client = WebhookClient.withUrl("WEBHOOK_URL");

for(int i = 0; i < 6; i++) {
   client.send("test #" + i);
}

after the 5th one I get

21:06:15.009 [Webhook-RateLimit Thread WebhookID: WEBHOOK_ID] DEBUG club.minnced.discord.webhook.WebhookClient - Backing off queue for 1999

but the 6ith is never sent? after waiting sometime it never sent so I was wondering if its setup to send after the wait or something.

More examples

I'm looking for more examples in the README to make usage of this library a bit less research heavy. If anyone wants to open a pull request with examples and usage details I would appreciate it.

I'm also happy to take examples that include other libraries like javacord/d4j/jda as long as the used library is clear through the description.

`org.json.JSONException: JSONObject["url"] not found`

[[33m[23:53:34.556][[0;39m [[[35mpool-2-thread-1[[0;39m/[[1;31mERROR[[0;39m] [[36mc.m.d.w.WebhookClient[[0;39m: There was some error while sending a webhook message
org.json.JSONException: JSONObject["url"] not found.
        at org.json.JSONObject.get(JSONObject.java:572)
        at org.json.JSONObject.getString(JSONObject.java:859)
        at club.minnced.discord.webhook.receive.EntityFactory.makeEmbedProvider(EntityFactory.java:176)
        at club.minnced.discord.webhook.receive.EntityFactory.makeEmbed(EntityFactory.java:213)
        at club.minnced.discord.webhook.receive.EntityFactory.convertToList(EntityFactory.java:276)
        at club.minnced.discord.webhook.receive.EntityFactory.makeMessage(EntityFactory.java:257)
        at club.minnced.discord.webhook.WebhookClient.executePair(WebhookClient.java:601)
        at club.minnced.discord.webhook.WebhookClient.drainQueue(WebhookClient.java:561)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
        at java.base/java.lang.Thread.run(Thread.java:832)

My guess: Seems to be related to webhooks with URLs, all webhooks that I'm sending have Twitch URLs.

0.8.2 causing kotlin to be shaded

0.8.2 causes kotlin to be shaded compared to 0.8.0 when I have this plugin as a dependency. Is this intentional? I don't see any reason why kotlin must be shaded.

Process not terminating correctly

Hello,

when using a WebhookClient and sending a message the process does not finish even when calling client.close().
My current code example is:

public class Main {

    public static void main(String[] args) {

	String url = "discord webhook link";

	System.out.println("Before");

        try (WebhookClient client = WebhookClient.withUrl(url)) {
            client.send("Hello World");
        }

        System.out.println("a");

    }
}

The output is

Before
After

but no termination of the program even though it is finished and the WebhookClient should be closed.
When calling a client method that is not send(...), for example isShutdown(), the process terminates as expected with:

Process finished with exit code 0

Other information:
Java 8
Discord-Webhook 0.7.2

Thanks in advance,
Qumo

Sending multiple embeds in one WebhookMesssage

I'm attempting to get a queue-based system up and running, which will collapse all given embeds into one message to send them speedily, but still comply with Discord's rate limits. Essentially, here is where I am at now:

// called every few seconds
fun run(): () -> Unit = {
    if (embeds.isNotEmpty()) {
        val toAdd = mutableListOf<WebhookEmbed>()
        for (i in 0 until 10) {
            val embed = embeds.poll()
            if (embed != null) toAdd.add(embed)
        }
        println("Amount of embeds in toAdd: ${toAdd.size}")
        send(WebhookMessage.embeds(toAdd)) // works perfectly as intended
    }
}

The amount of embeds that are added is the proper amount, and when I later check directly from the WebhookMessage object, I get the proper amount. However, in practice, the amount that is sent per message is only one.

I'm not receiving any errors or anything really indicating something not working correctly. I've tried multiple ways of creating the WebhookMessage object like using the WebhookMessageBuilder to test if the way they were being created could be the cause, but always got the same result so it must be something about how they're being sent.

I absolutely adore this library... it's been real handy for me and my projects and I hope that this can be fixed soon, or that I could be directed to a possible solution. If you require more information or code snippets, don't hesitate to let me know.

X-RateLimit* headers should be optional

Currently, WebhookClient::update0 requires 3 response headers to prevent a NPE.

X-RateLimit-Remaining
X-RateLimit-Limit
X-RateLimit-Reset-After

This causes NullPointerException when pointing to a mock discord server

Easiest solution is to provide a sensible default using response.header(key,value) instead of response.header(key)

Edit or remove some attachments of already sent webhooks.

To add an attachment of a webhook that has already been sent, I can simply use client.edit.
But according to https://discord.com/developers/docs/resources/channel#attachment-object I need to provide a header named attachments to modify or remove existing attachments.
I provided the following headers to leave the attachment with ID '0002' in the webhook '0001' and remove all other attachments, and it worked correctly.

curl -X PATCH \
  -H "Content-Type: application/json" \
  -d '{
    "attachments": [
      {
        "id": "0002"
      }
    ]
  }' \
  'https://discord.com/api/v10/webhooks/<kamilake>/messages/0001'

The code below is my Java implementation.

public class Main {
    public static void main(String[] args) throws Throwable {
        String url = "https://discord.com/api/v10/webhooks/1234/messages/0001";
        String attachmentId = "0002";

        OkHttpClient client = new OkHttpClient();
        String json = "{\"attachments\": [{\"id\": \"" + attachmentId + "\"}]}";
        RequestBody body = RequestBody.create(json, MediaType.get("application/json; charset=utf-8"));
        Request request = new Request.Builder().url(url).patch(body).build();
        Response response = client.newCall(request).execute()
        System.out.println(response.body().string());
    }
}

I couldn't find an API that does the above operation, so I made it myself. Is that implemented in discord-webhooks?

Let me know if it exists! I'm going to fix my bot.
If not.. Shall I make one?

On 0.4.1 WebhookClient cannot be used as a bean in Spring Boot

From the 0.4.1 update on, the WebhookClient cannot be used as a bean anymore in a Spring Boot application because it uses parameters of types of optional dependencies (other discord api libs) that are usually not included in the runtime classpath, so some Spring bean introspection fails.

I can write myself a wrapper fine, so it can be worked around easily, just thought you might want to know.

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'botStatusWebhookClient' defined in class path resource [space/npstr/aki/config/WebhookConfiguration.class]: Post-processing of merged bean definition failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [club.minnced.discord.webhook.WebhookClient] from ClassLoader [org.springframework.boot.loader.LaunchedURLClassLoader@4f2fe9c1]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:571) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$249/000000001102D280.getObject(Unknown Source) ~[na:na]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory$2.resolveCandidate(DefaultListableBeanFactory.java:1765) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1307) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.createOptionalDependency(DefaultListableBeanFactory.java:1768) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1214) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:884) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:788) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	... 42 common frames omitted
Caused by: java.lang.IllegalStateException: Failed to introspect Class [club.minnced.discord.webhook.WebhookClient] from ClassLoader [org.springframework.boot.loader.LaunchedURLClassLoader@4f2fe9c1]
	at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:481) ~[spring-core-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.util.ReflectionUtils.doWithLocalMethods(ReflectionUtils.java:321) ~[spring-core-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.buildLifecycleMetadata(InitDestroyAnnotationBeanPostProcessor.java:232) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.findLifecycleMetadata(InitDestroyAnnotationBeanPostProcessor.java:210) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(InitDestroyAnnotationBeanPostProcessor.java:149) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(CommonAnnotationBeanPostProcessor.java:294) ~[spring-context-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(AbstractAutowireCapableBeanFactory.java:1093) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:568) ~[spring-beans-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	... 55 common frames omitted
Caused by: java.lang.NoClassDefFoundError: discord4j.core.object.entity.Webhook
	at java.base/java.lang.Class.getDeclaredMethodsImpl(Native Method) ~[na:na]
	at java.base/java.lang.Class.getDeclaredMethods(Class.java:1129) ~[na:na]
	at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463) ~[spring-core-5.2.8.RELEASE.jar!/:5.2.8.RELEASE]
	... 62 common frames omitted
Caused by: java.lang.ClassNotFoundException: discord4j.core.object.entity.Webhook
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:605) ~[na:na]
	at java.base/java.lang.ClassLoader.loadClassHelper(ClassLoader.java:1180) ~[na:na]
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:1095) ~[na:na]
	at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:151) ~[aki.jar:na]
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:1078) ~[na:na]
	... 65 common frames omitted

Retrying after 429 when sending a file results in an empty file

When I am hitting the Rate limit and receiving a 429 while sending a message with an attachment, it retries after it but then the file upload breaks, and it results in a 0 byte file in the discord client.

I tested some things and couldn't get it working when hitting the limit although it could be also an issue on discord's side.

Would be glad if somebody could check this behavior too.

cannot enter string in #setTitle

WebhookEmbed embed = new WebhookEmbedBuilder()
     .setThumbnailUrl(headURL)
     .setColor(0x55e9e9)
     .setTitle("This is a title")
     .setDescription("This is a description")
     .build();

returns the error:

Required type: EmbedTitle
Provided: String

shadowJar command not found

Tried to use ./gradlew shadowJar (./ because im on mac) and it failed because shadowJar command was "not found"
Im probably just dumb, but if you could help me I would be very happy
BTW im using this for a minecraft mod, if that info helps.

Catching HttpExceptions in own code

Currently I use this library for logging by sending embeds to specified webhooks. But if the channels get remade and the new webhook link is still not replaced I get the following error.

Sending a webhook message failed with non-OK http response 
club.minnced.discord.webhook.exception.HttpException: Request returned failure 404: {"message": "Unknown Webhook", "code": 10015}
	at club.minnced.discord.webhook.WebhookClient.failure(WebhookClient.java:722)
	at club.minnced.discord.webhook.WebhookClient.executePair(WebhookClient.java:797)
	at club.minnced.discord.webhook.WebhookClient.drainQueue(WebhookClient.java:766)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
	at java.base/java.lang.Thread.run(Thread.java:831)

Is there any way to catch these exceptions or have them not display in the log files?

How do I cast to EmbedAuthor?

Why can't WebhookEmbedBuilder().setAuthor() accept a string? I'm looking at the docs and it must be EmbedAuthor type, I've tried casting but I don't understand how to.

Error in Sending Webhooks

Version :

<dependency>
    <groupId>club.minnced</groupId>
    <artifactId>discord-webhooks</artifactId>
    <version>0.3.2</version>
</dependency>

Environment:
Java 8 with SpringBoot

Code:

WebhookClient.withUrl("https://discordapp.com/api/webhooks/736596600071127061/[REDACTED]")
     .send("Test");

Error:

ERROR 20760 --- [596600071127061] c.minnced.discord.webhook.WebhookClient  : Sending a webhook message failed with non-OK http response

club.minnced.discord.webhook.exception.HttpException: Request returned failure 400: {"code": 50035, "errors": {"allowed_mentions": {"_errors": [{"code": "MODEL_TYPE_CONVERT", "message": "Only dictionaries may be used in a ModelType"}]}}, "message": "Invalid Form Body"}
	at club.minnced.discord.webhook.WebhookClient.failure(WebhookClient.java:375) [discord-webhooks-0.3.2.jar:na]
	at club.minnced.discord.webhook.WebhookClient.executePair(WebhookClient.java:438) [discord-webhooks-0.3.2.jar:na]
	at club.minnced.discord.webhook.WebhookClient.drainQueue(WebhookClient.java:411) [discord-webhooks-0.3.2.jar:na]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_252]
	at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_252]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) ~[na:1.8.0_252]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) ~[na:1.8.0_252]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_252]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_252]
	at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_252]

Support for changing webhook channels

I have been making a bot like tupperBox using JDA and discord-webhooks, that uses webhooks to send messages. The problem is that they are limited to a channel per webhook only, and I would like to reuse the same webhook on multiple channels, by changing the webhook's channel. I have seen no way to achieve this so far...
image

UnsupportedOperationException when sending DataMessages

When trying to send JDA messages that have been constructed using the MessageBuilder, the Webhook client fails with an UnsupportedOperationException.

The main issue seems to be caused by the isEphemeral() method, which is called regardless of the message type. I suggest allowing this attribute only for received messages.

Here's the corresponding stack trace:

java.lang.UnsupportedOperationException: This operation is not supported for Messages that were created by a MessageBuilder!
	at net.dv8tion.jda.internal.entities.DataMessage.unsupported(DataMessage.java:131) ~[JDA-4.4.0_350.jar:4.4.0_350]
	at net.dv8tion.jda.internal.entities.AbstractMessage.isEphemeral(AbstractMessage.java:610) ~[JDA-4.4.0_350.jar:4.4.0_350]
	at club.minnced.discord.webhook.send.WebhookMessageBuilder.fromJDA(WebhookMessageBuilder.java:436) ~[discord-webhooks-0.7.4.jar:?]
	at club.minnced.discord.webhook.external.JDAWebhookClient.send(JDAWebhookClient.java:119) ~[discord-webhooks-0.7.4.jar:?]

Support for component types such as buttons and select menus

Since this library can be used to handle interaction webhooks, it should also support message components. This would need to be added to WebhookMessage and WebhookMessageBuilder. For more details on components, read up here: Message Components.

A possible implementation could look like this:

ActionRow buttons = ActionRow.of(
  Button.primary("custom_id", "Label")
);

WebhookMessage message = new WebookMessageBuilder()
  .addComponents(buttons)
  .build();

ActionRow would be an implementation of LayoutComponent and Button would be an ActionComponent.

interface Component extends JSONString {
  int getType();
}

interface LayoutComponent extends Component {
  List<ActionComponent> getComponents();
}

interface ActionComponent extends Component {
  String getCustomId();
}

Since this library only handles incoming webhooks, events and similar cannot be supported. I'm only looking for the support of sending message components in webhook messages.

Logging and exception improvements

Currently logging is difficult to configure as there are multiple classifications of errors being passed at different times (asynchronously). Exceptions also are also generic, and there is no clear way to handle fairly common use cases such as invalid webhook urls and deleted webhooks.

Current logging:

  1. LOG.debug("Backing off queue for {}", delay);
  2. LOG.error("Sending a webhook message failed with non-OK http response", exception);
  3. LOG.error("Encountered 429, retrying after {}", delay);
  4. LOG.debug("Failed to update buckets due to unsuccessful response with code: {} and body: \n{}", response.code(), new IOUtil.Lazy(() -> new String(IOUtil.readAllBytes(IOUtil.getBody(response)))));
  5. LOG.error("Could not read http response", ex);

My thoughts on each of the log lines:

  1. This is a rate limit error and should mention that you are being rate limited at the bucket level. Suggest WARN level
  2. This should be split up into multiple categories of exception for example Sending a webhook message failed with non-OK http response club.minnced.discord.webhook.exception.HttpException: Request returned failure 404: {"message": "Unknown Webhook", "code": 10015} should be warn level and throw an UnknownWebhookException. Other common errors such as 400 or 500 should also have their own exception classes if you know what causes them. 400 and 500 I would suggest error level.
  3. This is also a rate limit error and should say so explicitly. Suggest warn level
  4. This seems a duplicate of #2
  5. JSONException would seem to be an error in your own client library. IOException I think is fine to throw as is.

Even with these changes, the errors will be intermingled and difficult to separate what to log. For example, most of these errors are actually returned in the Future and should be handled and logged by the clients so you would want to turn these errors off. The rate limit warnings on the other hand are never sent to the client so you need to keep the logs on. Highly suggest refactoring the code into multiple classes with different loggers. Alternatively, make different loggers for each of the classification of errors so that they can be configured properly.

Arguably, client libraries should never log errors and should simply provide callbacks for the client to perform the logging themselves. This may not be practical for the target audience for this library but I did want to put that option out there.

Overall, I love this library and am willing to make these changes in a PR if you wish but at high volume it becomes excessively noisy.

No virtual method put(Ljava/lang/String;Ljava/util/Collection;)Lorg/json/JSONObject

Hello. I get an error when linking the library:

java.lang.NoSuchMethodError: No virtual method put(Ljava/lang/String;Ljava/util/Collection;)Lorg/json/JSONObject; in class Lorg/json/JSONObject; or its super classes (declaration of 'org.json.JSONObject' appears in /apex/com.android.runtime/javalib/core-libart.jar)
        at club.minnced.discord.webhook.send.WebhookEmbed.toJSONString(WebhookEmbed.java:215)
        at club.minnced.discord.webhook.send.WebhookEmbed.toString(WebhookEmbed.java:190)
        at org.json.JSONStringer.value(JSONStringer.java:261)
        at org.json.JSONArray.writeTo(JSONArray.java:616)
        at org.json.JSONStringer.value(JSONStringer.java:242)
        at org.json.JSONObject.writeTo(JSONObject.java:733)
        at org.json.JSONObject.toString(JSONObject.java:701)
        at club.minnced.discord.webhook.send.WebhookMessage.getBody(WebhookMessage.java:354)
        at club.minnced.discord.webhook.WebhookClient.send(WebhookClient.java:304)

WebhookClient process still running after its closure

I tried to run a simple code to test my webhook. I did succeed at sending my message but the program kept running after it and i also received an SLF4 error. dunno why

code:

var url = "my webhook link";
try (WebhookClient webhook = WebhookClient.withUrl(url)) {
  webhook.send("test").thenRun(() -> {
    System.out.println("message sent");
  });
}

output:

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.
message sent

i also tried by explicitly closing the webhookclient without using the try(){ } statement but the issue still persist

I'm using intellij and jdk 11.0.10
library version i'm using is 0.5.5-rc

Embed Issue

it seems that Icon of an EmbedAuthor and EmbedFooter doesn't work properly.
image

I tried many types of image of URL, it still doesn't work.
image

Issue with gradle implementation

Hi,

I recently transferred one of my discord bot projects to gradle. When I try to compile it with the dependencies I get an error.

org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':jar Caused by: org.gradle.api.GradleException: Could not expand ZIP 'C:\Users\drews\.gradle\caches\modules-2\files-2.1\club.minnced\opus-java\1.0.4\596995aaf2f5b5091c4d251fdc11fa62680cc59e\opus-java-1.0.4.pom'' Caused by: java.util.zip.ZipException: archive is not a ZIP archive

It looks like a .pom file (which is for maven) is put into the opus folder and gradle tries to open that like a jar file. I'm not sure if this is something you have control over, but it only happens for this dependency.

[Feature] Allow disabling thread pool shutdown

It would be nice if you could disable the shutdown of the thread pool.

Because for some bots that send a lot of webhooks in many servers, it might be of interest to provide their own custom thread pool as opposed to constantly creating a new one. The only problem is, with the current implementation, the thread pool is always shut down when the webhook client is shut down.

Might PR with changes, since this is pretty simple & easy to do.

Using Components

Hello I've just started using this library, but I don't find anyway of adding Components to the webhook message, is this possible?

I need to add a button to the last sent webhook message, patching last one if it was sent before.

Message is not sent

Hey!
I use the following code, but the message isn't being sent
No errors.

package com.nelmin.friendfinder.api

import club.minnced.discord.webhook.WebhookClient
import club.minnced.discord.webhook.send.WebhookMessageBuilder
import com.nelmin.friendfinder.Secrets

class DiscordWebhook(content: String) {

    init {
        val webhook: WebhookClient = WebhookClient.withUrl(Secrets.DISCORD_WEBHOOK_URL)
        val message = WebhookMessageBuilder()

        message.setUsername("FriendFinder App Errors")
        message.setContent(content)

        webhook.send(message.build())
    }
}

Groovy compatibility broken

I use this lib from Groovy and used 0.1.8 before which worked fine.
Now I updated to 0.8.2 and it is not working anymore due to https://issues.apache.org/jira/browse/GROOVY-11116.
Summarized, having an optional dependency in the signature of a method makes the class unloadable if the dependency is absent.
So currently you can only use this lib from Groovy if you have all the optional dependencies present at runtime.
Would be nice if the WebhookClientBuilder could be split into one class per optional dependency to restore Groovy compatibility.

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.