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() {
initComponents();
txtConversation.enableHyperlinksAndCardPopups();
setBackground(new Color(0, 0, 0, CHAT_ALPHA));
changeGUISize(GUISizeHelper.chatFont);
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>/pings</b> - show players and watchers ping")
.append("<br/>").append("<b>/fix</b> - fix frozen game")
.append("<br/>").append("<b>[[card name]]</b> - insert card with popup info")
.toString(),
null, null, MessageType.USER_INFO, ChatMessage.MessageColor.BLUE);
break;

View file

@ -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<String, String> userMessages = new HashMap<>();
private static final HashMap<String, String> lastUserMessages = new HashMap<>(); // user->chat+message
private final ManagerFactory managerFactory;
private final ConcurrentHashMap<UUID, ChatSession> 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> user = managerFactory.userManager().getUserByName(userName);
if (chatSession != null) {
// special commads
if (message.startsWith("\\") || message.startsWith("/")) {
Optional<User> 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<User> 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 = "<font bgcolor=orange color=" + colour + '>' + cardName + "</font>";
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);
}
}