Skip to content

Commit

Permalink
feat: add clan title to chat notification metadata (#635)
Browse files Browse the repository at this point in the history
  • Loading branch information
iProdigy authored Dec 31, 2024
1 parent 155dc3f commit 524d97b
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Unreleased

- Minor: Add clan title to chat notification metadata. (#635)
- Minor: Add `criteria` to loot metadata that indicates why items were included or excluded. (#631)
- Minor: Allow hiding just split private chat from screenshots. (#630)
- Dev: Remove deprecated `getCachedNPCs`/`getCachedPlayers` calls. (#632)
Expand Down
5 changes: 4 additions & 1 deletion docs/json-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,8 @@ JSON for Matching Chat Message Notifications:
"extra": {
"type": "GAMEMESSAGE",
"message": "You've been playing for a while, consider taking a break from your screen.",
"source": null
"source": null,
"clanTitle": null
}
}
```
Expand All @@ -761,6 +762,8 @@ the `extra.source` value is set to the player's name that sent the message.

When `extra.type` is `UNKNOWN`, the `extra.source` value is set to the originating runelite event (e.g., `CommandExecuted`, `NotificationFired`).

When `extra.type` is `CLAN_CHAT` or `CLAN_GUEST_CHAT` or `CLAN_GIM_CHAT` or `CLAN_MESSAGE` (only for user joins), the `extra.clanTitle` object includes the clan rank `id` (integer) and title `name` (string), corresponding to RuneLite's [`ClanTitle` class](https://static.runelite.net/api/runelite-api/net/runelite/api/clan/ClanTitle.html).

### Metadata

JSON for Login Notifications:
Expand Down
48 changes: 46 additions & 2 deletions src/main/java/dinkplugin/notifiers/ChatNotifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@
import lombok.Synchronized;
import net.runelite.api.ChatMessageType;
import net.runelite.api.GameState;
import net.runelite.api.clan.ClanChannel;
import net.runelite.api.clan.ClanChannelMember;
import net.runelite.api.clan.ClanID;
import net.runelite.api.clan.ClanSettings;
import net.runelite.api.clan.ClanTitle;
import net.runelite.api.events.CommandExecuted;
import net.runelite.client.events.NotificationFired;
import net.runelite.client.util.Text;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -56,7 +62,7 @@ public void onConfig(String key, String value) {
public void onMessage(@NotNull ChatMessageType messageType, @Nullable String source, @NotNull String message) {
ChatNotificationType type = ChatNotificationType.MAPPINGS.get(messageType);
if (type != null && config.chatMessageTypes().contains(type) && isEnabled() && hasMatch(message)) {
String cleanSource = source != null ? source.replace('\u00A0', ' ') : null;
String cleanSource = source != null ? Text.sanitize(source) : null;
this.handleNotify(messageType, cleanSource, message);
}
}
Expand All @@ -81,6 +87,7 @@ public void onNotification(NotificationFired event) {
}

private void handleNotify(ChatMessageType type, String source, String message) {
var clanTitle = getClanTitle(type, source, message);
String playerName = Utils.getPlayerName(client);
Template template = Template.builder()
.template(config.chatNotifyMessage())
Expand All @@ -91,7 +98,7 @@ private void handleNotify(ChatMessageType type, String source, String message) {
createMessage(config.chatSendImage(), NotificationBody.builder()
.text(template)
.type(NotificationType.CHAT)
.extra(new ChatNotificationData(type, source, message))
.extra(new ChatNotificationData(type, source, clanTitle, message))
.playerName(playerName)
.build());
}
Expand All @@ -114,6 +121,43 @@ private void loadPatterns(String configValue) {
);
}

@Nullable
private ClanTitle getClanTitle(@NotNull ChatMessageType type, @Nullable String source, @NotNull String message) {
if (type == ChatMessageType.CLAN_MESSAGE && message.endsWith(" has joined.")) {
String name = message.substring(0, message.length() - " has joined.".length());
var title = getClanTitle(ChatMessageType.CLAN_CHAT, name);
return title != null ? title : getClanTitle(ChatMessageType.CLAN_GUEST_CHAT, name);
}
return getClanTitle(type, source);
}

@Nullable
private ClanTitle getClanTitle(@NotNull ChatMessageType type, @Nullable String name) {
if (name == null) return null;

ClanChannel channel;
ClanSettings settings;
if (type == ChatMessageType.CLAN_CHAT) {
channel = client.getClanChannel();
settings = client.getClanSettings();
} else if (type == ChatMessageType.CLAN_GUEST_CHAT) {
channel = client.getGuestClanChannel();
settings = client.getGuestClanSettings();
} else if (type == ChatMessageType.CLAN_GIM_CHAT) {
channel = client.getClanChannel(ClanID.GROUP_IRONMAN);
settings = client.getClanSettings(ClanID.GROUP_IRONMAN);
} else {
channel = null;
settings = null;
}

ClanChannelMember member;
if (channel == null || settings == null || (member = channel.findMember(name)) == null) {
return null;
}
return settings.titleForRank(member.getRank());
}

private static String join(CommandExecuted event) {
StringBuilder sb = new StringBuilder();
sb.append("::").append(event.getCommand());
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/dinkplugin/notifiers/data/ChatNotificationData.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.EqualsAndHashCode;
import lombok.Value;
import net.runelite.api.ChatMessageType;
import net.runelite.api.clan.ClanTitle;
import net.runelite.api.events.ChatMessage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -21,6 +22,15 @@ public class ChatNotificationData extends NotificationData {
@Nullable
String source;

/**
* Clan title of the player that sent the message.
* Only populated when {@link #getType()} is {@link ChatMessageType#CLAN_CHAT}
* or {@link ChatMessageType#CLAN_GUEST_CHAT} or {@link ChatMessageType#CLAN_GIM_CHAT}
* or sometimes {@link ChatMessageType#CLAN_MESSAGE} (for user joins).
*/
@Nullable
ClanTitle clanTitle;

@NotNull
String message;

Expand Down
56 changes: 50 additions & 6 deletions src/test/java/dinkplugin/notifiers/ChatNotifierTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
import dinkplugin.message.templating.Template;
import dinkplugin.notifiers.data.ChatNotificationData;
import net.runelite.api.ChatMessageType;
import net.runelite.api.clan.ClanChannel;
import net.runelite.api.clan.ClanChannelMember;
import net.runelite.api.clan.ClanRank;
import net.runelite.api.clan.ClanSettings;
import net.runelite.api.clan.ClanTitle;
import net.runelite.api.events.CommandExecuted;
import net.runelite.client.config.Notification;
import net.runelite.client.events.NotificationFired;
Expand All @@ -19,6 +24,7 @@

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
Expand All @@ -36,12 +42,13 @@ protected void setUp() {

// config mocks
when(config.notifyChat()).thenReturn(true);
when(config.chatMessageTypes()).thenReturn(EnumSet.of(ChatNotificationType.GAME, ChatNotificationType.COMMAND, ChatNotificationType.RUNELITE));
when(config.chatMessageTypes()).thenReturn(EnumSet.of(ChatNotificationType.GAME, ChatNotificationType.COMMAND, ChatNotificationType.RUNELITE, ChatNotificationType.CLAN));
when(config.chatNotifyMessage()).thenReturn("%USERNAME% received a chat message:\n\n```\n%MESSAGE%\n```");
setPatterns("You will be logged out in approximately 10 minutes.*\n" +
"You will be logged out in approximately 5 minutes.*\n" +
"Dragon impling is in the area\n" +
"::TriggerDink\n");
"::TriggerDink\n" +
"* has joined.");
}

@Test
Expand All @@ -60,7 +67,7 @@ void testNotify() {
.template(PLAYER_NAME + " received a chat message:\n\n```\n" + message + "\n```")
.build()
)
.extra(new ChatNotificationData(ChatMessageType.GAMEMESSAGE, null, message))
.extra(new ChatNotificationData(ChatMessageType.GAMEMESSAGE, null, null, message))
.type(NotificationType.CHAT)
.playerName(PLAYER_NAME)
.build()
Expand All @@ -83,7 +90,7 @@ void testNotifyCommand() {
.template(PLAYER_NAME + " received a chat message:\n\n```\n" + message + "\n```")
.build()
)
.extra(new ChatNotificationData(ChatMessageType.UNKNOWN, "CommandExecuted", message))
.extra(new ChatNotificationData(ChatMessageType.UNKNOWN, "CommandExecuted", null, message))
.type(NotificationType.CHAT)
.playerName(PLAYER_NAME)
.build()
Expand All @@ -106,7 +113,44 @@ void testNotifyTray() {
.template(PLAYER_NAME + " received a chat message:\n\n```\n" + message + "\n```")
.build()
)
.extra(new ChatNotificationData(ChatMessageType.UNKNOWN, "NotificationFired", message))
.extra(new ChatNotificationData(ChatMessageType.UNKNOWN, "NotificationFired", null, message))
.type(NotificationType.CHAT)
.playerName(PLAYER_NAME)
.build()
);
}

@Test
void testNotifyClan() {
// update mocks
var channel = mock(ClanChannel.class);
var settings = mock(ClanSettings.class);
when(client.getClanChannel()).thenReturn(channel);
when(client.getClanSettings()).thenReturn(settings);

var rank = ClanRank.OWNER;
var title = new ClanTitle(rank.getRank(), "Queen");
when(settings.titleForRank(rank)).thenReturn(title);

var member = mock(ClanChannelMember.class);
when(channel.findMember("Poki")).thenReturn(member);
when(member.getRank()).thenReturn(rank);

// fire event
String message = "Poki has joined.";
notifier.onMessage(ChatMessageType.CLAN_MESSAGE, "", message);

// verify notification message
verifyCreateMessage(
PRIMARY_WEBHOOK_URL,
false,
NotificationBody.builder()
.text(
Template.builder()
.template(PLAYER_NAME + " received a chat message:\n\n```\n" + message + "\n```")
.build()
)
.extra(new ChatNotificationData(ChatMessageType.CLAN_MESSAGE, "", title, message))
.type(NotificationType.CHAT)
.playerName(PLAYER_NAME)
.build()
Expand All @@ -119,7 +163,7 @@ void testIgnore() {
notifier.onMessage(ChatMessageType.GAMEMESSAGE, null, "You will be logged out in approximately 30 minutes.");
notifier.onMessage(ChatMessageType.PUBLICCHAT, null, "You will be logged out in approximately 10 minutes.");
notifier.onMessage(ChatMessageType.TRADE, null, "You will be logged out in approximately 10 minutes.");
notifier.onMessage(ChatMessageType.CLAN_MESSAGE, null, "You will be logged out in approximately 10 minutes.");
notifier.onMessage(ChatMessageType.PRIVATECHAT, null, "You will be logged out in approximately 10 minutes.");
notifier.onCommand(new CommandExecuted("You", "will be logged out in approximately 10 minutes.".split(" ")));
notifier.onCommand(new CommandExecuted("DontTriggerDink", new String[0]));
notifier.onNotification(new NotificationFired(Notification.ON, "TriggerDink", TrayIcon.MessageType.INFO));
Expand Down

0 comments on commit 524d97b

Please sign in to comment.