Repository: runelite/runelite
Pull request #17125 (2023-10-20 02:01:54)

twitch: prepend oauth prefix to token if missing
The `oauth:` prefix is [required](https://dev.twitch.tv/docs/irc/authenticate-bot/#sending-the-pass-and-nick-messages) by twitch. Sometimes users don't include it, which leads to support requests (for example these two from the past month):
* https://discord.com/channels/301497432909414422/442492468307427330/1164687514859216896
* https://discord.com/channels/301497432909414422/334801290645209099/1160017867929047110




Patch:
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java
index 5cd6d26e1..7deeefb88 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java
@@ -484,8 +484,18 @@ public class ItemChargePlugin extends Plugin
 				// Determine if the player mined with a Bracelet of Clay equipped.
 				if (equipment != null && equipment.contains(ItemID.BRACELET_OF_CLAY))
 				{
-					int charges = Ints.constrainToRange(getItemCharges(ItemChargeConfig.KEY_BRACELET_OF_CLAY) - 1, 0, MAX_BRACELET_OF_CLAY_CHARGES);
-					updateBraceletOfClayCharges(charges);
+					final ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY);
+
+					// Charge is not used if only 1 inventory slot is available when mining in Prifddinas
+					boolean ignore = inventory != null
+						&& inventory.count() == 27
+						&& message.equals(BRACELET_OF_CLAY_USE_TEXT_TRAHAEARN);
+
+					if (!ignore)
+					{
+						int charges = Ints.constrainToRange(getItemCharges(ItemChargeConfig.KEY_BRACELET_OF_CLAY) - 1, 0, MAX_BRACELET_OF_CLAY_CHARGES);
+						updateBraceletOfClayCharges(charges);
+					}
 				}
 			}
 			else if (message.equals(BRACELET_OF_CLAY_BREAK_TEXT))
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoConfig.java
index ce0c565c0..d4bc13587 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoConfig.java
@@ -63,4 +63,15 @@ public interface OpponentInfoConfig extends Config
 	{
 		return false;
 	}
+
+	@ConfigItem(
+		keyName = "showOpponentHealthOverlay",
+		name = "Show opponent health overlay",
+		description = "Shows a health bar overlay when a boss health overlay is not present",
+		position = 4
+	)
+	default boolean showOpponentHealthOverlay()
+	{
+		return true;
+	}
 }
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java
index 6b73f4945..fdb6619e8 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java
@@ -140,8 +140,8 @@ class OpponentInfoOverlay extends OverlayPanel
 		}
 
 		// The in-game hp hud is more accurate than our overlay and duplicates all of the information on it,
-		// so hide ours if it is visible.
-		if (opponentName == null || hasHpHud(opponent))
+		// so hide ours if it is visible, or if our overlay is toggled off.
+		if (opponentName == null || hasHpHud(opponent) || !opponentInfoConfig.showOpponentHealthOverlay())
 		{
 			return null;
 		}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/twitch/TwitchPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/TwitchPlugin.java
index a7474fcc2..5461fd241 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/twitch/TwitchPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/twitch/TwitchPlugin.java
@@ -112,13 +112,18 @@ public class TwitchPlugin extends Plugin implements TwitchListener
 			{
 				channel = "#" + channel;
 			}
+			String token = twitchConfig.oauthToken().trim();
+			if (!token.startsWith("oauth:"))
+			{
+				token = "oauth:" + token;
+			}
 
 			log.debug("Connecting to Twitch as {}", twitchConfig.username());
 
 			twitchIRCClient = new TwitchIRCClient(
 				this,
 				twitchConfig.username(),
-				twitchConfig.oauthToken(),
+				token,
 				channel
 			);
 			twitchIRCClient.start();


Test patch:
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java
index 20a1a0554..bd818b476 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/itemcharges/ItemChargePluginTest.java
@@ -44,6 +44,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -99,6 +100,9 @@ public class ItemChargePluginTest
 	private static final String CHECK_BLOOD_ESSENCE = "Your blood essence has 56 charges remaining";
 	private static final String CHECK_BRACELET_OF_CLAY = "You can mine 13 more pieces of soft clay before your bracelet crumbles to dust.";
 	private static final String USED_BRACELET_OF_CLAY = "You manage to mine some clay.";
+
+	private static final String USED_BRACELET_OF_CLAY_TRAHAEARN = "You manage to mine some soft clay.";
+
 	private static final String BREAK_BRACELET_OF_CLAY = "Your bracelet of clay crumbles to dust.";
 
 	@Mock
@@ -503,4 +507,51 @@ public class ItemChargePluginTest
 		itemChargePlugin.onChatMessage(chatMessage);
 		verify(configManager).setRSProfileConfiguration(ItemChargeConfig.GROUP, ItemChargeConfig.KEY_BRACELET_OF_CLAY, 28);
 	}
+	@Test
+	public void testBraceletOfClayUseTrahaearn()
+	{
+		// Set bracelet of clay charges to 13
+		when(configManager.getRSProfileConfiguration(ItemChargeConfig.GROUP, ItemChargeConfig.KEY_BRACELET_OF_CLAY, Integer.class))
+			.thenReturn(13);
+
+		// Equip bracelet of clay
+		ItemContainer equipmentItemContainer = mock(ItemContainer.class);
+		when(client.getItemContainer(InventoryID.EQUIPMENT))
+			.thenReturn(equipmentItemContainer);
+		when(equipmentItemContainer.contains(ItemID.BRACELET_OF_CLAY)).thenReturn(true);
+		when(equipmentItemContainer.getItems()).thenReturn(new Item[0]);
+
+		// Set inventory to 2 free slots
+		ItemContainer inventoryItemContainer = mock(ItemContainer.class);
+		when(inventoryItemContainer.count()).thenReturn(26);
+		when(client.getItemContainer(InventoryID.INVENTORY)).thenReturn(inventoryItemContainer);
+
+		// Verify bracelet of clay charges decreased
+		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", USED_BRACELET_OF_CLAY_TRAHAEARN, "", 0);
+		itemChargePlugin.onChatMessage(chatMessage);
+		verify(configManager).setRSProfileConfiguration(ItemChargeConfig.GROUP, ItemChargeConfig.KEY_BRACELET_OF_CLAY, 12);
+	}
+
+	@Test
+	public void testBraceletOfClayUseTrahaearn1FreeInvSlot()
+	{
+		// Equip bracelet of clay
+		ItemContainer equipmentItemContainer = mock(ItemContainer.class);
+		when(client.getItemContainer(InventoryID.EQUIPMENT))
+			.thenReturn(equipmentItemContainer);
+		when(equipmentItemContainer.contains(ItemID.BRACELET_OF_CLAY))
+			.thenReturn(true);
+
+		// Set inventory to 1 free slots
+		ItemContainer inventoryItemContainer = mock(ItemContainer.class);
+		when(inventoryItemContainer.count())
+			.thenReturn(27);
+		when(client.getItemContainer(InventoryID.INVENTORY))
+			.thenReturn(inventoryItemContainer);
+
+		// Verify bracelet of clay charges were not changed
+		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", USED_BRACELET_OF_CLAY_TRAHAEARN, "", 0);
+		itemChargePlugin.onChatMessage(chatMessage);
+		verify(configManager, Mockito.times(0)).setRSProfileConfiguration(Mockito.anyString(), Mockito.anyString(), Mockito.anyInt());
+	}
 }