Browse Source

Add join failed event

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

+ 1
- 0
CHANGELOG View File

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

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

27
  *
27
  *
28
  * profile {
28
  * profile {
29
  *     nickname = "MyBot"           // Required
29
  *     nickname = "MyBot"           // Required
30
- *     username = "bot
30
+ *     username = "bot"
31
  *     realName = "Botomatic v1.2"
31
  *     realName = "Botomatic v1.2"
32
  * }
32
  * }
33
  *
33
  *

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

95
 /** Raised when a user joins a channel. */
95
 /** Raised when a user joins a channel. */
96
 class ChannelJoined(metadata: EventMetadata, override val user: User, channel: String) : TargetedEvent(metadata, channel), SourcedEvent
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
 /** Raised when a user leaves a channel. */
131
 /** Raised when a user leaves a channel. */
99
 class ChannelParted(metadata: EventMetadata, override val user: User, channel: String, val reason: String = "") : TargetedEvent(metadata, channel), SourcedEvent
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
 package com.dmdirc.ktirc.messages
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
 internal const val CTCP_BYTE : Byte = 1
7
 internal const val CTCP_BYTE : Byte = 1
4
 
8
 
5
 internal const val RPL_WELCOME = "001"
9
 internal const val RPL_WELCOME = "001"
15
 internal const val RPL_MOTDSTART = "375"
19
 internal const val RPL_MOTDSTART = "375"
16
 internal const val RPL_ENDOFMOTD = "376"
20
 internal const val RPL_ENDOFMOTD = "376"
17
 
21
 
22
+internal const val ERR_TOOMANYCHANNELS = "405"
18
 internal const val ERR_NOMOTD = "422"
23
 internal const val ERR_NOMOTD = "422"
19
 internal const val ERR_NONICKNAMEGIVEN = "431"
24
 internal const val ERR_NONICKNAMEGIVEN = "431"
20
 internal const val ERR_ERRONEUSNICKNAME = "432"
25
 internal const val ERR_ERRONEUSNICKNAME = "432"
21
 internal const val ERR_NICKNAMEINUSE = "433"
26
 internal const val ERR_NICKNAMEINUSE = "433"
22
 internal const val ERR_NICKCOLLISION = "436"
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
 internal const val RPL_SASLSUCCESS = "903"
41
 internal const val RPL_SASLSUCCESS = "903"
25
 internal const val ERR_SASLFAIL = "904"
42
 internal const val ERR_SASLFAIL = "904"

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

1
 package com.dmdirc.ktirc.messages.processors
1
 package com.dmdirc.ktirc.messages.processors
2
 
2
 
3
+import com.dmdirc.ktirc.events.ChannelJoinFailed
3
 import com.dmdirc.ktirc.events.ChannelJoined
4
 import com.dmdirc.ktirc.events.ChannelJoined
5
+import com.dmdirc.ktirc.messages.*
4
 import com.dmdirc.ktirc.model.IrcMessage
6
 import com.dmdirc.ktirc.model.IrcMessage
5
 import com.dmdirc.ktirc.model.User
7
 import com.dmdirc.ktirc.model.User
6
 
8
 
7
 internal class JoinProcessor : MessageProcessor {
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
     private fun User.addExtendedJoinFields(params: List<ByteArray>) {
39
     private fun User.addExtendedJoinFields(params: List<ByteArray>) {
17
         if (params.size == 3) {
40
         if (params.size == 3) {
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
 package com.dmdirc.ktirc.messages.processors
1
 package com.dmdirc.ktirc.messages.processors
2
 
2
 
3
 import com.dmdirc.ktirc.TestConstants
3
 import com.dmdirc.ktirc.TestConstants
4
+import com.dmdirc.ktirc.events.ChannelJoinFailed
5
+import com.dmdirc.ktirc.events.ChannelJoined
4
 import com.dmdirc.ktirc.model.IrcMessage
6
 import com.dmdirc.ktirc.model.IrcMessage
5
 import com.dmdirc.ktirc.model.User
7
 import com.dmdirc.ktirc.model.User
6
 import com.dmdirc.ktirc.params
8
 import com.dmdirc.ktirc.params
17
     }
19
     }
18
 
20
 
19
     @Test
21
     @Test
20
-    fun `JoinProcessor raises join event`() {
22
+    fun `raises join event`() {
21
         val events = JoinProcessor().process(
23
         val events = JoinProcessor().process(
22
                 IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "JOIN", params("#crashandburn")))
24
                 IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "JOIN", params("#crashandburn")))
23
         assertEquals(1, events.size)
25
         assertEquals(1, events.size)
24
 
26
 
25
         assertEquals(TestConstants.time, events[0].metadata.time)
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
     @Test
33
     @Test
31
-    fun `JoinProcessor does nothing if prefix missing`() {
34
+    fun `does nothing if prefix missing`() {
32
         val events = JoinProcessor().process(
35
         val events = JoinProcessor().process(
33
                 IrcMessage(emptyMap(), null, "JOIN", params("#crashandburn")))
36
                 IrcMessage(emptyMap(), null, "JOIN", params("#crashandburn")))
34
         assertEquals(0, events.size)
37
         assertEquals(0, events.size)
35
     }
38
     }
36
 
39
 
37
     @Test
40
     @Test
38
-    fun `JoinProcessor adds real name and account from extended join`() {
41
+    fun `adds real name and account from extended join`() {
39
         val events = JoinProcessor().process(
42
         val events = JoinProcessor().process(
40
                 IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "JOIN", params("#crashandburn", "acidBurn", "Libby")))
43
                 IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "JOIN", params("#crashandburn", "acidBurn", "Libby")))
41
         assertEquals(1, events.size)
44
         assertEquals(1, events.size)
42
 
45
 
43
         assertEquals(TestConstants.time, events[0].metadata.time)
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
     @Test
52
     @Test
49
-    fun `JoinProcessor ignores account if the user is not authed`() {
53
+    fun `ignores account if the user is not authed`() {
50
         val events = JoinProcessor().process(
54
         val events = JoinProcessor().process(
51
                 IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "JOIN", params("#crashandburn", "*", "Libby")))
55
                 IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "JOIN", params("#crashandburn", "*", "Libby")))
52
         assertEquals(1, events.size)
56
         assertEquals(1, events.size)
53
 
57
 
54
         assertEquals(TestConstants.time, events[0].metadata.time)
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