mirror of
https://github.com/ergochat/ergo.git
synced 2025-02-16 21:50:39 +01:00
Add very initial snomasks
This commit is contained in:
parent
1afd3b8f78
commit
fd793d6adb
@ -9,6 +9,7 @@ New release of Oragono!
|
|||||||
|
|
||||||
### Config Changes
|
### Config Changes
|
||||||
* Added `debug` section containing additional debug settings.
|
* Added `debug` section containing additional debug settings.
|
||||||
|
* Added `modes` key on oper config, for setting modes on oper-up.
|
||||||
* Added ability to log to `stdout` in logger methods.
|
* Added ability to log to `stdout` in logger methods.
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
@ -16,6 +17,7 @@ New release of Oragono!
|
|||||||
### Added
|
### Added
|
||||||
* Added ability to log to stdout.
|
* Added ability to log to stdout.
|
||||||
* Added ability to use StackImpact profiling.
|
* Added ability to use StackImpact profiling.
|
||||||
|
* Added initial server notice masks (snomasks).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Socket code rewritten to be a lot faster and safer.
|
* Socket code rewritten to be a lot faster and safer.
|
||||||
|
@ -94,6 +94,7 @@ type OperConfig struct {
|
|||||||
Vhost string
|
Vhost string
|
||||||
WhoisLine string `yaml:"whois-line"`
|
WhoisLine string `yaml:"whois-line"`
|
||||||
Password string
|
Password string
|
||||||
|
Modes string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conf *OperConfig) PasswordBytes() []byte {
|
func (conf *OperConfig) PasswordBytes() []byte {
|
||||||
@ -323,6 +324,7 @@ type Oper struct {
|
|||||||
WhoisLine string
|
WhoisLine string
|
||||||
Vhost string
|
Vhost string
|
||||||
Pass []byte
|
Pass []byte
|
||||||
|
Modes string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operators returns a map of operator configs from the given OperClass and config.
|
// Operators returns a map of operator configs from the given OperClass and config.
|
||||||
@ -349,6 +351,7 @@ func (conf *Config) Operators(oc *map[string]OperClass) (map[string]Oper, error)
|
|||||||
} else {
|
} else {
|
||||||
oper.WhoisLine = class.WhoisLine
|
oper.WhoisLine = class.WhoisLine
|
||||||
}
|
}
|
||||||
|
oper.Modes = strings.TrimSpace(opConf.Modes)
|
||||||
|
|
||||||
// successful, attach to list of opers
|
// successful, attach to list of opers
|
||||||
operators[name] = oper
|
operators[name] = oper
|
||||||
|
131
irc/modes.go
131
irc/modes.go
@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/DanielOaks/girc-go/ircmsg"
|
"github.com/DanielOaks/girc-go/ircmsg"
|
||||||
|
"github.com/DanielOaks/oragono/irc/sno"
|
||||||
"github.com/tidwall/buntdb"
|
"github.com/tidwall/buntdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -97,7 +98,7 @@ const (
|
|||||||
LocalOperator Mode = 'O'
|
LocalOperator Mode = 'O'
|
||||||
Operator Mode = 'o'
|
Operator Mode = 'o'
|
||||||
Restricted Mode = 'r'
|
Restricted Mode = 'r'
|
||||||
ServerNotice Mode = 's' // deprecated
|
ServerNotice Mode = 's'
|
||||||
TLS Mode = 'Z'
|
TLS Mode = 'Z'
|
||||||
UserRoleplaying Mode = 'E'
|
UserRoleplaying Mode = 'E'
|
||||||
WallOps Mode = 'w'
|
WallOps Mode = 'w'
|
||||||
@ -105,7 +106,7 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
SupportedUserModes = Modes{
|
SupportedUserModes = Modes{
|
||||||
Away, Invisible, Operator, UserRoleplaying,
|
Away, Invisible, Operator, ServerNotice, UserRoleplaying,
|
||||||
}
|
}
|
||||||
// supportedUserModesString acts as a cache for when we introduce users
|
// supportedUserModesString acts as a cache for when we introduce users
|
||||||
supportedUserModesString = SupportedUserModes.String()
|
supportedUserModesString = SupportedUserModes.String()
|
||||||
@ -210,15 +211,77 @@ func modeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
return umodeHandler(server, client, msg)
|
return umodeHandler(server, client, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseUserModeChanges returns the valid changes, and the list of unknown chars.
|
||||||
|
func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) {
|
||||||
|
changes := make(ModeChanges, 0)
|
||||||
|
unknown := make(map[rune]bool)
|
||||||
|
|
||||||
|
if 0 < len(params) {
|
||||||
|
modeArg := params[0]
|
||||||
|
op := ModeOp(modeArg[0])
|
||||||
|
if (op == Add) || (op == Remove) {
|
||||||
|
modeArg = modeArg[1:]
|
||||||
|
} else {
|
||||||
|
unknown[rune(modeArg[0])] = true
|
||||||
|
return changes, unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
skipArgs := 1
|
||||||
|
|
||||||
|
for _, mode := range modeArg {
|
||||||
|
if mode == '-' || mode == '+' {
|
||||||
|
op = ModeOp(mode)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
change := ModeChange{
|
||||||
|
mode: Mode(mode),
|
||||||
|
op: op,
|
||||||
|
}
|
||||||
|
|
||||||
|
// put arg into modechange if needed
|
||||||
|
switch Mode(mode) {
|
||||||
|
case ServerNotice:
|
||||||
|
// always require arg
|
||||||
|
if len(params) > skipArgs {
|
||||||
|
change.arg = params[skipArgs]
|
||||||
|
skipArgs++
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isKnown bool
|
||||||
|
for _, supportedMode := range SupportedUserModes {
|
||||||
|
if rune(supportedMode) == mode {
|
||||||
|
isKnown = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isKnown {
|
||||||
|
unknown[mode] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
changes = append(changes, change)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes, unknown
|
||||||
|
}
|
||||||
|
|
||||||
// applyUserModeChanges applies the given changes, and returns the applied changes.
|
// applyUserModeChanges applies the given changes, and returns the applied changes.
|
||||||
func (client *Client) applyUserModeChanges(changes ModeChanges) ModeChanges {
|
func (client *Client) applyUserModeChanges(force bool, changes ModeChanges) ModeChanges {
|
||||||
applied := make(ModeChanges, 0)
|
applied := make(ModeChanges, 0)
|
||||||
|
|
||||||
for _, change := range changes {
|
for _, change := range changes {
|
||||||
switch change.mode {
|
switch change.mode {
|
||||||
case Invisible, ServerNotice, WallOps, UserRoleplaying:
|
case Invisible, WallOps, UserRoleplaying, Operator, LocalOperator:
|
||||||
switch change.op {
|
switch change.op {
|
||||||
case Add:
|
case Add:
|
||||||
|
if !force && (change.mode == Operator || change.mode == LocalOperator) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if client.flags[change.mode] {
|
if client.flags[change.mode] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -233,12 +296,21 @@ func (client *Client) applyUserModeChanges(changes ModeChanges) ModeChanges {
|
|||||||
applied = append(applied, change)
|
applied = append(applied, change)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Operator, LocalOperator:
|
case ServerNotice:
|
||||||
if change.op == Remove {
|
if !client.flags[Operator] {
|
||||||
if !client.flags[change.mode] {
|
continue
|
||||||
continue
|
}
|
||||||
|
var masks []sno.Mask
|
||||||
|
if change.op == Add || change.op == Remove {
|
||||||
|
for _, char := range change.arg {
|
||||||
|
masks = append(masks, sno.Mask(char))
|
||||||
}
|
}
|
||||||
delete(client.flags, change.mode)
|
}
|
||||||
|
if change.op == Add {
|
||||||
|
client.server.snomasks.AddMasks(client, masks...)
|
||||||
|
applied = append(applied, change)
|
||||||
|
} else if change.op == Remove {
|
||||||
|
client.server.snomasks.RemoveMasks(client, masks...)
|
||||||
applied = append(applied, change)
|
applied = append(applied, change)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,38 +344,36 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// assemble changes
|
// applied mode changes
|
||||||
changes := make(ModeChanges, 0)
|
|
||||||
applied := make(ModeChanges, 0)
|
applied := make(ModeChanges, 0)
|
||||||
|
|
||||||
if len(msg.Params) > 1 {
|
if 1 < len(msg.Params) {
|
||||||
modeArg := msg.Params[1]
|
// parse out real mode changes
|
||||||
op := ModeOp(modeArg[0])
|
params := msg.Params[1:]
|
||||||
if (op == Add) || (op == Remove) {
|
changes, unknown := ParseUserModeChanges(params...)
|
||||||
modeArg = modeArg[1:]
|
|
||||||
} else {
|
// alert for unknown mode changes
|
||||||
client.Send(nil, server.name, ERR_UNKNOWNMODE, client.nick, string(modeArg[0]), "is an unknown mode character to me")
|
for char := range unknown {
|
||||||
|
client.Send(nil, server.name, ERR_UNKNOWNMODE, client.nick, string(char), "is an unknown mode character to me")
|
||||||
|
}
|
||||||
|
if len(unknown) == 1 && len(changes) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mode := range modeArg {
|
// apply mode changes
|
||||||
if mode == '-' || mode == '+' {
|
applied = target.applyUserModeChanges(msg.Command == "SAMODE", changes)
|
||||||
op = ModeOp(mode)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
changes = append(changes, ModeChange{
|
|
||||||
mode: Mode(mode),
|
|
||||||
op: op,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
applied = target.applyUserModeChanges(changes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(applied) > 0 {
|
if len(applied) > 0 {
|
||||||
client.Send(nil, client.nickMaskString, "MODE", target.nick, applied.String())
|
client.Send(nil, client.nickMaskString, "MODE", target.nick, applied.String())
|
||||||
} else if client == target {
|
} else if client == target {
|
||||||
client.Send(nil, target.nickMaskString, RPL_UMODEIS, target.nick, target.ModeString())
|
client.Send(nil, target.nickMaskString, RPL_UMODEIS, target.nick, target.ModeString())
|
||||||
|
if client.flags[LocalOperator] || client.flags[Operator] {
|
||||||
|
masks := server.snomasks.String(client)
|
||||||
|
if 0 < len(masks) {
|
||||||
|
client.Send(nil, target.nickMaskString, RPL_SNOMASKIS, target.nick, masks, "Server notice masks")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -372,6 +442,7 @@ func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
|
|||||||
}
|
}
|
||||||
if !isKnown {
|
if !isKnown {
|
||||||
unknown[mode] = true
|
unknown[mode] = true
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
changes = append(changes, change)
|
changes = append(changes, change)
|
||||||
|
@ -17,6 +17,7 @@ const (
|
|||||||
RPL_CREATED = "003"
|
RPL_CREATED = "003"
|
||||||
RPL_MYINFO = "004"
|
RPL_MYINFO = "004"
|
||||||
RPL_ISUPPORT = "005"
|
RPL_ISUPPORT = "005"
|
||||||
|
RPL_SNOMASKIS = "008"
|
||||||
RPL_BOUNCE = "010"
|
RPL_BOUNCE = "010"
|
||||||
RPL_TRACELINK = "200"
|
RPL_TRACELINK = "200"
|
||||||
RPL_TRACECONNECTING = "201"
|
RPL_TRACECONNECTING = "201"
|
||||||
|
@ -23,8 +23,10 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/DanielOaks/girc-go/ircfmt"
|
||||||
"github.com/DanielOaks/girc-go/ircmsg"
|
"github.com/DanielOaks/girc-go/ircmsg"
|
||||||
"github.com/DanielOaks/oragono/irc/logger"
|
"github.com/DanielOaks/oragono/irc/logger"
|
||||||
|
"github.com/DanielOaks/oragono/irc/sno"
|
||||||
"github.com/tidwall/buntdb"
|
"github.com/tidwall/buntdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -123,6 +125,7 @@ type Server struct {
|
|||||||
rehashSignal chan os.Signal
|
rehashSignal chan os.Signal
|
||||||
restAPI *RestAPIConfig
|
restAPI *RestAPIConfig
|
||||||
signals chan os.Signal
|
signals chan os.Signal
|
||||||
|
snomasks *SnoManager
|
||||||
store *buntdb.DB
|
store *buntdb.DB
|
||||||
stsEnabled bool
|
stsEnabled bool
|
||||||
whoWas *WhoWasList
|
whoWas *WhoWasList
|
||||||
@ -233,6 +236,7 @@ func NewServer(configFilename string, config *Config, logger *logger.Manager) (*
|
|||||||
rehashSignal: make(chan os.Signal, 1),
|
rehashSignal: make(chan os.Signal, 1),
|
||||||
restAPI: &config.Server.RestAPI,
|
restAPI: &config.Server.RestAPI,
|
||||||
signals: make(chan os.Signal, len(ServerExitSignals)),
|
signals: make(chan os.Signal, len(ServerExitSignals)),
|
||||||
|
snomasks: NewSnoManager(),
|
||||||
stsEnabled: config.Server.STS.Enabled,
|
stsEnabled: config.Server.STS.Enabled,
|
||||||
whoWas: NewWhoWasList(config.Limits.WhowasEntries),
|
whoWas: NewWhoWasList(config.Limits.WhowasEntries),
|
||||||
}
|
}
|
||||||
@ -474,6 +478,7 @@ func (server *Server) Run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
server.logger.Debug("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
|
server.logger.Debug("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
|
||||||
|
// prolly don't need to alert snomasks on this, only on connection reg
|
||||||
|
|
||||||
go NewClient(server, conn.Conn, conn.IsTLS)
|
go NewClient(server, conn.Conn, conn.IsTLS)
|
||||||
continue
|
continue
|
||||||
@ -664,6 +669,7 @@ func (server *Server) tryRegister(c *Client) {
|
|||||||
|
|
||||||
// continue registration
|
// continue registration
|
||||||
server.logger.Debug("localconnect", fmt.Sprintf("Client registered [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname))
|
server.logger.Debug("localconnect", fmt.Sprintf("Client registered [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname))
|
||||||
|
server.snomasks.Send(sno.LocalConnects, fmt.Sprintf(ircfmt.Unescape("Client registered $c[grey][$r%s$c[grey]] [u:$r%s$c[grey]] [h:$r%s$c[grey]] [r:$r%s$c[grey]]"), c.nick, c.username, c.rawHostname, c.realname))
|
||||||
c.Register()
|
c.Register()
|
||||||
|
|
||||||
// send welcome text
|
// send welcome text
|
||||||
@ -1263,13 +1269,27 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
client.updateNickMask()
|
client.updateNickMask()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set new modes
|
||||||
|
var applied ModeChanges
|
||||||
|
if 0 < len(server.operators[name].Modes) {
|
||||||
|
modeChanges, unknownChanges := ParseUserModeChanges(strings.Split(server.operators[name].Modes, " ")...)
|
||||||
|
applied = client.applyUserModeChanges(true, modeChanges)
|
||||||
|
if 0 < len(unknownChanges) {
|
||||||
|
var runes string
|
||||||
|
for r := range unknownChanges {
|
||||||
|
runes += string(r)
|
||||||
|
}
|
||||||
|
client.Notice(fmt.Sprintf("Could not apply mode changes: +%s", runes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client.Send(nil, server.name, RPL_YOUREOPER, client.nick, "You are now an IRC operator")
|
client.Send(nil, server.name, RPL_YOUREOPER, client.nick, "You are now an IRC operator")
|
||||||
//TODO(dan): Should this be sent automagically as part of setting the flag/mode?
|
|
||||||
modech := ModeChanges{ModeChange{
|
applied = append(applied, ModeChange{
|
||||||
mode: Operator,
|
mode: Operator,
|
||||||
op: Add,
|
op: Add,
|
||||||
}}
|
})
|
||||||
client.Send(nil, server.name, "MODE", client.nick, modech.String())
|
client.Send(nil, server.name, "MODE", client.nick, applied.String())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
irc/sno/constants.go
Normal file
35
irc/sno/constants.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Package sno holds Server Notice masks for easy reference.
|
||||||
|
package sno
|
||||||
|
|
||||||
|
// Mask is a type of server notice mask.
|
||||||
|
type Mask rune
|
||||||
|
|
||||||
|
// Notice mask types
|
||||||
|
const (
|
||||||
|
LocalAccouncements Mask = 'a'
|
||||||
|
LocalConnects Mask = 'c'
|
||||||
|
LocalChannels Mask = 'j'
|
||||||
|
LocalKills Mask = 'k'
|
||||||
|
LocalNicks Mask = 'n'
|
||||||
|
LocalOpers Mask = 'o'
|
||||||
|
LocalQuits Mask = 'q'
|
||||||
|
Stats Mask = 't'
|
||||||
|
LocalAccounts Mask = 'u'
|
||||||
|
LocalXline Mask = 'x'
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// NoticeMaskNames has readable names for our snomask types.
|
||||||
|
NoticeMaskNames = map[Mask]string{
|
||||||
|
LocalAccouncements: "ANNOUNCEMENT",
|
||||||
|
LocalConnects: "CONNECT",
|
||||||
|
LocalChannels: "CHANNEL",
|
||||||
|
LocalKills: "KILL",
|
||||||
|
LocalNicks: "NICK",
|
||||||
|
LocalOpers: "OPER",
|
||||||
|
LocalQuits: "QUIT",
|
||||||
|
Stats: "STATS",
|
||||||
|
LocalAccounts: "ACCOUNT",
|
||||||
|
LocalXline: "XLINE",
|
||||||
|
}
|
||||||
|
)
|
117
irc/snomanager.go
Normal file
117
irc/snomanager.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/DanielOaks/girc-go/ircfmt"
|
||||||
|
"github.com/DanielOaks/oragono/irc/sno"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SnoManager keeps track of which clients to send snomasks to.
|
||||||
|
type SnoManager struct {
|
||||||
|
sendListMutex sync.RWMutex
|
||||||
|
sendLists map[sno.Mask]map[*Client]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSnoManager returns a new SnoManager
|
||||||
|
func NewSnoManager() *SnoManager {
|
||||||
|
var m SnoManager
|
||||||
|
m.sendLists = make(map[sno.Mask]map[*Client]bool)
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMasks adds the given snomasks to the client.
|
||||||
|
func (m *SnoManager) AddMasks(client *Client, masks ...sno.Mask) {
|
||||||
|
m.sendListMutex.Lock()
|
||||||
|
defer m.sendListMutex.Unlock()
|
||||||
|
|
||||||
|
for _, mask := range masks {
|
||||||
|
currentClientList := m.sendLists[mask]
|
||||||
|
|
||||||
|
if currentClientList == nil {
|
||||||
|
currentClientList = map[*Client]bool{}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentClientList[client] = true
|
||||||
|
|
||||||
|
m.sendLists[mask] = currentClientList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveMasks removes the given snomasks from the client.
|
||||||
|
func (m *SnoManager) RemoveMasks(client *Client, masks ...sno.Mask) {
|
||||||
|
m.sendListMutex.Lock()
|
||||||
|
defer m.sendListMutex.Unlock()
|
||||||
|
|
||||||
|
for _, mask := range masks {
|
||||||
|
currentClientList := m.sendLists[mask]
|
||||||
|
|
||||||
|
if currentClientList == nil || len(currentClientList) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(currentClientList, client)
|
||||||
|
|
||||||
|
m.sendLists[mask] = currentClientList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveClient removes the given client from all of our lists.
|
||||||
|
func (m *SnoManager) RemoveClient(client *Client) {
|
||||||
|
m.sendListMutex.Lock()
|
||||||
|
defer m.sendListMutex.Unlock()
|
||||||
|
|
||||||
|
for mask := range m.sendLists {
|
||||||
|
currentClientList := m.sendLists[mask]
|
||||||
|
|
||||||
|
if currentClientList == nil || len(currentClientList) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(currentClientList, client)
|
||||||
|
|
||||||
|
m.sendLists[mask] = currentClientList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send sends the given snomask to all users signed up for it.
|
||||||
|
func (m *SnoManager) Send(mask sno.Mask, content string) {
|
||||||
|
m.sendListMutex.RLock()
|
||||||
|
defer m.sendListMutex.RUnlock()
|
||||||
|
|
||||||
|
currentClientList := m.sendLists[mask]
|
||||||
|
|
||||||
|
if currentClientList == nil || len(currentClientList) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// make the message
|
||||||
|
name := sno.NoticeMaskNames[mask]
|
||||||
|
if name == "" {
|
||||||
|
name = string(mask)
|
||||||
|
}
|
||||||
|
message := fmt.Sprintf(ircfmt.Unescape("$c[grey]-$r%s$c[grey]-$c %s"), name, content)
|
||||||
|
|
||||||
|
// send it out
|
||||||
|
for client := range currentClientList {
|
||||||
|
client.Notice(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the snomasks currently enabled.
|
||||||
|
func (m *SnoManager) String(client *Client) string {
|
||||||
|
m.sendListMutex.RLock()
|
||||||
|
defer m.sendListMutex.RUnlock()
|
||||||
|
|
||||||
|
var masks string
|
||||||
|
for mask, clients := range m.sendLists {
|
||||||
|
for c := range clients {
|
||||||
|
if c == client {
|
||||||
|
masks += string(mask)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return masks
|
||||||
|
}
|
@ -196,6 +196,9 @@ opers:
|
|||||||
# custom hostname
|
# custom hostname
|
||||||
vhost: "n"
|
vhost: "n"
|
||||||
|
|
||||||
|
# modes are the modes to auto-set upon opering-up
|
||||||
|
modes: +is acjknoqtux
|
||||||
|
|
||||||
# password to login with /OPER command
|
# password to login with /OPER command
|
||||||
# generated using "oragono genpasswd"
|
# generated using "oragono genpasswd"
|
||||||
password: JDJhJDA0JE1vZmwxZC9YTXBhZ3RWT2xBbkNwZnV3R2N6VFUwQUI0RUJRVXRBRHliZVVoa0VYMnlIaGsu
|
password: JDJhJDA0JE1vZmwxZC9YTXBhZ3RWT2xBbkNwZnV3R2N6VFUwQUI0RUJRVXRBRHliZVVoa0VYMnlIaGsu
|
||||||
|
Loading…
x
Reference in New Issue
Block a user