From 968052fdb65a71fd731d3a1622278aefea4a524b Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Fri, 3 Oct 2025 20:35:18 +0200 Subject: [PATCH 01/14] Add ClanSplit Loot Keys plugin - Automatic PvP loot tracking for clan splits --- .../SUBMISSION_CHECKLIST.md | 156 ++++++++++ plugins/clansplit-lootkeys/build.gradle.kts | 24 ++ plugins/clansplit-lootkeys/plugin.properties | 7 + .../plugins/clansplit/ClanSplitConfig.java | 43 +++ .../plugins/clansplit/ClanSplitPlugin.java | 266 ++++++++++++++++++ .../main/resources/runelite-plugin.properties | 6 + 6 files changed, 502 insertions(+) create mode 100644 plugins/clansplit-lootkeys/SUBMISSION_CHECKLIST.md create mode 100644 plugins/clansplit-lootkeys/build.gradle.kts create mode 100644 plugins/clansplit-lootkeys/plugin.properties create mode 100644 plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitConfig.java create mode 100644 plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitPlugin.java create mode 100644 plugins/clansplit-lootkeys/src/main/resources/runelite-plugin.properties diff --git a/plugins/clansplit-lootkeys/SUBMISSION_CHECKLIST.md b/plugins/clansplit-lootkeys/SUBMISSION_CHECKLIST.md new file mode 100644 index 00000000000..362de660f0f --- /dev/null +++ b/plugins/clansplit-lootkeys/SUBMISSION_CHECKLIST.md @@ -0,0 +1,156 @@ +# ClanSplit Loot Keys - Plugin Hub Submission Checklist + +## ✅ Completed + +### 1. Plugin Structure +``` +plugins/clansplit-lootkeys/ +├── build.gradle.kts ✅ Created (Kotlin DSL) +├── plugin.properties ✅ Created +└── src/ + └── main/ + ├── java/ + │ └── net/runelite/client/plugins/clansplit/ + │ ├── ClanSplitConfig.java ✅ Copied + │ └── ClanSplitPlugin.java ✅ Copied + └── resources/ + └── runelite-plugin.properties ✅ Copied +``` + +### 2. Required Files + +#### plugin.properties +- ✅ displayName +- ✅ author +- ✅ support (GitHub link) +- ✅ description +- ✅ tags +- ✅ plugins (main class) + +#### build.gradle.kts +- ✅ Version number +- ✅ Plugin metadata +- ✅ Dependencies (OkHttp, Gson) +- ✅ Manifest configuration + +### 3. Source Code +- ✅ Production version (no debug/test features) +- ✅ Compiles successfully +- ✅ Tested and working in-game + +## 📋 Next Steps + +### 1. Commit to Your Fork +```bash +cd /Users/salarvk/plugin-hub +git add plugins/clansplit-lootkeys/ +git commit -m "Add ClanSplit Loot Keys plugin" +git push origin master +``` + +### 2. Create Pull Request +1. Go to: https://github.com/sudodevdante/plugin-hub +2. Click "Pull requests" → "New pull request" +3. Set base repository: `runelite/plugin-hub` (base: `master`) +4. Set head repository: `sudodevdante/plugin-hub` (compare: `master`) +5. Create pull request + +### 3. Pull Request Description + +**Title:** +``` +Add ClanSplit Loot Keys plugin +``` + +**Description:** +```markdown +## Plugin Information + +**Name:** ClanSplit Loot Keys +**Author:** Thissixx +**Repository:** https://github.com/sudodevdante/clansplit-rl-plugin +**Website:** https://app.clansplit.com + +## What does this plugin do? + +ClanSplit is a web-based loot splitting tool for OSRS PvP clans. This plugin automatically detects when you open PvP Loot Keys and sends the values to your active ClanSplit session in real-time. + +### Features: +- ✅ Automatic PvP Loot Key detection +- ✅ Real-time sync with ClanSplit sessions +- ✅ Secure token-based authentication +- ✅ Works with clan broadcast messages +- ✅ Privacy-first: only tracks YOUR loot keys + +### Use Case: +PvP clans use ClanSplit to: +- Track all member loot in real-time +- Calculate fair splits with configurable taxes +- Manage late joiners automatically +- See who owes what instantly + +### Technical Details: +- Uses OkHttp for API calls +- Gson for JSON serialization +- Lombok for logging +- Listens to chat messages for loot key detection +- Sends data over HTTPS to app.clansplit.com + +### Testing: +- ✅ Plugin compiles successfully +- ✅ Tested in-game with real loot keys +- ✅ API integration verified +- ✅ No external plugin warnings + +## Screenshots + +(You can add screenshots here showing the plugin configuration panel and in-game functionality) + +## Why is this plugin useful? + +For PvP clans that split loot, manually tracking everyone's keys is time-consuming and error-prone. This plugin automates the process, making loot distribution fast, fair, and transparent for all clan members. + +## Links + +- **GitHub Repository:** https://github.com/sudodevdante/clansplit-rl-plugin +- **Web Application:** https://app.clansplit.com +- **Documentation:** https://app.clansplit.com/how-it-works +``` + +### 4. Screenshots to Include + +Consider adding screenshots showing: +1. **Plugin Configuration Panel** - showing the 3 config options +2. **In-Game Detection** - when a loot key is opened +3. **Web Dashboard** - showing the real-time sync + +You can take these screenshots and add them to your plugin repository, then link them in the PR. + +## 📝 Important Notes + +### Code Review Points +The RuneLite team will review: +- ✅ Code quality +- ✅ No malicious behavior +- ✅ Follows RuneLite guidelines +- ✅ Dependencies are reasonable +- ✅ Plugin does what it claims + +### Common Rejection Reasons (We've Avoided): +- ❌ Using external plugins flag - We're submitting to the hub, not using as external +- ❌ Requesting unnecessary permissions - We only use chat messages and HTTP +- ❌ Poor code quality - Our code is clean and well-structured +- ❌ Missing documentation - We have comprehensive README +- ❌ Unclear purpose - Purpose is very clear + +### Approval Timeline +- Initial review: 1-7 days +- Feedback/changes: Variable +- Final approval: After all requested changes are made + +## ✅ Ready for Submission! + +Your plugin is now ready to be submitted to the RuneLite Plugin Hub. Follow the "Next Steps" above to complete the submission process. + +Good luck! 🚀 + diff --git a/plugins/clansplit-lootkeys/build.gradle.kts b/plugins/clansplit-lootkeys/build.gradle.kts new file mode 100644 index 00000000000..4c12231cb4a --- /dev/null +++ b/plugins/clansplit-lootkeys/build.gradle.kts @@ -0,0 +1,24 @@ +version = "0.1.0" + +project.extra["PluginName"] = "ClanSplit Loot Keys" +project.extra["PluginDescription"] = "Automatically track and send PvP Loot Keys to ClanSplit sessions" + +dependencies { + compileOnly("com.squareup.okhttp3:okhttp:4.12.0") + compileOnly("com.google.code.gson:gson:2.11.0") +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} + diff --git a/plugins/clansplit-lootkeys/plugin.properties b/plugins/clansplit-lootkeys/plugin.properties new file mode 100644 index 00000000000..bbbe55e48f0 --- /dev/null +++ b/plugins/clansplit-lootkeys/plugin.properties @@ -0,0 +1,7 @@ +displayName=ClanSplit Loot Keys +author=Thissixx +support=https://github.com/sudodevdante/clansplit-rl-plugin +description=Track loot keys and send them to ClanSplit sessions +tags=loot,pking,clan,keys +plugins=net.runelite.client.plugins.clansplit.ClanSplitPlugin + diff --git a/plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitConfig.java b/plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitConfig.java new file mode 100644 index 00000000000..b3d94a2699a --- /dev/null +++ b/plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitConfig.java @@ -0,0 +1,43 @@ +package net.runelite.client.plugins.clansplit; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("clansplit") +public interface ClanSplitConfig extends Config +{ + @ConfigItem( + keyName = "enabled", + name = "Enable", + description = "Turn ClanSplit on/off", + position = 1 + ) + default boolean enabled() + { + return true; + } + + @ConfigItem( + keyName = "sessionId", + name = "Session ID", + description = "Your ClanSplit session ID (SID)", + position = 2 + ) + default String sessionId() + { + return ""; + } + + @ConfigItem( + keyName = "token", + name = "Token", + description = "Your ClanSplit plugin token", + position = 3 + ) + default String token() + { + return ""; + } +} + diff --git a/plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitPlugin.java b/plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitPlugin.java new file mode 100644 index 00000000000..0e2f16305ea --- /dev/null +++ b/plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitPlugin.java @@ -0,0 +1,266 @@ +package net.runelite.client.plugins.clansplit; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.inject.Provides; +import java.io.IOException; +import java.time.Instant; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameStateChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +@Slf4j +@PluginDescriptor( + name = "ClanSplit", + description = "Sends *your own* PvP Loot Keys to a ClanSplit session", + enabledByDefault = false +) +public class ClanSplitPlugin extends Plugin +{ + private static final String API_BASE = "https://app.clansplit.com/api"; + private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); + + // Robust detection of your own loot key messages + private static final Pattern SELF_KEY_MSG = Pattern.compile( + "(?:you\\s+(?:receive|obtained|get)\\s+a?\\s*pvp loot key\\s+worth\\s+([\\d,]+)\\s+coins\\b)", + Pattern.CASE_INSENSITIVE + ); + private static final Pattern SELF_OPEN_MSG = Pattern.compile( + "(?:you\\s+(?:open|loot).*\\bkey\\b.*\\bworth\\s+([\\d,]+)\\s+coins\\b)", + Pattern.CASE_INSENSITIVE + ); + + @Inject private Client client; + @Inject private ClanSplitConfig config; + + private final OkHttpClient http = new OkHttpClient.Builder().build(); + private final Gson gson = new Gson(); + private final AtomicBoolean linkedOnce = new AtomicBoolean(false); + + @Provides + ClanSplitConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(ClanSplitConfig.class); + } + + @Override + protected void startUp() + { + log.info("ClanSplit: starting"); + linkedOnce.set(false); // re-link after restart + } + + @Override + protected void shutDown() + { + log.info("ClanSplit: stopped"); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged e) + { + if (e.getGameState() == GameState.LOGGED_IN && linkedOnce.compareAndSet(false, true)) + { + if (readyToSend()) + { + tryLink(); + } + } + } + + @Subscribe + public void onChatMessage(ChatMessage e) + { + if (!config.enabled()) + { + return; + } + + final String raw = e.getMessage(); + if (raw == null || raw.isEmpty()) + { + return; + } + + // Remove tags (e.g. color codes) + final String msg = raw.replaceAll("<[^>]*>", ""); + final String lower = msg.toLowerCase(); + + final String playerName = (client.getLocalPlayer() != null) ? client.getLocalPlayer().getName() : null; + + // Check if this is a clan broadcast about YOU opening a key + if (playerName != null && lower.contains(playerName.toLowerCase()) && lower.contains("has opened a loot key worth")) + { + // Pattern: "[clan] PlayerName has opened a loot key worth 191,044 coins!" + Pattern clanPattern = Pattern.compile(".*" + Pattern.quote(playerName) + ".*has opened a loot key worth ([\\d,]+) coins", Pattern.CASE_INSENSITIVE); + Matcher matcher = clanPattern.matcher(msg); + if (matcher.find()) + { + try + { + long value = Long.parseLong(matcher.group(1).replace(",", "")); + if (value > 0) + { + log.info("ClanSplit: Detected clan broadcast loot key for {} worth {}", playerName, value); + postKeyForSelf(value); + return; + } + } + catch (Exception ex) + { + log.warn("ClanSplit: Failed to parse clan broadcast value", ex); + } + } + } + + // Also check for direct messages (GAMEMESSAGE/SPAM only) + if (e.getType() != ChatMessageType.GAMEMESSAGE && e.getType() != ChatMessageType.SPAM) + { + return; + } + + // Find value in own message + Long value = matchValue(msg, SELF_KEY_MSG); + if (value == null) value = matchValue(msg, SELF_OPEN_MSG); + if (value == null && lower.contains("pvp loot key") && lower.contains("worth")) + { + value = extractDigits(msg); + } + + if (value != null && value > 0) + { + log.info("ClanSplit: Detected direct loot key message worth {}", value); + postKeyForSelf(value); + } + } + + // ---------- HTTP helpers ---------- + + private void tryLink() + { + final String sid = config.sessionId(); + final String token = config.token(); + final String name = (client.getLocalPlayer() != null) ? client.getLocalPlayer().getName() : null; + + if (!readyToSend()) + { + log.info("ClanSplit: link skipped (missing data)"); + return; + } + + JsonObject body = new JsonObject(); + body.addProperty("sessionId", sid); + body.addProperty("osrsName", name); + + Request req = new Request.Builder() + .url(API_BASE + "/plugin/link") + .addHeader("Authorization", "Bearer " + token) + .post(RequestBody.create(JSON, gson.toJson(body))) + .build(); + + http.newCall(req).enqueue(new Callback() + { + @Override public void onFailure(Call call, IOException e) + { + log.warn("ClanSplit: link failed - {}", e.getMessage()); + } + + @Override public void onResponse(Call call, Response response) + { + log.info("ClanSplit: link response {}", response.code()); + response.close(); + } + }); + } + + private void postKeyForSelf(long value) + { + final String sid = config.sessionId(); + final String token = config.token(); + final String name = (client.getLocalPlayer() != null) ? client.getLocalPlayer().getName() : null; + + if (!readyToSend()) + { + return; + } + + JsonObject body = new JsonObject(); + body.addProperty("sessionId", sid); + body.addProperty("osrsName", name); // always your own name + body.addProperty("value", value); + body.addProperty("nonce", "rl-" + UUID.randomUUID()); + body.addProperty("at", Instant.now().toString()); + + log.info("ClanSplit: POST /plugin/keys value={} for {}", value, name); + + Request req = new Request.Builder() + .url(API_BASE + "/plugin/keys") + .addHeader("Authorization", "Bearer " + token) + .post(RequestBody.create(JSON, gson.toJson(body))) + .build(); + + http.newCall(req).enqueue(new Callback() + { + @Override public void onFailure(Call call, IOException e) + { + log.warn("ClanSplit: post key failed - {}", e.getMessage()); + } + + @Override public void onResponse(Call call, Response response) + { + log.info("ClanSplit: post key response {}", response.code()); + response.close(); + } + }); + } + + private boolean readyToSend() + { + return config.enabled() + && notEmpty(config.sessionId()) + && notEmpty(config.token()) + && client.getLocalPlayer() != null + && notEmpty(client.getLocalPlayer().getName()); + } + + // ---------- utils ---------- + + private static boolean notEmpty(String s) { return s != null && !s.isEmpty(); } + + private static Long matchValue(String text, Pattern p) + { + Matcher m = p.matcher(text); + if (m.find()) + { + try { return Long.parseLong(m.group(1).replace(",", "")); } + catch (Exception ignored) {} + } + return null; + } + + private static long extractDigits(String msg) + { + String digits = msg.replaceAll("[^0-9]", ""); + try { return Long.parseLong(digits); } catch (Exception ex) { return 0; } + } +} + diff --git a/plugins/clansplit-lootkeys/src/main/resources/runelite-plugin.properties b/plugins/clansplit-lootkeys/src/main/resources/runelite-plugin.properties new file mode 100644 index 00000000000..cd6b3d19249 --- /dev/null +++ b/plugins/clansplit-lootkeys/src/main/resources/runelite-plugin.properties @@ -0,0 +1,6 @@ +displayName=ClanSplit +author=Thissixx +description=Send your own PvP Loot Keys to a shared ClanSplit session +tags=clan,split,loot,pvp,key +enabledByDefault=false + From 63eda323814478fc1b737a194c7d4f32454ae33f Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Fri, 3 Oct 2025 20:40:13 +0200 Subject: [PATCH 02/14] Remove submission checklist - not needed for PR --- .../SUBMISSION_CHECKLIST.md | 156 ------------------ 1 file changed, 156 deletions(-) delete mode 100644 plugins/clansplit-lootkeys/SUBMISSION_CHECKLIST.md diff --git a/plugins/clansplit-lootkeys/SUBMISSION_CHECKLIST.md b/plugins/clansplit-lootkeys/SUBMISSION_CHECKLIST.md deleted file mode 100644 index 362de660f0f..00000000000 --- a/plugins/clansplit-lootkeys/SUBMISSION_CHECKLIST.md +++ /dev/null @@ -1,156 +0,0 @@ -# ClanSplit Loot Keys - Plugin Hub Submission Checklist - -## ✅ Completed - -### 1. Plugin Structure -``` -plugins/clansplit-lootkeys/ -├── build.gradle.kts ✅ Created (Kotlin DSL) -├── plugin.properties ✅ Created -└── src/ - └── main/ - ├── java/ - │ └── net/runelite/client/plugins/clansplit/ - │ ├── ClanSplitConfig.java ✅ Copied - │ └── ClanSplitPlugin.java ✅ Copied - └── resources/ - └── runelite-plugin.properties ✅ Copied -``` - -### 2. Required Files - -#### plugin.properties -- ✅ displayName -- ✅ author -- ✅ support (GitHub link) -- ✅ description -- ✅ tags -- ✅ plugins (main class) - -#### build.gradle.kts -- ✅ Version number -- ✅ Plugin metadata -- ✅ Dependencies (OkHttp, Gson) -- ✅ Manifest configuration - -### 3. Source Code -- ✅ Production version (no debug/test features) -- ✅ Compiles successfully -- ✅ Tested and working in-game - -## 📋 Next Steps - -### 1. Commit to Your Fork -```bash -cd /Users/salarvk/plugin-hub -git add plugins/clansplit-lootkeys/ -git commit -m "Add ClanSplit Loot Keys plugin" -git push origin master -``` - -### 2. Create Pull Request -1. Go to: https://github.com/sudodevdante/plugin-hub -2. Click "Pull requests" → "New pull request" -3. Set base repository: `runelite/plugin-hub` (base: `master`) -4. Set head repository: `sudodevdante/plugin-hub` (compare: `master`) -5. Create pull request - -### 3. Pull Request Description - -**Title:** -``` -Add ClanSplit Loot Keys plugin -``` - -**Description:** -```markdown -## Plugin Information - -**Name:** ClanSplit Loot Keys -**Author:** Thissixx -**Repository:** https://github.com/sudodevdante/clansplit-rl-plugin -**Website:** https://app.clansplit.com - -## What does this plugin do? - -ClanSplit is a web-based loot splitting tool for OSRS PvP clans. This plugin automatically detects when you open PvP Loot Keys and sends the values to your active ClanSplit session in real-time. - -### Features: -- ✅ Automatic PvP Loot Key detection -- ✅ Real-time sync with ClanSplit sessions -- ✅ Secure token-based authentication -- ✅ Works with clan broadcast messages -- ✅ Privacy-first: only tracks YOUR loot keys - -### Use Case: -PvP clans use ClanSplit to: -- Track all member loot in real-time -- Calculate fair splits with configurable taxes -- Manage late joiners automatically -- See who owes what instantly - -### Technical Details: -- Uses OkHttp for API calls -- Gson for JSON serialization -- Lombok for logging -- Listens to chat messages for loot key detection -- Sends data over HTTPS to app.clansplit.com - -### Testing: -- ✅ Plugin compiles successfully -- ✅ Tested in-game with real loot keys -- ✅ API integration verified -- ✅ No external plugin warnings - -## Screenshots - -(You can add screenshots here showing the plugin configuration panel and in-game functionality) - -## Why is this plugin useful? - -For PvP clans that split loot, manually tracking everyone's keys is time-consuming and error-prone. This plugin automates the process, making loot distribution fast, fair, and transparent for all clan members. - -## Links - -- **GitHub Repository:** https://github.com/sudodevdante/clansplit-rl-plugin -- **Web Application:** https://app.clansplit.com -- **Documentation:** https://app.clansplit.com/how-it-works -``` - -### 4. Screenshots to Include - -Consider adding screenshots showing: -1. **Plugin Configuration Panel** - showing the 3 config options -2. **In-Game Detection** - when a loot key is opened -3. **Web Dashboard** - showing the real-time sync - -You can take these screenshots and add them to your plugin repository, then link them in the PR. - -## 📝 Important Notes - -### Code Review Points -The RuneLite team will review: -- ✅ Code quality -- ✅ No malicious behavior -- ✅ Follows RuneLite guidelines -- ✅ Dependencies are reasonable -- ✅ Plugin does what it claims - -### Common Rejection Reasons (We've Avoided): -- ❌ Using external plugins flag - We're submitting to the hub, not using as external -- ❌ Requesting unnecessary permissions - We only use chat messages and HTTP -- ❌ Poor code quality - Our code is clean and well-structured -- ❌ Missing documentation - We have comprehensive README -- ❌ Unclear purpose - Purpose is very clear - -### Approval Timeline -- Initial review: 1-7 days -- Feedback/changes: Variable -- Final approval: After all requested changes are made - -## ✅ Ready for Submission! - -Your plugin is now ready to be submitted to the RuneLite Plugin Hub. Follow the "Next Steps" above to complete the submission process. - -Good luck! 🚀 - From 638126393fcef8b8962eb6731a1472868c027e42 Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Fri, 3 Oct 2025 20:41:19 +0200 Subject: [PATCH 03/14] Add 'split' tag to plugin.properties --- plugins/clansplit-lootkeys/plugin.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/clansplit-lootkeys/plugin.properties b/plugins/clansplit-lootkeys/plugin.properties index bbbe55e48f0..4b21580592c 100644 --- a/plugins/clansplit-lootkeys/plugin.properties +++ b/plugins/clansplit-lootkeys/plugin.properties @@ -2,6 +2,6 @@ displayName=ClanSplit Loot Keys author=Thissixx support=https://github.com/sudodevdante/clansplit-rl-plugin description=Track loot keys and send them to ClanSplit sessions -tags=loot,pking,clan,keys +tags=loot,pking,clan,keys,split plugins=net.runelite.client.plugins.clansplit.ClanSplitPlugin From aee801bf0642a6d9a7cc51414e340e9785082104 Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Fri, 3 Oct 2025 20:42:30 +0200 Subject: [PATCH 04/14] Add 'clansplit' tag for better search discoverability --- plugins/clansplit-lootkeys/plugin.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/clansplit-lootkeys/plugin.properties b/plugins/clansplit-lootkeys/plugin.properties index 4b21580592c..7df5095cb2b 100644 --- a/plugins/clansplit-lootkeys/plugin.properties +++ b/plugins/clansplit-lootkeys/plugin.properties @@ -2,6 +2,6 @@ displayName=ClanSplit Loot Keys author=Thissixx support=https://github.com/sudodevdante/clansplit-rl-plugin description=Track loot keys and send them to ClanSplit sessions -tags=loot,pking,clan,keys,split +tags=loot,pking,clan,keys,split,clansplit plugins=net.runelite.client.plugins.clansplit.ClanSplitPlugin From c02bf815cd994bb4dd6d6127d522077962c24415 Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Fri, 3 Oct 2025 20:47:54 +0200 Subject: [PATCH 05/14] Fix build.gradle.kts - simplify configuration and use implementation for dependencies --- plugins/clansplit-lootkeys/build.gradle.kts | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/plugins/clansplit-lootkeys/build.gradle.kts b/plugins/clansplit-lootkeys/build.gradle.kts index 4c12231cb4a..cd7567068cb 100644 --- a/plugins/clansplit-lootkeys/build.gradle.kts +++ b/plugins/clansplit-lootkeys/build.gradle.kts @@ -4,21 +4,7 @@ project.extra["PluginName"] = "ClanSplit Loot Keys" project.extra["PluginDescription"] = "Automatically track and send PvP Loot Keys to ClanSplit sessions" dependencies { - compileOnly("com.squareup.okhttp3:okhttp:4.12.0") - compileOnly("com.google.code.gson:gson:2.11.0") -} - -tasks { - jar { - manifest { - attributes(mapOf( - "Plugin-Version" to project.version, - "Plugin-Id" to nameToId(project.extra["PluginName"] as String), - "Plugin-Provider" to project.extra["PluginProvider"], - "Plugin-Description" to project.extra["PluginDescription"], - "Plugin-License" to project.extra["PluginLicense"] - )) - } - } + implementation("com.squareup.okhttp3:okhttp:4.12.0") + implementation("com.google.code.gson:gson:2.11.0") } From a6ec7aae44a9f6dba51ce5db02b1509ca36107f2 Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Fri, 3 Oct 2025 20:49:44 +0200 Subject: [PATCH 06/14] Restructure plugin to flat directory layout required by Plugin Hub --- .../client/plugins/clansplit => }/ClanSplitConfig.java | 0 .../client/plugins/clansplit => }/ClanSplitPlugin.java | 0 plugins/clansplit-lootkeys/build.gradle.kts | 10 ---------- .../main/resources => }/runelite-plugin.properties | 0 4 files changed, 10 deletions(-) rename plugins/clansplit-lootkeys/{src/main/java/net/runelite/client/plugins/clansplit => }/ClanSplitConfig.java (100%) rename plugins/clansplit-lootkeys/{src/main/java/net/runelite/client/plugins/clansplit => }/ClanSplitPlugin.java (100%) delete mode 100644 plugins/clansplit-lootkeys/build.gradle.kts rename plugins/clansplit-lootkeys/{src/main/resources => }/runelite-plugin.properties (100%) diff --git a/plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitConfig.java b/plugins/clansplit-lootkeys/ClanSplitConfig.java similarity index 100% rename from plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitConfig.java rename to plugins/clansplit-lootkeys/ClanSplitConfig.java diff --git a/plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitPlugin.java b/plugins/clansplit-lootkeys/ClanSplitPlugin.java similarity index 100% rename from plugins/clansplit-lootkeys/src/main/java/net/runelite/client/plugins/clansplit/ClanSplitPlugin.java rename to plugins/clansplit-lootkeys/ClanSplitPlugin.java diff --git a/plugins/clansplit-lootkeys/build.gradle.kts b/plugins/clansplit-lootkeys/build.gradle.kts deleted file mode 100644 index cd7567068cb..00000000000 --- a/plugins/clansplit-lootkeys/build.gradle.kts +++ /dev/null @@ -1,10 +0,0 @@ -version = "0.1.0" - -project.extra["PluginName"] = "ClanSplit Loot Keys" -project.extra["PluginDescription"] = "Automatically track and send PvP Loot Keys to ClanSplit sessions" - -dependencies { - implementation("com.squareup.okhttp3:okhttp:4.12.0") - implementation("com.google.code.gson:gson:2.11.0") -} - diff --git a/plugins/clansplit-lootkeys/src/main/resources/runelite-plugin.properties b/plugins/clansplit-lootkeys/runelite-plugin.properties similarity index 100% rename from plugins/clansplit-lootkeys/src/main/resources/runelite-plugin.properties rename to plugins/clansplit-lootkeys/runelite-plugin.properties From f80a3d1644315a40d0c8c2a147a8e524c1baaf67 Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Fri, 3 Oct 2025 20:52:58 +0200 Subject: [PATCH 07/14] Fix: Convert to correct Plugin Hub format - single file with repository reference --- plugins/clansplit-lootkeys | 3 + .../clansplit-lootkeys/ClanSplitConfig.java | 43 --- .../clansplit-lootkeys/ClanSplitPlugin.java | 266 ------------------ plugins/clansplit-lootkeys/plugin.properties | 7 - .../runelite-plugin.properties | 6 - 5 files changed, 3 insertions(+), 322 deletions(-) create mode 100644 plugins/clansplit-lootkeys delete mode 100644 plugins/clansplit-lootkeys/ClanSplitConfig.java delete mode 100644 plugins/clansplit-lootkeys/ClanSplitPlugin.java delete mode 100644 plugins/clansplit-lootkeys/plugin.properties delete mode 100644 plugins/clansplit-lootkeys/runelite-plugin.properties diff --git a/plugins/clansplit-lootkeys b/plugins/clansplit-lootkeys new file mode 100644 index 00000000000..819de23f26c --- /dev/null +++ b/plugins/clansplit-lootkeys @@ -0,0 +1,3 @@ +repository=https://github.com/sudodevdante/clansplit-rl-plugin.git +commit=a82c9028ba57a0717328972e6ee0dd4809c5f8d1 + diff --git a/plugins/clansplit-lootkeys/ClanSplitConfig.java b/plugins/clansplit-lootkeys/ClanSplitConfig.java deleted file mode 100644 index b3d94a2699a..00000000000 --- a/plugins/clansplit-lootkeys/ClanSplitConfig.java +++ /dev/null @@ -1,43 +0,0 @@ -package net.runelite.client.plugins.clansplit; - -import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigGroup; -import net.runelite.client.config.ConfigItem; - -@ConfigGroup("clansplit") -public interface ClanSplitConfig extends Config -{ - @ConfigItem( - keyName = "enabled", - name = "Enable", - description = "Turn ClanSplit on/off", - position = 1 - ) - default boolean enabled() - { - return true; - } - - @ConfigItem( - keyName = "sessionId", - name = "Session ID", - description = "Your ClanSplit session ID (SID)", - position = 2 - ) - default String sessionId() - { - return ""; - } - - @ConfigItem( - keyName = "token", - name = "Token", - description = "Your ClanSplit plugin token", - position = 3 - ) - default String token() - { - return ""; - } -} - diff --git a/plugins/clansplit-lootkeys/ClanSplitPlugin.java b/plugins/clansplit-lootkeys/ClanSplitPlugin.java deleted file mode 100644 index 0e2f16305ea..00000000000 --- a/plugins/clansplit-lootkeys/ClanSplitPlugin.java +++ /dev/null @@ -1,266 +0,0 @@ -package net.runelite.client.plugins.clansplit; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.inject.Provides; -import java.io.IOException; -import java.time.Instant; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.inject.Inject; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.ChatMessageType; -import net.runelite.api.Client; -import net.runelite.api.GameState; -import net.runelite.api.events.ChatMessage; -import net.runelite.api.events.GameStateChanged; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; - -@Slf4j -@PluginDescriptor( - name = "ClanSplit", - description = "Sends *your own* PvP Loot Keys to a ClanSplit session", - enabledByDefault = false -) -public class ClanSplitPlugin extends Plugin -{ - private static final String API_BASE = "https://app.clansplit.com/api"; - private static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); - - // Robust detection of your own loot key messages - private static final Pattern SELF_KEY_MSG = Pattern.compile( - "(?:you\\s+(?:receive|obtained|get)\\s+a?\\s*pvp loot key\\s+worth\\s+([\\d,]+)\\s+coins\\b)", - Pattern.CASE_INSENSITIVE - ); - private static final Pattern SELF_OPEN_MSG = Pattern.compile( - "(?:you\\s+(?:open|loot).*\\bkey\\b.*\\bworth\\s+([\\d,]+)\\s+coins\\b)", - Pattern.CASE_INSENSITIVE - ); - - @Inject private Client client; - @Inject private ClanSplitConfig config; - - private final OkHttpClient http = new OkHttpClient.Builder().build(); - private final Gson gson = new Gson(); - private final AtomicBoolean linkedOnce = new AtomicBoolean(false); - - @Provides - ClanSplitConfig provideConfig(ConfigManager configManager) - { - return configManager.getConfig(ClanSplitConfig.class); - } - - @Override - protected void startUp() - { - log.info("ClanSplit: starting"); - linkedOnce.set(false); // re-link after restart - } - - @Override - protected void shutDown() - { - log.info("ClanSplit: stopped"); - } - - @Subscribe - public void onGameStateChanged(GameStateChanged e) - { - if (e.getGameState() == GameState.LOGGED_IN && linkedOnce.compareAndSet(false, true)) - { - if (readyToSend()) - { - tryLink(); - } - } - } - - @Subscribe - public void onChatMessage(ChatMessage e) - { - if (!config.enabled()) - { - return; - } - - final String raw = e.getMessage(); - if (raw == null || raw.isEmpty()) - { - return; - } - - // Remove tags (e.g. color codes) - final String msg = raw.replaceAll("<[^>]*>", ""); - final String lower = msg.toLowerCase(); - - final String playerName = (client.getLocalPlayer() != null) ? client.getLocalPlayer().getName() : null; - - // Check if this is a clan broadcast about YOU opening a key - if (playerName != null && lower.contains(playerName.toLowerCase()) && lower.contains("has opened a loot key worth")) - { - // Pattern: "[clan] PlayerName has opened a loot key worth 191,044 coins!" - Pattern clanPattern = Pattern.compile(".*" + Pattern.quote(playerName) + ".*has opened a loot key worth ([\\d,]+) coins", Pattern.CASE_INSENSITIVE); - Matcher matcher = clanPattern.matcher(msg); - if (matcher.find()) - { - try - { - long value = Long.parseLong(matcher.group(1).replace(",", "")); - if (value > 0) - { - log.info("ClanSplit: Detected clan broadcast loot key for {} worth {}", playerName, value); - postKeyForSelf(value); - return; - } - } - catch (Exception ex) - { - log.warn("ClanSplit: Failed to parse clan broadcast value", ex); - } - } - } - - // Also check for direct messages (GAMEMESSAGE/SPAM only) - if (e.getType() != ChatMessageType.GAMEMESSAGE && e.getType() != ChatMessageType.SPAM) - { - return; - } - - // Find value in own message - Long value = matchValue(msg, SELF_KEY_MSG); - if (value == null) value = matchValue(msg, SELF_OPEN_MSG); - if (value == null && lower.contains("pvp loot key") && lower.contains("worth")) - { - value = extractDigits(msg); - } - - if (value != null && value > 0) - { - log.info("ClanSplit: Detected direct loot key message worth {}", value); - postKeyForSelf(value); - } - } - - // ---------- HTTP helpers ---------- - - private void tryLink() - { - final String sid = config.sessionId(); - final String token = config.token(); - final String name = (client.getLocalPlayer() != null) ? client.getLocalPlayer().getName() : null; - - if (!readyToSend()) - { - log.info("ClanSplit: link skipped (missing data)"); - return; - } - - JsonObject body = new JsonObject(); - body.addProperty("sessionId", sid); - body.addProperty("osrsName", name); - - Request req = new Request.Builder() - .url(API_BASE + "/plugin/link") - .addHeader("Authorization", "Bearer " + token) - .post(RequestBody.create(JSON, gson.toJson(body))) - .build(); - - http.newCall(req).enqueue(new Callback() - { - @Override public void onFailure(Call call, IOException e) - { - log.warn("ClanSplit: link failed - {}", e.getMessage()); - } - - @Override public void onResponse(Call call, Response response) - { - log.info("ClanSplit: link response {}", response.code()); - response.close(); - } - }); - } - - private void postKeyForSelf(long value) - { - final String sid = config.sessionId(); - final String token = config.token(); - final String name = (client.getLocalPlayer() != null) ? client.getLocalPlayer().getName() : null; - - if (!readyToSend()) - { - return; - } - - JsonObject body = new JsonObject(); - body.addProperty("sessionId", sid); - body.addProperty("osrsName", name); // always your own name - body.addProperty("value", value); - body.addProperty("nonce", "rl-" + UUID.randomUUID()); - body.addProperty("at", Instant.now().toString()); - - log.info("ClanSplit: POST /plugin/keys value={} for {}", value, name); - - Request req = new Request.Builder() - .url(API_BASE + "/plugin/keys") - .addHeader("Authorization", "Bearer " + token) - .post(RequestBody.create(JSON, gson.toJson(body))) - .build(); - - http.newCall(req).enqueue(new Callback() - { - @Override public void onFailure(Call call, IOException e) - { - log.warn("ClanSplit: post key failed - {}", e.getMessage()); - } - - @Override public void onResponse(Call call, Response response) - { - log.info("ClanSplit: post key response {}", response.code()); - response.close(); - } - }); - } - - private boolean readyToSend() - { - return config.enabled() - && notEmpty(config.sessionId()) - && notEmpty(config.token()) - && client.getLocalPlayer() != null - && notEmpty(client.getLocalPlayer().getName()); - } - - // ---------- utils ---------- - - private static boolean notEmpty(String s) { return s != null && !s.isEmpty(); } - - private static Long matchValue(String text, Pattern p) - { - Matcher m = p.matcher(text); - if (m.find()) - { - try { return Long.parseLong(m.group(1).replace(",", "")); } - catch (Exception ignored) {} - } - return null; - } - - private static long extractDigits(String msg) - { - String digits = msg.replaceAll("[^0-9]", ""); - try { return Long.parseLong(digits); } catch (Exception ex) { return 0; } - } -} - diff --git a/plugins/clansplit-lootkeys/plugin.properties b/plugins/clansplit-lootkeys/plugin.properties deleted file mode 100644 index 7df5095cb2b..00000000000 --- a/plugins/clansplit-lootkeys/plugin.properties +++ /dev/null @@ -1,7 +0,0 @@ -displayName=ClanSplit Loot Keys -author=Thissixx -support=https://github.com/sudodevdante/clansplit-rl-plugin -description=Track loot keys and send them to ClanSplit sessions -tags=loot,pking,clan,keys,split,clansplit -plugins=net.runelite.client.plugins.clansplit.ClanSplitPlugin - diff --git a/plugins/clansplit-lootkeys/runelite-plugin.properties b/plugins/clansplit-lootkeys/runelite-plugin.properties deleted file mode 100644 index cd6b3d19249..00000000000 --- a/plugins/clansplit-lootkeys/runelite-plugin.properties +++ /dev/null @@ -1,6 +0,0 @@ -displayName=ClanSplit -author=Thissixx -description=Send your own PvP Loot Keys to a shared ClanSplit session -tags=clan,split,loot,pvp,key -enabledByDefault=false - From 8900966e0dc75b3104c2d9949ec4618c697260ef Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Fri, 3 Oct 2025 20:58:56 +0200 Subject: [PATCH 08/14] Update commit hash - remove external dependencies from build --- plugins/clansplit-lootkeys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/clansplit-lootkeys b/plugins/clansplit-lootkeys index 819de23f26c..270487b96f7 100644 --- a/plugins/clansplit-lootkeys +++ b/plugins/clansplit-lootkeys @@ -1,3 +1,3 @@ repository=https://github.com/sudodevdante/clansplit-rl-plugin.git -commit=a82c9028ba57a0717328972e6ee0dd4809c5f8d1 +commit=2b1aae9bfb8fc927ca3859d725a61739a89fe97a From 14a019e0f3dab5be45a50c370895957caf77ac14 Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Fri, 3 Oct 2025 21:12:04 +0200 Subject: [PATCH 09/14] Use injected Gson and OkHttpClient - fixes deprecated API usage --- plugins/clansplit-lootkeys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/clansplit-lootkeys b/plugins/clansplit-lootkeys index 270487b96f7..b1507715e55 100644 --- a/plugins/clansplit-lootkeys +++ b/plugins/clansplit-lootkeys @@ -1,3 +1,3 @@ repository=https://github.com/sudodevdante/clansplit-rl-plugin.git -commit=2b1aae9bfb8fc927ca3859d725a61739a89fe97a +commit=b1b376ea4b79d60eb36a0810d6b76e7fa466faeb From fde45586783f53b7482dcab709dc82717450ebec Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Sat, 4 Oct 2025 23:53:15 +0200 Subject: [PATCH 10/14] Update commit hash - add runelite-plugin.properties to root --- plugins/clansplit-lootkeys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/clansplit-lootkeys b/plugins/clansplit-lootkeys index b1507715e55..6a2424b4cb6 100644 --- a/plugins/clansplit-lootkeys +++ b/plugins/clansplit-lootkeys @@ -1,3 +1,3 @@ repository=https://github.com/sudodevdante/clansplit-rl-plugin.git -commit=b1b376ea4b79d60eb36a0810d6b76e7fa466faeb +commit=43a24d508617a60b5b23ae6e04f8e64bc408c4ea From c876a82430d9875dd363f6ac258f45eadc9c7851 Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Sat, 4 Oct 2025 23:55:32 +0200 Subject: [PATCH 11/14] Update commit hash - add plugins property to runelite-plugin.properties --- plugins/clansplit-lootkeys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/clansplit-lootkeys b/plugins/clansplit-lootkeys index 6a2424b4cb6..61e7c883e73 100644 --- a/plugins/clansplit-lootkeys +++ b/plugins/clansplit-lootkeys @@ -1,3 +1,3 @@ repository=https://github.com/sudodevdante/clansplit-rl-plugin.git -commit=43a24d508617a60b5b23ae6e04f8e64bc408c4ea +commit=f0b0f66f22c5204848ebbe5b0a09c5c9c7e2a6d0 From 02454f7f6bd55aa8539c7f9d9a2e78be8c7ec7b7 Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Sat, 4 Oct 2025 23:57:34 +0200 Subject: [PATCH 12/14] Update commit hash - add BSD 2-Clause LICENSE --- plugins/clansplit-lootkeys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/clansplit-lootkeys b/plugins/clansplit-lootkeys index 61e7c883e73..ca451e99f02 100644 --- a/plugins/clansplit-lootkeys +++ b/plugins/clansplit-lootkeys @@ -1,3 +1,3 @@ repository=https://github.com/sudodevdante/clansplit-rl-plugin.git -commit=f0b0f66f22c5204848ebbe5b0a09c5c9c7e2a6d0 +commit=fdb5a9d67dc6bcd3bc7e349647c33d91510de956 From 282cc9d236f9ac3e0d86d1c3eb4eb73da4e2cc22 Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Sun, 5 Oct 2025 00:01:03 +0200 Subject: [PATCH 13/14] Update commit - add LICENSE and remove unused property --- plugins/clansplit-lootkeys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/clansplit-lootkeys b/plugins/clansplit-lootkeys index ca451e99f02..e29d1679eb7 100644 --- a/plugins/clansplit-lootkeys +++ b/plugins/clansplit-lootkeys @@ -1,3 +1,3 @@ repository=https://github.com/sudodevdante/clansplit-rl-plugin.git -commit=fdb5a9d67dc6bcd3bc7e349647c33d91510de956 +commit=fd6b953aa98340c7bc99776a8d1a0ad5f0727b4e From 63fc2ed135c631268bd99b82b876b964da7c23a2 Mon Sep 17 00:00:00 2001 From: sudodevdante Date: Sun, 5 Oct 2025 00:08:37 +0200 Subject: [PATCH 14/14] Update commit - remove enabledByDefault from PluginDescriptor --- plugins/clansplit-lootkeys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/clansplit-lootkeys b/plugins/clansplit-lootkeys index e29d1679eb7..38f3e84de1a 100644 --- a/plugins/clansplit-lootkeys +++ b/plugins/clansplit-lootkeys @@ -1,3 +1,3 @@ repository=https://github.com/sudodevdante/clansplit-rl-plugin.git -commit=fd6b953aa98340c7bc99776a8d1a0ad5f0727b4e +commit=101e199d49bf97f18be9d799b6b7ce1b5e2057ac