3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-12-22 18:52:41 +01:00

modes: Start overhauling modes

This commit is contained in:
Daniel Oaks 2017-03-13 08:08:18 +10:00
parent b33b217fab
commit 657ed644cb

View File

@ -1,87 +1,45 @@
// Copyright (c) 2012-2014 Jeremy Latt // Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber // Copyright (c) 2014-2015 Edmund Huber
// Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net> // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license // released under the MIT license
package irc package irc
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
"github.com/DanielOaks/girc-go/ircmsg" "github.com/DanielOaks/girc-go/ircmsg"
) )
// user mode flags // ModeOp is an operation performed with modes
type UserMode rune type ModeOp rune
func (mode UserMode) String() string { func (op ModeOp) String() string {
return string(op)
}
const (
Add ModeOp = '+'
List ModeOp = '='
Remove ModeOp = '-'
)
// Mode represents a user/channel/server mode
type Mode rune
func (mode Mode) String() string {
return string(mode) return string(mode)
} }
type UserModes []UserMode // ModeChange is a single mode changing
func (modes UserModes) String() string {
strs := make([]string, len(modes))
for index, mode := range modes {
strs[index] = mode.String()
}
return strings.Join(strs, "")
}
type ModeChange struct { type ModeChange struct {
mode UserMode mode Mode
op ModeOp
}
func (change *ModeChange) String() string {
return fmt.Sprintf("%s%s", change.op, change.mode)
}
type ModeChanges []*ModeChange
func (changes ModeChanges) String() string {
if len(changes) == 0 {
return ""
}
op := changes[0].op
str := changes[0].op.String()
for _, change := range changes {
if change.op != op {
op = change.op
str += change.op.String()
}
str += change.mode.String()
}
return str
}
// channel mode flags
type ChannelMode rune
func (mode ChannelMode) String() string {
return string(mode)
}
type ChannelModes []ChannelMode
func (modes ChannelModes) String() string {
strs := make([]string, len(modes))
for index, mode := range modes {
strs[index] = mode.String()
}
return strings.Join(strs, "")
}
type ChannelModeChange struct {
mode ChannelMode
op ModeOp op ModeOp
arg string arg string
} }
func (change *ChannelModeChange) String() (str string) { func (change *ModeChange) String() (str string) {
if (change.op == Add) || (change.op == Remove) { if (change.op == Add) || (change.op == Remove) {
str = change.op.String() str = change.op.String()
} }
@ -92,9 +50,10 @@ func (change *ChannelModeChange) String() (str string) {
return return
} }
type ChannelModeChanges []*ChannelModeChange // ModeChanges are a collection of 'ModeChange's
type ModeChanges []ModeChange
func (changes ChannelModeChanges) String() string { func (changes ModeChanges) String() string {
if len(changes) == 0 { if len(changes) == 0 {
return "" return ""
} }
@ -119,82 +78,78 @@ func (changes ChannelModeChanges) String() string {
return str return str
} }
type ChannelModeCommand struct { // Modes is just a raw list of modes
channel string type Modes []Mode
changes ChannelModeChanges
} func (modes Modes) String() string {
strs := make([]string, len(modes))
type ModeOp rune for index, mode := range modes {
strs[index] = mode.String()
func (op ModeOp) String() string { }
return string(op) return strings.Join(strs, "")
} }
// User Modes
const ( const (
Add ModeOp = '+' Away Mode = 'a'
List ModeOp = '=' Invisible Mode = 'i'
Remove ModeOp = '-' LocalOperator Mode = 'O'
) Operator Mode = 'o'
Restricted Mode = 'r'
const ( ServerNotice Mode = 's' // deprecated
Away UserMode = 'a' TLS Mode = 'Z'
Invisible UserMode = 'i' UserRoleplaying Mode = 'E'
LocalOperator UserMode = 'O' WallOps Mode = 'w'
Operator UserMode = 'o'
Restricted UserMode = 'r'
ServerNotice UserMode = 's' // deprecated
TLS UserMode = 'Z'
UserRoleplaying UserMode = 'E'
WallOps UserMode = 'w'
) )
var ( var (
SupportedUserModes = UserModes{ SupportedUserModes = Modes{
Away, Invisible, Operator, UserRoleplaying, Away, Invisible, Operator, 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()
) )
// Channel Modes
const ( const (
BanMask ChannelMode = 'b' // arg BanMask Mode = 'b' // arg
ChanRoleplaying ChannelMode = 'E' // flag ChanRoleplaying Mode = 'E' // flag
ExceptMask ChannelMode = 'e' // arg ExceptMask Mode = 'e' // arg
InviteMask ChannelMode = 'I' // arg InviteMask Mode = 'I' // arg
InviteOnly ChannelMode = 'i' // flag InviteOnly Mode = 'i' // flag
Key ChannelMode = 'k' // flag arg Key Mode = 'k' // flag arg
Moderated ChannelMode = 'm' // flag Moderated Mode = 'm' // flag
NoOutside ChannelMode = 'n' // flag NoOutside Mode = 'n' // flag
OpOnlyTopic ChannelMode = 't' // flag OpOnlyTopic Mode = 't' // flag
Secret ChannelMode = 's' // flag Secret Mode = 's' // flag
UserLimit ChannelMode = 'l' // flag arg UserLimit Mode = 'l' // flag arg
) )
var ( var (
ChannelFounder ChannelMode = 'q' // arg ChannelFounder Mode = 'q' // arg
ChannelAdmin ChannelMode = 'a' // arg ChannelAdmin Mode = 'a' // arg
ChannelOperator ChannelMode = 'o' // arg ChannelOperator Mode = 'o' // arg
Halfop ChannelMode = 'h' // arg Halfop Mode = 'h' // arg
Voice ChannelMode = 'v' // arg Voice Mode = 'v' // arg
SupportedChannelModes = ChannelModes{ SupportedChannelModes = Modes{
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside, BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
OpOnlyTopic, Secret, UserLimit, ChanRoleplaying, OpOnlyTopic, Secret, UserLimit, ChanRoleplaying,
} }
// supportedChannelModesString acts as a cache for when we introduce users // supportedChannelModesString acts as a cache for when we introduce users
supportedChannelModesString = SupportedChannelModes.String() supportedChannelModesString = SupportedChannelModes.String()
DefaultChannelModes = ChannelModes{ DefaultChannelModes = Modes{
NoOutside, OpOnlyTopic, NoOutside, OpOnlyTopic,
} }
// ChannelPrivModes holds the list of modes that are privileged, ie founder/op/halfop, in order. // ChannelPrivModes holds the list of modes that are privileged, ie founder/op/halfop, in order.
// voice is not in this list because it cannot perform channel operator actions. // voice is not in this list because it cannot perform channel operator actions.
ChannelPrivModes = ChannelModes{ ChannelPrivModes = Modes{
ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, ChannelFounder, ChannelAdmin, ChannelOperator, Halfop,
} }
ChannelModePrefixes = map[ChannelMode]string{ ChannelModePrefixes = map[Mode]string{
ChannelFounder: "~", ChannelFounder: "~",
ChannelAdmin: "&", ChannelAdmin: "&",
ChannelOperator: "@", ChannelOperator: "@",
@ -203,6 +158,10 @@ var (
} }
) )
//
// channel membership prefixes
//
// SplitChannelMembershipPrefixes takes a target and returns the prefixes on it, then the name. // SplitChannelMembershipPrefixes takes a target and returns the prefixes on it, then the name.
func SplitChannelMembershipPrefixes(target string) (prefixes string, name string) { func SplitChannelMembershipPrefixes(target string) (prefixes string, name string) {
name = target name = target
@ -245,9 +204,48 @@ func modeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
if errChan == nil { if errChan == nil {
return cmodeHandler(server, client, msg) return cmodeHandler(server, client, msg)
} else {
return umodeHandler(server, client, msg)
} }
return umodeHandler(server, client, msg)
}
// applyModeChanges applies the given changes, and returns the applied changes.
func (client *Client) applyModeChanges(ModeChanges) ModeChanges {
applied := make(ModeChanges, 0)
for _, change := range changes {
switch change.mode {
case Invisible, ServerNotice, WallOps, UserRoleplaying:
switch change.op {
case Add:
if client.flags[change.mode] {
continue
}
client.flags[change.mode] = true
applied = append(applied, change)
case Remove:
if !client.flags[change.mode] {
continue
}
delete(client.flags, change.mode)
applied = append(applied, change)
}
case Operator, LocalOperator:
if change.op == Remove {
if !client.flags[change.mode] {
continue
}
delete(client.flags, change.mode)
applied = append(applied, change)
}
}
// can't do anything to TLS mode
}
// return the changes we could actually apply
return applied
} }
// MODE <target> [<modestring> [<mode arguments>...]] // MODE <target> [<modestring> [<mode arguments>...]]
@ -292,42 +290,12 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
continue continue
} }
changes = append(changes, &ModeChange{ changes = append(changes, &ModeChange{
mode: UserMode(mode), mode: Mode(mode),
op: op, op: op,
}) })
} }
for _, change := range changes { applied := target.applyModeChanges(changes)
switch change.mode {
case Invisible, ServerNotice, WallOps, UserRoleplaying:
switch change.op {
case Add:
if target.flags[change.mode] {
continue
}
target.flags[change.mode] = true
applied = append(applied, change)
case Remove:
if !target.flags[change.mode] {
continue
}
delete(target.flags, change.mode)
applied = append(applied, change)
}
case Operator, LocalOperator:
if change.op == Remove {
if !target.flags[change.mode] {
continue
}
delete(target.flags, change.mode)
applied = append(applied, change)
}
}
// can't do anything to TLS mode
}
} }
if len(applied) > 0 { if len(applied) > 0 {
@ -338,6 +306,16 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
return false return false
} }
//////
//////
//////
//////
//////
//////
//////
//////
//////
// MODE <target> [<modestring> [<mode arguments>...]] // MODE <target> [<modestring> [<mode arguments>...]]
func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
channelName, err := CasefoldChannel(msg.Params[0]) channelName, err := CasefoldChannel(msg.Params[0])