net.bashtech.geobot.ReceiverBot.java Source code

Java tutorial

Introduction

Here is the source code for net.bashtech.geobot.ReceiverBot.java

Source

/*
 * Copyright 2012 Andrew Bashore
 * This file is part of GeoBot.
 * 
 * GeoBot 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 2 of the License, or
 * (at your option) any later version.
 * 
 * GeoBot 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 GeoBot.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.bashtech.geobot;

import org.jibble.pircbot.IrcException;
import org.jibble.pircbot.NickAlreadyInUseException;
import org.jibble.pircbot.PircBot;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import com.pusher.client.Pusher;
import com.pusher.client.channel.ChannelEventListener;
import com.pusher.client.connection.ConnectionEventListener;
import com.pusher.client.connection.ConnectionState;
import com.pusher.client.connection.ConnectionStateChange;

import java.io.*;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ReceiverBot extends PircBot {
    static ReceiverBot instance;
    Timer joinCheck;
    Random random = new Random();
    private Pattern[] linkPatterns = new Pattern[4];
    private Pattern[] symbolsPatterns = new Pattern[2];
    private int lastPing = -1;
    private String bullet[] = { "#!" };

    private Pattern banNoticePattern = Pattern.compile("^You are permanently banned from talking in ([a-z_]+).$",
            Pattern.CASE_INSENSITIVE);
    private Pattern toNoticePattern = Pattern.compile(
            "^You are banned from talking in ([a-z_]+) for (?:[0-9]+) more seconds.$", Pattern.CASE_INSENSITIVE);

    long lastQuoted = 0;
    long newQuoted = System.currentTimeMillis();
    long lastQuoted1 = 0;
    long newQuoted1 = System.currentTimeMillis();

    long lastCommand = System.currentTimeMillis();
    private boolean privMsgSub = false;
    private boolean privMsgMod = false;
    private ArrayList<Long> msgTimer = new ArrayList<Long>();
    private ArrayList<QueuedMessage> queuedMessages = new ArrayList<QueuedMessage>();
    private boolean tried;
    private boolean delete;
    private boolean permitted;
    private long lastConch = System.currentTimeMillis();
    private long lastRollTime = System.currentTimeMillis();

    String botName;
    Pusher pusher;

    public ReceiverBot(String server, int port) {
        ReceiverBot.setInstance(this);

        linkPatterns[0] = Pattern.compile(".*http://.*", Pattern.CASE_INSENSITIVE);
        linkPatterns[1] = Pattern.compile(".*https://.*", Pattern.CASE_INSENSITIVE);
        linkPatterns[2] = Pattern.compile(
                ".*[-A-Za-z0-9]+\\s?(\\.|\\(dot\\))\\s?(ac|ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|as|asia|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cat|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|com|coop|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|info|int|io|iq|ir|is|it|je|jm|jo|jobs|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mo|mobi|mp|mq|mr|ms|mt|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|post|pr|pro|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sx|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|travel|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xxx|ye|yt|za|zm|zw)(\\W|$).*",
                Pattern.CASE_INSENSITIVE);
        linkPatterns[3] = Pattern.compile(
                ".*(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\s+|:|/|$).*");

        symbolsPatterns[0] = Pattern.compile(
                "(\\p{InPhonetic_Extensions}|\\p{InLetterlikeSymbols}|\\p{InDingbats}|\\p{InBoxDrawing}|\\p{InBlockElements}|\\p{InGeometricShapes}|\\p{InHalfwidth_and_Fullwidth_Forms}|||?????????|||||e|U||A|E)");
        symbolsPatterns[1] = Pattern.compile("[!-/:-@\\[-`{-~]");

        this.setName(BotManager.getInstance().nick);
        this.setLogin(this.getName());
        this.setMessageDelay(0);
        botName = this.getNick();
        this.setVerbose(BotManager.getInstance().verboseLogging);

        try {
            this.connect(server, port, BotManager.getInstance().password);
        } catch (NickAlreadyInUseException e) {
            logMain("RB: [ERROR] Nickname already in use - " + this.getNick() + " " + this.getServer());
        } catch (IOException e) {
            logMain("RB: [ERROR] Unable to connect to server - " + this.getNick() + " " + this.getServer());
        } catch (IrcException e) {
            logMain("RB: [ERROR] Error connecting to server - " + this.getNick() + " " + this.getServer());
        }

    }

    public void startPusher() {
        if (!BotManager.getInstance().pusherAppKey.equalsIgnoreCase("")) {

            pusher = new Pusher(BotManager.getInstance().pusherAppKey);
            startEventParser();
        }
    }

    public void startEventParser() {
        pusher.connect(new ConnectionEventListener() {
            @Override
            public void onConnectionStateChange(ConnectionStateChange change) {
                System.out.println("State changed to " + change.getCurrentState().toString() + " from "
                        + change.getPreviousState().toString());
                if (change.getCurrentState().toString().equalsIgnoreCase("disconnected")
                        && (change.getPreviousState().toString().equalsIgnoreCase("connected")
                                || change.getPreviousState().toString().equalsIgnoreCase("disconnecting"))) {
                    System.out.println("Disconnected, attempting to reconnect.");
                    pusher.connect();
                }
            }

            @Override
            public void onError(String message, String code, Exception e) {
                log("There was a problem connecting!");
            }
        }, ConnectionState.ALL);

        pusher.subscribe(BotManager.getInstance().nick.toLowerCase(), new ChannelEventListener() {
            @Override
            public void onSubscriptionSucceeded(String channelName) {
                log("Connected and listening to coebot.tv");
            }

            @Override
            public void onEvent(String channelName, String eventName, String data) {

                parseEvent(data);
            }
        }, "e");

    }

    public void parseEvent(String data) {
        try {
            JSONParser parser = new JSONParser();
            Object obj = parser.parse(data);

            JSONObject jsonObject = (JSONObject) obj;

            String channel = (String) jsonObject.get("channel");
            String editor = (String) jsonObject.get("editor");
            Channel channelInfo = getChannelObject("#" + (String) jsonObject.get("channel"));
            JSONArray actionArray = (JSONArray) jsonObject.get("actions");
            for (int i = 0; i < actionArray.size(); i++) {
                JSONObject actionObject = (JSONObject) actionArray.get(i);
                String action = (String) actionObject.get("action");
                if (channelInfo == null && !action.equals("join")) {
                    return;
                }

                switch (action) {
                case "disconnect": {
                    pusher.disconnect();
                    break;
                }
                case "join": {

                    if (!BotManager.getInstance().publicJoin) {

                        return;
                    }

                    if (JSONUtil.krakenChannelExist(channel)) {

                        BotManager.getInstance().addChannel("#" + channel, 2);

                        BotManager.getInstance().coebotJoinChannel(channel, getNick());

                    }
                    break;
                }
                case "part": {

                    BotManager.getInstance().removeChannel("#" + channel);
                    BotManager.getInstance().coebotPartChannel(channel, getNick());
                    break;
                }
                case "say": {
                    String message = (String) actionObject.get("message");
                    send("#" + channel, message);
                    break;
                }
                case "add command": {

                    String key = ((String) actionObject.get("key")).replaceAll("[^a-zA-Z0-9]", "");
                    key = key.toLowerCase();
                    String value = (String) actionObject.get("value");

                    channelInfo.setCommand(key, value, editor);
                    if (value.contains("(_PURGE_)") || value.contains("(_TIMEOUT_)") || value.contains("(_BAN_)")
                            || value.contains("(_COMMERCIAL_)") || value.contains("(_SUBMODE_ON_)")
                            || value.contains("(_SUBMODE_OFF_)")) {
                        channelInfo.setCommandsRestriction(key, 2);
                    } else
                        channelInfo.setCommandsRestriction(key, 1);
                    break;
                }
                case "rename command": {
                    String oldName = ((String) actionObject.get("oldName"));
                    String newName = ((String) actionObject.get("newName")).replaceAll("[^a-zA-Z0-9]", "");
                    channelInfo.renameCommand(oldName, newName, editor);
                    break;
                }
                case "delete command": {
                    String key = ((String) actionObject.get("key")).replaceAll("[^a-zA-Z0-9]", "");
                    key = key.toLowerCase();
                    channelInfo.removeCommand(key);

                    channelInfo.removeRepeatCommand(key);
                    channelInfo.removeScheduledCommand(key);
                    break;
                }
                case "restrict command": {
                    int level = 3;
                    String levelStr = (String) actionObject.get("restriction");
                    if (channelInfo.getCommand((String) actionObject.get("key")) != null) {
                        if (levelStr.equalsIgnoreCase("owner") || levelStr.equalsIgnoreCase("owners"))
                            level = 3;
                        if (levelStr.equalsIgnoreCase("mod") || levelStr.equalsIgnoreCase("mods"))
                            level = 2;
                        if (levelStr.equalsIgnoreCase("regular") || levelStr.equalsIgnoreCase("regulars"))
                            level = 1;
                        if (levelStr.equalsIgnoreCase("everyone"))
                            level = 0;

                        channelInfo.setCommandsRestriction((String) actionObject.get("key"), level);

                    }
                    break;
                }
                case "add autoreply": {

                    String pattern = ((String) actionObject.get("pattern")).replaceAll("_", " ");
                    String response = (String) actionObject.get("response");

                    channelInfo.addAutoReply(pattern, response);
                    break;
                }
                case "delete autoreply": {
                    Long index = (Long) actionObject.get("index");
                    int intIndex = index.intValue();

                    channelInfo.removeAutoReply(intIndex);
                    break;
                }
                case "edit autoreply": {
                    Long index = (Long) actionObject.get("index");
                    int intIndex = index.intValue();
                    String newResponse = (String) actionObject.get("response");
                    if (channelInfo.editAutoReplyResponse(intIndex, newResponse))
                        break;
                }
                case "add repeat": {
                    int delay = ((Long) actionObject.get("delay")).intValue();
                    int difference = ((Long) actionObject.get("difference")).intValue();
                    String key = (String) actionObject.get("key");

                    if (channelInfo.getCommand(key).equalsIgnoreCase("invalid") || delay < 30) {

                    } else {
                        channelInfo.setRepeatCommand(key, delay, difference);
                    }
                    break;
                }
                case "delete repeat": {
                    String key = (String) actionObject.get("key");
                    channelInfo.removeRepeatCommand(key);

                    break;
                }
                case "add scheduled": {
                    String key = (String) actionObject.get("key");

                    String pattern = (String) actionObject.get("pattern");
                    int difference = ((Long) actionObject.get("difference")).intValue();
                    if (pattern.equalsIgnoreCase("hourly"))
                        pattern = "0 * * * *";
                    else if (pattern.equalsIgnoreCase("semihourly"))
                        pattern = "0,30 * * * *";
                    else
                        pattern = pattern.replace("_", " ");

                    if (channelInfo.getCommand(key).equalsIgnoreCase("invalid") || pattern.contains(",,")) {
                    } else {
                        channelInfo.setScheduledCommand(key, pattern, difference);

                    }

                    break;
                }
                case "delete scheduled": {
                    String key = (String) actionObject.get("key");
                    channelInfo.removeScheduledCommand(key);

                    break;
                }
                case "set steam": {
                    channelInfo.removeScheduledCommand((String) actionObject.get("value"));
                    break;
                }
                case "set extralifeid": {
                    channelInfo.setExtraLifeID((String) actionObject.get("value"));
                    break;
                }
                case "set urban": {
                    boolean enabled = false;
                    String value = (String) actionObject.get("value");
                    if (value.equalsIgnoreCase("on") || value.equalsIgnoreCase("enabled")) {
                        enabled = true;
                    }
                    channelInfo.setUrban(enabled);

                    break;
                }
                case "set gamertag": {
                    channelInfo.setGamertag((String) actionObject.get("value"));
                    break;
                }
                case "set bullet": {
                    channelInfo.setBullet((String) actionObject.get("value"));
                    break;
                }
                case "set subsRegsMinusLinks": {
                    boolean status = false;
                    String value = (String) actionObject.get("value");
                    if (value.equalsIgnoreCase("on")) {
                        status = true;
                    }
                    channelInfo.setSubsRegsMinusLinks(status);
                    break;
                }
                case "set cooldown": {
                    int value = ((Long) actionObject.get("value")).intValue();
                    channelInfo.setCooldown(value);
                    break;
                }
                case "set throw": {
                    boolean status = false;
                    String value = (String) actionObject.get("value");
                    if (value.equalsIgnoreCase("on")) {
                        status = true;
                    }
                    channelInfo.setThrow(status);
                    break;
                }
                case "set lastfm": {
                    channelInfo.setLastfm((String) actionObject.get("value"));
                    break;
                }
                case "set mode": {
                    String value = (String) actionObject.get("value");
                    if ((value.equalsIgnoreCase("0") || value.equalsIgnoreCase("owner"))) {
                        channelInfo.setMode(0);

                    } else if (value.equalsIgnoreCase("1") || value.equalsIgnoreCase("mod")) {
                        channelInfo.setMode(1);

                    } else if (value.equalsIgnoreCase("2") || value.equalsIgnoreCase("everyone")) {
                        channelInfo.setMode(2);

                    } else if (value.equalsIgnoreCase("3") || value.equalsIgnoreCase("subs")
                            || value.equalsIgnoreCase("regs")) {
                        channelInfo.setMode(3);

                    } else if (value.equalsIgnoreCase("-1") || value.equalsIgnoreCase("admin")) {
                        channelInfo.setMode(-1);

                    }

                    break;
                }
                case "set commerciallength": {
                    int cLength = ((Long) actionObject.get("value")).intValue();
                    if (cLength == 30 || cLength == 60 || cLength == 90 || cLength == 120 || cLength == 150
                            || cLength == 180)
                        channelInfo.setCommercialLength(cLength);
                    break;
                }
                case "set tweet": {
                    String format = (String) actionObject.get("value");
                    if (!format.contains("(_TWEET_URL_)"))
                        channelInfo.setClickToTweetFormat(format);

                    break;
                }
                case "set prefix": {
                    channelInfo.setPrefix((String) actionObject.get("value"));
                    break;
                }
                case "set subscriberregulars": {
                    boolean status = false;
                    if (((String) actionObject.get("value")).equalsIgnoreCase("on"))
                        status = true;
                    channelInfo.setSubscriberRegulars(status);
                    break;
                }
                case "set subscriberalerts": {
                    boolean status = false;
                    if (((String) actionObject.get("value")).equalsIgnoreCase("on"))
                        status = true;
                    channelInfo.setSubAlert(status);
                    break;
                }
                case "set submessage": {
                    channelInfo.setSubMessage((String) actionObject.get("value"));
                    break;
                }
                case "set resubalerts": {
                    boolean status = false;
                    if (((String) actionObject.get("value")).equalsIgnoreCase("on"))
                        status = true;
                    channelInfo.setResubAlert(status);
                    break;
                }
                case "set resubmessage": {
                    channelInfo.setResubMessage((String) actionObject.get("value"));
                    break;
                }

                }
            }
        } catch (Exception e) {

            e.printStackTrace();
        }
    }

    public static ReceiverBot getInstance() {
        return instance;
    }

    public static void setInstance(ReceiverBot rb) {
        if (instance == null) {
            instance = rb;
        }
    }

    // private static String getTagValue(String sTag, Element eElement) {
    // NodeList nlList = eElement.getElementsByTagName(sTag).item(0)
    // .getChildNodes();
    //
    // Node nValue = (Node) nlList.item(0);
    //
    // return nValue.getNodeValue();
    // }

    private Channel getChannelObject(String channel) {
        Channel channelInfo = null;
        channelInfo = BotManager.getInstance().getChannel(channel);
        return channelInfo;
    }

    @Override
    protected void onDeop(String channel, String sourceNick, String sourceLogin, String sourceHostname,
            String recipient) {
        recipient = recipient.replace(":", "");
        System.out.println("DEBUG: Got DEOP for " + recipient + " in channel: " + channel);
        this.getChannelObject(channel).tagModerators.remove(recipient);
    }

    @Override
    protected void onOp(String channel, String sourceNick, String sourceLogin, String sourceHostname,
            String recipient) {
        recipient = recipient.replace(":", "");
        System.out.println("DEBUG: Got OP for " + recipient + " in channel: " + channel);
        this.getChannelObject(channel).tagModerators.add(recipient);
    }

    @Override
    protected void onConnect() {
        // Force TMI to send USERCOLOR AND SPECIALUSER messages.
        this.sendRawLine("TWITCHCLIENT 3");
        this.sendRawLine("CAP REQ :twitch.tv/tags");
        this.sendRawLine("CAP REQ :twitch.tv/commands");
    }

    @Override
    protected void onPrivateMessage(String sender, String login, String hostname, String message) {
        if (!message.startsWith("USERCOLOR") && !message.startsWith("EMOTESET")
                && !message.startsWith("SPECIALUSER") && !message.startsWith("HISTORYEND")
                && !message.startsWith("CLEARCHAT") && !message.startsWith("Your color"))
            BotManager.getInstance().log("RB PM: " + sender + " " + message);

        Matcher m = banNoticePattern.matcher(message);
        if (m.matches()) {
            String channel = "#" + m.group(1);
            BotManager.getInstance().log("SB: Detected ban in " + channel + ". Parting..");
            BotManager.getInstance().removeChannel(channel);
            BotManager.getInstance().coebotPartChannel(channel.substring(1), getNick());
        }

        m = toNoticePattern.matcher(message);
        if (m.matches()) {
            String channel = "#" + m.group(1);
            BotManager.getInstance().log("SB: Detected timeout in " + channel + ". Parting..");
            BotManager.getInstance().removeChannel(channel);
            BotManager.getInstance().coebotPartChannel(channel.substring(1), getNick());
        }

        if (sender.equals("jtv"))
            onAdministrativeMessage(message, null);
    }

    @Override
    protected void onAction(String sender, String login, String hostname, String target, String action,
            String[] tags) {
        this.onMessage(target, sender, login, hostname, "/me " + action, tags);
    }

    @Override
    protected void onMessage(String channel, String sender, String login, String hostname, String message,
            String[] tags) {
        if (tags != null) {
            String color = tags[0].substring(tags[0].indexOf("=") + 1);
            String displayName = tags[1].substring(tags[1].indexOf("=") + 1);
            String emotes = tags[2].substring(tags[2].indexOf("=") + 1);
            String subscriber = tags[3].substring(tags[3].indexOf("=") + 1);

            boolean subStatus = false;
            if (Integer.parseInt(subscriber) == 1) {

                subStatus = true;

            }
            privMsgSub = subStatus;
            String turbo = tags[4].substring(tags[4].indexOf("=") + 1);
            boolean turboStatus = false;
            if (Integer.parseInt(turbo) == 1) {
                turboStatus = true;
            }
            String userType;
            if (!tags[6].endsWith("=")) {
                userType = tags[6].substring(tags[6].indexOf("=") + 1);
                if (userType.equalsIgnoreCase("mod")) {
                    privMsgMod = true;
                }

            }
        }
        if (!BotManager.getInstance().useEventFeed)
            onChannelMessage(channel, channel, sender, message);

    }

    @SuppressWarnings("rawtypes")
    protected void onChannelMessage(String channel, String targetChannel, String sender, String message) {
        if (!BotManager.getInstance().verboseLogging && !message.startsWith("USERCOLOR")
                && !message.startsWith("EMOTESET") && !message.startsWith("SPECIALUSER")
                && !message.startsWith("HISTORYEND") && !message.startsWith("CLEARCHAT")
                && !message.startsWith("Your color"))
            logMain("MSG: " + targetChannel + " " + sender + " : " + message);
        message = message.replaceAll("\\s+", " ");
        Channel channelInfo = getChannelObject(targetChannel);
        String twitchName = channelInfo.getTwitchName();
        String prefix = channelInfo.getPrefix();
        bullet[0] = channelInfo.getChannelBullet();

        if (!sender.equalsIgnoreCase(this.getNick()))
            channelInfo.messageCount++; // Inc message count

        // Ignore messages from self.
        if (sender.equalsIgnoreCase(this.getNick())) {
            // System.out.println("Message from bot");
            return;
        }

        // Handle future administrative messages from JTV
        if (sender.equalsIgnoreCase("jtv")) {
            onAdministrativeMessage(message, channelInfo);
            return;
        }

        // Handle twitchnotify
        if (sender.equals("twitchnotify")) {
            int justSub = message.indexOf("just subscribed!");
            int reSub = message.indexOf("subscribed for");
            int months = 0;
            if (justSub > -1) {
                String newSub = message.substring(0, message.indexOf(" "));

                onNewSubscriber(channelInfo, newSub);

                return;
            }
            if (reSub > -1) {
                String newSub = message.substring(0, message.indexOf(" "));

                int start = message.indexOf(" for ") + 5;
                int end = message.indexOf("month", start);
                months = Integer.parseInt(message.substring(start, end).trim());
                onResubscribe(channelInfo, newSub, months);
            }

        }

        // Split message on spaces.
        String[] msg = message.trim().split(" ");

        // ********************************************************************************
        // ****************************** User Ranks
        // **************************************
        // ********************************************************************************

        boolean isAdmin = false;
        boolean isOwner = false;
        boolean isOp = false;
        boolean isRegular = false;
        boolean isSub = false;
        int accessLevel = 0;

        // Check for user level based on other factors.
        if (BotManager.getInstance().isAdmin(sender))
            isAdmin = true;
        if (BotManager.getInstance().isTagAdmin(sender) || BotManager.getInstance().isTagStaff(sender))
            isAdmin = true;
        if (channel.equalsIgnoreCase("#" + sender))
            isOwner = true;
        if (channelInfo.isModerator(sender) || privMsgMod) {
            isOp = true;
            privMsgMod = false;
        }
        if (channelInfo.isOwner(sender))
            isOwner = true;
        if (channelInfo.isRegular(sender) || (channelInfo.subscriberRegulars && privMsgSub)) {
            isRegular = true;
            privMsgSub = false;
        }
        if (privMsgSub && channelInfo.subsRegsMinusLinks) {
            accessLevel = 1;
            isSub = true;
            privMsgSub = false;
        }
        if (channelInfo.getIgnoredUsers().contains(sender.toLowerCase())) {

            isOwner = false;
            isRegular = false;
            isSub = false;
        }
        // Give users all the ranks below them
        if (isAdmin) {
            log("RB: " + sender + " is admin.");
            isOwner = true;
            isOp = true;
            isRegular = true;
            isSub = true;
            accessLevel = 99;
        } else if (isOwner) {
            log("RB: " + sender + " is owner.");
            isOp = true;
            isRegular = true;
            isSub = true;
            accessLevel = 3;
        } else if (isOp) {
            log("RB: " + sender + " is op.");
            isRegular = true;
            isSub = true;
            accessLevel = 2;
        } else if (isRegular) {
            log("RB: " + sender + " is regular.");
            isSub = true;
            accessLevel = 1;
        } else if (isSub) {
            log("RB: " + sender + " is a subscriber");
        }

        checkQueued();
        // !{botname} command
        if (msg[0].equalsIgnoreCase(prefix + this.getName())) {
            if (msg.length >= 2) {

                String[] newMsg = new String[msg.length - 1];
                for (int i = 1; i < msg.length; i++) {
                    newMsg[i - 1] = msg[i];
                }
                msg = newMsg;
                msg[0] = prefix + msg[0];

                message = fuseArray(msg, 0);
                System.out.println("DEBUG: Command rewritten as " + message);
            }

        }

        // !leave - Owner
        if ((msg[0].equalsIgnoreCase(prefix + "leave") || msg[0].equalsIgnoreCase(prefix + "part")) && isOwner) {
            send(channel, "Leaving channel " + channelInfo.getChannel() + ".");
            BotManager.getInstance().coebotPartChannel(channel.substring(1), getNick());
            BotManager.getInstance().removeChannel(channelInfo.getChannel());
            return;
        }

        // ********************************************************************************
        // ********************************** Filters
        // *************************************
        // ********************************************************************************

        // Global banned word filter
        if (!isOp && this.isGlobalBannedWord(message) && channelInfo.getShouldModerate()) {
            this.secondaryBan(channel, sender, FilterType.GLOBALBAN);
            logMain("GLOBALBAN: Global banned word timeout: " + sender + " in " + channel + " : " + message);
            logGlobalBan(channel, sender, message);
            return;
        }

        // Voluntary Filters
        if (channelInfo.useFilters && channelInfo.getShouldModerate()) {

            // if (!isRegular) {
            // Matcher m = vinePattern.matcher(message.replaceAll(" ", ""));
            // if (m.find()) {
            // logMain("VINEBAN: " + sender + " in " + channel + " : " +
            // message);
            // this.secondaryBan(channel, sender, FilterType.VINE);
            // logGlobalBan(channel, sender, message);
            // return;
            // }
            // }

            // Me filter
            if (channelInfo.getFilterMe() && !isSub) {
                if (msg[0].equalsIgnoreCase("/me") || message.startsWith("\u0001ACTION")) {
                    int warningCount = 0;

                    channelInfo.incWarningCount(sender, FilterType.ME);
                    warningCount = channelInfo.getWarningCount(sender, FilterType.ME);
                    this.secondaryTO(channel, sender, this.getTODuration(warningCount, channelInfo), FilterType.ME,
                            message);

                    if (channelInfo.checkSignKicks()) {
                        send(channel, sender + ", /me is not allowed in this channel - "
                                + this.getTimeoutText(warningCount, channelInfo));
                        channelInfo.increasePunCount();
                    }
                    return;

                }

            }

            // Cap filter
            if (channelInfo.getFilterCaps() && !isSub) {
                String messageNoWS = message.replaceAll("\\s", "");
                int capsNumber = getCapsNumber(messageNoWS);
                double capsPercent = ((double) capsNumber / messageNoWS.length()) * 100;
                if (message.length() >= channelInfo.getfilterCapsMinCharacters()
                        && capsPercent >= channelInfo.getfilterCapsPercent()
                        && capsNumber >= channelInfo.getfilterCapsMinCapitals()) {
                    int warningCount = 0;

                    channelInfo.incWarningCount(sender, FilterType.CAPS);
                    warningCount = channelInfo.getWarningCount(sender, FilterType.CAPS);
                    this.secondaryTO(channel, sender, this.getTODuration(warningCount, channelInfo),
                            FilterType.CAPS, message);

                    if (channelInfo.checkSignKicks()) {
                        send(channel, sender + ", please don't shout or talk in all caps - "
                                + this.getTimeoutText(warningCount, channelInfo));
                        channelInfo.increasePunCount();
                    }
                    return;
                }
            }

            // Link filter
            if (channelInfo.getFilterLinks() && !(channelInfo.isRegular(sender.toLowerCase()))
                    && this.containsLink(message, channelInfo)) {
                permitted = channelInfo.linkPermissionCheck(sender);
                int warningCount = 0;
                if (permitted) {
                    send(channel, "Link permitted. (" + sender + ")");
                } else {

                    channelInfo.incWarningCount(sender, FilterType.LINK);
                    warningCount = channelInfo.getWarningCount(sender, FilterType.LINK);
                    this.secondaryTO(channel, sender, this.getTODuration(warningCount, channelInfo),
                            FilterType.LINK, message);

                    if (channelInfo.checkSignKicks()) {
                        send(channel, sender + ", please ask a moderator before posting links - "
                                + this.getTimeoutText(warningCount, channelInfo));
                        channelInfo.increasePunCount();
                    }
                    return;
                }

            }

            // Length filter
            if (!(isSub) && (message.length() > channelInfo.getFilterMax())) {
                int warningCount = 0;

                channelInfo.incWarningCount(sender, FilterType.LENGTH);
                warningCount = channelInfo.getWarningCount(sender, FilterType.LENGTH);
                this.secondaryTO(channel, sender, this.getTODuration(warningCount, channelInfo), FilterType.LENGTH,
                        message);

                if (channelInfo.checkSignKicks()) {
                    send(channel, sender + ", please don't spam long messages - "
                            + this.getTimeoutText(warningCount, channelInfo));
                    channelInfo.increasePunCount();
                }

                return;
            }

            // Symbols filter
            if (channelInfo.getFilterSymbols() && !(isSub)) {
                String messageNoWS = message.replaceAll("\\s", "");
                int count = getSymbolsNumber(messageNoWS);
                double percent = (double) count / messageNoWS.length();

                if (count > channelInfo.getFilterSymbolsMin()
                        && (percent * 100 > channelInfo.getFilterSymbolsPercent())) {
                    int warningCount = 0;
                    channelInfo.incWarningCount(sender, FilterType.SYMBOLS);
                    warningCount = channelInfo.getWarningCount(sender, FilterType.SYMBOLS);
                    this.secondaryTO(channel, sender, this.getTODuration(warningCount, channelInfo),
                            FilterType.SYMBOLS, message);

                    if (channelInfo.checkSignKicks()) {
                        send(channel, sender + ", please don't spam symbols - "
                                + this.getTimeoutText(warningCount, channelInfo));
                        channelInfo.increasePunCount();
                    }
                    return;

                }
            }

            // Offensive filter
            if (!isSub && channelInfo.getFilterOffensive()) {
                boolean isOffensive = channelInfo.isOffensive(message);
                if (isOffensive) {
                    int warningCount = 0;

                    channelInfo.incWarningCount(sender, FilterType.OFFENSIVE);
                    warningCount = channelInfo.getWarningCount(sender, FilterType.OFFENSIVE);
                    this.secondaryTO(channel, sender, this.getTODuration(warningCount, channelInfo),
                            FilterType.OFFENSIVE, message);
                    channelInfo.increasePunCount();
                    if (channelInfo.checkSignKicks()) {
                        send(channel, sender + ", disallowed word or phrase - "
                                + this.getTimeoutText(warningCount, channelInfo));
                        channelInfo.increasePunCount();
                    }
                    return;
                }
            }

            // Emote filter
            if (!isSub && channelInfo.getFilterEmotes()) {
                if (countEmotes(message) > channelInfo.getFilterEmotesMax()) {
                    int warningCount = 0;

                    channelInfo.incWarningCount(sender, FilterType.EMOTES);
                    warningCount = channelInfo.getWarningCount(sender, FilterType.EMOTES);
                    this.secondaryTO(channel, sender, this.getTODuration(warningCount, channelInfo),
                            FilterType.EMOTES, message);

                    if (channelInfo.checkSignKicks()) {
                        send(channel, sender + ", please don't spam emotes - "
                                + this.getTimeoutText(warningCount, channelInfo));
                        channelInfo.increasePunCount();
                    }
                    return;

                }

                if (channelInfo.getFilterEmotesSingle() && checkSingleEmote(message)) {
                    int warningCount = 0;

                    channelInfo.incWarningCount(sender, FilterType.EMOTES);
                    warningCount = channelInfo.getWarningCount(sender, FilterType.EMOTES);
                    this.secondaryTO(channel, sender, this.getTODuration(warningCount, channelInfo),
                            FilterType.EMOTES, message);

                    if (channelInfo.checkSignKicks()) {
                        send(channel, sender + ", single emote messages are not allowed - "
                                + this.getTimeoutText(warningCount, channelInfo));
                        channelInfo.increasePunCount();
                    }
                    return;

                }

            }

        }

        // ignore messages from blacklisted users
        if (!(BotManager.getInstance().isAdmin(sender) || isAdmin)
                && (channelInfo.getIgnoredUsers().contains(sender.toLowerCase())
                        || BotManager.getInstance().blockedChannelList.contains("#" + sender.toLowerCase()))) {

            return;
        }
        // verbose logging on/off
        if (isAdmin && msg[0].equalsIgnoreCase(prefix + "verboseLogging")) {
            if (msg.length > 1) {
                if (msg[1].equalsIgnoreCase("true") || msg[1].equalsIgnoreCase("on")) {
                    this.setVerbose(true);
                    send(channel, "Verbose Logging turned on.");
                } else if (msg[1].equalsIgnoreCase("false") || msg[1].equalsIgnoreCase("off")) {
                    this.setVerbose(false);
                    send(channel, "Verbose Logging turned off.");

                }
            }
        }
        // xkcd
        if (msg[0].equalsIgnoreCase(prefix + "xkcd") && msg.length > 1 && isSub) {
            if (isInteger(msg[1])) {
                int number = Integer.parseInt(msg[1]);
                send(channel, "XKCD Comic #" + msg[1] + " Title: " + JSONUtil.getXKCDTitle(number) + "; Image: "
                        + JSONUtil.getXKCDImage(number) + " ; Alt-Text: " + JSONUtil.getXKCDAltText(number));
            } else
                send(channel, "Please enter an integer comic number.");
        }
        // Impersonation command
        if (isAdmin && msg[0].equalsIgnoreCase(prefix + "imp")) {
            if (msg.length >= 3) {
                channelInfo = getChannelObject("#" + msg[1]);
                twitchName = channelInfo.getTwitchName();

                String[] newMsg = new String[msg.length - 2];
                for (int i = 2; i < msg.length; i++) {
                    newMsg[i - 2] = msg[i];
                }
                msg = newMsg;

                message = fuseArray(msg, 0);
                send(channel, "Impersonating channel " + channelInfo.getChannel() + " with command: " + message);
                System.out.println(
                        "IMP: Impersonating channel " + channelInfo.getChannel() + " with command: " + message);
            }

        }

        // ********************************************************************************
        // ***************************** Poll Voting
        // **************************************
        // ********************************************************************************
        if (msg[0].equalsIgnoreCase(prefix + "vote")) {
            log("RB: Matched command !vote (user entry)");
            if (channelInfo.getPoll() != null && channelInfo.getPoll().getStatus() && msg.length > 1) {
                channelInfo.getPoll().vote(sender, msg[1]);
                return;
            }
        }
        // ********************************************************************************
        // ***************************** Giveaway Voting
        // **********************************
        // ********************************************************************************
        if (channelInfo.getGiveaway() != null && channelInfo.getGiveaway().getStatus()) {
            // Giveaway is open and accepting entries.
            channelInfo.getGiveaway().submitEntry(sender, msg[0]);
        }

        // ********************************************************************************
        // ***************************** Raffle Entry
        // *************************************
        // ********************************************************************************
        if (msg[0].equalsIgnoreCase(prefix + "raffle") && msg.length == 1) {
            log("RB: Matched command !raffle (user entry)");
            if (channelInfo.raffle != null) {
                channelInfo.raffle.enter(sender);
                return;
            }
        }
        // ********************************************************************************
        // ******************************* Mode Checks
        // ************************************
        // ********************************************************************************

        // !ht
        if (msg[0].equalsIgnoreCase(prefix + "highlightthat") || msg[0].equalsIgnoreCase(prefix + "ht")) {
            JSONUtil.highlightThat("http://coebot.tv/oldhl/botadd/" + channel.substring(1) + "/" + sender);
        }

        // Check channel mode.
        if ((channelInfo.getMode() == 0 || channelInfo.getMode() == -1) && !isOwner) {
            return;
        }
        if (channelInfo.getMode() == 1 && !isOp) {
            return;
        }
        if (channelInfo.getMode() == 3 && !isSub) {
            return;
        }
        // ********************************************************************************
        // ********************************* Commands
        // *************************************
        // ********************************************************************************

        // Command cooldown check
        if (msg[0].substring(0, 1).equalsIgnoreCase(prefix) && channelInfo.onCooldown(msg[0])) {
            if (!isOp)
                return;
        }

        // !bothelp - All
        if (msg[0].equalsIgnoreCase(prefix + "bothelp") || msg[0].equalsIgnoreCase(prefix + "help")) {
            log("RB: Matched command !bothelp");
            send(channel, BotManager.getInstance().bothelpMessage);
            return;
        }

        // !viewers - All
        if ((msg[0].equalsIgnoreCase(prefix + "viewers") || msg[0].equalsIgnoreCase(prefix + "lurkers"))) {
            log("RB: Matched command !viewers");

            try {
                send(channel, JSONUtil.krakenViewers(twitchName) + " viewers.");
            } catch (Exception e) {
                send(channel, "Stream is not live.");
            }

            return;
        }
        // !chatters
        if ((msg[0].equalsIgnoreCase(prefix + "chatters"))) {
            log("RB: Matched command !viewers");

            try {
                send(channel, JSONUtil.tmiChattersCount(twitchName) + " people currently connected to chat.");
            } catch (Exception e) {
                send(channel, "Stream is not live.");
            }

            return;
        }
        long newConch = System.currentTimeMillis();

        if ((newConch >= (lastConch + 15 * 1000L)) || isOp) {
            if ((msg[0].equalsIgnoreCase(prefix + "conch") || msg[0].equalsIgnoreCase(prefix + "helix")) && isSub) {
                log("RB: Matched command !conch");
                int rand = (int) Math.round(Math.random() * 14);
                if (msg[1].equalsIgnoreCase("#admin") && isAdmin) {
                    rand = Integer.parseInt(msg[2]);
                }
                switch (rand) {
                case 0:
                    send(channel, "It is certain.");
                    break;
                case 1:
                    send(channel, "It is decidedly so.");
                    break;
                case 2:
                    send(channel, "Better not to tell.");
                    break;
                case 3:
                    send(channel, "You may rely on it.");
                    break;
                case 4:
                    send(channel, "Don't count on it.");
                    break;
                case 5:
                    send(channel, "My reply is no.");
                    break;
                case 6:
                    send(channel, "Very doubtful.");
                    break;
                case 7:
                    send(channel, "My sources say no.");
                    break;
                case 8:
                    send(channel, "Most likely.");
                    break;
                case 9:
                    send(channel, "Signs point to yes.");
                    break;
                case 10:
                    send(channel, "Outlook doesn't look good.");
                    break;
                case 11:
                    send(channel, "The future seems hazy on this.");
                    break;
                case 12:
                    if (channelInfo.getQuoteSize() > 1) {
                        send(channel, "Maybe these words of wisdom can guide you: (_QUOTE_)");
                    } else
                        send(channel, "I can provide no help for your situation.");
                    break;
                default:
                    send(channel, "Unable to discern.");
                    break;

                }
                lastConch = newConch;
            }
        }
        if (msg[0].equalsIgnoreCase(prefix + "whisper") && isAdmin) {
            this.sendCommand("#jtv", ".w endsgamer test");

        }
        if (msg[0].equalsIgnoreCase(prefix + "punishstats") && isOp) {
            log("RB: Matched command !punishstats");

            long timeSince = channelInfo.timeSincePunished();
            int days = (int) (timeSince / 86400);
            int hours = (int) ((timeSince / 3600) % 24);
            int mins = (int) ((timeSince / 60) % 60);
            int seconds = (int) (timeSince % 60);
            String parsedSince = "";
            if (days > 0) {
                parsedSince = "It has been " + days + " days, " + hours + " hours, " + mins + " minutes, and "
                        + seconds + " seconds since a punishment has been issued.";
            } else if (hours > 0) {
                parsedSince = "It has been " + hours + " hours, " + mins + " minutes, and " + seconds
                        + " seconds since a punishment has been issued.";
            } else if (mins > 0) {
                parsedSince = "It has been " + mins + " minutes, and " + seconds
                        + " seconds since a punishment has been issued.";

            } else {
                parsedSince = "It has been " + seconds + " seconds since a punishment has been issued.";
            }
            send(channel,
                    "The number of punishments doled out is " + channelInfo.getPunCount() + ". " + parsedSince);
        }
        // +whatprefix
        if (msg[0].equalsIgnoreCase("+whatprefix") && isAdmin) {
            send(channel, "The prefix for this channel is: " + prefix);
        }
        // !altsend
        if (msg[0].equalsIgnoreCase(prefix + "altsend") && isAdmin) {
            if (msg.length > 2 && msg[1].startsWith("#")) {
                send(msg[1], fuseArray(msg, 2));
                send(channel, "Message sent to " + msg[1] + ": " + fuseArray(msg, 2));
            }
        }
        // !coetime
        // if (msg[0].equalsIgnoreCase(prefix + "coetime")) {
        // String time = Calendar.getInstance().getTime().toString();
        // System.out.println(time);
        // int indexColon = time.indexOf(":") - 2;
        // int end = time.indexOf(" ", indexColon + 1);
        // end = time.indexOf(" ", end + 1);
        // time = time.substring(indexColon, end);
        // send(channel, "CoeTime is currently: " + time);
        // }
        // !hug
        // if (msg[0].equalsIgnoreCase(prefix + "hug") && isSub) {
        // if (msg.length > 1) {
        // send(channel, "(>_)> " + fuseArray(msg, 1));
        // } else
        // send(channel, "Syntax is " + prefix + "hug <single word>");
        // }

        // !uptime - All

        if (msg[0].equalsIgnoreCase(prefix + "uptime")) {
            log("RB: Matched command !uptime");
            try {
                String uptime = JSONUtil.krakenCreated_at(channelInfo.getTwitchName());
                send(channel, this.getTimeStreaming(uptime));
            } catch (Exception e) {
                send(channel, "Error accessing Twitch API.");
            }
            return;
        }

        // !music - All
        if (msg[0].equalsIgnoreCase(prefix + "music")) {
            log("RB: Matched command !music");
            String currBullet = bullet[0];
            bullet[0] = "";
            send(channel, "Now playing: " + JSONUtil.lastFM(channelInfo.getLastfm()));
            bullet[0] = currBullet;

        }

        String senderTriggered = "";
        if (msg[0].equalsIgnoreCase(prefix + "whalepenis")) {
            if (msg.length > 1 && isOp) {
                senderTriggered = sender;
                if (msg[1].equalsIgnoreCase("on")) {
                    channelInfo.setWp(true);
                    send(channel, "Whale penis timer has been turned on");

                } else if (msg[1].equalsIgnoreCase("off")) {
                    channelInfo.setWp(false);
                    send(channel, "Whale penis timer has been turned off");

                } else if (msg[1].equalsIgnoreCase("stats")) {

                    long timeSince = channelInfo.timeSinceNoUpdate();
                    int days = (int) (timeSince / 86400);
                    int hours = (int) ((timeSince / 3600) % 24);
                    int mins = (int) ((timeSince / 60) % 60);
                    int seconds = (int) (timeSince % 60);

                    if (days > 0) {
                        send(channel, "It has been " + days + " days, " + hours + " hours, " + mins
                                + " minutes, and " + seconds
                                + " seconds since whale penis has last been mentioned. It has been mentioned "
                                + channelInfo.getWpCount() + " times.");
                    } else if (hours > 0) {
                        send(channel, "It has been " + hours + " hours, " + mins + " minutes, and " + seconds
                                + " seconds since whale penis has last been mentioned. It has been mentioned "
                                + channelInfo.getWpCount() + " times.");
                    } else if (mins > 0) {
                        send(channel, "It has been " + mins + " minutes, and " + seconds
                                + " seconds since whale penis has last been mentioned. It has been mentioned "
                                + channelInfo.getWpCount() + " times.");
                    } else {
                        send(channel, "It has been " + seconds
                                + " seconds since whale penis has last been mentioned. It has been mentioned "
                                + channelInfo.getWpCount() + " times.");
                    }
                } else
                    send(channel, "Command syntax: " + prefix + "whalepenis <on/off/stats>");
            }
        }

        // whale penis timer

        String combined = this.fuseArray(msg, 0);
        combined = combined.toLowerCase();

        if (((combined.indexOf("whalepenis") > -1) || (combined.indexOf("whale penis") > -1)) && channelInfo.getWp()
                && !sender.equalsIgnoreCase(getNick()) && !sender.equalsIgnoreCase(senderTriggered)) {

            channelInfo.increaseWpCount();
            channelInfo.timeSinceSaid();

        }

        // !lastfm - All
        if (msg[0].equalsIgnoreCase(prefix + "lastfm")) {
            log("RB: Matched command !lastfm");
            send(channel, "http://www.last.fm/user/" + channelInfo.getLastfm());
        }
        // !songlink
        if (msg[0].equalsIgnoreCase(prefix + "songlink")) {
            String songName = JSONUtil.lastFM(channelInfo.getLastfm());
            String url = JSONUtil.lastFMURL(channelInfo.getLastfm());
            send(channel, "You can get " + songName + " at " + url);
        }
        // statusgame
        if (msg[0].equalsIgnoreCase(prefix + "statusgame") && BotManager.getInstance().twitchChannels) {
            log("RB: Matched command !statusgame");
            if (isOp && msg.length > 1) {
                String newStatus = this.fuseArray(msg, 1);
                newStatus.trim();

                try {
                    channelInfo.updateStatus(newStatus);
                    if (JSONUtil.steam(channelInfo.getSteam(), "game").equals("(unavailable)")) {
                        channelInfo.updateGame("");
                    } else
                        channelInfo.updateGame(JSONUtil.steam(channelInfo.getSteam(), "game"));
                    send(channel, "Game and Status update sent.");
                } catch (Exception ex) {
                    send(channel, "Error updating game and/or status. Did you add me as an editor?");
                }

            }

        }
        // !wiki
        // if (msg[0].equalsIgnoreCase(prefix + "wiki") && isRegular) {
        // if (msg.length > 1) {
        // String searchTerm = fuseArray(msg, 1);
        // String response = JSONUtil.getWiki(searchTerm, 1);
        // if (response.length() > 256) {
        // response = response.substring(0, 256);
        // }
        // send(channel, response);
        // } else {
        // send(channel, "Usage is " + prefix + "wiki <article name>");
        // }
        // }
        // steamgame
        if (msg[0].equalsIgnoreCase(prefix + "steamgame") && BotManager.getInstance().twitchChannels) {
            log("RB: Matched command !steamgame");
            if (isOp) {

                try {

                    if (JSONUtil.steam(channelInfo.getSteam(), "game").equals("(unavailable)")) {
                        channelInfo.updateGame("");
                        send(channel, "Steam game unavailable, changed game to \"Not Playing\" status.");
                    } else {
                        channelInfo.updateGame(JSONUtil.steam(channelInfo.getSteam(), "game"));
                        send(channel, "Steam game updated to " + JSONUtil.steam(channelInfo.getSteam(), "game"));
                    }
                } catch (Exception ex) {
                    send(channel, "Error updating game. Did you add me as an editor?");
                }

            }

        }
        // !me
        if (msg[0].equalsIgnoreCase(prefix + "me") && isOp) {
            if (msg.length > 1) {
                String rest = fuseArray(msg, 1);
                sendCommand(channel, ".me " + rest);
            } else
                send(channel, "Useage is " + prefix + "me <string>");
        }

        // !game - All

        if (msg[0].equalsIgnoreCase(prefix + "game") && BotManager.getInstance().twitchChannels) {
            log("RB: Matched command !game");
            if (isOp && msg.length > 1) {
                String game = this.fuseArray(msg, 1);
                game.trim();
                if (game.equals("-"))
                    game = "";
                try {
                    if (channelInfo.updateGame(game)) {
                        send(channel, "Game update sent.");
                    } else
                        send(channel, "Error updating game, probably a Twitch API issue, try again in a minute.");
                } catch (Exception ex) {
                    send(channel, "Error updating game. Did you add me as an editor?");
                }

            } else {
                String game = JSONUtil.krakenGame(twitchName);
                if (game.length() > 0) {
                    send(channel, "Current game: " + game);
                } else {
                    send(channel, "No game set.");
                }
            }
            return;
        }

        // !status - All
        if (msg[0].equalsIgnoreCase(prefix + "status")) {
            log("RB: Matched command !status");
            if (isOp && msg.length > 1 && BotManager.getInstance().twitchChannels) {
                String status = this.fuseArray(msg, 1);
                status.trim();
                try {
                    boolean result = channelInfo.updateStatus(status);
                    if (result) {
                        send(channel, "Status update sent.");
                    } else
                        send(channel, "Status not updated, probably an API issue.");
                } catch (Exception ex) {
                    send(channel, "Error updating status. Did you add me as an editor?");
                }
            } else {
                String status = "";

                status = JSONUtil.krakenStatus(twitchName);

                if (status.length() > 0) {
                    send(channel, status);
                } else {
                    send(channel, "Unable to query API.");
                }
            }
            return;
        }

        // google command
        if (msg[0].equalsIgnoreCase(prefix + "google") && isSub) {
            log("RB: Matched command !google");
            if (msg.length > 1) {
                String rawQuery = fuseArray(msg, 1);
                String encodedQuery = "";
                try {
                    encodedQuery = URLEncoder.encode(rawQuery, "UTF-8");
                } catch (UnsupportedEncodingException e) {

                    e.printStackTrace();
                }
                String url = "https://www.google.com/search?q=" + encodedQuery;
                send(channel, JSONUtil.shortenUrlTinyURL(url));
            }
        }
        // isLive
        if (msg[0].equalsIgnoreCase(prefix + "islive") && isOp) {
            if (msg.length > 1) {
                if (JSONUtil.krakenIsLive(msg[1].toLowerCase())) {
                    send(channel, "Yes, " + msg[1].toLowerCase() + " is streaming " + JSONUtil.krakenGame(msg[1])
                            + " to " + JSONUtil.krakenViewers(msg[1]) + " viewers right now.");
                } else
                    send(channel, "No, " + msg[1].toLowerCase() + " isn't streaming right now.");
            } else {
                if (JSONUtil.krakenIsLive(channel.substring(1))) {
                    send(channel, "Yes, " + channel.substring(1) + " is streaming right now.");
                } else
                    send(channel, "No, " + channel.substring(1) + " isn't streaming right now.");
            }
        }
        // !ishere
        if (msg[0].equalsIgnoreCase(prefix + "ishere") && isOp) {
            if (msg.length > 1) {
                if (JSONUtil.tmiChatters(channel.substring(1)).contains(msg[1].toLowerCase())) {
                    send(channel, "Yes, " + msg[1] + " is connected to chat.");
                } else
                    send(channel, "No, " + msg[1] + " is not connected to chat.");

            }
        }
        // !host
        if (msg[0].equalsIgnoreCase(prefix + "host") && isOwner) {
            if (msg.length > 1) {
                if (msg[1].equalsIgnoreCase("random")) {

                    ArrayList<String> whitelisted = channelInfo.getRaidWhitelist();
                    int rand = (int) (Math.random() * whitelisted.size() - 1);
                    boolean found = false;

                    while (whitelisted.size() > 0) {
                        if (!JSONUtil.krakenIsLive(whitelisted.get(rand))) {
                            whitelisted.remove(rand);
                            rand = (int) (Math.random() * whitelisted.size() - 1);
                        } else {
                            found = true;
                            break;
                        }
                    }
                    if (found) {
                        send(channel, "Now hosting: " + whitelisted.get(rand));
                        sendCommand(channel, ".host " + whitelisted.get(rand));

                    } else
                        send(channel, "None of the whitelisted channels are streaming right now.");

                } else if (msg[1].equalsIgnoreCase("samegame") && isOwner) {
                    String response = JSONUtil.getGameChannel(JSONUtil.krakenGame(twitchName));
                    if (response.equalsIgnoreCase("No other channels playing this game")
                            || response.equalsIgnoreCase("Error Querying API")) {
                        send(channel, response);
                    } else {
                        send(channel, "Now hosting: " + response);
                        sendCommand(channel, ".host " + response);
                    }

                } else {
                    if (JSONUtil.krakenChannelExist((msg[1]))) {
                        send(channel, "Now hosting: " + msg[1]);
                        sendCommand(channel, ".host " + msg[1]);
                    } else
                        send(channel, msg[1] + " isn't streaming right now.");
                }

            } else
                send(channel, "Syntax is " + prefix + "host <random/channelName>");
        }
        if (msg[0].equalsIgnoreCase(prefix + "unhost") && isOwner) {
            sendCommand(channel, ".unhost");
            send(channel, "Exited host mode.");
        }
        // !raid commands
        if (msg[0].equalsIgnoreCase(prefix + "raid") && isOwner) {
            if (msg.length > 3) {
                if (msg[1].equalsIgnoreCase("whitelist")) {
                    if (msg[2].equalsIgnoreCase("add")) {
                        String channelname = msg[3].toLowerCase();
                        if (!channelInfo.getRaidWhitelist().contains(channelname)) {
                            channelInfo.addRaidWhitelist(msg[3]);
                            send(channel, msg[3] + " has been added to the raid whitelist.");
                        } else
                            send(channel, channelname + " is already in the raid whitelist");

                    } else if ((msg[2].equalsIgnoreCase("delete") || msg[2].equalsIgnoreCase("remove"))) {
                        String channelname = msg[3].toLowerCase();
                        if (channelInfo.getRaidWhitelist().contains(channelname)) {
                            channelInfo.deleteRaidWhitelist(msg[3]);
                            send(channel, msg[3] + " has been removed from the raid whitelist.");
                        } else
                            send(channel, channelname + " wasn't in the raid whitelist.");

                    } else
                        send(channel, "Syntax is " + prefix + "raid whitelist <add/remove> <channelName>");
                }

            } else if (isOwner && msg.length > 1) {
                if (isOwner && msg[1].equalsIgnoreCase("random")) {

                    ArrayList<String> whitelisted = channelInfo.getRaidWhitelist();
                    int rand = (int) (Math.random() * whitelisted.size() - 1);
                    boolean found = false;

                    while (whitelisted.size() > 0) {
                        if (!JSONUtil.krakenIsLive(whitelisted.get(rand))) {
                            whitelisted.remove(rand);
                            rand = (int) (Math.random() * whitelisted.size() - 1);
                        } else {
                            found = true;
                            break;
                        }
                    }
                    if (found)
                        send(channel,
                                "Go raid " + whitelisted.get(rand) + "! http://twitch.tv/" + whitelisted.get(rand));
                    else
                        send(channel, "None of the whitelisted channels are streaming right now.");
                } else if (isOwner && msg[1].equalsIgnoreCase("list")) {
                    ArrayList<String> list = channelInfo.getRaidWhitelist();
                    String raidList = "";
                    for (int i = 0; i < list.size(); i++) {
                        if (i == list.size() - 1) {
                            raidList += list.get(i);
                        } else
                            raidList += list.get(i) + ", ";
                    }
                    send(channel, raidList);
                } else if (msg[1].equalsIgnoreCase("samegame") && isOwner) {
                    String response = JSONUtil.getGameChannel(JSONUtil.krakenGame(twitchName));
                    if (response.equalsIgnoreCase("No other channels playing this game")
                            || response.equalsIgnoreCase("Error Querying API")) {
                        send(channel, response);
                    } else {
                        send(channel, "Go raid " + response + "! http://twitch.tv/" + response);
                    }

                } else
                    send(channel, "Go raid " + msg[1] + "! http://twitch.tv/" + msg[1]);
            } else
                send(channel, "Syntax is " + prefix + "raid whitelist <add/delete> <channelName> or " + prefix
                        + "raid <list/random/channelName>");
        }
        // !followme - Owner
        if (msg[0].equalsIgnoreCase(prefix + "followme") && isOwner && BotManager.getInstance().twitchChannels) {
            log("RB: Matched command !followme");
            BotManager.getInstance().followChannel(twitchName);
            send(channel, "Follow update sent.");
            return;
        }

        if ((msg[0].equalsIgnoreCase(prefix + "strawpoll") && isOp && msg.length > 1)) {
            if (msg.length > 3) {
                String newString = fuseArray(msg, 1);
                String[] params = newString.split(";");
                String[] options = params[1].split(",");
                String title = params[0];
                boolean multi = false;
                boolean permissive = false;
                if (params.length > 2) {

                    multi = Boolean.valueOf(Boolean.parseBoolean(params[2].trim()));

                    if (params.length > 3) {

                        permissive = Boolean.valueOf(Boolean.parseBoolean(params[3].trim()));

                    }
                }

                JSONObject postObject = new JSONObject();
                postObject.put("title", title);
                JSONArray optionsArr = new JSONArray();

                for (String s : options) {
                    optionsArr.add(s.trim());
                }
                postObject.put("options", optionsArr);
                postObject.put("multi", multi);
                postObject.put("permissive", permissive);
                postObject.put("captcha", false);
                String postData = postObject.toJSONString();
                System.out.println(postData);
                String id = BotManager.postRemoteDataStrawpoll(postData);
                if (id != null) {
                    channelInfo.setLastStrawpoll(Integer.parseInt(id));
                    send(channel, "Strawpoll.me/" + id);
                }
            } else if (msg[1].equalsIgnoreCase("results")) {

                String strawpollHtml = BotManager
                        .getRemoteContent("http://strawpoll.me/api/v2/polls/" + channelInfo.getLastStrawpoll());

                try {

                    JSONParser parser = new JSONParser();
                    Object obj = parser.parse(strawpollHtml);
                    JSONObject jsonObject = (JSONObject) obj;
                    JSONArray optionsArr = (JSONArray) jsonObject.get("options");
                    JSONArray votesArr = (JSONArray) jsonObject.get("votes");
                    String resultsString = "";
                    for (int i = 0; i < optionsArr.size(); i++) {
                        resultsString += "\"" + ((String) optionsArr.get(i)).trim() + "\": " + votesArr.get(i);
                        if (i < optionsArr.size() - 1)
                            resultsString += ", ";

                    }
                    send(channel, resultsString);
                } catch (Exception e) {
                    e.printStackTrace();
                    send(channel, "Error parsing the results.");
                }

            }
        }

        // !properties - Owner
        if (msg[0].equalsIgnoreCase(prefix + "properties") && isOp && BotManager.getInstance().twitchChannels) {
            log("RB: Matched command !properties");
            send(channel, JSONUtil.getChatProperties(channelInfo.getTwitchName()));
            return;
        }

        // !commands - Op
        if ((msg[0].equalsIgnoreCase(prefix + "commands") || msg[0].equalsIgnoreCase(prefix + "coemands"))
                && isSub) {
            log("RB: Matched command !commands");

            if (BotManager.getInstance().CoeBotTVAPIKey.length() < 4) {

                ArrayList<String> sorted = channelInfo.getCommandList();
                String sortedList = "";
                for (int i = 0; i < sorted.size(); i++) {
                    if (i == sorted.size() - 1) {
                        sortedList += sorted.get(i);
                    } else
                        sortedList += sorted.get(i) + ", ";
                }
                send(channel, "Commands: " + sortedList);
            } else {

                send(channel, "You can find the list of commands at coebot.tv/c/" + twitchName + "/#commands");
            }
            return;
        }
        // youtube title parser
        if ((BotManager.getInstance().YoutubeAPIKey.length() > 4) && channelInfo.shouldParseYoutube()) {
            String msgs = fuseArray(msg, 0);
            if (!msg[0].equalsIgnoreCase(prefix + "songrequest")) {
                if ((((msgs.indexOf("youtube.com/watch?v=") > -1) || msgs.indexOf("youtu.be/") > -1)
                        && (permitted || isRegular)) && !msgs.contains("--ignore")) {
                    msgs.trim();
                    if (msgs.indexOf("youtube.com/watch?v=") > -1) {
                        int indexOfId = msgs.indexOf("=") + 1;

                        int indexOfSpace = msgs.indexOf(" ", indexOfId);
                        int indexOfPound = msgs.indexOf("#", indexOfId);
                        int indexOfAnd = msgs.indexOf("&", indexOfId);
                        int indexOfQuestion = msgs.indexOf("?", indexOfId);
                        String id = "";

                        if (indexOfSpace > -1) {
                            if (indexOfPound > -1 && indexOfPound <= indexOfSpace) {
                                id = msgs.substring(indexOfId, indexOfPound);
                            } else {
                                id = msgs.substring(indexOfId, indexOfSpace);
                            }
                        } else {
                            if (indexOfPound > -1)
                                id = msgs.substring(indexOfId, indexOfPound);
                            else if (indexOfAnd > -1 && !msgs.contains("feature="))
                                id = msgs.substring(indexOfId, indexOfAnd);
                            else if (indexOfQuestion > -1)
                                id = msgs.substring(indexOfId, indexOfQuestion);
                            else
                                id = msgs.substring(indexOfId);
                        }
                        log("youtube id  " + id);
                        String title = JSONUtil.youtubeTitle(id);
                        if (title != null) {
                            send(channel, "Linked YouTube Video: \"" + title + "\"");
                        }

                    } else {
                        int indexOfId = msgs.indexOf(".be/") + 4;
                        int indexOfSpace = msgs.indexOf(" ", indexOfId);
                        int indexOfPound = msgs.indexOf("#", indexOfId);
                        int indexOfAnd = msgs.indexOf("&", indexOfId);
                        int indexOfQuestion = msgs.indexOf("?", indexOfId);
                        String id = "";

                        if (indexOfSpace > -1) {
                            if (indexOfPound > -1 && indexOfPound <= indexOfSpace) {
                                id = msgs.substring(indexOfId, indexOfPound);
                            } else {
                                id = msgs.substring(indexOfId, indexOfSpace);
                            }
                        } else {
                            if (indexOfPound > -1)
                                id = msgs.substring(indexOfId, indexOfPound);
                            else if (indexOfAnd > -1 && !msgs.contains("feature="))
                                id = msgs.substring(indexOfId, indexOfAnd);
                            else if (indexOfQuestion > -1)
                                id = msgs.substring(indexOfId, indexOfQuestion);
                            else
                                id = msgs.substring(indexOfId);
                        }
                        log("youtube id  " + id);
                        String title = JSONUtil.youtubeTitle(id);
                        if (title != null) {
                            send(channel, "Linked YouTube Video: \"" + title + "\"");
                        }
                    }
                }
            }
        }
        // !throw - All
        // if (msg[0].equalsIgnoreCase(prefix + "throw") && (isSub)) {
        // log("RB: Matched command !throw");
        // if (msg.length > 1) {
        //
        // send(channel, " ( " +
        // fuseArray(msg, 1));
        // }
        //
        // }

        // !topic
        if (msg[0].equalsIgnoreCase(prefix + "topic") && channelInfo.useTopic) {
            log("RB: Matched command !topic");
            if (msg.length < 2 || !isOp) {
                if (channelInfo.getTopic().equalsIgnoreCase("")) {
                    if (BotManager.getInstance().twitchChannels) {
                        String status = "";

                        status = JSONUtil.krakenStatus(twitchName);

                        if (status.length() > 0)
                            send(channel, status);
                        else
                            send(channel, "Unable to query API.");
                    } else {
                        send(channel, "Topic not set");
                    }
                } else {
                    send(channel,
                            "Topic: " + channelInfo.getTopic() + " (Set " + channelInfo.getTopicTime() + " ago)");
                }
            } else if (msg.length > 1 && isOp) {
                if (msg[1].equalsIgnoreCase("unset")) {
                    channelInfo.setTopic("");
                    send(channel, "No topic is set.");
                } else {
                    channelInfo.setTopic(message.substring(7));
                    send(channel,
                            "Topic: " + channelInfo.getTopic() + " (Set " + channelInfo.getTopicTime() + " ago)");
                }

            }
            return;
        }

        // !link
        if (msg[0].equalsIgnoreCase(prefix + "link") && isSub) {
            log("RB: Matched command !link");
            if (msg.length > 1) {
                String rawQuery = message.substring(6);
                String encodedQuery = "";
                try {
                    encodedQuery = URLEncoder.encode(rawQuery, "UTF-8");
                } catch (UnsupportedEncodingException e) {

                    e.printStackTrace();
                }
                String url = "http://lmgtfy.com/?q=" + encodedQuery;
                send(channel, "Link to \"" + rawQuery + "\": " + JSONUtil.shortenUrlTinyURL(url));
            }
            return;
        }

        // !commercial
        if ((msg[0].equalsIgnoreCase(prefix + "commercial") || msg[0].equalsIgnoreCase(prefix + "coemercial"))
                && BotManager.getInstance().twitchChannels && isOp) {
            if (isOp) {
                log("RB: Matched command !commercial");

                if (msg.length > 1) {
                    if (Integer.parseInt(msg[1]) % 30 == 0 && Integer.parseInt(msg[1]) <= 180) {
                        channelInfo.scheduleCommercial(Integer.parseInt(msg[1]));

                        send(channel, "A " + msg[1]
                                + " second commercial will be run in 45 seconds. Thank you for supporting this channel!");
                    } else
                        send(channel,
                                "Commercials must be a multiple of 30 seconds and less than 180 seconds long.");
                } else {
                    channelInfo.scheduleCommercial();
                    send(channel, "A commercial will be run in 45 seconds. Thank you for supporting this channel!");
                }

            }

        }
        // !cancel
        if (msg[0].equalsIgnoreCase(prefix + "cancel") && isOp) {
            log("RB: Matched command !cancel");

            channelInfo.cancelCommercial();
            send(channel, "The commercial has been cancelled.");

        }

        // !list -Ops
        if (msg[0].equalsIgnoreCase(prefix + "list") && isOp) {
            if (msg.length > 2) {
                if (msg[1].equalsIgnoreCase("add")) {
                    String listName = msg[2].replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
                    if (channelInfo.getCommand(listName) != null) {
                        send(channel, "This list name is already a command.");
                    }
                    boolean added = channelInfo.addList(listName, 1);
                    if (added) {
                        send(channel, "List \"" + listName + "\" created.");
                    } else {
                        send(channel, "Unable to create list, or it already existed.");
                    }

                } else if (msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove")) {
                    String listName = msg[2].replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
                    boolean removed = channelInfo.deleteList(listName);
                    if (removed) {
                        send(channel, "List \"" + listName + "\" removed.");
                    } else {
                        send(channel, "Unable to remove list, or it doesn't exist.");
                    }

                } else if (msg[1].equalsIgnoreCase("restrict") && msg.length > 3) {
                    String listName = msg[2].toLowerCase();
                    String levelStr = msg[3].toLowerCase();
                    int level = -1;
                    if (channelInfo.checkList(listName)) {
                        if (levelStr.equalsIgnoreCase("owner") || levelStr.equalsIgnoreCase("owners")) {
                            level = 3;
                        }
                        if (levelStr.equalsIgnoreCase("mod") || levelStr.equalsIgnoreCase("mods")
                                || levelStr.equalsIgnoreCase("moderators")
                                || levelStr.equalsIgnoreCase("moderator")) {
                            level = 2;
                        }
                        if (levelStr.equalsIgnoreCase("regular") || levelStr.equalsIgnoreCase("regulars")
                                || levelStr.equalsIgnoreCase("regs") || levelStr.equalsIgnoreCase("subs")) {
                            level = 1;
                        }
                        if (levelStr.equalsIgnoreCase("everyone") || levelStr.equalsIgnoreCase("all")) {
                            level = 0;
                        }
                        if (level > -1) {
                            channelInfo.restrictList(listName, level);
                            send(channel, listName + " successfully restricted to " + levelStr + ".");

                        } else {
                            send(channel, "Not a valid restriction group.");
                        }
                    } else {
                        send(channel, "That list does not exist.");
                    }
                } else if (msg[1].equalsIgnoreCase("rename") && msg.length > 3) {
                    String listName = msg[2].toLowerCase();
                    String newName = msg[3].toLowerCase().replaceAll("[^a-zA-Z0-9]", "");
                    if (channelInfo.checkList(listName)) {
                        channelInfo.renameList(listName, newName);
                        send(channel, "List \"" + listName + "\" renamed to \"" + newName + "\"");
                    }
                }

            }
        }

        // !command - Ops
        if ((msg[0].equalsIgnoreCase(prefix + "command") || (msg[0].equalsIgnoreCase(prefix + "coemand")))
                && isOp) {
            log("RB: Matched command !command");
            if (msg.length < 3) {
                send(channel,
                        "Syntax: \"!command add/delete [name] [message]\" - Name is the command trigger without \"!\" and message is the response.");
            } else if (msg.length > 2) {
                if (msg[1].equalsIgnoreCase("add") && msg.length > 3) {

                    String key = msg[2].replaceAll("[^a-zA-Z0-9]", "");
                    key = key.toLowerCase();
                    if (channelInfo.checkList(key)) {
                        send(channel, "This command name is already registered as a list.");
                        return;
                    }
                    String value = fuseArray(msg, 3);
                    int restriction = 1;

                    if (value.contains("(_PURGE_)") || value.contains("(_TIMEOUT_)") || value.contains("(_BAN_)")
                            || value.contains("(_COMMERCIAL_)")
                            || (value.contains("(_VARS_") && (value.contains("_INCREMENT_")
                                    || value.contains("_DECREMENT_") || value.contains("_SET_")))
                            || value.contains("(_SUBMODE_ON_)") || value.contains("(_SUBMODE_OFF_)")) {
                        restriction = 2;
                    } else {
                        channelInfo.setCommandsRestriction(key, 1);
                    }

                    channelInfo.setCommand(key, value, sender, restriction);
                    send(channel, "Command added/updated.");

                } else if (msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove")) {
                    String key = msg[2].replaceAll("[^a-zA-Z0-9]", "");
                    key = key.toLowerCase();
                    boolean removed = channelInfo.removeCommand(key);

                    channelInfo.removeRepeatCommand(key);
                    channelInfo.removeScheduledCommand(key);
                    if (removed) {
                        send(channel, "Command " + key + " removed.");
                    } else
                        send(channel, "Command " + key + " doesn't exist.");

                } else if (msg[1].equalsIgnoreCase("restrict") && msg.length >= 4) {
                    String command = msg[2].toLowerCase();
                    String levelStr = msg[3].toLowerCase();
                    int level = 0;
                    if (channelInfo.getCommand(command) != null) {
                        if (levelStr.equalsIgnoreCase("owner") || levelStr.equalsIgnoreCase("owners")) {
                            level = 3;
                        }
                        if (levelStr.equalsIgnoreCase("mod") || levelStr.equalsIgnoreCase("mods")
                                || levelStr.equalsIgnoreCase("moderators")
                                || levelStr.equalsIgnoreCase("moderator")) {
                            level = 2;
                        }
                        if (levelStr.equalsIgnoreCase("regular") || levelStr.equalsIgnoreCase("regulars")
                                || levelStr.equalsIgnoreCase("regs") || levelStr.equalsIgnoreCase("subs")) {
                            level = 1;
                        }
                        if (levelStr.equalsIgnoreCase("everyone") || levelStr.equalsIgnoreCase("all")) {
                            level = 0;
                        }

                        if (channelInfo.setCommandsRestriction(command, level))
                            send(channel, prefix + command + " restricted to " + levelStr + " only.");
                        else
                            send(channel, "Error setting restriction.");
                    } else {
                        send(channel, "Command does not exist.");
                    }
                } else if (msg[1].equalsIgnoreCase("rename") && msg.length > 3) {
                    String key = msg[2].toLowerCase();
                    String newKey = msg[3].toLowerCase();
                    boolean result = channelInfo.renameCommand(key, newKey, sender);
                    if (result) {
                        send(channel, "Command " + key + " renamed to " + newKey + " successfully.");
                    } else {
                        send(channel, "Unable to rename a command that doesn't exist.");
                    }
                } else if (msg[1].equalsIgnoreCase("clone") && msg.length > 3) {
                    if (msg[2].startsWith("#")) {
                        String otherchan = msg[2].toLowerCase();
                        Channel otherChannel = getChannelObject(otherchan);
                        if (otherChannel != null) {
                            String key = msg[3].toLowerCase().replaceAll("[^a-zA-Z0-9]", "");
                            String response = otherChannel.getCommand(key);
                            if (response != null) {
                                int restriction = otherChannel.getRestriction(msg[3]);
                                channelInfo.setCommand(key, response, sender, restriction);
                                send(channel, "Command " + key + " cloned from channel " + otherchan);
                            } else {
                                send(channel, "Command " + key + " doesn't exist in channel " + otherchan);
                            }
                        } else {
                            send(channel, otherchan + " doesn't have a config with this bot.");
                        }
                    } else {
                        send(channel, "Channel to be cloned from must be in format: #channel");
                    }
                } else if ((msg[1].equalsIgnoreCase("author") || msg[1].equalsIgnoreCase("editor"))
                        && msg.length > 2) {
                    String key = msg[2].toLowerCase();
                    String editor = channelInfo.getAuthor(key);
                    if (editor != null && !editor.equals("-1")) {
                        send(channel, "The last person to modify this command was " + editor + ".");
                    } else if (editor != null) {
                        send(channel, "That command does not exist.");
                    } else {
                        send(channel, "The last editor for this command is unknown.");
                    }
                }
            }
            return;
        }

        // !repeat - Ops
        if (msg[0].equalsIgnoreCase(prefix + "repeat") && isOp) {
            log("RB: Matched command !repeat");
            if (msg.length < 3) {
                if (msg.length > 1 && msg[1].equalsIgnoreCase("list")) {
                    String commandsRepeatKey = "";

                    Iterator itr = channelInfo.commandsRepeat.entrySet().iterator();

                    while (itr.hasNext()) {
                        Map.Entry pairs = (Map.Entry) itr.next();
                        RepeatCommand rc = (RepeatCommand) pairs.getValue();
                        commandsRepeatKey += pairs.getKey() + " [" + (rc.active == true ? "ON" : "OFF") + "] ("
                                + rc.delay + ")" + ", ";
                    }
                    send(channel, "Repeating commands: " + commandsRepeatKey);
                } else {
                    send(channel,
                            "Syntax: \"!repeat add/delete [commandname] [delay in seconds] [message difference - optional]\"");
                }
            } else if (msg.length > 2) {
                if (msg[1].equalsIgnoreCase("add") && msg.length > 3) {
                    String key = msg[2];
                    try {
                        int delay = Integer.parseInt(msg[3]);
                        int difference = 1;
                        if (msg.length == 5)
                            difference = Integer.parseInt(msg[4]);

                        if (channelInfo.getCommand(key).equalsIgnoreCase("invalid") || delay < 30) {
                            // Key not found or delay to short
                            send(channel, "Command not found or delay is less than 30 seconds.");
                        } else {
                            channelInfo.setRepeatCommand(key, delay, difference);
                            send(channel, "Command " + key + " will repeat every " + delay + " seconds if "
                                    + difference + " messages have passed.");
                        }

                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }

                } else if (msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove")) {
                    String key = msg[2];

                    channelInfo.removeRepeatCommand(key);
                    send(channel, "Command " + key + " will no longer repeat.");

                } else if (msg[1].equalsIgnoreCase("on") || msg[1].equalsIgnoreCase("off")) {
                    String key = msg[2];
                    if (msg[1].equalsIgnoreCase("on")) {
                        channelInfo.setRepeatCommandStatus(key, true);
                        send(channel, "Repeat command " + key + " has been enabled.");
                    } else if (msg[1].equalsIgnoreCase("off")) {
                        channelInfo.setRepeatCommandStatus(key, false);
                        send(channel, "Repeat command " + key + " has been disabled.");
                    }

                }
            }
            return;
        }

        // !schedule - Ops
        if (msg[0].equalsIgnoreCase(prefix + "schedule") && isOp) {
            log("RB: Matched command !schedule");
            if (msg.length < 3) {
                if (msg.length > 1 && msg[1].equalsIgnoreCase("list")) {
                    String commandsScheduleKey = "";

                    Iterator itr = channelInfo.commandsSchedule.entrySet().iterator();

                    while (itr.hasNext()) {
                        Map.Entry pairs = (Map.Entry) itr.next();
                        ScheduledCommand sc = (ScheduledCommand) pairs.getValue();
                        commandsScheduleKey += pairs.getKey() + " [" + (sc.active == true ? "ON" : "OFF") + "]"
                                + ", ";
                    }
                    send(channel, "Scheduled commands: " + commandsScheduleKey);
                } else {
                    send(channel,
                            "Syntax: \"!schedule add/delete/on/off [commandname] [pattern] [message difference - optional]\"");
                }
            } else if (msg.length > 2) {
                if (msg[1].equalsIgnoreCase("add") && msg.length > 3) {
                    String key = msg[2];
                    try {
                        String pattern = msg[3];
                        if (pattern.equals("hourly"))
                            pattern = "0 * * * *";
                        else if (pattern.equals("semihourly"))
                            pattern = "0,30 * * * *";
                        else
                            pattern = pattern.replace("_", " ");

                        int difference = 1;
                        if (msg.length == 5)
                            difference = Integer.parseInt(msg[4]);

                        if (channelInfo.getCommand(key).equalsIgnoreCase("invalid") || pattern.contains(",,")) {
                            // Key not found or delay to short
                            send(channel, "Command not found or invalid pattern.");
                        } else {
                            channelInfo.setScheduledCommand(key, pattern, difference);
                            send(channel, "Command " + key + " will repeat every " + pattern + " if " + difference
                                    + " messages have passed.");
                        }

                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }

                } else if (msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove")) {
                    String key = msg[2];
                    channelInfo.removeScheduledCommand(key);
                    send(channel, "Command " + key + " will no longer repeat.");

                } else if (msg[1].equalsIgnoreCase("on") || msg[1].equalsIgnoreCase("off")) {
                    String key = msg[2];
                    if (msg[1].equalsIgnoreCase("on")) {
                        channelInfo.setScheduledCommandStatus(key, true);
                        send(channel, "Scheduled command " + key + " has been enabled.");
                    } else if (msg[1].equalsIgnoreCase("off")) {
                        channelInfo.setScheduledCommandStatus(key, false);
                        send(channel, "Scheduled command " + key + " has been disabled.");
                    }

                }
            }
            return;
        }

        // !autoreply - Ops
        if (msg[0].equalsIgnoreCase(prefix + "autoreply") && isOp) {
            log("RB: Matched command !autoreply");
            if (msg.length < 3) {
                if (msg.length > 1 && msg[1].equalsIgnoreCase("list")) {
                    // for (int i = 0; i < channelInfo.autoReplyTrigger.size();
                    // i++) {
                    // String cleanedTrigger = channelInfo.autoReplyTrigger
                    // .get(i).toString().replaceAll("\\.\\*", "*")
                    // .replaceAll("\\\\Q", "")
                    // .replaceAll("\\\\E", "");
                    // send(channel,
                    // "[" + (i + 1) + "] " + cleanedTrigger
                    // + " ---> "
                    // + channelInfo.autoReplyResponse.get(i));
                    // }
                    send(channel, "You can find this channel's autoreplies at coebot.tv/c/" + twitchName
                            + "/#autoreplies");
                } else {
                    send(channel, "Syntax: \"!autoreply add/delete/list [pattern] [response]\"");
                }
            } else if (msg.length > 2) {
                if (msg[1].equalsIgnoreCase("add") && msg.length > 3) {
                    String pattern = msg[2].replaceAll("_", " ");
                    String response = fuseArray(msg, 3);

                    channelInfo.addAutoReply(pattern, response);
                    send(channel, "Autoreply added.");
                } else if (msg[1].equalsIgnoreCase("editresponse") && msg.length > 3) {
                    if (Main.isInteger(msg[2])) {
                        int pos = Integer.parseInt(msg[2]);
                        String newResponse = fuseArray(msg, 3);
                        if (channelInfo.editAutoReplyResponse(pos, newResponse))
                            send(channel, "Autoreply response edited.");
                        else
                            send(channel, "Autoreply not found. Are you sure you have the correct number?");
                    }
                } else if ((msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove"))
                        && msg.length > 2) {
                    if (Main.isInteger(msg[2])) {
                        int pos = Integer.parseInt(msg[2]);

                        if (channelInfo.removeAutoReply(pos))
                            send(channel, "Autoreply removed.");
                        else
                            send(channel, "Autoreply not found. Are you sure you have the correct number?");
                    }
                }
            }
            return;
        }

        // !poll - Ops
        if (msg[0].equalsIgnoreCase(prefix + "poll") && isOp) {
            log("RB: Matched command !poll");
            if (msg.length < 2) {
                send(channel, "Syntax: \"" + prefix + "poll create [option option ... option]\"");
            } else if (msg.length >= 2) {
                if (msg[1].equalsIgnoreCase("create")) {
                    String[] options = new String[msg.length - 2];
                    int oc = 0;
                    for (int c = 2; c < msg.length; c++) {
                        options[oc] = msg[c];
                        oc++;
                    }
                    channelInfo.setPoll(new Poll(options));
                    send(channel, "Poll created. Do '" + prefix + "poll start' to start voting.");
                } else if (msg[1].equalsIgnoreCase("start")) {
                    if (channelInfo.getPoll() != null) {
                        if (channelInfo.getPoll().getStatus()) {
                            send(channel, "Poll is alreay running.");
                        } else {
                            channelInfo.getPoll().setStatus(true);
                            send(channel, "Poll started. Type: " + prefix + "vote <option> to start voting.");
                        }
                    }
                } else if (msg[1].equalsIgnoreCase("stop")) {
                    if (channelInfo.getPoll() != null) {
                        if (channelInfo.getPoll().getStatus()) {
                            channelInfo.getPoll().setStatus(false);
                            send(channel, "Poll stopped.");
                        } else {
                            send(channel, "Poll is not running.");
                        }
                    }
                } else if (msg[1].equalsIgnoreCase("results")) {
                    if (channelInfo.getPoll() != null) {
                        send(channel, channelInfo.getPoll().getResultsString());
                        // String[] results =
                        // channelInfo.getPoll().getResults();
                        // for(int c=0;c<results.length;c++){
                        // send(channel, results[c]);
                        // }
                    }

                }
            }
            return;
        }

        // !giveaway - Ops
        if ((msg[0].equalsIgnoreCase(prefix + "giveaway") || msg[0].equalsIgnoreCase("!ga")) && isOp) {
            log("RB: Matched command !giveaway");
            if (msg.length < 2) {
                send(channel,
                        "Syntax: \"!giveaway create [max number] [time to run in seconds]\". Time is optional.");
            } else if (msg.length >= 2) {
                if (msg[1].equalsIgnoreCase("create")) {
                    String max = "" + 100;
                    if (msg.length > 2) {
                        max = msg[2];
                    }
                    channelInfo.setGiveaway(new Giveaway(max));
                    if (msg.length > 3 && channelInfo.getGiveaway().isInteger(msg[3])) {
                        this.startGaTimer(Integer.parseInt(msg[3]), channelInfo);
                    } else {
                        send(channel, "Giveaway created. Do !giveaway start' to start." + " Range 1-"
                                + channelInfo.getGiveaway().getMax() + ".");
                    }
                } else if (msg[1].equalsIgnoreCase("start")) {
                    if (channelInfo.getGiveaway() != null) {
                        if (channelInfo.getGiveaway().getStatus()) {
                            send(channel, "Giveaway is alreay running.");
                        } else {
                            channelInfo.getGiveaway().setStatus(true);
                            send(channel, "Giveaway started.");
                        }
                    }
                } else if (msg[1].equalsIgnoreCase("stop")) {
                    if (channelInfo.getGiveaway() != null) {
                        if (channelInfo.getGiveaway().getStatus()) {
                            channelInfo.getGiveaway().setStatus(false);
                            send(channel, "Giveaway stopped.");
                        } else {
                            send(channel, "Giveaway is not running.");
                        }
                    }
                } else if (msg[1].equalsIgnoreCase("results")) {
                    if (channelInfo.getGiveaway() != null) {
                        send(channel, channelInfo.getGiveaway().getResultsString());
                        // String[] results =
                        // channelInfo.getGiveaway().getResults();
                        // for(int c=0;c<results.length;c++){
                        // send(channel, results[c]);
                        // }
                    } else {
                        send(channel, "No giveaway results.");
                    }

                }
            }
            return;
        }

        // !raffle - Ops
        if (msg[0].equalsIgnoreCase(prefix + "raffle") && isOp) {
            log("RB: Matched command !raffle");
            if (msg.length >= 2) {
                if (msg[1].equalsIgnoreCase("enable")) {
                    if (channelInfo.raffle == null) {
                        channelInfo.raffle = new Raffle();
                    }
                    channelInfo.raffle.setEnabled(true);

                    send(channel, "Raffle enabled. Use \"" + prefix + "raffle\" to enter!");
                } else if (msg[1].equalsIgnoreCase("disable")) {
                    if (channelInfo.raffle != null) {
                        channelInfo.raffle.setEnabled(false);
                    }

                    send(channel, "Raffle disabled.");
                } else if (msg[1].equalsIgnoreCase("reset")) {
                    if (channelInfo.raffle != null) {
                        channelInfo.raffle.reset();
                    }

                    send(channel, "Raffle entries cleared.");
                } else if (msg[1].equalsIgnoreCase("count")) {
                    if (channelInfo.raffle != null) {
                        send(channel, "Raffle has " + channelInfo.raffle.count() + " entries.");
                    } else {
                        send(channel, "Raffle has 0 entries.");
                    }
                } else if (msg[1].equalsIgnoreCase("winner")) {
                    if (channelInfo.raffle != null) {
                        send(channel, "Winner is " + channelInfo.raffle.pickWinner() + "!");
                    } else {
                        send(channel, "No raffle history found.");
                    }
                }
            } else {
                if (channelInfo.raffle != null) {
                    channelInfo.raffle.enter(sender);
                }
            }
            return;
        }

        // !random - Ops
        if ((msg[0].equalsIgnoreCase(prefix + "random") || msg[0].equalsIgnoreCase(prefix + "roll"))) {
            log("RB: Matched command !random");

            if (msg.length > 1) {
                if (msg[1].equalsIgnoreCase("regular") && isOp) {
                    logMain("Matched command random regular");
                    ArrayList<String> onlineRegs = new ArrayList<String>();
                    ArrayList<String> chatters = JSONUtil.tmiChatters(twitchName);
                    Set<String> regs = channelInfo.getRegulars();
                    for (String s : chatters) {
                        if (regs.contains(s.toLowerCase())) {
                            onlineRegs.add(s);
                        }
                    }
                    if (onlineRegs.size() > 0) {
                        String selected = onlineRegs.get((int) (Math.random() * onlineRegs.size()));

                        send(channel, selected + " is the lucky random regular!");
                    } else
                        send(channel, "No regulars are connected to chat right now.");
                }
                String level = channelInfo.getRollLevel();
                boolean shouldTO = channelInfo.getRollTimeout();
                int defaultRoll = channelInfo.getRollDefault();
                if (level.equals("everyone") || (level.equals("regulars") && isRegular)
                        || (level.equals("moderators") && isOp)) {
                    long newRollTime = System.currentTimeMillis();

                    if ((newRollTime >= (lastRollTime + channelInfo.getRollCooldown() * 1000L)) || isOp) {
                        lastRollTime = newRollTime;
                        if (msg[1].equalsIgnoreCase("coin")) {
                            Random rand = new Random();
                            boolean coin = rand.nextBoolean();
                            if (coin == true)
                                send(channel, "Heads!");
                            else
                                send(channel, "Tails!");
                        } else if (isInteger(msg[1])) {
                            int randMax = Integer.parseInt(msg[1]);
                            if (randMax <= 0)
                                return;
                            long randReturn = Math.round((Math.random() * (randMax - 1)) + 1);

                            send(channel, sender + " rolled: " + randReturn);
                            if (randMax > 1 && randReturn == 1 && shouldTO) {
                                sendCommand(channel, ".timeout " + sender.toLowerCase() + " " + randMax * 5);
                            }
                        } else if (msg[1].matches("[123456]d\\d+")) {
                            int numDice = Integer.parseInt(msg[1].substring(0, 1));

                            int randMax = Integer.parseInt(msg[1].substring(2));
                            String rollReturn = sender + " rolled: ";

                            if (randMax <= 0) {
                                return;
                            }
                            for (int i = 0; i < numDice; i++) {
                                long randReturn = Math.round((Math.random() * (randMax - 1)) + 1);

                                rollReturn += randReturn + ", ";
                                if (randMax > 1 && randReturn == 1 && shouldTO) {
                                    sendCommand(channel,
                                            ".timeout " + sender.toLowerCase() + " " + randMax * 5 * numDice);
                                }
                            }
                            send(channel, rollReturn.substring(0, rollReturn.length() - 2) + ".");
                        } else {
                            long randReturn = Math.round((Math.random() * (defaultRoll - 1)) + 1);
                            send(channel, sender + " rolled: " + randReturn);
                            if (defaultRoll > 1 && randReturn == 1 && shouldTO) {
                                sendCommand(channel, ".timeout " + sender.toLowerCase() + " " + defaultRoll * 5);
                            }
                        }
                    }
                }
            }

        }
        // !songrequest
        if (msg[0].equalsIgnoreCase(prefix + "songrequest") && isSub && channelInfo.getSongRequest()) {
            String response = BotManager.postRemoteDataSongRequest(msg[1], channel.substring(1), sender);
            if (response != null) {
                JSONParser parser = new JSONParser();
                Object resp;
                try {
                    resp = parser.parse(response);
                    JSONObject respObj = (JSONObject) resp;
                    String status = (String) respObj.get("status");
                    if (status.equalsIgnoreCase("ok")) {
                        long queuePosition = (Long) respObj.get("queuePosition");
                        String title = (String) respObj.get("title");
                        send(channel, "Song requested: " + title + " by " + sender + ". Queue Position: "
                                + queuePosition);
                    } else if (status.equalsIgnoreCase("bad param: requestedBy")) {
                        send(channel, sender + " is unable to request songs.");
                    } else if (status.equalsIgnoreCase("bad param: url")) {
                        send(channel, "Unable to process song url.");
                    }
                } catch (Exception e) {

                    e.printStackTrace();
                }
            }
        }
        // !highlights
        if (msg[0].equalsIgnoreCase(prefix + "highlights")) {
            send(channel,
                    "You can find all marked highlights at: coebot.tv/c/" + channel.substring(1) + "#highlights");
        }

        // ##########################QUOTES##############################
        if (msg[0].equalsIgnoreCase(prefix + "quotes") && isSub) {
            send(channel, "http://coebot.tv/c/" + channel.substring(1) + "/#quotes");
        }
        if (msg[0].equalsIgnoreCase(prefix + "quote") && isSub) {
            if (msg.length > 1) {

                long newQuoted = System.currentTimeMillis();

                if ((newQuoted >= (lastQuoted + 30 * 1000L)) || isOp) {

                    // getQuote
                    if (msg[1].equalsIgnoreCase("get")) {
                        log("RB: Matched command !getQuote");
                        if (isSub && msg.length > 2) {
                            int index = Integer.parseInt(msg[2]);
                            send(channel, "Quote #" + index + ": " + channelInfo.getQuote(index - 1));
                            lastQuoted = System.currentTimeMillis();
                        } else {
                            send(channel, "Syntax is " + prefix + "quote get <index>");
                        }
                    }

                }
                // randomquote
                long newQuoted1 = System.currentTimeMillis();
                if ((newQuoted1 >= (lastQuoted1 + 30 * 1000L)) || isOp) {
                    // randomquote
                    if (msg[1].equalsIgnoreCase("random")) {
                        log("RB: Matched command !randomquote");
                        if (msg.length > 1) {
                            int randQuotes = (int) (Math.random() * channelInfo.getQuoteSize());
                            if (randQuotes > -1) {
                                int tempNum = randQuotes + 1;
                                send(channel, "Quote #" + tempNum + ": " + channelInfo.getQuote(randQuotes));
                                lastQuoted1 = System.currentTimeMillis();
                            } else
                                send(channel, "Error, whoops");
                        } else {
                            send(channel, "Syntax is " + prefix + "quote random");
                        }
                    }
                }
                // !editquote
                if (msg[1].equalsIgnoreCase("edit")) {
                    log("RB: Matched command !editquote");
                    if (isOp && msg.length > 3) {
                        int index = Integer.parseInt(msg[2]) - 1;
                        String quoteReceived = this.fuseArray(msg, 3);
                        quoteReceived = quoteReceived.trim();
                        boolean edited = channelInfo.editQuote(index, quoteReceived, sender);
                        if (edited) {
                            index += 1;
                            send(channel, "Quote #" + index + " edited successfully.");
                        } else {
                            send(channel, "No quote at requested index to edit.");
                        }

                    }
                }
                // !addQuote
                if (msg[1].equalsIgnoreCase("add")) {
                    log("RB: Matched command !addQuote");
                    if (isOp && msg.length > 2) {

                        String quoteReceived = this.fuseArray(msg, 2);

                        quoteReceived = quoteReceived.trim();
                        int numQuote = channelInfo.addQuote(quoteReceived, sender);
                        if (numQuote > -1) {
                            numQuote++;
                            send(channel, quoteReceived + " added, this is quote #" + numQuote);
                        } else
                            send(channel, "Quote already exists.");

                    }
                }

                // !getQuoteIndex
                if (msg[1].equalsIgnoreCase("getindex")) {
                    log("RB: Matched command !getQuoteIndex");
                    if (isSub && msg.length > 2 && BotManager.getInstance().twitchChannels) {
                        String quoteReceived = this.fuseArray(msg, 2);
                        quoteReceived = quoteReceived.trim();
                        int index = channelInfo.getQuoteIndex(quoteReceived);
                        if (index > -1) {
                            index++;
                            send(channel, "This quote's index is " + index);
                        } else {
                            send(channel, "Quote not found, make sure you have the EXACT quote");
                        }
                    }
                }
                // search
                if (msg[1].equalsIgnoreCase("search") && isOp) {
                    log("RB: Matched command !search");
                    String containers = "Quotes containing \"" + fuseArray(msg, 2) + "\": ";

                    int num = 0;
                    for (int i = 0; i < channelInfo.getQuoteSize(); i++) {
                        if (channelInfo.getQuote(i).toLowerCase()
                                .contains(fuseArray(msg, 2).toLowerCase().trim())) {

                            num++;
                            int tempNum = i + 1;
                            containers += tempNum + ",";
                        }
                    }
                    if (num > 0) {
                        send(channel, containers.substring(0, containers.length() - 1) + ".");
                    } else
                        send(channel, "No quotes contained that phrase, sorry.");

                }
                // delQuote
                if (msg[1].equalsIgnoreCase("delete") || msg[1].equals("remove")) {
                    log("RB: Matched command !delQuote");
                    if (isOp && msg.length > 2) {
                        int index = Integer.parseInt(msg[2]);
                        if (channelInfo.deleteQuote(index - 1)) {
                            send(channel, "Quote #" + index + " deleted successfully.");
                        } else {
                            send(channel, "Error deleting quote");
                        }
                    }
                }
                // getquote without get
                if ((newQuoted >= (lastQuoted + 30 * 1000L)) || isOp) {
                    if (isInteger(msg[1])) {
                        if (isSub) {
                            int index = Integer.parseInt(msg[1]);
                            send(channel, "Quote #" + index + ": " + channelInfo.getQuote(index - 1));
                            lastQuoted = System.currentTimeMillis();
                        }
                    }
                }
            } else {
                send(channel, "Your syntax is incorrect, please check the documentation.");
            }
        }

        if (msg[0].equalsIgnoreCase(prefix + "var")) {
            if (msg[1].equalsIgnoreCase("set") && isOp && msg.length > 3) {
                String varName = msg[2];
                String newValue = fuseArray(msg, 3);
                boolean result = JSONUtil.setVar(channel.substring(1), varName, newValue);
                if (result) {
                    send(channel, "Variable " + varName + " set to " + newValue + ".");
                } else {
                    send(channel, "Failed to set variable " + varName + ".");
                }
            } else if ((msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove")) && isOp
                    && msg.length > 2) {
                String varName = msg[2];
                boolean result = JSONUtil.deleteVar(channel.substring(1), varName);
                if (result) {
                    send(channel, "Variable " + varName + " deleted successfully.");
                } else {
                    send(channel, "Unable to delete variable " + varName + ".");
                }

            } else if (msg[1].equalsIgnoreCase("get") && isSub && msg.length > 2) {
                String varName = msg[2];
                String channelName = channel.substring(1);
                if (msg.length > 3) {
                    channelName = msg[3];
                }
                String result = JSONUtil.getVar(channelName, varName);
                if (result != null) {
                    send(channel, "Variable " + varName + "'s value is " + result);
                } else {
                    send(channel, "Variable " + varName + " doesn't exist.");
                }
            } else if (msg[1].equalsIgnoreCase("increment") && isOp && msg.length > 2) {
                int incVal = 1;
                String varName = msg[2];
                if (msg.length > 3) {
                    incVal = Integer.parseInt(msg[3]);
                }
                String result = JSONUtil.incVar(channel.substring(1), varName, incVal);
                if (result != null) {
                    send(channel, "Variable " + varName + "'s value is now " + result);
                } else {
                    send(channel, "Variable " + varName + " doesn't exist.");
                }
            } else if (msg[1].equalsIgnoreCase("decrement") && isOp && msg.length > 2) {
                int incVal = 1;
                String varName = msg[2];
                if (msg.length > 3) {
                    incVal = Integer.parseInt(msg[3]);
                }
                String result = JSONUtil.decVar(channel.substring(1), varName, incVal);
                if (result != null) {
                    send(channel, "Variable " + varName + "'s value is now " + result);
                } else {
                    send(channel, "Variable " + varName + " doesn't exist.");
                }
            }

        }

        // ********************************************************************************
        // ***************************** Moderation Commands
        // ******************************
        // ********************************************************************************

        // Moderation commands - Ops
        if (isOp && channelInfo.getShouldModerate()) {
            if (msg[0].equalsIgnoreCase("+m") && msg.length > 1) {
                int time = Integer.parseInt(msg[1]);
                sendCommand(channel, ".slow " + time);
            } else if (msg[0].equalsIgnoreCase("+m")) {
                sendCommand(channel, ".slow ");
            }
            if (msg[0].equalsIgnoreCase("-m")) {
                sendCommand(channel, ".slowoff");
            }
            if (msg[0].equalsIgnoreCase("+s")) {
                sendCommand(channel, ".subscribers");
            }
            if (msg[0].equalsIgnoreCase("-s")) {
                sendCommand(channel, ".subscribersoff");
            }
            if (msg[0].equalsIgnoreCase("+r9k")) {
                sendCommand(channel, ".r9kbeta");
            }
            if (msg[0].equalsIgnoreCase("-r9k")) {
                sendCommand(channel, ".r9kbetaoff");
            }
            if (msg.length > 0) {
                if (msg[0].equalsIgnoreCase("+b") || msg[0].equalsIgnoreCase(prefix + "ban")) {
                    sendCommand(channel, ".ban " + msg[1].toLowerCase());
                    send(channel, msg[1].toLowerCase() + " was banned.");
                    channelInfo.increasePunCount();
                }
                if (msg[0].equalsIgnoreCase("-b")) {
                    sendCommand(channel, ".unban " + msg[1].toLowerCase());
                    sendCommand(channel, ".timeout " + msg[1].toLowerCase() + " 1");
                    send(channel, msg[1].toLowerCase() + " was unbanned.");
                }
                if (msg[0].equalsIgnoreCase("+t")) {
                    if (msg.length > 2) {
                        sendCommand(channel, ".timeout " + msg[1].toLowerCase() + " " + msg[2]);
                        send(channel, msg[1].toLowerCase() + " was timed out for " + msg[2] + " seconds.");
                        channelInfo.increasePunCount();
                    } else {
                        sendCommand(channel, ".timeout " + msg[1].toLowerCase());
                        send(channel, msg[1].toLowerCase() + " was timed out.");
                        channelInfo.increasePunCount();
                    }
                }
                if (msg[0].equalsIgnoreCase("-t")) {
                    sendCommand(channel, ".timeout " + msg[1].toLowerCase() + " 1");
                    send(channel, msg[1].toLowerCase() + " is no longer timed out.");
                }
                if (msg[0].equalsIgnoreCase("+p")) {
                    sendCommand(channel, ".timeout " + msg[1].toLowerCase() + " 1");
                    send(channel, msg[1].toLowerCase() + "'s chat history was purged.");
                    channelInfo.increasePunCount();
                }
            }

        }
        // !winner

        if (msg[0].equalsIgnoreCase(prefix + "winner") && isOp) {
            log("RB: Matched command !winner");

            ArrayList<String> chatters = JSONUtil.tmiChatters(twitchName);
            if (chatters != null) {
                int randomNum = (int) (Math.random() * chatters.size());

                if (randomNum > -1) {
                    send(channel, "And the winner is... " + chatters.get(randomNum) + "!");
                }
            } else {
                ArrayList<String> chatters1 = JSONUtil.tmiChatters(twitchName);
                if (chatters1 != null) {
                    int randomNum = (int) (Math.random() * chatters1.size());

                    if (randomNum > -1) {
                        send(channel, "And the winner is... " + chatters1.get(randomNum) + "!");
                    }
                } else {
                    ArrayList<String> chatters2 = JSONUtil.tmiChatters(twitchName);
                    if (chatters2 != null) {
                        int randomNum = (int) (Math.random() * chatters2.size());

                        if (randomNum > -1) {
                            send(channel, "And the winner is... " + chatters2.get(randomNum) + "!");
                        }
                    } else
                        send(channel, "Error accessing API after 3 tries.");
                }
            }

        }

        // !whatshouldiplay
        if (msg[0].equalsIgnoreCase(prefix + "whatshouldiplay") && isOwner) {
            String result = JSONUtil.whatShouldIPlay(channelInfo.getSteam());
            send(channel, "You could always play: " + result);
        }
        // !race
        if (msg[0].equalsIgnoreCase(prefix + "race")) {
            String result = JSONUtil.getRace(channel.substring(1));
            if (result != null)
                send(channel, "You can find the race " + channel.substring(1) + " is currently in at " + result);
            else
                send(channel, channel.substring(1) + " is not currently in a race.");
        }

        if (msg[0].equalsIgnoreCase(prefix + "urban") && isRegular) {
            if (msg.length > 1) {
                log("RB: Matched command !urban");
                String fused = fuseArray(msg, 1);
                fused = fused.replaceAll(" ", "+");
                if (channelInfo.getUrban()) {
                    String result = JSONUtil.defineUrban(fused);
                    if (result.length() > 140)
                        result = result.substring(0, 140);
                    send(channel, "\"" + result + "\"");
                }

            }
        }
        // !disconnect
        if (msg[0].equalsIgnoreCase(prefix + "disconnect") && isAdmin) {
            this.disconnect();
        }
        // !clear - Ops
        if (msg[0].equalsIgnoreCase(prefix + "clear") && isOp) {
            log("RB: Matched command !clear");
            sendCommand(channel, ".clear");
            return;
        }

        // Filters
        if (msg[0].equalsIgnoreCase(prefix + "filter") && isOp) {
            if (msg.length < 2) {
                send(channel,
                        "Syntax: !filter <option> [sub options]. Options: on/off, status, me, enablewarnings, timeoutduration, displaywarnings, messagelength, links, pd, banphrase, caps, emotes, and symbols.");
                return;
            }

            // Shift down a notch
            String[] newMsg = new String[msg.length - 1];
            for (int i = 1; i < msg.length; i++) {
                newMsg[i - 1] = msg[i];
            }
            msg = newMsg;

            // Global disable
            if (msg[0].equalsIgnoreCase("on")) {
                channelInfo.setFiltersFeature(true);
                send(channel, "Feature: Filters is on");
                return;
            } else if (msg[0].equalsIgnoreCase("off")) {
                channelInfo.setFiltersFeature(false);
                send(channel, "Feature: Filters is off");
                return;
            }

            if (msg[0].equalsIgnoreCase("status")) {
                send(channel, "Global: " + channelInfo.useFilters);
                send(channel, "Enable warnings: " + channelInfo.getEnableWarnings());
                send(channel, "Timeout duration: " + channelInfo.getTimeoutDuration());
                send(channel, "Display warnings: " + channelInfo.checkSignKicks());
                send(channel, "Max message length: " + channelInfo.getFilterMax());
                send(channel, "Me: " + channelInfo.getFilterMe());
                send(channel, "Links: " + channelInfo.getFilterLinks());
                send(channel, "Banned phrases: " + channelInfo.getFilterOffensive() + " ~ severity="
                        + channelInfo.config.get("banPhraseSeverity"));
                send(channel,
                        "Caps: " + channelInfo.getFilterCaps() + " ~ percent=" + channelInfo.getfilterCapsPercent()
                                + ", minchars=" + channelInfo.getfilterCapsMinCharacters() + ", mincaps="
                                + channelInfo.getfilterCapsMinCapitals());
                send(channel, "Emotes: " + channelInfo.getFilterEmotes() + " ~ max="
                        + channelInfo.getFilterEmotesMax() + ", single=" + channelInfo.getFilterEmotesSingle());
                send(channel, "Symbols: " + channelInfo.getFilterSymbols() + " ~ percent="
                        + channelInfo.getFilterSymbolsPercent() + ", min=" + channelInfo.getFilterSymbolsMin());
            }

            if (msg[0].equalsIgnoreCase("me") && msg.length == 2) {
                if (msg[1].equalsIgnoreCase("on")) {
                    channelInfo.setFilterMe(true);
                    send(channel, "Feature: /me filter is on");
                } else if (msg[1].equalsIgnoreCase("off")) {
                    channelInfo.setFilterMe(false);
                    send(channel, "Feature: /me filter is off");
                }
                return;
            }

            if (msg[0].equalsIgnoreCase("enablewarnings") && msg.length == 2) {
                if (msg[1].equalsIgnoreCase("on")) {
                    channelInfo.setEnableWarnings(true);
                    send(channel, "Feature: Timeout warnings are on");
                } else if (msg[1].equalsIgnoreCase("off")) {
                    channelInfo.setEnableWarnings(false);
                    send(channel, "Feature: Timeout warnings are off");
                }
            }

            if (msg[0].equalsIgnoreCase("timeoutduration") && msg.length == 2) {
                if (Main.isInteger(msg[1])) {
                    int duration = Integer.parseInt(msg[1]);
                    channelInfo.setTimeoutDuration(duration);
                    send(channel, "Timeout duration is " + channelInfo.getTimeoutDuration());
                } else {
                    send(channel, "You must specify an integer for the duration");
                }
            }

            if (msg[0].equalsIgnoreCase("displaywarnings") && msg.length == 2) {
                if (msg[1].equalsIgnoreCase("on")) {
                    channelInfo.setSignKicks(true);
                    send(channel, "Feature: Display warnings is on");
                } else if (msg[1].equalsIgnoreCase("off")) {
                    channelInfo.setSignKicks(false);
                    send(channel, "Feature: Display warnings is off");
                }
            }

            if (msg[0].equalsIgnoreCase("messagelength") && msg.length == 2) {
                if (Main.isInteger(msg[1])) {
                    channelInfo.setFilterMax(Integer.parseInt(msg[1]));
                    send(channel, "Max message length set to " + channelInfo.getFilterMax());
                } else {
                    send(channel, "Must be an integer.");
                }
            }

            // !links - Owner
            if (msg[0].equalsIgnoreCase("links")) {
                log("RB: Matched command !links");
                if (msg.length == 1) {
                    send(channel, "Syntax: \"!links on/off\"");
                } else if (msg.length == 2) {
                    if (msg[1].equalsIgnoreCase("on")) {
                        channelInfo.setFilterLinks(true);
                        send(channel, "Link filter: " + channelInfo.getFilterLinks());
                    } else if (msg[1].equalsIgnoreCase("off")) {
                        channelInfo.setFilterLinks(false);
                        send(channel, "Link filter: " + channelInfo.getFilterLinks());
                    }
                }
                return;
            }

            // !pd - Owner
            if (msg[0].equalsIgnoreCase("pd")) {
                log("RB: Matched command !pd");
                if (msg.length == 1) {
                    send(channel, "Syntax: \"!pd add/delete [domain]\" and \"!pd list\"");
                } else if (msg.length > 2) {
                    if (msg[1].equalsIgnoreCase("add")) {
                        if (channelInfo.isDomainPermitted(msg[2])) {
                            send(channel, "Domain already exists. " + "(" + msg[2] + ")");
                        } else {
                            channelInfo.addPermittedDomain(msg[2]);
                            send(channel, "Domain added. " + "(" + msg[2] + ")");
                        }
                    } else if (msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove")) {
                        if (channelInfo.isDomainPermitted(msg[2])) {
                            channelInfo.removePermittedDomain(msg[2]);
                            send(channel, "Domain removed. " + "(" + msg[2] + ")");
                        } else {
                            send(channel, "Domain does not exist. " + "(" + msg[2] + ")");
                        }
                    }
                } else if (msg.length > 1 && msg[1].equalsIgnoreCase("list") && isOp) {
                    String tempList = "Permitted domains: ";
                    for (String s : channelInfo.getpermittedDomains()) {
                        tempList += s + ", ";
                    }
                    send(channel, tempList);
                }
                return;
            }

            // !banphrase - Owner
            if (msg[0].equalsIgnoreCase("banphrase")) {
                log("RB: Matched command !banphrase");
                if (isOwner)
                    log("RB: Is owner");
                if (msg.length == 1) {
                    send(channel,
                            "Syntax: \"!banphrase on/off\", \"!banphrase add/delete [string to purge]\", \"!banphrase list\"");
                } else if (msg.length > 1) {
                    if (msg[1].equalsIgnoreCase("on")) {
                        channelInfo.setFilterOffensive(true);
                        send(channel, "Ban phrase filter is on");
                    } else if (msg[1].equalsIgnoreCase("off")) {
                        channelInfo.setFilterOffensive(false);
                        send(channel, "Ban phrase filter is off");
                    } else if (msg[1].equalsIgnoreCase("clear")) {
                        channelInfo.clearBannedPhrases();
                        send(channel, "Banned phrases cleared.");
                    } else if (msg[1].equalsIgnoreCase("list")) {
                        String tempList = "Banned phrases words: ";
                        for (String s : channelInfo.getOffensive()) {
                            tempList += s + ", ";
                        }
                        send(channel, tempList);
                    } else if (msg[1].equalsIgnoreCase("add") && msg.length > 2) {
                        String phrase = fuseArray(msg, 2);
                        if (phrase.contains(",,")) {
                            send(channel, "Cannot contain double commas (,,)");
                        } else if (channelInfo.isBannedPhrase(fuseArray(msg, 2))) {
                            send(channel, "Word already exists. " + "(" + phrase + ")");
                        } else {
                            if (phrase.startsWith("REGEX:") && !isOwner) {
                                send(channel, "You must have Admin status to add regex phrases.");
                                return;
                            }
                            channelInfo.addOffensive(phrase);
                            send(channel, "Word added. " + "(" + phrase + ")");
                        }

                        // } else if (msg[1].equalsIgnoreCase("severity")) {
                        // if (msg.length > 2 && Main.isInteger(msg[2])) {
                        // int severity = Integer.parseInt(msg[2]);
                        // channelInfo.config.put("banPhraseSeverity",
                        // severity);
                        //
                        // send(channel,
                        // "Severity set to "
                        // + channelInfo.config
                        // .get("banPhraseSeverity"));
                        // } else {
                        // send(channel,
                        // "Severity is "
                        // + channelInfo.config
                        // .get("banPhraseSeverity"));
                        // }
                    } else if (msg[1].equalsIgnoreCase("delete")
                            || msg[1].equalsIgnoreCase("remove") && msg.length > 2) {
                        String phrase = fuseArray(msg, 2);
                        channelInfo.removeOffensive(phrase);
                        send(channel, "Word removed. " + "(" + phrase + ")");
                    }
                }
                return;
            }

            // !caps - Owner
            if (msg[0].equalsIgnoreCase("caps")) {
                log("RB: Matched command !caps");
                if (msg.length == 1) {
                    send(channel,
                            "Syntax: \"!caps on/off\", \"!caps percent/minchars/mincaps [value]\", \"!caps status\"");
                } else if (msg.length > 1) {
                    if (msg[1].equalsIgnoreCase("on")) {
                        channelInfo.setFilterCaps(true);
                        send(channel, "Caps filter: " + channelInfo.getFilterCaps());
                    } else if (msg[1].equalsIgnoreCase("off")) {
                        channelInfo.setFilterCaps(false);
                        send(channel, "Caps filter: " + channelInfo.getFilterCaps());
                    } else if (msg[1].equalsIgnoreCase("percent")) {
                        if (msg.length > 2) {
                            channelInfo.setfilterCapsPercent(Integer.parseInt(msg[2]));
                            send(channel, "Caps filter percent: " + channelInfo.getfilterCapsPercent());
                        }
                    } else if (msg[1].equalsIgnoreCase("minchars")) {
                        if (msg.length > 2 && Main.isInteger(msg[2])) {
                            channelInfo.setfilterCapsMinCharacters(Integer.parseInt(msg[2]));
                            send(channel,
                                    "Caps filter min characters: " + channelInfo.getfilterCapsMinCharacters());
                        }
                    } else if (msg[1].equalsIgnoreCase("mincaps")) {
                        if (msg.length > 2 && Main.isInteger(msg[2])) {
                            channelInfo.setfilterCapsMinCapitals(Integer.parseInt(msg[2]));
                            send(channel, "Caps filter min caps: " + channelInfo.getfilterCapsMinCapitals());
                        }
                    } else if (msg[1].equalsIgnoreCase("status")) {
                        send(channel,
                                "Caps filter=" + channelInfo.getFilterCaps() + ", percent="
                                        + channelInfo.getfilterCapsPercent() + ", minchars="
                                        + channelInfo.getfilterCapsMinCharacters() + ", mincaps="
                                        + channelInfo.getfilterCapsMinCapitals());
                    }
                }
                return;
            }

            // !emotes - Owner
            if (msg[0].equalsIgnoreCase("emotes")) {
                log("RB: Matched command !emotes");
                if (msg.length == 1) {
                    send(channel, "Syntax: \"!emotes on/off\", \"!emotes max [value]\", \"!emotes single on/off\"");
                } else if (msg.length > 1) {
                    if (msg[1].equalsIgnoreCase("on")) {
                        channelInfo.setFilterEmotes(true);
                        send(channel, "Emotes filter: " + channelInfo.getFilterEmotes());
                    } else if (msg[1].equalsIgnoreCase("off")) {
                        channelInfo.setFilterEmotes(false);
                        send(channel, "Emotes filter: " + channelInfo.getFilterEmotes());
                    } else if (msg[1].equalsIgnoreCase("max")) {
                        if (msg.length > 2 && Main.isInteger(msg[2])) {
                            channelInfo.setFilterEmotesMax(Integer.parseInt(msg[2]));
                            send(channel, "Emotes filter max: " + channelInfo.getFilterEmotesMax());
                        }
                    } else if (msg[1].equalsIgnoreCase("status")) {
                        send(channel,
                                "Emotes filter=" + channelInfo.getFilterEmotes() + ", max="
                                        + channelInfo.getFilterEmotesMax() + ", single="
                                        + channelInfo.getFilterEmotesSingle());
                    } else if (msg[1].equalsIgnoreCase("single") && msg.length > 2) {
                        if (msg[2].equalsIgnoreCase("on")) {
                            channelInfo.setFilterEmotesSingle(true);
                            send(channel, "Single Emote filter: " + channelInfo.getFilterEmotesSingle());
                        } else if (msg[2].equalsIgnoreCase("off")) {
                            channelInfo.setFilterEmotesSingle(false);
                            send(channel, "Single Emote filter: " + channelInfo.getFilterEmotesSingle());
                        }
                    }
                }
                return;
            }

            // !symbols - Owner
            if (msg[0].equalsIgnoreCase("symbols")) {
                log("RB: Matched command !symbols");
                if (msg.length == 1) {
                    send(channel,
                            "Syntax: \"!symbols on/off\", \"!symbols percent/min [value]\", \"!symbols status\"");
                } else if (msg.length > 1) {
                    if (msg[1].equalsIgnoreCase("on")) {
                        channelInfo.setFilterSymbols(true);
                        send(channel, "Symbols filter: " + channelInfo.getFilterSymbols());
                    } else if (msg[1].equalsIgnoreCase("off")) {
                        channelInfo.setFilterSymbols(false);
                        send(channel, "Symbols filter: " + channelInfo.getFilterSymbols());
                    } else if (msg[1].equalsIgnoreCase("percent")) {
                        if (msg.length > 2 && Main.isInteger(msg[2])) {
                            channelInfo.setFilterSymbolsPercent(Integer.parseInt(msg[2]));
                            send(channel, "Symbols filter percent: " + channelInfo.getFilterSymbolsPercent());
                        }
                    } else if (msg[1].equalsIgnoreCase("min")) {
                        if (msg.length > 2 && Main.isInteger(msg[2])) {
                            channelInfo.setFilterSymbolsMin(Integer.parseInt(msg[2]));
                            send(channel, "Symbols filter min symbols: " + channelInfo.getFilterSymbolsMin());
                        }
                    } else if (msg[1].equalsIgnoreCase("status")) {
                        send(channel,
                                "Symbols filter=" + channelInfo.getFilterSymbols() + ", percent="
                                        + channelInfo.getFilterSymbolsPercent() + ", min="
                                        + channelInfo.getFilterSymbolsMin());
                    }
                }
                return;
            }

            return;
        }
        // binding of isaac stuff
        if (msg[0].equalsIgnoreCase(prefix + "boi") && isSub) {
            if (msg.length > 1) {
                if (msg[1].equalsIgnoreCase("wiki")) {
                    if (msg.length > 2) {
                        String searchTerms = fuseArray(msg, 2);
                        String itemDesc = JSONUtil.BOIItemInfo(searchTerms);

                        send(channel, itemDesc);

                    } else
                        send(channel, "Usage is " + prefix + "boi wiki <item name>");
                }

                if (msg[1].equalsIgnoreCase("seed") && isSub) {
                    String seed = JSONUtil.BOISeed(channel);
                    if (seed != null) {
                        send(channel, twitchName + "'s current BOI:R seed is: " + seed);
                    } else
                        send(channel, twitchName + "'s BOI:R build hasn't been created yet.");

                }
                if (msg[1].equalsIgnoreCase("floor") && isSub) {
                    String floor = JSONUtil.BOIFloor(channel);
                    if (floor != null) {
                        send(channel, twitchName + " is currently on: " + floor);
                    } else
                        send(channel, twitchName + "'s BOI:R build hasn't been created yet.");

                }
                if (msg[1].equalsIgnoreCase("items") && isSub) {

                    ArrayList<String> items = JSONUtil.BOIItems(channel);
                    if (items != null) {
                        String itemString = "";
                        for (String s : items) {
                            itemString += s + ", ";

                        }
                        itemString = itemString.trim();
                        if (itemString.length() > 240) {
                            int end = itemString.indexOf(",", 220);
                            itemString = itemString.substring(0, end);
                        } else
                            itemString = itemString.substring(0, itemString.length() - 1);
                        send(channel, twitchName + "'s current items are: " + itemString);
                    } else
                        send(channel, twitchName + "'s BOI:R build hasn't been created yet.");

                }
                if (msg[1].equalsIgnoreCase("transformations") && isSub) {
                    Long gProgress = JSONUtil.BOIGuppyProgress(channel);
                    if (gProgress != null) {
                        Long fProgress = JSONUtil.BOIFlyProgress(channel);

                        ArrayList<String> gitems = JSONUtil.BOIGuppyItems(channel);
                        String gitemString = "";
                        for (String s : gitems) {
                            gitemString += s + ", ";

                        }
                        gitemString = gitemString.trim();
                        gitemString = gitemString.substring(0, gitemString.length() - 1);

                        ArrayList<String> items = JSONUtil.BOIFlyItems(channel);
                        String itemString = "";
                        for (String s : items) {
                            itemString += s + ", ";

                        }
                        itemString = itemString.trim();
                        itemString = itemString.substring(0, itemString.length() - 1);

                        if (gProgress > 2) {
                            send(channel, twitchName + " is Guppy, with " + gitemString);
                        } else if (fProgress > 2) {
                            send(channel, twitchName + " is the Lord of the Flies, with " + itemString);
                        } else if (gProgress == 0 && fProgress == 0) {
                            send(channel, twitchName + " has no transformation items.");
                        } else if (gProgress == 0 && fProgress > 0) {
                            send(channel, twitchName + " has " + fProgress
                                    + "/3 items required for Lord of the Flies, with " + itemString);
                        } else if (gProgress > 0 && fProgress == 0) {
                            send(channel, twitchName + " has " + gProgress
                                    + "/3 items required for Guppy form, with " + gitemString);
                        } else
                            send(channel,
                                    twitchName + " has " + gProgress + "/3 items required for Guppy form, with "
                                            + gitemString + " and " + fProgress
                                            + "/3 items required for Lord of the Flies, with " + itemString);
                    } else
                        send(channel, twitchName + "' doesn't have any transformation items yet.");
                }
            } else
                send(channel, "You can see all of " + twitchName + "'s BOI:R info at "
                        + JSONUtil.shortenUrlTinyURL("http://coebot.tv/c/" + twitchName + "/#boir"));
        }
        // !sendupdate
        if (msg[0].equalsIgnoreCase(prefix + "sendUpdate") && isAdmin) {
            channelInfo.updateSite();
            send(channel, "Channel config has been manually pushed to coebot.tv/c/" + twitchName);
        }

        // coebot ignores

        if (msg[0].equalsIgnoreCase(prefix + "ignore") && isOp) {
            if (msg.length == 2) {
                if (msg[1].equalsIgnoreCase("list")) {
                    String tempList = "Ignored users: ";
                    ArrayList<String> ignored = channelInfo.getIgnoredUsers();
                    java.util.Collections.sort(ignored);
                    for (int i = 0; i < ignored.size(); i++) {
                        if (i == ignored.size() - 1) {
                            tempList += (ignored.get(i));
                        } else
                            tempList += (ignored.get(i) + ", ");
                    }
                    send(channel, tempList);
                }
            } else if (msg.length > 2) {
                if (msg[1].equalsIgnoreCase("add")) {
                    if (msg[2].equalsIgnoreCase(channel.substring(1))) {
                        send(channel, "You can't add the channel owner to the ignore list.");
                        return;
                    }
                    if (BotManager.getInstance().isAdmin(msg[2].toLowerCase())) {
                        send(channel, "You can't add bot admins to the ignore list.");
                        return;
                    }
                    if (channelInfo.addIgnoredUser(msg[2].toLowerCase())) {
                        send(channel,
                                msg[2].toLowerCase() + " has been added to the ignore list for this channel.");

                    } else
                        send(channel, msg[2].toLowerCase() + " is already on the ignore list for this channel.");

                } else if (msg[1].equalsIgnoreCase("remove") || msg[1].equalsIgnoreCase("delete")) {
                    if (channelInfo.removeIgnoredUser(msg[2].toLowerCase())) {
                        send(channel, msg[2].toLowerCase()
                                + " was successfully removed from the ignore list for this channel.");
                    } else
                        send(channel, msg[2].toLowerCase() + " was not on this channel's ignore list.");
                }
            }
        }

        // !permit - Allows users to post 1 link
        if ((msg[0].equalsIgnoreCase(prefix + "permit") || msg[0].equalsIgnoreCase(prefix + "allow"))
                && channelInfo.getFilterLinks() && channelInfo.useFilters && isOp) {
            log("RB: Matched command !permit");
            if (msg.length == 1) {
                send(channel, "Syntax: \"!permit [username]\"");
            } else if (msg.length > 1) {
                if (!channelInfo.isRegular(msg[1])) {
                    channelInfo.permitUser(msg[1]);
                    send(channel, msg[1] + " may now post 1 link.");
                } else {
                    send(channel, msg[1] + " is a regular and does not need to be permitted.");
                }
            }
            return;
        }

        // !regular - Owner
        if ((msg[0].equalsIgnoreCase(prefix + "regular") || msg[0].equalsIgnoreCase(prefix + "regulars")) && isOp) {
            log("RB: Matched command !regular");
            if (msg.length < 2) {
                send(channel, "Syntax: \"!regular add/delete [name]\", \"!regular list\"");
            } else if (msg.length > 2) {
                if (msg[1].equalsIgnoreCase("add")) {
                    if (channelInfo.isRegular(msg[2])) {
                        send(channel, "User already exists." + "(" + msg[2] + ")");
                    } else {
                        channelInfo.addRegular(msg[2]);
                        send(channel, "User added. " + "(" + msg[2] + ")");
                    }
                } else if (msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove")) {
                    if (channelInfo.isRegular(msg[2])) {
                        channelInfo.removeRegular(msg[2]);
                        send(channel, "User removed." + "(" + msg[2] + ")");
                    } else {
                        send(channel, "User does not exist. " + "(" + msg[2] + ")");
                    }
                }
            } else if (msg.length > 1 && msg[1].equalsIgnoreCase("list") && isOp) {
                String tempList = "Regulars: ";
                ArrayList<String> arrRegs = new ArrayList<String>();
                for (String s : channelInfo.getRegulars()) {
                    arrRegs.add(s);
                }
                java.util.Collections.sort(arrRegs);
                for (int i = 0; i < arrRegs.size(); i++) {
                    if (i == arrRegs.size() - 1) {
                        tempList += (arrRegs.get(i));
                    } else
                        tempList += (arrRegs.get(i) + ", ");
                }
                send(channel, tempList);
            }
            return;
        }

        // !mod - Owner
        if (msg[0].equalsIgnoreCase(prefix + "mod") && isOwner) {
            log("RB: Matched command !mod");
            if (msg.length < 2) {
                send(channel, "Syntax: \"!mod add/delete [name]\", \"!mod list\"");
            }
            if (msg.length > 2) {
                if (msg[1].equalsIgnoreCase("add")) {
                    if (channelInfo.isModerator(msg[2])) {
                        send(channel, "User already exists. " + "(" + msg[2] + ")");
                    } else {
                        channelInfo.addModerator(msg[2]);
                        send(channel, "User added. " + "(" + msg[2] + ")");
                    }
                } else if (msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove")) {
                    if (channelInfo.isModerator(msg[2])) {
                        channelInfo.removeModerator(msg[2]);
                        send(channel, "User removed. " + "(" + msg[2] + ")");
                    } else {
                        send(channel, "User does not exist. " + "(" + msg[2] + ")");
                    }
                }
            } else if (msg.length > 1 && msg[1].equalsIgnoreCase("list") && isOwner) {
                String tempList = "Moderators: ";
                ArrayList<String> arrRegs = new ArrayList<String>();
                for (String s : channelInfo.getModerators()) {
                    arrRegs.add(s);
                }
                java.util.Collections.sort(arrRegs);
                for (int i = 0; i < arrRegs.size(); i++) {
                    if (i == arrRegs.size() - 1) {
                        tempList += (arrRegs.get(i));
                    } else
                        tempList += (arrRegs.get(i) + ", ");
                }
                send(channel, tempList);
            }
            return;
        }

        // !owner - Owner
        if (msg[0].equalsIgnoreCase(prefix + "owner") && isOwner) {
            log("RB: Matched command !owner");
            if (msg.length < 2) {
                send(channel, "Syntax: \"!owner add/delete [name]\", \"!owner list\"");
            }
            if (msg.length > 2) {
                if (msg[1].equalsIgnoreCase("add")) {
                    if (channelInfo.isOwner(msg[2])) {
                        send(channel, "User already exists. " + "(" + msg[2] + ")");
                    } else {
                        channelInfo.addOwner(msg[2]);
                        send(channel, "User added. " + "(" + msg[2] + ")");
                    }
                } else if (msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove")) {
                    if (channelInfo.isOwner(msg[2])) {
                        channelInfo.removeOwner(msg[2]);
                        send(channel, "User removed. " + "(" + msg[2] + ")");
                    } else {
                        send(channel, "User does not exist. " + "(" + msg[2] + ")");
                    }
                }
            } else if (msg.length > 1 && msg[1].equalsIgnoreCase("list") && isOwner) {
                String tempList = "Owners: ";
                ArrayList<String> arrRegs = new ArrayList<String>();
                for (String s : channelInfo.getOwners()) {
                    arrRegs.add(s);
                }
                java.util.Collections.sort(arrRegs);
                for (int i = 0; i < arrRegs.size(); i++) {
                    if (i == arrRegs.size() - 1) {
                        tempList += (arrRegs.get(i));
                    } else
                        tempList += (arrRegs.get(i) + ", ");
                }
                send(channel, tempList);
            }
            return;
        }

        // xboxGame
        if (msg[0].equalsIgnoreCase(prefix + "xboxgame") && isOwner) {
            String gamerTag = channelInfo.getGamerTag();
            String lastGame = JSONUtil.xboxLastGame(gamerTag);
            try {
                if (lastGame.equals("(unavailable)")) {
                    channelInfo.updateGame("");
                    send(channel, "Xbox Live game unavailable, changed game to \"Not Playing\" status.");
                } else {
                    channelInfo.updateGame(lastGame);
                    send(channel, "Xbox Live game updated to the game you last played on Xbox Live, " + lastGame);
                }
            } catch (Exception ex) {
                send(channel, "Error updating game. Did you add me as an editor?");
            }

        }
        // custom commands from another channel
        if (msg[0].startsWith(prefix + "#") && isOwner && msg[0].contains("/")) {

            String otherChannel = msg[0].substring(1, msg[0].indexOf("/"));
            Channel newChannelInfo = getChannelObject(otherChannel);

            logMain(otherChannel);
            String command = msg[0].substring(msg[0].indexOf("/") + 1);
            logMain(command);
            String value = newChannelInfo.getCommand(command);
            logMain(value);
            if (value != null) {
                log("RB: Matched command " + command);

                if (value.contains("(_PURGE_)")) {
                    value = value.replace("(_PURGE_)", msg[1].toLowerCase());
                    sendCommand(channel, ".timeout " + msg[1].toLowerCase() + " 1");
                } else if (value.contains("(_TIMEOUT_)")) {
                    value = value.replace("(_TIMEOUT_)", msg[1].toLowerCase());
                    sendCommand(channel, ".timeout " + msg[1].toLowerCase());

                } else if (value.contains("(_BAN_)")) {
                    value = value.replace("(_BAN_)", msg[1].toLowerCase());
                    sendCommand(channel, ".ban " + msg[1].toLowerCase());
                }

                String msgstr = fuseArray(msg, 0);
                if (value.contains("(_PARAMETER_)") && !msgstr.contains("(_") && !msgstr.contains("_)")) {

                    String[] parts = fuseArray(msg, 1).split(";");
                    if (parts.length > 1) {
                        for (String s : parts) {
                            value = value.replaceFirst("\\(_PARAMETER_\\)", s.trim());
                        }
                    } else
                        value = value.replace("(_PARAMETER_)", parts[0]);

                }
                if (value.contains("(_PARAMETER_CAPS_)") && !msgstr.contains("(_") && !msgstr.contains("_)")) {

                    String[] parts = fuseArray(msg, 1).split(";");
                    if (parts.length > 1) {
                        for (String s : parts) {
                            value = value.replaceFirst("\\(_PARAMETER_CAPS_\\)", s.trim());
                        }
                    } else
                        value = value.replace("(_PARAMETER_CAPS_)", parts[0].toUpperCase());

                }

                send(channel, sender, value);

            }
        }

        // !set - Owner
        if (msg[0].equalsIgnoreCase(prefix + "set") && isOp) {
            log("RB: Matched command !set");
            if (msg.length == 1) {
                send(channel,
                        "Syntax: \"!set [option] [value]\". Options: topic, filters, throw, signedkicks, joinsparts, lastfm, steam, mode, chatlogging, maxlength");
            } else if (msg[1].equalsIgnoreCase("topic")) {
                if (msg[2].equalsIgnoreCase("on")) {
                    channelInfo.setTopicFeature(true);
                    send(channel, "Feature: Topic is on");
                } else if (msg[2].equalsIgnoreCase("off")) {
                    channelInfo.setTopicFeature(false);
                    send(channel, "Feature: Topic is off");
                }

            } else if (msg[1].equalsIgnoreCase("parseYoutube")) {
                if (msg[2].equalsIgnoreCase("on") || msg[2].equalsIgnoreCase("enabled")) {
                    channelInfo.setParseYoutube(true);
                    send(channel, "Youtube video title parsing is now enabled.");
                } else if (msg[2].equalsIgnoreCase("off") || msg[2].equalsIgnoreCase("disabled")) {
                    channelInfo.setParseYoutube(false);
                    send(channel, "Youtube video title parsing is now disabled.");
                }
            } else if (msg[1].equalsIgnoreCase("shouldModerate")) {
                if (msg[2].equalsIgnoreCase("on") || msg[2].equalsIgnoreCase("enabled")) {
                    channelInfo.setShouldModerate(true);
                    send(channel, this.getNick() + " will attempt to moderate in this channel.");
                } else if (msg[2].equalsIgnoreCase("off") || msg[2].equalsIgnoreCase("disabled")) {
                    channelInfo.setShouldModerate(false);
                    send(channel, this.getNick() + " will not attempt to moderate in this channel.");
                }
            } else if (msg[1].equalsIgnoreCase("roll")) {
                if (msg[2].equalsIgnoreCase("timeoutoncriticalfail")) {
                    if (msg[3].equalsIgnoreCase("on") || msg[3].equalsIgnoreCase("enabled")) {
                        channelInfo.setRollTimeout(true);
                        send(channel, "Timeout on critical fail has been enabled.");
                    } else if (msg[3].equalsIgnoreCase("off") || msg[3].equalsIgnoreCase("disabled")) {
                        channelInfo.setRollTimeout(false);
                        send(channel, "Timeout on critical fail has been disabled.");
                    }
                } else if (msg[2].equalsIgnoreCase("default")) {
                    if (isInteger(msg[3])) {
                        channelInfo.setRollDefault(Integer.parseInt(msg[3]));
                        send(channel, "Default maximum roll has been set to: " + msg[3]);
                    } else {
                        send(channel, "The default roll must be an integer.");
                    }
                } else if (msg[2].equalsIgnoreCase("cooldown")) {
                    if (isInteger(msg[3])) {
                        channelInfo.setRollCooldown(Integer.parseInt(msg[3]));
                        send(channel, "The cooldown between rolls has been set to: " + msg[3] + " seconds.");
                    } else {
                        send(channel, "The roll cooldown must be an integer.");
                    }
                } else if (msg[2].equalsIgnoreCase("userlevel")) {
                    if (msg[3].equalsIgnoreCase("everyone") || msg[3].equalsIgnoreCase("all")) {
                        channelInfo.setRollLevel("everyone");
                        send(channel, "The user level required for " + prefix + "roll has been set to: everyone");
                    } else if (msg[3].equalsIgnoreCase("regulars") || msg[3].equalsIgnoreCase("regs")) {
                        channelInfo.setRollLevel("regulars");
                        send(channel, "The user level required for " + prefix + "roll has been set to: regulars");
                    } else if (msg[3].equalsIgnoreCase("mods") || msg[3].equalsIgnoreCase("moderators")) {
                        channelInfo.setRollLevel("moderators");
                        send(channel, "The user level required for " + prefix + "roll has been set to: moderators");
                    }

                }

            } else if (msg[1].equalsIgnoreCase("songrequest") && isOwner) {

                if (msg.length > 2) {
                    boolean enabled = false;
                    if (msg[2].equalsIgnoreCase("on") || msg[2].equalsIgnoreCase("enabled")) {
                        enabled = true;
                        send(channel, prefix + "songrequest <song link> is now enabled.");
                    } else if (msg[2].equalsIgnoreCase("off") || msg[2].equalsIgnoreCase("disabled")) {
                        send(channel, prefix + "songrequest <song link> is now disabled.");
                    }
                    channelInfo.setSongRequest(enabled);

                }
            } else if (msg[1].equalsIgnoreCase("extralifeid") && isOwner) {
                if (msg.length > 2) {
                    if (isInteger(msg[2])) {
                        channelInfo.setExtraLifeID(msg[2]);
                        send(channel, "Your Extra Life ID has been set to: " + msg[2]);
                    } else
                        send(channel, "Your Extra Life ID must be a number.");
                }
            }
            // enable/disable urban
            else if (msg[1].equalsIgnoreCase("urban") && isOwner) {
                if (msg.length > 2) {
                    boolean enabled = false;
                    if (msg[2].equalsIgnoreCase("on") || msg[2].equalsIgnoreCase("enabled")) {
                        enabled = true;
                        send(channel, "The use of " + prefix + "urban is now enabled.");
                    } else if (msg[2].equalsIgnoreCase("off") || msg[2].equalsIgnoreCase("disabled")) {
                        send(channel, "The use of " + prefix + "urban is now disabled.");
                    }
                    channelInfo.setUrban(enabled);

                }
            }
            // setgamertag
            else if (msg[1].equalsIgnoreCase("gamertag")) {
                if (msg.length > 2) {
                    String gamerTag = this.fuseArray(msg, 2).trim();
                    channelInfo.setGamertag(gamerTag);
                    send(channel, "Xbox Live Gamertag has been set to " + gamerTag);
                }
            } // setbullet
            else if (msg[1].equalsIgnoreCase("bullet") && isOwner) {
                if (msg.length > 2) {
                    if (!msg[2].startsWith("/") && !msg[2].startsWith(".") && !msg[2].equalsIgnoreCase("")
                            && msg[2].length() < 30) {

                        bullet[0] = msg[2];

                        channelInfo.setBullet(msg[2]);
                        send(channel, "Bullet is now set to \"" + bullet[0] + "\"");
                    } else
                        send(channel, "Bullet cannot start with \"/\" or \".\"");

                } else
                    send(channel, "Usage is " + prefix + "set bullet <new bullet>");
            } // setSubsRegsMinusLinks
            else if (msg[1].equalsIgnoreCase("subsRegsMinusLinks")) {
                if (msg.length > 2) {
                    if (msg[2].equalsIgnoreCase("on")) {
                        channelInfo.setSubsRegsMinusLinks(true);
                        send(channel,
                                "Subscribers are now considered regulars except for the ability to post links.");
                    } else if (msg[2].equalsIgnoreCase("off")) {
                        channelInfo.setSubsRegsMinusLinks(false);
                        send(channel, "Subscribers are now considered non-regulars.");
                    } else
                        send(channel, "Syntax is " + prefix + "set subsRegsMinusLinks <on/off>");
                } else
                    send(channel, "Syntax is " + prefix + "set subsRegsMinusLinks <on/off>");
            }

            // set cooldown for custom commands
            else if (msg[1].equalsIgnoreCase("cooldown")) {
                if (msg.length > 2) {
                    int newCooldown = Integer.parseInt(msg[2]);
                    channelInfo.setCooldown(newCooldown);
                    send(channel, "Cooldown for custom commands is now " + newCooldown + " seconds.");
                } else
                    send(channel, "Usage is " + prefix + "setbullet <new bullet>");
            } else if (msg[1].equalsIgnoreCase("throw")) {
                if (msg[2].equalsIgnoreCase("on")) {
                    channelInfo.setThrow(true);
                    send(channel, "Feature: !throw is on");
                } else if (msg[2].equalsIgnoreCase("off")) {
                    channelInfo.setThrow(false);
                    send(channel, "Feature: !throw is off");
                }
            } else if (msg[1].equalsIgnoreCase("lastfm")) {
                if (msg[2].equalsIgnoreCase("off")) {
                    channelInfo.setLastfm("");
                    send(channel, "Feature: Lastfm is off.");
                } else {
                    channelInfo.setLastfm(msg[2]);
                    send(channel, "Feature: Lastfm user set to " + msg[2]);
                }
            } else if (msg[1].equalsIgnoreCase("steam")) {
                if (msg[2].equalsIgnoreCase("off")) {
                    channelInfo.setSteam("");
                    send(channel, "Feature: Steam is off.");
                } else {
                    channelInfo.setSteam(msg[2]);
                    send(channel, "Feature: Steam id set to " + msg[2]);
                }
            } else if (msg[1].equalsIgnoreCase("mode")) {
                if (msg.length < 3) {
                    send(channel, "Mode set to " + channelInfo.getMode() + "");
                } else if ((msg[2].equalsIgnoreCase("0") || msg[2].equalsIgnoreCase("owner")) && isOwner) {
                    channelInfo.setMode(0);
                    send(channel, "Mode set to admin/owner only.");
                } else if (msg[2].equalsIgnoreCase("1") || msg[2].equalsIgnoreCase("mod")) {
                    channelInfo.setMode(1);
                    send(channel, "Mode set to admin/owner/mod only.");
                } else if (msg[2].equalsIgnoreCase("2") || msg[2].equalsIgnoreCase("everyone")) {
                    channelInfo.setMode(2);
                    send(channel, "Mode set to everyone.");
                } else if (msg[2].equalsIgnoreCase("3") || msg[2].equalsIgnoreCase("subs")
                        || msg[2].equalsIgnoreCase("regs")) {
                    channelInfo.setMode(3);
                    send(channel, "Mode set to regulars/subs only.");
                } else if (msg[2].equalsIgnoreCase("-1") || msg[2].equalsIgnoreCase("admin")) {
                    channelInfo.setMode(-1);
                    send(channel, "Special moderation mode activated.");
                }
            } else if (msg[1].equalsIgnoreCase("commerciallength")) {
                if (msg.length > 2) {
                    int cLength = Integer.parseInt(msg[2]);
                    if (cLength == 30 || cLength == 60 || cLength == 90 || cLength == 120 || cLength == 150
                            || cLength == 180) {
                        channelInfo.setCommercialLength(cLength);
                        send(channel,
                                "Commercial length is set to " + channelInfo.getCommercialLength() + " seconds.");
                    }
                } else {
                    send(channel, "Commercial length is " + channelInfo.getCommercialLength() + " seconds.");
                }
            } else if (msg[1].equalsIgnoreCase("tweet")) {
                if (msg.length < 3) {
                    send(channel, "ClickToTweet format: " + channelInfo.getClickToTweetFormat());
                } else {
                    String format = fuseArray(msg, 2);
                    if (!format.contains("(_TWEET_URL_)")) {
                        channelInfo.setClickToTweetFormat(format);
                        send(channel, "ClickToTweet format: " + channelInfo.getClickToTweetFormat());
                    } else {
                        send(channel, "_TWEET_URL_ is not allowed.");
                    }

                }
            } else if (msg[1].equalsIgnoreCase("prefix")) {
                if (msg.length > 2) {
                    if (msg[2].length() > 1) {
                        send(channel, "Prefix may only be 1 character.");
                    } else if (msg[2].equals("/") || msg[2].equals(".")) {
                        send(channel, "Command prefix cannot be / or .");
                    } else {
                        channelInfo.setPrefix(msg[2]);
                        send(channel, "Command prefix is " + channelInfo.getPrefix());
                    }
                } else {
                    send(channel, "Command prefix is " + channelInfo.getPrefix());
                }
            } else if (msg[1].equalsIgnoreCase("emoteset") && msg.length > 2) {
                channelInfo.setEmoteSet(msg[2]);
                send(channel, "Emote set ID set to " + channelInfo.getEmoteSet());
            } else if (msg[1].equalsIgnoreCase("subscriberregulars")) {
                if (msg[2].equalsIgnoreCase("on")) {
                    channelInfo.setSubscriberRegulars(true);
                    send(channel, "Subscribers will now be treated as regulars.");
                } else if (msg[2].equalsIgnoreCase("off")) {
                    channelInfo.setSubscriberRegulars(false);
                    send(channel, "Subscribers will no longer be treated as regulars.");
                }
            } else if (msg[1].equalsIgnoreCase("subscriberalerts")) {
                if (msg.length < 3) {
                    send(channel, "Subscriber alerts: " + channelInfo.getSubAlert());
                    send(channel, "Subscriber alert message: " + channelInfo.getSubMessage());
                } else if (msg[2].equalsIgnoreCase("on")) {
                    channelInfo.setSubAlert(true);

                    send(channel, "Subscriber alerts enabled.");
                } else if (msg[2].equalsIgnoreCase("off")) {
                    channelInfo.setSubAlert(false);

                    send(channel, "Subscriber alerts disabled.");
                } else if (msg[2].equalsIgnoreCase("message") && msg.length > 3) {
                    channelInfo.setSubMessage(fuseArray(msg, 3));

                    send(channel, "Subscriber alert message set to: " + channelInfo.getSubMessage());

                }
            } else if (msg[1].equalsIgnoreCase("resubalerts")) {
                if (msg.length < 3) {
                    send(channel, "Resub alerts: " + channelInfo.getResubAlert() + "");
                    send(channel, "Subscriber alert message: " + channelInfo.config.get("resubMessage"));
                } else if (msg[2].equalsIgnoreCase("on")) {
                    channelInfo.setResubAlert(true);

                    send(channel, "Resub alerts enabled.");
                } else if (msg[2].equalsIgnoreCase("off")) {
                    channelInfo.setResubAlert(false);

                    send(channel, "Resub alerts disabled.");
                } else if (msg[2].equalsIgnoreCase("message") && msg.length > 3) {

                    channelInfo.setResubMessage(fuseArray(msg, 3));

                    send(channel, "Resub alert message set to: " + channelInfo.getResubMessage());

                }
            }
            return;
        }
        if (msg[0].equalsIgnoreCase(prefix + "fakesub") && isAdmin) {
            onNewSubscriber(channelInfo, "fakeUser");
        }
        if (msg[0].equalsIgnoreCase(prefix + "fakeresub") && isAdmin) {

            onResubscribe(channelInfo, "fakeUser", 3);

        }
        if (msg[0].equalsIgnoreCase(prefix + "resubalerts") && isAdmin) {

            send(channel, channelInfo.getResubAlert() + "");
            send(channel, channelInfo.getResubMessage());

        }
        if (msg[0].equalsIgnoreCase(prefix + "resubalerts") && isAdmin) {

            send(channel, channelInfo.getSubAlert() + "");
            send(channel, channelInfo.getSubMessage());

        }
        // !modchan - Mod
        if (msg[0].equalsIgnoreCase(prefix + "modchan") && isOwner) {
            log("RB: Matched command !modchan");
            if (channelInfo.getMode() == 2) {
                channelInfo.setMode(1);
                send(channel, "Mode set to admin/owner/mod only.");
            } else if (channelInfo.getMode() == 1) {
                channelInfo.setMode(2);
                send(channel, "Mode set to everyone.");
            } else {
                send(channel, "Mode can only be changed by bot admin.");
            }
            return;
        }

        // !join
        if (msg[0].equalsIgnoreCase(prefix + "join") && channel.equalsIgnoreCase("#" + getNick())) {
            log("RB: Matched command !join");

            if (!BotManager.getInstance().publicJoin) {
                send(channel, "Public joining is disabled at this time.");
                return;
            }

            if (JSONUtil.krakenChannelExist(sender)) {
                send(channel, "Joining channel #" + sender + ".");
                // boolean joinStatus = BotManager.getInstance().addChannel(
                // "#" + sender, 2);

                boolean createStatus = false;
                boolean joinStatus = BotManager.getInstance().checkChannel("#" + sender);
                String created = BotManager.getInstance().coebotJoinChannel(sender, getNick());
                if (created.equalsIgnoreCase("ok")) {
                    createStatus = true;
                }

                if (!joinStatus && createStatus) {
                    BotManager.getInstance().addChannel("#" + sender, 2);
                    send(channel, "Channel #" + sender + " joined.");

                } else {
                    send(channel, "Already in channel #" + sender + " or could not join.");
                }
            } else {
                send(channel, "Unable to join " + sender
                        + ". This could be because your channel is on Justin.tv and not Twitch. If you are sure your channel is on Twitch, try again later.");
            }
            return;
        }

        if (msg[0].equalsIgnoreCase(prefix + "rejoin")) {
            log("RB: Matched command !rejoin");
            if (msg.length > 1 && isAdmin) {
                if (msg[1].contains("#")) {
                    send(channel, "Rejoining channel " + msg[1] + ".");
                    boolean joinStatus = BotManager.getInstance().rejoinChannel(msg[1]);
                    if (joinStatus) {
                        send(channel, "Channel " + msg[1] + " rejoined.");
                    } else {
                        send(channel, "Bot is not assigned to channel " + msg[1] + ".");
                    }

                } else {
                    send(channel, "Invalid channel format. Must be in format #channelname.");
                }
            } else {
                send(channel, "Rejoining channel #" + sender + ".");
                boolean joinStatus = BotManager.getInstance().rejoinChannel("#" + sender);
                if (joinStatus) {
                    send(channel, "Channel #" + sender + " rejoined.");
                } else {
                    send(channel, "Bot is not assigned to channel #" + sender + ".");
                }
            }
            return;
        }

        // ********************************************************************************
        // **************************** Administration Commands
        // ***************************
        // ********************************************************************************

        if (msg[0].equalsIgnoreCase(prefix + "admin") && isAdmin && msg.length > 1) {
            if (msg[1].equalsIgnoreCase("channels")) {
                send(channel, "Currently in " + BotManager.getInstance().channelList.size() + " channels.");
                String channelString = "";
                for (Map.Entry<String, Channel> entry : BotManager.getInstance().channelList.entrySet()) {
                    channelString += entry.getValue().getChannel() + ", ";
                }
                send(channel, "Channels: " + channelString);
                return;
            } else if (msg[1].equalsIgnoreCase("join") && msg.length > 2) {
                if (msg[2].startsWith("#")) {
                    String toJoin = msg[2];
                    int mode = 2;
                    if (msg.length > 3 && Main.isInteger(msg[3]))
                        mode = Integer.parseInt(msg[3]);
                    send(channel, "Joining channel " + toJoin + " with mode (" + mode + ").");

                    boolean createStatus = false;
                    String created = BotManager.getInstance().coebotJoinChannel(toJoin.substring(1), getNick());
                    if (created.equalsIgnoreCase("ok")) {
                        createStatus = true;
                    }
                    boolean joinStatus = BotManager.getInstance().checkChannel(toJoin);
                    if (!joinStatus && createStatus) {
                        BotManager.getInstance().addChannel(toJoin, 2);
                        send(channel, "Channel " + toJoin + " joined.");

                    } else {
                        send(channel, "Already in channel " + toJoin + " or could not join.");
                    }

                } else {
                    send(channel, "Invalid channel format. Must be in format #channelname.");
                }
                return;
            } else if (msg[1].equalsIgnoreCase("part") && msg.length > 2) {
                if (msg[2].startsWith("#")) {
                    String toPart = msg[2];
                    send(channel, "Channel " + toPart + " parting...");
                    BotManager.getInstance().removeChannel(toPart);
                    BotManager.getInstance().coebotPartChannel(toPart.substring(1), getNick());
                    send(channel, "Channel " + toPart + " parted.");
                } else {
                    send(channel, "Invalid channel format. Must be in format #channelname.");
                }
                return;
            } else if (msg[1].equalsIgnoreCase("block")) {
                if (msg[2].startsWith("#")) {
                    BotManager.getInstance().addBlockedChannel(msg[2].toLowerCase());
                    BotManager.getInstance().coebotPartChannel(msg[2].toLowerCase().substring(1), getNick());
                    BotManager.getInstance().removeChannel(msg[2].toLowerCase());
                    send(channel, msg[2].toLowerCase() + " added to the list of blocked channels.");
                } else {
                    send(channel, "Invalid channel format. Must be in format #channelname.");
                }

            } else if (msg[1].equalsIgnoreCase("unblock")) {
                if (msg[2].startsWith("#")) {
                    BotManager.getInstance().removeBlockedChannel(msg[2].toLowerCase());
                    send(channel, msg[2].toLowerCase() + " removed from the list of blocked channels.");
                } else {
                    send(channel, "Invalid channel format. Must be in format #channelname.");
                }

            } else if (msg[1].equalsIgnoreCase("reconnect")) {
                send(channel, "Reconnecting all servers.");
                BotManager.getInstance().reconnectAllBotsSoft();
                return;
            } else if (msg[1].equalsIgnoreCase("reload") && msg.length > 2) {
                if (msg[2].startsWith("#")) {
                    String toReload = msg[2];
                    send(channel, "Reloading channel " + toReload);
                    BotManager.getInstance().reloadChannel(toReload);
                    send(channel, "Channel " + toReload + " reloaded.");
                } else {
                    send(channel, "Invalid channel format. Must be in format #channelname.");
                }
                return;
            } else if (msg[1].equalsIgnoreCase("color") && msg.length > 2) {
                sendCommand(channel, ".color " + msg[2]);
                send(channel, "Color set to " + msg[2]);
                return;
            } else if (msg[1].equalsIgnoreCase("loadfilter")) {
                BotManager.getInstance().loadGlobalBannedWords();
                BotManager.getInstance().loadBanPhraseList();
                send(channel, "Global banned filter reloaded.");
                return;
            } else if (msg[1].equalsIgnoreCase("spam")) {
                if (msg.length > 3 && Main.isInteger(msg[2])) {
                    String toSpam = fuseArray(msg, 3);
                    for (int i = 0; i < Integer.parseInt(msg[2]); i++)
                        send(channel, toSpam + " " + (i + 1));
                    return;
                }
            } else if (msg[1].startsWith("#")) {
                if (msg.length > 2) {

                    onChannelMessage(channel, msg[1], sender, fuseArray(msg, 2));
                }
            } else if (msg[1].equalsIgnoreCase("trimchannels")) {
                System.out.println("Starting channel trim...");
                JSONUtil.trimChannels(10L);

            }
        }
        // ********************************************************************************
        // ***************************** Info/Catch-all Command
        // ***************************
        // ********************************************************************************

        long cooldown = channelInfo.getCooldown() * 1L;

        if (msg[0].substring(0, 1).equalsIgnoreCase(prefix)) {
            String command = msg[0].substring(1).toLowerCase();
            String value = channelInfo.getCommand(command);
            if (value != null) {
                log("RB: Matched command " + msg[0]);

                if (channelInfo.checkCommandRestriction(command, accessLevel)) {
                    long currentTime = System.currentTimeMillis();
                    if (currentTime > (lastCommand + cooldown * 1000L) || isOp) {
                        lastCommand = currentTime;

                        if (value.contains("(_PURGE_)")) {
                            value = value.replace("(_PURGE_)", msg[1].toLowerCase());
                            sendCommand(channel, ".timeout " + msg[1].toLowerCase() + " 1");
                        } else if (value.contains("(_TIMEOUT_)")) {
                            value = value.replace("(_TIMEOUT_)", msg[1].toLowerCase());
                            sendCommand(channel, ".timeout " + msg[1].toLowerCase());

                        } else if (value.contains("(_BAN_)")) {
                            value = value.replace("(_BAN_)", msg[1].toLowerCase());
                            sendCommand(channel, ".ban " + msg[1].toLowerCase());
                        }
                        if (value.contains("(_SUBMODE_ON_)")) {
                            sendCommand(channel, ".subscribers");
                            value = value.replace("(_SUBMODE_ON_)", "");
                        } else if (value.contains("(_SUBMODE_OFF_)")) {
                            sendCommand(channel, ".subscribersoff");
                            value = value.replace("(_SUBMODE_OFF_)", "");
                        }
                        String msgstr = fuseArray(msg, 0);
                        if (value.contains("(_PARAMETER_)") && !msgstr.contains("(_") && !msgstr.contains("_)")) {

                            String[] parts = fuseArray(msg, 1).split(";");
                            if (parts.length > 1) {
                                for (String s : parts) {
                                    value = value.replaceFirst("\\(_PARAMETER_\\)", s.trim());
                                }
                            } else
                                value = value.replace("(_PARAMETER_)", parts[0]);

                        }
                        if (value.contains("(_PARAMETER_CAPS_)") && !msgstr.contains("(_")
                                && !msgstr.contains("_)")) {

                            String[] parts = fuseArray(msg, 1).split(";");
                            if (parts.length > 1) {
                                for (String s : parts) {
                                    value = value.replaceFirst("\\(_PARAMETER_CAPS_\\)", s.trim());
                                }
                            } else
                                value = value.replace("(_PARAMETER_CAPS_)", parts[0].toUpperCase());

                        }
                        channelInfo.increaseCommandCount(command);
                        send(channel, sender, value);

                    }
                }

            } else {
                if (channelInfo.checkList(command) && msg.length > 1) {
                    if (msg[1].equalsIgnoreCase("add") && isOp) {
                        String rest = fuseArray(msg, 2);
                        boolean added = channelInfo.addToList(command, rest);
                        if (added) {
                            send(channel, "\"" + rest + "\" has been added to the list \"" + command + "\"");
                        } else {
                            send(channel, "That list item already exists in this list.");
                        }

                    } else if (msg[1].equalsIgnoreCase("delete") || msg[1].equalsIgnoreCase("remove") && isOp) {
                        if (isInteger(msg[2])) {
                            int index = Integer.parseInt(msg[2]) - 1;
                            boolean removed = channelInfo.removeFromList(command, index);
                            if (removed) {
                                send(channel, "Successfully removed list item #" + index + 1);
                            } else {
                                send(channel, "List item #" + msg[2] + " doesn't exist.");
                            }
                        }
                    } else if (msg[1].equalsIgnoreCase("restrict") && isOp && msg.length > 2) {

                        String levelStr = msg[3].toLowerCase();
                        int level = -1;
                        if (channelInfo.checkList(command)) {
                            if (levelStr.equalsIgnoreCase("owner") || levelStr.equalsIgnoreCase("owners")) {
                                level = 3;
                            }
                            if (levelStr.equalsIgnoreCase("mod") || levelStr.equalsIgnoreCase("mods")
                                    || levelStr.equalsIgnoreCase("moderators")
                                    || levelStr.equalsIgnoreCase("moderator")) {
                                level = 2;
                            }
                            if (levelStr.equalsIgnoreCase("regular") || levelStr.equalsIgnoreCase("regulars")
                                    || levelStr.equalsIgnoreCase("regs") || levelStr.equalsIgnoreCase("subs")) {
                                level = 1;
                            }
                            if (levelStr.equalsIgnoreCase("everyone") || levelStr.equalsIgnoreCase("all")) {
                                level = 0;
                            }
                            if (level > -1) {
                                channelInfo.restrictList(command, level);
                                send(channel, command + " successfully restricted to " + levelStr + ".");

                            } else {
                                send(channel, "Not a valid restriction group.");
                            }
                        } else {
                            send(channel, "That list does not exist.");
                        }

                    } else if (msg[1].equalsIgnoreCase("get") && msg.length > 2 && isInteger(msg[2])) {
                        if (accessLevel < channelInfo.checkListRestriction(command)) {
                            return;
                        }
                        int index = Integer.valueOf(msg[2]) - 1;
                        if (index >= 0) {
                            String listValue = channelInfo.getListItem(command, index);
                            if (listValue != null) {
                                if (listValue.contains("(_PURGE_)")) {
                                    listValue = listValue.replace("(_PURGE_)", msg[3].toLowerCase());
                                    sendCommand(channel, ".timeout " + msg[3].toLowerCase() + " 1");
                                } else if (listValue.contains("(_TIMEOUT_)")) {
                                    listValue = listValue.replace("(_TIMEOUT_)", msg[3].toLowerCase());
                                    sendCommand(channel, ".timeout " + msg[3].toLowerCase());

                                } else if (listValue.contains("(_BAN_)")) {
                                    listValue = listValue.replace("(_BAN_)", msg[1].toLowerCase());
                                    sendCommand(channel, ".ban " + msg[3].toLowerCase());
                                }
                                if (listValue.contains("(_SUBMODE_ON_)")) {
                                    sendCommand(channel, ".subscribers");
                                    listValue = listValue.replace("(_SUBMODE_ON_)", "");
                                } else if (listValue.contains("(_SUBMODE_OFF_)")) {
                                    sendCommand(channel, ".subscribersoff");
                                    listValue = listValue.replace("(_SUBMODE_OFF_)", "");
                                }
                                String msgstr = fuseArray(msg, 0);
                                if (listValue.contains("(_PARAMETER_)") && !msgstr.contains("(_")
                                        && !msgstr.contains("_)")) {

                                    String[] parts = fuseArray(msg, 3).split(";");
                                    if (parts.length > 1) {
                                        for (String s : parts) {
                                            listValue = listValue.replaceFirst("\\(_PARAMETER_\\)", s.trim());
                                        }
                                    } else
                                        listValue = listValue.replace("(_PARAMETER_)", parts[0]);

                                }
                                if (listValue.contains("(_PARAMETER_CAPS_)") && !msgstr.contains("(_")
                                        && !msgstr.contains("_)")) {

                                    String[] parts = fuseArray(msg, 3).split(";");
                                    if (parts.length > 1) {
                                        for (String s : parts) {
                                            listValue = listValue.replaceFirst("\\(_PARAMETER_CAPS_\\)", s.trim());
                                        }
                                    } else
                                        listValue = listValue.replace("(_PARAMETER_CAPS_)", parts[0].toUpperCase());

                                }
                                send(channel, sender, listValue);
                            } else {
                                send(channel, "No item at requested index.");
                            }
                        } else {
                            send(channel, "No item at requested index.");
                        }
                    } else if (isInteger(msg[1])) {
                        if (accessLevel < channelInfo.checkListRestriction(command)) {
                            return;
                        }
                        int index = Integer.valueOf(msg[1]) - 1;
                        if (index >= 0) {
                            String listValue = channelInfo.getListItem(command, index);
                            if (listValue != null) {
                                if (listValue.contains("(_PURGE_)")) {
                                    listValue = listValue.replace("(_PURGE_)", msg[2].toLowerCase());
                                    sendCommand(channel, ".timeout " + msg[2].toLowerCase() + " 1");
                                } else if (listValue.contains("(_TIMEOUT_)")) {
                                    listValue = listValue.replace("(_TIMEOUT_)", msg[2].toLowerCase());
                                    sendCommand(channel, ".timeout " + msg[2].toLowerCase());

                                } else if (listValue.contains("(_BAN_)")) {
                                    listValue = listValue.replace("(_BAN_)", msg[1].toLowerCase());
                                    sendCommand(channel, ".ban " + msg[2].toLowerCase());
                                }
                                String msgstr = fuseArray(msg, 0);
                                if (listValue.contains("(_SUBMODE_ON_)")) {
                                    sendCommand(channel, ".subscribers");
                                    listValue = listValue.replace("(_SUBMODE_ON_)", "");
                                } else if (listValue.contains("(_SUBMODE_OFF_)")) {
                                    sendCommand(channel, ".subscribersoff");
                                    listValue = listValue.replace("(_SUBMODE_OFF_)", "");
                                }
                                if (listValue.contains("(_PARAMETER_)") && !msgstr.contains("(_")
                                        && !msgstr.contains("_)")) {

                                    String[] parts = fuseArray(msg, 2).split(";");
                                    if (parts.length > 1) {
                                        for (String s : parts) {
                                            listValue = listValue.replaceFirst("\\(_PARAMETER_\\)", s.trim());
                                        }
                                    } else
                                        listValue = listValue.replace("(_PARAMETER_)", parts[0]);

                                }
                                if (listValue.contains("(_PARAMETER_CAPS_)") && !msgstr.contains("(_")
                                        && !msgstr.contains("_)")) {

                                    String[] parts = fuseArray(msg, 2).split(";");
                                    if (parts.length > 1) {
                                        for (String s : parts) {
                                            listValue = listValue.replaceFirst("\\(_PARAMETER_CAPS_\\)", s.trim());
                                        }
                                    } else
                                        listValue = listValue.replace("(_PARAMETER_CAPS_)", parts[0].toUpperCase());

                                }
                                send(channel, sender, listValue);
                            }
                        } else {
                            send(channel, "No item at requested index.");
                        }
                    } else if (msg[1].equalsIgnoreCase("random")) {
                        if (accessLevel < channelInfo.checkListRestriction(command)) {
                            return;
                        }
                        int size = channelInfo.getListSize(command);
                        System.out.println("size " + size);
                        int randReturn = (int) Math.round((Math.random() * (size - 1)) + 1);

                        String listValue = channelInfo.getListItem(command, randReturn - 1);
                        if (listValue != null) {
                            if (listValue.contains("(_PURGE_)")) {
                                listValue = listValue.replace("(_PURGE_)", msg[2].toLowerCase());
                                sendCommand(channel, ".timeout " + msg[2].toLowerCase() + " 1");
                            } else if (listValue.contains("(_TIMEOUT_)")) {
                                listValue = listValue.replace("(_TIMEOUT_)", msg[2].toLowerCase());
                                sendCommand(channel, ".timeout " + msg[2].toLowerCase());

                            } else if (listValue.contains("(_BAN_)")) {
                                listValue = listValue.replace("(_BAN_)", msg[1].toLowerCase());
                                sendCommand(channel, ".ban " + msg[2].toLowerCase());
                            }
                            if (listValue.contains("(_SUBMODE_ON_)")) {
                                sendCommand(channel, ".subscribers");
                                listValue = listValue.replace("(_SUBMODE_ON_)", "");
                            } else if (listValue.contains("(_SUBMODE_OFF_)")) {
                                sendCommand(channel, ".subscribersoff");
                                listValue = listValue.replace("(_SUBMODE_OFF_)", "");
                            }
                            String msgstr = fuseArray(msg, 0);
                            if (listValue.contains("(_PARAMETER_)") && !msgstr.contains("(_")
                                    && !msgstr.contains("_)")) {

                                String[] parts = fuseArray(msg, 2).split(";");
                                if (parts.length > 1) {
                                    for (String s : parts) {
                                        listValue = listValue.replaceFirst("\\(_PARAMETER_\\)", s.trim());
                                    }
                                } else
                                    listValue = listValue.replace("(_PARAMETER_)", parts[0]);

                            }
                            if (listValue.contains("(_PARAMETER_CAPS_)") && !msgstr.contains("(_")
                                    && !msgstr.contains("_)")) {

                                String[] parts = fuseArray(msg, 2).split(";");
                                if (parts.length > 1) {
                                    for (String s : parts) {
                                        listValue = listValue.replaceFirst("\\(_PARAMETER_CAPS_\\)", s.trim());
                                    }
                                } else
                                    listValue = listValue.replace("(_PARAMETER_CAPS_)", parts[0].toUpperCase());

                            }
                            send(channel, sender, listValue);
                        }

                    }
                }
            }

        }

        // ********************************************************************************
        // *********************************** Auto Reply
        // *********************************
        // ********************************************************************************
        boolean matched = false;
        for (int i = 0; i < channelInfo.autoReplyTrigger.size(); i++) {
            Matcher m = channelInfo.autoReplyTrigger.get(i).matcher(message);
            if (m.matches()) {
                if (matched) {
                    matched = false;
                    break;
                }
                matched = true;
                if (!channelInfo.onCooldown(channelInfo.autoReplyTrigger.get(i).toString())) {
                    String value = channelInfo.autoReplyResponse.get(i);
                    if (value.contains("(_REGULARS_ONLY_)")) {
                        if (isSub) {
                            value = value.replace("(_REGULARS_ONLY_)", "");
                            if (value.contains("(_PURGE_)")) {
                                value = value.replace("(_PURGE_)", sender);
                                sendCommand(channel, ".timeout " + sender + " 1");
                            } else if (value.contains("(_TIMEOUT_)")) {
                                value = value.replace("(_TIMEOUT_)", sender);
                                sendCommand(channel, ".timeout " + sender);

                            } else if (value.contains("(_BAN_)")) {
                                value = value.replace("(_BAN_)", sender);
                                sendCommand(channel, ".ban " + sender);
                            } else if (value.contains("(_COMMAND_") && value.contains("_)")) {
                                int commandStart = value.indexOf("(_COMMAND_") + 10;
                                int commandEnd = value.indexOf("_)", commandStart + 1);
                                String origCommand = value.substring(commandStart, commandEnd);
                                String command = origCommand.toLowerCase().replaceAll("[^a-zA-Z0-9]", "");
                                String response = channelInfo.getCommand(command);
                                value = value.replace("(_COMMAND_" + origCommand + "_)", response);

                            }
                            send(channel, sender, value);
                            channelInfo.registerCommandUsage(channelInfo.autoReplyTrigger.get(i).toString());
                        }
                    } else {

                        if (value.contains("(_PURGE_)")) {
                            value = value.replace("(_PURGE_)", sender);
                            sendCommand(channel, ".timeout " + sender + " 1");
                        } else if (value.contains("(_TIMEOUT_)")) {
                            value = value.replace("(_TIMEOUT_)", sender);
                            sendCommand(channel, ".timeout " + sender);

                        } else if (value.contains("(_BAN_)")) {
                            value = value.replace("(_BAN_)", sender);
                            sendCommand(channel, ".ban " + sender);
                        } else if (value.contains("(_COMMAND_") && value.contains("_)")) {
                            int commandStart = value.indexOf("(_COMMAND_") + 10;
                            int commandEnd = value.indexOf("_)", commandStart + 1);
                            String origCommand = value.substring(commandStart, commandEnd);
                            String command = origCommand.toLowerCase().replaceAll("[^a-zA-Z0-9]", "");
                            String response = channelInfo.getCommand(command);
                            value = value.replace("(_COMMAND_" + origCommand + "_)", response);

                        }

                        send(channel, sender, value);
                        channelInfo.registerCommandUsage(channelInfo.autoReplyTrigger.get(i).toString());
                    }
                }
            }
        }
    }

    protected void onAdministrativeMessage(String message, Channel channelInfo) {
        String[] msg = message.trim().split(" ");

        if (msg.length > 0) {
            if (msg[0].equalsIgnoreCase("SPECIALUSER")) {
                String user = msg[1];
                String tag = msg[2];

                if (tag.equalsIgnoreCase("admin") || tag.equalsIgnoreCase("staff"))
                    BotManager.getInstance().addTagAdmin(user);
                if (tag.equalsIgnoreCase("staff"))
                    BotManager.getInstance().addTagStaff(user);
                if (tag.equalsIgnoreCase("subscriber") && channelInfo != null) {
                    if (!user.equalsIgnoreCase("Coebot")) {
                        privMsgSub = true;
                    }

                }
                if (tag.equalsIgnoreCase("subscriber") && channelInfo == null) {
                    if (!user.equalsIgnoreCase("Coebot")) {
                        privMsgSub = true;
                    }
                }

            } else if (msg[0].equalsIgnoreCase("CLEARCHAT")) {
                if (msg.length > 1) {
                    String user = msg[1];
                    if (!BotManager.getInstance().verboseLogging)
                        System.out.println("RAW: CLEARCHAT " + user);
                } else {
                    if (!BotManager.getInstance().verboseLogging)
                        System.out.println("RAW: CLEARCHAT");
                }
            } else if (msg[0].equalsIgnoreCase("HISTORYEND")) {
                String channel = msg[1];
                Channel ci = BotManager.getInstance().getChannel("#" + channel);
                ci.active = true;

            } else if (msg[0].equalsIgnoreCase("EMOTESET")) {
                String user = msg[1];
                String setsList = msg[2].replaceAll("(\\[|\\])", "");
                String[] sets = setsList.split(",");
                for (String s : sets)
                    BotManager.getInstance().addSubBySet(user, s);
            }
        }
    }

    protected void onResubscribe(Channel channel, String username, int months) {
        // System.out.println("RB: New subscriber in " + channel.getTwitchName()
        // + " " + username);
        logMain("Resub to " + channel.getChannel() + ": " + username);

        if (channel.getResubAlert()) {
            String msgFormat = channel.getResubMessage().replace("(_MONTHS_)", months + "").replace("(_1_)",
                    username);

            send(channel.getChannel(), msgFormat);

        }
    }

    protected void onNewSubscriber(Channel channel, String username) {
        // System.out.println("RB: New subscriber in " + channel.getTwitchName()
        // + " " + username);
        logMain("New subscriber to " + channel.getChannel() + ": " + username);

        if (channel.getSubAlert()) {
            String msgFormat = channel.getSubMessage();

            send(channel.getChannel(), msgFormat.replace("(_1_)", username));
        }
    }

    @Override
    public void onDisconnect() {
        lastPing = -1;
        try {
            System.out.println("INFO: Internal reconnection: " + this.getServer());
            String[] channels = this.getChannels();
            try {
                System.out.println("Sleeping for 20 seconds to allow for more JOINs");
                Thread.sleep(20000);
            } catch (InterruptedException e) {

                e.printStackTrace();
            }
            System.out.println("Reconnecting...");
            this.reconnect();
            for (int i = 0; i < channels.length; i++) {

                try {

                    Thread.sleep(600);
                    System.out.println("Joining: " + channels[i]);
                    this.joinChannel(channels[i]);
                } catch (InterruptedException e) {
                    System.out.println("unable to sleep");
                    e.printStackTrace();
                }
            }
            System.out.println("Done rejoining channels.");
        } catch (NickAlreadyInUseException e) {
            logMain("RB: [ERROR] Nickname already in use - " + this.getNick() + " " + this.getServer());
        } catch (IOException e) {
            logMain("RB: [ERROR] Unable to connect to server - " + this.getNick() + " " + this.getServer());
        } catch (IrcException e) {
            logMain("RB: [ERROR] Error connecting to server - " + this.getNick() + " " + this.getServer());
        }

    }

    public void onJoin(String channel, String sender, String login, String hostname) {

        Channel channelInfo = getChannelObject(channel);

        if (channelInfo == null)
            return;

        if (this.getNick().equalsIgnoreCase(sender)) {
            log("RB: Got self join for " + channel);
            if (BotManager.getInstance().ignoreHistory) {
                System.out.println("DEBUG: Marking " + channel + " as inactive.");
                channelInfo.active = false;
            }
        }
    }

    public void onPart(String channel, String sender, String login, String hostname) {

        Channel channelInfo = getChannelObject(channel);

        if (channelInfo == null)
            return;
    }

    public void send(String target, String sender, String message) {
        send(target, sender, message, null);
    }

    public void send(String target, String message) {
        send(target, null, message, null);
    }

    public void send(String target, String sender, String message, String[] args) {
        if (msgTimer.size() > 19) {

            msgTimer.add(System.currentTimeMillis());
            System.out.println(msgTimer.size());

            long diff = 0;
            try {
                diff = msgTimer.get(20) - msgTimer.get(0);
            } catch (Exception e) {
                logMain("RESETTING THE MSGTIMER QUEUE");
                e.printStackTrace();
                msgTimer = new ArrayList<Long>();
            }
            log("RB: There are " + msgTimer.size() + " times in msgTimer. Diff = " + diff);
            if (diff > 30 * 1000L) {

                msgTimer.remove(0);
                Channel channelInfo = getChannelObject(target);

                if (!BotManager.getInstance().verboseLogging)
                    logMain("SEND: " + target + " " + getNick() + " : " + message);

                message = MessageReplaceParser.parseMessage(target, sender, message, args);

                // if (message.equals("")) {
                // logMain("Empty message, not attempting to send.");
                // msgTimer.remove(msgTimer.size()-1);
                // if (tried) {
                // delete = true;
                // tried = false;
                // }
                // checkQueued();
                // return;
                // }

                boolean useBullet = true;

                if (message.startsWith("/me "))
                    useBullet = false;

                // Split if message > X characters
                if (message.length() > 0) {
                    List<String> chunks = Main.splitEqually(message, 500);
                    int c = 1;
                    if (target == null) {
                        sendMessage(target, "The bullet is null.");
                    }
                    for (String chunk : chunks) {
                        sendMessage(target, (useBullet ? channelInfo.getChannelBullet() + " " : "")
                                + (chunks.size() > 1 ? "[" + c + "] " : "") + chunk);
                        c++;
                        useBullet = true;
                    }
                }

                if (tried) {
                    delete = true;
                    tried = false;
                }
                checkQueued();
            } else {
                msgTimer.remove(20);
                log("RB: Prevented overflow of messages that would result in a ban.");
                QueuedMessage qm = new QueuedMessage(target, sender, message, args);
                boolean matchesAny = false;
                for (int i = 0; i < queuedMessages.size(); i++) {
                    if (queuedMessages.get(i).getMessage().equals(message)
                            && queuedMessages.get(i).getTarget().equals(target))
                        matchesAny = true;
                }
                if (!matchesAny) {
                    queuedMessages.add(qm);
                }

            }
        } else {
            log("RB: seeding msgTimer list");
            for (int i = 0; i < (20); i++) {
                msgTimer.add(System.currentTimeMillis() - 31000L);
            }
            QueuedMessage qm = new QueuedMessage(target, sender, message, args);
            queuedMessages.add(qm);
        }

    }

    public void checkQueued() {
        log("There are " + queuedMessages.size() + " queued messages");
        if (delete) {
            queuedMessages.remove(0);
            delete = false;
        }
        if (queuedMessages.size() > 0) {
            tried = true;
            QueuedMessage qm = queuedMessages.get(0);
            if (qm.isCommand()) {

                sendCommand(qm.getTarget(), qm.getMessage());
            } else {
                send(qm.getTarget(), qm.getSender(), qm.getMessage(), qm.getArgs());

            }
        }

    }

    public void sendCommand(String target, String message) {
        if (msgTimer.size() > 19) {
            System.out.println(msgTimer.size());
            msgTimer.add(System.currentTimeMillis());

            long diff = 0;
            try {
                diff = msgTimer.get(20) - msgTimer.get(0);
            } catch (Exception e) {
                logMain("RESETTING THE MSGTIMER QUEUE");
                e.printStackTrace();
                msgTimer = new ArrayList<Long>();
            }

            log("RB: There are " + msgTimer.size() + " times in msgTimer. Diff = " + diff);

            if (diff > 30 * 1000L) {
                msgTimer.remove(0);

                sendMessage(target, message);
                if (tried) {
                    delete = true;
                    tried = false;
                }
                checkQueued();

            } else {
                msgTimer.remove(20);
                log("RB: Prevented overflow of messages that would result in a ban.");
                QueuedMessage qm = new QueuedMessage(target, message, true);
                boolean matchesAny = false;
                for (int i = 0; i < queuedMessages.size(); i++) {
                    if (queuedMessages.get(i).getMessage().equals(message)
                            && queuedMessages.get(i).getTarget().equals(target))
                        matchesAny = true;
                }
                if (!matchesAny)
                    queuedMessages.add(qm);

            }
        } else {
            log("RB: seeding msgTimer list");
            for (int i = 0; i < (20); i++) {
                msgTimer.add(System.currentTimeMillis() - 31000L);
            }

        }
    }

    @Override
    public void onServerPing(String response) {
        super.onServerPing(response);
        lastPing = (int) (System.currentTimeMillis() / 1000);
    }

    public void log(String line) {
        if (this.getVerbose()) {
            logMain(System.currentTimeMillis() + " " + line);
        }
    }

    public void logMain(String line) {
        BotManager.getInstance().log(line);
    }

    public void startJoinCheck() {

        joinCheck = new Timer();

        int delay = 60000;

        joinCheck.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                String[] currentChanList = ReceiverBot.this.getChannels();
                for (Map.Entry<String, Channel> entry : BotManager.getInstance().channelList.entrySet()) {
                    boolean inList = false;
                    for (String c : currentChanList) {
                        if (entry.getValue().getChannel().equals(c))
                            inList = true;
                    }

                    if (!inList) {
                        log("RB: " + entry.getValue().getChannel() + " is not in the joined list.");
                        ReceiverBot.this.joinChannel(entry.getValue().getChannel(), false);
                        try {
                            Thread.sleep(600);
                        } catch (InterruptedException e) {

                            e.printStackTrace();
                        }
                    }

                }
            }
        }, delay, delay);

    }

    private int getSymbolsNumber(String s) {
        int symbols = 0;
        for (Pattern p : symbolsPatterns) {
            Matcher m = p.matcher(s);
            while (m.find())
                symbols += 1;
        }
        return symbols;
    }

    private int getCapsNumber(String s) {
        int caps = 0;
        for (int i = 0; i < s.length(); i++) {
            if (Character.isUpperCase(s.charAt(i))) {
                caps++;
            }
        }

        return caps;
    }

    // private int countConsecutiveCapitals(String s) {
    // int caps = 0;
    // int max = 0;
    // // boolean con = true;
    // for (int i = 0; i < s.length(); i++) {
    // if (Character.isUpperCase(s.charAt(i))) {
    // caps++;
    // } else {
    // if (caps > 0 && caps > max)
    // max = caps;
    // caps = 0;
    // }
    // }
    // if (caps > max)
    // return caps;
    // else
    // return max;
    // }

    private boolean containsLink(String message, Channel ch) {
        String[] splitMessage = message.toLowerCase().split(" ");
        for (String m : splitMessage) {
            for (Pattern pattern : linkPatterns) {

                Matcher match = pattern.matcher(m);
                if (match.matches()) {
                    log("RB: Link match on " + pattern.pattern());
                    if (ch.checkPermittedDomain(m))
                        return false;
                    else
                        return true;
                }
            }
        }

        return false;
    }

    // private boolean containsSymbol(String message, Channel ch) {
    //
    // for (Pattern pattern : symbolsPatterns) {
    // Matcher match = pattern.matcher(message);
    // if (match.find()) {
    // log("RB: Symbol match on " + pattern.pattern());
    // return true;
    // }
    //
    // }
    //
    // return false;
    // }

    private int countEmotes(String message) {

        String str = message;
        int count = 0;
        for (String findStr : BotManager.getInstance().emoteSet) {
            int lastIndex = 0;
            while (lastIndex != -1) {

                lastIndex = str.indexOf(findStr, lastIndex);

                if (lastIndex != -1) {
                    count++;
                    lastIndex += findStr.length();
                }
            }
        }
        return count;
    }

    private boolean checkSingleEmote(String message) {
        for (String emote : BotManager.getInstance().emoteSet) {
            if (emote.equals(message))
                return true;
        }
        return false;
    }

    public boolean isGlobalBannedWord(String message) {
        for (Pattern reg : BotManager.getInstance().globalBannedWords) {
            Matcher match = reg.matcher(message.toLowerCase());
            if (match.matches()) {
                log("RB: Global banned word matched: " + reg.toString());
                return true;
            }
        }
        return false;
    }

    private String getTimeoutText(int count, Channel channel) {
        if (channel.getEnableWarnings()) {
            if (count > 1) {
                return "timeout";
            } else {
                return "warning";
            }
        } else {
            return "timeout";
        }
    }

    private int getTODuration(int count, Channel channel) {
        if (channel.getEnableWarnings()) {
            if (count > 1) {
                return channel.getTimeoutDuration();
            } else {
                return 10;
            }
        } else {
            return channel.getTimeoutDuration();
        }
    }

    private void secondaryTO(final String channel, final String name, final int duration, FilterType type,
            String message) {

        String line = "FILTER: Issuing a timeout on " + name + " in " + channel + " for " + type.toString() + " ("
                + duration + ")";
        logMain(line);
        line = "FILTER: Affected Message: " + message;
        logMain(line);

        int iterations = BotManager.getInstance().multipleTimeout;

        for (int i = 0; i < iterations; i++) {
            Timer timer = new Timer();
            int delay = 1000 * i;
            timer.schedule(new TimerTask() {
                public void run() {
                    ReceiverBot.this.sendCommand(channel, ".timeout " + name + " " + duration);
                }
            }, delay);
        }

    }

    private void secondaryBan(final String channel, final String name, FilterType type) {

        String line = "RB: Issuing a ban on " + name + " in " + channel + " for " + type.toString();
        logMain(line);

        int iterations = BotManager.getInstance().multipleTimeout;
        for (int i = 0; i < iterations; i++) {
            Timer timer = new Timer();
            int delay = 1000 * i;
            System.out.println("Delay: " + delay);
            timer.schedule(new TimerTask() {
                public void run() {
                    ReceiverBot.this.sendCommand(channel, ".ban " + name);
                }
            }, delay);
        }

    }

    private void startGaTimer(int seconds, Channel channelInfo) {
        if (channelInfo.getGiveaway() != null) {
            channelInfo.getGiveaway().setTimer(new Timer());
            int delay = seconds * 1000;

            if (!channelInfo.getGiveaway().getStatus()) {
                channelInfo.getGiveaway().setStatus(true);
                send(channelInfo.getChannel(), "> Giveaway started. (" + seconds + " seconds)");
            }

            channelInfo.getGiveaway().getTimer().schedule(new giveawayTimer(channelInfo), delay);
        }
    }

    public String getTimeStreaming(String uptime) {
        uptime = uptime.replace("Z", "UTC");
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

        format.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
        try {
            Date then = format.parse(uptime);
            return "Streaming for " + this.getTimeTilNow(then) + ".";
        } catch (ParseException e) {
            e.printStackTrace();

        }

        return "An error occurred or stream is offline.";
    }

    public boolean checkStalePing() {
        if (lastPing == -1)
            return false;

        int difference = ((int) (System.currentTimeMillis() / 1000)) - lastPing;

        if (difference > BotManager.getInstance().pingInterval) {
            log("RB: Ping is stale. Last ping= " + lastPing + " Difference= " + difference);
            lastPing = -1;
            return true;
        }

        return false;
    }

    private String fuseArray(String[] array, int start) {
        String fused = "";
        for (int c = start; c < array.length; c++)
            fused += array[c] + " ";

        return fused.trim();

    }

    public String getTimeTilNow(Date date) {
        long difference = (long) (System.currentTimeMillis() / 1000) - (date.getTime() / 1000);
        String returnString = "";

        if (difference >= 86400) {
            int days = (int) (difference / 86400);
            returnString += days + "d ";
            difference -= days * 86400;
        }
        if (difference >= 3600) {
            int hours = (int) (difference / 3600);
            returnString += hours + "h ";
            difference -= hours * 3600;
        }

        int seconds = (int) (difference / 60);
        returnString += seconds + "m";
        difference -= seconds * 60;

        return returnString;
    }

    public void logGlobalBan(String channel, String sender, String message) {
        String line = sender + "," + channel + ",\"" + message + "\"\n";

        // System.out.print(line);
        try {
            BufferedWriter out = new BufferedWriter(
                    new OutputStreamWriter(new FileOutputStream("globalbans.csv", true), "UTF-8"));
            out.write(line);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static boolean isInteger(String str) {
        try {
            Integer.parseInt(str.trim());
            return true;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }

    private class giveawayTimer extends TimerTask {
        private Channel channelInfo;

        public giveawayTimer(Channel channelInfo2) {
            super();
            channelInfo = channelInfo2;
        }

        public void run() {
            if (channelInfo.getGiveaway() != null) {
                if (channelInfo.getGiveaway().getStatus()) {
                    channelInfo.getGiveaway().setStatus(false);
                    ReceiverBot.this.send(channelInfo.getChannel(), "> Giveaway over.");
                }
            }
        }
    }
}