3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-29 07:29:31 +01:00

Refactor chanserv.go to match nickserv.go, unify the two

This commit is contained in:
Daniel Oaks 2018-04-01 11:51:34 +10:00
parent 9af74d367a
commit 6fb4284e32
2 changed files with 153 additions and 53 deletions

View File

@ -5,16 +5,62 @@ package irc
import ( import (
"fmt" "fmt"
"sort"
"strings" "strings"
"github.com/goshuirc/irc-go/ircfmt" "github.com/goshuirc/irc-go/ircfmt"
"github.com/oragono/oragono/irc/modes" "github.com/oragono/oragono/irc/modes"
"github.com/oragono/oragono/irc/sno" "github.com/oragono/oragono/irc/sno"
"github.com/oragono/oragono/irc/utils"
) )
// ChanServNotice sends the client a notice from ChanServ. const chanservHelp = `ChanServ lets you register and manage channels.
func (rb *ResponseBuffer) ChanServNotice(text string) {
rb.Add(nil, fmt.Sprintf("ChanServ!services@%s", rb.target.server.name), "NOTICE", rb.target.nick, text) To see in-depth help for a specific ChanServ command, try:
$b/CS HELP <command>$b
Here are the commands you can use:
%s`
type csCommand struct {
capabs []string // oper capabs the given user has to have to access this command
handler func(server *Server, client *Client, command, params string, rb *ResponseBuffer)
help string
helpShort string
oper bool // true if the user has to be an oper to use this command
}
var (
chanservCommands = map[string]*csCommand{
"help": {
help: `Syntax: $bHELP [command]$b
HELP returns information on the given command.`,
helpShort: `$bHELP$b shows in-depth information about commands.`,
},
"op": {
handler: csOpHandler,
help: `Syntax: $bOP #channel [nickname]$b
OP makes the given nickname, or yourself, a channel admin. You can only use
this command if you're the founder of the channel.`,
helpShort: `$bOP$b makes the given user (or yourself) a channel admin.`,
},
"register": {
handler: csRegisterHandler,
help: `Syntax: $bREGISTER #channel$b
REGISTER lets you own the given channel. If you rejoin this channel, you'll be
given admin privs on it. Modes set on the channel and the topic will also be
remembered.`,
helpShort: `$bREGISTER$b lets you own a given channel.`,
},
}
)
// csNotice sends the client a notice from ChanServ
func csNotice(rb *ResponseBuffer, text string) {
rb.Add(nil, "ChanServ", "NOTICE", rb.target.Nick(), text)
} }
// chanservReceiveNotice handles NOTICEs that ChanServ receives. // chanservReceiveNotice handles NOTICEs that ChanServ receives.
@ -24,68 +70,115 @@ func (server *Server) chanservNoticeHandler(client *Client, message string, rb *
// chanservReceiveNotice handles NOTICEs that ChanServ receives. // chanservReceiveNotice handles NOTICEs that ChanServ receives.
func (server *Server) chanservPrivmsgHandler(client *Client, message string, rb *ResponseBuffer) { func (server *Server) chanservPrivmsgHandler(client *Client, message string, rb *ResponseBuffer) {
var params []string commandName, params := utils.ExtractParam(message)
for _, p := range strings.Split(message, " ") { commandName = strings.ToLower(commandName)
if len(p) > 0 {
params = append(params, p) commandInfo := chanservCommands[commandName]
} if commandInfo == nil {
} csNotice(rb, client.t("Unknown command. To see available commands, run /CS HELP"))
if len(params) < 1 {
rb.ChanServNotice(client.t("You need to run a command"))
//TODO(dan): dump CS help here
return return
} }
command := strings.ToLower(params[0]) if commandInfo.oper && !client.HasMode(modes.Operator) {
server.logger.Debug("chanserv", fmt.Sprintf("Client %s ran command %s", client.nick, command)) csNotice(rb, client.t("Command restricted"))
return
if command == "register" {
if len(params) < 2 {
rb.ChanServNotice(client.t("Syntax: REGISTER <channel>"))
return
}
server.chanservRegisterHandler(client, params[1], rb)
} else if command == "op" {
if len(params) < 2 {
rb.ChanServNotice(client.t("Syntax: OP <channel> [<nick>]"))
return
}
var clientToOp string
if 2 < len(params) {
clientToOp = params[2]
}
server.chanservOpHandler(client, params[1], clientToOp, rb)
} else {
rb.ChanServNotice(client.t("Sorry, I don't know that command"))
} }
if 0 < len(commandInfo.capabs) && !client.HasRoleCapabs(commandInfo.capabs...) {
csNotice(rb, client.t("Command restricted"))
return
}
// custom help handling here to prevent recursive init loop
if commandName == "help" {
csHelpHandler(server, client, commandName, params, rb)
return
}
if commandInfo.handler == nil {
csNotice(rb, client.t("Command error. Please report this to the developers"))
return
}
server.logger.Debug("chanserv", fmt.Sprintf("Client %s ran command %s", client.Nick(), commandName))
commandInfo.handler(server, client, commandName, params, rb)
} }
// chanservOpHandler handles the ChanServ OP subcommand. func csHelpHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
func (server *Server) chanservOpHandler(client *Client, channelName, clientToOp string, rb *ResponseBuffer) { csNotice(rb, ircfmt.Unescape(client.t("*** $bChanServ HELP$b ***")))
if params == "" {
// show general help
var shownHelpLines sort.StringSlice
for _, commandInfo := range chanservCommands {
// skip commands user can't access
if commandInfo.oper && !client.HasMode(modes.Operator) {
continue
}
if 0 < len(commandInfo.capabs) && !client.HasRoleCapabs(commandInfo.capabs...) {
continue
}
shownHelpLines = append(shownHelpLines, " "+client.t(commandInfo.helpShort))
}
// sort help lines
sort.Sort(shownHelpLines)
// assemble help text
assembledHelpLines := strings.Join(shownHelpLines, "\n")
fullHelp := ircfmt.Unescape(fmt.Sprintf(client.t(chanservHelp), assembledHelpLines))
// push out help text
for _, line := range strings.Split(fullHelp, "\n") {
csNotice(rb, line)
}
} else {
commandInfo := chanservCommands[strings.ToLower(strings.TrimSpace(params))]
if commandInfo == nil {
csNotice(rb, client.t("Unknown command. To see available commands, run /cs HELP"))
} else {
for _, line := range strings.Split(ircfmt.Unescape(client.t(commandInfo.help)), "\n") {
csNotice(rb, line)
}
}
}
csNotice(rb, ircfmt.Unescape(client.t("*** $bEnd of ChanServ HELP$b ***")))
}
func csOpHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
channelName, clientToOp := utils.ExtractParam(params)
if channelName == "" {
csNotice(rb, ircfmt.Unescape(client.t("Syntax: $bOP #channel [nickname]$b")))
return
}
clientToOp = strings.TrimSpace(clientToOp)
channelKey, err := CasefoldChannel(channelName) channelKey, err := CasefoldChannel(channelName)
if err != nil { if err != nil {
rb.ChanServNotice(client.t("Channel name is not valid")) csNotice(rb, client.t("Channel name is not valid"))
return return
} }
channelInfo := server.channels.Get(channelKey) channelInfo := server.channels.Get(channelKey)
if channelInfo == nil { if channelInfo == nil {
rb.ChanServNotice(client.t("Channel does not exist")) csNotice(rb, client.t("Channel does not exist"))
return return
} }
clientAccount := client.Account() clientAccount := client.Account()
if clientAccount == "" { if clientAccount == "" {
rb.ChanServNotice(client.t("You must be logged in to op on a channel")) csNotice(rb, client.t("You must be logged in to op on a channel"))
return return
} }
if clientAccount != channelInfo.Founder() { if clientAccount != channelInfo.Founder() {
rb.ChanServNotice(client.t("You must be the channel founder to op")) csNotice(rb, client.t("You must be the channel founder to op"))
return return
} }
@ -94,7 +187,7 @@ func (server *Server) chanservOpHandler(client *Client, channelName, clientToOp
casefoldedNickname, err := CasefoldName(clientToOp) casefoldedNickname, err := CasefoldName(clientToOp)
target = server.clients.Get(casefoldedNickname) target = server.clients.Get(casefoldedNickname)
if err != nil || target == nil { if err != nil || target == nil {
rb.ChanServNotice(client.t("Could not find given client")) csNotice(rb, client.t("Could not find given client"))
return return
} }
} else { } else {
@ -116,47 +209,52 @@ func (server *Server) chanservOpHandler(client *Client, channelName, clientToOp
} }
} }
rb.ChanServNotice(fmt.Sprintf(client.t("Successfully op'd in channel %s"), channelName)) csNotice(rb, fmt.Sprintf(client.t("Successfully op'd in channel %s"), channelName))
server.logger.Info("chanserv", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.nick, clientToOp, channelName)) server.logger.Info("chanserv", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.nick, clientToOp, channelName))
server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] CS OP'd $c[grey][$r%s$c[grey]] in channel $c[grey][$r%s$c[grey]]"), client.nickMaskString, clientToOp, channelName)) server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] CS OP'd $c[grey][$r%s$c[grey]] in channel $c[grey][$r%s$c[grey]]"), client.nickMaskString, clientToOp, channelName))
} }
// chanservRegisterHandler handles the ChanServ REGISTER subcommand. func csRegisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
func (server *Server) chanservRegisterHandler(client *Client, channelName string, rb *ResponseBuffer) {
if !server.channelRegistrationEnabled { if !server.channelRegistrationEnabled {
rb.ChanServNotice(client.t("Channel registration is not enabled")) csNotice(rb, client.t("Channel registration is not enabled"))
return
}
channelName := strings.TrimSpace(params)
if channelName == "" {
csNotice(rb, ircfmt.Unescape(client.t("Syntax: $bREGISTER #channel$b")))
return return
} }
channelKey, err := CasefoldChannel(channelName) channelKey, err := CasefoldChannel(channelName)
if err != nil { if err != nil {
rb.ChanServNotice(client.t("Channel name is not valid")) csNotice(rb, client.t("Channel name is not valid"))
return return
} }
channelInfo := server.channels.Get(channelKey) channelInfo := server.channels.Get(channelKey)
if channelInfo == nil || !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) { if channelInfo == nil || !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) {
rb.ChanServNotice(client.t("You must be an oper on the channel to register it")) csNotice(rb, client.t("You must be an oper on the channel to register it"))
return return
} }
if client.Account() == "" { if client.Account() == "" {
rb.ChanServNotice(client.t("You must be logged in to register a channel")) csNotice(rb, client.t("You must be logged in to register a channel"))
return return
} }
// this provides the synchronization that allows exactly one registration of the channel: // this provides the synchronization that allows exactly one registration of the channel:
err = channelInfo.SetRegistered(client.Account()) err = channelInfo.SetRegistered(client.Account())
if err != nil { if err != nil {
rb.ChanServNotice(err.Error()) csNotice(rb, err.Error())
return return
} }
// registration was successful: make the database reflect it // registration was successful: make the database reflect it
go server.channelRegistry.StoreChannel(channelInfo, true) go server.channelRegistry.StoreChannel(channelInfo, true)
rb.ChanServNotice(fmt.Sprintf(client.t("Channel %s successfully registered"), channelName)) csNotice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName)) server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString)) server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))

View File

@ -120,7 +120,7 @@ or other verification.`,
} }
) )
// send a notice from the NickServ "nick" // csNotice sends the client a notice from NickServ
func nsNotice(rb *ResponseBuffer, text string) { func nsNotice(rb *ResponseBuffer, text string) {
rb.Add(nil, "NickServ", "NOTICE", rb.target.Nick(), text) rb.Add(nil, "NickServ", "NOTICE", rb.target.Nick(), text)
} }
@ -167,6 +167,8 @@ func (server *Server) nickservPrivmsgHandler(client *Client, message string, rb
return return
} }
server.logger.Debug("nickserv", fmt.Sprintf("Client %s ran command %s", client.Nick(), commandName))
commandInfo.handler(server, client, commandName, params, rb) commandInfo.handler(server, client, commandName, params, rb)
} }