Browse Source

Add join failed event

tags/v0.11.0
Chris Smith 5 years ago
parent
commit
fe284ce3e0

+ 1
- 0
CHANGELOG View File

@@ -1,6 +1,7 @@
1 1
 vNEXT (in development)
2 2
 
3 3
  * Added SourcedEvent interface for events that have a user source attached
4
+ * Added ChannelJoinFailed event
4 5
 
5 6
 v0.10.3
6 7
 

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/Dsl.kt View File

@@ -27,7 +27,7 @@ internal data class IrcClientConfig(
27 27
  *
28 28
  * profile {
29 29
  *     nickname = "MyBot"           // Required
30
- *     username = "bot
30
+ *     username = "bot"
31 31
  *     realName = "Botomatic v1.2"
32 32
  * }
33 33
  *

+ 33
- 0
src/main/kotlin/com/dmdirc/ktirc/events/Events.kt View File

@@ -95,6 +95,39 @@ class PingReceived(metadata: EventMetadata, val nonce: ByteArray) : IrcEvent(met
95 95
 /** Raised when a user joins a channel. */
96 96
 class ChannelJoined(metadata: EventMetadata, override val user: User, channel: String) : TargetedEvent(metadata, channel), SourcedEvent
97 97
 
98
+/** Raised when an attempt to join a channel fails. */
99
+class ChannelJoinFailed(metadata: EventMetadata, channel: String, val reason: JoinError) : TargetedEvent(metadata, channel) {
100
+    /** Reasons a join may fail. */
101
+    enum class JoinError {
102
+        /** We are already in the maximum number of channels allowed by the server. */
103
+        TooManyChannels,
104
+        /** The channel is no-hiding (+H), but we have invisible join/parts enabled. */
105
+        NoHiding,
106
+        /** The channel is keyed (+k) and a valid key was not provided. */
107
+        NeedKey,
108
+        /** The channel is invite only (+i) and no invite was received. */
109
+        NeedInvite,
110
+        /** The channel is limited to registered users only, and we are not registered. */
111
+        NeedRegisteredNick,
112
+        /** The channel is secure-only, and we're not using TLS. */
113
+        NeedTls,
114
+        /** The channel is limited to server admins and we are not one. */
115
+        NeedAdmin,
116
+        /** The channel is limited to ircops and we are not one. */
117
+        NeedOper,
118
+        /** We are banned from the channel. */
119
+        Banned,
120
+        /** The channel is limited (+l) and currently full. */
121
+        ChannelFull,
122
+        /** The channel name is disallowed by the server. */
123
+        BadChannelName,
124
+        /** We're trying to joiin too many channels and have been throttled. */
125
+        Throttled,
126
+        /** We don't know why. */
127
+        Unknown
128
+    }
129
+}
130
+
98 131
 /** Raised when a user leaves a channel. */
99 132
 class ChannelParted(metadata: EventMetadata, override val user: User, channel: String, val reason: String = "") : TargetedEvent(metadata, channel), SourcedEvent
100 133
 

+ 17
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/NumericConstants.kt View File

@@ -1,5 +1,9 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3
+// Sources:
4
+// ircd-seven: https://github.com/freenode/ircd-seven/blob/master/src/messages.tab
5
+// unreal: https://github.com/unrealircd/unrealircd/blob/master/src/s_err.c
6
+
3 7
 internal const val CTCP_BYTE : Byte = 1
4 8
 
5 9
 internal const val RPL_WELCOME = "001"
@@ -15,11 +19,24 @@ internal const val RPL_MOTD = "372"
15 19
 internal const val RPL_MOTDSTART = "375"
16 20
 internal const val RPL_ENDOFMOTD = "376"
17 21
 
22
+internal const val ERR_TOOMANYCHANNELS = "405"
18 23
 internal const val ERR_NOMOTD = "422"
19 24
 internal const val ERR_NONICKNAMEGIVEN = "431"
20 25
 internal const val ERR_ERRONEUSNICKNAME = "432"
21 26
 internal const val ERR_NICKNAMEINUSE = "433"
22 27
 internal const val ERR_NICKCOLLISION = "436"
28
+internal const val ERR_NOHIDING = "459" // Unreal
29
+internal const val ERR_CHANNELISFULL = "471"
30
+internal const val ERR_INVITEONLYCHAN = "473"
31
+internal const val ERR_BANNEDFROMCHAN = "474"
32
+internal const val ERR_BADCHANNELKEY = "475"
33
+internal const val ERR_NEEDREGGEDNICK = "477"
34
+internal const val ERR_BADCHANNAME = "479"
35
+internal const val ERR_THROTTLE = "480" // ircd-seven
36
+internal const val ERR_SECUREONLYCHAN = "489" // Unreal, inspircd
37
+internal const val ERR_TOOMANYJOINS = "500" // Unreal, same as ERR_THROTTLE
38
+internal const val ERR_ADMINONLY = "519" // Unreal
39
+internal const val ERR_OPERONLY = "520" // Unreal
23 40
 
24 41
 internal const val RPL_SASLSUCCESS = "903"
25 42
 internal const val ERR_SASLFAIL = "904"

+ 46
- 5
src/main/kotlin/com/dmdirc/ktirc/messages/processors/JoinProcessor.kt View File

@@ -1,17 +1,40 @@
1 1
 package com.dmdirc.ktirc.messages.processors
2 2
 
3
+import com.dmdirc.ktirc.events.ChannelJoinFailed
3 4
 import com.dmdirc.ktirc.events.ChannelJoined
5
+import com.dmdirc.ktirc.messages.*
4 6
 import com.dmdirc.ktirc.model.IrcMessage
5 7
 import com.dmdirc.ktirc.model.User
6 8
 
7 9
 internal class JoinProcessor : MessageProcessor {
8 10
 
9
-    override val commands = arrayOf("JOIN")
11
+    override val commands = arrayOf(
12
+            "JOIN",
13
+            ERR_TOOMANYCHANNELS,
14
+            ERR_NOHIDING,
15
+            ERR_CHANNELISFULL,
16
+            ERR_INVITEONLYCHAN,
17
+            ERR_BANNEDFROMCHAN,
18
+            ERR_BADCHANNELKEY,
19
+            ERR_NEEDREGGEDNICK,
20
+            ERR_BADCHANNAME,
21
+            ERR_THROTTLE,
22
+            ERR_SECUREONLYCHAN,
23
+            ERR_TOOMANYJOINS,
24
+            ERR_ADMINONLY,
25
+            ERR_OPERONLY)
10 26
 
11
-    override fun process(message: IrcMessage) = message.sourceUser?.let { user ->
12
-        user.addExtendedJoinFields(message.params)
13
-        listOf(ChannelJoined(message.metadata, user, String(message.params[0])))
14
-    } ?: emptyList()
27
+    override fun process(message: IrcMessage) = when {
28
+        message.command == "JOIN" ->
29
+            message.sourceUser?.let { user ->
30
+                user.addExtendedJoinFields(message.params)
31
+                listOf(ChannelJoined(message.metadata, user, String(message.params[0])))
32
+            } ?: emptyList()
33
+        message.params.size >= 2 ->
34
+            listOf(ChannelJoinFailed(message.metadata, String(message.params[1]), message.command.toReason()))
35
+        else ->
36
+            emptyList()
37
+    }
15 38
 
16 39
     private fun User.addExtendedJoinFields(params: List<ByteArray>) {
17 40
         if (params.size == 3) {
@@ -20,4 +43,22 @@ internal class JoinProcessor : MessageProcessor {
20 43
         }
21 44
     }
22 45
 
46
+    private fun String.toReason() = when (this) {
47
+        ERR_TOOMANYCHANNELS -> ChannelJoinFailed.JoinError.TooManyChannels
48
+        ERR_NOHIDING -> ChannelJoinFailed.JoinError.NoHiding
49
+        ERR_CHANNELISFULL -> ChannelJoinFailed.JoinError.ChannelFull
50
+        ERR_INVITEONLYCHAN -> ChannelJoinFailed.JoinError.NeedInvite
51
+        ERR_BANNEDFROMCHAN -> ChannelJoinFailed.JoinError.Banned
52
+        ERR_BADCHANNELKEY -> ChannelJoinFailed.JoinError.NeedKey
53
+        ERR_NEEDREGGEDNICK -> ChannelJoinFailed.JoinError.NeedRegisteredNick
54
+        ERR_BADCHANNAME -> ChannelJoinFailed.JoinError.BadChannelName
55
+        ERR_THROTTLE -> ChannelJoinFailed.JoinError.Throttled
56
+        ERR_SECUREONLYCHAN -> ChannelJoinFailed.JoinError.NeedTls
57
+        ERR_TOOMANYJOINS -> ChannelJoinFailed.JoinError.Throttled
58
+        ERR_ADMINONLY -> ChannelJoinFailed.JoinError.NeedAdmin
59
+        ERR_OPERONLY -> ChannelJoinFailed.JoinError.NeedOper
60
+        else -> ChannelJoinFailed.JoinError.Unknown
61
+    }
62
+
23 63
 }
64
+

+ 169
- 10
src/test/kotlin/com/dmdirc/ktirc/messages/processors/JoinProcessorTest.kt View File

@@ -1,6 +1,8 @@
1 1
 package com.dmdirc.ktirc.messages.processors
2 2
 
3 3
 import com.dmdirc.ktirc.TestConstants
4
+import com.dmdirc.ktirc.events.ChannelJoinFailed
5
+import com.dmdirc.ktirc.events.ChannelJoined
4 6
 import com.dmdirc.ktirc.model.IrcMessage
5 7
 import com.dmdirc.ktirc.model.User
6 8
 import com.dmdirc.ktirc.params
@@ -17,43 +19,200 @@ internal class JoinProcessorTest {
17 19
     }
18 20
 
19 21
     @Test
20
-    fun `JoinProcessor raises join event`() {
22
+    fun `raises join event`() {
21 23
         val events = JoinProcessor().process(
22 24
                 IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "JOIN", params("#crashandburn")))
23 25
         assertEquals(1, events.size)
24 26
 
25 27
         assertEquals(TestConstants.time, events[0].metadata.time)
26
-        assertEquals(User("acidburn", "libby", "root.localhost"), events[0].user)
27
-        assertEquals("#crashandburn", events[0].target)
28
+        val event = events[0] as ChannelJoined
29
+        assertEquals(User("acidburn", "libby", "root.localhost"), event.user)
30
+        assertEquals("#crashandburn", event.target)
28 31
     }
29 32
 
30 33
     @Test
31
-    fun `JoinProcessor does nothing if prefix missing`() {
34
+    fun `does nothing if prefix missing`() {
32 35
         val events = JoinProcessor().process(
33 36
                 IrcMessage(emptyMap(), null, "JOIN", params("#crashandburn")))
34 37
         assertEquals(0, events.size)
35 38
     }
36 39
 
37 40
     @Test
38
-    fun `JoinProcessor adds real name and account from extended join`() {
41
+    fun `adds real name and account from extended join`() {
39 42
         val events = JoinProcessor().process(
40 43
                 IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "JOIN", params("#crashandburn", "acidBurn", "Libby")))
41 44
         assertEquals(1, events.size)
42 45
 
43 46
         assertEquals(TestConstants.time, events[0].metadata.time)
44
-        assertEquals(User("acidburn", "libby", "root.localhost", account = "acidBurn", realName = "Libby"), events[0].user)
45
-        assertEquals("#crashandburn", events[0].target)
47
+        val event = events[0] as ChannelJoined
48
+        assertEquals(User("acidburn", "libby", "root.localhost", account = "acidBurn", realName = "Libby"), event.user)
49
+        assertEquals("#crashandburn", event.target)
46 50
     }
47 51
 
48 52
     @Test
49
-    fun `JoinProcessor ignores account if the user is not authed`() {
53
+    fun `ignores account if the user is not authed`() {
50 54
         val events = JoinProcessor().process(
51 55
                 IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "JOIN", params("#crashandburn", "*", "Libby")))
52 56
         assertEquals(1, events.size)
53 57
 
54 58
         assertEquals(TestConstants.time, events[0].metadata.time)
55
-        assertEquals(User("acidburn", "libby", "root.localhost", realName = "Libby"), events[0].user)
56
-        assertEquals("#crashandburn", events[0].target)
59
+        val event = events[0] as ChannelJoined
60
+        assertEquals(User("acidburn", "libby", "root.localhost", realName = "Libby"), event.user)
61
+        assertEquals("#crashandburn", event.target)
62
+    }
63
+
64
+    @Test
65
+    fun `raises join failed if too many channels reached`() {
66
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "405", params("acidBurn", "#thegibson", "You have joined too many channels")))
67
+        assertEquals(1, events.size)
68
+
69
+        assertEquals(TestConstants.time, events[0].metadata.time)
70
+        val event = events[0] as ChannelJoinFailed
71
+        assertEquals("#thegibson", event.target)
72
+        assertEquals(ChannelJoinFailed.JoinError.TooManyChannels, event.reason)
73
+    }
74
+
75
+    @Test
76
+    fun `raises join failed if hiding not allowed`() {
77
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "459", params("acidBurn", "#thegibson", "You are not allowed to hide")))
78
+        assertEquals(1, events.size)
79
+
80
+        assertEquals(TestConstants.time, events[0].metadata.time)
81
+        val event = events[0] as ChannelJoinFailed
82
+        assertEquals("#thegibson", event.target)
83
+        assertEquals(ChannelJoinFailed.JoinError.NoHiding, event.reason)
84
+    }
85
+
86
+    @Test
87
+    fun `raises join failed if channel is full`() {
88
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "471", params("acidBurn", "#thegibson", "Channel is full")))
89
+        assertEquals(1, events.size)
90
+
91
+        assertEquals(TestConstants.time, events[0].metadata.time)
92
+        val event = events[0] as ChannelJoinFailed
93
+        assertEquals("#thegibson", event.target)
94
+        assertEquals(ChannelJoinFailed.JoinError.ChannelFull, event.reason)
95
+    }
96
+
97
+    @Test
98
+    fun `raises join failed if channel is invite only`() {
99
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "473", params("acidBurn", "#thegibson", "Channel is invite only")))
100
+        assertEquals(1, events.size)
101
+
102
+        assertEquals(TestConstants.time, events[0].metadata.time)
103
+        val event = events[0] as ChannelJoinFailed
104
+        assertEquals("#thegibson", event.target)
105
+        assertEquals(ChannelJoinFailed.JoinError.NeedInvite, event.reason)
106
+    }
107
+
108
+    @Test
109
+    fun `raises join failed if banned`() {
110
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "474", params("acidBurn", "#thegibson", "You are banned")))
111
+        assertEquals(1, events.size)
112
+
113
+        assertEquals(TestConstants.time, events[0].metadata.time)
114
+        val event = events[0] as ChannelJoinFailed
115
+        assertEquals("#thegibson", event.target)
116
+        assertEquals(ChannelJoinFailed.JoinError.Banned, event.reason)
117
+    }
118
+
119
+    @Test
120
+    fun `raises join failed if channel is keyed`() {
121
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "475", params("acidBurn", "#thegibson", "Bad key (+k)")))
122
+        assertEquals(1, events.size)
123
+
124
+        assertEquals(TestConstants.time, events[0].metadata.time)
125
+        val event = events[0] as ChannelJoinFailed
126
+        assertEquals("#thegibson", event.target)
127
+        assertEquals(ChannelJoinFailed.JoinError.NeedKey, event.reason)
128
+    }
129
+
130
+    @Test
131
+    fun `raises join failed if need a registered nickname`() {
132
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "477", params("acidBurn", "#thegibson", "Must be registered")))
133
+        assertEquals(1, events.size)
134
+
135
+        assertEquals(TestConstants.time, events[0].metadata.time)
136
+        val event = events[0] as ChannelJoinFailed
137
+        assertEquals("#thegibson", event.target)
138
+        assertEquals(ChannelJoinFailed.JoinError.NeedRegisteredNick, event.reason)
139
+    }
140
+
141
+    @Test
142
+    fun `raises join failed if bad channel name`() {
143
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "479", params("acidBurn", "#thegibson", "Bad channel name")))
144
+        assertEquals(1, events.size)
145
+
146
+        assertEquals(TestConstants.time, events[0].metadata.time)
147
+        val event = events[0] as ChannelJoinFailed
148
+        assertEquals("#thegibson", event.target)
149
+        assertEquals(ChannelJoinFailed.JoinError.BadChannelName, event.reason)
150
+    }
151
+
152
+    @Test
153
+    fun `raises join failed if throttled`() {
154
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "480", params("acidBurn", "#thegibson", "You are throttled")))
155
+        assertEquals(1, events.size)
156
+
157
+        assertEquals(TestConstants.time, events[0].metadata.time)
158
+        val event = events[0] as ChannelJoinFailed
159
+        assertEquals("#thegibson", event.target)
160
+        assertEquals(ChannelJoinFailed.JoinError.Throttled, event.reason)
161
+    }
162
+
163
+    @Test
164
+    fun `raises join failed if secure only`() {
165
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "489", params("acidBurn", "#thegibson", "You must use a secure connection")))
166
+        assertEquals(1, events.size)
167
+
168
+        assertEquals(TestConstants.time, events[0].metadata.time)
169
+        val event = events[0] as ChannelJoinFailed
170
+        assertEquals("#thegibson", event.target)
171
+        assertEquals(ChannelJoinFailed.JoinError.NeedTls, event.reason)
172
+    }
173
+
174
+    @Test
175
+    fun `raises join failed if too many joins`() {
176
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "500", params("acidBurn", "#thegibson", "Too many joins")))
177
+        assertEquals(1, events.size)
178
+
179
+        assertEquals(TestConstants.time, events[0].metadata.time)
180
+        val event = events[0] as ChannelJoinFailed
181
+        assertEquals("#thegibson", event.target)
182
+        assertEquals(ChannelJoinFailed.JoinError.Throttled, event.reason)
183
+    }
184
+
185
+    @Test
186
+    fun `raises join failed if admin onnly`() {
187
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "519", params("acidBurn", "#thegibson", "Admins only")))
188
+        assertEquals(1, events.size)
189
+
190
+        assertEquals(TestConstants.time, events[0].metadata.time)
191
+        val event = events[0] as ChannelJoinFailed
192
+        assertEquals("#thegibson", event.target)
193
+        assertEquals(ChannelJoinFailed.JoinError.NeedAdmin, event.reason)
194
+    }
195
+
196
+    @Test
197
+    fun `raises join failed if oper only`() {
198
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "520", params("acidBurn", "#thegibson", "Opers only")))
199
+        assertEquals(1, events.size)
200
+
201
+        assertEquals(TestConstants.time, events[0].metadata.time)
202
+        val event = events[0] as ChannelJoinFailed
203
+        assertEquals("#thegibson", event.target)
204
+        assertEquals(ChannelJoinFailed.JoinError.NeedOper, event.reason)
205
+    }
206
+
207
+    @Test
208
+    fun `raises join failed if unknown numeric`() {
209
+        val events = JoinProcessor().process(IrcMessage(emptyMap(), null, "999", params("acidBurn", "#thegibson", "Weird")))
210
+        assertEquals(1, events.size)
211
+
212
+        assertEquals(TestConstants.time, events[0].metadata.time)
213
+        val event = events[0] as ChannelJoinFailed
214
+        assertEquals("#thegibson", event.target)
215
+        assertEquals(ChannelJoinFailed.JoinError.Unknown, event.reason)
57 216
     }
58 217
 
59 218
 }

Loading…
Cancel
Save