It was and it is so bad that I refuse to describe changes made. Just one more refactoring iteration ><

This commit is contained in:
Dmitry Isaenko 2020-10-25 23:39:05 +03:00
parent b3da839bcd
commit f3cdfc532c
18 changed files with 806 additions and 700 deletions

View file

@ -30,8 +30,8 @@
<groupId>org.ini4j</groupId>
<artifactId>ini4j</artifactId>
<version>0.5.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>

View file

@ -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[]> nickMap; // Mask(Pattern) ->, Action | Where Action[0] could be: raw
private boolean joinFloodTrackNeed = false;
private boolean joinFloodTrackNeed;
private JoinFloodHandler jfh;
private boolean joinCloneTrackNeed = false; // todo:fix
private boolean joinCloneTrackNeed;
private JoinCloneHandler jch;
public ChanelCommander(BlockingQueue<String> stream, String serverName, String channel) throws Exception{
@ -40,21 +40,21 @@ public class ChanelCommander implements Runnable {
try {
while (true) {
String data = streamQueue.take();
String[] dataStrings = data.split(" ",3);
String[] dataStrings = data.split(" :?",3);
switch (dataStrings[0]) {
switch (dataStrings[1]) {
case "NICK":
nickCame(dataStrings[2]+dataStrings[1].replaceAll("^.+?!","!"));
nickCame(dataStrings[2]+dataStrings[0].replaceAll("^.+?!","!"));
break; // todo: need to track join flood
case "JOIN":
if (joinFloodTrackNeed)
jfh.track(simplifyNick(dataStrings[1]));
jfh.track(simplifyNick(dataStrings[0]));
if (joinCloneTrackNeed)
jch.track(dataStrings[1]);
joinCame(dataStrings[1]);
jch.track(dataStrings[0]);
joinCame(dataStrings[0]);
break;
case "PRIVMSG":
privmsgCame(dataStrings[1], dataStrings[2]);
privmsgCame(dataStrings[0], dataStrings[2]);
break;
/*
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){
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?
@ -94,7 +95,7 @@ public class ChanelCommander implements Runnable {
StringBuilder whatToSendStringBuilder;
ArrayList<String> whatToSendList;
for (int i = 0; i<cmdOrMsg.length;) {
for (int i = 0; i < cmdOrMsg.length;) {
switch (cmdOrMsg[i]) {
case "\\chanmsg":
whatToSendList = new ArrayList<>();
@ -163,15 +164,19 @@ public class ChanelCommander implements Runnable {
else
whatToSendStringBuilder.append(cmdOrMsg[i]);
}
if (objectRegexp != null) {
if (objectRegexp == null)
break;
String objectToCtcp = arg1.trim().replaceAll(objectRegexp, ""); // note: trim() ?
if (!objectToCtcp.isEmpty()){
if (objectToCtcp.isEmpty())
break;
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
CTCPHelper.getInstance().registerRequest(server, simplifyNick(arg2), CTCPType.substring(2).toUpperCase(), objectToCtcp, whatToSendStringBuilder.toString());
}
}
registerCTCPforUser(simplifyNick(arg2), CTCPType.substring(2).toUpperCase(), objectToCtcp, whatToSendStringBuilder.toString());
break;
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.
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();
executiveStr.append("PRIVMSG ");
if(sendToPrivate) {
if(isToUser) {
executiveStr.append(simplifyNick(who));
executiveStr.append(" :");
}
@ -236,6 +248,13 @@ public class ChanelCommander implements Runnable {
private void readConfing() throws Exception{
ConfigurationChannel configChannel = ConfigurationManager.getConfiguration(server).getChannelConfig(channel);
if (configChannel == null){
joinMap = new HashMap<>();
msgMap = new HashMap<>();
nickMap = new HashMap<>();
return;
}
joinMap = configChannel.getJoinMap();
msgMap = configChannel.getMsgMap();
nickMap = configChannel.getNickMap();

View file

@ -18,8 +18,16 @@ public class PrivateMsgCommander { // TODO: add black list: add users afte
}
public void receiver(String sender, String message){
if (!password.isEmpty()) {
if (administrators.contains(sender) && !message.isEmpty()) {
if (password.isEmpty() || 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);
cmd[0] = cmd[0].toLowerCase();
if (cmd.length > 1)
@ -217,13 +225,10 @@ public class PrivateMsgCommander { // TODO: add black list: add users afte
break;
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)");
} // TODO: chanel limits set/remove
} else {
if (!message.isEmpty() && message.startsWith("login ")) {
login(sender, message.replaceAll("^([\t\\s]+)?login([\t\\s]+)|([\t\\s]+$)", ""));
}
}
} // TODO: add options for setting channel limits (MODEs)
}
private boolean isNotAuthorized(String sender){
return ! administrators.contains(sender);
}
private void join(String channel){
@ -233,14 +238,12 @@ public class PrivateMsgCommander { // TODO: add black list: add users afte
raw("PART "+channel);
}
private void quit(String message){
ReconnectControl.update(server, false);
if (message.isEmpty()){
raw("QUIT :"+ GlobalData.getAppVersion());
}
else
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){
message = message.trim();

View file

@ -8,5 +8,6 @@ public class GlobalData {
System.getProperty("os.version"),
System.getProperty("os.arch"));
}
public static final String applicationHomePage = "https://github.com/developersu/InnaIrcBot";
public static final int CHANNEL_QUEUE_CAPACITY = 500;
}

View file

@ -21,10 +21,10 @@ public class ChanConsumer implements Runnable {
private final BlockingQueue<String> chanConsumerQueue;
private final String serverName;
private final String channelName;
private Worker writerWorker;
private ArrayList<String> userList;
private final ArrayList<String> users;
private Worker logWorker;
private String nick;
private final boolean rejoin;
private final boolean autoRejoin;
private final Map<String, IrcChannel> channels;
private Thread channelCommanderThread;
@ -32,17 +32,20 @@ public class ChanConsumer implements Runnable {
private boolean endThread = false;
private boolean hasBeenKicked;
ChanConsumer(String serverName,
IrcChannel thisIrcChannel,
String ownNick,
Map<String, IrcChannel> channels) throws Exception{
Map<String, IrcChannel> channels) throws Exception
{
this.chanConsumerQueue = thisIrcChannel.getChannelQueue();
this.serverName = serverName;
this.channelName = thisIrcChannel.toString();
this.writerWorker = LogDriver.getWorker(serverName, channelName);
this.userList = new ArrayList<>();
this.logWorker = LogDriver.getWorker(serverName, channelName);
this.users = new ArrayList<>();
this.nick = ownNick;
this.rejoin = ConfigurationManager.getConfiguration(serverName).getRejoinOnKick();
this.autoRejoin = ConfigurationManager.getConfiguration(serverName).getRejoinOnKick();
this.channels = channels;
getChanelCommander();
}
@ -55,97 +58,115 @@ public class ChanConsumer implements Runnable {
}
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
try {
while (! endThread) {
data = chanConsumerQueue.take();
dataStrings = data.split(" ",3);
String data = chanConsumerQueue.take();
String[] dataStrings = data.split(" :?",3);
if (! trackUsers(dataStrings[0], dataStrings[1], dataStrings[2]))
if (trackUsers(dataStrings[1], dataStrings[0], dataStrings[2]))
continue;
// Send to chanel commander thread
queue.add(data); // TODO: Check and add consistency validation
// Send to channel commander thread
// 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:
this.fixLogDriverIssues(dataStrings[0], dataStrings[1], dataStrings[2]);
if (! logWorker.logAdd(dataStrings[1], dataStrings[0], dataStrings[2])){ // Write logs checks if LogDriver consistent.
this.fixLogDriverIssues(dataStrings[1], dataStrings[0], dataStrings[2]);
}
}
channels.remove(channelName);
} 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
channelCommanderThread.interrupt();
close();
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) {
case "PRIVMSG": // most common, we don't have to handle anything else
return true;
return false;
case "JOIN":
addUser(simplifyNick(initiatorArg));
return true;
addUser(initiator);
return false;
case "PART":
deleteUser(simplifyNick(initiatorArg)); // nick non-simple
return true;
deleteUser(initiator);
return false;
case "QUIT":
if (userList.contains(simplifyNick(initiatorArg))) {
deleteUser(simplifyNick(initiatorArg)); // nick non-simple
return true;
if (users.contains(initiator)) {
deleteUser(initiator);
return false;
}
else
return false; // user quit, but he/she is not in this channel
return true; // user quit, but he/she is not in this channel
case "KICK":
if (rejoin && nick.equals(subjectArg.replaceAll("(^.+?\\s)|(\\s.+$)", ""))) // if it's me and I have rejoin policy 'Auto-Rejoin on kick'.
StreamProvider.writeToStream(serverName, "JOIN " + channelName);
deleteUser(subjectArg.replaceAll("(^.+?\\s)|(\\s.+$)", "")); // nick already simplified
String kickedUser = subject.replaceAll("(^.+?\\s)|(\\s.+$)", "");
if (nick.equals(kickedUser) && autoRejoin) { // TODO: FIX
hasBeenKicked = true;
deleteUser(kickedUser);
return true;
}
deleteUser(kickedUser);
return false;
case "NICK":
if (userList.contains(simplifyNick(initiatorArg))) {
swapUsers(simplifyNick(initiatorArg), subjectArg);
return true;
}
else {
return false; // user changed nick, but he/she is not in this channel
if (users.contains(initiator)) {
swapUsers(initiator, subject);
return false;
}
return true; // user changed nick, but he/she is not in this channel
case "353":
String userOnChanStr = subjectArg.substring(subjectArg.indexOf(":") + 1);
String userOnChanStr = subject.substring(subject.indexOf(":") + 1);
userOnChanStr = userOnChanStr.replaceAll("[%@+]", "").trim();
String[] usersOnChanArr = userOnChanStr.split(" ");
userList.addAll(Arrays.asList(usersOnChanArr));
users.addAll(Arrays.asList(usersOnChanArr));
return true;
default:
return true;
return false;
}
}
private void addUser(String user){
if (!userList.contains(user))
userList.add(user);
if (! users.contains(user))
users.add(user);
}
private void deleteUser(String user){
if (user.equals(nick)) {
endThread = true;
}
userList.remove(user);
users.remove(user);
}
private void swapUsers(String userNickOld, String userNickNew){
userList.remove(userNickOld);
userList.add(userNickNew);
users.remove(userNickOld);
users.add(userNickNew);
if (userNickOld.equals(nick))
this.nick = userNickNew;
nick = userNickNew;
}
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){
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
if (! writerWorker.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 = LogDriver.getWorker(serverName, channelName); // Reset logDriver and try using the same one
if (! logWorker.logAdd(a, b, c)){ // Write to it what was not written (most likely) and if it's still not consistent:
logWorker = new WorkerZero();
System.out.println("ChanConsumer (@"+serverName+"/"+channelName+")->fixLogDriverIssues(): failed to use defined LogDriver. Using ZeroWorker instead.");
}
}

View file

@ -11,103 +11,53 @@ import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.BlockingQueue;
public class DataProvider implements Runnable {
private final ConfigurationFile configurationFile;
private final ConfigurationFile configuration;
private final String server;
private final String nickName;
private final String nick;
private BufferedReader mainReader;
private Thread systemConsumerThread;
private Map<String, IrcChannel> ircChannels;
private IrcChannel systemConsumerChannel;
/**
* Initiate connection and prepare input/output streams for run()
* */
public DataProvider(ConfigurationFile configurationFile){
this.configurationFile = configurationFile;
this.server = configurationFile.getServerName();
this.nickName = configurationFile.getUserNick();
public DataProvider(ConfigurationFile configuration){
this.configuration = configuration;
this.server = configuration.getServerName();
this.nick = configuration.getUserNick();
}
public void run(){
try {
connect();
} catch (Exception e){
System.out.println("Internal issue: DataProvider->run() caused exception:\n\t"+e.getMessage());
e.printStackTrace();
close();
return;
}
connectSocket();
ReconnectControl.register(server);
LogDriver.setLogDriver(server);
/* Used for sending data into consumers objects*/
Map<String, IrcChannel> ircChannels = Collections.synchronizedMap(new HashMap<>());
ircChannels = Collections.synchronizedMap(new HashMap<>());
systemConsumerChannel = new IrcChannel("");
ircChannels.put(systemConsumerChannel.toString(), systemConsumerChannel);
IrcChannel systemConsumerChannel = new IrcChannel("");
BlockingQueue<String> systemConsumerQueue = systemConsumerChannel.getChannelQueue();
startSystemConsumer();
sendUserNickAndIdent();
Thread SystemConsumerThread = new Thread(
new SystemConsumer(systemConsumerQueue, nickName, ircChannels, this.configurationFile));
SystemConsumerThread.start();
startLoop();
StreamProvider.setSysConsumer(server, systemConsumerQueue); // Register system consumer at StreamProvider
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]);
} catch (Exception e){
System.out.println("DataProvider exception: "+e.getMessage());
}
}
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();
}
}
private void connect() throws Exception{
final int port = configurationFile.getServerPort();
private void connectSocket() throws Exception{
final int port = configuration.getServerPort();
InetAddress inetAddress = InetAddress.getByName(server);
Socket socket = new Socket(); // TODO: set timeout?
Socket socket = new Socket();
for (int i = 0; i < 5; i++) {
socket.connect(new InetSocketAddress(inetAddress, port), 5000); // 5sec
if (socket.isConnected())
@ -117,18 +67,63 @@ public class DataProvider implements Runnable {
throw new Exception("Unable to connect server.");
StreamProvider.setStream(server, socket);
InputStream inStream = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(inStream, StandardCharsets.UTF_8); //TODO set charset in options;
mainReader = new BufferedReader(isr);
//TODO set charset in options;
mainReader = new BufferedReader(
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)
);
}
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){
StreamProvider.writeToStream(server,"PONG :" + rawData.replace("PING :", ""));
private void startLoop() throws Exception{
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(){
try {
systemConsumerThread.interrupt();
systemConsumerThread.join();
StreamProvider.delStream(server);
ReconnectControl.notify(server);
}
catch (InterruptedException ignored){}
}
}

View file

@ -5,20 +5,17 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
public class StreamProvider {
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){
try {
srvStreamMap.get(server).write(message+"\n");
srvStreamMap.get(server).flush();
// If this application says something, then pass it into system consumer thread to handle
if (message.startsWith("PRIVMSG ")) {
srvSysConsumersMap.get(server).add("INNA "+message);
SystemConsumer.getSystemConsumer(server).add("INNA "+message);
}
} catch (IOException e){
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){
srvStreamMap.remove(server);
srvSysConsumersMap.remove(server);
}
public static synchronized void setSysConsumer(String server, BlockingQueue<String> queue){
srvSysConsumersMap.put(server, queue);
}
}

View 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);
}
}

View file

@ -4,76 +4,78 @@ import InnaIrcBot.Commanders.CTCPHelper;
import InnaIrcBot.Commanders.PrivateMsgCommander;
import InnaIrcBot.ReconnectControl;
import InnaIrcBot.config.ConfigurationFile;
import InnaIrcBot.GlobalData;
import InnaIrcBot.IrcChannel;
import InnaIrcBot.logging.LogDriver;
import InnaIrcBot.logging.WorkerSystem;
import java.time.LocalDateTime;
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.BlockingQueue;
public class SystemConsumer implements Runnable{
private final BlockingQueue<String> systemQueue;
private WorkerSystem writerWorker;
private final WorkerSystem writerWorker;
private String nick;
private String serverName;
private final String server;
private final Map<String, IrcChannel> channels;
private ConfigurationFile configurationFile;
private final ConfigurationFile configurationFile;
private PrivateMsgCommander commander;
private final PrivateMsgCommander commander;
private LocalDateTime lastCTCPReplyTime;
private ArrayList<Thread> channelThreads;
private final ArrayList<Thread> channelThreads;
private int nickTail = 0;
private final SystemCTCP systemCTCP;
SystemConsumer(BlockingQueue<String> systemQueue, String userNick, Map<String, IrcChannel> channels, ConfigurationFile configurationFile) {
this.systemQueue = systemQueue;
this.writerWorker = LogDriver.getSystemWorker(configurationFile.getServerName());
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.serverName = configurationFile.getServerName();
this.server = configurationFile.getServerName();
this.writerWorker = LogDriver.getSystemWorker(server);
this.channels = channels;
this.channelThreads = new ArrayList<>();
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
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
channel.interrupt();
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
+"] Thread SystemConsumer \""+ server +"\": ended"); // TODO:REMOVE
}
writerWorker.close();
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] THREAD "+serverName+":[system] ended"); // TODO:REMOVE DEBUG
}
private void setMainRoutine(){
private void startMainRoutine(){
try {
while (true) {
String data = systemQueue.take();
String[] dataStrings = data.split(" ",3);
String[] dataStrings = data.split(" :?",3);
//TODO: handle mode change
switch (dataStrings[0]){
switch (dataStrings[1]){
case "PRIVMSG":
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 {
commander.receiver(dataStrings[1], dataStrings[2].replaceAll("^.+?:", "").trim());
writerWorker.logAdd("[system]", "PRIVMSG from "+dataStrings[1]+" received: ",
dataStrings[2].replaceAll("^.+?:", "").trim());
commander.receiver(dataStrings[0], dataStrings[2].replaceAll("^.+?:", "").trim());
writerWorker.log(dataStrings[1]+" "+dataStrings[0]+" :", dataStrings[2].replaceAll("^.+?:", "").trim());
}
break;
case "INNA":
@ -81,7 +83,7 @@ public class SystemConsumer implements Runnable{
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]);
handleSpecial(dataStrings[0], splitter[0], splitter[1]);
}
}
break;
@ -91,46 +93,14 @@ public class SystemConsumer implements Runnable{
}
}
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){
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 void handleSpecial(String event, String channelName, String message){
@ -138,22 +108,22 @@ public class SystemConsumer implements Runnable{
if (ircChannel == null)
return;
String ircFormatterMessage = event+" "+nick+" "+channelName+" "+message;
//System.out.println("Formatted: |"+event+"|"+nick+"|"+channelName+" "+message+"|");
ircChannel.getChannelQueue().add(ircFormatterMessage);
}
//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){
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.]");
break;
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]");
break;
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.*$", "");
IrcChannel ircChannel = channels.get(channelName);
@ -164,7 +134,7 @@ public class SystemConsumer implements Runnable{
case "NICK":
if (sender.startsWith(nick+"!")) {
nick = message.trim();
writerWorker.logAdd("[system]", "catch own NICK change:", sender+" to: "+message);
writerWorker.log("catch own NICK change:", sender+" to: "+message);
}
break;
case "JOIN":
@ -182,43 +152,53 @@ public class SystemConsumer implements Runnable{
newIrcChannelThread.start();
channelThreads.add(newIrcChannelThread);
//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;
case "401": // 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)",""));
writerWorker.logAdd("[system]", "catch: "+eventNum+" from: "+sender+" :",message+" [ok]");
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(serverName, simplifyNick(sender), message.replaceAll("^.+?:", "").trim());
writerWorker.logAdd("[system]", "NOTICE from "+sender+" received: ", message.replaceAll("^.+?:", "").trim());
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(serverName,"NICK "+newNick);
StreamProvider.writeToStream(server,"NICK "+newNick);
break;
case "464": // password for server/znc/bnc
StreamProvider.writeToStream(serverName,"PASS "+configurationFile.getServerPass());
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).");
ReconnectControl.update(serverName, false);
break;
case "465":
ReconnectControl.update(serverName, false);
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.logAdd("[system]", "catch: "+eventNum+" from: "+sender+" :",message);
writerWorker.log("catch: "+eventNum+" from: "+sender+" :", message);
break;
// 431 ERR_NONICKNAMEGIVEN how can we get this?
// 436 ERR_NICKCOLLISION
@ -240,7 +220,7 @@ public class SystemConsumer implements Runnable{
message.append("\n");
}
StreamProvider.writeToStream(serverName, message.toString());
StreamProvider.writeToStream(server, message.toString());
}
private void sendNickPassword(){
@ -249,11 +229,11 @@ public class SystemConsumer implements Runnable{
switch (configurationFile.getUserNickAuthStyle()){
case "freenode":
StreamProvider.writeToStream(serverName,"PRIVMSG NickServ :IDENTIFY "
StreamProvider.writeToStream(server,"PRIVMSG NickServ :IDENTIFY "
+ configurationFile.getUserNickPass());
break;
case "rusnet":
StreamProvider.writeToStream(serverName,"NickServ IDENTIFY "
StreamProvider.writeToStream(server,"NickServ IDENTIFY "
+ configurationFile.getUserNickPass());
}
}
@ -261,12 +241,21 @@ public class SystemConsumer implements Runnable{
private void joinChannels(){
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(cnl);
joinMessage.append(channel);
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);
}
}

View file

@ -1,23 +1,35 @@
package InnaIrcBot;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class ReconnectControl {
private static final HashMap<String, Boolean> serversList = new HashMap<>();
public static synchronized void register(String serverName){
serversList.put(serverName, true);
private static final Map<String, Boolean> serversList = Collections.synchronizedMap(new HashMap<>());
private static final Map<String, Integer> serversReconnects = Collections.synchronizedMap(new HashMap<>());
public static void register(String server){
serversList.putIfAbsent(server, true);
serversReconnects.putIfAbsent(server, 0);
}
public static synchronized void update(String serverName, boolean needReconnect){
serversList.replace(serverName, needReconnect);
public static void update(String server, boolean needReconnect){
serversList.replace(server, needReconnect);
}
public static synchronized void notify(String serverName) {
if (serversList.get(serverName) == null || ! serversList.get(serverName)){
serversList.remove(serverName);
public static synchronized void notify(String server){
if (! serversList.getOrDefault(server, false))
return;
int count = serversReconnects.get(server);
if (count > 5) {
serversList.replace(server, false);
return;
}
count++;
serversReconnects.put(server, count);
System.out.println("DEBUG: Thread "+serverName+" removed from observable list after unexpected finish.\n\t");
ConnectionsBuilder.getConnections().startNewConnection(serverName);
System.out.println("Main thread \"" + server + "\" removed from observable list after unexpected finish.\n");
ConnectionsBuilder.getConnections().startNewConnection(server);
}
}

View file

@ -23,6 +23,7 @@ public class ConfigurationFile {
private boolean rejoinOnKick;
private String botAdministratorPassword;
private String applicationLogDir;
private int cooldownTime;
private LogDriverConfiguration logDriverConfiguration;
private List<String> channels;
private HashMap<String, ConfigurationChannel> channelConfigs;
@ -39,6 +40,8 @@ public class ConfigurationFile {
public boolean getRejoinOnKick() { return rejoinOnKick; }
public String getBotAdministratorPassword() { return botAdministratorPassword; }
public String getApplicationLogDir() { return applicationLogDir; }
public int getCooldownTime() { return cooldownTime; }
public LogDriverConfiguration getLogDriverConfiguration(){ return logDriverConfiguration; }
public List<String> getChannels() { return channels; }
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.botAdministratorPassword = mainSection.getOrDefault("bot administrator password", "");
this.applicationLogDir = mainSection.getOrDefault("application logs", "");
this.cooldownTime = mainSection.get("cooldown (sec)", int.class);
}
private void parseChannels(Wini ini){

View file

@ -62,36 +62,6 @@ public class ConfigurationFileGenerator {
if (! Files.exists(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{
final String mainSectionName = "main";
final String channelSectionName = "channels";
@ -127,6 +97,7 @@ public class ConfigurationFileGenerator {
mainSection.put( "auto rejoin", true);
mainSection.put( "bot administrator password", "i_pswd");
mainSection.put( "application logs", "/tmp");
mainSection.put("cooldown (sec)", 3);
Ini.Section loggingSection = ini.add("logging");
loggingSection.put( "driver", "files");

View file

@ -5,7 +5,7 @@ import InnaIrcBot.logging.SupportedLogDrivers;
public class LogDriverConfiguration {
private String name;
private final String path;
private String path;
private final String mongoURI;
private final String mongoTable;
@ -48,12 +48,8 @@ public class LogDriverConfiguration {
}
private void validatePath(){
try {
checkFieldNotEmpty(path);
}
catch (Exception e){
name = SupportedLogDrivers.zero;
}
if (path == null)
path = ".";
}
private void validateMongo(){

View file

@ -29,11 +29,14 @@ public class WorkerFiles implements Worker {
createFileWriter();
consistent = true;
} 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){
if (dirLocation.isEmpty())
dirLocation = ".";
if (! dirLocation.endsWith(File.separator))
dirLocation += File.separator;
@ -47,13 +50,13 @@ public class WorkerFiles implements Worker {
if (file.isDirectory())
return;
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())
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{
@ -116,7 +119,7 @@ public class WorkerFiles implements Worker {
fileWriter.write(string);
fileWriter.flush();
} 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());
close();
consistent = false;
@ -180,7 +183,7 @@ public class WorkerFiles implements Worker {
}
catch (NullPointerException ignore) {}
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.
}
this.consistent = false;

View file

@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit;
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 String server;
private MongoCollection<Document> collection;
private final MongoCollection<Document> collection;
private boolean consistent;
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() {
@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());
}
}
};
ServerListener mongoServerListener = getServerListener();
MongoClientSettings MCS = MongoClientSettings.builder()
// .addCommandListener(mongoCommandListener)
@ -106,6 +87,30 @@ public class WorkerMongoDB implements Worker { //TODO consider ski
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(){
if (! consistent)
return;
@ -148,23 +153,22 @@ public class WorkerMongoDB implements Worker { //TODO consider ski
document.append("message1", message);
break;
}
insert(document);
return consistent;
}
private void insert(Document document){
try {
collection.insertOne(document); // TODO: call finalize?
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){
System.out.println("BotMongoWorker (@"+this.server +")->logAdd(): MongoDB Exception.");
System.out.println("BotMongoWorker (@"+this.server +")->logAdd(): MongoDB Exception: "+me.getMessage());
this.close();
} catch (IllegalStateException ise){
System.out.println("BotMongoWorker (@"+this.server +")->logAdd(): Illegal state exception: MongoDB server already closed (not an issue).");
this.close();
}
return consistent;
}
private long getDate(){ return System.currentTimeMillis() / 1000L; } // UNIX time

View file

@ -9,8 +9,8 @@ import java.sql.*;
public class WorkerSQLite implements Worker {
private Connection connection;
private boolean consistent = false;
private final Connection connection;
private boolean consistent;
private PreparedStatement preparedStatement;
private final String server;
@ -21,10 +21,11 @@ public class WorkerSQLite implements Worker {
this.server = server;
String dbFileLocation = logDriverConfiguration.getPath();
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.
if (!dir.exists()) {
throw new Exception("BotSQLiteWorker (@"+server+")->constructor(): Failure:\n\tUnable to create directory to store DB file: " + dbFileLocation);
if (! dir.exists()) {
throw new Exception("WorkerSQLite for"+server+": Unable to create directory to store DB file: " + dbFileLocation);
}
String connectionURL;
if (dbFileLocation.endsWith(File.separator))
@ -38,7 +39,21 @@ public class WorkerSQLite implements Worker {
sqlConfig.setOpenMode(SQLiteOpenMode.NOMUTEX); //SQLITE_OPEN_NOMUTEX : multithreaded mode
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
Statement statement = connection.createStatement();
String query = "CREATE TABLE IF NOT EXISTS \""+channel+"\" ("
@ -119,26 +134,15 @@ public class WorkerSQLite implements Worker {
}
// Validating result: it table in DB have expected schema. If not, removing and recreating table.
for (boolean element: schemaResultCheck) {
if (!element) {
System.out.println("BotSQLiteWorker (@"+server+")->Constructor(): Notice:\n\tFound already existing table for channel with incorrect syntax: removing table and re-creating.");
if (! element) {
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(query);
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(){
return System.currentTimeMillis() / 1000L; // UNIX time
}

View file

@ -13,60 +13,68 @@ public class WorkerSystem{
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 final String server;
private boolean consistent;
private boolean consistent = false;
private String filePath;
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 {
if (! logFile.getParentFile().exists()) {
if (! logFile.getParentFile().mkdirs()){
System.out.println("BotSystemWorker (@"+server+")->constructor() failed:\n" +
"\tUnable to create sub-directory(-ies) to store log file: " + appLogDir);
return;
}
}
fileWriter = new FileWriter(logFile, true);
formatFilePath(server, appLogDir);
fileWriter = new FileWriter(createServerLogsFile(), true);
consistent = true;
} catch (SecurityException e){
System.out.println("BotSystemWorker (@"+server+")->constructor() failed.\n" +
"\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());
} catch (Exception e){
System.out.println("BotSystemWorker for "+server+" failed: " + e.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(){
return "["+ LocalTime.now().format(dateFormat)+"] ";
return "["+ LocalTime.now().format(dateFormat)+"]";
}
public void logAdd(String event, String initiatorArg, String messageArg) {
if (consistent) {
public void log(String initiatorArg, String messageArg) {
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 {
fileWriter.write(genDate() + event + " " + initiatorArg + " " + messageArg + "\n");
fileWriter.write(message);
fileWriter.flush();
} catch (Exception e) { // ??? No ideas. Just in case. Consider removing.
System.out.println("BotSystemWorker (@" + server + ")->logAdd() failed\n\tUnable to write logs of because of exception:\n\t" + e.getMessage());
//this.close();
} catch (Exception e) {
System.out.println("BotSystemWorker: unable to write application logs: " + e.getMessage());
consistent = false;
//this.close();
}
return;
}
System.out.println(genDate() + event + " " + initiatorArg + " " + messageArg + "\n");
}
public void registerInSystemWorker(Closeable thing){

View file

@ -1,7 +1,6 @@
package InnaIrcBot.Commanders;
import InnaIrcBot.ProvidersConsumers.StreamProvider;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@ -37,7 +36,6 @@ class JoinFloodHandlerTest {
testSocket.connect(new InetSocketAddress(60000));
StreamProvider.setStream(serverName, testSocket);
StreamProvider.setSysConsumer(serverName, new ArrayBlockingQueue<>(100));
}
catch (IOException e){
e.printStackTrace();