master
Dmitry Isaenko 2018-12-16 17:27:44 +03:00
commit b72c61a019
29 changed files with 2154 additions and 0 deletions

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

16
InnaIrcBot.iml Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="gson-2.8.5" level="project" />
<orderEntry type="library" name="sqlite-jdbc-3.23.1" level="project" />
</component>
</module>

View File

@ -0,0 +1,40 @@
/**
* InnaIrcBot
* @author Dmitry Isaenko
* @version 0.1 "Батлкрузер"
* Russia, 2018.
* */
package InnaIrcBot;
import InnaIrcBot.Config.StorageFile;
import InnaIrcBot.Config.StorageReader;
import InnaIrcBot.ProvidersConsumers.DataProvider;
public class BotStart {
//TODO: Steam link, flood control, kikbana
//TODO: setDaemon(true)
//TODO: multiple connections to one server not allowed
public static void main(String[] args){
if (args.length != 0) {
if (args.length >= 2) {
if (args[0].equals("--configuration") || args[0].equals("-c")) {
new Connections(args);
} else if (args[0].equals("--generate") || args[0].equals("-g")) {
StorageReader.generateDefaultConfig(args[1]);
} else if (args[0].equals("--version") || args[0].equals("-v")) {
System.out.println(GlobalData.getAppVersion());
}
}
else if (args[0].equals("--generate") || args[0].equals("-g")){
StorageReader.generateDefaultConfig(null);
}
}
else {
System.out.println("Usage:\n"
+" \t-c, --configuration <name.config> [<name1.config> ...]\tRead Config\n"
+"\t-g, --generate\t[name.config]\t\t\t\t\t\t\tGenerate Config\n"
+"\t-v, --version\t\t\t\t\t\t\t\t\t\t\tGet application version");
}
}
}

View File

@ -0,0 +1,235 @@
package InnaIrcBot.Commanders;
import InnaIrcBot.ProvidersConsumers.StreamProvider;
import java.io.*;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.regex.Pattern;
//TODO: FLOOD, JOIN FLOOD
// TODO: @ configuration level: if in result we have empty string, no need to pass it to server
public class ChanelCommander implements Runnable {
private BufferedReader reader;
private String server;
private String chanel;
//TODO: add timers
private HashMap<String, String[]> joinMap; // 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 boolean joinFloodTrackNeed = false;
private JoinFloodHandler jfh;
public ChanelCommander(BufferedReader streamReader, String serverName, String chan, String configFilePath){
this.reader = streamReader;
this.server = serverName;
this.chanel = chan;
this.joinMap = new HashMap<>();
this.msgMap = new HashMap<>();
this.nickMap = new HashMap<>();
readConfing(configFilePath);
}
@Override
public void run() {
System.out.println("Thread for ChanelCommander started"); // TODO:REMOVE DEBUG
String data;
String[] dataStrings;
try {
while ((data = reader.readLine()) != null) {
dataStrings = data.split(" ",3);
//event initiatorArg messageArg
switch (dataStrings[0]) {
case "NICK":
nickCame(dataStrings[2]+dataStrings[1].replaceAll("^.+?!","!"));
break; // todo: need to track join flood
case "JOIN":
if (joinFloodTrackNeed)
jfh.track(simplifyNick(dataStrings[1]));
joinCame(dataStrings[1]);
break;
case "PRIVMSG":
privmsgCame(dataStrings[1], dataStrings[2]);
break;
/*
case "PART": // todo: need to track join flood? Fuck that. Track using JOIN
break;
case "QUIT": // todo: need this?
break;
case "TOPIC": // todo: need this?
break;
case "MODE": // todo: need this?
break;
case "KICK": // todo: need this?
break; */
default:
break;
}
}
} catch (java.io.IOException e){
System.out.println("Internal issue: thread ChanelCommander->run() caused I/O exception:\n\t"+e); // TODO: reconnect
}
System.out.println("Thread for ChanelCommander ended"); // TODO:REMOVE DEBUG
}
// Do we need old nick?
private void nickCame(String newNick){
came(nickMap, newNick, newNick);
}
private void joinCame(String who){
came(joinMap, who, who);
}
private void privmsgCame(String who, String what){
came(msgMap, what, who);
}
private void came(HashMap<String, String[]> map, String arg1, String arg2){
for (String pattern : map.keySet())
if (Pattern.matches(pattern, arg1)){ // NOTE: validation based on new nick //TODO: parse here
String[] cmdOrMsg = map.get(pattern);
for (int i = 0; i<cmdOrMsg.length;) {
//switch (map.get(pattern)[0]){
ArrayList<String> whatToSend;
switch (cmdOrMsg[i]) {
case "\\chanmsg":
whatToSend = new ArrayList<>();
for (i++; (i < cmdOrMsg.length) && !(cmdOrMsg[i].startsWith("\\")); i++)
whatToSend.add(cmdOrMsg[i]);
msgAction(whatToSend.toArray(new String[0]), arg2, false);
break;
case "\\privmsg":
whatToSend = new ArrayList<>();
for (i++; (i < cmdOrMsg.length) && !(cmdOrMsg[i].startsWith("\\")); i++)
whatToSend.add(cmdOrMsg[i]);
msgAction(whatToSend.toArray(new String[0]), arg2, true);
break;
case "\\ban":
banAction(arg2);
i++;
break;
case "\\kick":
whatToSend = new ArrayList<>();
for (i++; (i < cmdOrMsg.length) && !(cmdOrMsg[i].startsWith("\\")); i++)
whatToSend.add(cmdOrMsg[i]);
kickAction(whatToSend.toArray(new String[0]), arg2);
break;
case "\\kickban":
whatToSend = new ArrayList<>();
for (i++; (i < cmdOrMsg.length) && !(cmdOrMsg[i].startsWith("\\")); i++)
whatToSend.add(cmdOrMsg[i]);
banAction(arg2);
kickAction(whatToSend.toArray(new String[0]), arg2);
break;
case "\\raw":
StringBuilder whatToSendRaw = new StringBuilder();
for (i++; (i < cmdOrMsg.length) && !(cmdOrMsg[i].startsWith("\\")); i++)
whatToSendRaw.append(cmdOrMsg[i]);
StreamProvider.writeToStream(server, whatToSendRaw.toString()); //TODO
break; //todo: add script
default:
i++;
}
}
}
}
///////// /////////
private void msgAction(String[] messages, String who, boolean sendToPrivate){
StringBuilder executiveStr = new StringBuilder();
executiveStr.append("PRIVMSG ");
if(sendToPrivate) {
executiveStr.append(simplifyNick(who));
executiveStr.append(" :");
}
else {
executiveStr.append(chanel);
executiveStr.append(" :");
executiveStr.append(simplifyNick(who));
executiveStr.append(": ");
}
for (int i = 1; i<messages.length; i++){
if ( ! messages[i].startsWith("\\"))
executiveStr.append(messages[i]);
else if (messages[i].equals("\\time"))
executiveStr.append(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
}
//System.out.println(executiveStr); //TODO: debug
StreamProvider.writeToStream(server, executiveStr.toString());
}
private void banAction(String whom){
StreamProvider.writeToStream(server, "MODE "+chanel+" +b "+simplifyNick(whom)+"*!*@*");
StreamProvider.writeToStream(server, "MODE "+chanel+" +b "+"*!*@"+whom.replaceAll("^.+@",""));
}
private void kickAction(String[] messages, String whom){
StringBuilder executiveStr = new StringBuilder();
executiveStr.append("KICK ");
executiveStr.append(chanel);
executiveStr.append(" ");
executiveStr.append(simplifyNick(whom));
executiveStr.append(" :");
for (int i = 1; i<messages.length; i++){
if ( ! messages[i].startsWith("\\"))
executiveStr.append(messages[i]);
else if (messages[i].equals("\\time"))
executiveStr.append(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
}
StreamProvider.writeToStream(server, executiveStr.toString());
}
// TSV
private void parse(String[] directive){
if (directive.length >= 3 && directive[0] != null && !directive[0].startsWith("#") && directive[1] != null && directive[2] != null){
//System.out.println(Arrays.toString(directive)); // TODO:debug
switch (directive[0].toLowerCase()){
case "join":
joinMap.put(directive[1], Arrays.copyOfRange(directive, 2, directive.length));
break;
case "msg":
msgMap.put(directive[1], Arrays.copyOfRange(directive, 2, directive.length));
break;
case "nick":
nickMap.put(directive[1], Arrays.copyOfRange(directive, 2, directive.length));
break;
case "joinfloodcontrol":
if (!directive[1].isEmpty() && !directive[2].isEmpty() && Pattern.matches("^[0-9]+?$", directive[1].trim()) && Pattern.matches("^[0-9]+?$", directive[2].trim())) {
int events = Integer.valueOf(directive[1].trim());
int timeFrame = Integer.valueOf(directive[2].trim());
if (events > 0 && timeFrame > 0) {
jfh = new JoinFloodHandler(events, timeFrame, server, chanel);
joinFloodTrackNeed = true;
} else {
System.out.println("Internal issue: thread ChanelCommander->parse(): 'Number of events' and/or 'Time Frame in seconds' should be greater then 0");
}
}
else
System.out.println("Internal issue: thread ChanelCommander->parse(): 'Number of events' and/or 'Time Frame in seconds' should be numbers greater then 0");
break;
}
}
}
private String simplifyNick(String nick){ return nick.replaceAll("!.*$",""); }
private void readConfing(String confFilesPath){
if (!confFilesPath.endsWith(File.separator))
confFilesPath += File.separator;
File file = new File(confFilesPath+server+chanel+".csv"); // TODO: add/search for filename
if (!file.exists())
return;
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null) {
parse(line.split("\t"));
}
} catch (FileNotFoundException e) {
System.out.println("Internal issue: thread ChanelCommander->readConfig() can't find file:\t\n"+e);
} catch (IOException e){
System.out.println("Internal issue: thread ChanelCommander->readConfig() I/O exception:\t\n"+e);
}
}
}

View File

@ -0,0 +1,60 @@
package InnaIrcBot.Commanders;
import InnaIrcBot.ProvidersConsumers.StreamProvider;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
//TODO: implement method to
public class JoinFloodHandler {
private int eventBufferSize; // How many events should happens before we start validation
private int timeFrameInSeconds; // For which period critical amount of events should happens
private String server;
private String chanel;
private HashMap<String, LprFIFOQueue> usersOnChanel;
public JoinFloodHandler(int events, int timeFrameInSeconds, String serverName, String chanelName){
this.eventBufferSize = events;
this.timeFrameInSeconds = timeFrameInSeconds;
this.server = serverName;
this.chanel = chanelName;
this.usersOnChanel = new HashMap<>();
}
public void track(String userNick){
if(usersOnChanel.containsKey(userNick)){
LocalDateTime timeOfFirstEvent = usersOnChanel.get(userNick).addLastGetFirst();
if (timeOfFirstEvent.isAfter(LocalDateTime.now().minusSeconds(timeFrameInSeconds))) { // If first event in the queue happened after 'timeFrameSeconds ago from now'
StreamProvider.writeToStream(server, "PRIVMSG "+chanel+" :"+userNick+": join flood ("+eventBufferSize+" connections in "+timeFrameInSeconds+"seconds).\n"+
"MODE "+chanel+" +b "+userNick+"!*@*"); // Shut joins-liker down. By nick TODO: consider other ban methods
usersOnChanel.remove(userNick); // Delete viewing history (in case op/hop decided to unban)
}
}
else {
usersOnChanel.put(userNick, new LprFIFOQueue(eventBufferSize)); // Create buffer for tracking new user joined
usersOnChanel.get(userNick).addLastGetFirst(); // Write his/her first join time
}
}
}
class LprFIFOQueue {
private int size;
private ArrayList<LocalDateTime> dateTimes;// todo stack or deque
LprFIFOQueue(int size){
this.size = size;
this.dateTimes = new ArrayList<>();
}
LocalDateTime addLastGetFirst(){ // FIFO-like
// set
if (dateTimes.size() >= size)
dateTimes.remove(0);
dateTimes.add(LocalDateTime.now()); // add current time
// get
if (dateTimes.size() < size)
return LocalDateTime.MIN; // todo: check if null is better
else
return dateTimes.get(0);
}
}

View File

@ -0,0 +1,316 @@
package InnaIrcBot.Commanders;
import InnaIrcBot.GlobalData;
import InnaIrcBot.ProvidersConsumers.StreamProvider;
import InnaIrcBot.ReconnectControl;
import java.util.ArrayList;
public class PrivateMsgCommander { // TODO: add black list: add users after failed login queries ; temporary ban
private String serverName;
private ArrayList<String> administrators;
private String password;
public PrivateMsgCommander(String server, String adminPassword){
this.serverName = server;
this.administrators = new ArrayList<>();
this.password = adminPassword.trim();
}
public void receiver(String sender, String message){
if (!password.isEmpty()) {
if (administrators.contains(sender) && !message.isEmpty()) {
String[] cmd = message.split("(\\s)|(\t)+?", 2);
cmd[0] = cmd[0].toLowerCase();
switch (cmd[0]){
case "tell":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] tellArgs = cmd[1].split("(\\s)|(\t)+?", 2);
tell(tellArgs[0], tellArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: tell <nick> <message>");
break;
case "join":
if (cmd.length == 2)
join(cmd[1]);
else
tell(simplifyNick(sender), "Pattern: join <channel>");
break;
case "quit":
if (cmd.length == 2)
quit(cmd[1]);
else
quit("");
break;
case "nick":
if (cmd.length == 2)
nick(cmd[1]);
else
tell(simplifyNick(sender), "Pattern: nick <new_Nick>");
break;
case "part": //TODO: update
if (cmd.length == 2)
part(cmd[1]);
else
tell(simplifyNick(sender), "Pattern: part <channel> [reason]");
break;
case "ctcp":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] ctcpArgs = cmd[1].split("(\\s)|(\t)+?", 2);
ctcp(ctcpArgs[0], ctcpArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: ctcp <object> <CTCP-command>");
break;
case "notice":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] noticeArgs = cmd[1].split("(\\s)|(\t)+?", 2);
notice(noticeArgs[0], noticeArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: notice <nick> <message>");
break;
case "umode":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] modeArgs = cmd[1].split("(\\s)|(\t)+?", 2);
umode(modeArgs[0], modeArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: umode <object> <[+|-]mode_single_char>");
break;
case "raw":
if (cmd.length == 2)
raw(cmd[1]);
else
tell(simplifyNick(sender), "Pattern: raw <any_text_to_server>");
break;
case "cmode":
if ((cmd.length >= 2) && (cmd[1].split("(\\s)|(\t)+?",3).length >= 2)) {
String[] args = cmd[1].split("(\\s)|(\t)+?", 3);
if (args.length == 2)
cmode(args[0], args[1], null);
else if(args.length == 3)
cmode(args[0], args[1], args[2]);
}
else
tell(simplifyNick(sender), "Pattern: cmode <channel> [<mode>|<mode> <pattern_user>]");
break;
case "k":
case "kick":
if ((cmd.length >= 2) && (cmd[1].split("(\\s)|(\t)+?",3).length >= 2)) {
String[] args = cmd[1].split("(\\s)|(\t)+?", 3);
if (args.length == 2)
kick(args[0], args[1], null);
else if(args.length == 3)
kick(args[0], args[1], args[2]);
}
else
tell(simplifyNick(sender), "Pattern: [k|kick] <channel> <user> [reason]");
break;
case "b":
case "ban":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] banArgs = cmd[1].split("(\\s)|(\t)+?", 2);
ban(banArgs[0], banArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: [b|ban] <channel> <user>");
break;
case "-b":
case "unban":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] banArgs = cmd[1].split("(\\s)|(\t)+?", 2);
unban(banArgs[0], banArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: [-b|unban] <channel> <user>");
break;
case "kb":
case "kickban":
if ((cmd.length >= 2) && (cmd[1].split("(\\s)|(\t)+?",3).length >= 2)) {
String[] args = cmd[1].split("(\\s)|(\t)+?", 3);
if (args.length == 2)
kickban(args[0], args[1], null);
else if(args.length == 3)
kickban(args[0], args[1], args[2]);
}
else
tell(simplifyNick(sender), "Pattern: [kb|kickban] <channel> <user>");
break;
case "v":
case "voice":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] voiceArgs = cmd[1].split("(\\s)|(\t)+?", 2);
voice(voiceArgs[0], voiceArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: [v|voice] <channel> <user>");
break;
case "-v":
case "unvoice":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] voiceArgs = cmd[1].split("(\\s)|(\t)+?", 2);
unvoice(voiceArgs[0], voiceArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: [-v|unvoice] <channel> <user>");
break;
case "h":
case "hop":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] hopArgs = cmd[1].split("(\\s)|(\t)+?", 2);
hop(hopArgs[0], hopArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: [h|hop] <channel> <user>");
break;
case "-h":
case "unhop":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] hopArgs = cmd[1].split("(\\s)|(\t)+?", 2);
unhop(hopArgs[0], hopArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: [-h|unhop] <channel> <user>");
break;
case "o":
case "op":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] operatorArgs = cmd[1].split("(\\s)|(\t)+?", 2);
op(operatorArgs[0], operatorArgs[1]);
}
else
tell(simplifyNick(sender), "Pattern: [o|operator] <channel> <user>");
break;
case "-o":
case "unop":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] args = cmd[1].split("(\\s)|(\t)+?", 2);
unop(args[0], args[1]);
}
else
tell(simplifyNick(sender), "Pattern: [-o|unoperator] <channel> <user>");
break;
case "topic":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] args = cmd[1].split("(\\s)|(\t)+?", 2);
topic(args[0], args[1]);
}
else
tell(simplifyNick(sender), "Pattern: topic <channel> <topic>");
break;
case "invite":
if ((cmd.length == 2) && (cmd[1].split("(\\s)|(\t)+?",2).length == 2)) {
String[] args = cmd[1].split("(\\s)|(\t)+?", 2);
invite(args[0], args[1]);
}
else
tell(simplifyNick(sender), "Pattern: invite <user> <channel>");
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]+$)", ""));
}
}
}
}
private void join(String channel){
raw("JOIN "+channel);
}
private void part(String channel){
raw("PART "+channel);
}
private void quit(String message){
if (message.isEmpty()){
raw("QUIT :"+ GlobalData.getAppVersion());
}
else
raw("QUIT :"+message);
ReconnectControl.update(serverName, false);
//ReconnectControl.update(serverName, true);
//System.exit(0); // TODO: change to normal exit
}
private void tell(String channelUser, String message){
raw("PRIVMSG "+channelUser+" :"+message);
}
private void nick(String newNick){
raw("NICK "+newNick);
}
private void ctcp(String object, String command){
raw("PRIVMSG "+object+" :\u0001"+command.toUpperCase()+"\u0001");
}
private void notice(String channelUser, String message){
raw("NOTICE "+channelUser+" :"+message);
}
private void umode(String object, String mode){
raw("MODE "+object+" "+mode);
}
private void cmode(String object, String mode, String user){
if (user == null)
raw("MODE "+object+" "+mode);
else
raw("MODE "+object+" "+mode+" "+user);
}
private void raw(String rawText){
StreamProvider.writeToStream(serverName, rawText);
}
private void kick(String chanel, String user, String reason){
if (reason == null)
raw("KICK "+chanel+" "+user+" :requested");
else
raw("KICK "+chanel+" "+user+" :"+reason);
}
private void ban(String chanel, String user){
cmode(chanel, "+b", simplifyNick(user)+"*!*@*"); // TODO: work on patter.n
if (user.contains("@")){
cmode(chanel, "+b", "*!*@"+user.replaceAll("^.+@",""));
}
}
private void unban(String chanel, String user){
cmode(chanel, "-b", simplifyNick(user)+"*!*@*");
if (user.contains("@")){
cmode(chanel, "-b", "*!*@"+user.replaceAll("^.+@",""));
}
}
private void kickban(String chanel, String user, String reason){
cmode(chanel, "+b", simplifyNick(user)+"*!*@*");
kick(chanel, user, reason);
}
private void voice(String chanel, String user){
cmode(chanel, "+v", user);
}
private void unvoice(String chanel, String user){
cmode(chanel, "-v", user);
}
private void hop(String chanel, String user){
cmode(chanel, "+h", user);
}
private void unhop(String chanel, String user){
cmode(chanel, "-h", user);
}
private void op(String chanel, String user){
cmode(chanel, "+o", user);
}
private void unop(String chanel, String user){
cmode(chanel, "-o", user);
}
private void topic(String channel, String topic){ raw("TOPIC "+channel+" :"+topic); }
private void invite(String user, String chanel){
raw("INVITE "+user+" "+chanel);
}
private void login(String candidate, String key){
if (key.equals(password)) {
administrators.add(candidate);
tell(simplifyNick(candidate), "Granted.");
}
}
private String simplifyNick(String nick){ return nick.replaceAll("!.*$",""); }
}

View File

@ -0,0 +1,63 @@
package InnaIrcBot.Config;
public class StorageFile {
private final String serverName;
private final int serverPort;
private final String[] channels;
private final String userNick;
private final String userIdent;
private final String userRealName;
private final String userNickPass;
private final String userNickAuthStyle;
private final String userMode;
private final boolean rejoinOnKick;
private final String logDriver;
private final String[] logDriverParameters;
private final String botAdministratorPassword;
private final String chanelConfigurationsPath;
public String getServerName() { return serverName; }
public int getServerPort() { return serverPort; }
public String[] getChannels() { return channels; }
public String getUserNick() { return userNick; }
public String getUserIdent() { return userIdent; }
public String getUserRealName() { return userRealName; }
public String getUserNickPass() { return userNickPass; }
public String getUserNickAuthStyle() { return userNickAuthStyle; }
public String getUserMode() { return userMode; }
public boolean getRejoinOnKick() { return rejoinOnKick; }
public String getLogDriver() { return logDriver; }
public String[] getLogDriverParameters() { return logDriverParameters; }
public String getBotAdministratorPassword() { return botAdministratorPassword; }
public String getChanelConfigurationsPath() { return chanelConfigurationsPath; }
public StorageFile(String serverName,
int serverPort,
String[] channels,
String userNick,
String userIdent,
String userRealName,
String userNickPass,
String userNickAuthStyle,
String userMode,
boolean rejoinOnKick,
String logDriver,
String[] logDriverParameters,
String botAdministratorPassword,
String chanelConfigurationsPath){
this.serverName = serverName;
this.serverPort = serverPort;
this.channels = channels;
this.userIdent = userIdent;
this.userNick = userNick;
this.userRealName = userRealName;
this.userNickPass = userNickPass;
this.userNickAuthStyle = userNickAuthStyle;
this.userMode = userMode;
this.rejoinOnKick = rejoinOnKick;
this.logDriver = logDriver;
this.logDriverParameters = logDriverParameters;
this.botAdministratorPassword = botAdministratorPassword;
this.chanelConfigurationsPath = chanelConfigurationsPath;
}
}

View File

@ -0,0 +1,95 @@
package InnaIrcBot.Config;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class StorageReader {
public static StorageFile readConfig(String pathToFile){ // TODO: NULL or object
StorageFile storageObject = null;
File configFile = new File(pathToFile);
try (Reader fileReader = new InputStreamReader(new FileInputStream(configFile))) {
storageObject = new Gson().fromJson(fileReader, StorageFile.class);
return validateConfig(storageObject);
} catch (java.io.FileNotFoundException e){
System.out.println("Configuration file not found.");
return null;
} catch (java.io.IOException e){
System.out.println("Configuration file is empty or incorrect.");
return null;
}
}
private static StorageFile validateConfig(StorageFile sf){ //TODO: more validation
if(sf.getServerName().isEmpty()){
System.out.println("Server not defined in configuration file.");
return null;
}
else if(sf.getServerPort() <= 0){
System.out.println("Server port set incorrectly in configuration file.");
return null;
}
else
return sf;
}
public static void generateDefaultConfig(String pathToFile){
File savingFile;
if (pathToFile == null) { // no pathToFile? create new in homeDir
pathToFile = System.getProperty("user.dir")
+File.separator
+"myBotConfig.conf";
savingFile = new File(pathToFile);
}
else if(pathToFile.endsWith(File.separator)) { // ends with .../ then create in dir
pathToFile = pathToFile + "myBotConfig.conf";
savingFile = new File(pathToFile);
if (!savingFile.getParentFile().exists())
savingFile.getParentFile().mkdirs();
}
else { // check if it's dir, if yes, then create inside
savingFile = new File(pathToFile);
if (savingFile.exists() && savingFile.isDirectory()) {
pathToFile = pathToFile + File.separator + "myBotConfig.conf";
savingFile = new File(pathToFile);
}
else if (!savingFile.getParentFile().exists())
savingFile.getParentFile().mkdirs();
}
try {
savingFile.createNewFile();
Writer writerFile = new OutputStreamWriter(new FileOutputStream(savingFile.getAbsolutePath()), StandardCharsets.UTF_8);
StorageFile storageFileObject = new StorageFile("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")
);
Gson writingStorageObject = new GsonBuilder().setPrettyPrinting().create();
writingStorageObject.toJson(storageFileObject, writerFile);
writerFile.close();
System.out.println("Configuration file created: " + pathToFile);
} catch (java.io.FileNotFoundException e){
System.out.println("Configuration file not found or can't create:\n\t"+e);
} catch (java.io.UnsupportedEncodingException e){
System.out.println("Unsupported encoding of the configuration file:\n\t"+e);
} catch (java.io.IOException e){
System.out.println("Unable to write configuration file: I/O exception:\n\t"+e);
}
}
}

View File

@ -0,0 +1,82 @@
package InnaIrcBot;
import InnaIrcBot.Config.StorageFile;
import InnaIrcBot.Config.StorageReader;
import InnaIrcBot.ProvidersConsumers.DataProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
public class Connections {
//todo PING Server if not available
private HashMap<String, StorageFile> configs;
private ArrayList<Thread> connectionsList;
Connections(String[] args){
this.configs = new HashMap<>();
this.connectionsList = new ArrayList<>();
StorageFile tempConf = null;
for (int i = 1; i < args.length; i++){
if ((tempConf = StorageReader.readConfig(args[i])) != null) {
configs.put(tempConf.getServerName(), tempConf);
}
else
System.out.println("Connections->constructor: configuration argument dropped: "+args[i]);
}
if (!configs.isEmpty()){
handleThreads();
}
else {
System.out.println("Connections->constructor: Nothing to execute.");
}
}
private void createAndStartThread(String serverName){
//if connectionsList already contains record with name, it should be removed from there first
// if there are few configs with same server name then.. fuckup
Runnable runnableConnection = new DataProvider(configs.get(serverName));
Thread threadConnection = new Thread(runnableConnection, serverName);
threadConnection.start();
connectionsList.add(threadConnection);
}
private void handleThreads() {
// Crate array of threads
for (String serverName : configs.keySet())
createAndStartThread(serverName);
// Watch threads
Iterator<Thread> it = connectionsList.iterator();
while (it.hasNext()) {
System.out.println("\n" + it.next().getName() + "\n");
}
while (!connectionsList.isEmpty()) { // While we have something in connectionList
while (it.hasNext()) { // Proceed for every thread in the list
Thread curThread = it.next();
if (!curThread.isAlive()) // If thread is dead
if (ReconnectControl.get(curThread.getName())) { // And ReconnectControl says that this thread shouldn't be dead
ReconnectControl.delete(curThread.getName()); // [Try to] remove rule-record from ReconnectControl structure
connectionsList.remove(curThread);
createAndStartThread(curThread.getName());
System.out.println("DEBUG: Thread "+curThread.getName()+" going to restart after unexpected finish.\n\t"+connectionsList.toString());
break;
} else { // And ReconnectControl says that this thread death expected
ReconnectControl.delete(curThread.getName()); // [Try to] remove rule-record from ReconnectControl structure
connectionsList.remove(curThread);
System.out.println("DEBUG: Thread "+curThread.getName()+" removed from observable list after expected finish.\n\t"+connectionsList.toString());
break;
}
}
if (connectionsList.isEmpty()) {
System.out.println("connectionsList.isEmpty()");
break;
}
else {
it = connectionsList.iterator();
}
}
}
}

View File

@ -0,0 +1,8 @@
package InnaIrcBot;
public class GlobalData {
public static final String version = "InnaIrcBot v0.1 \"Батлкрузер\"";
public static synchronized String getAppVersion(){
return version;
}
}

View File

@ -0,0 +1,38 @@
package InnaIrcBot.LogDriver;
import java.util.HashMap;
public class BotDriver {
private static HashMap<String, String[][]> serverDriver = new HashMap<>();
/**
* Define driver for desired server
* */ // TODO: add proxy worker for using with multiple drivers
public static synchronized boolean setFileDriver(String serverName, String driver, String[] driverParams){
if (!driver.isEmpty() && driverParams != null && driverParams.length > 0 && driverParams[0] != null && !driverParams[0].isEmpty()) {
String[][] drvAndParams = {
{driver},
driverParams
};
serverDriver.put(serverName, drvAndParams);
return true;
}
else
return false;
}
public static synchronized Worker getWorker(String serverName, String chanelName){
if (serverDriver.containsKey(serverName)) {
switch (serverDriver.get(serverName)[0][0]) {
case "Files":
return new BotFilesWorker(serverName, serverDriver.get(serverName)[1], chanelName);
case "SQLite":
return new BotSQLiteWorker(serverName, serverDriver.get(serverName)[1], chanelName);
case "Zero":
return new BotZeroWorker();
default:
System.out.println("Configuration issue: BotDriver->getWorker() can't find required driver \""+serverDriver.get(serverName)[0][0]+"\".Using \"ZeroWorker\".");
return new BotZeroWorker();
}
}
return null;
}
}

View File

@ -0,0 +1,163 @@
package InnaIrcBot.LogDriver;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.regex.Pattern;
public class BotFilesWorker implements Worker {
private String filePath;
private boolean consistent = false;
private DateTimeFormatter dateFormat;
private LocalDate fileWriterDay;
private FileWriter fileWriter;
public BotFilesWorker(String server, String[] driverParameters, String channel){
if (System.getProperty("os.name").startsWith("Windows")){
channel = channel.replaceAll("\",",",");
}
else {
channel = channel.replaceAll("/",",");
}
driverParameters[0] = driverParameters[0].trim(); //Consider parameters[0] as dirLocation
String dirLocation;
if (driverParameters[0].endsWith(File.separator))
dirLocation = driverParameters[0]+server;
else
dirLocation = driverParameters[0]+File.separator+server;
File dir = new File(dirLocation);
dir.mkdirs();
if (!dir.exists()) {
System.out.println("Unable to create directory to store files: " + dirLocation); //TODO: notify requester
this.consistent = false;
}
this.filePath = dirLocation+File.separator+channel;
dateFormat = DateTimeFormatter.ofPattern("HH:mm:ss");
if (resetFileWriter(false))
this.consistent = true;
}
private boolean resetFileWriter(boolean reassign){
try {
if (reassign)
this.fileWriter.close();
this.fileWriterDay = LocalDate.now();
this.fileWriter = new FileWriter(this.filePath+"_"+LocalDate.now().toString()+".txt", true);
return true;
}
catch (java.io.IOException e){
System.out.println("Internal issue: BotFilesWorker->constructor() can't create file to store logs: "+this.filePath);
return false;
}
}
@Override
public boolean isConsistent() {
return consistent;
}
/**
* argument[0] should be always 'from whom'
* argument[1] should be always 'subject'
* */
@Override
public void logAdd(String event, String initiatorArg, String messageArg) {
switch (event){
case "PRIVMSG":
PRIVMSG(initiatorArg, messageArg);
break;
case "JOIN":
JOIN(initiatorArg, messageArg);
break;
case "MODE":
MODE(initiatorArg, messageArg);
break;
case "KICK":
KICK(initiatorArg, messageArg);
break;
case "PART":
PART(initiatorArg, messageArg);
break;
case "QUIT":
QUIT(initiatorArg, messageArg);
break;
case "NICK":
NICK(initiatorArg, messageArg);
break;
case "TOPIC":
TOPIC(initiatorArg, messageArg);
break;
default:
this.prettyPrint("["+LocalTime.now().format(dateFormat)+"] "+event+" "+initiatorArg+" "+messageArg+"\n"); // TODO: QA @ big data
break;
}
}
@Override
public void close() {
try {
fileWriter.close();
}
catch (java.io.IOException e){
System.out.println("Internal issue: BotFilesWorker->close() failed\n\tUnable to properly close file: "+this.filePath); // Live with it.
}
}
private void prettyPrint(String string){
if (LocalDate.now().isAfter(fileWriterDay))
resetFileWriter(true);
try {
fileWriter.write(string);
fileWriter.flush();
} catch (IOException e) {
System.out.println("Internal issue: BotFilesWorker->prettyPrint() failed\n\tUnable to write logs of "+this.filePath+" because of internal failure in LocalTime representation.");
consistent = false;
}
}
private String genDate(){
return "["+LocalTime.now().format(dateFormat)+"] ";
}
private String getUserNameOnly(String userNameFull){return userNameFull.replaceAll("!.+$", "");}
private String getUserNameAndHost(String userNameFull){return userNameFull.replaceAll("!.+$", "")+" [!"+userNameFull.replaceAll("^.+!", "")+"] ";}
private void PRIVMSG(String initiatorArg, String messageArg){
String msg = messageArg.substring(messageArg.indexOf(":")+1);
if (!Pattern.matches("^\\u0001ACTION .+\\u0001", msg)){
this.prettyPrint(genDate()+"<"+getUserNameOnly(initiatorArg)+"> "+msg+"\n");
}
else {
this.prettyPrint(genDate()+getUserNameOnly(initiatorArg)+msg.replaceAll("(^\\u0001ACTION)|(\\u0001$)","")+"\n");
}
}
private void JOIN(String initiatorArg, String messageArg){
this.prettyPrint(genDate()+">> "+getUserNameAndHost(initiatorArg)+"joined "+messageArg+"\n");
}
private void MODE(String initiatorArg, String messageArg){
String initiatorChain;
if (initiatorArg.contains("!"))
initiatorChain = getUserNameAndHost(initiatorArg)+"set";
else
initiatorChain = initiatorArg+" set";
this.prettyPrint(genDate()+"-!- "+initiatorChain+messageArg.substring(messageArg.indexOf(" "))+"\n");
}
private void KICK(String initiatorArg, String messageArg){
this.prettyPrint(genDate()+"!<< "+messageArg.replaceAll("(^.+?\\s)|(\\s.+$)", "")+
" kicked by "+getUserNameAndHost(initiatorArg)+"with reason: "+messageArg.replaceAll("^.+?:", "")+"\n");
}
private void PART(String initiatorArg, String messageArg){
this.prettyPrint(genDate()+"<< "+getUserNameAndHost(initiatorArg)+"parted: "+messageArg.replaceAll("^.+?:","")+"\n");
}
private void QUIT(String initiatorArg, String messageArg){
this.prettyPrint(genDate()+"<< "+getUserNameAndHost(initiatorArg)+" quit: "+messageArg.replaceAll("^.+?:","")+"\n");
}
private void NICK(String initiatorArg, String messageArg){
this.prettyPrint(genDate()+"-!- "+getUserNameAndHost(initiatorArg)+"changed nick to: "+messageArg+"\n");
}
private void TOPIC(String initiatorArg, String messageArg) {
this.prettyPrint(genDate()+"-!- "+getUserNameAndHost(initiatorArg)+"has changed topic to: "+messageArg.replaceAll("^.+?:", "")+"\n");
}
}

View File

@ -0,0 +1,201 @@
package InnaIrcBot.LogDriver;
import org.sqlite.SQLiteConfig;
import org.sqlite.SQLiteOpenMode;
import java.io.File;
import java.sql.*;
public class BotSQLiteWorker implements Worker {
private Connection connection;
private boolean consistent = false;
private PreparedStatement preparedStatement;
/**
* Don't even think of changing this balalaika.
* */
public BotSQLiteWorker(String server, String[] driverParameters, String channel){ // TODO: threads on SQLite level // remember: One file one DB
driverParameters[0] = driverParameters[0].trim();
File dir = new File(driverParameters[0]);
dir.mkdirs();
if (!dir.exists()) {
System.out.println("Unable to create directory to store DB file: " + driverParameters[0]); //TODO: notify requester
this.consistent = false;
}
String connectionURL;
if (driverParameters[0].endsWith(File.separator))
connectionURL = "jdbc:sqlite:"+driverParameters[0]+server+".db";
else
connectionURL = "jdbc:sqlite:"+driverParameters[0]+File.separator+server+".db";
String safeChanName = channel.trim().replaceAll("\"","\\\""); // TODO: use trim in every driver/worker?
try {
SQLiteConfig sqlConfig = new SQLiteConfig();
sqlConfig.setOpenMode(SQLiteOpenMode.NOMUTEX); //SQLITE_OPEN_NOMUTEX : multithreaded mode
this.connection = DriverManager.getConnection(connectionURL, sqlConfig.toProperties());
if (connection != null){
// Create table if not created
Statement statement = connection.createStatement();
String query = "CREATE TABLE IF NOT EXISTS \""+safeChanName+"\" ("
+ " id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
+ " unixtime INTEGER,"
+ " event TEXT,"
+ " subject TEXT,"
+ " message TEXT,"
+ " object TEXT"
+");";
statement.executeUpdate(query);
// Check table representation
ResultSet rs = statement.executeQuery("PRAGMA table_info(\""+safeChanName+"\");"); // executeQuery never null
boolean[] schemaResultCheck = {false, false, false, false, false, false};
while (rs.next()) {
switch (rs.getInt("cid")) {
case 0:
if (rs.getString("name").equals("id")
&& rs.getString("type").equals("INTEGER")
&& (rs.getInt("notnull") == 1)
&& (rs.getString("dflt_value") == null)
&& (rs.getInt("pk") == 1))
schemaResultCheck[0] = true;
//System.out.println("Got 0");
break;
case 1:
if (rs.getString("name").equals("unixtime")
&& rs.getString("type").equals("INTEGER")
&& (rs.getInt("notnull") == 0)
&& (rs.getString("dflt_value") == null)
&& (rs.getInt("pk") == 0))
schemaResultCheck[1] = true;
//System.out.println("Got 1");
break;
case 2:
if (rs.getString("name").equals("event")
&& rs.getString("type").equals("TEXT")
&& (rs.getInt("notnull") == 0)
&& (rs.getString("dflt_value") == null)
&& (rs.getInt("pk") == 0))
schemaResultCheck[2] = true;
//System.out.println("Got 2");
break;
case 3:
if (rs.getString("name").equals("subject")
&& rs.getString("type").equals("TEXT")
&& (rs.getInt("notnull") == 0)
&& (rs.getString("dflt_value") == null)
&& (rs.getInt("pk") == 0))
schemaResultCheck[3] = true;
//System.out.println("Got 3");
break;
case 4:
if (rs.getString("name").equals("message")
&& rs.getString("type").equals("TEXT")
&& (rs.getInt("notnull") == 0)
&& (rs.getString("dflt_value") == null)
&& (rs.getInt("pk") == 0))
schemaResultCheck[4] = true;
//System.out.println("Got 4");
break;
case 5:
if (rs.getString("name").equals("object")
&& rs.getString("type").equals("TEXT")
&& (rs.getInt("notnull") == 0)
&& (rs.getString("dflt_value") == null)
&& (rs.getInt("pk") == 0))
schemaResultCheck[5] = true;
//System.out.println("Got 5");
break;
default:
for (int i = 0; i <= 5; i++) {
schemaResultCheck[i] = false; // If more then 5 elements, ruin results
}
}
}
// 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: Found already existing table for channel with incoorect syntax: removing table and re-creating.");
statement.executeUpdate("DROP TABLE \"" + safeChanName + "\";");
statement.executeUpdate(query);
break;
}
}
this.consistent = true;
this.preparedStatement = connection.prepareStatement(
"INSERT INTO \""+safeChanName
+"\" (unixtime, event, subject, message, object) "
+"VALUES (?, ?, ?, ?, ?);");
}
else {
this.consistent = false;
}
}
catch (SQLException e){
System.out.println("Internal issue: BotSQLiteWorker->constructor() failed\n\t"+e);
this.consistent = false;
}
}
private long getDate(){
return System.currentTimeMillis() / 1000L; // UNIX time
}
@Override
public boolean isConsistent() {return consistent; }
@Override
public void logAdd(String event, String initiatorArg, String messageArg) {
try {
preparedStatement.setLong(1, getDate());
preparedStatement.setString(2, event);
preparedStatement.setString(3, initiatorArg);
switch (event) {
case "NICK":
case "JOIN":
preparedStatement.setString(4, messageArg);
preparedStatement.setString(5, null);
break;
case "PART":
case "QUIT":
case "TOPIC":
preparedStatement.setString(4, messageArg.replaceAll("^.+?:", ""));
preparedStatement.setString(5, null);
break;
case "MODE":
preparedStatement.setString(4, messageArg.replaceAll("(^(.+?\\s){1})|(\\s.+$)",""));
preparedStatement.setString(5, messageArg.replaceAll("^(.+?\\s){2}", ""));
break;
case "KICK":
preparedStatement.setString(4,messageArg.replaceAll("^.+?:", ""));
preparedStatement.setString(5,messageArg.replaceAll("(^.+?\\s)|(\\s.+$)", ""));
break;
case "PRIVMSG":
preparedStatement.setString(4,messageArg.replaceAll("^:", ""));
preparedStatement.setString(5,null);
break;
default:
preparedStatement.setString(4,messageArg);
preparedStatement.setString(5,null);
break;
}
preparedStatement.executeUpdate();
}
catch (SQLException e){
System.out.println("Internal issue: BotSQLiteWorker->logAdd() failed\n\t"+e);
this.consistent = false;
}
}
@Override
public void close() {
try {
//System.out.println("SQLite drier closed");
this.connection.close();
}
catch (SQLException e){
System.out.println("Internal issue: BotSQLiteWorker->close() failed\n\t" + e);
}
}
}

View File

@ -0,0 +1,12 @@
package InnaIrcBot.LogDriver;
public class BotZeroWorker implements Worker{
@Override
public boolean isConsistent() {return true;}
@Override
public void logAdd(String event, String initiatorArg, String messageArg) {}
@Override
public void close() {}
}

View File

@ -0,0 +1,13 @@
package InnaIrcBot.LogDriver;
public interface Worker {
boolean consistent = false;
boolean isConsistent();
void logAdd(String event,
String initiatorArg,
String messageArg);
void close();
}

View File

@ -0,0 +1,150 @@
package InnaIrcBot.ProvidersConsumers;
import InnaIrcBot.Commanders.ChanelCommander;
import InnaIrcBot.LogDriver.BotDriver;
import InnaIrcBot.LogDriver.Worker;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
public class ChanConsumer implements Runnable {
private BufferedReader reader;
private String serverName;
private String channelName;
private Worker writerWorker;
private ArrayList<String> userList;
private String nick;
private boolean rejoin;
private Map<String, PrintWriter> chanList;
private String configFilePath;
private PrintWriter chanelCommanderPipe;
private boolean endThread = false;
ChanConsumer(BufferedReader streamReader, String serverName, String channelName, String ownNick, String[] usersOnChan, boolean rejoinAlways, Map<String, PrintWriter> map, String configFilePath){
this.reader = streamReader;
this.serverName = serverName;
this.channelName = channelName;
this.writerWorker = BotDriver.getWorker(serverName, channelName);
this.userList = new ArrayList<>();
this.userList.addAll(Arrays.asList(usersOnChan));
this.nick = ownNick;
this.rejoin = rejoinAlways;
this.chanList = map;
this.configFilePath = configFilePath;
// Create chanel commander thread, get pipe
this.chanelCommanderPipe = getChanelCommander();
}
public void run(){
String data;
String[] dataStrings;
System.out.println("["+LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+"] THREAD "+serverName+":"+this.channelName +" started"); // TODO:REMOVE DEBUG
try {
while ((data = reader.readLine()) != null) {
dataStrings = data.split(" ",3);
if (!trackUsers(dataStrings[0], dataStrings[1], dataStrings[2]))
continue;
writerWorker.logAdd(dataStrings[0], dataStrings[1], dataStrings[2]);
// Send to chanel commander thread
chanelCommanderPipe.println(data);
chanelCommanderPipe.flush();
//System.out.println("|"+dataStrings[0]+"|"+dataStrings[1]+"|"+dataStrings[2]+"|");
//System.out.println("Thread: "+this.channelName +"\n\tArray:"+ userList);
if (endThread) {
reader.close();
chanList.get(channelName).close();
chanList.remove(channelName);
break;
}
}
} catch (java.io.IOException e){
System.out.println("Internal issue: thread ChanConsumer->run() caused I/O exception:\n\t"+e); // TODO: reconnect
}
writerWorker.close();
//Chanel commander thread's pipe should be closed
chanelCommanderPipe.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){
switch (event) {
case "PRIVMSG": // most common, we don't have to handle anything else
return true;
case "JOIN":
addUsers(initiatorArg);
return true;
case "PART":
delUsers(initiatorArg);
return true;
case "QUIT": // TODO fix: use regex
if (userList.contains(initiatorArg.replaceAll("!.+$", ""))) {
delUsers(initiatorArg);
return true;
} else
return false; // user quit, but he/she is not in this channel
case "KICK":
if (rejoin && nick.equals(subjectArg.substring(subjectArg.indexOf(" ") + 1, subjectArg.indexOf(" :"))))
StreamProvider.writeToStream(serverName, "JOIN " + channelName);
delUsers(subjectArg.substring(subjectArg.indexOf(" ") + 1, subjectArg.indexOf(" :")));
return true;
case "NICK":
if (userList.contains(initiatorArg.replaceAll("!.+$", ""))) {
swapUsers(initiatorArg, subjectArg);
return true;
} else {
return false; // user changed nick, but he/she is not in this channel
}
default:
return true;
}
}
private void addUsers(String user){
if (!userList.contains(user.replaceAll("!.+$", "")))
userList.add(user.replaceAll("!.+$", ""));
}
private void delUsers(String user){
if (user.replaceAll("!.+$", "").equals(nick)) {
endThread = true;
}
userList.remove(user.replaceAll("!.+$", ""));
}
private void swapUsers(String userNickOld, String userNickNew){
userList.remove(userNickOld.replaceAll("!.+$", ""));
userList.add(userNickNew);
if (userNickOld.replaceAll("!.+$", "").equals(nick))
this.nick = userNickNew;
}
// Create ChanelCommander
private PrintWriter getChanelCommander(){
PipedOutputStream streamOut = new PipedOutputStream();
try {
BufferedReader streamBufferedReader = new BufferedReader(
new InputStreamReader(
new PipedInputStream(streamOut), StandardCharsets.UTF_8)
);
ChanelCommander commander = new ChanelCommander(streamBufferedReader, serverName, channelName, configFilePath);
new Thread(commander).start();
return new PrintWriter(streamOut);
} catch (IOException e) {
System.out.println("Internal issue: ChanConsumer->getChanelCommander() I/O exception while initialized child objects."); // caused by Socket
endThread = true;
return null;
}
}
}

View File

@ -0,0 +1,233 @@
package InnaIrcBot.ProvidersConsumers;
import InnaIrcBot.Config.StorageFile;
import InnaIrcBot.LogDriver.BotDriver;
import InnaIrcBot.ReconnectControl;
import java.io.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class DataProvider implements Runnable {
private StorageFile configFile;
private String serverName;
private String userNick;
private BufferedReader rawStreamReader;
private boolean ableToRun = true;
/**
* Initiate connection and prepare input/output streams for run()
* */
public DataProvider(StorageFile storageFile){
this.configFile = storageFile;
this.serverName = storageFile.getServerName();
int port = storageFile.getServerPort();
try {
InetAddress inetAddress = InetAddress.getByName(serverName);
Socket socket = new Socket(); // TODO: set timeout?
for (int i=0; i<5; i++) {
socket.connect(new InetSocketAddress(inetAddress, port), 5000); // 10sec = 10000
if (socket.isConnected())
break;
}
if (socket.isConnected())
System.out.println("Socket connected");
else {
System.out.println("Unable to connect to remote server after 5 retry.");
this.ableToRun = false;
return;
}
if (!StreamProvider.setStream(serverName, socket)) {
this.ableToRun = false;
return;
}
InputStream inStream = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(inStream, StandardCharsets.UTF_8); //TODO set charset in options;
this.rawStreamReader = new BufferedReader(isr);
} catch(UnknownHostException e){
this.ableToRun = false;
System.out.println("Internal issue: DataProvider->constructor caused unknown host exception:\n\t"+e); // caused by InetAddress
} catch (IOException e){
this.ableToRun = false;
System.out.println("Internal issue: DataProvider->constructor caused I/O exception\n\t"+e); // caused by Socket
}
}
//HANDLE ALWAYS in case thread decided to die
private void close(){
StreamProvider.delStream(serverName);
}
public void run(){
if (!ableToRun) {
this.close();
return;
}
if(!this.initConnection(rawStreamReader)) {
this.close();
return;
}
//Prepare logDriver for using in threads.
if(!BotDriver.setFileDriver(serverName, configFile.getLogDriver(), configFile.getLogDriverParameters())) {
this.close();
return;
}
/* Used for sending data into consumers objects*/
Map<String, PrintWriter> channelsMap = Collections.synchronizedMap(new HashMap<String, PrintWriter>());
try {
PipedOutputStream streamOut = new PipedOutputStream(); // K-K-K-KOMBO \m/
BufferedReader streamBufferedReader = new BufferedReader(
new InputStreamReader(
new PipedInputStream(streamOut), StandardCharsets.UTF_8)
);
Runnable consumer = new SystemConsumer(streamBufferedReader, userNick, channelsMap, this.configFile);
new Thread(consumer).start();
channelsMap.put("", new PrintWriter(streamOut)); // Not sure that PrintWriter is thread-safe..
} catch (IOException e){
System.out.println("Internal issue: DataProvider->run() I/O exception while initialized child objects.\n\t"+e); // caused by Socket
this.close();
return;
}
////////////////////////////////////// Start loop //////////////////////////////////////////////////////////////
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.
String chan;
// Say 'yes, we need reconnect if connection somehow died'
ReconnectControl.register(serverName);
while ((rawMessage = rawStreamReader.readLine()) != null) {
//System.out.println(rawMessage);
if (rawMessage.startsWith(":")) {
rawStrings = rawMessage
.substring(1)
.split(" :?", 3); // Removing ':'
chan = rawStrings[2].replaceAll("(\\s.?$)|(\\s.+?$)", "");
//System.out.println("\tChannel: "+chan+"\n\tAction: "+rawStrings[1]+"\n\tSender: "+rawStrings[0]+"\n\tMessage: "+rawStrings[2]+"\n");
if (rawStrings[1].equals("QUIT") || rawStrings[1].equals("NICK")) { // replace regex
for (PrintWriter value : channelsMap.values()) {
value.println(rawStrings[1] + " " + rawStrings[0] + " " + rawStrings[2]);
value.flush();
}
} else if (channelsMap.containsKey(chan)) {
channelsMap.get(chan).println(rawStrings[1] + " " + rawStrings[0] + " " + rawStrings[2]);
channelsMap.get(chan).flush();
} else {
channelsMap.get("").println(rawStrings[1] + " " + rawStrings[0] + " " + rawStrings[2]);
channelsMap.get("").flush();
}
} else if (rawMessage.startsWith("PING :")) {
pingSrvResponse(rawMessage);
} else {
System.out.println("Not a valid response=" + rawMessage);
}
}
} catch (IOException e){
System.out.println("Socket issue: I/O exception"); //Connection closed. TODO: MAYBE try reconnect
}
finally {
for (PrintWriter p :channelsMap.values()) {
p.close();
}
this.close();
}
}
private void pingSrvResponse(String rawData){
StreamProvider.writeToStream(serverName,"PONG :" + rawData.replace("PING :", ""));
}
/**
* Initiate connection before starting main routine.
* */
private boolean initConnection(BufferedReader genericStreamReader){
int nickTail = 0; // handle appendix to nickname
this.userNick = configFile.getUserNick();
if (this.userNick == null || this.userNick.isEmpty()) {
System.out.println("Configuration issue: no nickname specified.");
return false;
}
String rawMessage;
StreamProvider.writeToStream(serverName,"NICK "+this.userNick);
StreamProvider.writeToStream(serverName,"USER "+configFile.getUserIdent()+" 8 * :"+configFile.getUserRealName()); // TODO: Add usermode 4 rusnet
if (!configFile.getUserNickPass().isEmpty() && (!configFile.getUserNickAuthStyle().isEmpty() && configFile.getUserNickAuthStyle().toLowerCase().equals("freenode")))
StreamProvider.writeToStream(serverName,"PRIVMSG NickServ :IDENTIFY "+configFile.getUserNickPass());
else if (!configFile.getUserNickPass().isEmpty() && (!configFile.getUserNickAuthStyle().isEmpty() && configFile.getUserNickAuthStyle().toLowerCase().equals("rusnet")))
StreamProvider.writeToStream(serverName,"NickServ IDENTIFY "+configFile.getUserNickPass());
else if (!configFile.getUserNickPass().isEmpty())
System.out.println("Configuration issue: Unable to determinate method of user nick identification (by password): could be \"freenode\" or \"rusnet\"\nSee \"userNickAuthStyle\".");
try {
// 431 ERR_NONICKNAMEGIVEN how can we get this?
// 432 ERR_ERRONEUSNICKNAME covered
// 433 ERR_NICKNAMEINUSE covered
// 436 ERR_NICKCOLLISION
while ((rawMessage = genericStreamReader.readLine()) != null){
System.out.println(rawMessage);
if (rawMessage.startsWith("PING :")) {
pingSrvResponse(rawMessage);
}
if (rawMessage.contains(" 001 ")) {
StringBuilder message = new StringBuilder();
if (!configFile.getUserMode().trim().isEmpty()){
String modes = configFile.getUserMode();
modes = modes.replaceAll("[\t\\s]", "");
for(char c :modes.toCharArray()) {
message.append("MODE ");
message.append(userNick);
message.append(" +");
message.append(c);
message.append("\n");
}
}
StreamProvider.writeToStream(serverName,message.toString());
return true;
}
else if (rawMessage.contains(" 433 ")) {
if (this.userNick.substring(userNick.length()-1, userNick.length()-1).equals("|"))
this.userNick = this.userNick.substring(0,userNick.length()-1)+Integer.toString(nickTail++);
else
this.userNick = this.userNick+"|"+Integer.toString(nickTail++);
StreamProvider.writeToStream(serverName,"NICK "+this.userNick);
}
else if (rawMessage.contains(" 432 ")) {
System.out.println("Configuration issue: Nickname contains unacceptable characters (432 ERR_ERRONEUSNICKNAME).");
return false;
}
}
} catch (IOException e){
System.out.println("Internal issue: DataProvider->initConnection() caused I/O exception.");
return false;
}
return false;
}
}

View File

@ -0,0 +1,34 @@
package InnaIrcBot.ProvidersConsumers;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.HashMap;
public class StreamProvider {
private static HashMap<String, OutputStreamWriter> srvStreamMap = new HashMap<>();
//private static OutputStreamWriter streamWriter;
public static synchronized void writeToStream(String server, String message){
try {
srvStreamMap.get(server).write(message+"\n");
srvStreamMap.get(server).flush();
} catch (java.io.IOException e){
System.out.println("Internal issue: StreamProvider->writeToStream() caused I/O exception.");
}
}
public static synchronized boolean setStream(String server, Socket socket){
try {
OutputStream outStream = socket.getOutputStream();
srvStreamMap.put(server, new OutputStreamWriter(outStream));
return true;
} catch (java.io.IOException e){
System.out.println("Internal issue: StreamProvider->setStream() caused I/O exception.");
return false;
}
}
public static synchronized void delStream(String server){
srvStreamMap.remove(server);
}
}

View File

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

43
src/InnaIrcBot/README.md Normal file
View File

@ -0,0 +1,43 @@
# InnaIrcBot
Another one IRC bot in deep beta.
##Usage
` -c, --configuration <name.config> [<name1.config> ...] Read Config`
` -g, --generate [name.config] Generate Config`
` -v, --version Get application version`
####Configuration settings
"userNickAuthStyle": "rusnet" or "freenode"
* rusnet - send '/nickserv IDENTIFY mySecretPass'
* freenode - send '/msg nickserv IDENTIFY mySecretPass'
"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.
##License
Source code spreads under the GNU General Public License v3 or higher. Please see LICENSE file.
Used libraries:
* GSON: https://github.com/google/gson
* sqliteJDBC: https://bitbucket.org/xerial/sqlite-jdbc
## TODO:
- [ ] Documentation
- [ ] Code refactoring
- [ ] QA: good regression testing
- [ ] CI/CD
- [ ] Suppress messages from server or handle them separately from selected worker
- [ ] Logs backend workers as threads (SQLite and co. are too slow)
- [ ] Logs backend worker for mongodb
- [ ] Logs backend worker for redis/redis node
- [ ] Re-implement connection routine
- [ ] Availability to run scripts @ 'ChanelCommander'
- [ ] Docker(+compose) package
- [ ] Flood tracker
- [ ] Deep configuration files validation
- [ ] Maven or Gradle build
- [ ] ncurses-like or/and GUI configuration files (server/chanel setting) editor

View File

@ -0,0 +1,23 @@
package InnaIrcBot;
import java.util.HashMap;
public class ReconnectControl {
private static HashMap<String, Boolean> serversList = new HashMap<>();
public static synchronized void register(String serverName){
serversList.put(serverName, true);
}
public static synchronized void update(String serverName, boolean needReconnect){
serversList.replace(serverName, needReconnect);
}
public static synchronized boolean get(String serverName){ // could be null and it should be considered as false
if (serversList.get(serverName) == null)
return false;
else
return serversList.get(serverName);
}
public static synchronized void delete(String serverName){
serversList.remove(serverName);
}
}

Binary file not shown.

Binary file not shown.

3
src/META-INF/MANIFEST.MF Normal file
View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: InnaIrcBot.BotStart

37
src/test/DriverTest.java Normal file
View File

@ -0,0 +1,37 @@
import InnaIrcBot.LogDriver.BotDriver;
import InnaIrcBot.LogDriver.Worker;
public class DriverTest {
public static void main(String[] args){
if (BotDriver.setFileDriver("irc.tomsk.net", "SQLiteDriver", new String[]{"/tmp/"}))
System.out.println("Successful driver initiation");
else {
System.out.println("Failed driver initiation");
return;
}
Worker fw1 = BotDriver.getWorker("irc.tomsk.net","#lpr");
Worker fw2 = BotDriver.getWorker("irc.tomsk.net","#main");
Worker fw3 = BotDriver.getWorker("irc.tomsk.net","##loper");
if ((fw1 !=null) && (fw2 !=null) && (fw3 !=null)){
System.out.println("LogFile1: "+fw1.isConsistent());
System.out.println("LogFile2: "+fw2.isConsistent());
System.out.println("LogFile3: "+fw3.isConsistent());
fw1.logAdd("JOIN", "de_su!loper@desktop.lan", "message1");
fw1.logAdd("PART", "de_su!loper@desktop.lan", "#chan1");
fw2.logAdd("JOIN", "de_su!loper@desktop.lan", "message2");
fw2.logAdd("PART", "de_su!loper@desktop.lan", "#chan2");
fw3.logAdd("JOIN", "de_su!loper@desktop.lan", "message3");
fw3.logAdd("PART", "de_su!loper@desktop.lan", "#chan3");
fw1.close();
fw2.close();
fw3.close();
}
}
}

View File

@ -0,0 +1,14 @@
import InnaIrcBot.ReconnectControl;
import com.sun.org.apache.regexp.internal.RE;
public class ReconnectControlTest {
public static void main(String args[]){
ReconnectControl.register("testing");
ReconnectControl.register("testing1");
ReconnectControl.update("testing1", false);
System.out.println(ReconnectControl.get("testing"));
System.out.println(ReconnectControl.get("testing1"));
System.out.println(ReconnectControl.get("wrong"));
}
}

View File

@ -0,0 +1,27 @@
import InnaIrcBot.Config.StorageFile;
public class StorageFileTest {
static public void main(String[] args){
StorageFile config = new StorageFile(
"",
0,
null,
"",
"",
"",
"",
"",
"",
true,
"",
new String[]{null},
"",
""
);
System.out.println(config.getLogDriver().isEmpty());
System.out.println(config.getLogDriverParameters().length);
}
}

View File

@ -0,0 +1,10 @@
import InnaIrcBot.Config.StorageFile;
import InnaIrcBot.Config.StorageReader;
public class StorageReaderTest {
public static void main(String[] args){
// StorageReader.readConfig("/home/loper/bot.config");
// StorageFile storageFile = StorageReader.getConfig();
// System.out.println(storageFile.getLogDriver() == null);
}
}

View File

@ -0,0 +1,26 @@
import InnaIrcBot.Commanders.JoinFloodHandler;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
public class brokenJoinFloodHandlerTest {
public static void main(String[] args){
new brokenJoinFloodHandlerTest();
JoinFloodHandler jdh = new JoinFloodHandler(5, 5, "SRV_NAME", "#CHAN_NAME");
System.out.println("Envents:\t5\n"
+"Time Frame:\t5 sec\n"
+"ServerName:\tSRV_NAME\n"
+"Chanel:\t#CHAN_NAME\n");
for (int i=0; i<40; i++) {
System.out.println("Join for two users happened @"+ LocalDateTime.now());
jdh.track("eblan");
jdh.track("eban'ko");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}