From 4fe37f978cd72a4b31e3efaee73bbdf0fcae3b20 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 9 Dec 2021 18:55:07 -0500 Subject: [PATCH] friendslist: add option to hide per-friend login notifications --- .../main/java/net/runelite/api/Client.java | 2 +- .../net/runelite/api/FriendContainer.java | 37 +++++++++ .../java/net/runelite/api/PendingLogin.java | 45 +++++++++++ .../plugins/friendlist/FriendListConfig.java | 4 +- .../plugins/friendlist/FriendListPlugin.java | 76 +++++++++++++++++++ .../friendlist/FriendListPluginTest.java | 14 +++- 6 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 runelite-api/src/main/java/net/runelite/api/FriendContainer.java create mode 100644 runelite-api/src/main/java/net/runelite/api/PendingLogin.java diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index e53d03ec7f..91cf6a842c 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -1276,7 +1276,7 @@ public interface Client extends GameEngine * * @return */ - NameableContainer getFriendContainer(); + FriendContainer getFriendContainer(); /** * Retrieve the nameable container containing ignores diff --git a/runelite-api/src/main/java/net/runelite/api/FriendContainer.java b/runelite-api/src/main/java/net/runelite/api/FriendContainer.java new file mode 100644 index 0000000000..b288db028b --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/FriendContainer.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api; + +/** + * A nameable container of friends + */ +public interface FriendContainer extends NameableContainer +{ + /** + * Get the recent logins/logouts of friends from the last few seconds + * @return + */ + Deque getPendingLogins(); +} diff --git a/runelite-api/src/main/java/net/runelite/api/PendingLogin.java b/runelite-api/src/main/java/net/runelite/api/PendingLogin.java new file mode 100644 index 0000000000..c7c3a1d1a3 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/PendingLogin.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api; + +/** + * A pending friend login/out. This is used to suppress world hop notifications + * by buffering the pending logins to try to match a pending logout with a pending + * login and cancel both. + */ +public interface PendingLogin +{ + /** + * The name of the player + * @return + */ + String getName(); + + /** + * The world the player logged into, or 0 if a logout. + * @return + */ + short getWorld(); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListConfig.java index 441820c465..9c556c3dec 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListConfig.java @@ -28,9 +28,11 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; -@ConfigGroup("friendlist") +@ConfigGroup(FriendListConfig.GROUP) public interface FriendListConfig extends Config { + String GROUP = "friendlist"; + @ConfigItem( keyName = "showWorldOnLogin", name = "Show world on login", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListPlugin.java index da5ebda38b..b3d85305ce 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/friendlist/FriendListPlugin.java @@ -26,30 +26,40 @@ package net.runelite.client.plugins.friendlist; import com.google.inject.Provides; +import java.time.temporal.ChronoUnit; +import java.util.Iterator; import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; import net.runelite.api.ChatPlayer; import net.runelite.api.Client; import net.runelite.api.Friend; import net.runelite.api.Ignore; +import net.runelite.api.MenuAction; import net.runelite.api.MessageNode; import net.runelite.api.NameableContainer; +import net.runelite.api.PendingLogin; import net.runelite.api.ScriptID; import net.runelite.api.VarPlayer; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.ScriptPostFired; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; 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 net.runelite.client.task.Schedule; import net.runelite.client.util.Text; @PluginDescriptor( name = "Friend List", description = "Add extra information to the friend and ignore lists" ) +@Slf4j public class FriendListPlugin extends Plugin { private static final int MAX_FRIENDS_P2P = 400; @@ -58,12 +68,21 @@ public class FriendListPlugin extends Plugin private static final int MAX_IGNORES_P2P = 400; private static final int MAX_IGNORES_F2P = 100; + private static final String HIDE_NOTIFICATIONS = "Hide notifications"; + private static final String SHOW_NOTIFICATIONS = "Show notifications"; + @Inject private Client client; @Inject private FriendListConfig config; + @Inject + private ConfigManager configManager; + + @Inject + private ChatMessageManager chatMessageManager; + @Provides FriendListConfig getConfig(ConfigManager configManager) { @@ -144,6 +163,46 @@ public class FriendListPlugin extends Plugin } } + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) + { + final int groupId = WidgetInfo.TO_GROUP(event.getActionParam1()); + + // Look for "Message" on friends list + if (groupId == WidgetInfo.FRIENDS_LIST.getGroupId() && event.getOption().equals("Message")) + { + String friend = Text.toJagexName(Text.removeTags(event.getTarget())); + + client.createMenuEntry(-1) + .setOption(isHideNotification(friend) ? SHOW_NOTIFICATIONS : HIDE_NOTIFICATIONS) + .setType(MenuAction.RUNELITE) + .setTarget(event.getTarget()) //Preserve color codes here + .onClick(e -> + { + boolean hidden = isHideNotification(friend); + setHideNotifications(friend, !hidden); + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .value("Login notifications for " + friend + " are now " + (hidden ? "shown." : "hidden.")) + .build()); + }); + } + } + + @Schedule(period = 5, unit = ChronoUnit.SECONDS) + public void setHideNotifications() + { + for (Iterator it = client.getFriendContainer().getPendingLogins().iterator(); it.hasNext(); ) + { + PendingLogin pendingLogin = it.next(); + if (isHideNotification(Text.toJagexName(pendingLogin.getName()))) + { + log.debug("Removing login notification for {}", pendingLogin.getName()); + it.remove(); + } + } + } + private void setFriendsListTitle(final String title) { Widget friendListTitleWidget = client.getWidget(WidgetInfo.FRIEND_CHAT_TITLE); @@ -173,4 +232,21 @@ public class FriendListPlugin extends Plugin return null; } + + private void setHideNotifications(String friend, boolean hide) + { + if (hide) + { + configManager.setConfiguration(FriendListConfig.GROUP, "hidenotification_" + friend, true); + } + else + { + configManager.unsetConfiguration(FriendListConfig.GROUP, "hidenotification_" + friend); + } + } + + private boolean isHideNotification(String friend) + { + return configManager.getConfiguration(FriendListConfig.GROUP, "hidenotification_" + friend, Boolean.class) == Boolean.TRUE; + } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/friendlist/FriendListPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/friendlist/FriendListPluginTest.java index 4b3fd12f32..ee8b485b6a 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/friendlist/FriendListPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/friendlist/FriendListPluginTest.java @@ -31,9 +31,11 @@ import com.google.inject.testing.fieldbinder.BoundFieldModule; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.Friend; +import net.runelite.api.FriendContainer; import net.runelite.api.MessageNode; -import net.runelite.api.NameableContainer; import net.runelite.api.events.ChatMessage; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.config.ConfigManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -54,6 +56,14 @@ public class FriendListPluginTest @Bind private FriendListConfig config; + @Mock + @Bind + private ConfigManager configManager; + + @Mock + @Bind + private ChatMessageManager chatMessageManager; + @Inject private FriendListPlugin friendListPlugin; @@ -78,7 +88,7 @@ public class FriendListPluginTest Friend friend = mock(Friend.class); when(friend.getWorld()).thenReturn(311); - NameableContainer friendContainer = mock(NameableContainer.class); + FriendContainer friendContainer = mock(FriendContainer.class); when(friendContainer.findByName("test\u00a0rsn")).thenReturn(friend); when(client.getFriendContainer()).thenReturn(friendContainer);