v0.4 release

This commit is contained in:
Dmitry Isaenko 2019-01-27 06:19:31 +03:00
parent cd1e70838c
commit 9fe3b5e11b
16 changed files with 223 additions and 51 deletions

View file

@ -0,0 +1,25 @@
{
"serverName": "irc.tomsk.net",
"serverPort": 6666,
"serverPass": "",
"channels": [
"#lpr"
],
"userNick": "InnaBot",
"userIdent": "rusnet",
"userRealName": "IRCBot",
"userNickPass": "",
"userNickAuthStyle": "rusnet",
"userMode": "x",
"rejoinOnKick": true,
"logDriver": "MongoDB",
"logDriverParameters": [
"192.168.1.186:27017",
"irc",
"mongo_user_name",
"mongo_password"
],
"botAdministratorPassword": "very_secret_password",
"chanelConfigurationsPath": "/home/USERNAME/TSV_CONF_FOLDER/",
"applicationLogDir": "/tmp/appLogs"
}

View file

@ -16,6 +16,7 @@ Another one IRC bot in deep-deep beta.
"logDriver" could be "Files", "SQLite" or "Zero"
* Files - log everything to files using /yourPathSet/serverName/#chanelName_YYYY-MM-DD.txt format.
* SQLite - use /yourPathSet/server.db (or /yourPathSet/yourFileName.db) sqlite file.
* MongoDB - write files to MongoDB. See ConfigurationExamples folder.
## License
Source code spreads under the GNU General Public License v3 or higher. Please see LICENSE file.
@ -31,7 +32,7 @@ Used libraries:
- [ ] Code refactoring
- [ ] QA: good regression testing
- [x] CI/CD Jenkins
- [ ] Suppress messages from server or handle them separately from selected worker
- [x] Suppress messages from server or handle them separately from selected worker
- [ ] Logs backend workers as threads (SQLite and co. are too slow)
- [x] Logs backend worker for mongodb
- [ ] Logs backend worker for redis/redis node
@ -44,4 +45,4 @@ Used libraries:
- [ ] ncurses-like or/and GUI configuration files (server/chanel setting) editor
- [ ] CTCP support for using @ 'ChanelCommander'
- [ ] Access roles support (i.e. delegating some rights to another users)
- [ ] Logs for application
- [ ] Logs for application (partly implemented)

View file

@ -16,6 +16,7 @@ public class StorageFile {
private final String[] logDriverParameters;
private final String botAdministratorPassword;
private final String chanelConfigurationsPath;
private final String applicationLogDir;
public String getServerName() { return serverName; }
public int getServerPort() { return serverPort; }
@ -32,6 +33,7 @@ public class StorageFile {
public String[] getLogDriverParameters() { return logDriverParameters; }
public String getBotAdministratorPassword() { return botAdministratorPassword; }
public String getChanelConfigurationsPath() { return chanelConfigurationsPath; }
public String getApplicationLogDir() { return applicationLogDir; }
public StorageFile(String serverName,
int serverPort,
@ -47,7 +49,8 @@ public class StorageFile {
String logDriver,
String[] logDriverParameters,
String botAdministratorPassword,
String chanelConfigurationsPath){
String chanelConfigurationsPath,
String applicationLogDir){
this.serverName = serverName;
this.serverPort = serverPort;
this.serverPass = serverPass;
@ -63,5 +66,6 @@ public class StorageFile {
this.logDriverParameters = logDriverParameters;
this.botAdministratorPassword = botAdministratorPassword;
this.chanelConfigurationsPath = chanelConfigurationsPath;
this.applicationLogDir = applicationLogDir;
}
}

View file

@ -78,7 +78,8 @@ public class StorageReader {
"Files",
new String[] {System.getProperty("user.home")},
"pswd",
System.getProperty("user.home")
System.getProperty("user.home"),
"/var/logs/"
);
Gson writingStorageObject = new GsonBuilder().setPrettyPrinting().create();

View file

@ -4,17 +4,20 @@ import java.util.HashMap;
public class BotDriver {
private static HashMap<String, String[][]> serverDriver = new HashMap<>();
private static HashMap<String, BotSystemWorker> systemLogWorkerMap = new HashMap<>();
/**
* Define driver for desired server
* */
// TODO: add proxy worker for using with multiple drivers
public static synchronized boolean setLogDriver(String serverName, String driver, String[] driverParams){
public static synchronized boolean setLogDriver(String serverName, String driver, String[] driverParams, String applicationLogDir){
if (!driver.isEmpty() && driverParams != null && driverParams.length > 0 && driverParams[0] != null && !driverParams[0].isEmpty()) {
String[][] drvAndParams = {
{driver},
driverParams
};
serverDriver.put(serverName, drvAndParams);
systemLogWorkerMap.put(serverName, new BotSystemWorker(serverName, applicationLogDir));
return true;
}
else
@ -45,9 +48,12 @@ public class BotDriver {
return new BotZeroWorker();
}
// If channel found that it's impossible to use defined worker from user settings and asking for use ZeroWorker
public static synchronized Worker getZeroWorker(){
return new BotZeroWorker();
public static synchronized Worker getZeroWorker(){ return new BotZeroWorker(); }
public static synchronized BotSystemWorker getSystemWorker(String serverName){
return systemLogWorkerMap.get(serverName);
}
private static Worker validateConstancy(Worker worker, String srv, String chan){ // synchronized?
if (worker.isConsistent()){
return worker;

View file

@ -40,7 +40,7 @@ public class BotFilesWorker implements Worker {
return; // consistent = false;
}
if (!dir.exists()) {
System.out.println("BotFilesWorker (@"+server+")->constructor() failed:\n\tUnable to create directory to store files: " + dirLocation); //TODO: notify requester
System.out.println("BotFilesWorker (@"+server+")->constructor() failed:\n\tUnable to create directory to store files: " + dirLocation);
return;
}

View file

@ -25,8 +25,6 @@ public class BotMongoWorker implements Worker { //TODO consi
private MongoCollection<Document> collection;
private boolean consistent = false;
private boolean isItSystemThread = false;
public BotMongoWorker(String ircServer, String[] driverParameters, String channel){
this.ircServer = ircServer;
@ -49,9 +47,6 @@ public class BotMongoWorker implements Worker { //TODO consi
else
return; // consistent = false
if (channel.equals("system")) // Set ircServer variable only if it's 'system' log thread.
this.isItSystemThread = true;
if (!serversMap.containsKey(ircServer)){
/* // Leave this validations for better times.
CommandListener mongoCommandListener = new CommandListener() {
@ -132,6 +127,25 @@ public class BotMongoWorker implements Worker { //TODO consi
consistent = false;
// no need to close() obviously
}
if (consistent){
ThingToCloseOnDie thing = new ThingToCloseOnDie() {
@Override
public void die() {
if (serversMap.containsKey(ircServer)) {
try {
serversMap.get(ircServer).close();
serversMap.remove(ircServer);
}catch (Exception e){
System.out.println("ThingToCloseOnDie: something went wrong when tried to close MongoDB connection\t\n"+e);
}
}
}
};
BotDriver.getSystemWorker(ircServer).registerInSystemWorker(thing);
}
}
@Override
@ -194,13 +208,6 @@ public class BotMongoWorker implements Worker { //TODO consi
@Override
public void close() {
// If ircServer != null then it's system thread and when it's interrupted we have to close connection to DB for used server
// And remove it from HashMap
if (this.isItSystemThread && serversMap.containsKey(ircServer)) {
serversMap.get(ircServer).close();
serversMap.remove(ircServer);
//System.out.println("BotMongoWorker (@"+this.ircServer+")->close()"); // expected exit
}
consistent = false;
}
private void close(String server) {

View file

@ -0,0 +1,106 @@
package InnaIrcBot.LogDriver;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class BotSystemWorker implements SystemWorker{
private FileWriter fileWriter;
private DateTimeFormatter dateFormat;
private ThingToCloseOnDie thingToCloseOnDie; // call .die() method of this classes when this (system log class) dies.
String ircServer;
private boolean consistent = false;
public BotSystemWorker(String ircServer, String appLogDir){
this.ircServer = ircServer;
this.dateFormat = DateTimeFormatter.ofPattern("HH:mm:ss");
if (appLogDir.isEmpty()) {
if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
appLogDir = System.getProperty("user.home")+ File.separator
+"AppData"+File.separator
+"Local"+File.separator
+"InnaIrcBot"+File.separator;
} else {
appLogDir = "/var/log/innaircbot/";
}
}
if (!appLogDir.endsWith(File.separator))
appLogDir = appLogDir+File.separator;
appLogDir = appLogDir+ircServer;
File logFile = new File(appLogDir);
try {
logFile.getParentFile().mkdirs();
} catch (SecurityException e){
System.out.println("BotSystemWorker (@"+ircServer+")->constructor() failed. Unable to create sub-directory(-ies) to store logs file ("+appLogDir+"):\n\t"+e);
return; // Consistent = false
}
if (!logFile.getParentFile().exists()) {
System.out.println("BotSystemWorker (@"+ircServer+")->constructor() failed:\n\tUnable to create sub-directory(-ies) to store log file: " + appLogDir);
return;
}
try {
this.fileWriter = new FileWriter(logFile, true);
consistent = true;
} catch (IOException oie){
System.out.println("BotSystemWorker (@"+ircServer+")->constructor() failed:\n\tUnable to open file to store logs: " + appLogDir);
}
}
private String genDate(){
return "["+ LocalTime.now().format(dateFormat)+"] ";
}
@Override
public void logAdd(String event, String initiatorArg, String messageArg) {
if (consistent) {
try {
fileWriter.write(genDate() + event + " " + initiatorArg + " " + messageArg + "\n");
fileWriter.flush();
} catch (IOException e) {
System.out.println("BotSystemWorker (@" + ircServer + ")->logAdd() failed\n\tUnable to write logs of because of internal failure in LocalTime representation.");
//this.close();
consistent = false;
} catch (NullPointerException npe) {
System.out.println("BotSystemWorker (@" + ircServer + ")->logAdd() failed\n\tUnable to write logs of because file descriptor already closed/was not opened.");
consistent = false;
} catch (Exception unknowne) { // ??? No ideas. Just in case. Consider removing.
System.out.println("BotSystemWorker (@" + ircServer + ")->logAdd() failed\n\tUnable to write logs of because of exception:\n\t" + unknowne);
//this.close();
consistent = false;
}
}
else {
System.out.println(genDate() + event + " " + initiatorArg + " " + messageArg + "\n");
}
}
@Override
public void registerInSystemWorker(ThingToCloseOnDie thing){
if (this.thingToCloseOnDie == null){ // only one needed
this.thingToCloseOnDie = thing;
}
}
@Override
public void close() {
if (thingToCloseOnDie != null)
thingToCloseOnDie.die();
if (fileWriter != null) {
try {
fileWriter.close();
}
catch (java.io.IOException e){
System.out.println("BotSystemWorker (@"+ircServer+")->close() failed\n\tUnable to properly close logs file."); // Live with it.
}
}
consistent = false;
}
}

View file

@ -0,0 +1,12 @@
package InnaIrcBot.LogDriver;
public interface SystemWorker {
void registerInSystemWorker(ThingToCloseOnDie thing);
void logAdd(String event,
String initiatorArg,
String messageArg);
void close();
}

View file

@ -0,0 +1,5 @@
package InnaIrcBot.LogDriver;
public interface ThingToCloseOnDie {
void die();
}

View file

@ -69,7 +69,7 @@ public class DataProvider implements Runnable {
public void run(){
if (!ableToRun || !this.initConnection(rawStreamReader)
|| !BotDriver.setLogDriver(serverName, configFile.getLogDriver(), configFile.getLogDriverParameters())) { //Prepare logDriver for using in threads.
|| !BotDriver.setLogDriver(serverName, configFile.getLogDriver(), configFile.getLogDriverParameters(), configFile.getApplicationLogDir())) { //Prepare logDriver for using in threads.
this.close();
return;
}

View file

@ -4,7 +4,7 @@ import InnaIrcBot.Commanders.PrivateMsgCommander;
import InnaIrcBot.Config.StorageFile;
import InnaIrcBot.GlobalData;
import InnaIrcBot.LogDriver.BotDriver;
import InnaIrcBot.LogDriver.Worker;
import InnaIrcBot.LogDriver.BotSystemWorker;
import java.io.*;
import java.nio.charset.StandardCharsets;
@ -18,7 +18,7 @@ import java.util.regex.Pattern;
public class SystemConsumer implements Runnable{
private BufferedReader reader;
private Worker writerWorker;
private BotSystemWorker writerWorker;
private String nick;
private String serverName;
private Map<String, PrintWriter> channelsMap;
@ -29,9 +29,8 @@ public class SystemConsumer implements Runnable{
private PrivateMsgCommander commander;
SystemConsumer(BufferedReader streamReader, String userNick, Map<String, PrintWriter> map, StorageFile storage) {
this.writerWorker = BotDriver.getWorker(storage.getServerName(), "system");
//this.writerWorker = BotDriver.getWorker(storage.getServerName(), "system");
this.writerWorker = BotDriver.getSystemWorker(storage.getServerName());
this.nick = userNick;
this.serverName = storage.getServerName();
this.channelsMap = map;
@ -76,14 +75,15 @@ public class SystemConsumer implements Runnable{
if (getProxy(dataStrings[0], dataStrings[1], dataStrings[2]))
continue; // TODO: check this. Continue is fair?
if (dataStrings[0].equals("PRIVMSG") && dataStrings[2].indexOf("\u0001") < dataStrings[2].lastIndexOf("\u0001"))
replyCTCP(dataStrings[1], dataStrings[2].substring(dataStrings[2].indexOf(":")+1));
else if (Pattern.matches("(^[0-9]{3}$)|(^NICK$)|(^JOIN$)", dataStrings[0])){
if (dataStrings[0].equals("PRIVMSG") && dataStrings[2].indexOf("\u0001") < dataStrings[2].lastIndexOf("\u0001")) {
replyCTCP(simplifyNick(dataStrings[1]), dataStrings[2].substring(dataStrings[2].indexOf(":") + 1));
}
else if (Pattern.matches("(^[0-9]{3}$)|(^NICK$)|(^JOIN$)|(^QUIT$)", dataStrings[0])){
handleNumeric(dataStrings[0], dataStrings[1], dataStrings[2]);
}
else if (dataStrings[0].equals("PRIVMSG")) {
commander.receiver(dataStrings[1], dataStrings[2].replaceAll("^.+?:", "").trim());
writerWorker.logAdd("[system]", "PRIVMSG sent to", "commander");
writerWorker.logAdd("[system]", "PRIVMSG from "+dataStrings[1]+" received: ", dataStrings[2].replaceAll("^.+?:", "").trim());
}
else if (dataStrings[0].equals("INNA")) {
String[] splitter;
@ -123,30 +123,32 @@ public class SystemConsumer implements Runnable{
}
}
private void replyCTCP(String sender, String message){
private void replyCTCP(String sender, String message){ // got simplified nick
if (message.equals("\u0001VERSION\u0001")){
StreamProvider.writeToStream(serverName,"NOTICE "+simplifyNick(sender)+" :\u0001VERSION "+ GlobalData.getAppVersion()+"\u0001");
writerWorker.logAdd("[system]", "catch/handled CTCP VERSION from", simplifyNick(sender));
System.out.println(sender+" "+message);
System.out.println("NOTICE "+simplifyNick(sender)+" \u0001VERSION "+ GlobalData.getAppVersion()+"\u0001");
StreamProvider.writeToStream(serverName,"NOTICE "+sender+" :\u0001VERSION "+ GlobalData.getAppVersion()+"\u0001");
writerWorker.logAdd("[system]", "catch/handled CTCP VERSION from", sender);
//System.out.println(sender+" "+message);
//System.out.println("NOTICE "+sender+" \u0001VERSION "+ GlobalData.getAppVersion()+"\u0001");
}
else if (message.startsWith("\u0001PING ") && message.endsWith("\u0001")){
StreamProvider.writeToStream(serverName,"NOTICE "+simplifyNick(sender)+" :"+message);
writerWorker.logAdd("[system]", "catch/handled CTCP PING from", simplifyNick(sender));
//System.out.println(":"+simplifyNick(sender)+" NOTICE "+sender.substring(0,sender.indexOf("!"))+" "+message);
StreamProvider.writeToStream(serverName,"NOTICE "+sender+" :"+message);
writerWorker.logAdd("[system]", "catch/handled CTCP PING from", sender);
//System.out.println(":"+sender+" NOTICE "+sender.substring(0,sender.indexOf("!"))+" "+message);
}
else if (message.equals("\u0001CLIENTINFO\u0001")){
StreamProvider.writeToStream(serverName,"NOTICE "+simplifyNick(sender)+" :\u0001CLIENTINFO ACTION PING VERSION TIME CLIENTINFO\u0001");
writerWorker.logAdd("[system]", "catch/handled CTCP CLIENTINFO from", simplifyNick(sender));
//System.out.println(":"+simplifyNick(sender)+" NOTICE "+sender.substring(0,sender.indexOf("!"))+" \u0001CLIENTINFO ACTION PING VERSION TIME CLIENTINFO\u0001");
StreamProvider.writeToStream(serverName,"NOTICE "+sender+" :\u0001CLIENTINFO ACTION PING VERSION TIME CLIENTINFO\u0001");
writerWorker.logAdd("[system]", "catch/handled CTCP CLIENTINFO from", sender);
//System.out.println(":"+sender+" NOTICE "+sender.substring(0,sender.indexOf("!"))+" \u0001CLIENTINFO ACTION PING VERSION TIME CLIENTINFO\u0001");
}
else if (message.equals("\u0001TIME\u0001")){
StreamProvider.writeToStream(serverName,"NOTICE "+simplifyNick(sender)+" :\u0001TIME "+ ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME)+"\u0001");
writerWorker.logAdd("[system]", "catch/handled CTCP TIME from", simplifyNick(sender));
//System.out.println(":"+simplifyNick(sender)+" NOTICE "+sender.substring(0,sender.indexOf("!"))+" \u0001TIME "+ ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME)+"\u0001");
} else
writerWorker.logAdd("[system]", "catch CTCP request \""+message+"\" from ", simplifyNick(sender));
StreamProvider.writeToStream(serverName,"NOTICE "+sender+" :\u0001TIME "+ ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME)+"\u0001");
writerWorker.logAdd("[system]", "catch/handled CTCP TIME from", sender);
//System.out.println(":"+sender+" NOTICE "+sender.substring(0,sender.indexOf("!"))+" \u0001TIME "+ ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME)+"\u0001");
}
else
writerWorker.logAdd("[system]", "catch unknown CTCP request \""+message+"\" from ", sender);
}
private String simplifyNick(String nick){ return nick.replaceAll("!.*$",""); }
@ -204,7 +206,7 @@ public class SystemConsumer implements Runnable{
case "NICK":
if (sender.startsWith(nick+"!")) {
nick = message.trim();
writerWorker.logAdd("[system]", "catch/handled own NICK change from:", sender+" to: "+message);
writerWorker.logAdd("[system]", "catch own NICK change from:", sender+" to: "+message);
}
break;
case "JOIN":
@ -212,10 +214,11 @@ public class SystemConsumer implements Runnable{
proxyAList.put(message, new ArrayList<>()); // Add new channel name to proxy watch-list
proxyAList.get(message).add(eventNum+" "+sender+" "+message); // Add message to array linked
this.proxyRequired = true; // Ask for proxy validators
writerWorker.logAdd("[system]", "joined to channel ", "message");
}
break;
default:
writerWorker.logAdd("[system]", "catch: "+eventNum+" from: "+sender+" :",message);
writerWorker.logAdd("[system]", "catch: "+eventNum+" from: "+sender+" :",message); // TODO: QUIT comes here. Do something.
break;
}
}

View file

@ -6,7 +6,7 @@ import InnaIrcBot.LogDriver.Worker;
public class DriverTestFiles {
public static void main(String[] args){
if (BotDriver.setLogDriver("irc.tomsk.net", "files", new String[]{"/tmp/logs/"}))
if (BotDriver.setLogDriver("irc.tomsk.net", "files", new String[]{"/tmp/logs/"}, "/tmp/appLogs/"))
System.out.println("DRVT_Files: Successful driver initiation");
else {
System.out.println("DRVT_Files: Failed driver initiation");

View file

@ -9,7 +9,8 @@ public class DriverTestMongo {
if (BotDriver.setLogDriver("irc.tomsk.net", "MongoDB", new String[]{"192.168.1.186:27017",
"irc",
"loper",
"password"}))
"password"},
"/tmp/appLogs/"))
System.out.println("DRVT_Mongo:Successful driver initiation");
else {
System.out.println("DRVT_Mongo:Failed driver initiation");

View file

@ -6,7 +6,7 @@ import InnaIrcBot.LogDriver.Worker;
public class DriverTestSQLite {
public static void main(String[] args){
if (BotDriver.setLogDriver("irc.tomsk.net", "SQLite", new String[]{"/tmp/logs/mylogs"}))
if (BotDriver.setLogDriver("irc.tomsk.net", "SQLite", new String[]{"/tmp/logs/mylogs"}, "/tmp/appLogs/"))
System.out.println("DRVT_SQLite:Successful driver initiation");
else {
System.out.println("DRVT_SQLite:Failed driver initiation");

View file

@ -19,6 +19,7 @@ public class StorageFileTest {
"",
new String[]{null},
"",
"",
""
);