Skip to content

Commit

Permalink
feat: add setting to keep config synced with a custom URL (#488)
Browse files Browse the repository at this point in the history
  • Loading branch information
iProdigy committed Jul 20, 2024
1 parent b38d973 commit 03ef0ab
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 5 deletions.
14 changes: 14 additions & 0 deletions src/main/java/dinkplugin/DinkPluginConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,20 @@ default boolean includeLocation() {
return true;
}

@ConfigItem(
keyName = SettingsManager.DYNAMIC_IMPORT_CONFIG_KEY,
name = "Dynamic Config URL",
description = "Synchronizes your Dink configuration with the specified URL.<br/>" +
"Whenever Dink starts, it imports the config offered by the URL.<br/>" +
"The config applies to all webhooks, so ensure you trust this URL.<br/>" +
"Only one URL is supported",
position = 1017,
section = advancedSection
)
default String dynamicConfigUrl() {
return "";
}

@ConfigItem(
keyName = "discordWebhook", // do not rename; would break old configs
name = "Primary Webhook URLs",
Expand Down
73 changes: 70 additions & 3 deletions src/main/java/dinkplugin/SettingsManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.events.ConfigChanged;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
Expand All @@ -30,6 +37,7 @@
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.swing.SwingUtilities;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -53,6 +61,8 @@
@RequiredArgsConstructor(onConstructor_ = { @Inject })
public class SettingsManager {
public static final String CONFIG_GROUP = "dinkplugin";
public static final String DYNAMIC_IMPORT_CONFIG_KEY = "dynamicConfigUrl";

private static final Set<Integer> PROBLEMATIC_VARBITS;

/**
Expand Down Expand Up @@ -90,6 +100,7 @@ public class SettingsManager {
private final DinkPlugin plugin;
private final DinkPluginConfig config;
private final ConfigManager configManager;
private final OkHttpClient httpClient;

/**
* Check whether a username complies with the configured RSN filter list.
Expand Down Expand Up @@ -128,6 +139,7 @@ public void init() {
.addAll(keysBySection.getOrDefault(DinkPluginConfig.webhookSection.toLowerCase().replace(" ", ""), Collections.emptySet()))
.add("metadataWebhook") // MetaNotifier's configuration is in the Advanced section
.build();
importDynamicConfig(config.dynamicConfigUrl());
}

void onCommand(CommandExecuted event) {
Expand Down Expand Up @@ -186,6 +198,11 @@ void onConfigChanged(ConfigChanged event) {
return;
}

if (DYNAMIC_IMPORT_CONFIG_KEY.equals(key)) {
importDynamicConfig(value);
return;
}

if (value != null && value.isEmpty() && ("embedFooterText".equals(key) || "embedFooterIcon".equals(key) || "deathIgnoredRegions".equals(key) || ChatNotifier.PATTERNS_CONFIG_KEY.equals(key))) {
SwingUtilities.invokeLater(() -> {
if (StringUtils.isEmpty(configManager.getConfiguration(CONFIG_GROUP, key))) {
Expand Down Expand Up @@ -335,6 +352,50 @@ private void setFilteredNames(String configValue) {
log.debug("Updated RSN Filter List to: {}", filteredNames);
}

private void importDynamicConfig(String url) {
if (url == null || url.isBlank()) return;

HttpUrl httpUrl = HttpUrl.parse(url);
if (httpUrl == null) {
plugin.addChatWarning("The specified Dynamic Config URL is invalid");
return;
}

Request request = new Request.Builder().url(httpUrl).get().build();
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) {
ResponseBody body = response.body();
if (body == null) {
plugin.addChatWarning("The specified Dynamic Config URL did not provide any settings to import");
return;
}

Map<String, Object> map;
try {
map = gson.fromJson(body.charStream(), new TypeToken<Map<String, Object>>() {}.getType());
} catch (Exception e) {
log.warn("Could not deserialize dynamic config", e);
plugin.addChatWarning("Failed to parse settings from the Dynamic Config URL");
return;
} finally {
body.close();
}

// prevent never-ending requests if service always yields a different config URL
map.remove(DYNAMIC_IMPORT_CONFIG_KEY);

handleImport(map, true);
}

@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
log.warn("Could not reach dynamic config url", e);
plugin.addChatWarning("Failed to read the specified Dynamic Config URL");
}
});
}

/**
* Exports the full Dink config to a JSON map, excluding empty lists,
* which is copied to the user's clipboard in string form
Expand Down Expand Up @@ -392,15 +453,15 @@ private void importConfig() {
return null;
}
})
.thenAcceptAsync(this::handleImport)
.thenAcceptAsync(m -> handleImport(m, false))
.exceptionally(e -> {
plugin.addChatWarning("Failed to read clipboard");
return null;
});
}

@Synchronized
private void handleImport(Map<String, Object> map) {
private void handleImport(Map<String, Object> map, boolean quiet) {
if (map == null) return;

AtomicInteger numUpdated = new AtomicInteger();
Expand Down Expand Up @@ -462,11 +523,17 @@ private void handleImport(Map<String, Object> map) {
}
});

int count = numUpdated.get();
if (quiet && count <= 0) {
log.debug("Updated 0 config settings from map of size {}", map.size());
return;
}

plugin.addChatSuccess(
String.format(
"Updated %d config settings (from %d total specified in import). " +
"Please close and open the plugin settings panel for these changes to be visually reflected.",
numUpdated.get(),
count,
map.size()
)
);
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/dinkplugin/notifiers/BaseNotifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import dinkplugin.message.NotificationBody;
import dinkplugin.util.WorldUtils;
import net.runelite.api.Client;
import net.runelite.api.Player;
import net.runelite.api.WorldType;
import org.apache.commons.lang3.StringUtils;

Expand Down Expand Up @@ -34,7 +35,8 @@ public boolean isEnabled() {
if (WorldUtils.isIgnoredWorld(world)) {
return false;
}
return settingsManager.isNamePermitted(client.getLocalPlayer().getName());
Player player = client.getLocalPlayer();
return player != null && settingsManager.isNamePermitted(player.getName());
}

protected abstract String getWebhookUrl();
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/dinkplugin/notifiers/MockedNotifierTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ abstract class MockedNotifierTest extends MockedTestBase {
protected ConfigManager configManager = Mockito.mock(ConfigManager.class);

@Bind
protected SettingsManager settingsManager = Mockito.spy(new SettingsManager(gson, client, clientThread, plugin, config, configManager));
protected SettingsManager settingsManager = Mockito.spy(new SettingsManager(gson, client, clientThread, plugin, config, configManager, httpClient));

@Bind
protected DiscordMessageHandler messageHandler = Mockito.spy(new DiscordMessageHandler(gson, client, drawManager, httpClient, config, executor, clientThread, discordService));
Expand Down

0 comments on commit 03ef0ab

Please sign in to comment.