func (b *Bot) stopServer(srv *Server) (stopped bool) {
	b.serverControl <- serverOp{srv, false, nil}
	return <-b.serverStop
}
stopServer stops the current server if it's running.
package bot
import (
	"errors"
	"fmt"
	"net"
	"os"
	"strings"
	"sync"
	"time"
	"github.com/aarondl/ultimateq/config"
	"github.com/aarondl/ultimateq/data"
	"github.com/aarondl/ultimateq/dispatch"
	"github.com/aarondl/ultimateq/dispatch/cmd"
	"github.com/aarondl/ultimateq/irc"
	"github.com/aarondl/ultimateq/parse"
	"gopkg.in/inconshreveable/log15.v2"
)
const (
	defaultReconnScale = time.Second
	errFmtReaderClosed = "bot: %v reader closed\n"
	errFmtClosingServer = "bot: Error closing server (%v)\n"
	errFmtExtensionInitErr = "ext: Failed to load extension (%s): %v"
	errFmtExtensionDeinitErr = "ext: Failed to load extension (%s): %v"
	fmtFailedConnecting = "bot: %v failed to connect (%v)"
	fmtDisconnected = "bot: %v disconnected"
	fmtReconnecting = "bot: %v reconnecting in %v..."
)
var (
	errParsingIrcMessage = "Failed to parse irc message"
	errLogFile = errors.New("bot: Could not open log file")
	errInvalidConfig = errors.New("bot: Invalid Configuration")
	errUnknownServerID = errors.New("bot: Unknown Network id")
	errServerKilled = errors.New("bot: Server killed")
	errServerKilledReconn = errors.New("bot: Server reconnection aborted")
)
type (
	ConnProvider func(string) (net.Conn, error)
	StoreProvider func(string) (*data.Store, error)
	LoggerProvider func() log15.Handler
	serverOp struct {
		server   *Server
		starting bool
		err      error
	}
)
type Bot struct {
	servers map[string]*Server
	conf    *config.Config
	store   *data.Store
	log15.Logger
	botEnd        chan error
	serverControl chan serverOp
	serverStart   chan bool
	serverStop    chan bool
	serverEnd     chan serverOp
	dispatchCore *dispatch.Core
	dispatcher   *dispatch.Dispatcher
	cmds         *dispatch.CommandDispatcher
	coreCommands *coreCmds
	attachHandlers bool
	connProvider   ConnProvider
	storeProvider  StoreProvider
	logProvider    LoggerProvider
	msgDispatchers sync.WaitGroup
	protectServers sync.RWMutex
}
func CheckConfig(c *config.Config) bool {
	if ers := c.Errors(); len(ers) > 0 || !c.Validate() {
		c.DisplayErrors(log15.Root())
		return false
	}
	return true
}
func New(conf *config.Config) (*Bot, error) {
	if !CheckConfig(conf) {
		return nil, errInvalidConfig
	}
	return createBot(conf, nil, nil, nil, true, true)
}
func (b *Bot) Start() <-chan error {
	b.protectServers.RLock()
	nServers := len(b.servers)
	for _, srv := range b.servers {
		go b.startServer(srv, true, true)
	}
	b.protectServers.RUnlock()
	go b.monitorServers(nServers)
	return b.botEnd
}
func (b *Bot) monitorServers(nServers int) {
	servers := 0
	allHadLifeChance := false
	serverControl := b.serverControl
	var serverEnd chan serverOp
	for {
		if !allHadLifeChance && servers == nServers {
			serverEnd = b.serverEnd
			allHadLifeChance = true
		}
		select {
		case op := <-serverControl:
			isStarted := op.server.started
			if op.starting {
				op.server.killable = make(chan int)
				b.serverStart <- !isStarted
				if !isStarted {
					op.server.started = true
					servers++
				}
			} else {
				if isStarted {
					op.server.started = false
					close(op.server.killable)
				}
				b.serverStop <- isStarted
			}
		case op := <-serverEnd:
			op.server.started = false
			b.botEnd <- op.err
			servers--
		}
		if servers == 0 {
			close(b.botEnd)
			break
		}
	}
}
func (b *Bot) StartNetwork(networkID string) (started bool) {
	b.protectServers.RLock()
	defer b.protectServers.RUnlock()
	var srv *Server
	if srv, started = b.servers[networkID]; started {
		go b.startServer(srv, true, true)
	}
	return
}
func (b *Bot) startServer(srv *Server, writing, reading bool) {
	var err error
	var disconnect bool
	var temporary bool
	b.serverControl <- serverOp{srv, true, nil}
	if !<-b.serverStart {
		return
	}
	for err == nil {
		srv.setStatus(STATUS_CONNECTING)
		err, temporary = srv.createIrcClient()
		disconnect = err != nil && temporary
		if err == nil {
			srv.Info("Connected")
			srv.setStatus(STATUS_STARTED)
			srv.client.SpawnWorkers(writing, reading)
			disconnect, err = b.dispatch(srv)
			if err != nil {
				break
			}
		}
		srv.Info("Disconnected", "err", err)
		cfg := srv.conf.Network(srv.networkID)
		noReconn, _ := cfg.NoReconnect()
		reconnTime, _ := cfg.ReconnectTimeout()
		if !disconnect || noReconn {
			break
		}
		err = nil
		srv.Close()
		wait := time.Duration(reconnTime) * srv.reconnScale
		srv.setStatus(STATUS_RECONNECTING)
		srv.Info("Reconnecting", "timeout", wait)
		select {
		case <-srv.killable:
			err = errServerKilledReconn
		case <-time.After(wait):
		}
	}
	srv.Close()
	b.serverEnd <- serverOp{srv, false, err}
	srv.setStatus(STATUS_STOPPED)
}
func (b *Bot) dispatch(srv *Server) (disconnect bool, err error) {
	var ircMsg *irc.Event
	var parseErr error
	readCh := srv.client.ReadChannel()
	b.dispatchMessage(srv,
		irc.NewEvent(srv.networkID, srv.netInfo, irc.CONNECT, srv.networkID))
	for err == nil && !disconnect {
		select {
		case ev, ok := <-readCh:
			if !ok {
				disconnect = true
				break
			}
			ircMsg, parseErr = parse.Parse(ev)
			if parseErr != nil {
				b.Warn(errParsingIrcMessage, "err", parseErr, "ev", ev)
				break
			}
			ircMsg.NetworkID = srv.networkID
			ircMsg.NetworkInfo = srv.netInfo
			if srv.state != nil {
				srv.state.Update(ircMsg)
			}
			if b.checkIgnored(ircMsg.Sender) {
				continue
			}
			b.dispatchMessage(srv, ircMsg)
		case <-srv.killable:
			err = errServerKilled
			break
		}
	}
	b.dispatchMessage(srv,
		irc.NewEvent(srv.networkID, srv.netInfo, irc.DISCONNECT, srv.networkID))
	return
}
func (b *Bot) dispatchMessage(s *Server, ev *irc.Event) {
	if b.coreCommands != nil {
		if handled, err := b.coreCommands.commands.Dispatch(s.writer, ev, b); err != nil {
			b.Logger.Error("cmd dispatch failed", "err", err)
		} else if handled {
			return
		}
	}
	b.dispatcher.Dispatch(s.writer, ev)
	if _, err := b.cmds.Dispatch(s.writer, ev, b); err != nil {
		b.Logger.Error("cmd dispatch failed", "err", err)
	}
}
func (b *Bot) Stop() {
	b.protectServers.RLock()
	defer b.protectServers.RUnlock()
	for _, srv := range b.servers {
		b.stopServer(srv)
	}
}
func (b *Bot) StopNetwork(networkID string) (stopped bool) {
	b.protectServers.RLock()
	defer b.protectServers.RUnlock()
	if srv := b.getServer(networkID); srv != nil {
		stopped = b.stopServer(srv)
	}
	return
}