commit 9a595ff
Hubert Hirtz
·
2021-11-04 13:24:59 +0000 UTC
parent bf2a5cb
Take mode changes into account
2 files changed,
+98,
-2
+39,
-2
1@@ -117,6 +117,7 @@ type Session struct {
2
3 // ISUPPORT features
4 casemap func(string) string
5+ chanmodes [4]string
6 chantypes string
7 linelen int
8 historyLimit int
9@@ -877,14 +878,44 @@ func (s *Session) handleRegistered(msg Message) (Event, error) {
10 }, nil
11 }
12 case "MODE":
13- var channel string
14- if err := msg.ParseParams(&channel); err != nil {
15+ var channel, mode string
16+ if err := msg.ParseParams(&channel, &mode); err != nil {
17 return nil, err
18 }
19
20 channelCf := s.Casemap(channel)
21
22 if c, ok := s.channels[channelCf]; ok {
23+ modeChanges, err := ParseChannelMode(mode, msg.Params[2:], s.chanmodes, s.prefixModes)
24+ if err != nil {
25+ return nil, err
26+ }
27+ for _, change := range modeChanges {
28+ i := strings.IndexByte(s.prefixModes, change.Mode)
29+ if i < 0 {
30+ continue
31+ }
32+ nickCf := s.Casemap(change.Param)
33+ user := s.users[nickCf]
34+ membership, ok := c.Members[user]
35+ if !ok {
36+ continue
37+ }
38+ var newMembership []byte
39+ if change.Enable {
40+ newMembership = append([]byte(membership), s.prefixSymbols[i])
41+ sort.Slice(newMembership, func(i, j int) bool {
42+ i = strings.IndexByte(s.prefixSymbols, newMembership[i])
43+ j = strings.IndexByte(s.prefixSymbols, newMembership[j])
44+ return i < j
45+ })
46+ } else if j := strings.IndexByte(membership, s.prefixSymbols[i]); j >= 0 {
47+ newMembership = []byte(membership)
48+ newMembership = append(newMembership[:j], newMembership[j+1:]...)
49+ }
50+ c.Members[user] = string(newMembership)
51+ }
52+ s.channels[channelCf] = c
53 return ModeChangeEvent{
54 Channel: c.Name,
55 Mode: strings.Join(msg.Params[1:], " "),
56@@ -1169,6 +1200,12 @@ func (s *Session) updateFeatures(features []string) {
57 default:
58 s.casemap = CasemapRFC1459
59 }
60+ case "CHANMODES":
61+ // We only care about the first four params
62+ types := strings.SplitN(value, ",", 5)
63+ for i := 0; i < len(types) && i < len(s.chanmodes); i++ {
64+ s.chanmodes[i] = types[i]
65+ }
66 case "CHANTYPES":
67 s.chantypes = value
68 case "CHATHISTORY":
+59,
-0
1@@ -502,3 +502,62 @@ func ParseNameReply(trailing string, prefixes string) (names []Member) {
2
3 return
4 }
5+
6+// Mode types available in the CHANMODES 005 token.
7+const (
8+ ModeTypeA int = iota
9+ ModeTypeB
10+ ModeTypeC
11+ ModeTypeD
12+)
13+
14+type ModeChange struct {
15+ Enable bool
16+ Mode byte
17+ Param string
18+}
19+
20+// ParseChannelMode parses a MODE message for a channel, according to the
21+// CHANMODES of the server.
22+func ParseChannelMode(mode string, params []string, chanmodes [4]string, membershipModes string) ([]ModeChange, error) {
23+ var changes []ModeChange
24+ enable := true
25+ paramIdx := 0
26+ for i := 0; i < len(mode); i++ {
27+ m := mode[i]
28+ if m == '+' || m == '-' {
29+ enable = m == '+'
30+ continue
31+ }
32+ modeType := -1
33+ for t := 0; t < 4; t++ {
34+ if 0 <= strings.IndexByte(chanmodes[t], m) {
35+ modeType = t
36+ break
37+ }
38+ }
39+ if 0 <= strings.IndexByte(membershipModes, m) {
40+ modeType = ModeTypeB
41+ } else if modeType == -1 {
42+ return nil, fmt.Errorf("unknown mode %c", m)
43+ }
44+ // ref: https://modern.ircdocs.horse/#mode-message
45+ if modeType == ModeTypeA || modeType == ModeTypeB || (enable && modeType == ModeTypeC) {
46+ if len(params) <= paramIdx {
47+ return nil, fmt.Errorf("missing mode params")
48+ }
49+ changes = append(changes, ModeChange{
50+ Enable: enable,
51+ Mode: m,
52+ Param: params[paramIdx],
53+ })
54+ paramIdx++
55+ } else {
56+ changes = append(changes, ModeChange{
57+ Enable: enable,
58+ Mode: m,
59+ })
60+ }
61+ }
62+ return changes, nil
63+}