func (c *coreCmds) auth(w irc.Writer, ev *cmd.Event) (
	internal, external error) {
	var access *data.StoredUser
	pwd := ev.Args["password"]
	uname := ev.Args["username"]
	if len(uname) == 0 {
		uname = strings.TrimLeft(ev.Username(), "~")
	}
	state := c.b.State(ev.NetworkID)
	store := c.b.Store()
	host, nick := ev.Sender, ev.Nick()
	nChans, _ := state.NChannelsByUser(nick)
	access = ev.StoredUser
	if access == nil {
		access = store.AuthedUser(ev.NetworkID, host)
	}
	if access != nil {
		external = errors.New(errMsgAuthed)
		return
	}
	var err error
	if nChans > 0 {
		_, err = store.AuthUserPerma(ev.NetworkID, host, uname, pwd)
	} else {
		_, err = store.AuthUserTmp(ev.NetworkID, host, uname, pwd)
	}
	if err != nil {
		if authErr, ok := err.(data.AuthError); ok {
			external = authErr
		} else {
			internal = err
		}
		return
	}
	w.Noticef(nick, authSuccess, strings.ToLower(uname))
	return
}
auth authenticates a user.
package bot
import (
	"fmt"
	"regexp"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"github.com/aarondl/ultimateq/data"
	"github.com/aarondl/ultimateq/dispatch"
	"github.com/aarondl/ultimateq/dispatch/cmd"
	"github.com/aarondl/ultimateq/irc"
	"github.com/pkg/errors"
)
var rgxFlags = regexp.MustCompile(`^[A-Za-z]+$`)
const (
	extension = `core`
	register  = `register`
	auth      = `auth`
	logout    = `logout`
	access    = `access`
	users     = `users`
	gusers    = `gusers`
	susers    = `susers`
	deluser   = `deluser`
	delme     = `delme`
	passwd    = `passwd`
	masks     = `masks`
	addmask   = `addmask`
	delmask   = `delmask`
	resetpasswd = `setpasswd`
	ggive      = `ggive`
	sgive      = `sgive`
	give       = `give`
	gtake      = `gtake`
	stake      = `stake`
	take       = `take`
	takeAllArg = `all`
	help = `help`
	errFmtRegister   = `bot: A core command registration failed: %v`
	errMsgInternal   = `There was an internal error, try again later.`
	errFmtInternal   = `commander: Error processing command %v (%v)`
	errFmtExpired    = `commander: Could not find user [%v]`
	cmdExec          = "bot: Core command executed"
	errInternalError = "bot: Core command error"
	errInternalPanic = "bot: Core command panic"
	errMsgAuthed        = `You are already authenticated.`
	errFmtUserNotFound  = `The user [%v] could not be found.`
	errFmtUserNotAuthed = `The user [%v] is not authenticated.`
	registerDesc    = `Registers an account.`
	registerSuccess = `Registered [%v] successfully. You have been ` +
		`automatically authenticated.`
	registerSuccessFirst = `Registered [%v] successfully. ` +
		`As the first user, you have been given all permissions and ` +
		`privileges as well as being automatically authenticated. \o/`
	registerFailure = `The username [%v] is already registered.`
	authDesc        = `Authenticate a user to an account.`
	authSuccess     = `Successfully authenticated [%v].`
	logoutDesc      = `Logs the current user out of the account. Admins can ` +
		`add a user param to log that user out.`
	logoutSuccess  = `Successfully logged out.`
	accessDesc     = `Access retrieves the access for the user.`
	accessSuccess  = `Access for [%v]: %v`
	deluserDesc    = `Deletes a user account from the bot.`
	deluserSuccess = `Removed user [%v].`
	deluserFailure = `User [%v] does not exist.`
	delmeDesc      = `Deletes the current user's account.`
	delmeSuccess   = `Removed your user account [%v].`
	delmeFailure   = `User account could not be removed.`
	passwdDesc     = `Change the current user's account password.`
	passwdSuccess  = `Successfully updated password.`
	passwdFailure  = `Old password did not match the current password.`
	masksDesc      = `Retrieves the current user's mask list. Admins can add` +
		` a user param to see that user's masks.`
	masksSuccess = `Masks: %v`
	masksFailure = `No masks set.`
	addmaskDesc  = `Adds a mask to the current user. Admins can add a user` +
		` param to add a mask to that user.`
	addmaskSuccess = `Host [%v] added successfully.`
	addmaskFailure = `Host [%v] already exists.`
	delmaskDesc    = `Deletes a mask from the current user. Admins can add a` +
		` user param to remove a mask to that user.`
	delmaskSuccess = `Host [%v] removed successfully.`
	delmaskFailure = `Host [%v] not found.`
	resetpasswdDesc          = `Resets a user's password.`
	resetpasswdSuccess       = `Password reset successful.`
	resetpasswdSuccessTarget = `Your password was reset by %v, it is now: %v`
	ggiveDesc = `Gives global access to a user.` +
		` Arguments can be numeric levels or flags.`
	ggiveSuccess = `User [%v] now has: (%v) globally.`
	sgiveDesc    = `Gives network access to a user.` +
		` Arguments can be numeric levels or flags.`
	sgiveSuccess = `User [%v] now has: (%v) network-wide.`
	giveDesc     = `Gives channel access to a user.` +
		` Arguments can be numeric levels or flags.`
	giveSuccess = `User [%v] now has: (%v) on %v`
	gtakeDesc   = `Takes global access from a user. If no arguments are ` +
		`given, takes the level access, otherwise removes the given flags. ` +
		`Use all to take all access.`
	stakeDesc = `Takes network access from a user. If no arguments are ` +
		`given, takes the level access, otherwise removes the given flags. ` +
		`Use all to take all access.`
	takeDesc = `Takes channel access from a user. If no arguments are ` +
		`given, takes the level access, otherwise removes the given flags. ` +
		`Use all to take all access.`
	giveFailure = `Invalid arguments, must be numeric accesses from 1-255 or ` +
		`flags in the range: A-Za-z.`
	giveFailureHas = `User [%v](%v) already has: %v`
	takeFailure    = `Invalid arguments, leave empty to delete level access, ` +
		`specific flags to delete those flags, or the keyword all to delete ` +
		`everything. (given: %v)`
	takeFailureNo = `No action taken. User [%v](%v) has none of the given ` +
		`access to remove.`
	gusersDesc    = `Lists all the users added to the global access list.`
	gusersNoUsers = `No global users`
	gusersHead    = `Showing %v users:`
	usersDesc = `Lists all the users added to the channel's access list. ` +
		`If no channel specified then list for current channel.`
	usersNoUsers = `No users for %v`
	usersHead    = `Showing %v users for %v:`
	susersDesc    = `Lists all the users added to the network's access list. `
	susersNoUsers = `No users for %v`
	susersHead    = `Showing %v users for %v:`
	usersListHeadUser   = `User`
	usersListHeadAccess = `Access`
	usersList           = `%-*v %v`
	helpSuccess      = `Cmds:`
	helpSuccessUsage = `Usage: %v %v`
	helpFailure      = `No help available for (%v), try "help" for a list of ` +
		`all commands.`
	helpDesc = `Help with no arguments shows all commands, help with an ` +
		`argument performs a search, if only one match is found gives ` +
		`detailed information about that command.`
)
type (
	argv           []string
	giveHelperFunc func(*data.StoredUser, uint8, string) (string, bool)
	takeHelperFunc func(*data.StoredUser, bool, bool, string) (string, bool)
)
var commands = []struct {
	Name   string
	Desc   string
	Authed bool
	Public bool
	Level  uint8
	Flags  string
	Args   []string
}{
	{
		Name:   register,
		Desc:   registerDesc,
		Authed: false,
		Public: false,
		Level:  0,
		Flags:  ``,
		Args:   argv{`password`, `[username]`},
	},
	{
		Name:   auth,
		Desc:   authDesc,
		Authed: false,
		Public: false,
		Level:  0,
		Flags:  ``,
		Args:   argv{`password`, `[username]`},
	},
	{
		Name:   logout,
		Desc:   logoutDesc,
		Authed: true,
		Public: true,
		Level:  0,
		Flags:  ``,
		Args:   argv{`[*user]`},
	},
	{
		Name:   access,
		Desc:   accessDesc,
		Authed: true,
		Public: true,
		Level:  0,
		Flags:  ``,
		Args:   argv{`[*user]`},
	},
	{
		Name:   gusers,
		Desc:   gusersDesc,
		Authed: false,
		Public: true,
		Level:  0,
		Flags:  ``,
		Args:   nil,
	},
	{
		Name:   susers,
		Desc:   susersDesc,
		Authed: false,
		Public: true,
		Level:  0,
		Flags:  ``,
		Args:   nil,
	},
	{
		Name:   users,
		Desc:   usersDesc,
		Authed: false,
		Public: true,
		Level:  0,
		Flags:  ``,
		Args:   argv{`[chan]`},
	},
	{
		Name:   deluser,
		Desc:   deluserDesc,
		Authed: true,
		Public: true,
		Level:  0,
		Flags:  ``,
		Args:   argv{`*user`},
	},
	{
		Name:   delme,
		Desc:   delmeDesc,
		Authed: true,
		Public: true,
		Level:  0,
		Flags:  ``,
		Args:   nil,
	},
	{
		Name:   passwd,
		Desc:   passwdDesc,
		Authed: true,
		Public: false,
		Level:  0,
		Flags:  ``,
		Args:   argv{`oldpassword`, `newpassword`},
	},
	{
		Name:   masks,
		Desc:   masksDesc,
		Authed: true,
		Public: false,
		Level:  0,
		Flags:  ``,
		Args:   argv{`[*user]`},
	},
	{
		Name:   addmask,
		Desc:   addmaskDesc,
		Authed: true,
		Public: false,
		Level:  0,
		Flags:  ``,
		Args:   argv{`mask`, `[*user]`},
	},
	{
		Name:   delmask,
		Desc:   delmaskDesc,
		Authed: true,
		Public: false,
		Level:  0,
		Flags:  ``,
		Args:   argv{`mask`, `[*user]`},
	},
	{
		Name:   resetpasswd,
		Desc:   resetpasswdDesc,
		Authed: true,
		Public: false,
		Level:  0,
		Flags:  ``,
		Args:   argv{`nick`, `*user`},
	},
	{
		Name:   ggive,
		Desc:   ggiveDesc,
		Authed: true,
		Public: true,
		Level:  0,
		Flags:  `G`,
		Args:   argv{`*user`, `levelOrFlags...`},
	},
	{
		Name:   sgive,
		Desc:   sgiveDesc,
		Authed: true,
		Public: true,
		Level:  0,
		Flags:  `GS`,
		Args:   argv{`*user`, `levelOrFlags...`},
	},
	{
		Name:   give,
		Desc:   giveDesc,
		Authed: true,
		Public: true,
		Level:  0,
		Flags:  `GSC`,
		Args:   argv{`#chan`, `*user`, `levelOrFlags...`},
	},
	{
		Name:   gtake,
		Desc:   gtakeDesc,
		Authed: true,
		Public: true,
		Level:  0,
		Flags:  `G`,
		Args:   argv{`*user`, `[allOrFlags]`},
	},
	{
		Name:   stake,
		Desc:   stakeDesc,
		Authed: true,
		Public: true,
		Level:  0,
		Flags:  `GS`,
		Args:   argv{`*user`, `[allOrFlags]`},
	},
	{
		Name:   take,
		Desc:   takeDesc,
		Authed: true,
		Public: true,
		Level:  0,
		Flags:  `GSC`,
		Args:   argv{`#chan`, `*user`, `[allOrFlags]`},
	},
	{
		Name:   help,
		Desc:   helpDesc,
		Authed: false,
		Public: true,
		Level:  0,
		Flags:  ``,
		Args:   argv{`[command]`},
	},
}
// coreCmds is the bot's command handling struct. The bot itself uses
type coreCmds struct {
	b *Bot
	core     *dispatch.Core
	commands *dispatch.CommandDispatcher
}
func NewCoreCmds(b *Bot) (*coreCmds, error) {
	dispatchCore := dispatch.NewCore(b.Logger)
	c := &coreCmds{
		b:        b,
		core:     dispatchCore,
		commands: dispatch.NewCommandDispatcher(b.mkPrefixFetcher(), dispatchCore),
	}
	for _, command := range commands {
		privacy := cmd.Private
		if command.Public {
			privacy = cmd.AnyScope
		}
		var commandObj *cmd.Command
		if command.Authed {
			commandObj = cmd.NewAuthed(
				extension,
				command.Name,
				command.Desc,
				c,
				cmd.Privmsg,
				privacy,
				command.Level,
				command.Flags,
				command.Args...,
			)
		} else {
			commandObj = cmd.New(
				extension,
				command.Name,
				command.Desc,
				c,
				cmd.Privmsg,
				privacy,
				command.Args...,
			)
		}
		_, err := c.commands.Register("", "", commandObj)
		if err != nil {
			return nil, errors.Errorf(errFmtRegister, err)
		}
	}
	return c, nil
}
func (c *coreCmds) Cmd(cmd string, w irc.Writer,
	ev *cmd.Event) (internal error) {
	var external error
	c.b.Info(cmdExec, "cmd", cmd)
	defer func() {
		if r := recover(); r != nil {
			c.b.Error(errInternalPanic, "cmd", cmd, "panic", r)
			var buf [4096]byte
			n := runtime.Stack(buf[:], false)
			c.b.Error(fmt.Sprintf("%s", buf[:n]))
		}
	}()
	switch cmd {
	case register:
		internal, external = c.register(w, ev)
	case auth:
		internal, external = c.auth(w, ev)
	case logout:
		internal, external = c.logout(w, ev)
	case access:
		internal, external = c.access(w, ev)
	case gusers:
		internal, external = c.gusers(w, ev)
	case susers:
		internal, external = c.susers(w, ev)
	case users:
		internal, external = c.users(w, ev)
	case deluser:
		internal, external = c.deluser(w, ev)
	case delme:
		internal, external = c.delme(w, ev)
	case passwd:
		internal, external = c.passwd(w, ev)
	case masks:
		internal, external = c.masks(w, ev)
	case addmask:
		internal, external = c.addmask(w, ev)
	case delmask:
		internal, external = c.delmask(w, ev)
	case resetpasswd:
		internal, external = c.resetpasswd(w, ev)
	case ggive:
		internal, external = c.ggive(w, ev)
	case sgive:
		internal, external = c.sgive(w, ev)
	case give:
		internal, external = c.give(w, ev)
	case gtake:
		internal, external = c.gtake(w, ev)
	case stake:
		internal, external = c.stake(w, ev)
	case take:
		internal, external = c.take(w, ev)
	case help:
		internal, external = c.help(w, ev)
	}
	if internal != nil {
		c.b.Error(errInternalError, "cmd", cmd, "err", internal)
	}
	return external
}
func (c *coreCmds) register(w irc.Writer,
	ev *cmd.Event) (internal, external error) {
	var access *data.StoredUser
	pwd := ev.Args["password"]
	uname := ev.Args["username"]
	if len(uname) == 0 {
		uname = strings.TrimLeft(ev.Username(), "~")
	}
	host := ev.Sender
	nick := ev.Nick()
	store := c.b.Store()
	state := c.b.State(ev.NetworkID)
	access, internal = store.FindUser(uname)
	if internal != nil {
		return
	}
	if access != nil {
		return nil, fmt.Errorf(registerFailure, uname)
	}
	access, internal = data.NewStoredUser(uname, pwd)
	if internal != nil {
		return
	}
	nChans, _ := state.NChannelsByUser(nick)
	var hasAny bool
	hasAny, internal = store.HasAny()
	if internal != nil {
		return
	}
	if !hasAny {
		access.Access[":"] = data.Access{Level: ^uint8(0), Flags: ^uint64(0)}
	}
	internal = store.SaveUser(access)
	if internal != nil {
		return
	}
	if nChans > 0 {
		_, internal = store.AuthUserPerma(ev.NetworkID, host, uname, pwd)
	} else {
		_, internal = store.AuthUserTmp(ev.NetworkID, host, uname, pwd)
	}
	if internal != nil {
		return
	}
	uname = strings.ToLower(uname)
	if !hasAny {
		w.Noticef(nick, registerSuccessFirst, uname)
	} else {
		w.Noticef(nick, registerSuccess, uname)
	}
	return
}