All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.plugins.ircbot.v2.PircConnection Maven / Gradle / Ivy

/**
 * 
 */
package hudson.plugins.ircbot.v2;

import hudson.plugins.im.IMConnectionListener;
import hudson.plugins.im.IMMessage;
import hudson.plugins.im.IMMessageListener;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;

import org.jibble.pircbot.PircBot;

public class PircConnection extends PircBot {

	private static final Logger LOGGER = Logger.getLogger(PircConnection.class.getName());
	
	public static final String CHAT_ESTABLISHER = new String("<<>>");
	
	private final List listeners = new CopyOnWriteArrayList();
	
	private final List msgListeners = new CopyOnWriteArrayList();
	
	private final List joinListeners = new CopyOnWriteArrayList();
	
	private final boolean useNotice;
	
	private volatile boolean explicitDisconnect = false;

	public PircConnection(String name, boolean useNotice) {
	    this.useNotice = useNotice;
        setName(name);
        
        // lower delay between sending 2 messages to 500ms as we will sometimes send
        // output which will consist of multiple lines (see comment in sendIMMessage)
        // (lower than this doesn't seem to work as we will otherwise be easily
        // be throttled by IRC servers)
        setMessageDelay(500);
    }

	public void sendIMMessage(String target, String message) {
		// many IRC clients don't seem to handle new lines well (see e.g. https://bugzilla.redhat.com/show_bug.cgi?id=136542)
		// Therefore the following won't work most of the time:
//		message = message.replace("\n", "\020n");
//		sendNotice(target, message);
		
		// send multiple messages instead: 
		
		String[] lines = message.split("\\r?\\n|\\r");
		for (String line : lines) {
		    if (this.useNotice) {
		        sendNotice(target, line);
		    } else {
		        sendMessage(target, line);
		    }
		}
	}
	
    /**
     * {@inheritDoc} 
     */
    @Override
    protected void onMessage(String channel, String sender,
            String login, String hostname, String message) {
    	for (MessageListener l : this.msgListeners) {
    		if(l.target.equals(channel)) {
    			l.listener.onMessage(new IMMessage(sender, channel, message));
    		}
    	}
    }

    /**
     * {@inheritDoc} 
     */
    @Override
    protected void onPrivateMessage(String sender, String login,
            String hostname, String message) {
    	for (MessageListener l : this.msgListeners) {
    		if (getName().equals(l.target)) {
    		    if (l.sender == CHAT_ESTABLISHER || sender.equals(l.sender)) {
    		        l.listener.onMessage(new IMMessage(sender, getNick(), message));
    		    }
    		}
    	}
    }
    
    /**
     * Someone send me a notice. Possibly NickServ after identifying.
     */
    @Override
	protected void onNotice(String sourceNick, String sourceLogin,
			String sourceHostname, String target, String notice) {
		super.onNotice(sourceNick, sourceLogin, sourceHostname, target, notice);
		LOGGER.info("Notice from " + sourceNick + ": '" + normalize(notice) + "'");
	}

	/**
     * {@inheritDoc}
     */
    @Override
    protected void onJoin(String channel, String sender, String login, String hostname) {
    	for (JoinListener l : this.joinListeners) {
    		if (getName().equals(sender)) {
    			l.channelJoined(channel);
    		}
    	}
    }
    
    @Override
    protected void onServerResponse(int code, String response) {
    	if (code >= 400 && code <= 599) {
    		LOGGER.warning("IRC server responded error " + code + " Message:\n" +
    				response);
    	}
    }
    
    public final void closeConnection() {
    	this.explicitDisconnect = true;
    	super.disconnect();
    	
    	// PircBot#disconnect is brain-dead as it doesn't do the opposite of connect.
    	// Specifically: it doesn't stop the input- and output-threads!
    	// Therefore we do it ourselves in #onDisconnect
    }
    
    @Override
	protected void onDisconnect() {
        
        // clean up resources. make sure that no old input/output thread survive
        super.dispose();
        
    	if (!explicitDisconnect) {
	    	for (IMConnectionListener l : this.listeners) {
	    		l.connectionBroken(null);
	    	}
    	}
    	explicitDisconnect = false;
		super.onDisconnect();
	}

    // Note that the add/removeXyzListener methods needn't be synchronized because of the CopyOnWriteLists
    
	public void addConnectionListener(IMConnectionListener listener) {
    	this.listeners.add(listener);
    }
    
    public void removeConnectionListener(IMConnectionListener listener) {
    	this.listeners.remove(listener);
    }
    
    public void addMessageListener(String target, IMMessageListener listener) {
        this.msgListeners.add(new MessageListener(target, listener));
    }

	public void addMessageListener(String target, String sender, IMMessageListener listener) {
		this.msgListeners.add(new MessageListener(target, sender, listener));
	}

	public void removeMessageListener(String target, IMMessageListener listener) {
		this.msgListeners.remove(new MessageListener(target, listener));
	}
	
	public void addJoinListener(JoinListener listener) {
		this.joinListeners.add(listener);
	}

	public void removeJoinListener(JoinListener listener) {
		this.joinListeners.remove(listener);
	}
	
	private static final class MessageListener {
		private final String target;
		private final String sender;
		private final IMMessageListener listener;

		public MessageListener(String expectedMessageTarget, IMMessageListener listener) {
			this.target = expectedMessageTarget;
			this.sender = null;
			this.listener = listener;
		}

		public MessageListener(String expectedMessageTarget, String expectedMessageSender,
                IMMessageListener listener) {
		    this.target = expectedMessageTarget;
		    this.sender = expectedMessageSender;
            this.listener = listener;
        }

        @Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result
					+ ((listener == null) ? 0 : listener.hashCode());
			result = prime * result
					+ ((target == null) ? 0 : target.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			MessageListener other = (MessageListener) obj;
			if (listener == null) {
				if (other.listener != null)
					return false;
			} else if (!listener.equals(other.listener))
				return false;
			if (target == null) {
				if (other.target != null)
					return false;
			} else if (!target.equals(other.target))
				return false;
			return true;
		}
	}
	
	/**
	 * Removes any IRC special characters (I know of. Where is a authorative guide for them??)
	 * for the message.
	 * 
	 * http://oreilly.com/pub/h/1953
	 */
	private static String normalize(String ircMessage) {
		String msg = ircMessage.replace("\u0001", "");
		msg = msg.replace("\u0002", "");
		msg = msg.replace("\u0016", "");
		msg = msg.replace("\u000F", "");
		
		return msg;
	}
	
	public interface JoinListener {
		void channelJoined(String channelName);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy