Skip to content

Commit

Permalink
chore: update default thumbnail icon
Browse files Browse the repository at this point in the history
  • Loading branch information
iProdigy committed Mar 1, 2025
1 parent 8f7b393 commit 2838c6d
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 32 deletions.
7 changes: 3 additions & 4 deletions docs/external-plugin-messaging.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ Users can opt-out of this capability by disabling `External Plugin Requests > En
Plugins can request that a screenshot is included with the notification, but users can also opt-out by
disabling `External Plugin Requests > Send Image` (default: on) and `External Plugin Requests > Allow Overriding 'Send Image'` (default: off).

Plugins can include a Discord url for the webhook, otherwise Dink will utilize `External Webhook Override`
Plugins can include urls for the webhook, otherwise Dink will utilize `External Webhook Override`
(or `Primary Webhook URLs` if an external url override is not specified).
If a plugin requests a non-Discord url, it will be ignored in favor of the Dink configuration.

Below we describe the payload structure for how plugins can customize the webhook body and include a full code example to streamline implementation.

Expand All @@ -23,7 +22,7 @@ The `Map<String, Object>` that is supplied to `PluginMessage` will be converted
| ---------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `text` | Y | String | The body text of the notification. This field supports templating (see `replacements` below) and by default `%USERNAME%` is an available replacement. |
| `sourcePlugin` | Y | String | The human-facing name of the plugin submitting the webhook notification request. |
| `urls` | N | String | The Discord URLs that the notification should be sent to (newline separated). |
| `urls` | N | List | A list of `okhttp3.HttpUrl`s that the notification should be sent to. |
| `title` | N | String | The title for the Discord embed. |
| `thumbnail` | N | String | A URL to an image for the thumbnail icon of the Discord embed. |
| `imageRequested` | N | boolean | Whether dink should include a screenshot with the notification. |
Expand All @@ -44,7 +43,7 @@ data.put("title", "An optional embed title for your notification");
data.put("imageRequested", true);
data.put("fields", List.of(new Field("sample key", "sample value")));
data.put("metadata", Map.of("custom key", "custom value"));
data.put("urls", "https://discord.com/api/webhooks/a/b \n https://discord.com/api/webhooks/c/d");
data.put("urls", Arrays.asList(HttpUrl.parse("https://discord.com/api/webhooks/a/b"), HttpUrl.parse("https://discord.com/api/webhooks/c/d")));

PluginMessage dinkRequest = new PluginMessage("dink", "notify", data);
eventBus.post(dinkRequest);
Expand Down
18 changes: 6 additions & 12 deletions src/main/java/dinkplugin/domain/ExternalNotificationRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@

import dinkplugin.message.Field;
import dinkplugin.message.templating.impl.SimpleReplacement;
import dinkplugin.util.ConfigUtil;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import okhttp3.HttpUrl;
import org.jetbrains.annotations.Nullable;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;

@Data
Expand All @@ -26,16 +24,12 @@ public class ExternalNotificationRequest {
private @Nullable List<Field> fields;
private @Nullable Map<String, SimpleReplacement> replacements;
private @Nullable Map<String, Object> metadata;
@Getter(AccessLevel.PRIVATE) // to avoid accidentally using the requested url without sanitization
private @Nullable String urls;
private @Nullable List<HttpUrl> urls;

public String getSanitizedUrls() {
return ConfigUtil.readDelimited(this.urls)
.map(HttpUrl::parse)
.filter(Objects::nonNull)
.filter(url -> "discord.com".equalsIgnoreCase(url.host()))
.map(HttpUrl::toString)
.collect(Collectors.joining("\n"));
public String getUrls(Supplier<String> defaultValue) {
return urls != null
? urls.stream().filter(Objects::nonNull).map(HttpUrl::toString).collect(Collectors.joining("\n"))
: defaultValue.get();
}

public List<Field> getFields() {
Expand Down
15 changes: 9 additions & 6 deletions src/main/java/dinkplugin/notifiers/ExternalPluginNotifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import dinkplugin.message.templating.Replacements;
import dinkplugin.message.templating.Template;
import dinkplugin.notifiers.data.ExternalNotificationData;
import dinkplugin.util.HttpUrlAdapter;
import dinkplugin.util.Utils;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.GameState;
Expand All @@ -21,7 +22,6 @@
@Singleton
public class ExternalPluginNotifier extends BaseNotifier {

@Inject
private Gson gson;

@Override
Expand All @@ -34,6 +34,13 @@ protected String getWebhookUrl() {
return config.externalWebhook();
}

@Inject
void init(Gson gson) {
this.gson = gson.newBuilder()
.registerTypeAdapter(HttpUrl.class, new HttpUrlAdapter())
.create();
}

public void onNotify(Map<String, Object> data) {
if (!isEnabled()) {
log.debug("Skipping requested external dink since notifier is disabled: {}", data);
Expand Down Expand Up @@ -91,11 +98,7 @@ private void handleNotify(ExternalNotificationRequest input) {
.extra(new ExternalNotificationData(input.getSourcePlugin(), input.getFields(), input.getMetadata()))
.build();

var urls = input.getSanitizedUrls();
if (!urls.isEmpty()) {
log.info("{} requested a dink notification to externally-specified url(s)", input.getSourcePlugin());
}

var urls = input.getUrls(this::getWebhookUrl);
createMessage(urls, image, body);
}

Expand Down
20 changes: 20 additions & 0 deletions src/main/java/dinkplugin/util/HttpUrlAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dinkplugin.util;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import okhttp3.HttpUrl;

import java.io.IOException;

public class HttpUrlAdapter extends TypeAdapter<HttpUrl> {
@Override
public void write(JsonWriter out, HttpUrl url) throws IOException {
out.value(url != null ? url.toString() : null);
}

@Override
public HttpUrl read(JsonReader in) throws IOException {
return in.hasNext() ? HttpUrl.parse(in.nextString()) : null;
}
}
26 changes: 16 additions & 10 deletions src/test/java/dinkplugin/notifiers/ExternalPluginNotifierTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import dinkplugin.message.templating.impl.SimpleReplacement;
import dinkplugin.notifiers.data.ExternalNotificationData;
import net.runelite.client.events.PluginMessage;
import okhttp3.HttpUrl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
Expand Down Expand Up @@ -43,11 +44,11 @@ protected void setUp() {
@Test
void testNotify() {
// fire event
plugin.onPluginMessage(new PluginMessage("dink", "notify", samplePayload("https://example.com")));
plugin.onPluginMessage(new PluginMessage("dink", "notify", samplePayload("https://example.com/")));

// verify notification
verifyCreateMessage(
null,
"https://example.com/",
false,
NotificationBody.builder()
.type(NotificationType.EXTERNAL_PLUGIN)
Expand All @@ -67,10 +68,13 @@ void testNotify() {
}

@Test
void testUrl() {
// fire event
void testFallbackUrl() {
// update config mocks
String url = "https://discord.com/example";
plugin.onPluginMessage(new PluginMessage("dink", "notify", samplePayload(url)));
when(config.externalWebhook()).thenReturn(url);

// fire event
plugin.onPluginMessage(new PluginMessage("dink", "notify", samplePayload(null)));

// verify notification
verifyCreateMessage(
Expand Down Expand Up @@ -188,7 +192,7 @@ void testRequestImageDenied() {
@Test
void testIgnoreNamespace() {
// fire event
plugin.onPluginMessage(new PluginMessage("DANK", "notify", samplePayload("https://example.com")));
plugin.onPluginMessage(new PluginMessage("DANK", "notify", samplePayload("https://example.com/")));

// ensure no notification
verify(messageHandler, never()).createMessage(any(), anyBoolean(), any());
Expand All @@ -197,7 +201,7 @@ void testIgnoreNamespace() {
@Test
void testIgnoreName() {
// fire event
plugin.onPluginMessage(new PluginMessage("dink", "donk", samplePayload("https://example.com")));
plugin.onPluginMessage(new PluginMessage("dink", "donk", samplePayload("https://example.com/")));

// ensure no notification
verify(messageHandler, never()).createMessage(any(), anyBoolean(), any());
Expand All @@ -209,13 +213,13 @@ void testDisabled() {
when(config.notifyExternal()).thenReturn(false);

// fire event
plugin.onPluginMessage(new PluginMessage("dink", "notify", samplePayload("https://example.com")));
plugin.onPluginMessage(new PluginMessage("dink", "notify", samplePayload("https://example.com/")));

// ensure no notification
verify(messageHandler, never()).createMessage(any(), anyBoolean(), any());
}

private static Map<String, Object> samplePayload(String urls) {
private static Map<String, Object> samplePayload(String url) {
Map<String, Object> data = new HashMap<>();
data.put("text", "Hello %TARGET% from %USERNAME%");
data.put("title", "My Title");
Expand All @@ -224,7 +228,9 @@ private static Map<String, Object> samplePayload(String urls) {
data.put("replacements", Map.of("%TARGET%", new SimpleReplacement("world", null)));
data.put("metadata", Map.of("hello", "world"));
data.put("sourcePlugin", "MyExternalPlugin");
data.put("urls", urls);
if (url != null) {
data.put("urls", Collections.singletonList(HttpUrl.parse(url)));
}
return data;
}

Expand Down

0 comments on commit 2838c6d

Please sign in to comment.