cz.dasnet.dasik.Dasik.java Source code

Java tutorial

Introduction

Here is the source code for cz.dasnet.dasik.Dasik.java

Source

/*
 *  Copyright (C) 2011 Matus Goljer
 *  This file is a part of Project DASik, an IRC bot.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package cz.dasnet.dasik;

import cz.dasnet.dasik.util.ArrayUtils;
import java.util.EnumSet;
import java.io.FileWriter;
import org.dom4j.io.XMLWriter;
import java.util.Date;
import org.dom4j.Element;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import cz.dasnet.dasik.entities.Channel;
import cz.dasnet.dasik.dao.LearnDaoImpl;
import java.io.UnsupportedEncodingException;
import java.util.Set;
import cz.dasnet.dasik.dao.UserDao;
import org.jibble.pircbot.User;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import cz.dasnet.dasik.plugin.Plugin;
import java.util.List;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import cz.dasnet.dasik.config.Config;
import cz.dasnet.dasik.config.ConfigException;
import cz.dasnet.dasik.dao.LearnDao;
import cz.dasnet.dasik.dao.UserDaoImpl;
import cz.dasnet.dasik.entities.ChannelConfig;
import cz.dasnet.dasik.entities.ChannelUser;
import cz.dasnet.dasik.util.ChannelLogger;
import cz.dasnet.dasik.util.Functions;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.jibble.pircbot.IrcException;
import org.jibble.pircbot.PircBot;
import static cz.dasnet.dasik.util.CollectionUtils.newHashMap;
import static cz.dasnet.dasik.util.CollectionUtils.newHashSet;
import static cz.dasnet.dasik.util.CollectionUtils.newArrayList;
import static cz.dasnet.dasik.util.CollectionUtils.any;

/**
 * Main class representing a bot instance.
 *
 * @author Matus Goljer
 * @version 1.0
 */
public class Dasik extends PircBot {

    private static Logger log = Logger.getLogger(Dasik.class);
    private Config config;
    private DataSource dataSource;
    private UserDao userDao;
    private LearnDao learnDao;
    private Map<String, String> channels = newHashMap();
    private Map<String, String> nickToAuth = newHashMap();
    private Map<String, String> authToNick = newHashMap();
    private Map<String, String> nickToMask = newHashMap();
    private Map<String, String> maskToNick = newHashMap();
    private Map<String, ChannelEventListener> channelListeners = new LinkedHashMap<String, ChannelEventListener>();
    private Map<String, Channel> activeChannels = newHashMap();
    private Map<String, ChannelConfig> channelConfigs = newHashMap();
    private Set<String> plugins = newHashSet();
    private ChannelLogger chanlog;
    private PluginLoader pluginLoader;
    private boolean authed = false;
    private Set<String> ignoredHosts = newHashSet();
    private boolean masterCommandHandled = false;

    public Dasik() throws ConfigException {
        log.info("");
        log.info("Creating new bot instance ...");
        this.config = new Config();

        log.info("Setting up datasource ...");
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName(config.getProperty("datasource.driver", "com.mysql.jdbc.Driver"));
        ds.setUrl(config.getProperty("datasource.connection", "jdbc:mysql://localhost:3306/dasik"));
        ds.setUsername(config.getProperty("datasource.username", "root"));
        ds.setPassword(config.getProperty("datasource.password", "root"));
        dataSource = ds;

        log.info("Creating User DAO ...");
        userDao = new UserDaoImpl(dataSource);

        log.info("Creating Learn DAO ...");
        learnDao = new LearnDaoImpl(dataSource);

        log.info("Loading channel list ...");
        String channs = config.getProperty("irc.channel.autojoin");
        if (channs != null) {
            for (String chan : channs.split("\\s+")) {
                String pass = config.getProperty("irc.channel.pass." + chan, "");
                channels.put(chan, pass);
            }
        }

        log.info("Initializing logger ...");
        chanlog = new ChannelLogger(config.getProperty("log.basepath", "logs"));
        channelListeners.put("@log", chanlog);

        log.info("Creating plugin loader ...");
        pluginLoader = new PluginLoader(this);

        log.info("Loading plugins ...");
        String commands = config.getProperty("command.autoload");
        if (commands != null) {
            plugins.addAll(Arrays.asList(commands.split("\\s+")));
        }

        Map<String, Plugin> loadedPlugins = pluginLoader.loadPlugins(plugins);
        channelListeners.putAll(loadedPlugins);

        log.info("Loading channel configs ...");
        for (String plugin : plugins) {
            String ignore = config.getProperty("command.ignoreOn." + plugin);
            if (ignore != null) {
                String[] chans = ignore.split("\\s+");
                for (String chan : chans) {
                    ChannelConfig c = channelConfigs.get(chan);
                    if (c == null) {
                        c = new ChannelConfig(chan);
                        channelConfigs.put(chan, c);
                    }
                    c.ignorePlugin(plugin);
                }
            }
        }

        log.info("Bot created");
    }

    public static void main(String[] args) throws ConfigException, UnsupportedEncodingException {
        Dasik bot = new Dasik();

        bot.setName(bot.getConfig().getProperty("irc.name", "DASik"));
        bot.setLogin(bot.getConfig().getProperty("irc.name", "DASik"));
        bot.setFinger(bot.getConfig().getProperty("irc.name", "DASik"));
        bot.setAutoNickChange(true);
        bot.setVerbose(true);
        bot.setEncoding("UTF-8");

        try {
            //bot.startIdentServer();
            bot.connect(bot.getConfig().getProperty("irc.server", "irc.quakenet.org"),
                    bot.getConfig().getPropertyInt("irc.port", 6667));
        } catch (IrcException ex) {
            log.error("Unable to connect to server", ex);
        } catch (IOException ex) {
            log.error("Unable to connect to server", ex);
        }
    }

    public void close() {
        close(true);
    }

    public void close(boolean exit) {
        chanlog.close();
        if (exit) {
            for (ChannelEventListener cl : channelListeners.values()) {
                if (cl instanceof Plugin) {
                    Plugin plugin = (Plugin) cl;
                    plugin.unloaded(this);
                }
            }
            log.info("Session closed");
            System.exit(0);
        }
        log.info("Session closed");
    }

    @Override
    protected void onConnect() {
        log.info("Connected on " + getServer());
        auth();
        if (authed) {
            requestInvites();
        }
        final Dasik bot = this;
        try {
            Timer timer = new Timer();
            timer.schedule(new TimerTask() {

                @Override
                public void run() {
                    bot.joinChannels();
                }
            }, 1000);

            TimerTask dumpTask = new TimerTask() {

                @Override
                public void run() {
                    Document document = DocumentHelper.createDocument();
                    Element channelinfo = document.addElement("channelinfo");

                    for (String c : activeChannels.keySet()) {
                        Element channel = channelinfo.addElement("channel");
                        Element name = channel.addElement("name");
                        name.setText(c);
                        Element size = channel.addElement("size");
                        size.setText("" + getUsers(c).length);
                    }

                    Element updatetime = channelinfo.addElement("updatetime");
                    updatetime.setText(new Long(new Date().getTime() / 1000).toString());

                    try {
                        XMLWriter writer = new XMLWriter(new FileWriter("channelinfo.xml"));
                        writer.write(document);
                        writer.close();
                    } catch (IOException ex) {
                        log.error("Unable to dump channel info", ex);
                    }
                }
            };

            Timer dump = new Timer("dump", true);
            dump.schedule(dumpTask, 10000, 60000);
        } catch (Exception ex) {
            this.joinChannels();
            log.error("Channel autojoin timer failed to schedule the task.", ex);
        }
    }

    @Override
    public void onDisconnect() {
        activeChannels.clear();
        while (!isConnected()) {
            try {
                Thread.sleep(10000);
                reconnect();
            } catch (Exception ex) {
                log.error("An error occured while reconnecting", ex);
            }
        }
    }

    private void auth() {
        String target = config.getProperty("irc.auth.service");
        String msg = config.getProperty("irc.auth.message");
        if (target != null && msg != null) {
            sendMessage(target, msg);
        }
        log.debug("Auth request sent ...");
        setMode(getNick(), "+x");
    }

    private void joinChannels() {
        for (Map.Entry<String, String> channel : channels.entrySet()) {
            if (channel.getValue().isEmpty()) {
                joinChannel(channel.getKey());
            } else {
                joinChannel(channel.getKey(), channel.getValue());
            }
            log.debug("Channel join request sent: " + channel.getKey() + " ...");
        }
    }

    private void requestInvites() {
        send("q", "invite");
        log.debug("Channel invite request sent");
    }

    private void removeNickFromIAL(String nick) {
        removeNickFromIAL(nick, false);
    }

    private void removeNickFromIAL(String nick, boolean quit) {
        String auth = nickToAuth(nick);
        String mask = nickToMask(nick);

        boolean noCommonChannel = true;
        for (Channel c : activeChannels.values()) {
            if (c.getUsers().containsKey(nick)) {
                noCommonChannel = false;
                break;
            }
        }

        if (quit || noCommonChannel) {
            nickToAuth.remove(nick.toLowerCase());
            nickToMask.remove(nick.toLowerCase());
            maskToNick.remove(mask);
            authToNick.remove(auth);
        }
    }

    private void sendWhoOnUser(String nick) {
        if (!nickToAuth.containsKey(nick)) {
            sendRawLine("WHO " + nick + " n%nahu");
            log.debug("WHO user request sent for " + nick + " ...");
        } else {
            log.debug("WHO user request ignored for " + nick + ", nick already in database!");
        }
    }

    private void sendWhoOnChannel(String channel) {
        sendRawLine("WHO " + channel + " %nahu");
        log.debug("WHO channel request sent on " + channel + " ...");
    }

    @Override
    protected void onServerResponse(int code, String response) {
        String[] tokens = response.split("\\s+");

        if (code == 354) {
            //:servercentral.il.us.quakenet.org 354 DASik2 ~DASik ircnavod.users.quakenet.org DASik2 ircnavod
            String user = tokens[1];
            String host = tokens[2];
            String nick = tokens[3];
            String auth = tokens[4];
            String fullhost = user + "@" + host;
            if (!authed && auth.equalsIgnoreCase(config.getProperty("irc.auth.account"))) {
                authed = true;
                boolean requestInvite = config.getPropertyBool("irc.autoInvite", false);
                if (requestInvite) {
                    requestInvites();
                }
            }

            if (!auth.equals("0")) {
                nickToAuth.put(nick.toLowerCase(), auth.toLowerCase());
                authToNick.put(auth.toLowerCase(), nick);
                onUserAuth(nick);
            }

            nickToMask.put(nick.toLowerCase(), fullhost.toLowerCase());
            maskToNick.put(fullhost.toLowerCase(), nick);

            log.info("User WHO processed: nick: " + nick + " host: " + fullhost + " auth: " + auth);
        }
    }

    public void send(String target, String message) {
        sendMessage(target, message);
        chanlog.onSentMessage(target, message, this);
    }

    public void describe(String target, String message) {
        sendAction(target, message);
        chanlog.onSentAction(target, message, this);
    }

    public Config getConfig() {
        return config;
    }

    public Set<String> getIgnoredHosts() {
        return ignoredHosts;
    }

    public Map<String, ChannelConfig> getChannelConfigs() {
        return channelConfigs;
    }

    public Map<String, Channel> getActiveChannels() {
        return activeChannels;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public UserDao getUserDao() {
        return userDao;
    }

    public LearnDao getLearnDao() {
        return learnDao;
    }

    public String authToNick(String auth) {
        return authToNick.get(auth.toLowerCase());
    }

    public String maskToNick(String mask) {
        return maskToNick.get(mask.toLowerCase());
    }

    public String nickToAuth(String nick) {
        return nickToAuth.get(nick.toLowerCase());
    }

    public String nickToMask(String nick) {
        return nickToMask.get(nick.toLowerCase());
    }

    public String getCommand(String message) {
        return getCommand(message, false);
    }

    public String getCommand(String message, boolean admin) {
        String prefix;
        if (admin) {
            prefix = config.getProperty("command.adminPrefix", "@");
        } else {
            prefix = config.getProperty("command.prefix", "!");
        }

        if (prefix.contains(message.substring(0, 1))) {
            return message.split("\\s+")[0].substring(1);
        }
        return null;
    }

    public boolean isMasterAuthorised(String nick) {
        String[] master = config.getProperty("command.master").split("\\s+");
        final String mask = nickToMask(nick);

        if (any(Arrays.asList(master), new Functions.Predicate<String>() {

            public boolean filter(String item) {
                return item.equalsIgnoreCase(mask);
            }
        })) {
            return true;
        }
        return false;
    }

    public ChannelAccess getChannelAccess(String nick, String channel) {
        org.jibble.pircbot.User[] users = getUsers(channel);
        for (org.jibble.pircbot.User u : users) {
            if (u.getNick().equals(nick)) {
                if (u.isOp()) {
                    return ChannelAccess.OP;
                }
                if (u.hasVoice()) {
                    return ChannelAccess.VOICE;
                }
                return ChannelAccess.NORMAL;
            }
        }
        return ChannelAccess.NORMAL;
    }

    @Override
    protected void onInvite(String targetNick, String sourceNick, String sourceLogin, String sourceHostname,
            String channel) {
        if (sourceNick.equalsIgnoreCase("q")) {
            String[] list = channel.split(" ");
            String[] ignore = config.getProperty("irc.channel.ignore", "").split("\\s+");
            channel = list[list.length - 1];
            if (!ArrayUtils.contains(ignore, channel)) {
                joinChannel(channel);
            }
        }
    }

    public void onUserAuth(String userNick) {
        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onUserAuth(userNick, this);
        }
    }

    @Override
    public void onAction(String sender, String login, String hostname, String target, String action) {
        if (target.equalsIgnoreCase(sender)) {
            return;
        }

        if (ignoredHosts.contains(nickToMask(sender)) && !isMasterAuthorised(sender)) {
            return;
        }

        ChannelConfig c = channelConfigs.get(target);
        for (Map.Entry<String, ChannelEventListener> cl : channelListeners.entrySet()) {
            if (c == null || !c.isIgnoringPlugin(cl.getKey()) || cl.getKey().equals("AdminCommand")) {
                cl.getValue().onAction(sender, login, hostname, target, action, this);
            }
        }

        //        for (ChannelEventListener cl : channelListeners.values()) {
        //            cl.onAction(sender, login, hostname, target, action, this);
        //        }
    }

    @Override
    public void onMessage(String channel, String sender, String login, String hostname, String message) {
        String command = getCommand(message, true);
        String[] token = message.split("\\s+");

        if (isMasterAuthorised(sender)) {
            if ("plugin".equals(command) && token.length >= 2) {
                String subcommand = token[1];
                if ("reloadall".equals(subcommand)) {
                    Map<String, Plugin> loadedPlugins = pluginLoader.loadPlugins(plugins, false);
                    for (String s : loadedPlugins.keySet()) {
                        if (channelListeners.containsKey(s)) {
                            ChannelEventListener old = channelListeners.remove(s);
                            if (old instanceof Plugin) {
                                ((Plugin) old).unloaded(this);
                            }
                        }
                        channelListeners.put(s, loadedPlugins.get(s));
                        loadedPlugins.get(s).loaded(this);
                    }
                    send(channel, loadedPlugins.size() + " of " + plugins.size() + " reloaded successfuly");
                    masterCommandHandled = true;
                    return;
                } else if ("reload".equals(subcommand) && token.length >= 3) {
                    List<String> successful = newArrayList();
                    List<String> failed = newArrayList();
                    for (int i = 2; i < token.length; i++) {
                        Plugin loadedPlugin = pluginLoader.loadPlugin(token[i], false);
                        if (loadedPlugin != null) {
                            if (channelListeners.containsKey(token[i])) {
                                ChannelEventListener old = channelListeners.remove(token[i]);
                                if (old instanceof Plugin) {
                                    ((Plugin) old).unloaded(this);
                                }
                            }
                            loadedPlugin.loaded(this);
                            channelListeners.put(token[i], loadedPlugin);
                            successful.add(token[i]);
                            plugins.add(token[i]);
                        } else {
                            failed.add(token[i]);
                        }
                    }
                    if (!successful.isEmpty()) {
                        send(channel, "Plugins " + successful + " reloaded successfuly");
                    }
                    if (!failed.isEmpty()) {
                        send(channel, "An error occured while reloading plugins: " + failed);
                    }
                    masterCommandHandled = true;
                    return;
                } else if ("ignore".equals(subcommand) && token.length >= 3) {
                    String pluginName = token[2];
                    List<String> chans = newArrayList();
                    for (int i = 3; i < token.length; i++) {
                        chans.add(token[i]);
                    }
                    if (chans.isEmpty() && !channel.equalsIgnoreCase(sender)) {
                        chans.add(channel);
                    }

                    for (String chan : chans) {
                        ChannelConfig c = channelConfigs.get(chan);
                        if (c == null) {
                            c = new ChannelConfig(chan);
                            channelConfigs.put(chan, c);
                        }
                        c.ignorePlugin(pluginName);
                        if (plugins.contains(pluginName)) {
                            send(channel, "Ignoring plugin " + pluginName + " on " + chan);
                        } else {
                            send(channel, "Ignoring plugin " + pluginName + " on " + chan
                                    + " (warning: plugin with such name does not exist)");
                        }
                    }
                    masterCommandHandled = true;
                    return;
                } else if ("unignore".equals(subcommand) && token.length >= 3) {
                    String pluginName = token[2];
                    List<String> chans = newArrayList();
                    for (int i = 3; i < token.length; i++) {
                        chans.add(token[i]);
                    }
                    if (chans.isEmpty() && !channel.equalsIgnoreCase(sender)) {
                        chans.add(channel);
                    }

                    for (String chan : chans) {
                        ChannelConfig c = channelConfigs.get(chan);
                        if (c == null) {
                            continue;
                        }
                        c.unignorePlugin(pluginName);
                        if (plugins.contains(pluginName)) {
                            send(channel, "Unignoring plugin " + pluginName + " on " + chan);
                        } else {
                            send(channel, "Unignoring plugin " + pluginName + " on " + chan
                                    + " (warning: plugin with such name does not exist)");
                        }
                    }
                    masterCommandHandled = true;
                    return;
                } else if ("list".equals(subcommand)) {
                    StringBuilder sb = new StringBuilder("Plugin list: ");

                    for (String s : channelListeners.keySet()) {
                        sb.append(s).append(", ");
                    }
                    send(channel, sb.toString());
                }
            }
        }

        if (channel.equalsIgnoreCase(sender)) {
            return;
        }

        if (ignoredHosts.contains(nickToMask(sender)) && !isMasterAuthorised(sender)) {
            return;
        }

        ChannelConfig c = channelConfigs.get(channel);
        for (Map.Entry<String, ChannelEventListener> cl : channelListeners.entrySet()) {
            if (c == null || !c.isIgnoringPlugin(cl.getKey()) || cl.getKey().equals("AdminCommand")) {
                cl.getValue().onMessage(channel, sender, login, hostname, message, this);
            }
        }
    }

    @Override
    public void onPrivateMessage(String sender, String login, String hostname, String message) {
        String command = getCommand(message, true);

        if (command != null && isMasterAuthorised(sender)) {
            masterCommandHandled = false;
            onMessage(sender, sender, login, hostname, message);
            if (masterCommandHandled) {
                return;
            }
        }

        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onPrivateMessage(sender, login, hostname, message, this);
        }
    }

    @Override
    public void onJoin(String channel, String sender, String login, String hostname) {
        // handle auths
        if (sender.equals(getNick())) {
            if (!activeChannels.containsKey(channel)) {
                Channel c = new Channel(channel);
                activeChannels.put(channel, c);
            }
        } else {
            sendWhoOnUser(sender);
        }

        Channel c = activeChannels.get(channel);
        c.addUser(new ChannelUser(sender, EnumSet.noneOf(ChannelAccess.class)));

        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onJoin(channel, sender, login, hostname, this);
        }
    }

    @Override
    public void onPart(String channel, String sender, String login, String hostname) {
        if (sender.equals(getNick())) {
            activeChannels.remove(channel);
        }

        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onPart(channel, sender, login, hostname, this);
        }

        Channel c = activeChannels.get(channel);
        if (c != null) {
            c.removeUser(sender);
        }

        removeNickFromIAL(sender);
    }

    @Override
    public void onKick(String channel, String kickerNick, String kickerLogin, String kickerHostname,
            String recipientNick, String reason) {
        if (recipientNick.equals(getNick())) {
            activeChannels.remove(channel);
        }

        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onKick(channel, kickerNick, kickerLogin, kickerHostname, recipientNick, reason, this);
        }

        Channel c = activeChannels.get(channel);
        if (c != null) {
            c.removeUser(recipientNick);
        }

        removeNickFromIAL(recipientNick);
    }

    @Override
    public void onQuit(String sourceNick, String sourceLogin, String sourceHostname, String reason) {
        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onQuit(sourceNick, sourceLogin, sourceHostname, reason, this);
        }

        for (Channel c : activeChannels.values()) {
            c.removeUser(sourceNick);
        }

        removeNickFromIAL(sourceNick, true);
    }

    @Override
    public void onMode(String channel, String sourceNick, String sourceLogin, String sourceHostname, String mode) {
        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onMode(channel, sourceNick, sourceLogin, sourceHostname, mode, this);
        }
    }

    @Override
    public void onTopic(String channel, String topic, String setBy, long date, boolean changed) {
        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onTopic(channel, topic, setBy, date, changed, this);
        }
    }

    @Override
    public void onNickChange(String oldNick, String login, String hostname, String newNick) {
        for (Channel c : activeChannels.values()) {
            ChannelUser cu = c.getUser(oldNick);
            if (cu != null) {
                c.removeUser(cu.getNick());
                cu.setNick(newNick);
                c.addUser(cu);
            }
        }

        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onNickChange(oldNick, login, hostname, newNick, this);
        }

        removeNickFromIAL(oldNick);
        sendWhoOnUser(newNick);
    }

    @Override
    public void onOp(String channel, String sourceNick, String sourceLogin, String sourceHostname,
            String recipient) {
        Channel c = activeChannels.get(channel);
        if (c != null) {
            ChannelUser cu = c.getUser(recipient);
            cu.getMode().add(ChannelAccess.OP);
        }

        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onOp(channel, sourceNick, sourceLogin, sourceHostname, recipient, this);
        }
    }

    @Override
    public void onDeop(String channel, String sourceNick, String sourceLogin, String sourceHostname,
            String recipient) {
        Channel c = activeChannels.get(channel);
        if (c != null) {
            ChannelUser cu = c.getUser(recipient);
            cu.getMode().remove(ChannelAccess.OP);
        }

        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onDeop(channel, sourceNick, sourceLogin, sourceHostname, recipient, this);
        }
    }

    @Override
    public void onVoice(String channel, String sourceNick, String sourceLogin, String sourceHostname,
            String recipient) {
        Channel c = activeChannels.get(channel);
        if (c != null) {
            ChannelUser cu = c.getUser(recipient);
            cu.getMode().add(ChannelAccess.VOICE);
        }

        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onVoice(channel, sourceNick, sourceLogin, sourceHostname, recipient, this);
        }
    }

    @Override
    public void onDeVoice(String channel, String sourceNick, String sourceLogin, String sourceHostname,
            String recipient) {
        Channel c = activeChannels.get(channel);
        if (c != null) {
            ChannelUser cu = c.getUser(recipient);
            cu.getMode().remove(ChannelAccess.VOICE);
        }

        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onDeVoice(channel, sourceNick, sourceLogin, sourceHostname, recipient, this);
        }
    }

    @Override
    public void onSetChannelBan(String channel, String sourceNick, String sourceLogin, String sourceHostname,
            String hostmask) {
        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onSetChannelBan(channel, sourceNick, sourceLogin, sourceHostname, hostmask, this);
        }
    }

    @Override
    public void onRemoveChannelBan(String channel, String sourceNick, String sourceLogin, String sourceHostname,
            String hostmask) {
        for (ChannelEventListener cl : channelListeners.values()) {
            cl.onRemoveChannelBan(channel, sourceNick, sourceLogin, sourceHostname, hostmask, this);
        }
    }

    @Override
    protected void onUserList(String channel, User[] users) {
        if (!activeChannels.containsKey(channel)) {
            Channel c = new Channel(channel);
            activeChannels.put(channel, c);
        }

        Channel c = activeChannels.get(channel);

        for (User u : users) {
            String p = u.getPrefix();
            EnumSet<ChannelAccess> mode = EnumSet.noneOf(ChannelAccess.class);
            if (p.contains("@")) {
                mode.add(ChannelAccess.OP);
            }
            if (p.contains("+")) {
                mode.add(ChannelAccess.VOICE);
            }
            c.addUser(new ChannelUser(u.getNick(), mode));
        }

        sendWhoOnChannel(channel);
    }
}