diff --git a/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.java b/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.java
index 67dfd47f7d4..ce99d6e805a 100644
--- a/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.java
+++ b/Mage.Client/src/main/java/mage/client/chat/ChatPanelBasic.java
@@ -97,6 +97,7 @@ public class ChatPanelBasic extends javax.swing.JPanel {
*/
public ChatPanelBasic() {
initComponents();
+ txtConversation.enableHyperlinksAndCardPopups();
setBackground(new Color(0, 0, 0, CHAT_ALPHA));
changeGUISize(GUISizeHelper.chatFont);
if (jScrollPaneTxt != null) {
diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java
index ed3bed84271..cc923107c8f 100644
--- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java
+++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java
@@ -533,6 +533,7 @@ public class CallbackClientImpl implements CallbackClient {
.append("
").append("/w username message - send private message to player (whisper)")
.append("
").append("/pings - show players and watchers ping")
.append("
").append("/fix - fix frozen game")
+ .append("
").append("[[card name]] - insert card with popup info")
.toString(),
null, null, MessageType.USER_INFO, ChatMessage.MessageColor.BLUE);
break;
diff --git a/Mage.Server/src/main/java/mage/server/ChatManagerImpl.java b/Mage.Server/src/main/java/mage/server/ChatManagerImpl.java
index 4b9ef0d4b11..bf9adc9a42c 100644
--- a/Mage.Server/src/main/java/mage/server/ChatManagerImpl.java
+++ b/Mage.Server/src/main/java/mage/server/ChatManagerImpl.java
@@ -2,11 +2,13 @@ package mage.server;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
+import mage.constants.Constants;
import mage.game.Game;
import mage.server.exceptions.UserNotFoundException;
import mage.server.game.GameController;
import mage.server.managers.ChatManager;
import mage.server.managers.ManagerFactory;
+import mage.util.GameLog;
import mage.utils.SystemUtil;
import mage.view.ChatMessage.MessageColor;
import mage.view.ChatMessage.MessageType;
@@ -24,12 +26,12 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
- * @author BetaSteward_at_googlemail.com
+ * @author BetaSteward_at_googlemail.com, JayDi85
*/
public class ChatManagerImpl implements ChatManager {
private static final Logger logger = Logger.getLogger(ChatManagerImpl.class);
- private static final HashMap userMessages = new HashMap<>();
+ private static final HashMap lastUserMessages = new HashMap<>(); // user->chat+message
private final ManagerFactory managerFactory;
private final ConcurrentHashMap chatSessions = new ConcurrentHashMap<>();
@@ -59,7 +61,7 @@ public class ChatManagerImpl implements ChatManager {
@Override
public void clearUserMessageStorage() {
- userMessages.clear();
+ lastUserMessages.clear();
}
@Override
@@ -91,14 +93,16 @@ public class ChatManagerImpl implements ChatManager {
}
}
- final Pattern cardNamePattern = Pattern.compile("\\[(.*?)\\]");
+ final Pattern cardNamePattern = Pattern.compile("\\[\\[(.*?)\\]\\]"); // uses scryfall bot style from github: [[name]]
@Override
public void broadcast(UUID chatId, String userName, String message, MessageColor color, boolean withTime, Game game, MessageType messageType, SoundToPlay soundToPlay) {
+ String finalMessage = message;
ChatSession chatSession = chatSessions.get(chatId);
+ Optional user = managerFactory.userManager().getUserByName(userName);
if (chatSession != null) {
+ // special commads
if (message.startsWith("\\") || message.startsWith("/")) {
- Optional user = managerFactory.userManager().getUserByName(userName);
if (user.isPresent()) {
if (!performUserCommand(user.get(), message, chatId, false)) {
performUserCommand(user.get(), message, chatId, true);
@@ -107,66 +111,70 @@ public class ChatManagerImpl implements ChatManager {
}
}
+ // enrich non-game messages with popup hints and other refs
if (messageType != MessageType.GAME && !userName.isEmpty()) {
- Optional u = managerFactory.userManager().getUserByName(userName);
- if (u.isPresent()) {
-
- User user = u.get();
- String messageId = chatId.toString() + message;
- if (messageId.equals(userMessages.get(userName))) {
- // prevent identical messages
- String informUser = "Your message appears to be identical to your last message";
- chatSessions.get(chatId).broadcastInfoToUser(user, informUser);
- return;
- }
-
- if (message.length() > 500) {
- message = message.replaceFirst("^(.{500}).*", "$1 (rest of message truncated)");
- }
-
- String messageToCheck = message;
- Matcher matchPattern = cardNamePattern.matcher(message);
- while (matchPattern.find()) {
- String cardName = matchPattern.group(1);
- CardInfo cardInfo = CardRepository.instance.findPreferredCoreExpansionCard(cardName);
- if (cardInfo != null) {
- String colour = "silver";
- if (cardInfo.getCard().getColor(null).isMulticolored()) {
- colour = "yellow";
- } else if (cardInfo.getCard().getColor(null).isWhite()) {
- colour = "white";
- } else if (cardInfo.getCard().getColor(null).isBlue()) {
- colour = "blue";
- } else if (cardInfo.getCard().getColor(null).isBlack()) {
- colour = "black";
- } else if (cardInfo.getCard().getColor(null).isRed()) {
- colour = "red";
- } else if (cardInfo.getCard().getColor(null).isGreen()) {
- colour = "green";
- }
- messageToCheck = messageToCheck.replaceFirst("\\[" + cardName + "\\]", "card");
- String displayCardName = "";
- message = message.replaceFirst("\\[" + cardName + "\\]", displayCardName);
- }
- }
-
- userMessages.put(userName, messageId);
+ if (user.isPresent()) {
+ // muted by admin
if (messageType == MessageType.TALK) {
- if (user.getChatLockedUntil() != null) {
- if (user.getChatLockedUntil().compareTo(Calendar.getInstance().getTime()) > 0) {
- chatSessions.get(chatId).broadcastInfoToUser(user, "Your chat is muted until " + SystemUtil.dateFormat.format(user.getChatLockedUntil()));
+ if (user.get().getChatLockedUntil() != null) {
+ if (user.get().getChatLockedUntil().compareTo(Calendar.getInstance().getTime()) > 0) {
+ // still muted
+ chatSessions.get(chatId).broadcastInfoToUser(user.get(), "Your chat is muted until "
+ + SystemUtil.dateFormat.format(user.get().getChatLockedUntil()));
return;
} else {
- user.setChatLockedUntil(null);
+ // reset and allows
+ user.get().setChatLockedUntil(null);
}
}
-
}
+ // spam protection
+ String messageId = chatId.toString() + message;
+ if (messageId.equals(lastUserMessages.get(userName))) {
+ chatSessions.get(chatId).broadcastInfoToUser(user.get(), "Ignore duplicated message");
+ return;
+ }
+ lastUserMessages.put(userName, messageId);
+
+ // message limit
+ if (message.length() > Constants.MAX_CHAT_MESSAGE_SIZE) {
+ // too big messages must be ignored in parent code, so only system messages possible here
+ message = message.replaceFirst("^(.{" + Constants.MAX_CHAT_MESSAGE_SIZE + "}).*", "$1 (rest of message truncated)");
+ }
+
+ // insert card names with popup support
+ String messageToCheck = message;
+ Matcher matchPattern = cardNamePattern.matcher(message);
+ int foundCount = 0;
+ while (matchPattern.find()) {
+ // abuse protection
+ foundCount++;
+ if (foundCount > 5) {
+ break;
+ }
+
+ // TODO: add search by lower case and partly text like github bot (maybe slow and abuseable, so test before implement)
+ String searchName = matchPattern.group(1);
+ CardInfo cardInfo = CardRepository.instance.findCard(searchName, true);
+ if (cardInfo != null) {
+ String newMessagePart = GameLog.getColoredObjectIdName(
+ cardInfo.getCard().getColor(),
+ UUID.randomUUID(),
+ cardInfo.getName(),
+ "",
+ cardInfo.getName()
+ );
+ messageToCheck = messageToCheck.replaceFirst("\\[\\[" + searchName + "\\]\\]", "[[" + newMessagePart + "]]");
+ }
+ }
+ finalMessage = messageToCheck;
}
}
- chatSession.broadcast(userName, message, color, withTime, game, messageType, soundToPlay);
+
+ // all other messages
+ chatSession.broadcast(userName, finalMessage, color, withTime, game, messageType, soundToPlay);
}
}