Browse Source

implement CHANSERV AMODE

tags/v0.12.0
Shivaram Lingamneni 6 years ago
parent
commit
1016f86f70
4 changed files with 126 additions and 27 deletions
  1. 1
    7
      irc/channel.go
  2. 78
    0
      irc/chanserv.go
  3. 34
    0
      irc/getters.go
  4. 13
    20
      irc/modes/modes.go

+ 1
- 7
irc/channel.go View File

@@ -232,13 +232,7 @@ func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) b
232 232
 
233 233
 	clientModes := channel.members[client]
234 234
 
235
-	// get voice, since it's not a part of ChannelPrivModes
236
-	if clientModes.HasMode(permission) {
237
-		return true
238
-	}
239
-
240
-	// check regular modes
241
-	for _, mode := range modes.ChannelPrivModes {
235
+	for _, mode := range modes.ChannelUserModes {
242 236
 		if clientModes.HasMode(mode) {
243 237
 			return true
244 238
 		}

+ 78
- 0
irc/chanserv.go View File

@@ -5,6 +5,7 @@ package irc
5 5
 
6 6
 import (
7 7
 	"fmt"
8
+	"sort"
8 9
 	"strings"
9 10
 
10 11
 	"github.com/goshuirc/irc-go/ircfmt"
@@ -42,6 +43,18 @@ remembered.`,
42 43
 			helpShort:    `$bREGISTER$b lets you own a given channel.`,
43 44
 			authRequired: true,
44 45
 		},
46
+		"amode": {
47
+			handler: csAmodeHandler,
48
+			help: `Syntax: $bAMODE #channel [mode change] [account]$b
49
+
50
+AMODE lists or modifies persistent mode settings that affect channel members.
51
+For example, $bAMODE #channel +o dan$b grants the the holder of the "dan"
52
+account the +o operator mode every time they join #channel. To list current
53
+accounts and modes, use $bAMODE #channel$b. Note that users are always
54
+referenced by their registered account names, not their nicknames.`,
55
+			helpShort:    `$bAMODE$b modifies persistent mode settings for channel members.`,
56
+			authRequired: true,
57
+		},
45 58
 	}
46 59
 )
47 60
 
@@ -50,6 +63,71 @@ func csNotice(rb *ResponseBuffer, text string) {
50 63
 	rb.Add(nil, "ChanServ", "NOTICE", rb.target.Nick(), text)
51 64
 }
52 65
 
66
+func csAmodeHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
67
+	channelName, modeChange := utils.ExtractParam(params)
68
+
69
+	channel := server.channels.Get(channelName)
70
+	if channel == nil {
71
+		csNotice(rb, client.t("Channel does not exist"))
72
+		return
73
+	}
74
+
75
+	clientAccount := client.Account()
76
+	if clientAccount == "" || clientAccount != channel.Founder() {
77
+		csNotice(rb, client.t("You must be the channel founder to use AMODE"))
78
+		return
79
+	}
80
+
81
+	modeChanges, unknown := modes.ParseChannelModeChanges(strings.Fields(modeChange)...)
82
+
83
+	if len(modeChanges) > 1 || len(unknown) > 0 {
84
+		csNotice(rb, client.t("Invalid mode change"))
85
+		return
86
+	}
87
+
88
+	if len(modeChanges) == 0 || modeChanges[0].Op == modes.List {
89
+		persistentModes := channel.AccountToUmode()
90
+		// sort the persistent modes in descending order of priority, i.e.,
91
+		// ascending order of their index in the ChannelUserModes list
92
+		sort.Slice(persistentModes, func(i, j int) bool {
93
+			index := func(modeChange modes.ModeChange) int {
94
+				for idx, mode := range modes.ChannelUserModes {
95
+					if modeChange.Mode == mode {
96
+						return idx
97
+					}
98
+				}
99
+				return len(modes.ChannelUserModes)
100
+			}
101
+			return index(persistentModes[i]) < index(persistentModes[j])
102
+		})
103
+		csNotice(rb, fmt.Sprintf(client.t("Channel %s has %d persistent modes set"), channelName, len(persistentModes)))
104
+		for _, modeChange := range persistentModes {
105
+			csNotice(rb, fmt.Sprintf(client.t("Account %s receives mode +%s"), modeChange.Arg, string(modeChange.Mode)))
106
+		}
107
+		return
108
+	}
109
+
110
+	accountIsValid := false
111
+	change := modeChanges[0]
112
+	// Arg is the account name, casefold it here
113
+	change.Arg, _ = CasefoldName(change.Arg)
114
+	if change.Arg != "" {
115
+		_, err := server.accounts.LoadAccount(change.Arg)
116
+		accountIsValid = (err == nil)
117
+	}
118
+	if !accountIsValid {
119
+		csNotice(rb, client.t("Account does not exist"))
120
+		return
121
+	}
122
+	applied := channel.ApplyAccountToUmodeChange(change)
123
+	if applied {
124
+		csNotice(rb, fmt.Sprintf(client.t("Successfully set mode %s"), change.String()))
125
+		go server.channelRegistry.StoreChannel(channel, IncludeLists)
126
+	} else {
127
+		csNotice(rb, client.t("Change was a no-op"))
128
+	}
129
+}
130
+
53 131
 func csOpHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
54 132
 	channelName, clientToOp := utils.ExtractParam(params)
55 133
 

+ 34
- 0
irc/getters.go View File

@@ -303,3 +303,37 @@ func (channel *Channel) Founder() string {
303 303
 	defer channel.stateMutex.RUnlock()
304 304
 	return channel.registeredFounder
305 305
 }
306
+
307
+func (channel *Channel) AccountToUmode() (result []modes.ModeChange) {
308
+	channel.stateMutex.RLock()
309
+	defer channel.stateMutex.RUnlock()
310
+	for account, mode := range channel.accountToUMode {
311
+		result = append(result, modes.ModeChange{
312
+			Mode: mode,
313
+			Arg:  account,
314
+			Op:   modes.Add,
315
+		})
316
+	}
317
+
318
+	return
319
+}
320
+
321
+func (channel *Channel) ApplyAccountToUmodeChange(change modes.ModeChange) (applied bool) {
322
+	channel.stateMutex.Lock()
323
+	defer channel.stateMutex.Unlock()
324
+
325
+	current := channel.accountToUMode[change.Arg]
326
+	switch change.Op {
327
+	case modes.Add:
328
+		applied = (current != change.Mode)
329
+		if applied {
330
+			channel.accountToUMode[change.Arg] = change.Mode
331
+		}
332
+	case modes.Remove:
333
+		applied = (current == change.Mode)
334
+		if applied {
335
+			delete(channel.accountToUMode, change.Arg)
336
+		}
337
+	}
338
+	return
339
+}

+ 13
- 20
irc/modes/modes.go View File

@@ -147,6 +147,12 @@ var (
147 147
 		ChannelFounder, ChannelAdmin, ChannelOperator, Halfop,
148 148
 	}
149 149
 
150
+	// ChannelUserModes holds the list of all modes that can be applied to a user in a channel,
151
+	// including Voice, in descending order of precedence
152
+	ChannelUserModes = Modes{
153
+		ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice,
154
+	}
155
+
150 156
 	ChannelModePrefixes = map[Mode]string{
151 157
 		ChannelFounder:  "~",
152 158
 		ChannelAdmin:    "&",
@@ -176,20 +182,13 @@ func SplitChannelMembershipPrefixes(target string) (prefixes string, name string
176 182
 }
177 183
 
178 184
 // GetLowestChannelModePrefix returns the lowest channel prefix mode out of the given prefixes.
179
-func GetLowestChannelModePrefix(prefixes string) *Mode {
180
-	var lowest *Mode
181
-
182
-	if strings.Contains(prefixes, "+") {
183
-		lowest = &Voice
184
-	} else {
185
-		for i, mode := range ChannelPrivModes {
186
-			if strings.Contains(prefixes, ChannelModePrefixes[mode]) {
187
-				lowest = &ChannelPrivModes[i]
188
-			}
185
+func GetLowestChannelModePrefix(prefixes string) (lowest *Mode) {
186
+	for i, mode := range ChannelUserModes {
187
+		if strings.Contains(prefixes, ChannelModePrefixes[mode]) {
188
+			lowest = &ChannelPrivModes[i]
189 189
 		}
190 190
 	}
191
-
192
-	return lowest
191
+	return
193 192
 }
194 193
 
195 194
 //
@@ -304,15 +303,12 @@ func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
304 303
 					break
305 304
 				}
306 305
 			}
307
-			for _, supportedMode := range ChannelPrivModes {
306
+			for _, supportedMode := range ChannelUserModes {
308 307
 				if rune(supportedMode) == mode {
309 308
 					isKnown = true
310 309
 					break
311 310
 				}
312 311
 			}
313
-			if mode == rune(Voice) {
314
-				isKnown = true
315
-			}
316 312
 			if !isKnown {
317 313
 				unknown[mode] = true
318 314
 				continue
@@ -392,14 +388,11 @@ func (set *ModeSet) Prefixes(isMultiPrefix bool) (prefixes string) {
392 388
 	defer set.RUnlock()
393 389
 
394 390
 	// add prefixes in order from highest to lowest privs
395
-	for _, mode := range ChannelPrivModes {
391
+	for _, mode := range ChannelUserModes {
396 392
 		if set.modes[mode] {
397 393
 			prefixes += ChannelModePrefixes[mode]
398 394
 		}
399 395
 	}
400
-	if set.modes[Voice] {
401
-		prefixes += ChannelModePrefixes[Voice]
402
-	}
403 396
 
404 397
 	if !isMultiPrefix && len(prefixes) > 1 {
405 398
 		prefixes = string(prefixes[0])

Loading…
Cancel
Save