package InnaIrcBot.ProvidersConsumers; import InnaIrcBot.Commanders.PrivateMsgCommander; import InnaIrcBot.Config.StorageFile; import InnaIrcBot.GlobalData; import InnaIrcBot.LogDriver.BotDriver; import InnaIrcBot.LogDriver.Worker; import java.io.*; import java.nio.charset.StandardCharsets; import java.time.LocalTime; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; public class SystemConsumer implements Runnable{ private BufferedReader reader; private Worker writerWorker; private String nick; private String serverName; private Map channelsMap; private boolean proxyRequired; private HashMap> proxyAList; private StorageFile storageFile; private PrivateMsgCommander commander; SystemConsumer(BufferedReader streamReader, String userNick, Map map, StorageFile storage) { this.writerWorker = BotDriver.getWorker(storage.getServerName(), "system"); this.nick = userNick; this.serverName = storage.getServerName(); this.channelsMap = map; this.reader = streamReader; this.proxyRequired = false; this.proxyAList = new HashMap<>(); this.storageFile = storage; this.commander = new PrivateMsgCommander(serverName, storageFile.getBotAdministratorPassword()); // Start pre-set channels StringBuilder message = new StringBuilder(); for (String cnl : storageFile.getChannels()) { // TODO: add validation of channels. message.append("JOIN "); message.append(cnl); message.append("\n"); } StreamProvider.writeToStream(serverName,message.toString()); } @Override public void run() { System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] THREAD "+serverName+":[system] started"); // TODO:REMOVE DEBUG setMainRoutine(); for (PrintWriter p :channelsMap.values()) //TODO: check, code duplication. see Data provider constructor p.close(); writerWorker.close(); System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] THREAD "+serverName+":[system] ended"); // TODO:REMOVE DEBUG } private void setMainRoutine(){ String data; String dataStrings[]; try { while ((data = reader.readLine()) != null) { dataStrings = data.split(" ",3); if (proxyRequired) if (getProxy(dataStrings[0], dataStrings[1], dataStrings[2])) continue; if (dataStrings[0].equals("PRIVMSG") && dataStrings[2].indexOf("\u0001") < dataStrings[2].lastIndexOf("\u0001")) replyCTCP(dataStrings[1], dataStrings[2].substring(dataStrings[2].indexOf(":")+1)); else if (Pattern.matches("(^[0-9]{3}$)|(^NICK$)|(^JOIN$)", dataStrings[0])){ handleNumeric(dataStrings[0], dataStrings[1], dataStrings[2]); } else if (dataStrings[0].equals("PRIVMSG")) { commander.receiver(dataStrings[1], dataStrings[2].replaceAll("^.+?:", "").trim()); writerWorker.logAdd("[system]", "PRIVMSG sent to", "commander"); } else writerWorker.logAdd(dataStrings[0], dataStrings[1], dataStrings[2]); // TODO: Track users //System.out.println("System: "+"|"+dataStrings[0]+"|"+dataStrings[1]+"|"+dataStrings[2]+"|"); } } catch (java.io.IOException e){ System.out.println("Internal issue: thread SystemConsumer->run() caused I/O exception."); // TODO: reconnect } } private boolean getProxy(String eventNum, String sender, String message){ //TODO: if can't join: like channel with password if (eventNum.equals("353")) return false; // never mind and let it flows as usual. else { String chan = message.replaceAll("(\\s.?$)|(\\s.+?$)", ""); if (eventNum.equals("QUIT") || eventNum.equals("NICK")) { for (ArrayList key : proxyAList.values()) key.add(eventNum + " " + sender + " " + message); return false; } else if (chan.equals(nick)) return false; else if (proxyAList.keySet().contains(chan)) { proxyAList.get(chan).add(eventNum + " " + sender + " " + message); return true; } else return false; } } private void replyCTCP(String sender, String message){ if (message.equals("\u0001VERSION\u0001")){ StreamProvider.writeToStream(serverName,"NOTICE "+simplifyNick(sender)+" :\u0001VERSION "+ GlobalData.getAppVersion()+"\u0001"); writerWorker.logAdd("[system]", "catch/handled CTCP VERSION from", simplifyNick(sender)); System.out.println(sender+" "+message); System.out.println("NOTICE "+simplifyNick(sender)+" \u0001VERSION "+ GlobalData.getAppVersion()+"\u0001"); } else if (message.startsWith("\u0001PING ") && message.endsWith("\u0001")){ StreamProvider.writeToStream(serverName,"NOTICE "+simplifyNick(sender)+" :"+message); writerWorker.logAdd("[system]", "catch/handled CTCP PING from", simplifyNick(sender)); //System.out.println(":"+simplifyNick(sender)+" NOTICE "+sender.substring(0,sender.indexOf("!"))+" "+message); } else if (message.equals("\u0001CLIENTINFO\u0001")){ StreamProvider.writeToStream(serverName,"NOTICE "+simplifyNick(sender)+" :\u0001CLIENTINFO ACTION PING VERSION TIME CLIENTINFO\u0001"); writerWorker.logAdd("[system]", "catch/handled CTCP CLIENTINFO from", simplifyNick(sender)); //System.out.println(":"+simplifyNick(sender)+" NOTICE "+sender.substring(0,sender.indexOf("!"))+" \u0001CLIENTINFO ACTION PING VERSION TIME CLIENTINFO\u0001"); } else if (message.equals("\u0001TIME\u0001")){ StreamProvider.writeToStream(serverName,"NOTICE "+simplifyNick(sender)+" :\u0001TIME "+ ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME)+"\u0001"); writerWorker.logAdd("[system]", "catch/handled CTCP TIME from", simplifyNick(sender)); //System.out.println(":"+simplifyNick(sender)+" NOTICE "+sender.substring(0,sender.indexOf("!"))+" \u0001TIME "+ ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME)+"\u0001"); } else writerWorker.logAdd("[system]", "catch CTCP request \""+message+"\" from ", simplifyNick(sender)); } private String simplifyNick(String nick){ return nick.replaceAll("!.*$",""); } //todo: nandle nickserv messages private void handleNumeric(String eventNum, String sender, String message){ switch (eventNum){ case "433": // TODO: try to use alternative nickname writerWorker.logAdd("[system]", "catch/handled:", eventNum+" [nickname already in use]"); break; case "353": String chan = message.substring(message.indexOf(" ")+3); chan = chan.substring(0, chan.indexOf(" ")); if (proxyAList.containsKey(chan)) { String userOnChanStr = message.substring(message.indexOf(":") + 1); userOnChanStr = userOnChanStr.replaceAll("[%@+]", "").trim(); String[] usersOnChanArr = userOnChanStr.split(" "); PipedOutputStream streamOut = new PipedOutputStream(); try { BufferedReader streamBufferedReader = new BufferedReader( new InputStreamReader( new PipedInputStream(streamOut), StandardCharsets.UTF_8) ); channelsMap.put(chan, new PrintWriter(streamOut)); // % @ + ChanConsumer consumer = new ChanConsumer(streamBufferedReader, storageFile.getServerName(), chan, nick, usersOnChanArr, storageFile.getRejoinOnKick(), channelsMap, storageFile.getChanelConfigurationsPath()); new Thread(consumer).start(); for (String msgStored : proxyAList.get(chan)) { channelsMap.get(chan).println(msgStored); channelsMap.get(chan).flush(); } proxyAList.remove(chan); if (proxyAList.isEmpty()) { proxyRequired = false; } } catch (IOException e) { System.out.println("Internal issue: SystemConsumer->handleNumeric() @ JOIN: I/O exception while initialized child objects."); // caused by Socket return; //TODO: QA } } else System.out.println("Some internal shit happens that shouldn't happens never ever. Take your cat, call scientists and wait for singularity. Panic allowed."); break; case "NICK": if (sender.startsWith(nick+"!")) { String oldNick = nick; nick = message.trim(); writerWorker.logAdd("[system]", "catch/handled own NICK change from:", sender+" to: "+message); } break; case "JOIN": if (sender.startsWith(nick+"!")) { proxyAList.put(message, new ArrayList<>()); // Add new channel name to proxy watch-list proxyAList.get(message).add(eventNum+" "+sender+" "+message); // Add message to array linked this.proxyRequired = true; // Ask for proxy validators } break; default: writerWorker.logAdd("[system]", "catch: "+eventNum+" from: "+sender+" :",message); break; } } }