It was and it is so bad that I refuse to describe changes made. Just one more refactoring iteration ><
This commit is contained in:
parent
b3da839bcd
commit
f3cdfc532c
18 changed files with 806 additions and 700 deletions
2
pom.xml
2
pom.xml
|
@ -30,8 +30,8 @@
|
||||||
<groupId>org.ini4j</groupId>
|
<groupId>org.ini4j</groupId>
|
||||||
<artifactId>ini4j</artifactId>
|
<artifactId>ini4j</artifactId>
|
||||||
<version>0.5.4</version>
|
<version>0.5.4</version>
|
||||||
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-cli</groupId>
|
<groupId>commons-cli</groupId>
|
||||||
<artifactId>commons-cli</artifactId>
|
<artifactId>commons-cli</artifactId>
|
||||||
|
|
|
@ -20,10 +20,10 @@ public class ChanelCommander implements Runnable {
|
||||||
private HashMap<String, String[]> msgMap; // Mask(Pattern) ->, Action | Where Action[0] could be: raw
|
private HashMap<String, String[]> msgMap; // Mask(Pattern) ->, Action | Where Action[0] could be: raw
|
||||||
private HashMap<String, String[]> nickMap; // Mask(Pattern) ->, Action | Where Action[0] could be: raw
|
private HashMap<String, String[]> nickMap; // Mask(Pattern) ->, Action | Where Action[0] could be: raw
|
||||||
|
|
||||||
private boolean joinFloodTrackNeed = false;
|
private boolean joinFloodTrackNeed;
|
||||||
private JoinFloodHandler jfh;
|
private JoinFloodHandler jfh;
|
||||||
|
|
||||||
private boolean joinCloneTrackNeed = false; // todo:fix
|
private boolean joinCloneTrackNeed;
|
||||||
private JoinCloneHandler jch;
|
private JoinCloneHandler jch;
|
||||||
|
|
||||||
public ChanelCommander(BlockingQueue<String> stream, String serverName, String channel) throws Exception{
|
public ChanelCommander(BlockingQueue<String> stream, String serverName, String channel) throws Exception{
|
||||||
|
@ -40,21 +40,21 @@ public class ChanelCommander implements Runnable {
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
String data = streamQueue.take();
|
String data = streamQueue.take();
|
||||||
String[] dataStrings = data.split(" ",3);
|
String[] dataStrings = data.split(" :?",3);
|
||||||
|
|
||||||
switch (dataStrings[0]) {
|
switch (dataStrings[1]) {
|
||||||
case "NICK":
|
case "NICK":
|
||||||
nickCame(dataStrings[2]+dataStrings[1].replaceAll("^.+?!","!"));
|
nickCame(dataStrings[2]+dataStrings[0].replaceAll("^.+?!","!"));
|
||||||
break; // todo: need to track join flood
|
break; // todo: need to track join flood
|
||||||
case "JOIN":
|
case "JOIN":
|
||||||
if (joinFloodTrackNeed)
|
if (joinFloodTrackNeed)
|
||||||
jfh.track(simplifyNick(dataStrings[1]));
|
jfh.track(simplifyNick(dataStrings[0]));
|
||||||
if (joinCloneTrackNeed)
|
if (joinCloneTrackNeed)
|
||||||
jch.track(dataStrings[1]);
|
jch.track(dataStrings[0]);
|
||||||
joinCame(dataStrings[1]);
|
joinCame(dataStrings[0]);
|
||||||
break;
|
break;
|
||||||
case "PRIVMSG":
|
case "PRIVMSG":
|
||||||
privmsgCame(dataStrings[1], dataStrings[2]);
|
privmsgCame(dataStrings[0], dataStrings[2]);
|
||||||
break;
|
break;
|
||||||
/*
|
/*
|
||||||
case "PART": // todo: need to track join flood? Fuck that. Track using JOIN
|
case "PART": // todo: need to track join flood? Fuck that. Track using JOIN
|
||||||
|
@ -68,9 +68,10 @@ public class ChanelCommander implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InterruptedException ie){
|
catch (InterruptedException ie){
|
||||||
System.out.println("Internal issue: thread ChanelCommander->run() interrupted.\n\t"); // TODO: reconnect
|
System.out.println("ChanelCommander interrupted.");
|
||||||
}
|
}
|
||||||
System.out.println("Thread for ChanelCommander ended"); // TODO:REMOVE DEBUG
|
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] ChanelCommander thread "
|
||||||
|
+server+":"+this.channel +" ended");// TODO:REMOVE DEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we need old nick?
|
// Do we need old nick?
|
||||||
|
@ -163,15 +164,19 @@ public class ChanelCommander implements Runnable {
|
||||||
else
|
else
|
||||||
whatToSendStringBuilder.append(cmdOrMsg[i]);
|
whatToSendStringBuilder.append(cmdOrMsg[i]);
|
||||||
}
|
}
|
||||||
if (objectRegexp != null) {
|
|
||||||
|
if (objectRegexp == null)
|
||||||
|
break;
|
||||||
|
|
||||||
String objectToCtcp = arg1.trim().replaceAll(objectRegexp, ""); // note: trim() ?
|
String objectToCtcp = arg1.trim().replaceAll(objectRegexp, ""); // note: trim() ?
|
||||||
if (!objectToCtcp.isEmpty()){
|
|
||||||
|
if (objectToCtcp.isEmpty())
|
||||||
|
break;
|
||||||
|
|
||||||
if (CTCPType.startsWith("\\c"))
|
if (CTCPType.startsWith("\\c"))
|
||||||
CTCPHelper.getInstance().registerRequest(server, channel, CTCPType.substring(2).toUpperCase(), objectToCtcp, whatToSendStringBuilder.toString());
|
registerCTCPforChannel(CTCPType.substring(2).toUpperCase(), objectToCtcp, whatToSendStringBuilder.toString());
|
||||||
else
|
else
|
||||||
CTCPHelper.getInstance().registerRequest(server, simplifyNick(arg2), CTCPType.substring(2).toUpperCase(), objectToCtcp, whatToSendStringBuilder.toString());
|
registerCTCPforUser(simplifyNick(arg2), CTCPType.substring(2).toUpperCase(), objectToCtcp, whatToSendStringBuilder.toString());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -180,15 +185,22 @@ public class ChanelCommander implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////// /////////
|
///////// /////////
|
||||||
|
private void registerCTCPforChannel(String CTCPType, String object, String message){
|
||||||
|
CTCPHelper.getInstance().registerRequest(server, channel, CTCPType, object, message);
|
||||||
|
}
|
||||||
|
private void registerCTCPforUser(String user, String CTCPType, String object, String message){
|
||||||
|
CTCPHelper.getInstance().registerRequest(server, user, CTCPType, object, message);
|
||||||
|
}
|
||||||
private void whoisAction(String who){ // TODO: maybe we have to extend functionality to reuse received information.
|
private void whoisAction(String who){ // TODO: maybe we have to extend functionality to reuse received information.
|
||||||
StreamProvider.writeToStream(server, "WHOIS "+simplifyNick(who));
|
StreamProvider.writeToStream(server, "WHOIS "+simplifyNick(who));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void msgAction(String[] messages, String who, boolean sendToPrivate){
|
private void msgAction(String[] messages, String who, boolean isToUser){
|
||||||
StringBuilder executiveStr = new StringBuilder();
|
StringBuilder executiveStr = new StringBuilder();
|
||||||
executiveStr.append("PRIVMSG ");
|
executiveStr.append("PRIVMSG ");
|
||||||
if(sendToPrivate) {
|
if(isToUser) {
|
||||||
executiveStr.append(simplifyNick(who));
|
executiveStr.append(simplifyNick(who));
|
||||||
executiveStr.append(" :");
|
executiveStr.append(" :");
|
||||||
}
|
}
|
||||||
|
@ -236,6 +248,13 @@ public class ChanelCommander implements Runnable {
|
||||||
|
|
||||||
private void readConfing() throws Exception{
|
private void readConfing() throws Exception{
|
||||||
ConfigurationChannel configChannel = ConfigurationManager.getConfiguration(server).getChannelConfig(channel);
|
ConfigurationChannel configChannel = ConfigurationManager.getConfiguration(server).getChannelConfig(channel);
|
||||||
|
if (configChannel == null){
|
||||||
|
joinMap = new HashMap<>();
|
||||||
|
msgMap = new HashMap<>();
|
||||||
|
nickMap = new HashMap<>();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
joinMap = configChannel.getJoinMap();
|
joinMap = configChannel.getJoinMap();
|
||||||
msgMap = configChannel.getMsgMap();
|
msgMap = configChannel.getMsgMap();
|
||||||
nickMap = configChannel.getNickMap();
|
nickMap = configChannel.getNickMap();
|
||||||
|
|
|
@ -18,8 +18,16 @@ public class PrivateMsgCommander { // TODO: add black list: add users afte
|
||||||
}
|
}
|
||||||
|
|
||||||
public void receiver(String sender, String message){
|
public void receiver(String sender, String message){
|
||||||
if (!password.isEmpty()) {
|
if (password.isEmpty() || message.isEmpty())
|
||||||
if (administrators.contains(sender) && !message.isEmpty()) {
|
return;
|
||||||
|
|
||||||
|
if (isNotAuthorized(sender)){
|
||||||
|
if (message.startsWith("login ")) {
|
||||||
|
login(sender, message.substring("login ".length()).trim());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String[] cmd = message.split("(\\s)|(\t)+?", 2);
|
String[] cmd = message.split("(\\s)|(\t)+?", 2);
|
||||||
cmd[0] = cmd[0].toLowerCase();
|
cmd[0] = cmd[0].toLowerCase();
|
||||||
if (cmd.length > 1)
|
if (cmd.length > 1)
|
||||||
|
@ -217,13 +225,10 @@ public class PrivateMsgCommander { // TODO: add black list: add users afte
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
tell(simplifyNick(sender), "Unknown command. Could be: join, part, quit, tell, nick, ctcp, notice, umode, cmode, raw, kick[k], ban[b], unban[-b], kickban[kb], voice[v], unvoice[-v], hop[h], unhop[-h], op[o], unop[-o], topic, invite and (login)");
|
tell(simplifyNick(sender), "Unknown command. Could be: join, part, quit, tell, nick, ctcp, notice, umode, cmode, raw, kick[k], ban[b], unban[-b], kickban[kb], voice[v], unvoice[-v], hop[h], unhop[-h], op[o], unop[-o], topic, invite and (login)");
|
||||||
} // TODO: chanel limits set/remove
|
} // TODO: add options for setting channel limits (MODEs)
|
||||||
} else {
|
|
||||||
if (!message.isEmpty() && message.startsWith("login ")) {
|
|
||||||
login(sender, message.replaceAll("^([\t\\s]+)?login([\t\\s]+)|([\t\\s]+$)", ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
private boolean isNotAuthorized(String sender){
|
||||||
|
return ! administrators.contains(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void join(String channel){
|
private void join(String channel){
|
||||||
|
@ -233,14 +238,12 @@ public class PrivateMsgCommander { // TODO: add black list: add users afte
|
||||||
raw("PART "+channel);
|
raw("PART "+channel);
|
||||||
}
|
}
|
||||||
private void quit(String message){
|
private void quit(String message){
|
||||||
|
ReconnectControl.update(server, false);
|
||||||
if (message.isEmpty()){
|
if (message.isEmpty()){
|
||||||
raw("QUIT :"+ GlobalData.getAppVersion());
|
raw("QUIT :"+ GlobalData.getAppVersion());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
raw("QUIT :"+message);
|
raw("QUIT :"+message);
|
||||||
ReconnectControl.update(server, false);
|
|
||||||
//ReconnectControl.update(serverName, true);
|
|
||||||
//System.exit(0); // TODO: change to normal exit
|
|
||||||
}
|
}
|
||||||
private void tell(String channelUser, String message){
|
private void tell(String channelUser, String message){
|
||||||
message = message.trim();
|
message = message.trim();
|
||||||
|
|
|
@ -8,5 +8,6 @@ public class GlobalData {
|
||||||
System.getProperty("os.version"),
|
System.getProperty("os.version"),
|
||||||
System.getProperty("os.arch"));
|
System.getProperty("os.arch"));
|
||||||
}
|
}
|
||||||
|
public static final String applicationHomePage = "https://github.com/developersu/InnaIrcBot";
|
||||||
public static final int CHANNEL_QUEUE_CAPACITY = 500;
|
public static final int CHANNEL_QUEUE_CAPACITY = 500;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,10 @@ public class ChanConsumer implements Runnable {
|
||||||
private final BlockingQueue<String> chanConsumerQueue;
|
private final BlockingQueue<String> chanConsumerQueue;
|
||||||
private final String serverName;
|
private final String serverName;
|
||||||
private final String channelName;
|
private final String channelName;
|
||||||
private Worker writerWorker;
|
private final ArrayList<String> users;
|
||||||
private ArrayList<String> userList;
|
private Worker logWorker;
|
||||||
private String nick;
|
private String nick;
|
||||||
private final boolean rejoin;
|
private final boolean autoRejoin;
|
||||||
private final Map<String, IrcChannel> channels;
|
private final Map<String, IrcChannel> channels;
|
||||||
|
|
||||||
private Thread channelCommanderThread;
|
private Thread channelCommanderThread;
|
||||||
|
@ -32,17 +32,20 @@ public class ChanConsumer implements Runnable {
|
||||||
|
|
||||||
private boolean endThread = false;
|
private boolean endThread = false;
|
||||||
|
|
||||||
|
private boolean hasBeenKicked;
|
||||||
|
|
||||||
ChanConsumer(String serverName,
|
ChanConsumer(String serverName,
|
||||||
IrcChannel thisIrcChannel,
|
IrcChannel thisIrcChannel,
|
||||||
String ownNick,
|
String ownNick,
|
||||||
Map<String, IrcChannel> channels) throws Exception{
|
Map<String, IrcChannel> channels) throws Exception
|
||||||
|
{
|
||||||
this.chanConsumerQueue = thisIrcChannel.getChannelQueue();
|
this.chanConsumerQueue = thisIrcChannel.getChannelQueue();
|
||||||
this.serverName = serverName;
|
this.serverName = serverName;
|
||||||
this.channelName = thisIrcChannel.toString();
|
this.channelName = thisIrcChannel.toString();
|
||||||
this.writerWorker = LogDriver.getWorker(serverName, channelName);
|
this.logWorker = LogDriver.getWorker(serverName, channelName);
|
||||||
this.userList = new ArrayList<>();
|
this.users = new ArrayList<>();
|
||||||
this.nick = ownNick;
|
this.nick = ownNick;
|
||||||
this.rejoin = ConfigurationManager.getConfiguration(serverName).getRejoinOnKick();
|
this.autoRejoin = ConfigurationManager.getConfiguration(serverName).getRejoinOnKick();
|
||||||
this.channels = channels;
|
this.channels = channels;
|
||||||
getChanelCommander();
|
getChanelCommander();
|
||||||
}
|
}
|
||||||
|
@ -55,97 +58,115 @@ public class ChanConsumer implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(){
|
public void run(){
|
||||||
String data;
|
|
||||||
String[] dataStrings;
|
|
||||||
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] ChanConsumer thread "+serverName+":"+this.channelName +" started"); // TODO:REMOVE DEBUG
|
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] ChanConsumer thread "+serverName+":"+this.channelName +" started"); // TODO:REMOVE DEBUG
|
||||||
try {
|
try {
|
||||||
while (! endThread) {
|
while (! endThread) {
|
||||||
data = chanConsumerQueue.take();
|
String data = chanConsumerQueue.take();
|
||||||
dataStrings = data.split(" ",3);
|
String[] dataStrings = data.split(" :?",3);
|
||||||
|
|
||||||
if (! trackUsers(dataStrings[0], dataStrings[1], dataStrings[2]))
|
if (trackUsers(dataStrings[1], dataStrings[0], dataStrings[2]))
|
||||||
continue;
|
continue;
|
||||||
// Send to chanel commander thread
|
// Send to channel commander thread
|
||||||
queue.add(data); // TODO: Check and add consistency validation
|
// TODO: Check and add consistency validation
|
||||||
|
queue.add(data);
|
||||||
|
|
||||||
if (!writerWorker.logAdd(dataStrings[0], dataStrings[1], dataStrings[2])){ // Write logs, check if LogDriver consistent. If not:
|
if (! logWorker.logAdd(dataStrings[1], dataStrings[0], dataStrings[2])){ // Write logs checks if LogDriver consistent.
|
||||||
this.fixLogDriverIssues(dataStrings[0], dataStrings[1], dataStrings[2]);
|
this.fixLogDriverIssues(dataStrings[1], dataStrings[0], dataStrings[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
channels.remove(channelName);
|
|
||||||
} catch (InterruptedException e){
|
} catch (InterruptedException e){
|
||||||
System.out.println("ChanConsumer (@"+serverName+"/"+channelName+")->run(): Interrupted\n\t"+e); // TODO: reconnect?
|
System.out.println("ChanConsumer (@"+serverName+"/"+channelName+")->run(): Interrupted\n\t"+e.getMessage()); // TODO: reconnect?
|
||||||
}
|
}
|
||||||
writerWorker.close();
|
|
||||||
//Kill sub-thread
|
close();
|
||||||
channelCommanderThread.interrupt();
|
|
||||||
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] THREAD "+serverName+":"+this.channelName +" ended"); // TODO:REMOVE DEBUG
|
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] THREAD "+serverName+":"+this.channelName +" ended"); // TODO:REMOVE DEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean trackUsers(String event, String initiatorArg, String subjectArg){
|
private boolean trackUsers(String event, String initiator, String subject){
|
||||||
|
initiator = simplifyNick(initiator);
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case "PRIVMSG": // most common, we don't have to handle anything else
|
case "PRIVMSG": // most common, we don't have to handle anything else
|
||||||
return true;
|
return false;
|
||||||
case "JOIN":
|
case "JOIN":
|
||||||
addUser(simplifyNick(initiatorArg));
|
addUser(initiator);
|
||||||
return true;
|
return false;
|
||||||
case "PART":
|
case "PART":
|
||||||
deleteUser(simplifyNick(initiatorArg)); // nick non-simple
|
deleteUser(initiator);
|
||||||
return true;
|
return false;
|
||||||
case "QUIT":
|
case "QUIT":
|
||||||
if (userList.contains(simplifyNick(initiatorArg))) {
|
if (users.contains(initiator)) {
|
||||||
deleteUser(simplifyNick(initiatorArg)); // nick non-simple
|
deleteUser(initiator);
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
return true; // user quit, but he/she is not in this channel
|
||||||
return false; // user quit, but he/she is not in this channel
|
|
||||||
case "KICK":
|
case "KICK":
|
||||||
if (rejoin && nick.equals(subjectArg.replaceAll("(^.+?\\s)|(\\s.+$)", ""))) // if it's me and I have rejoin policy 'Auto-Rejoin on kick'.
|
String kickedUser = subject.replaceAll("(^.+?\\s)|(\\s.+$)", "");
|
||||||
StreamProvider.writeToStream(serverName, "JOIN " + channelName);
|
if (nick.equals(kickedUser) && autoRejoin) { // TODO: FIX
|
||||||
deleteUser(subjectArg.replaceAll("(^.+?\\s)|(\\s.+$)", "")); // nick already simplified
|
hasBeenKicked = true;
|
||||||
|
deleteUser(kickedUser);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
deleteUser(kickedUser);
|
||||||
|
return false;
|
||||||
case "NICK":
|
case "NICK":
|
||||||
if (userList.contains(simplifyNick(initiatorArg))) {
|
if (users.contains(initiator)) {
|
||||||
swapUsers(simplifyNick(initiatorArg), subjectArg);
|
swapUsers(initiator, subject);
|
||||||
return true;
|
return false;
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false; // user changed nick, but he/she is not in this channel
|
|
||||||
}
|
}
|
||||||
|
return true; // user changed nick, but he/she is not in this channel
|
||||||
case "353":
|
case "353":
|
||||||
String userOnChanStr = subjectArg.substring(subjectArg.indexOf(":") + 1);
|
String userOnChanStr = subject.substring(subject.indexOf(":") + 1);
|
||||||
userOnChanStr = userOnChanStr.replaceAll("[%@+]", "").trim();
|
userOnChanStr = userOnChanStr.replaceAll("[%@+]", "").trim();
|
||||||
String[] usersOnChanArr = userOnChanStr.split(" ");
|
String[] usersOnChanArr = userOnChanStr.split(" ");
|
||||||
userList.addAll(Arrays.asList(usersOnChanArr));
|
users.addAll(Arrays.asList(usersOnChanArr));
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addUser(String user){
|
private void addUser(String user){
|
||||||
if (!userList.contains(user))
|
if (! users.contains(user))
|
||||||
userList.add(user);
|
users.add(user);
|
||||||
}
|
}
|
||||||
private void deleteUser(String user){
|
private void deleteUser(String user){
|
||||||
if (user.equals(nick)) {
|
if (user.equals(nick)) {
|
||||||
endThread = true;
|
endThread = true;
|
||||||
}
|
}
|
||||||
userList.remove(user);
|
users.remove(user);
|
||||||
}
|
}
|
||||||
private void swapUsers(String userNickOld, String userNickNew){
|
private void swapUsers(String userNickOld, String userNickNew){
|
||||||
userList.remove(userNickOld);
|
users.remove(userNickOld);
|
||||||
userList.add(userNickNew);
|
users.add(userNickNew);
|
||||||
if (userNickOld.equals(nick))
|
if (userNickOld.equals(nick))
|
||||||
this.nick = userNickNew;
|
nick = userNickNew;
|
||||||
}
|
}
|
||||||
private String simplifyNick(String nick){ return nick.replaceAll("!.*$",""); }
|
private String simplifyNick(String nick){ return nick.replaceAll("!.*$",""); }
|
||||||
|
|
||||||
|
private void close(){
|
||||||
|
try{
|
||||||
|
channels.remove(channelName);
|
||||||
|
logWorker.close();
|
||||||
|
channelCommanderThread.interrupt(); //kill sub-thread
|
||||||
|
channelCommanderThread.join();
|
||||||
|
handleAutoRejoin();
|
||||||
|
}
|
||||||
|
catch (InterruptedException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleAutoRejoin(){
|
||||||
|
if (hasBeenKicked && autoRejoin) {
|
||||||
|
StreamProvider.writeToStream(serverName, "JOIN " + channelName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void fixLogDriverIssues(String a, String b, String c){
|
private void fixLogDriverIssues(String a, String b, String c){
|
||||||
System.out.println("ChanConsumer (@"+serverName+"/"+channelName+")->fixLogDriverIssues(): Some issues detected. Trying to fix...");
|
System.out.println("ChanConsumer (@"+serverName+"/"+channelName+")->fixLogDriverIssues(): Some issues detected. Trying to fix...");
|
||||||
this.writerWorker = LogDriver.getWorker(serverName, channelName); // Reset logDriver and try using the same one
|
logWorker = LogDriver.getWorker(serverName, channelName); // Reset logDriver and try using the same one
|
||||||
if (! writerWorker.logAdd(a, b, c)){ // Write to it what was not written (most likely) and if it's still not consistent:
|
if (! logWorker.logAdd(a, b, c)){ // Write to it what was not written (most likely) and if it's still not consistent:
|
||||||
this.writerWorker = new WorkerZero();
|
logWorker = new WorkerZero();
|
||||||
System.out.println("ChanConsumer (@"+serverName+"/"+channelName+")->fixLogDriverIssues(): failed to use defined LogDriver. Using ZeroWorker instead.");
|
System.out.println("ChanConsumer (@"+serverName+"/"+channelName+")->fixLogDriverIssues(): failed to use defined LogDriver. Using ZeroWorker instead.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,103 +11,53 @@ import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
|
|
||||||
|
|
||||||
public class DataProvider implements Runnable {
|
public class DataProvider implements Runnable {
|
||||||
private final ConfigurationFile configurationFile;
|
private final ConfigurationFile configuration;
|
||||||
private final String server;
|
private final String server;
|
||||||
private final String nickName;
|
private final String nick;
|
||||||
private BufferedReader mainReader;
|
private BufferedReader mainReader;
|
||||||
|
|
||||||
|
private Thread systemConsumerThread;
|
||||||
|
private Map<String, IrcChannel> ircChannels;
|
||||||
|
private IrcChannel systemConsumerChannel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiate connection and prepare input/output streams for run()
|
* Initiate connection and prepare input/output streams for run()
|
||||||
* */
|
* */
|
||||||
public DataProvider(ConfigurationFile configurationFile){
|
public DataProvider(ConfigurationFile configuration){
|
||||||
this.configurationFile = configurationFile;
|
this.configuration = configuration;
|
||||||
this.server = configurationFile.getServerName();
|
this.server = configuration.getServerName();
|
||||||
this.nickName = configurationFile.getUserNick();
|
this.nick = configuration.getUserNick();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(){
|
public void run(){
|
||||||
try {
|
try {
|
||||||
connect();
|
connectSocket();
|
||||||
} catch (Exception e){
|
|
||||||
System.out.println("Internal issue: DataProvider->run() caused exception:\n\t"+e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReconnectControl.register(server);
|
ReconnectControl.register(server);
|
||||||
|
|
||||||
LogDriver.setLogDriver(server);
|
LogDriver.setLogDriver(server);
|
||||||
|
|
||||||
/* Used for sending data into consumers objects*/
|
ircChannels = Collections.synchronizedMap(new HashMap<>());
|
||||||
Map<String, IrcChannel> ircChannels = Collections.synchronizedMap(new HashMap<>());
|
systemConsumerChannel = new IrcChannel("");
|
||||||
|
ircChannels.put(systemConsumerChannel.toString(), systemConsumerChannel);
|
||||||
|
|
||||||
IrcChannel systemConsumerChannel = new IrcChannel("");
|
startSystemConsumer();
|
||||||
BlockingQueue<String> systemConsumerQueue = systemConsumerChannel.getChannelQueue();
|
sendUserNickAndIdent();
|
||||||
|
|
||||||
Thread SystemConsumerThread = new Thread(
|
startLoop();
|
||||||
new SystemConsumer(systemConsumerQueue, nickName, ircChannels, this.configurationFile));
|
|
||||||
SystemConsumerThread.start();
|
|
||||||
|
|
||||||
StreamProvider.setSysConsumer(server, systemConsumerQueue); // Register system consumer at StreamProvider
|
} catch (Exception e){
|
||||||
|
System.out.println("DataProvider exception: "+e.getMessage());
|
||||||
ircChannels.put(systemConsumerChannel.toString(), systemConsumerChannel); // Not sure that PrintWriter is thread-safe..
|
|
||||||
////////////////////////////////////// Start loop //////////////////////////////////////////////////////////////
|
|
||||||
StreamProvider.writeToStream(server,"NICK "+this.nickName);
|
|
||||||
StreamProvider.writeToStream(server,"USER "+ configurationFile.getUserIdent()+" 8 * :"+ configurationFile.getUserRealName()); // TODO: Add usermode 4 rusnet
|
|
||||||
|
|
||||||
try {
|
|
||||||
String rawMessage;
|
|
||||||
String[] rawStrings; // prefix[0] command[1] command-parameters\r\n[2]
|
|
||||||
//if there is no prefix, you should assume the message came from your client.
|
|
||||||
|
|
||||||
while ((rawMessage = mainReader.readLine()) != null) {
|
|
||||||
System.out.println(rawMessage);
|
|
||||||
if (rawMessage.startsWith(":")) {
|
|
||||||
rawStrings = rawMessage
|
|
||||||
.substring(1)
|
|
||||||
.split(" :?", 3); // Removing ':'
|
|
||||||
|
|
||||||
String chan = rawStrings[2].replaceAll("(\\s.?$)|(\\s.+?$)", "");
|
|
||||||
|
|
||||||
if (rawStrings[1].equals("QUIT") || rawStrings[1].equals("NICK")) { // replace regex
|
|
||||||
for (IrcChannel ircChannel : ircChannels.values()) {
|
|
||||||
ircChannel.getChannelQueue().add(rawStrings[1] + " " + rawStrings[0] + " " + rawStrings[2]);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (ircChannels.containsKey(chan)) {
|
|
||||||
IrcChannel chnl = ircChannels.get(chan);
|
|
||||||
chnl.getChannelQueue().add(rawStrings[1] + " " + rawStrings[0] + " " + rawStrings[2]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
systemConsumerQueue.add(rawStrings[1] + " " + rawStrings[0] + " " + rawStrings[2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (rawMessage.startsWith("PING :")) {
|
|
||||||
sendPingReply(rawMessage);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.out.println("Not a valid response=" + rawMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e){
|
|
||||||
System.out.println("Socket issue: I/O exception: "+e.getMessage()); //Connection closed. TODO: MAYBE try reconnect
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
SystemConsumerThread.interrupt();
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void connect() throws Exception{
|
private void connectSocket() throws Exception{
|
||||||
final int port = configurationFile.getServerPort();
|
final int port = configuration.getServerPort();
|
||||||
InetAddress inetAddress = InetAddress.getByName(server);
|
InetAddress inetAddress = InetAddress.getByName(server);
|
||||||
Socket socket = new Socket(); // TODO: set timeout?
|
Socket socket = new Socket();
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
socket.connect(new InetSocketAddress(inetAddress, port), 5000); // 5sec
|
socket.connect(new InetSocketAddress(inetAddress, port), 5000); // 5sec
|
||||||
if (socket.isConnected())
|
if (socket.isConnected())
|
||||||
|
@ -117,18 +67,63 @@ public class DataProvider implements Runnable {
|
||||||
throw new Exception("Unable to connect server.");
|
throw new Exception("Unable to connect server.");
|
||||||
|
|
||||||
StreamProvider.setStream(server, socket);
|
StreamProvider.setStream(server, socket);
|
||||||
|
//TODO set charset in options;
|
||||||
InputStream inStream = socket.getInputStream();
|
mainReader = new BufferedReader(
|
||||||
InputStreamReader isr = new InputStreamReader(inStream, StandardCharsets.UTF_8); //TODO set charset in options;
|
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)
|
||||||
mainReader = new BufferedReader(isr);
|
);
|
||||||
|
}
|
||||||
|
private void sendUserNickAndIdent(){
|
||||||
|
StreamProvider.writeToStream(server,"NICK " + nick);
|
||||||
|
StreamProvider.writeToStream(server,"USER " + configuration.getUserIdent()+" 8 * :"+ configuration.getUserRealName()); // TODO: Add usermode 4 rusnet
|
||||||
|
}
|
||||||
|
private void startSystemConsumer(){
|
||||||
|
systemConsumerThread = new Thread(
|
||||||
|
new SystemConsumer(nick, ircChannels, configuration));
|
||||||
|
systemConsumerThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendPingReply(String rawData){
|
private void startLoop() throws Exception{
|
||||||
StreamProvider.writeToStream(server,"PONG :" + rawData.replace("PING :", ""));
|
String rawMessage;
|
||||||
|
while ((rawMessage = mainReader.readLine()) != null) {
|
||||||
|
if (rawMessage.startsWith(":")) {
|
||||||
|
handleRegular(rawMessage.substring(1));
|
||||||
|
}
|
||||||
|
else if (rawMessage.startsWith("PING :")) {
|
||||||
|
sendPingReply(rawMessage);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
System.out.println(rawMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void handleRegular(String rawMessage){
|
||||||
|
//System.out.println(rawMessage);
|
||||||
|
String[] rawStrings = rawMessage.split(" :?", 3);
|
||||||
|
|
||||||
|
if (rawStrings[1].equals("QUIT") || rawStrings[1].equals("NICK")) {
|
||||||
|
for (IrcChannel ircChannel : ircChannels.values()) {
|
||||||
|
ircChannel.getChannelQueue().add(rawMessage);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String channel = rawStrings[2].replaceAll("(\\s.?$)|(\\s.+?$)", "");
|
||||||
|
|
||||||
|
IrcChannel ircChannel = ircChannels.getOrDefault(channel, systemConsumerChannel);
|
||||||
|
ircChannel.getChannelQueue().add(rawMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendPingReply(String message){
|
||||||
|
StreamProvider.writeToStream(server, message.replaceFirst("PING", "PONG"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void close(){
|
private void close(){
|
||||||
|
try {
|
||||||
|
systemConsumerThread.interrupt();
|
||||||
|
systemConsumerThread.join();
|
||||||
StreamProvider.delStream(server);
|
StreamProvider.delStream(server);
|
||||||
ReconnectControl.notify(server);
|
ReconnectControl.notify(server);
|
||||||
}
|
}
|
||||||
|
catch (InterruptedException ignored){}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,20 +5,17 @@ import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
|
|
||||||
public class StreamProvider {
|
public class StreamProvider {
|
||||||
|
|
||||||
private static final HashMap<String, OutputStreamWriter> srvStreamMap = new HashMap<>();
|
private static final HashMap<String, OutputStreamWriter> srvStreamMap = new HashMap<>();
|
||||||
private static final HashMap<String, BlockingQueue<String>> srvSysConsumersMap = new HashMap<>();
|
|
||||||
|
|
||||||
public static synchronized void writeToStream(String server, String message){
|
public static synchronized void writeToStream(String server, String message){
|
||||||
try {
|
try {
|
||||||
srvStreamMap.get(server).write(message+"\n");
|
srvStreamMap.get(server).write(message+"\n");
|
||||||
srvStreamMap.get(server).flush();
|
srvStreamMap.get(server).flush();
|
||||||
// If this application says something, then pass it into system consumer thread to handle
|
|
||||||
if (message.startsWith("PRIVMSG ")) {
|
if (message.startsWith("PRIVMSG ")) {
|
||||||
srvSysConsumersMap.get(server).add("INNA "+message);
|
SystemConsumer.getSystemConsumer(server).add("INNA "+message);
|
||||||
}
|
}
|
||||||
} catch (IOException e){
|
} catch (IOException e){
|
||||||
System.out.println("Internal issue: StreamProvider->writeToStream() caused I/O exception:\n\t"+e.getMessage());
|
System.out.println("Internal issue: StreamProvider->writeToStream() caused I/O exception:\n\t"+e.getMessage());
|
||||||
|
@ -30,10 +27,5 @@ public class StreamProvider {
|
||||||
}
|
}
|
||||||
public static synchronized void delStream(String server){
|
public static synchronized void delStream(String server){
|
||||||
srvStreamMap.remove(server);
|
srvStreamMap.remove(server);
|
||||||
srvSysConsumersMap.remove(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized void setSysConsumer(String server, BlockingQueue<String> queue){
|
|
||||||
srvSysConsumersMap.put(server, queue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
86
src/main/java/InnaIrcBot/ProvidersConsumers/SystemCTCP.java
Normal file
86
src/main/java/InnaIrcBot/ProvidersConsumers/SystemCTCP.java
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package InnaIrcBot.ProvidersConsumers;
|
||||||
|
|
||||||
|
import InnaIrcBot.GlobalData;
|
||||||
|
import InnaIrcBot.logging.WorkerSystem;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
public class SystemCTCP {
|
||||||
|
private final String server;
|
||||||
|
private LocalDateTime lastReplyTime;
|
||||||
|
private final int cooldownTime;
|
||||||
|
private final WorkerSystem writerWorker;
|
||||||
|
|
||||||
|
SystemCTCP(String server, int cooldownTime, WorkerSystem writerWorker){
|
||||||
|
this.server = server;
|
||||||
|
this.lastReplyTime = LocalDateTime.now();
|
||||||
|
this.cooldownTime = cooldownTime;
|
||||||
|
this.writerWorker = writerWorker;
|
||||||
|
}
|
||||||
|
|
||||||
|
void replyCTCP(String sender, String message) {
|
||||||
|
if (isTooManyRequests())
|
||||||
|
return;
|
||||||
|
|
||||||
|
lastReplyTime = LocalDateTime.now();
|
||||||
|
|
||||||
|
switch (message) {
|
||||||
|
case "\u0001VERSION\u0001":
|
||||||
|
replyVersion(sender);
|
||||||
|
log("CTCP VERSION from", sender);
|
||||||
|
return;
|
||||||
|
case "\u0001CLIENTINFO\u0001":
|
||||||
|
replyClientInfo(sender);
|
||||||
|
log("CTCP CLIENTINFO from", sender);
|
||||||
|
return;
|
||||||
|
case "\u0001TIME\u0001":
|
||||||
|
replyTime(sender);
|
||||||
|
log( "CTCP TIME from", sender);
|
||||||
|
return;
|
||||||
|
case "\u0001SOURCE\u0001":
|
||||||
|
replySource(sender);
|
||||||
|
log( "CTCP TIME from", sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.startsWith("\u0001PING ") && message.endsWith("\u0001")) {
|
||||||
|
replyPing(sender, message);
|
||||||
|
log( "CTCP PING from", sender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log( "CTCP not supported: \"" + message + "\" from ", sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTooManyRequests(){
|
||||||
|
return lastReplyTime.isAfter(LocalDateTime.now().minusSeconds(cooldownTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replyVersion(String sender){
|
||||||
|
reply("NOTICE " + sender + " :\u0001VERSION " + GlobalData.getAppVersion() + "\u0001");
|
||||||
|
}
|
||||||
|
private void replyClientInfo(String sender){
|
||||||
|
reply("NOTICE " + sender + " :\u0001CLIENTINFO ACTION PING VERSION TIME CLIENTINFO SOURCE\u0001");
|
||||||
|
}
|
||||||
|
private void replyTime(String sender){
|
||||||
|
reply("NOTICE " + sender + " :\u0001TIME " + timeStamp() + "\u0001");
|
||||||
|
}
|
||||||
|
private void replySource(String sender){
|
||||||
|
reply("NOTICE " + sender + " :\u0001SOURCE " + GlobalData.applicationHomePage + "\u0001");
|
||||||
|
}
|
||||||
|
private void replyPing(String sender, String message){
|
||||||
|
reply("NOTICE " + sender + " :" + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reply(String message){
|
||||||
|
StreamProvider.writeToStream(server, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void log(String event, String sender){
|
||||||
|
writerWorker.log(event, sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String timeStamp(){
|
||||||
|
return ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,76 +4,78 @@ import InnaIrcBot.Commanders.CTCPHelper;
|
||||||
import InnaIrcBot.Commanders.PrivateMsgCommander;
|
import InnaIrcBot.Commanders.PrivateMsgCommander;
|
||||||
import InnaIrcBot.ReconnectControl;
|
import InnaIrcBot.ReconnectControl;
|
||||||
import InnaIrcBot.config.ConfigurationFile;
|
import InnaIrcBot.config.ConfigurationFile;
|
||||||
import InnaIrcBot.GlobalData;
|
|
||||||
import InnaIrcBot.IrcChannel;
|
import InnaIrcBot.IrcChannel;
|
||||||
import InnaIrcBot.logging.LogDriver;
|
import InnaIrcBot.logging.LogDriver;
|
||||||
import InnaIrcBot.logging.WorkerSystem;
|
import InnaIrcBot.logging.WorkerSystem;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
|
||||||
public class SystemConsumer implements Runnable{
|
public class SystemConsumer implements Runnable{
|
||||||
private final BlockingQueue<String> systemQueue;
|
private final BlockingQueue<String> systemQueue;
|
||||||
private WorkerSystem writerWorker;
|
private final WorkerSystem writerWorker;
|
||||||
private String nick;
|
private String nick;
|
||||||
private String serverName;
|
private final String server;
|
||||||
private final Map<String, IrcChannel> channels;
|
private final Map<String, IrcChannel> channels;
|
||||||
private ConfigurationFile configurationFile;
|
private final ConfigurationFile configurationFile;
|
||||||
|
|
||||||
private PrivateMsgCommander commander;
|
private final PrivateMsgCommander commander;
|
||||||
|
|
||||||
private LocalDateTime lastCTCPReplyTime;
|
private final ArrayList<Thread> channelThreads;
|
||||||
|
|
||||||
private ArrayList<Thread> channelThreads;
|
|
||||||
private int nickTail = 0;
|
private int nickTail = 0;
|
||||||
|
private final SystemCTCP systemCTCP;
|
||||||
|
|
||||||
SystemConsumer(BlockingQueue<String> systemQueue, String userNick, Map<String, IrcChannel> channels, ConfigurationFile configurationFile) {
|
private static final HashMap<String, BlockingQueue<String>> systemConsumers = new HashMap<>();
|
||||||
this.systemQueue = systemQueue;
|
public static synchronized BlockingQueue<String> getSystemConsumer(String server){
|
||||||
this.writerWorker = LogDriver.getSystemWorker(configurationFile.getServerName());
|
return systemConsumers.get(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemConsumer(String userNick, Map<String, IrcChannel> channels, ConfigurationFile configurationFile) {
|
||||||
|
this.systemQueue = channels.get("").getChannelQueue();
|
||||||
this.nick = userNick;
|
this.nick = userNick;
|
||||||
this.serverName = configurationFile.getServerName();
|
this.server = configurationFile.getServerName();
|
||||||
|
this.writerWorker = LogDriver.getSystemWorker(server);
|
||||||
this.channels = channels;
|
this.channels = channels;
|
||||||
this.channelThreads = new ArrayList<>();
|
this.channelThreads = new ArrayList<>();
|
||||||
this.configurationFile = configurationFile;
|
this.configurationFile = configurationFile;
|
||||||
this.commander = new PrivateMsgCommander(serverName, this.configurationFile.getBotAdministratorPassword());
|
this.commander = new PrivateMsgCommander(server, this.configurationFile.getBotAdministratorPassword());
|
||||||
|
this.systemCTCP = new SystemCTCP(server, configurationFile.getCooldownTime(), writerWorker);
|
||||||
|
|
||||||
lastCTCPReplyTime = LocalDateTime.now();
|
systemConsumers.put(server, systemQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] THREAD "+serverName+":[system] started"); // TODO:REMOVE DEBUG
|
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
|
||||||
|
+"] Thread SystemConsumer \""+ server +"\": started"); // TODO:REMOVE
|
||||||
|
|
||||||
setMainRoutine();
|
startMainRoutine();
|
||||||
|
close();
|
||||||
|
|
||||||
for (Thread channel : channelThreads) { //TODO: check, code duplication. see Data provider constructor
|
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
|
||||||
channel.interrupt();
|
+"] Thread SystemConsumer \""+ server +"\": ended"); // TODO:REMOVE
|
||||||
}
|
}
|
||||||
|
|
||||||
writerWorker.close();
|
private void startMainRoutine(){
|
||||||
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] THREAD "+serverName+":[system] ended"); // TODO:REMOVE DEBUG
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setMainRoutine(){
|
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
String data = systemQueue.take();
|
String data = systemQueue.take();
|
||||||
String[] dataStrings = data.split(" ",3);
|
String[] dataStrings = data.split(" :?",3);
|
||||||
//TODO: handle mode change
|
//TODO: handle mode change
|
||||||
switch (dataStrings[0]){
|
switch (dataStrings[1]){
|
||||||
case "PRIVMSG":
|
case "PRIVMSG":
|
||||||
if (dataStrings[2].indexOf("\u0001") < dataStrings[2].lastIndexOf("\u0001")) {
|
if (dataStrings[2].indexOf("\u0001") < dataStrings[2].lastIndexOf("\u0001")) {
|
||||||
replyCTCP(simplifyNick(dataStrings[1]), dataStrings[2].substring(dataStrings[2].indexOf(":") + 1));
|
String sender = simplifyNick(dataStrings[0]);
|
||||||
|
String message = dataStrings[2].substring(dataStrings[2].indexOf(":") + 1);
|
||||||
|
systemCTCP.replyCTCP(sender, message);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
commander.receiver(dataStrings[1], dataStrings[2].replaceAll("^.+?:", "").trim());
|
commander.receiver(dataStrings[0], dataStrings[2].replaceAll("^.+?:", "").trim());
|
||||||
writerWorker.logAdd("[system]", "PRIVMSG from "+dataStrings[1]+" received: ",
|
writerWorker.log(dataStrings[1]+" "+dataStrings[0]+" :", dataStrings[2].replaceAll("^.+?:", "").trim());
|
||||||
dataStrings[2].replaceAll("^.+?:", "").trim());
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "INNA":
|
case "INNA":
|
||||||
|
@ -81,7 +83,7 @@ public class SystemConsumer implements Runnable{
|
||||||
if (dataStrings.length > 2){ // Don't touch 'cuz it's important
|
if (dataStrings.length > 2){ // Don't touch 'cuz it's important
|
||||||
splitter = dataStrings[2].split(" ", 2);
|
splitter = dataStrings[2].split(" ", 2);
|
||||||
if (splitter.length == 2){
|
if (splitter.length == 2){
|
||||||
handleSpecial(dataStrings[1], splitter[0], splitter[1]);
|
handleSpecial(dataStrings[0], splitter[0], splitter[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -91,46 +93,14 @@ public class SystemConsumer implements Runnable{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InterruptedException ie){
|
catch (InterruptedException ie){
|
||||||
System.out.println("Thread SystemConsumer->run() interrupted."); // TODO: reconnect OR AT LEAST DIE
|
System.out.println("Thread SystemConsumer interrupted."); // TODO: reconnect OR AT LEAST DIE
|
||||||
}
|
}
|
||||||
catch (Exception e){
|
catch (Exception e){
|
||||||
System.out.println("Internal issue: thread SystemConsumer->run(): "+e.getMessage()); // TODO: DO.. some thing
|
System.out.println("Internal issue: SystemConsumer: "+e.getMessage()); // TODO: DO.. some thing
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void replyCTCP(String sender, String message) { // got simplified nick
|
|
||||||
// TODO: Consider moving to config file. Now set to 3 sec
|
|
||||||
if (lastCTCPReplyTime.isAfter(LocalDateTime.now().minusSeconds(3)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
lastCTCPReplyTime = LocalDateTime.now();
|
|
||||||
|
|
||||||
switch (message) {
|
|
||||||
case "\u0001VERSION\u0001":
|
|
||||||
StreamProvider.writeToStream(serverName, "NOTICE " + sender + " :\u0001VERSION " + GlobalData.getAppVersion() + "\u0001");
|
|
||||||
writerWorker.logAdd("[system]", "catch/handled CTCP VERSION from", sender);
|
|
||||||
return;
|
|
||||||
case "\u0001CLIENTINFO\u0001":
|
|
||||||
StreamProvider.writeToStream(serverName, "NOTICE " + sender + " :\u0001CLIENTINFO ACTION PING VERSION TIME CLIENTINFO SOURCE\u0001");
|
|
||||||
writerWorker.logAdd("[system]", "catch/handled CTCP CLIENTINFO from", sender);
|
|
||||||
return;
|
|
||||||
case "\u0001TIME\u0001":
|
|
||||||
StreamProvider.writeToStream(serverName, "NOTICE " + sender + " :\u0001TIME " + ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME) + "\u0001");
|
|
||||||
writerWorker.logAdd("[system]", "catch/handled CTCP TIME from", sender);
|
|
||||||
return;
|
|
||||||
case "\u0001SOURCE\u0001":
|
|
||||||
StreamProvider.writeToStream(serverName, "NOTICE " + sender + " :\u0001SOURCE https://github.com/developersu/InnaIrcBot\u0001");
|
|
||||||
writerWorker.logAdd("[system]", "catch/handled CTCP TIME from", sender);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (message.startsWith("\u0001PING ") && message.endsWith("\u0001")) {
|
|
||||||
StreamProvider.writeToStream(serverName, "NOTICE " + sender + " :" + message);
|
|
||||||
writerWorker.logAdd("[system]", "catch/handled CTCP PING from", sender);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
writerWorker.logAdd("[system]", "catch unknown CTCP request \"" + message + "\" from ", sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String simplifyNick(String nick){ return nick.replaceAll("!.*$",""); }
|
private String simplifyNick(String nick){ return nick.replaceAll("!.*$",""); }
|
||||||
|
|
||||||
private void handleSpecial(String event, String channelName, String message){
|
private void handleSpecial(String event, String channelName, String message){
|
||||||
|
@ -138,22 +108,22 @@ public class SystemConsumer implements Runnable{
|
||||||
if (ircChannel == null)
|
if (ircChannel == null)
|
||||||
return;
|
return;
|
||||||
String ircFormatterMessage = event+" "+nick+" "+channelName+" "+message;
|
String ircFormatterMessage = event+" "+nick+" "+channelName+" "+message;
|
||||||
//System.out.println("Formatted: |"+event+"|"+nick+"|"+channelName+" "+message+"|");
|
|
||||||
ircChannel.getChannelQueue().add(ircFormatterMessage);
|
ircChannel.getChannelQueue().add(ircFormatterMessage);
|
||||||
}
|
}
|
||||||
//todo: handle nickserv messages somehow
|
//todo: handle nickserv messages somehow
|
||||||
private void handleNumeric(String eventNum, String sender, String message) throws Exception{
|
private void handleNumeric(String sender, String eventNum, String message) throws Exception{
|
||||||
switch (eventNum){
|
switch (eventNum){
|
||||||
case "501": // Notify user about incorrect setup
|
case "501": // Notify user about incorrect setup
|
||||||
writerWorker.logAdd("[system]", "catch/handled:", eventNum
|
writerWorker.log("catch/handled:", eventNum
|
||||||
+ " [MODE message was sent with a nickname parameter and that the a mode flag sent was not recognized.]");
|
+ " [MODE message was sent with a nickname parameter and that the a mode flag sent was not recognized.]");
|
||||||
break;
|
break;
|
||||||
case "433": // TODO: try to use alternative nickname
|
case "433": // TODO: try to use alternative nickname
|
||||||
writerWorker.logAdd("[system]", "catch/handled:", eventNum
|
writerWorker.log("catch/handled:", eventNum
|
||||||
+ " [nickname already in use and will be changed]");
|
+ " [nickname already in use and will be changed]");
|
||||||
break;
|
break;
|
||||||
case "353":
|
case "353":
|
||||||
writerWorker.logAdd("[system]", "catch/handled:", eventNum+" [RPL_NAMREPLY]");
|
writerWorker.log("catch/handled:", eventNum+" [RPL_NAMREPLY]");
|
||||||
String channelName = message.substring(nick.length()+3).replaceAll("\\s.*$", "");
|
String channelName = message.substring(nick.length()+3).replaceAll("\\s.*$", "");
|
||||||
|
|
||||||
IrcChannel ircChannel = channels.get(channelName);
|
IrcChannel ircChannel = channels.get(channelName);
|
||||||
|
@ -164,7 +134,7 @@ public class SystemConsumer implements Runnable{
|
||||||
case "NICK":
|
case "NICK":
|
||||||
if (sender.startsWith(nick+"!")) {
|
if (sender.startsWith(nick+"!")) {
|
||||||
nick = message.trim();
|
nick = message.trim();
|
||||||
writerWorker.logAdd("[system]", "catch own NICK change:", sender+" to: "+message);
|
writerWorker.log("catch own NICK change:", sender+" to: "+message);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "JOIN":
|
case "JOIN":
|
||||||
|
@ -182,43 +152,53 @@ public class SystemConsumer implements Runnable{
|
||||||
newIrcChannelThread.start();
|
newIrcChannelThread.start();
|
||||||
channelThreads.add(newIrcChannelThread);
|
channelThreads.add(newIrcChannelThread);
|
||||||
//proxyAList.get(message).add(eventNum+" "+sender+" "+message); // Add message to array linked
|
//proxyAList.get(message).add(eventNum+" "+sender+" "+message); // Add message to array linked
|
||||||
writerWorker.logAdd("[system]", "joined to channel ", message);
|
writerWorker.log("joined channel ", message);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "401": // No such nick/channel
|
case "401": // No such nick/channel
|
||||||
//System.out.println("|"+message.replaceAll("^(\\s)?.+?(\\s)|((\\s)?:No such nick/channel)","")+"|");
|
//System.out.println("|"+message.replaceAll("^(\\s)?.+?(\\s)|((\\s)?:No such nick/channel)","")+"|");
|
||||||
CTCPHelper.getInstance().handleErrorReply(serverName, message.replaceAll("^(\\s)?.+?(\\s)|((\\s)?:No such nick/channel)",""));
|
CTCPHelper.getInstance().handleErrorReply(server, message.replaceAll("^(\\s)?.+?(\\s)|((\\s)?:No such nick/channel)",""));
|
||||||
writerWorker.logAdd("[system]", "catch: "+eventNum+" from: "+sender+" :",message+" [ok]");
|
writerWorker.log("catch: "+eventNum+" from: "+sender+" :",message+" [ok]");
|
||||||
break;
|
break;
|
||||||
case "NOTICE":
|
case "NOTICE":
|
||||||
CTCPHelper.getInstance().handleCtcpReply(serverName, simplifyNick(sender), message.replaceAll("^.+?:", "").trim());
|
CTCPHelper.getInstance().handleCtcpReply(server, simplifyNick(sender), message.replaceAll("^.+?:", "").trim());
|
||||||
writerWorker.logAdd("[system]", "NOTICE from "+sender+" received: ", message.replaceAll("^.+?:", "").trim());
|
writerWorker.log("NOTICE from "+sender+" received: ", message.replaceAll("^.+?:", "").trim());
|
||||||
break;
|
break;
|
||||||
case "001":
|
case "001":
|
||||||
sendUserModes();
|
sendUserModes();
|
||||||
sendNickPassword();
|
sendNickPassword();
|
||||||
joinChannels();
|
joinChannels();
|
||||||
|
writerWorker.log(eventNum, message);
|
||||||
break;
|
break;
|
||||||
case "443":
|
case "443":
|
||||||
String newNick = nick+"|"+nickTail++;
|
String newNick = nick+"|"+nickTail++;
|
||||||
StreamProvider.writeToStream(serverName,"NICK "+newNick);
|
StreamProvider.writeToStream(server,"NICK "+newNick);
|
||||||
break;
|
break;
|
||||||
case "464": // password for server/znc/bnc
|
case "464": // password for server/znc/bnc
|
||||||
StreamProvider.writeToStream(serverName,"PASS "+configurationFile.getServerPass());
|
StreamProvider.writeToStream(server,"PASS "+configurationFile.getServerPass());
|
||||||
|
writerWorker.log(eventNum, message);
|
||||||
break;
|
break;
|
||||||
case "432":
|
case "432":
|
||||||
|
writerWorker.log(eventNum, message);
|
||||||
System.out.println("Configuration issue: Nickname contains unacceptable characters (432 ERR_ERRONEUSNICKNAME).");
|
System.out.println("Configuration issue: Nickname contains unacceptable characters (432 ERR_ERRONEUSNICKNAME).");
|
||||||
ReconnectControl.update(serverName, false);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case "465":
|
case "465":
|
||||||
ReconnectControl.update(serverName, false);
|
ReconnectControl.update(server, false);
|
||||||
|
writerWorker.log(eventNum, message);
|
||||||
break;
|
break;
|
||||||
case "QUIT": // TODO: Do something?
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
writerWorker.logAdd("[system]", "catch: "+eventNum+" from: "+sender+" :",message);
|
writerWorker.log("catch: "+eventNum+" from: "+sender+" :", message);
|
||||||
break;
|
break;
|
||||||
// 431 ERR_NONICKNAMEGIVEN how can we get this?
|
// 431 ERR_NONICKNAMEGIVEN how can we get this?
|
||||||
// 436 ERR_NICKCOLLISION
|
// 436 ERR_NICKCOLLISION
|
||||||
|
@ -240,7 +220,7 @@ public class SystemConsumer implements Runnable{
|
||||||
message.append("\n");
|
message.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamProvider.writeToStream(serverName, message.toString());
|
StreamProvider.writeToStream(server, message.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendNickPassword(){
|
private void sendNickPassword(){
|
||||||
|
@ -249,11 +229,11 @@ public class SystemConsumer implements Runnable{
|
||||||
|
|
||||||
switch (configurationFile.getUserNickAuthStyle()){
|
switch (configurationFile.getUserNickAuthStyle()){
|
||||||
case "freenode":
|
case "freenode":
|
||||||
StreamProvider.writeToStream(serverName,"PRIVMSG NickServ :IDENTIFY "
|
StreamProvider.writeToStream(server,"PRIVMSG NickServ :IDENTIFY "
|
||||||
+ configurationFile.getUserNickPass());
|
+ configurationFile.getUserNickPass());
|
||||||
break;
|
break;
|
||||||
case "rusnet":
|
case "rusnet":
|
||||||
StreamProvider.writeToStream(serverName,"NickServ IDENTIFY "
|
StreamProvider.writeToStream(server,"NickServ IDENTIFY "
|
||||||
+ configurationFile.getUserNickPass());
|
+ configurationFile.getUserNickPass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,12 +241,21 @@ public class SystemConsumer implements Runnable{
|
||||||
private void joinChannels(){
|
private void joinChannels(){
|
||||||
StringBuilder joinMessage = new StringBuilder();
|
StringBuilder joinMessage = new StringBuilder();
|
||||||
|
|
||||||
for (String cnl : configurationFile.getChannels()) { // TODO: add validation of channels.
|
for (String channel : configurationFile.getChannels()) { // TODO: add validation of channels.
|
||||||
joinMessage.append("JOIN ");
|
joinMessage.append("JOIN ");
|
||||||
joinMessage.append(cnl);
|
joinMessage.append(channel);
|
||||||
joinMessage.append("\n");
|
joinMessage.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamProvider.writeToStream(serverName, joinMessage.toString());
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,23 +1,35 @@
|
||||||
package InnaIrcBot;
|
package InnaIrcBot;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class ReconnectControl {
|
public class ReconnectControl {
|
||||||
private static final HashMap<String, Boolean> serversList = new HashMap<>();
|
private static final Map<String, Boolean> serversList = Collections.synchronizedMap(new HashMap<>());
|
||||||
public static synchronized void register(String serverName){
|
private static final Map<String, Integer> serversReconnects = Collections.synchronizedMap(new HashMap<>());
|
||||||
serversList.put(serverName, true);
|
|
||||||
|
public static void register(String server){
|
||||||
|
serversList.putIfAbsent(server, true);
|
||||||
|
serversReconnects.putIfAbsent(server, 0);
|
||||||
}
|
}
|
||||||
public static synchronized void update(String serverName, boolean needReconnect){
|
public static void update(String server, boolean needReconnect){
|
||||||
serversList.replace(serverName, needReconnect);
|
serversList.replace(server, needReconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized void notify(String serverName) {
|
public static synchronized void notify(String server){
|
||||||
if (serversList.get(serverName) == null || ! serversList.get(serverName)){
|
if (! serversList.getOrDefault(server, false))
|
||||||
serversList.remove(serverName);
|
return;
|
||||||
|
|
||||||
|
int count = serversReconnects.get(server);
|
||||||
|
|
||||||
|
if (count > 5) {
|
||||||
|
serversList.replace(server, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
count++;
|
||||||
|
serversReconnects.put(server, count);
|
||||||
|
|
||||||
System.out.println("DEBUG: Thread "+serverName+" removed from observable list after unexpected finish.\n\t");
|
System.out.println("Main thread \"" + server + "\" removed from observable list after unexpected finish.\n");
|
||||||
ConnectionsBuilder.getConnections().startNewConnection(serverName);
|
ConnectionsBuilder.getConnections().startNewConnection(server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ public class ConfigurationFile {
|
||||||
private boolean rejoinOnKick;
|
private boolean rejoinOnKick;
|
||||||
private String botAdministratorPassword;
|
private String botAdministratorPassword;
|
||||||
private String applicationLogDir;
|
private String applicationLogDir;
|
||||||
|
private int cooldownTime;
|
||||||
private LogDriverConfiguration logDriverConfiguration;
|
private LogDriverConfiguration logDriverConfiguration;
|
||||||
private List<String> channels;
|
private List<String> channels;
|
||||||
private HashMap<String, ConfigurationChannel> channelConfigs;
|
private HashMap<String, ConfigurationChannel> channelConfigs;
|
||||||
|
@ -39,6 +40,8 @@ public class ConfigurationFile {
|
||||||
public boolean getRejoinOnKick() { return rejoinOnKick; }
|
public boolean getRejoinOnKick() { return rejoinOnKick; }
|
||||||
public String getBotAdministratorPassword() { return botAdministratorPassword; }
|
public String getBotAdministratorPassword() { return botAdministratorPassword; }
|
||||||
public String getApplicationLogDir() { return applicationLogDir; }
|
public String getApplicationLogDir() { return applicationLogDir; }
|
||||||
|
public int getCooldownTime() { return cooldownTime; }
|
||||||
|
|
||||||
public LogDriverConfiguration getLogDriverConfiguration(){ return logDriverConfiguration; }
|
public LogDriverConfiguration getLogDriverConfiguration(){ return logDriverConfiguration; }
|
||||||
public List<String> getChannels() { return channels; }
|
public List<String> getChannels() { return channels; }
|
||||||
public ConfigurationChannel getChannelConfig(String channel) { return channelConfigs.get(channel); }
|
public ConfigurationChannel getChannelConfig(String channel) { return channelConfigs.get(channel); }
|
||||||
|
@ -76,6 +79,7 @@ public class ConfigurationFile {
|
||||||
this.rejoinOnKick = mainSection.get("auto rejoin", boolean.class);
|
this.rejoinOnKick = mainSection.get("auto rejoin", boolean.class);
|
||||||
this.botAdministratorPassword = mainSection.getOrDefault("bot administrator password", "");
|
this.botAdministratorPassword = mainSection.getOrDefault("bot administrator password", "");
|
||||||
this.applicationLogDir = mainSection.getOrDefault("application logs", "");
|
this.applicationLogDir = mainSection.getOrDefault("application logs", "");
|
||||||
|
this.cooldownTime = mainSection.get("cooldown (sec)", int.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseChannels(Wini ini){
|
private void parseChannels(Wini ini){
|
||||||
|
|
|
@ -62,36 +62,6 @@ public class ConfigurationFileGenerator {
|
||||||
if (! Files.exists(folderPath))
|
if (! Files.exists(folderPath))
|
||||||
Files.createDirectories(folderPath);
|
Files.createDirectories(folderPath);
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
private void createConfigurationFileOld() throws IOException{
|
|
||||||
File configurationFile = new File(this.fileLocation);
|
|
||||||
|
|
||||||
Writer writerFile = new OutputStreamWriter(new FileOutputStream(configurationFile.getAbsolutePath()), StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
ConfigurationFile configurationFileObject = new ConfigurationFile("srv",
|
|
||||||
6667,
|
|
||||||
"",
|
|
||||||
new String[] {"#lpr",
|
|
||||||
"#main"},
|
|
||||||
"user_nick",
|
|
||||||
"ident",
|
|
||||||
"bot",
|
|
||||||
"",
|
|
||||||
"freenode",
|
|
||||||
"ix",
|
|
||||||
true,
|
|
||||||
"files",
|
|
||||||
new String[] {System.getProperty("user.home")},
|
|
||||||
"pswd",
|
|
||||||
System.getProperty("user.home"),
|
|
||||||
"/var/logs/"
|
|
||||||
);
|
|
||||||
|
|
||||||
Gson writingStorageObject = new GsonBuilder().setPrettyPrinting().create();
|
|
||||||
writingStorageObject.toJson(configurationFileObject, writerFile);
|
|
||||||
writerFile.close();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
private void createConfigurationFile() throws IOException{
|
private void createConfigurationFile() throws IOException{
|
||||||
final String mainSectionName = "main";
|
final String mainSectionName = "main";
|
||||||
final String channelSectionName = "channels";
|
final String channelSectionName = "channels";
|
||||||
|
@ -127,6 +97,7 @@ public class ConfigurationFileGenerator {
|
||||||
mainSection.put( "auto rejoin", true);
|
mainSection.put( "auto rejoin", true);
|
||||||
mainSection.put( "bot administrator password", "i_pswd");
|
mainSection.put( "bot administrator password", "i_pswd");
|
||||||
mainSection.put( "application logs", "/tmp");
|
mainSection.put( "application logs", "/tmp");
|
||||||
|
mainSection.put("cooldown (sec)", 3);
|
||||||
|
|
||||||
Ini.Section loggingSection = ini.add("logging");
|
Ini.Section loggingSection = ini.add("logging");
|
||||||
loggingSection.put( "driver", "files");
|
loggingSection.put( "driver", "files");
|
||||||
|
|
|
@ -5,7 +5,7 @@ import InnaIrcBot.logging.SupportedLogDrivers;
|
||||||
public class LogDriverConfiguration {
|
public class LogDriverConfiguration {
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private final String path;
|
private String path;
|
||||||
|
|
||||||
private final String mongoURI;
|
private final String mongoURI;
|
||||||
private final String mongoTable;
|
private final String mongoTable;
|
||||||
|
@ -48,12 +48,8 @@ public class LogDriverConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validatePath(){
|
private void validatePath(){
|
||||||
try {
|
if (path == null)
|
||||||
checkFieldNotEmpty(path);
|
path = ".";
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
name = SupportedLogDrivers.zero;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateMongo(){
|
private void validateMongo(){
|
||||||
|
|
|
@ -29,11 +29,14 @@ public class WorkerFiles implements Worker {
|
||||||
createFileWriter();
|
createFileWriter();
|
||||||
consistent = true;
|
consistent = true;
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
System.out.println("BotFilesWorker (@"+server+")->constructor(): Failure:\n" + e.getMessage());
|
System.out.println("WorkerFiles (@"+server+")->constructor(): Failure:\n" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void formatFilePath(String server, String dirLocation){
|
private void formatFilePath(String server, String dirLocation){
|
||||||
|
if (dirLocation.isEmpty())
|
||||||
|
dirLocation = ".";
|
||||||
|
|
||||||
if (! dirLocation.endsWith(File.separator))
|
if (! dirLocation.endsWith(File.separator))
|
||||||
dirLocation += File.separator;
|
dirLocation += File.separator;
|
||||||
|
|
||||||
|
@ -47,13 +50,13 @@ public class WorkerFiles implements Worker {
|
||||||
if (file.isDirectory())
|
if (file.isDirectory())
|
||||||
return;
|
return;
|
||||||
else
|
else
|
||||||
throw new Exception("BotFilesWorker->createServerFolder() "+filePath+" is file while directory expected.");
|
throw new Exception("WorkerFiles->createServerFolder() "+filePath+" is file while directory expected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.mkdirs())
|
if (file.mkdirs())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
throw new Exception("BotFilesWorker->createServerFolder() Can't create directory: "+filePath);
|
throw new Exception("WorkerFiles->createServerFolder() Can't create directory: "+filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetFileWriter() throws IOException{
|
private void resetFileWriter() throws IOException{
|
||||||
|
@ -116,7 +119,7 @@ public class WorkerFiles implements Worker {
|
||||||
fileWriter.write(string);
|
fileWriter.write(string);
|
||||||
fileWriter.flush();
|
fileWriter.flush();
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
System.out.println("BotFilesWorker->prettyPrint() failed\n" +
|
System.out.println("WorkerFiles->prettyPrint() failed\n" +
|
||||||
"\tUnable to write logs to " + this.filePath + " "+e.getMessage());
|
"\tUnable to write logs to " + this.filePath + " "+e.getMessage());
|
||||||
close();
|
close();
|
||||||
consistent = false;
|
consistent = false;
|
||||||
|
@ -180,7 +183,7 @@ public class WorkerFiles implements Worker {
|
||||||
}
|
}
|
||||||
catch (NullPointerException ignore) {}
|
catch (NullPointerException ignore) {}
|
||||||
catch (IOException e){
|
catch (IOException e){
|
||||||
System.out.println("BotFilesWorker->close() failed\n" +
|
System.out.println("WorkerFiles->close() failed\n" +
|
||||||
"\tUnable to properly close file: "+this.filePath); // Live with it.
|
"\tUnable to properly close file: "+this.filePath); // Live with it.
|
||||||
}
|
}
|
||||||
this.consistent = false;
|
this.consistent = false;
|
||||||
|
|
|
@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
public class WorkerMongoDB implements Worker { //TODO consider skipping checks if server already added.
|
public class WorkerMongoDB implements Worker { //TODO consider skipping checks if server already added.
|
||||||
private final static Map<String, MongoClient> serversMap = Collections.synchronizedMap(new HashMap<>());
|
private final static Map<String, MongoClient> serversMap = Collections.synchronizedMap(new HashMap<>());
|
||||||
private final String server;
|
private final String server;
|
||||||
private MongoCollection<Document> collection;
|
private final MongoCollection<Document> collection;
|
||||||
private boolean consistent;
|
private boolean consistent;
|
||||||
|
|
||||||
public WorkerMongoDB(String server, LogDriverConfiguration logDriverConfiguration, String channel) throws Exception{
|
public WorkerMongoDB(String server, LogDriverConfiguration logDriverConfiguration, String channel) throws Exception{
|
||||||
|
@ -59,26 +59,7 @@ public class WorkerMongoDB implements Worker { //TODO consider ski
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
ServerListener mongoServerListener = new ServerListener() {
|
ServerListener mongoServerListener = getServerListener();
|
||||||
@Override
|
|
||||||
public void serverOpening(ServerOpeningEvent serverOpeningEvent) {
|
|
||||||
System.out.println("BotMongoWorker (@"+server+"): ServerListener: Server opened successfully: "+serverOpeningEvent.getServerId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void serverClosed(ServerClosedEvent serverClosedEvent) {
|
|
||||||
System.out.println("BotMongoWorker (@"+server+"): ServerListener: Server has been closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void serverDescriptionChanged(ServerDescriptionChangedEvent serverDescriptionChangedEvent) {
|
|
||||||
if (!serverDescriptionChangedEvent.getNewDescription().isOk()) {
|
|
||||||
close(server); // server recieved by constructor, not this.server
|
|
||||||
System.out.println("BotMongoWorker (@"+server+"): ServerListener: Server description changed (exception occurs): "
|
|
||||||
+ serverDescriptionChangedEvent.getNewDescription().getException());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
MongoClientSettings MCS = MongoClientSettings.builder()
|
MongoClientSettings MCS = MongoClientSettings.builder()
|
||||||
// .addCommandListener(mongoCommandListener)
|
// .addCommandListener(mongoCommandListener)
|
||||||
|
@ -106,6 +87,30 @@ public class WorkerMongoDB implements Worker { //TODO consider ski
|
||||||
|
|
||||||
setClosable();
|
setClosable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ServerListener getServerListener(){
|
||||||
|
return new ServerListener() {
|
||||||
|
@Override
|
||||||
|
public void serverOpening(ServerOpeningEvent serverOpeningEvent) {
|
||||||
|
System.out.println("BotMongoWorker (@"+server+"): ServerListener: Server opened successfully: "+serverOpeningEvent.getServerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serverClosed(ServerClosedEvent serverClosedEvent) {
|
||||||
|
System.out.println("BotMongoWorker (@"+server+"): ServerListener: Server has been closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serverDescriptionChanged(ServerDescriptionChangedEvent serverDescriptionChangedEvent) {
|
||||||
|
if (serverDescriptionChangedEvent.getNewDescription().isOk())
|
||||||
|
return;
|
||||||
|
System.out.println("BotMongoWorker (@"+server+"): ServerListener: Server description changed (exception occurs): "
|
||||||
|
+ serverDescriptionChangedEvent.getNewDescription().getException());
|
||||||
|
close(server); // server recieved by constructor, not this.server
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void setClosable(){
|
private void setClosable(){
|
||||||
if (! consistent)
|
if (! consistent)
|
||||||
return;
|
return;
|
||||||
|
@ -148,23 +153,22 @@ public class WorkerMongoDB implements Worker { //TODO consider ski
|
||||||
document.append("message1", message);
|
document.append("message1", message);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insert(document);
|
||||||
|
|
||||||
|
return consistent;
|
||||||
|
}
|
||||||
|
private void insert(Document document){
|
||||||
try {
|
try {
|
||||||
collection.insertOne(document); // TODO: call finalize?
|
collection.insertOne(document); // TODO: call finalize?
|
||||||
consistent = true; // if no exceptions, then true
|
consistent = true; // if no exceptions, then true
|
||||||
} catch (MongoCommandException mce){
|
|
||||||
System.out.println("BotMongoWorker (@"+this.server +")->logAdd(): Command exception. Check if username/password set correctly.");
|
|
||||||
this.close();
|
|
||||||
} catch (MongoTimeoutException mte) {
|
|
||||||
System.out.println("BotMongoWorker (@"+this.server +")->logAdd(): Timeout exception.");
|
|
||||||
this.close();
|
|
||||||
}catch (MongoException me){
|
}catch (MongoException me){
|
||||||
System.out.println("BotMongoWorker (@"+this.server +")->logAdd(): MongoDB Exception.");
|
System.out.println("BotMongoWorker (@"+this.server +")->logAdd(): MongoDB Exception: "+me.getMessage());
|
||||||
this.close();
|
this.close();
|
||||||
} catch (IllegalStateException ise){
|
} catch (IllegalStateException ise){
|
||||||
System.out.println("BotMongoWorker (@"+this.server +")->logAdd(): Illegal state exception: MongoDB server already closed (not an issue).");
|
System.out.println("BotMongoWorker (@"+this.server +")->logAdd(): Illegal state exception: MongoDB server already closed (not an issue).");
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
return consistent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getDate(){ return System.currentTimeMillis() / 1000L; } // UNIX time
|
private long getDate(){ return System.currentTimeMillis() / 1000L; } // UNIX time
|
||||||
|
|
|
@ -9,8 +9,8 @@ import java.sql.*;
|
||||||
|
|
||||||
public class WorkerSQLite implements Worker {
|
public class WorkerSQLite implements Worker {
|
||||||
|
|
||||||
private Connection connection;
|
private final Connection connection;
|
||||||
private boolean consistent = false;
|
private boolean consistent;
|
||||||
private PreparedStatement preparedStatement;
|
private PreparedStatement preparedStatement;
|
||||||
|
|
||||||
private final String server;
|
private final String server;
|
||||||
|
@ -21,10 +21,11 @@ public class WorkerSQLite implements Worker {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
String dbFileLocation = logDriverConfiguration.getPath();
|
String dbFileLocation = logDriverConfiguration.getPath();
|
||||||
File dir = new File(dbFileLocation);
|
File dir = new File(dbFileLocation);
|
||||||
|
|
||||||
dir.mkdirs(); // ignore result, because if it's already exists we're good. Otherwise, it will be created. Only issue that can occur is SecurityException thrown, so let's catch it.
|
dir.mkdirs(); // ignore result, because if it's already exists we're good. Otherwise, it will be created. Only issue that can occur is SecurityException thrown, so let's catch it.
|
||||||
|
|
||||||
if (! dir.exists()) {
|
if (! dir.exists()) {
|
||||||
throw new Exception("BotSQLiteWorker (@"+server+")->constructor(): Failure:\n\tUnable to create directory to store DB file: " + dbFileLocation);
|
throw new Exception("WorkerSQLite for"+server+": Unable to create directory to store DB file: " + dbFileLocation);
|
||||||
}
|
}
|
||||||
String connectionURL;
|
String connectionURL;
|
||||||
if (dbFileLocation.endsWith(File.separator))
|
if (dbFileLocation.endsWith(File.separator))
|
||||||
|
@ -38,7 +39,21 @@ public class WorkerSQLite implements Worker {
|
||||||
sqlConfig.setOpenMode(SQLiteOpenMode.NOMUTEX); //SQLITE_OPEN_NOMUTEX : multithreaded mode
|
sqlConfig.setOpenMode(SQLiteOpenMode.NOMUTEX); //SQLITE_OPEN_NOMUTEX : multithreaded mode
|
||||||
|
|
||||||
this.connection = DriverManager.getConnection(connectionURL, sqlConfig.toProperties());
|
this.connection = DriverManager.getConnection(connectionURL, sqlConfig.toProperties());
|
||||||
if (connection != null){
|
if (connection == null){
|
||||||
|
System.out.println("WorkerSQLite for"+server+": Connection to SQLite is not established.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
createSQLiteDatabaseTables(channel);
|
||||||
|
this.consistent = true;
|
||||||
|
|
||||||
|
this.preparedStatement = connection.prepareStatement(
|
||||||
|
"INSERT INTO \""+channel
|
||||||
|
+"\" (unixtime, event, subject, message, object) "
|
||||||
|
+"VALUES (?, ?, ?, ?, ?);");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSQLiteDatabaseTables(String channel) throws Exception{
|
||||||
// Create table if not created
|
// Create table if not created
|
||||||
Statement statement = connection.createStatement();
|
Statement statement = connection.createStatement();
|
||||||
String query = "CREATE TABLE IF NOT EXISTS \""+channel+"\" ("
|
String query = "CREATE TABLE IF NOT EXISTS \""+channel+"\" ("
|
||||||
|
@ -120,25 +135,14 @@ public class WorkerSQLite implements Worker {
|
||||||
// Validating result: it table in DB have expected schema. If not, removing and recreating table.
|
// Validating result: it table in DB have expected schema. If not, removing and recreating table.
|
||||||
for (boolean element: schemaResultCheck) {
|
for (boolean element: schemaResultCheck) {
|
||||||
if (! element) {
|
if (! element) {
|
||||||
System.out.println("BotSQLiteWorker (@"+server+")->Constructor(): Notice:\n\tFound already existing table for channel with incorrect syntax: removing table and re-creating.");
|
System.out.println("WorkerSQLite for "+server+": Found already existing table for channel with incorrect syntax: removing and re-creating.");
|
||||||
statement.executeUpdate("DROP TABLE \"" + channel + "\";");
|
statement.executeUpdate("DROP TABLE \"" + channel + "\";");
|
||||||
statement.executeUpdate(query);
|
statement.executeUpdate(query);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.consistent = true;
|
|
||||||
|
|
||||||
this.preparedStatement = connection.prepareStatement(
|
|
||||||
"INSERT INTO \""+channel
|
|
||||||
+"\" (unixtime, event, subject, message, object) "
|
|
||||||
+"VALUES (?, ?, ?, ?, ?);");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.out.println("BotSQLiteWorker (@"+server+")->constructor() failed:\n\t Connection to SQLite not established.");
|
|
||||||
this.consistent = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
private long getDate(){
|
private long getDate(){
|
||||||
return System.currentTimeMillis() / 1000L; // UNIX time
|
return System.currentTimeMillis() / 1000L; // UNIX time
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,60 +13,68 @@ public class WorkerSystem{
|
||||||
private final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("HH:mm:ss");
|
private final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||||
private Closeable thingToCloseOnDie; // call .close() method of this classes when this (system log class) dies.
|
private Closeable thingToCloseOnDie; // call .close() method of this classes when this (system log class) dies.
|
||||||
|
|
||||||
private final String server;
|
private boolean consistent;
|
||||||
|
|
||||||
private boolean consistent = false;
|
private String filePath;
|
||||||
|
|
||||||
public WorkerSystem(String server, String appLogDir){
|
public WorkerSystem(String server, String appLogDir){
|
||||||
this.server = server;
|
|
||||||
|
|
||||||
if (appLogDir.isEmpty()) {
|
|
||||||
appLogDir = System.getProperty("java.io.tmpdir")+File.separator+"innaircbot"+File.separator;
|
|
||||||
}
|
|
||||||
else if (! appLogDir.endsWith(File.separator)) {
|
|
||||||
appLogDir += File.separator;
|
|
||||||
}
|
|
||||||
|
|
||||||
appLogDir += server;
|
|
||||||
|
|
||||||
File logFile = new File(appLogDir);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (! logFile.getParentFile().exists()) {
|
formatFilePath(server, appLogDir);
|
||||||
if (! logFile.getParentFile().mkdirs()){
|
|
||||||
System.out.println("BotSystemWorker (@"+server+")->constructor() failed:\n" +
|
fileWriter = new FileWriter(createServerLogsFile(), true);
|
||||||
"\tUnable to create sub-directory(-ies) to store log file: " + appLogDir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fileWriter = new FileWriter(logFile, true);
|
|
||||||
consistent = true;
|
consistent = true;
|
||||||
} catch (SecurityException e){
|
} catch (Exception e){
|
||||||
System.out.println("BotSystemWorker (@"+server+")->constructor() failed.\n" +
|
System.out.println("BotSystemWorker for "+server+" failed: " + e.getMessage());
|
||||||
"\tUnable to create sub-directory(-ies) to store logs file ("+appLogDir+"):\n\t"+e.getMessage());
|
|
||||||
} catch (IOException oie){
|
|
||||||
System.out.println("BotSystemWorker (@"+server+")->constructor() failed:\n" +
|
|
||||||
"\tUnable to open file to store logs: " + appLogDir + " "+ oie.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void formatFilePath(String server, String dirLocation){
|
||||||
|
if (dirLocation.isEmpty())
|
||||||
|
dirLocation = "./";
|
||||||
|
|
||||||
|
if (dirLocation.endsWith(File.separator))
|
||||||
|
this.filePath = dirLocation+server+".log";
|
||||||
|
else
|
||||||
|
this.filePath = dirLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createServerLogsFile() throws Exception{
|
||||||
|
final File file = new File(filePath);
|
||||||
|
|
||||||
|
if (file.exists()){
|
||||||
|
if (file.isFile())
|
||||||
|
return file;
|
||||||
|
else
|
||||||
|
throw new Exception("WorkerSystem: \""+filePath+"\" is directory while file expected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.createNewFile())
|
||||||
|
return file;
|
||||||
|
|
||||||
|
throw new Exception("WorkerSystem: Can't create file: "+filePath);
|
||||||
|
}
|
||||||
|
|
||||||
private String genDate(){
|
private String genDate(){
|
||||||
return "["+ LocalTime.now().format(dateFormat)+"]";
|
return "["+ LocalTime.now().format(dateFormat)+"]";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logAdd(String event, String initiatorArg, String messageArg) {
|
public void log(String initiatorArg, String messageArg) {
|
||||||
if (consistent) {
|
String message = String.format("%s %s %s\n", genDate(), initiatorArg, messageArg);
|
||||||
|
|
||||||
|
if (consistent)
|
||||||
|
logToFile(message);
|
||||||
|
else
|
||||||
|
System.out.println(message);
|
||||||
|
}
|
||||||
|
private void logToFile(String message){
|
||||||
try {
|
try {
|
||||||
fileWriter.write(genDate() + event + " " + initiatorArg + " " + messageArg + "\n");
|
fileWriter.write(message);
|
||||||
fileWriter.flush();
|
fileWriter.flush();
|
||||||
} catch (Exception e) { // ??? No ideas. Just in case. Consider removing.
|
} catch (Exception e) {
|
||||||
System.out.println("BotSystemWorker (@" + server + ")->logAdd() failed\n\tUnable to write logs of because of exception:\n\t" + e.getMessage());
|
System.out.println("BotSystemWorker: unable to write application logs: " + e.getMessage());
|
||||||
//this.close();
|
|
||||||
consistent = false;
|
consistent = false;
|
||||||
|
//this.close();
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
System.out.println(genDate() + event + " " + initiatorArg + " " + messageArg + "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerInSystemWorker(Closeable thing){
|
public void registerInSystemWorker(Closeable thing){
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package InnaIrcBot.Commanders;
|
package InnaIrcBot.Commanders;
|
||||||
|
|
||||||
import InnaIrcBot.ProvidersConsumers.StreamProvider;
|
import InnaIrcBot.ProvidersConsumers.StreamProvider;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -37,7 +36,6 @@ class JoinFloodHandlerTest {
|
||||||
testSocket.connect(new InetSocketAddress(60000));
|
testSocket.connect(new InetSocketAddress(60000));
|
||||||
|
|
||||||
StreamProvider.setStream(serverName, testSocket);
|
StreamProvider.setStream(serverName, testSocket);
|
||||||
StreamProvider.setSysConsumer(serverName, new ArrayBlockingQueue<>(100));
|
|
||||||
}
|
}
|
||||||
catch (IOException e){
|
catch (IOException e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
Loading…
Reference in a new issue