From 2a086a64bde0efba1e4a7ac1126ca60fec0ed94b Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 24 Feb 2020 14:54:53 +0100 Subject: [IrcNotification] New Task --- pom.xml | 5 + .../java/org/openslx/satserver/util/IrcClient.java | 326 +++++++++++++++++++++ .../openslx/taskmanager/tasks/IrcNotification.java | 68 +++++ 3 files changed, 399 insertions(+) create mode 100644 src/main/java/org/openslx/satserver/util/IrcClient.java create mode 100644 src/main/java/org/openslx/taskmanager/tasks/IrcNotification.java diff --git a/pom.xml b/pom.xml index 0eab9c2..d880690 100644 --- a/pom.xml +++ b/pom.xml @@ -115,5 +115,10 @@ jsch 0.1.54 + + org.schwering + irclib + 2.0.0.Alpha3 + diff --git a/src/main/java/org/openslx/satserver/util/IrcClient.java b/src/main/java/org/openslx/satserver/util/IrcClient.java new file mode 100644 index 0000000..f7b6632 --- /dev/null +++ b/src/main/java/org/openslx/satserver/util/IrcClient.java @@ -0,0 +1,326 @@ +package org.openslx.satserver.util; + +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.TrustManager; + +import org.apache.log4j.Logger; +import org.schwering.irc.lib.IRCConfig; +import org.schwering.irc.lib.IRCConfigBuilder; +import org.schwering.irc.lib.IRCConnection; +import org.schwering.irc.lib.IRCConnectionFactory; +import org.schwering.irc.lib.IRCEventListener; +import org.schwering.irc.lib.IRCExceptionHandler; +import org.schwering.irc.lib.IRCSSLSupport; +import org.schwering.irc.lib.IRCTrafficLogger; +import org.schwering.irc.lib.IRCUser; +import org.schwering.irc.lib.util.IRCModeParser; + +public class IrcClient +{ + + private static final Logger LOGGER = Logger.getLogger( IrcClient.class ); + + private static final Map connections = new HashMap<>(); + + static + { + new Thread() { + public void run() + { + while ( !Thread.interrupted() ) { + try { + Thread.sleep( 60000 ); + } catch ( InterruptedException e ) { + return; + } + List copy; + synchronized ( connections ) { + copy = new ArrayList( connections.values() ); + } + for ( Wrapper c : copy ) { + c.ping(); + } + } + }; + }.start(); + } + + public static void sendMessage( String serverAddress, String channel, String nick, String message, List outErrors ) + { + String host = serverAddress; + Wrapper wrapper = null; + int port = -1; + boolean ssl = false; + synchronized ( connections ) { + wrapper = connections.get( serverAddress ); + if ( wrapper == null ) { + port = 6667; + int i = serverAddress.indexOf( ':', serverAddress.lastIndexOf( ']' ) + 1 ); + if ( i != -1 ) { + host = serverAddress.substring( 0, i ); + if ( i + 1 < serverAddress.length() && serverAddress.charAt( i + 1 ) == '+' ) { + ssl = true; + i++; + } + port = Util.parseInt( serverAddress.substring( i + 1 ), 0 ); + } + if ( Util.isEmpty( host ) ) { + outErrors.add( "Host is empty after parsing" ); + return; + } + if ( host.charAt( 0 ) == '[' && host.length() > 2 ) { + host = host.substring( 1, host.length() - 1 ); + } + + IRCSSLSupport sslSupport = null; + if ( ssl ) { + sslSupport = new IRCSSLSupport() + { // Return null everywhere so java uses the defaults + @Override + public TrustManager[] getTrustManagers() + { + return null; + } + + @Override + public SecureRandom getSecureRandom() + { + return null; + } + + @Override + public KeyManager[] getKeyManagers() + { + return null; + } + }; + } + wrapper = new Wrapper( serverAddress ); + IRCConfig config = IRCConfigBuilder.newBuilder().autoPong( true ).encoding( "UTF-8" ).host( host ) + .port( port ).nick( nick ).timeout( 70000 ).exceptionHandler( wrapper ).trafficLogger( IRCTrafficLogger.SYSTEM_OUT ) + .sslSupport( sslSupport ).build(); + IRCConnection connection = IRCConnectionFactory.newConnection( config ); + wrapper.setConnection( connection ); + connections.put( serverAddress, wrapper ); + } + } // sync end + synchronized ( wrapper ) { + if ( !wrapper.connection.isConnected() ) { + try { + wrapper.connection.connect(); + } catch ( KeyManagementException | NoSuchAlgorithmException | IOException e ) { + LOGGER.warn( "Cannot connect to " + host + " port " + port + " SSL: " + ssl, e ); + outErrors.add( "Cannot connect to " + host + " port " + port + " SSL: " + ssl ); + return; + } + } + if ( wrapper.connection.isConnected() ) { + wrapper.sendMessage( channel, message ); + } + } + } + + static class Wrapper implements IRCExceptionHandler, IRCEventListener + { + + private IRCConnection connection; + private final String host; + private final Set channels = new HashSet<>(); + + public Wrapper( String host ) + { + this.host = host; + } + + public synchronized void ping() + { + LOGGER.debug( "Pinging " + host ); + try { + connection.send( "PING keepalive" ); + } catch ( Exception e ) { + } + } + + public synchronized void sendMessage( String channel, String message ) + { + try { + if ( !channels.contains( channel.toLowerCase() ) ) { + connection.doJoin( channel ); + } + connection.doPrivmsg( channel, message ); + } catch ( Exception e ) { + } + } + + public void setConnection( IRCConnection connection ) + { + if ( this.connection != null ) + throw new RuntimeException( "Connection already set" ); + this.connection = connection; + connection.addIRCEventListener( this ); + } + + /* + * IRCExceptionHandler + */ + + @Override + public void exception( IRCConnection connection, Throwable exception ) + { + LOGGER.warn( "IRC Exception for " + host, exception ); + if ( exception instanceof IOException ) { + connection.close(); + } + } + + /* + * IRCEventListener + */ + + @Override + public void onRegistered() + { + LOGGER.debug( "Registered" ); + } + + @Override + public void onDisconnected() + { + LOGGER.warn( host + " is dead." ); + synchronized ( connections ) { + connections.remove( this.host ); + } + } + + @Override + public void onError( String msg ) + { + // TODO Auto-generated method stub + + } + + @Override + public void onError( int num, String msg ) + { + // TODO Auto-generated method stub + + } + + @Override + public void onInvite( String chan, IRCUser user, String passiveNick ) + { + // TODO Auto-generated method stub + + } + + @Override + public synchronized void onJoin( String chan, IRCUser user ) + { + if ( user.getNick().equalsIgnoreCase( connection.getNick() ) ) { + LOGGER.debug( "I joined " + chan ); + channels.add( chan.toLowerCase() ); + } + } + + @Override + public synchronized void onKick( String chan, IRCUser user, String passiveNick, String msg ) + { + if ( passiveNick.equalsIgnoreCase( connection.getNick() ) ) { + LOGGER.debug( "I was kicked from " + chan ); + channels.remove( chan.toLowerCase() ); + } + } + + @Override + public void onMode( String chan, IRCUser user, IRCModeParser modeParser ) + { + // TODO Auto-generated method stub + + } + + @Override + public void onMode( IRCUser user, String passiveNick, String mode ) + { + // TODO Auto-generated method stub + + } + + @Override + public void onNick( IRCUser user, String newNick ) + { + // TODO Auto-generated method stub + + } + + @Override + public void onNotice( String target, IRCUser user, String msg ) + { + // TODO Auto-generated method stub + + } + + @Override + public synchronized void onPart( String chan, IRCUser user, String msg ) + { + if ( user.getNick().equalsIgnoreCase( connection.getNick() ) ) { + LOGGER.debug( "I left " + chan ); + channels.remove( chan.toLowerCase() ); + } + } + + @Override + public void onPing( String ping ) + { + // TODO Auto-generated method stub + + } + + @Override + public void onPrivmsg( String target, IRCUser user, String msg ) + { + // TODO Auto-generated method stub + + } + + @Override + public void onQuit( IRCUser user, String msg ) + { + // TODO Auto-generated method stub + + } + + @Override + public void onReply( int num, String value, String msg ) + { + // TODO Auto-generated method stub + + } + + @Override + public void onTopic( String chan, IRCUser user, String topic ) + { + // TODO Auto-generated method stub + + } + + @Override + public void unknown( String prefix, String command, String middle, String trailing ) + { + // TODO Auto-generated method stub + + } + + } + +} diff --git a/src/main/java/org/openslx/taskmanager/tasks/IrcNotification.java b/src/main/java/org/openslx/taskmanager/tasks/IrcNotification.java new file mode 100644 index 0000000..92f29f4 --- /dev/null +++ b/src/main/java/org/openslx/taskmanager/tasks/IrcNotification.java @@ -0,0 +1,68 @@ +package org.openslx.taskmanager.tasks; + +import java.util.ArrayList; + +import org.openslx.satserver.util.IrcClient; +import org.openslx.satserver.util.Util; +import org.openslx.taskmanager.api.AbstractTask; + +import com.google.gson.annotations.Expose; + +public class IrcNotification extends AbstractTask +{ + + @Expose + private String serverAddress; + + @Expose + private String channel; + + @Expose + private String message; + + @Expose + private String nickName; + + private Output status = new Output(); + + @Override + protected boolean initTask() + { + this.setStatusObject( status ); + if ( Util.isEmpty( serverAddress ) ) { + status.add( "serverAddress empty" ); + return false; + } + if ( Util.isEmpty( channel ) ) { + status.add( "channel empty" ); + return false; + } + if ( Util.isEmpty( message ) ) { + status.add( "message empty" ); + return false; + } + if ( Util.isEmpty( nickName ) ) { + nickName = "bwlp-" + (int) ( Math.random() * 10000 ); + } + return true; + } + + @Override + protected boolean execute() + { + ArrayList errors = new ArrayList<>( 0 ); + IrcClient.sendMessage( serverAddress, channel, nickName, message, errors ); + return true; + } + + public static class Output + { + private String messages; + + public void add( String message ) + { + this.messages += message + "\n"; + } + } + +} -- cgit v1.2.3-55-g7522