@Subscribe
	public void onChatMessage(ChatMessage chatMessage)
	{
		MessageNode messageNode = chatMessage.getMessageNode();
		boolean update = false;

		switch (chatMessage.getType())
		{
			case TRADEREQ:
				if (chatMessage.getMessage().contains("wishes to trade with you."))
				{
					notifier.notify(config.notifyOnTrade(), chatMessage.getMessage());
				}
				break;
			case CHALREQ_TRADE:
				if (chatMessage.getMessage().contains("wishes to duel with you."))
				{
					notifier.notify(config.notifyOnDuel(), chatMessage.getMessage());
				}
				break;
			case BROADCAST:
				// Some broadcasts have links attached, notated by `|` followed by a number, while others contain color tags.
				// We don't want to see either in the printed notification.
				String broadcast = chatMessage.getMessage();

				int urlTokenIndex = broadcast.lastIndexOf('|');
				if (urlTokenIndex != -1)
				{
					broadcast = broadcast.substring(0, urlTokenIndex);
				}

				notifier.notify(config.notifyOnBroadcast(), Text.removeFormattingTags(broadcast));
				break;
			case PRIVATECHAT:
			case MODPRIVATECHAT:
				notifier.notify(config.notifyOnPM(), Text.removeTags(chatMessage.getName()) + ": " + chatMessage.getMessage());
				break;
			case PRIVATECHATOUT:
			case DIALOG:
			case MESBOX:
				return;
			case MODCHAT:
			case PUBLICCHAT:
			case FRIENDSCHAT:
			case CLAN_CHAT:
			case CLAN_GUEST_CHAT:
			case CLAN_GIM_CHAT:
			case AUTOTYPER:
			case MODAUTOTYPER:
				if (client.getLocalPlayer() != null && Text.toJagexName(Text.removeTags(chatMessage.getName())).equals(client.getLocalPlayer().getName()))
				{
					return;
				}
				break;
			case CONSOLE:
				// Don't notify for notification messages
				if (chatMessage.getName().equals(runeliteTitle))
				{
					return;
				}
				break;
		}

		if (usernameMatcher == null && client.getLocalPlayer() != null && client.getLocalPlayer().getName() != null)
		{
			String username = client.getLocalPlayer().getName();
			String pattern = Arrays.stream(username.split(" "))
				.map(s -> s.isEmpty() ? "" : Pattern.quote(s))
				.collect(Collectors.joining("[\u00a0\u0020]")); // space or nbsp
			usernameMatcher = Pattern.compile("\\b" + pattern + "\\b", Pattern.CASE_INSENSITIVE);
		}

		if (config.highlightOwnName() && usernameMatcher != null)
		{
			final String message = messageNode.getValue();
			Matcher matcher = usernameMatcher.matcher(message);
			if (matcher.find())
			{
				final String username = client.getLocalPlayer().getName();
				StringBuffer stringBuffer = new StringBuffer();
				do
				{
					final int start = matcher.start(); // start not end, since username won't contain a col tag
					final String closeColor = MoreObjects.firstNonNull(
						getLastColor(message.substring(0, start)),
						"<col" + ChatColorType.NORMAL + '>');
					final String replacement = "<col" + ChatColorType.HIGHLIGHT.name() + "><u>" + username + "</u>" + closeColor;
					matcher.appendReplacement(stringBuffer, replacement);
				}
				while (matcher.find());

				matcher.appendTail(stringBuffer);

				messageNode.setValue(stringBuffer.toString());
				update = true;

				if (chatMessage.getType() == ChatMessageType.PUBLICCHAT
					|| chatMessage.getType() == ChatMessageType.PRIVATECHAT
					|| chatMessage.getType() == ChatMessageType.FRIENDSCHAT
					|| chatMessage.getType() == ChatMessageType.MODCHAT
					|| chatMessage.getType() == ChatMessageType.MODPRIVATECHAT
					|| chatMessage.getType() == ChatMessageType.CLAN_CHAT
					|| chatMessage.getType() == ChatMessageType.CLAN_GUEST_CHAT)
				{
					sendNotification(config.notifyOnOwnName(), chatMessage);
				}
			}
		}

		boolean matchesHighlight = false;
		// Get nodeValue to store and update in between the different pattern passes
		// The messageNode value is only set after all patterns have been processed
		String nodeValue = messageNode.getValue();

		for (Pattern pattern : highlightPatterns)
		{
			Matcher matcher = pattern.matcher(nodeValue);
			if (!matcher.find())
			{
				continue;
			}

			StringBuffer stringBuffer = new StringBuffer();

			do
			{
				final int end = matcher.end();
				// Determine the ending color by finding the last color tag up to and
				// including the match.
				final String closeColor = MoreObjects.firstNonNull(
					getLastColor(nodeValue.substring(0, end)),
					"<col" + ChatColorType.NORMAL + '>');
				// Strip color tags from the highlighted region so that it remains highlighted correctly
				final String value = stripColor(matcher.group());

				matcher.appendReplacement(stringBuffer, "<col" + ChatColorType.HIGHLIGHT + '>' + value + closeColor);

				update = true;
				matchesHighlight = true;
			}
			while (matcher.find());

			// Append stringBuffer with remainder of message and update nodeValue
			matcher.appendTail(stringBuffer);
			nodeValue = stringBuffer.toString();
		}

		if (matchesHighlight)
		{
			messageNode.setValue(nodeValue);
			sendNotification(config.notifyOnHighlight(), chatMessage);
		}

		if (update)
		{
			messageNode.setRuneLiteFormatMessage(messageNode.getValue());
		}
	}
@Test
	public void testLocalPlayerSelfMention()
	{
		final String localPlayerName = "Broo klyn";

		MessageNode messageNode = mock(MessageNode.class);

		Player localPlayer = mock(Player.class);
		when(client.getLocalPlayer()).thenReturn(localPlayer);
		when(localPlayer.getName()).thenReturn(localPlayerName);

		lenient().when(config.highlightOwnName()).thenReturn(true);
		lenient().when(messageNode.getValue()).thenReturn("Spread love it's the Broo klyn way");

		ChatMessage chatMessage = new ChatMessage();
		chatMessage.setType(ChatMessageType.PUBLICCHAT);
		chatMessage.setName("Broo\u00a0klyn");
		chatMessage.setMessageNode(messageNode);

		chatNotificationsPlugin.onChatMessage(chatMessage);

		verify(messageNode, times(0)).setValue(any());
	}