Browse Source

Support IRCv3 away notifications

Issue #15
tags/v1.1.0
Chris Smith 5 years ago
parent
commit
4e5aa5b827

+ 3
- 0
CHANGELOG View File

@@ -1,5 +1,8 @@
1 1
 vNEXT (in development)
2 2
 
3
+ * Away support:
4
+   * Added sendAway() method
5
+   * Added UserAway event and fanned-out ChannelAway
3 6
  * (Internal) improved the way byte buffers are used to
4 7
    reduce array copying and clean up code
5 8
 

+ 65
- 38
docs/index.adoc View File

@@ -964,6 +964,19 @@ directly on IRC.
964 964
 Raised when a user changes the topic of a channel we are joined to. If
965 965
 the topic was cleared/removed, the `topic` parameter will be `null`.
966 966
 
967
+==== ChannelAway
968
+* Type: IrcEvent, TargetedEvent, SourcedEvent
969
+* Properties:
970
+** `user`: `User` - the user whose away state has changed
971
+** `target`: `String` - the channel that the user is in
972
+** `message`: `String?` - the away message, or `null` if the user is back
973
+
974
+After a <<UserAway>> event, KtIrc will "fan out" the event to all of the
975
+channels that we share with the user and raise a `ChannelAway`
976
+event for each channel. This is designed to make implementing certain features
977
+easier; if you fully handle a UserAway event there is no need to also
978
+handle the ChannelAway events, and vice-versa.
979
+
967 980
 === Channel/User events
968 981
 
969 982
 TODO
@@ -988,6 +1001,20 @@ TODO
988 1001
 
989 1002
 TODO
990 1003
 
1004
+==== UserAway
1005
+* Type: IrcEvent, SourcedEvent
1006
+* Properties:
1007
+** `user`: `User` - the user who has changed their away state
1008
+** `message`: `String?` - the away message, or `null` if the user is back
1009
+
1010
+Raised when we are informed that a user has changed away states. If the server
1011
+supports the `away-notify` capability we will receive notifications for all
1012
+users in our common channels; otherwise, we will only receive notifications
1013
+for our own user.
1014
+
1015
+If the user is away but we don't know the reason for it, the `message`
1016
+property will be empty.
1017
+
991 1018
 ==== UserQuit
992 1019
 
993 1020
 TODO
@@ -1136,171 +1163,171 @@ The following table shows KtIrc's IRCv3 support as of this release:
1136 1163
 3+h| Capability negotiation
1137 1164
 
1138 1165
 | https://ircv3.net/specs/core/capability-negotiation.html[CAP]
1139
-| {set:cellbgcolor:green} Supported
1166
+| {set:cellbgcolor:#a7eeaa} Supported
1140 1167
 | {set:cellbgcolor!}
1141 1168
 See <<Supported capabilities>> for the caps KtIrc will negotiate
1142 1169
 
1143 1170
 | https://ircv3.net/specs/core/capability-negotiation.html#cap-ls-version[CAP 302]
1144
-| {set:cellbgcolor:green} Supported
1171
+| {set:cellbgcolor:#a7eeaa} Supported
1145 1172
 | {set:cellbgcolor!}
1146 1173
 See <<Supported capabilities>> for the caps KtIrc will negotiate
1147 1174
 
1148 1175
 | https://ircv3.net/specs/core/capability-negotiation.html#cap-notify[cap-notify]
1149
-| {set:cellbgcolor:red} Not yet supported
1176
+| {set:cellbgcolor:#f7d5d3} No support
1150 1177
 | {set:cellbgcolor!}
1151 1178
 
1152 1179
 3+h| Published specifications
1153 1180
 
1154 1181
 | https://ircv3.net/specs/extensions/account-notify-3.1.html[account-notify] v3.1
1155
-| {set:cellbgcolor:green} Supported
1182
+| {set:cellbgcolor:#a7eeaa} Supported
1156 1183
 | {set:cellbgcolor!}
1157 1184
 See <<UserAccountChanged>>
1158 1185
 
1159 1186
 | https://ircv3.net/specs/extensions/account-tag-3.2.html[account-tag] v3.2
1160
-| {set:cellbgcolor:green} Supported
1187
+| {set:cellbgcolor:#a7eeaa} Supported
1161 1188
 | {set:cellbgcolor!}
1162 1189
 Accounts are automatically added to `User` properties in events
1163 1190
 
1164 1191
 | https://ircv3.net/specs/extensions/away-notify-3.1.html[away-notify] v3.1
1165
-| {set:cellbgcolor:orange} Partial support
1192
+| {set:cellbgcolor:#eeeeaa} Partial support
1166 1193
 | {set:cellbgcolor!}
1167
-Negotiated but not yet exposed as an event
1194
+See <<UserAway>>. State not yet tracked.
1168 1195
 
1169 1196
 | https://ircv3.net/specs/extensions/batch-3.2.html[batch] v3.2
1170
-| {set:cellbgcolor:green} Supported
1197
+| {set:cellbgcolor:#a7eeaa} Supported
1171 1198
 | {set:cellbgcolor!}
1172 1199
 See <<BatchReceived>>
1173 1200
 
1174 1201
 | https://ircv3.net/specs/extensions/chghost-3.2.html[chghost] v3.2
1175
-| {set:cellbgcolor:green} Supported
1202
+| {set:cellbgcolor:#a7eeaa} Supported
1176 1203
 | {set:cellbgcolor!}
1177 1204
 See <<UserHostChanged>>
1178 1205
 
1179 1206
 | https://ircv3.net/specs/extensions/echo-message-3.2.html[echo-message] v3.2
1180
-| {set:cellbgcolor:green} Supported
1207
+| {set:cellbgcolor:#a7eeaa} Supported
1181 1208
 | {set:cellbgcolor!}
1182 1209
 See also the `alwaysEchoMessages` <<Behaviour>>
1183 1210
 
1184 1211
 | https://ircv3.net/specs/extensions/extended-join-3.1.html[extended-join] v3.1
1185
-| {set:cellbgcolor:green} Supported
1212
+| {set:cellbgcolor:#a7eeaa} Supported
1186 1213
 | {set:cellbgcolor!}
1187 1214
 Additional details are automatically added to `User` properties in events
1188 1215
 
1189 1216
 | https://ircv3.net/specs/extensions/invite-notify-3.2.html[invite-notify] v3.2
1190
-| {set:cellbgcolor:red} Not yet supported
1217
+| {set:cellbgcolor:#f7d5d3} No support
1191 1218
 | {set:cellbgcolor!}
1192 1219
 
1193 1220
 | https://ircv3.net/specs/extensions/message-tags.html[message-tags]
1194
-| {set:cellbgcolor:green} Supported
1221
+| {set:cellbgcolor:#a7eeaa} Supported
1195 1222
 | {set:cellbgcolor!}
1196 1223
 Exposed in the metadata property of <<Events>>
1197 1224
 
1198 1225
 | https://ircv3.net/specs/core/monitor-3.2.html[monitor]
1199
-| {set:cellbgcolor:red} Not yet supported
1226
+| {set:cellbgcolor:#f7d5d3} No support
1200 1227
 | {set:cellbgcolor!}
1201 1228
 
1202 1229
 | https://ircv3.net/specs/extensions/multi-prefix-3.1.html[multi-prefix] v3.1
1203
-| {set:cellbgcolor:green} Supported
1230
+| {set:cellbgcolor:#a7eeaa} Supported
1204 1231
 | {set:cellbgcolor!}
1205 1232
 Automatically included in <<ChannelState>>
1206 1233
 
1207 1234
 | https://ircv3.net/specs/extensions/sasl-3.1.html[SASL] v3.1
1208
-| {set:cellbgcolor:green} Supported
1235
+| {set:cellbgcolor:#a7eeaa} Supported
1209 1236
 | {set:cellbgcolor!}
1210 1237
 See <<SASL configuration>>
1211 1238
 
1212 1239
 | https://ircv3.net/specs/extensions/sasl-3.2.html[SASL] v3.2
1213
-| {set:cellbgcolor:orange} Partial support
1240
+| {set:cellbgcolor:#eeeeaa} Partial support
1214 1241
 | {set:cellbgcolor!}
1215 1242
 Notifications via `cap-notify` not yet supported. See <<SASL configuration>>
1216 1243
 
1217 1244
 | https://ircv3.net/specs/extensions/server-time-3.2.html[server-time] v3.2
1218
-| {set:cellbgcolor:green} Supported
1245
+| {set:cellbgcolor:#a7eeaa} Supported
1219 1246
 | {set:cellbgcolor!}
1220 1247
 Exposed in the metadata property of <<Events>>
1221 1248
 
1222 1249
 | https://ircv3.net/specs/extensions/sts.html[sts]
1223
-| {set:cellbgcolor:red} Not yet supported
1250
+| {set:cellbgcolor:#f7d5d3} No support
1224 1251
 | {set:cellbgcolor!}
1225 1252
 
1226 1253
 | https://ircv3.net/specs/extensions/userhost-in-names-3.2.html[userhost-in-names] v3.2
1227
-| {set:cellbgcolor:green} Supported
1254
+| {set:cellbgcolor:#a7eeaa} Supported
1228 1255
 | {set:cellbgcolor!}
1229 1256
 Automatically included in <<UserState>>
1230 1257
 
1231 1258
 | https://ircv3.net/specs/extensions/webirc.html[webirc]
1232
-| {set:cellbgcolor:red} Not yet supported
1259
+| {set:cellbgcolor:#f7d5d3} No support
1233 1260
 | {set:cellbgcolor!}
1234 1261
 
1235 1262
 3+h| Draft specifications
1236 1263
 
1237 1264
 | https://github.com/ircv3/ircv3-specifications/pull/363[brb]
1238
-| {set:cellbgcolor:red} Not yet supported
1265
+| {set:cellbgcolor:#f7d5d3} No support
1239 1266
 | {set:cellbgcolor!}
1240 1267
 
1241 1268
 | https://github.com/ircv3/ircv3-specifications/pull/308[channel renaming]
1242
-| {set:cellbgcolor:red} Not yet supported
1269
+| {set:cellbgcolor:#f7d5d3} No support
1243 1270
 | {set:cellbgcolor!}
1244 1271
 
1245 1272
 | https://github.com/ircv3/ircv3-specifications/pull/349[chathistory]
1246
-| {set:cellbgcolor:red} Not yet supported
1273
+| {set:cellbgcolor:#f7d5d3} No support
1247 1274
 | {set:cellbgcolor!}
1248 1275
 
1249 1276
 | https://github.com/ircv3/ircv3-specifications/pull/346[delivered]
1250
-| {set:cellbgcolor:red} Not yet supported
1277
+| {set:cellbgcolor:#f7d5d3} No support
1251 1278
 | {set:cellbgcolor!}
1252 1279
 
1253 1280
 | https://github.com/ircv3/ircv3-specifications/pull/304[editmsg]
1254
-| {set:cellbgcolor:red} Not yet supported
1281
+| {set:cellbgcolor:#f7d5d3} No support
1255 1282
 | {set:cellbgcolor!}
1256 1283
 
1257 1284
 | https://ircv3.net/specs/extensions/labeled-response.html[labeled-response]
1258
-| {set:cellbgcolor:green} Supported
1285
+| {set:cellbgcolor:#a7eeaa} Supported
1259 1286
 | {set:cellbgcolor!}
1260 1287
 Exposed in the metadata property of <<Events>>
1261 1288
 
1262 1289
 | https://ircv3.net/specs/extensions/message-ids.html[message-ids]
1263
-| {set:cellbgcolor:red} Not yet supported
1290
+| {set:cellbgcolor:#f7d5d3} No support
1264 1291
 | {set:cellbgcolor!}
1265 1292
 
1266 1293
 | https://github.com/ircv3/ircv3-specifications/pull/330[migrate]
1267
-| {set:cellbgcolor:red} Not yet supported
1294
+| {set:cellbgcolor:#f7d5d3} No support
1268 1295
 | {set:cellbgcolor!}
1269 1296
 
1270 1297
 | https://ircv3.net/specs/client-tags/react.html[react]
1271
-| {set:cellbgcolor:orange} Partial support
1298
+| {set:cellbgcolor:#eeeeaa} Partial support
1272 1299
 | {set:cellbgcolor!}
1273 1300
 Sending via <<React>> method, no events generated
1274 1301
 
1275 1302
 | https://github.com/ircv3/ircv3-specifications/pull/347[read]
1276
-| {set:cellbgcolor:red} Not yet supported
1303
+| {set:cellbgcolor:#f7d5d3} No support
1277 1304
 | {set:cellbgcolor!}
1278 1305
 
1279 1306
 | https://github.com/ircv3/ircv3-specifications/pull/276[register]
1280
-| {set:cellbgcolor:red} Not yet supported
1307
+| {set:cellbgcolor:#f7d5d3} No support
1281 1308
 | {set:cellbgcolor!}
1282 1309
 
1283 1310
 | https://ircv3.net/specs/client-tags/reply.html[reply]
1284
-| {set:cellbgcolor:orange} Partial support
1311
+| {set:cellbgcolor:#eeeeaa} Partial support
1285 1312
 | {set:cellbgcolor!}
1286 1313
 Sending via <<Reply>> method, not processed on incoming messages
1287 1314
 
1288 1315
 | https://github.com/ircv3/ircv3-specifications/pull/306[resume]
1289
-| {set:cellbgcolor:red} Not yet supported
1316
+| {set:cellbgcolor:#f7d5d3} No support
1290 1317
 | {set:cellbgcolor!}
1291 1318
 
1292 1319
 | https://github.com/ircv3/ircv3-specifications/pull/361[setname]
1293
-| {set:cellbgcolor:red} Not yet supported
1320
+| {set:cellbgcolor:#f7d5d3} No support
1294 1321
 | {set:cellbgcolor!}
1295 1322
 
1296 1323
 | https://github.com/ircv3/ircv3-specifications/pull/357[standard replies]
1297
-| {set:cellbgcolor:red} Not yet supported
1324
+| {set:cellbgcolor:#f7d5d3} No support
1298 1325
 | {set:cellbgcolor!}
1299 1326
 
1300 1327
 | https://github.com/ircv3/ircv3-specifications/pull/348[typing]
1301
-| {set:cellbgcolor:red} Not yet supported
1328
+| {set:cellbgcolor:#f7d5d3} No support
1302 1329
 | {set:cellbgcolor!}
1303 1330
 
1304
-3+^h|Vendor specifications
1331
+3+h|Vendor specifications
1305 1332
 
1306 1333
 |===

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

@@ -177,6 +177,15 @@ class ChannelNickChanged(metadata: EventMetadata, override val user: User, chann
177 177
     override val replacedUsers: Array<String>? = null
178 178
 }
179 179
 
180
+/**
181
+ * Raised when a user's away state changes, and they are in a common channel. If [message] is null then the user is
182
+ * now back.
183
+ *
184
+ * This event is only raised for other users if the server supports the 'away-notify' capability.
185
+ */
186
+class ChannelAway(metadata: EventMetadata, override val user: User, channel: String, val message: String?)
187
+    : TargetedEvent(metadata, channel), SourcedEvent
188
+
180 189
 /** Raised when a batch of the channel's member list has been received. More batches may follow. */
181 190
 class ChannelNamesReceived(metadata: EventMetadata, channel: String, val names: List<String>) : TargetedEvent(metadata, channel)
182 191
 
@@ -252,6 +261,13 @@ class UserHostChanged(metadata: EventMetadata, override val user: User, val newI
252 261
  */
253 262
 class UserAccountChanged(metadata: EventMetadata, override val user: User, val newAccount: String?) : IrcEvent(metadata), SourcedEvent
254 263
 
264
+/**
265
+ * Raised when a user's away state changes. If [message] is null then the user is now back.
266
+ *
267
+ * This event is only raised for other users if the server supports the 'away-notify' capability.
268
+ */
269
+class UserAway(metadata: EventMetadata, override val user: User, val message: String?) : IrcEvent(metadata), SourcedEvent
270
+
255 271
 /** Raised when available server capabilities are received. More batches may follow. */
256 272
 class ServerCapabilitiesReceived(metadata: EventMetadata, val capabilities: Map<String, String>) : IrcEvent(metadata)
257 273
 

+ 10
- 0
src/main/kotlin/com/dmdirc/ktirc/events/mutators/ChannelFanOutMutator.kt View File

@@ -12,11 +12,13 @@ internal class ChannelFanOutMutator : EventMutator {
12 12
     override fun mutateEvent(client: IrcClient, messageEmitter: MessageEmitter, event: IrcEvent) = sequence<IrcEvent> {
13 13
         yield(event)
14 14
         when (event) {
15
+            is UserAway -> handleAway(client, event)
15 16
             is UserQuit -> handleQuit(client, event)
16 17
             is UserNickChanged -> handleNickChanged(client, event)
17 18
         }
18 19
     }.toList()
19 20
 
21
+
20 22
     private suspend fun SequenceScope<IrcEvent>.handleQuit(client: IrcClient, event: UserQuit) {
21 23
         client.channelState.forEach {
22 24
             if (it.users.contains(event.user.nickname)) {
@@ -25,6 +27,14 @@ internal class ChannelFanOutMutator : EventMutator {
25 27
         }
26 28
     }
27 29
 
30
+    private suspend fun SequenceScope<IrcEvent>.handleAway(client: IrcClient, event: UserAway) {
31
+        client.channelState.forEach {
32
+            if (it.users.contains(event.user.nickname)) {
33
+                yield(ChannelAway(event.metadata, event.user, it.name, event.message))
34
+            }
35
+        }
36
+    }
37
+
28 38
     private suspend fun SequenceScope<IrcEvent>.handleNickChanged(client: IrcClient, event: UserNickChanged) {
29 39
         client.channelState.forEach {
30 40
             it.users[event.user.nickname]?.let { chanUser ->

+ 25
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/InternalMessageBuilders.kt View File

@@ -0,0 +1,25 @@
1
+package com.dmdirc.ktirc.messages
2
+
3
+import com.dmdirc.ktirc.IrcClient
4
+
5
+/** Sends a message to ask the server to list capabilities. */
6
+internal fun IrcClient.sendCapabilityList() = send("CAP", "LS", "302")
7
+
8
+/** Sends a message indicating the end of capability negotiation. */
9
+internal fun IrcClient.sendCapabilityEnd() = send("CAP", "END")
10
+
11
+/** Sends a message requesting the specified caps are enabled. */
12
+internal fun IrcClient.sendCapabilityRequest(capabilities: Collection<String>) =
13
+        send("CAP", "REQ", capabilities.joinToString(" "))
14
+
15
+/** Sends the connection password to the server. */
16
+internal fun IrcClient.sendPassword(password: String) = send("PASS", password)
17
+
18
+/** Sends a response to a PING event. */
19
+internal fun IrcClient.sendPong(nonce: ByteArray) = send("PONG", String(nonce))
20
+
21
+/** Sends a message to register a user with the server. */
22
+internal fun IrcClient.sendUser(userName: String, realName: String) = send("USER", userName, "0", "*", realName)
23
+
24
+/** Starts an authentication request. */
25
+internal fun IrcClient.sendAuthenticationMessage(data: String = "+") = send("AUTHENTICATE", data)

+ 5
- 21
src/main/kotlin/com/dmdirc/ktirc/messages/MessageBuilders.kt View File

@@ -3,16 +3,6 @@ package com.dmdirc.ktirc.messages
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.model.MessageTag
5 5
 
6
-/** Sends a message to ask the server to list capabilities. */
7
-internal fun IrcClient.sendCapabilityList() = send("CAP", "LS", "302")
8
-
9
-/** Sends a message indicating the end of capability negotiation. */
10
-internal fun IrcClient.sendCapabilityEnd() = send("CAP", "END")
11
-
12
-/** Sends a message requesting the specified caps are enabled. */
13
-internal fun IrcClient.sendCapabilityRequest(capabilities: Collection<String>) =
14
-        send("CAP", "REQ", capabilities.joinToString(" "))
15
-
16 6
 /** Sends a request to join the given channel. */
17 7
 fun IrcClient.sendJoin(channel: String) = send("JOIN", channel)
18 8
 
@@ -26,11 +16,11 @@ fun IrcClient.sendModeRequest(target: String) = send("MODE", target)
26 16
 /** Sends a request to change to the given nickname. */
27 17
 fun IrcClient.sendNickChange(nick: String) = send("NICK", nick)
28 18
 
29
-/** Sends the connection password to the server. */
30
-internal fun IrcClient.sendPassword(password: String) = send("PASS", password)
31
-
32
-/** Sends a response to a PING event. */
33
-internal fun IrcClient.sendPong(nonce: ByteArray) = send("PONG", String(nonce))
19
+/** Sends a request to set or unset our away state. */
20
+fun IrcClient.sendAway(reason: String? = null) =
21
+        // We need to pass emptyMap() in the second case to avoid using the deprecated send(String) method
22
+        // Once that's removed, the redundant map can be taken out.
23
+        reason?.let { send("AWAY", reason) } ?: send(emptyMap(), "AWAY")
34 24
 
35 25
 /** Sends a CTCP message of the specified [type] and with optional [data] to [target] (a user or a channel). */
36 26
 fun IrcClient.sendCtcp(target: String, type: String, data: String? = null) =
@@ -56,12 +46,6 @@ fun IrcClient.sendTagMessage(target: String, tags: Map<MessageTag, String>, inRe
56 46
     send(inReplyTo?.let { tags + (MessageTag.Reply to inReplyTo) } ?: tags, "TAGMSG", target)
57 47
 }
58 48
 
59
-/** Sends a message to register a user with the server. */
60
-internal fun IrcClient.sendUser(userName: String, realName: String) = send("USER", userName, "0", "*", realName)
61
-
62
-/** Starts an authentication request. */
63
-internal fun IrcClient.sendAuthenticationMessage(data: String = "+") = send("AUTHENTICATE", data)
64
-
65 49
 /**
66 50
  * Utility method for creating a map of tags to avoid type inference problems.
67 51
  */

+ 21
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/processors/AwayProcessor.kt View File

@@ -0,0 +1,21 @@
1
+package com.dmdirc.ktirc.messages.processors
2
+
3
+import com.dmdirc.ktirc.events.UserAway
4
+import com.dmdirc.ktirc.model.IrcMessage
5
+import com.dmdirc.ktirc.model.asUser
6
+
7
+internal class AwayProcessor : MessageProcessor {
8
+
9
+    override val commands = arrayOf("AWAY")
10
+
11
+    override fun process(message: IrcMessage) = sequence {
12
+        when (message.command) {
13
+            "AWAY" -> message.prefix?.let { yield(UserAway(message.metadata, it.asUser(), message.awayMessage)) }
14
+        }
15
+    }.toList()
16
+
17
+    private val IrcMessage.awayMessage: String?
18
+        get() = if (params.isEmpty()) null else String(params[0])
19
+
20
+}
21
+

+ 1
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/processors/MessageProcessor.kt View File

@@ -19,6 +19,7 @@ internal interface MessageProcessor {
19 19
 
20 20
 internal val messageProcessors = listOf(
21 21
         AccountProcessor(),
22
+        AwayProcessor(),
22 23
         AuthenticationProcessor(),
23 24
         BatchProcessor(),
24 25
         CapabilityProcessor(),

+ 36
- 0
src/test/kotlin/com/dmdirc/ktirc/events/mutators/ChannelFanOutMutatorTest.kt View File

@@ -64,6 +64,42 @@ internal class ChannelFanOutMutatorTest {
64 64
         assertTrue("#dumpsterdiving" in names)
65 65
     }
66 66
 
67
+    @Test
68
+    fun `raises ChannelAway event for each channel a user shares when away`() {
69
+        with (ChannelState("#thegibson") { CaseMapping.Rfc }) {
70
+            users += ChannelUser("ZeroCool")
71
+            fakeChannelStateMap += this
72
+        }
73
+
74
+        with (ChannelState("#dumpsterdiving") { CaseMapping.Rfc }) {
75
+            users += ChannelUser("ZeroCool")
76
+            fakeChannelStateMap += this
77
+        }
78
+
79
+        with (ChannelState("#chat") { CaseMapping.Rfc }) {
80
+            users += ChannelUser("AcidBurn")
81
+            fakeChannelStateMap += this
82
+        }
83
+
84
+        val quitEvent = UserAway(EventMetadata(TestConstants.time), User("zerocool", "dade", "root.localhost"), "Hack the planet!")
85
+        val events = mutator.mutateEvent(ircClient, messageEmitter, quitEvent)
86
+
87
+        val names = mutableListOf<String>()
88
+        assertEquals(3, events.size)
89
+        assertSame(quitEvent, events[0])
90
+        events.subList(1, events.size).forEach { event ->
91
+            (event as ChannelAway).let {
92
+                assertEquals(TestConstants.time, it.metadata.time)
93
+                assertEquals("zerocool", it.user.nickname)
94
+                assertEquals("Hack the planet!", it.message)
95
+                names.add(it.target)
96
+            }
97
+        }
98
+
99
+        assertTrue("#thegibson" in names)
100
+        assertTrue("#dumpsterdiving" in names)
101
+    }
102
+
67 103
     @Test
68 104
     fun `raises ChannelNickChanged event for each channel a user changes nicks in`() {
69 105
         with (ChannelState("#thegibson") { CaseMapping.Rfc }) {

+ 12
- 0
src/test/kotlin/com/dmdirc/ktirc/messages/MessageBuildersTest.kt View File

@@ -52,6 +52,18 @@ internal class MessageBuildersTest {
52 52
         verify { mockClient.send("NICK", "AcidBurn") }
53 53
     }
54 54
 
55
+    @Test
56
+    fun `sendAway sends blank AWAY message with no args`() {
57
+        mockClient.sendAway()
58
+        verify { mockClient.send(emptyMap(), "AWAY") }
59
+    }
60
+
61
+    @Test
62
+    fun `sendAway sends AWAY message with reason`() {
63
+        mockClient.sendAway("Hacking the planet")
64
+        verify { mockClient.send("AWAY", "Hacking the planet") }
65
+    }
66
+
55 67
     @Test
56 68
     fun `sendPassword sends correct PASS message`() {
57 69
         mockClient.sendPassword("hacktheplanet")

+ 51
- 0
src/test/kotlin/com/dmdirc/ktirc/messages/processors/AwayProcessorTest.kt View File

@@ -0,0 +1,51 @@
1
+package com.dmdirc.ktirc.messages.processors
2
+
3
+import com.dmdirc.ktirc.TestConstants
4
+import com.dmdirc.ktirc.model.IrcMessage
5
+import com.dmdirc.ktirc.model.User
6
+import com.dmdirc.ktirc.params
7
+import com.dmdirc.ktirc.util.currentTimeProvider
8
+import org.junit.jupiter.api.Assertions
9
+import org.junit.jupiter.api.Assertions.assertEquals
10
+import org.junit.jupiter.api.Assertions.assertNull
11
+import org.junit.jupiter.api.BeforeEach
12
+import org.junit.jupiter.api.Test
13
+
14
+internal class AwayProcessorTest {
15
+
16
+    @BeforeEach
17
+    fun setUp() {
18
+        currentTimeProvider = { TestConstants.time }
19
+    }
20
+
21
+    @Test
22
+    fun `raises away changed event with reason`() {
23
+        val events = AwayProcessor().process(
24
+                IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "AWAY", params("Hacking the planet")))
25
+        assertEquals(1, events.size)
26
+
27
+        val event = events[0]
28
+        assertEquals(TestConstants.time, event.metadata.time)
29
+        assertEquals(User("acidburn", "libby", "root.localhost"), event.user)
30
+        assertEquals("Hacking the planet", event.message)
31
+    }
32
+
33
+    @Test
34
+    fun `raises away changed event with no reason`() {
35
+        val events = AwayProcessor().process(
36
+                IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "AWAY", emptyList()))
37
+        assertEquals(1, events.size)
38
+
39
+        val event = events[0]
40
+        assertEquals(TestConstants.time, event.metadata.time)
41
+        assertEquals(User("acidburn", "libby", "root.localhost"), event.user)
42
+        assertNull(event.message)
43
+    }
44
+
45
+    @Test
46
+    fun `does nothing on away if prefix missing`() {
47
+        val events = AwayProcessor().process(IrcMessage(emptyMap(), null, "AWAY", params("*")))
48
+        Assertions.assertEquals(0, events.size)
49
+    }
50
+
51
+}

Loading…
Cancel
Save