GUI: improved chat messages with card names and popup supports (example: [[card name]], closes #4189);

This commit is contained in:
Oleg Agafonov 2023-11-25 16:26:12 +04:00
parent d1f9e9cc90
commit 7475ee922a
3 changed files with 64 additions and 54 deletions

View file

@ -97,6 +97,7 @@ public class ChatPanelBasic extends javax.swing.JPanel {
*/ */
public ChatPanelBasic() { public ChatPanelBasic() {
initComponents(); initComponents();
txtConversation.enableHyperlinksAndCardPopups();
setBackground(new Color(0, 0, 0, CHAT_ALPHA)); setBackground(new Color(0, 0, 0, CHAT_ALPHA));
changeGUISize(GUISizeHelper.chatFont); changeGUISize(GUISizeHelper.chatFont);
if (jScrollPaneTxt != null) { if (jScrollPaneTxt != null) {

View file

@ -533,6 +533,7 @@ public class CallbackClientImpl implements CallbackClient {
.append("<br/>").append("<b>/w username message</b> - send private message to player (whisper)") .append("<br/>").append("<b>/w username message</b> - send private message to player (whisper)")
.append("<br/>").append("<b>/pings</b> - show players and watchers ping") .append("<br/>").append("<b>/pings</b> - show players and watchers ping")
.append("<br/>").append("<b>/fix</b> - fix frozen game") .append("<br/>").append("<b>/fix</b> - fix frozen game")
.append("<br/>").append("<b>[[card name]]</b> - insert card with popup info")
.toString(), .toString(),
null, null, MessageType.USER_INFO, ChatMessage.MessageColor.BLUE); null, null, MessageType.USER_INFO, ChatMessage.MessageColor.BLUE);
break; break;

View file

@ -2,11 +2,13 @@ package mage.server;
import mage.cards.repository.CardInfo; import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository; import mage.cards.repository.CardRepository;
import mage.constants.Constants;
import mage.game.Game; import mage.game.Game;
import mage.server.exceptions.UserNotFoundException; import mage.server.exceptions.UserNotFoundException;
import mage.server.game.GameController; import mage.server.game.GameController;
import mage.server.managers.ChatManager; import mage.server.managers.ChatManager;
import mage.server.managers.ManagerFactory; import mage.server.managers.ManagerFactory;
import mage.util.GameLog;
import mage.utils.SystemUtil; import mage.utils.SystemUtil;
import mage.view.ChatMessage.MessageColor; import mage.view.ChatMessage.MessageColor;
import mage.view.ChatMessage.MessageType; import mage.view.ChatMessage.MessageType;
@ -24,12 +26,12 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com, JayDi85
*/ */
public class ChatManagerImpl implements ChatManager { public class ChatManagerImpl implements ChatManager {
private static final Logger logger = Logger.getLogger(ChatManagerImpl.class); private static final Logger logger = Logger.getLogger(ChatManagerImpl.class);
private static final HashMap<String, String> userMessages = new HashMap<>(); private static final HashMap<String, String> lastUserMessages = new HashMap<>(); // user->chat+message
private final ManagerFactory managerFactory; private final ManagerFactory managerFactory;
private final ConcurrentHashMap<UUID, ChatSession> chatSessions = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, ChatSession> chatSessions = new ConcurrentHashMap<>();
@ -59,7 +61,7 @@ public class ChatManagerImpl implements ChatManager {
@Override @Override
public void clearUserMessageStorage() { public void clearUserMessageStorage() {
userMessages.clear(); lastUserMessages.clear();
} }
@Override @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 @Override
public void broadcast(UUID chatId, String userName, String message, MessageColor color, boolean withTime, Game game, MessageType messageType, SoundToPlay soundToPlay) { 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); ChatSession chatSession = chatSessions.get(chatId);
if (chatSession != null) {
if (message.startsWith("\\") || message.startsWith("/")) {
Optional<User> user = managerFactory.userManager().getUserByName(userName); Optional<User> user = managerFactory.userManager().getUserByName(userName);
if (chatSession != null) {
// special commads
if (message.startsWith("\\") || message.startsWith("/")) {
if (user.isPresent()) { if (user.isPresent()) {
if (!performUserCommand(user.get(), message, chatId, false)) { if (!performUserCommand(user.get(), message, chatId, false)) {
performUserCommand(user.get(), message, chatId, true); 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()) { if (messageType != MessageType.GAME && !userName.isEmpty()) {
Optional<User> u = managerFactory.userManager().getUserByName(userName); if (user.isPresent()) {
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 = "<font bgcolor=orange color=" + colour + '>' + cardName + "</font>";
message = message.replaceFirst("\\[" + cardName + "\\]", displayCardName);
}
}
userMessages.put(userName, messageId);
// muted by admin
if (messageType == MessageType.TALK) { if (messageType == MessageType.TALK) {
if (user.getChatLockedUntil() != null) { if (user.get().getChatLockedUntil() != null) {
if (user.getChatLockedUntil().compareTo(Calendar.getInstance().getTime()) > 0) { if (user.get().getChatLockedUntil().compareTo(Calendar.getInstance().getTime()) > 0) {
chatSessions.get(chatId).broadcastInfoToUser(user, "Your chat is muted until " + SystemUtil.dateFormat.format(user.getChatLockedUntil())); // still muted
chatSessions.get(chatId).broadcastInfoToUser(user.get(), "Your chat is muted until "
+ SystemUtil.dateFormat.format(user.get().getChatLockedUntil()));
return; return;
} else { } 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 + "]]");
} }
} }
chatSession.broadcast(userName, message, color, withTime, game, messageType, soundToPlay); finalMessage = messageToCheck;
}
}
// all other messages
chatSession.broadcast(userName, finalMessage, color, withTime, game, messageType, soundToPlay);
} }
} }