// Copyright (c) 2015 Christopher "BlayTheNinth" Baker
package net.blay09.mods.eirairc.util;
import net.blay09.mods.eirairc.api.event.FormatMessage;
import net.blay09.mods.eirairc.api.event.FormatNick;
import net.blay09.mods.eirairc.api.irc.IRCChannel;
import net.blay09.mods.eirairc.api.irc.IRCConnection;
import net.blay09.mods.eirairc.api.irc.IRCContext;
import net.blay09.mods.eirairc.api.irc.IRCUser;
import net.blay09.mods.eirairc.config.SharedGlobalConfig;
import net.blay09.mods.eirairc.config.settings.BotSettings;
import net.blay09.mods.eirairc.config.settings.GeneralSettings;
import net.blay09.mods.eirairc.irc.IRCUserImpl;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.event.ClickEvent;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraftforge.common.MinecraftForge;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MessageFormat {
public enum Target {
IRC,
Minecraft
}
public enum Mode {
Message,
Emote,
CTCP
}
private static final Pattern playerTagPattern = Pattern.compile("[\\[][^\\]]+[\\]]");
public static final Pattern urlPattern = Pattern.compile("(?:(https?)://)?([-\\w_\\.]{2,}\\.[a-z]{2,4})(/\\S*)?");
public static final Pattern namePattern = Pattern.compile("@([^ ]+)");
public static String getMessageFormat(IRCContext context, boolean isEmote) {
BotSettings botSettings = ConfigHelper.getBotSettings(context);
if (context instanceof IRCUser) {
if (isEmote) {
return botSettings.getMessageFormat().ircPrivateEmote;
} else {
return botSettings.getMessageFormat().ircPrivateMessage;
}
} else {
if (isEmote) {
return botSettings.getMessageFormat().ircChannelEmote;
} else {
return botSettings.getMessageFormat().ircChannelMessage;
}
}
}
public static IChatComponent createChatComponentForMessage(String message) {
IChatComponent rootComponent = new ChatComponentText("");
StringBuilder buffer = new StringBuilder();
Matcher urlMatcher = urlPattern.matcher(message);
Matcher nameMatcher = namePattern.matcher(message);
int currentIndex = 0;
while(currentIndex < message.length()) {
// Find the next word in the message
int nextWhitespace = message.indexOf(' ', currentIndex);
if(nextWhitespace == -1) {
nextWhitespace = message.length();
}
// Update Matchers to check the correct region
urlMatcher.region(currentIndex, nextWhitespace);
nameMatcher.region(currentIndex, nextWhitespace);
if(urlMatcher.matches()) {
// Flush the buffer
if(buffer.length() > 0) {
rootComponent = appendTextToRoot(rootComponent, buffer.toString());
buffer = new StringBuilder();
}
// Create URL component
String urlText = urlMatcher.group();
ChatComponentText urlComponent = new ChatComponentText(urlText);
// Make sure a protocol is specified for the ClickEvent value to prevent NPE in GuiChat (getScheme())
if(!urlText.startsWith("http://") && !urlText.startsWith("https://")) {
urlText = "http://" + urlText;
}
urlComponent.getChatStyle().setChatClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, urlText));
rootComponent = appendSiblingToRoot(rootComponent, urlComponent);
currentIndex = nextWhitespace;
} else if(nameMatcher.matches()) {
// Flush the buffer
if(buffer.length() > 0) {
rootComponent = appendTextToRoot(rootComponent, buffer.toString());
buffer = new StringBuilder();
}
// Create Name Component
String nameText = nameMatcher.group();
ChatComponentText nameComponent = new ChatComponentText(nameText);
nameComponent.getChatStyle().setItalic(true);
nameComponent.getChatStyle().setChatClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, nameText + " "));
rootComponent = appendSiblingToRoot(rootComponent, nameComponent);
currentIndex = nextWhitespace;
} else {
buffer.append(message.substring(currentIndex, Math.min(message.length(), nextWhitespace + 1)));
currentIndex = nextWhitespace + 1;
}
}
// Flush the buffer
if(buffer.length() > 0) {
rootComponent = appendTextToRoot(rootComponent, buffer.toString());
}
return rootComponent;
}
private static IChatComponent appendSiblingToRoot(IChatComponent root, IChatComponent sibling) {
root.appendSibling(sibling);
return root;
}
private static IChatComponent appendTextToRoot(IChatComponent root, String text) {
FormatMessage emoticons = new FormatMessage(new ChatComponentText(text));
MinecraftForge.EVENT_BUS.post(emoticons);
root.appendSibling(emoticons.component);
return root;
}
public static String filterLinks(String message) {
String[] s = message.split(" ");
StringBuilder sb = new StringBuilder();
for(int i = 0; i < s.length; i++) {
Matcher matcher = urlPattern.matcher(s[i]);
if(i > 0) {
sb.append(" ");
}
sb.append(matcher.replaceAll(I19n.format("eirairc:general.linkRemoved")));
}
return sb.toString();
}
public static String filterPlayerTags(String playerName) {
return playerTagPattern.matcher(playerName).replaceAll("");
}
private static String filterAllowedCharacters(String message) {
StringBuilder sb = new StringBuilder();
char[] charArray = message.toCharArray();
for (char c : charArray) {
if (isAllowedCharacter(c)) {
sb.append(c);
}
}
return sb.toString();
}
public static String formatNick(String nick, IRCContext context, Target target, Mode mode) {
if(target == Target.IRC) {
if(SharedGlobalConfig.hidePlayerTags.get()) {
nick = filterPlayerTags(nick);
}
nick = String.format(ConfigHelper.getBotSettings(context).mcNickFormat.get(), nick);
if(SharedGlobalConfig.preventUserPing.get()) {
nick = nick.substring(0, 1) + '\u0081' + nick.substring(1);
}
}
return nick;
}
public static String formatMessage(String format, IRCContext context, ICommandSender sender, String message, Target target, Mode mode) {
String result = formatChatComponent(format, context, sender, message, target, mode).getUnformattedText();
BotSettings botSettings = ConfigHelper.getBotSettings(context);
if(target == Target.IRC) {
result = IRCFormatting.toIRC(result, !botSettings.convertColors.get());
} else if(target == Target.Minecraft) {
result = IRCFormatting.toMC(result, !botSettings.convertColors.get());
result = filterAllowedCharacters(result);
}
return result;
}
public static IChatComponent formatChatComponent(String format, IRCContext context, ICommandSender sender, String message, Target target, Mode mode) {
IChatComponent root = new ChatComponentText("");
EnumChatFormatting nextColor = null;
StringBuilder sb = new StringBuilder();
int currentIdx = 0;
while(currentIdx < format.length()) {
char c = format.charAt(currentIdx);
if(c == '{') {
int tokenEnd = format.indexOf('}', currentIdx);
if(tokenEnd != -1) {
boolean validToken = true;
String token = format.substring(currentIdx + 1, tokenEnd);
IChatComponent component = null;
switch (token) {
case "SERVER":
component = new ChatComponentText(Utils.getCurrentServerName());
break;
case "USER":
component = new ChatComponentText(sender.getName());
break;
case "CHANNEL":
component = new ChatComponentText(context != null ? context.getName() : "");
break;
case "NICK":
if (sender instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) sender;
component = player.getDisplayName().createCopy();
String displayName = component.getUnformattedText();
displayName = formatNick(displayName, context, target, mode);
component = new ChatComponentText(displayName);
if (mode != Mode.Emote) {
EnumChatFormatting nameColor = IRCFormatting.getColorFormattingForPlayer(player);
if (nameColor != null && nameColor != EnumChatFormatting.WHITE) {
component.getChatStyle().setColor(nameColor);
}
}
} else {
component = new ChatComponentText(sender.getName());
}
break;
case "MESSAGE":
if(message != null) {
BotSettings botSettings = ConfigHelper.getBotSettings(context);
if (target == Target.Minecraft) {
message = IRCFormatting.toMC(message, !botSettings.convertColors.get());
message = filterAllowedCharacters(message);
} else if (target == Target.IRC) {
message = IRCFormatting.toIRC(message, !botSettings.convertColors.get());
}
component = createChatComponentForMessage(message);
} else {
component = new ChatComponentText("");
}
break;
default:
validToken = false;
break;
}
if(validToken) {
if(sb.length() > 0) {
IChatComponent newComponent;
newComponent = new ChatComponentText(sb.toString());
root.appendSibling(newComponent);
if(nextColor != null) {
newComponent.getChatStyle().setColor(nextColor);
}
sb = new StringBuilder();
}
root.appendSibling(component);
if(nextColor != null) {
component.getChatStyle().setColor(nextColor);
}
currentIdx += token.length() + 2;
continue;
}
}
} else if(c == '\u00a7') {
nextColor = IRCFormatting.getColorFromMCColorCode(format.charAt(currentIdx + 1));
currentIdx += 2;
continue;
}
sb.append(c);
currentIdx++;
}
if(sb.length() > 0) {
IChatComponent newComponent;
newComponent = new ChatComponentText(sb.toString());
root.appendSibling(newComponent);
if(nextColor != null) {
newComponent.getChatStyle().setColor(nextColor);
}
}
return root;
}
public static String formatMessage(String format, IRCConnection connection, IRCContext targetContext, IRCUser user, String message, Target target, Mode mode) {
String result = formatChatComponent(format, connection, targetContext, user, message, target, mode).getUnformattedText();
BotSettings botSettings = ConfigHelper.getBotSettings(targetContext);
if(target == Target.IRC) {
result = IRCFormatting.toIRC(result, !botSettings.convertColors.get());
} else if(target == Target.Minecraft) {
result = IRCFormatting.toMC(result, !botSettings.convertColors.get());
result = filterAllowedCharacters(result);
}
return result;
}
public static IChatComponent formatChatComponent(String format, IRCConnection connection, IRCContext targetContext, IRCUser sender, String message, Target target, Mode mode) {
IChatComponent root = new ChatComponentText("");
EnumChatFormatting nextColor = null;
StringBuilder sb = new StringBuilder();
int currentIdx = 0;
while(currentIdx < format.length()) {
char c = format.charAt(currentIdx);
if(c == '{') {
int tokenEnd = format.indexOf('}', currentIdx);
if(tokenEnd != -1) {
boolean validToken = true;
String token = format.substring(currentIdx + 1, tokenEnd);
IChatComponent component = null;
switch (token) {
case "SERVER":
component = new ChatComponentText(connection.getIdentifier());
break;
case "CHANNEL":
component = new ChatComponentText(targetContext != null ? targetContext.getName() : "#");
break;
case "USER":
if (sender != null) {
component = new ChatComponentText(sender.getIdentifier());
} else {
component = new ChatComponentText(connection.getIdentifier());
}
break;
case "NICK":
if (sender != null) {
FormatNick event = new FormatNick(sender, targetContext, new ChatComponentText(((IRCUserImpl) sender).getDisplayName()));
MinecraftForge.EVENT_BUS.post(event);
GeneralSettings settings = ConfigHelper.getGeneralSettings(targetContext);
if(settings.showNameFlags.get() && targetContext instanceof IRCChannel) {
component = new ChatComponentText(sender.getChannelModePrefix((IRCChannel) targetContext));
component.appendSibling(event.component);
} else {
component = event.component;
}
if (mode != Mode.Emote) {
EnumChatFormatting nameColor = IRCFormatting.getColorFormattingForUser(targetContext instanceof IRCChannel ? (IRCChannel) targetContext : null, sender);
if (nameColor != null) {
component.getChatStyle().setColor(nameColor);
}
}
} else {
component = new ChatComponentText(connection.getIdentifier());
}
break;
case "MESSAGE":
if(message != null) {
BotSettings botSettings = ConfigHelper.getBotSettings(targetContext);
if (target == Target.Minecraft) {
message = IRCFormatting.toMC(message, !botSettings.convertColors.get());
message = filterAllowedCharacters(message);
} else if (target == Target.IRC) {
message = IRCFormatting.toIRC(message, !botSettings.convertColors.get());
}
component = createChatComponentForMessage(message);
} else {
component = new ChatComponentText("");
}
break;
default:
validToken = false;
break;
}
if(validToken) {
if(sb.length() > 0) {
IChatComponent newComponent;
newComponent = new ChatComponentText(sb.toString());
root.appendSibling(newComponent);
if(nextColor != null) {
newComponent.getChatStyle().setColor(nextColor);
}
sb = new StringBuilder();
}
root.appendSibling(component);
if(nextColor != null) {
component.getChatStyle().setColor(nextColor);
}
currentIdx += token.length() + 2;
continue;
}
}
} else if(c == '\u00a7') {
if(sb.length() > 0) {
IChatComponent newComponent = new ChatComponentText(sb.toString());
root.appendSibling(newComponent);
if(nextColor != null) {
newComponent.getChatStyle().setColor(nextColor);
}
sb = new StringBuilder();
}
nextColor = IRCFormatting.getColorFromMCColorCode(format.charAt(currentIdx + 1));
currentIdx += 2;
continue;
}
sb.append(c);
currentIdx++;
}
if(sb.length() > 0) {
IChatComponent newComponent;
newComponent = new ChatComponentText(sb.toString());
root.appendSibling(newComponent);
if(nextColor != null) {
newComponent.getChatStyle().setColor(nextColor);
}
}
return root;
}
private static boolean isAllowedCharacter(char c) {
return c >= 32 && c != 127;
}
}