innaircbot/src/main/java/InnaIrcBot/ProvidersConsumers/SystemConsumer.java

261 lines
11 KiB
Java

package InnaIrcBot.ProvidersConsumers;
import InnaIrcBot.Commanders.CTCPHelper;
import InnaIrcBot.Commanders.PrivateMsgCommander;
import InnaIrcBot.ReconnectControl;
import InnaIrcBot.config.ConfigurationFile;
import InnaIrcBot.IrcChannel;
import InnaIrcBot.logging.WorkerSystem;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
public class SystemConsumer implements Runnable{
private final BlockingQueue<String> systemQueue;
private final WorkerSystem writerWorker;
private String nick;
private final String server;
private final Map<String, IrcChannel> channels;
private final ConfigurationFile configurationFile;
private final PrivateMsgCommander commander;
private final ArrayList<Thread> channelThreads;
private int nickTail = 0;
private final SystemCTCP systemCTCP;
private static final HashMap<String, BlockingQueue<String>> systemConsumers = new HashMap<>();
public static synchronized BlockingQueue<String> getSystemConsumer(String server){
return systemConsumers.get(server);
}
SystemConsumer(String userNick, Map<String, IrcChannel> channels, ConfigurationFile configurationFile) {
this.systemQueue = channels.get("").getChannelQueue();
this.nick = userNick;
this.server = configurationFile.getServerName();
WorkerSystem.setLogDriver(server);
this.writerWorker = WorkerSystem.getSystemWorker(server);
this.channels = channels;
this.channelThreads = new ArrayList<>();
this.configurationFile = configurationFile;
this.commander = new PrivateMsgCommander(server, this.configurationFile.getBotAdministratorPassword());
this.systemCTCP = new SystemCTCP(server, configurationFile.getCooldownTime(), writerWorker);
systemConsumers.put(server, systemQueue);
}
@Override
public void run() {
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
+"] Thread SystemConsumer \""+ server +"\": started"); // TODO:REMOVE
startMainRoutine();
close();
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
+"] Thread SystemConsumer \""+ server +"\": ended"); // TODO:REMOVE
}
private void startMainRoutine(){
try {
while (true) {
String data = systemQueue.take();
String[] dataStrings = data.split(" :?",3);
//TODO: handle mode change
if (dataStrings[0].equals("INNA")){
String[] splitter;
if (dataStrings.length > 2){ // Don't touch 'cuz it's important
splitter = dataStrings[2].split(" ", 2);
if (splitter.length == 2){
handleSpecial(dataStrings[1], splitter[0], splitter[1]);
}
}
continue;
}
if ("PRIVMSG".equals(dataStrings[1])) {
if (dataStrings[2].indexOf("\u0001") < dataStrings[2].lastIndexOf("\u0001")) {
String sender = simplifyNick(dataStrings[0]);
String message = dataStrings[2].substring(dataStrings[2].indexOf(":") + 1);
systemCTCP.replyCTCP(sender, message);
}
else {
commander.receiver(dataStrings[0], dataStrings[2].replaceAll("^.+?:", "").trim());
writerWorker.log(dataStrings[1] + " " + dataStrings[0] + " :", dataStrings[2].replaceAll("^.+?:", "").trim());
}
}
else {
handleNumeric(dataStrings[0], dataStrings[1], dataStrings[2]);
}
}
}
catch (InterruptedException ie){
System.out.println("Thread SystemConsumer interrupted."); // TODO: reconnect OR AT LEAST DIE
}
catch (Exception e){
System.out.println("Internal issue: SystemConsumer: "+e.getMessage()); // TODO: DO.. some thing
e.printStackTrace();
}
}
private String simplifyNick(String nick){ return nick.replaceAll("!.*$",""); }
private void handleSpecial(String event, String channelName, String message){
IrcChannel ircChannel = channels.get(channelName);
if (ircChannel == null)
return;
String ircFormatterMessage = nick+" "+event+" "+channelName+" "+message;
ircChannel.getChannelQueue().add(ircFormatterMessage);
}
//todo: handle nickserv messages somehow
private void handleNumeric(String sender, String eventNum, String message) throws Exception{
switch (eventNum){
case "501": // Notify user about incorrect setup
writerWorker.log("catch/handled:", eventNum
+ " [MODE message was sent with a nickname parameter and that the a mode flag sent was not recognized.]");
break;
case "433": // TODO: try to use alternative nickname
writerWorker.log("catch/handled:", eventNum
+ " [nickname already in use and will be changed]");
break;
case "353":
writerWorker.log("handled:", eventNum+" [RPL_NAMREPLY]");
String channelName = message.substring(nick.length()+3).replaceAll("\\s.*$", "");
IrcChannel ircChannel = channels.get(channelName);
if (ircChannel == null)
return;
ircChannel.getChannelQueue().add(sender+" "+eventNum+" "+message);
break;
case "NICK":
if (sender.startsWith(nick+"!")) {
nick = message.trim();
writerWorker.log("catch own NICK change:", sender+" to: "+message);
}
break;
case "JOIN":
if (sender.startsWith(nick+"!")) {
IrcChannel newIrcChannel = new IrcChannel(message);
channels.put(message, newIrcChannel);
// % @ +
ChanConsumer consumer = new ChanConsumer(
configurationFile.getServerName(),
newIrcChannel,
nick,
channels);
Thread newIrcChannelThread = new Thread(consumer);
newIrcChannelThread.start();
channelThreads.add(newIrcChannelThread);
newIrcChannel.getChannelQueue().add(sender+" "+eventNum+" "+message);
writerWorker.log("joined channel ", message);
}
break;
case "401": // No such nick/channel
//System.out.println("|"+message.replaceAll("^(\\s)?.+?(\\s)|((\\s)?:No such nick/channel)","")+"|");
CTCPHelper.getInstance().handleErrorReply(server, message.replaceAll("^(\\s)?.+?(\\s)|((\\s)?:No such nick/channel)",""));
writerWorker.log("catch: "+eventNum+" from: "+sender+" :",message+" [ok]");
break;
case "NOTICE":
CTCPHelper.getInstance().handleCtcpReply(server, simplifyNick(sender), message.replaceAll("^.+?:", "").trim());
writerWorker.log("NOTICE from "+sender+" received: ", message.replaceAll("^.+?:", "").trim());
break;
case "001":
sendUserModes();
sendNickPassword();
joinChannels();
writerWorker.log(eventNum, message);
break;
case "443":
String newNick = nick+"|"+nickTail++;
StreamProvider.writeToStream(server,"NICK "+newNick);
break;
case "464": // password for server/znc/bnc
StreamProvider.writeToStream(server,"PASS "+configurationFile.getServerPass());
writerWorker.log(eventNum, message);
break;
case "432":
writerWorker.log(eventNum, message);
System.out.println("Configuration issue: Nickname contains unacceptable characters (432 ERR_ERRONEUSNICKNAME).");
case "465":
ReconnectControl.update(server, false);
writerWorker.log(eventNum, message);
break;
case "QUIT": // TODO: Do something?
writerWorker.log(eventNum, message);
break;
case "375":
writerWorker.log("MOTD Start:", message.replaceAll("^.+?:", ""));
break;
case "372":
writerWorker.log("MOTD:", message.replaceAll("^.+?:", ""));
break;
case "376":
writerWorker.log("MOTD End:", message.replaceAll("^.+?:", ""));
break;
default:
writerWorker.log("catch: "+eventNum+" from: "+sender+" :", message);
break;
// 431 ERR_NONICKNAMEGIVEN how can we get this?
// 436 ERR_NICKCOLLISION
}
}
private void sendUserModes(){
String modes = configurationFile.getUserMode().replaceAll("[\t\\s]", "");
if (modes.isEmpty())
return;
StringBuilder message = new StringBuilder();
for(char mode : modes.toCharArray()) {
message.append("MODE ");
message.append(nick);
message.append(" +");
message.append(mode);
message.append("\n");
}
StreamProvider.writeToStream(server, message.toString());
}
private void sendNickPassword(){
if (configurationFile.getUserNickPass().isEmpty())
return;
switch (configurationFile.getUserNickAuthStyle()){
case "freenode":
StreamProvider.writeToStream(server,"PRIVMSG NickServ :IDENTIFY "
+ configurationFile.getUserNickPass());
break;
case "rusnet":
StreamProvider.writeToStream(server,"NickServ IDENTIFY "
+ configurationFile.getUserNickPass());
}
}
private void joinChannels(){
StringBuilder joinMessage = new StringBuilder();
for (String channel : configurationFile.getChannels()) { // TODO: add validation of channels.
joinMessage.append("JOIN ");
joinMessage.append(channel);
joinMessage.append("\n");
}
StreamProvider.writeToStream(server, joinMessage.toString());
}
private void close(){
for (Thread channel : channelThreads) { //TODO: check, code duplication. see Data provider constructor
channel.interrupt();
}
writerWorker.close();
systemConsumers.remove(server);
}
}