Browse Source

Expose the local user object directly

tags/v1.1.0
Chris Smith 5 years ago
parent
commit
496b3394ac

+ 2
- 0
CHANGELOG View File

4
    * Added sendAway() method
4
    * Added sendAway() method
5
    * Added UserAway event and fanned-out ChannelAway
5
    * Added UserAway event and fanned-out ChannelAway
6
    * Away message is now updated in the UserState
6
    * Away message is now updated in the UserState
7
+ * Added IrcClient.localUser property, returning the local User
8
+ * Deprecated ServerState.localNickname in favour of localUser.
7
  * (Internal) improved the way byte buffers are used to
9
  * (Internal) improved the way byte buffers are used to
8
    reduce array copying and clean up code
10
    reduce array copying and clean up code
9
 
11
 

+ 29
- 2
docs/index.adoc View File

414
 * `Negotiating` - we are logging in, negotiating capabilities, etc
414
 * `Negotiating` - we are logging in, negotiating capabilities, etc
415
 * `Ready` - we are connected and commands may be sent
415
 * `Ready` - we are connected and commands may be sent
416
 
416
 
417
-.serverState.localNickname (String)
417
+.serverState.localNickname (String) [DEPRECATED]
418
 The current nickname we are using on the IRC server. While connecting this
418
 The current nickname we are using on the IRC server. While connecting this
419
 will default to the nickname from the <<User profile>>, but it may be updated
419
 will default to the nickname from the <<User profile>>, but it may be updated
420
 if e.g. the nick is in use or not allowed.
420
 if e.g. the nick is in use or not allowed.
421
 
421
 
422
+[WARNING]
423
+====
424
+This property is deprecated in favour of the <<LocalUser>> property of `IrcClient`.
425
+You should migrate to using `localUser.nickname` in place of `serverSate.localNickname`.
426
+====
427
+
422
 .serverState.serverName (String)
428
 .serverState.serverName (String)
423
 The name the server uses for itself. While connecting this defaults to the
429
 The name the server uses for itself. While connecting this defaults to the
424
 hostname given in the <<Server settings>>, but it will be updated to the
430
 hostname given in the <<Server settings>>, but it will be updated to the
549
 ----
555
 ----
550
 
556
 
551
 The UserState returns a `KnownUser` object which exposes a `details`
557
 The UserState returns a `KnownUser` object which exposes a `details`
552
-property containing the user details, and a `channels` property
558
+property containing the <<User>> details, and a `channels` property
553
 containing the common channel names. You can also use the `in`
559
 containing the common channel names. You can also use the `in`
554
 operator to check if the user is in a channel:
560
 operator to check if the user is in a channel:
555
 
561
 
571
 <3> Returns all common channels we share with the user; will never
577
 <3> Returns all common channels we share with the user; will never
572
     include channels that the KtIrc client is not joined to.
578
     include channels that the KtIrc client is not joined to.
573
 
579
 
580
+==== User
581
+
582
+User objects have the following properties:
583
+
584
+* `nickname` - the current nickname of the user, always set
585
+* `ident` - the ident (username/"gecos") of the user, if known (null otherwise)
586
+* `hostname` - the hostname of the user, if known (null otherwise)
587
+* `account` - the account of the user, if known (null if account unknown, or user not registered)
588
+* `realName` - the real name of the user, if known (null otherwise)
589
+* `awayMessage` - the away message of the user, if known (null if away state unknown, or user not away)
590
+
591
+=== LocalUser
592
+
593
+Contains a <<User>> instance corresponding to our own details on the IRC
594
+network. This is the same instance that would be returned from
595
+`ircClient.userState[nickname]` for the current nickname.
596
+
597
+While connecting this will default to a User with only a nickname, which will
598
+be taken from the <<User profile>>. It will be updated as more information
599
+is received from the IRC server.
600
+
574
 === ChannelState
601
 === ChannelState
575
 
602
 
576
 The ChannelState keeps track of the state for all channels that the client
603
 The ChannelState keeps track of the state for all channels that the client

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

28
      */
28
      */
29
     val userState: UserState
29
     val userState: UserState
30
 
30
 
31
+    /**
32
+     * Details of our user on IRC.
33
+     */
34
+    val localUser: User
35
+
31
     /**
36
     /**
32
      * The configured behaviour of the client.
37
      * The configured behaviour of the client.
33
      */
38
      */
126
      * Utility method to determine if the given user is the one we are connected to IRC as. Should only be used after a
131
      * Utility method to determine if the given user is the one we are connected to IRC as. Should only be used after a
127
      * [com.dmdirc.ktirc.events.ServerReady] event has been received.
132
      * [com.dmdirc.ktirc.events.ServerReady] event has been received.
128
      */
133
      */
129
-    fun isLocalUser(nickname: String) = caseMapping.areEquivalent(nickname, serverState.localNickname)
134
+    fun isLocalUser(nickname: String) = caseMapping.areEquivalent(nickname, localUser.nickname)
130
 
135
 
131
     /**
136
     /**
132
      * Determines if the given [target] appears to be a channel or not. Should only be used after a
137
      * Determines if the given [target] appears to be a channel or not. Should only be used after a

+ 5
- 2
src/main/kotlin/com/dmdirc/ktirc/IrcClientImpl.kt View File

45
 
45
 
46
     override val serverState = ServerState(config.profile.nickname, config.server.host, config.sasl)
46
     override val serverState = ServerState(config.profile.nickname, config.server.host, config.sasl)
47
     override val channelState = ChannelStateMap { caseMapping }
47
     override val channelState = ChannelStateMap { caseMapping }
48
-    override val userState = UserState { caseMapping }
48
+    override val localUser = User(config.profile.nickname)
49
+    override val userState = UserState { caseMapping }.apply { this += localUser }
49
 
50
 
50
     private val messageHandler = MessageHandler(messageProcessors, eventMutators, eventHandlers)
51
     private val messageHandler = MessageHandler(messageProcessors, eventMutators, eventHandlers)
51
     private val messageBuilder = MessageBuilder()
52
     private val messageBuilder = MessageBuilder()
142
         if (command == "PRIVMSG" && behaviour.alwaysEchoMessages && !serverState.capabilities.enabledCapabilities.contains(Capability.EchoMessages)) {
143
         if (command == "PRIVMSG" && behaviour.alwaysEchoMessages && !serverState.capabilities.enabledCapabilities.contains(Capability.EchoMessages)) {
143
             emitEvent(MessageReceived(
144
             emitEvent(MessageReceived(
144
                     EventMetadata(currentTimeProvider()),
145
                     EventMetadata(currentTimeProvider()),
145
-                    userState[serverState.localNickname]?.details ?: User(serverState.localNickname),
146
+                    localUser,
146
                     arguments[0],
147
                     arguments[0],
147
                     arguments[1]
148
                     arguments[1]
148
             ))
149
             ))
163
         serverState.reset()
164
         serverState.reset()
164
         channelState.clear()
165
         channelState.clear()
165
         userState.reset()
166
         userState.reset()
167
+        localUser.reset(config.profile.nickname)
168
+        userState += localUser
166
         socket = null
169
         socket = null
167
         connecting.tryLock()
170
         connecting.tryLock()
168
         connecting.unlock()
171
         connecting.unlock()

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

46
  * Utility to determine whether the given message is to our local user or not.
46
  * Utility to determine whether the given message is to our local user or not.
47
  */
47
  */
48
 internal fun IrcClient.isToMe(message: MessageReceived) =
48
 internal fun IrcClient.isToMe(message: MessageReceived) =
49
-        caseMapping.areEquivalent(message.target, serverState.localNickname)
49
+        caseMapping.areEquivalent(message.target, localUser.nickname)

+ 6
- 0
src/main/kotlin/com/dmdirc/ktirc/events/handlers/UserStateHandler.kt View File

2
 
2
 
3
 import com.dmdirc.ktirc.IrcClient
3
 import com.dmdirc.ktirc.IrcClient
4
 import com.dmdirc.ktirc.events.*
4
 import com.dmdirc.ktirc.events.*
5
+import com.dmdirc.ktirc.model.User
5
 import com.dmdirc.ktirc.model.UserState
6
 import com.dmdirc.ktirc.model.UserState
6
 
7
 
7
 internal class UserStateHandler : EventHandler {
8
 internal class UserStateHandler : EventHandler {
17
             is UserHostChanged -> handleHostChanged(client, event)
18
             is UserHostChanged -> handleHostChanged(client, event)
18
             is UserQuit -> handleQuit(client.userState, event)
19
             is UserQuit -> handleQuit(client.userState, event)
19
             is UserAway -> handleAway(client.userState, event)
20
             is UserAway -> handleAway(client.userState, event)
21
+            is ServerWelcome -> handleServerWelcome(client.localUser, event)
20
         }
22
         }
21
     }
23
     }
22
 
24
 
94
         }
96
         }
95
     }
97
     }
96
 
98
 
99
+    private fun handleServerWelcome(localUser: User, event: ServerWelcome) {
100
+        localUser.nickname = event.localNick
101
+    }
102
+
97
 }
103
 }

+ 3
- 0
src/main/kotlin/com/dmdirc/ktirc/model/ServerState.kt View File

4
 import com.dmdirc.ktirc.events.EventMetadata
4
 import com.dmdirc.ktirc.events.EventMetadata
5
 import com.dmdirc.ktirc.events.IrcEvent
5
 import com.dmdirc.ktirc.events.IrcEvent
6
 import com.dmdirc.ktirc.io.CaseMapping
6
 import com.dmdirc.ktirc.io.CaseMapping
7
+import com.dmdirc.ktirc.util.RemoveIn
7
 import com.dmdirc.ktirc.util.logger
8
 import com.dmdirc.ktirc.util.logger
8
 import kotlinx.coroutines.channels.SendChannel
9
 import kotlinx.coroutines.channels.SendChannel
9
 import java.util.concurrent.atomic.AtomicLong
10
 import java.util.concurrent.atomic.AtomicLong
33
      * in use when connecting. Once you have received a [com.dmdirc.ktirc.events.ServerWelcome] event you can
34
      * in use when connecting. Once you have received a [com.dmdirc.ktirc.events.ServerWelcome] event you can
34
      * rely on this value being current.
35
      * rely on this value being current.
35
      * */
36
      * */
37
+    @RemoveIn("3.0.0")
38
+    @Deprecated("use ircClient.localUser.nickname instead")
36
     var localNickname: String = initialNickname
39
     var localNickname: String = initialNickname
37
         internal set
40
         internal set
38
 
41
 

+ 9
- 0
src/main/kotlin/com/dmdirc/ktirc/model/User.kt View File

19
         other.realName?.let { realName = it }
19
         other.realName?.let { realName = it }
20
         other.awayMessage?.let { awayMessage = it }
20
         other.awayMessage?.let { awayMessage = it }
21
     }
21
     }
22
+
23
+    internal fun reset(newNickname: String) {
24
+        nickname = newNickname
25
+        ident = null
26
+        hostname = null
27
+        account = null
28
+        realName = null
29
+        awayMessage = null
30
+    }
22
 }
31
 }
23
 
32
 
24
 internal fun ByteArray.asUser() = String(this).asUser()
33
 internal fun ByteArray.asUser() = String(this).asUser()

+ 19
- 6
src/test/kotlin/com/dmdirc/ktirc/IrcClientImplTest.kt View File

5
 import com.dmdirc.ktirc.io.LineBufferedSocket
5
 import com.dmdirc.ktirc.io.LineBufferedSocket
6
 import com.dmdirc.ktirc.messages.tagMap
6
 import com.dmdirc.ktirc.messages.tagMap
7
 import com.dmdirc.ktirc.model.*
7
 import com.dmdirc.ktirc.model.*
8
+import com.dmdirc.ktirc.util.RemoveIn
8
 import com.dmdirc.ktirc.util.currentTimeProvider
9
 import com.dmdirc.ktirc.util.currentTimeProvider
9
 import com.dmdirc.ktirc.util.generateLabel
10
 import com.dmdirc.ktirc.util.generateLabel
10
 import io.mockk.*
11
 import io.mockk.*
286
     @Test
287
     @Test
287
     fun `indicates if user is local user or not`() {
288
     fun `indicates if user is local user or not`() {
288
         val client = IrcClientImpl(normalConfig)
289
         val client = IrcClientImpl(normalConfig)
289
-        client.serverState.localNickname = "[acidBurn]"
290
+        client.localUser.nickname = "[acidBurn]"
290
 
291
 
291
         assertTrue(client.isLocalUser(User("{acidBurn}", "libby", "root.localhost")))
292
         assertTrue(client.isLocalUser(User("{acidBurn}", "libby", "root.localhost")))
292
         assertFalse(client.isLocalUser(User("acid-Burn", "libby", "root.localhost")))
293
         assertFalse(client.isLocalUser(User("acid-Burn", "libby", "root.localhost")))
295
     @Test
296
     @Test
296
     fun `indicates if nickname is local user or not`() {
297
     fun `indicates if nickname is local user or not`() {
297
         val client = IrcClientImpl(normalConfig)
298
         val client = IrcClientImpl(normalConfig)
298
-        client.serverState.localNickname = "[acidBurn]"
299
+        client.localUser.nickname = "[acidBurn]"
299
 
300
 
300
         assertTrue(client.isLocalUser("{acidBurn}"))
301
         assertTrue(client.isLocalUser("{acidBurn}"))
301
         assertFalse(client.isLocalUser("acid-Burn"))
302
         assertFalse(client.isLocalUser("acid-Burn"))
304
     @Test
305
     @Test
305
     fun `uses current case mapping to check local user`() {
306
     fun `uses current case mapping to check local user`() {
306
         val client = IrcClientImpl(normalConfig)
307
         val client = IrcClientImpl(normalConfig)
307
-        client.serverState.localNickname = "[acidBurn]"
308
+        client.localUser.nickname = "[acidBurn]"
308
         client.serverState.features[ServerFeature.ServerCaseMapping] = CaseMapping.Ascii
309
         client.serverState.features[ServerFeature.ServerCaseMapping] = CaseMapping.Ascii
309
         assertFalse(client.isLocalUser(User("{acidBurn}", "libby", "root.localhost")))
310
         assertFalse(client.isLocalUser(User("{acidBurn}", "libby", "root.localhost")))
310
     }
311
     }
311
 
312
 
312
     @Test
313
     @Test
313
-    @SuppressWarnings("deprecation")
314
+    @Deprecated("Tests deprecated method")
315
+    @RemoveIn("2.0.0")
314
     fun `sends text to socket`() = runBlocking {
316
     fun `sends text to socket`() = runBlocking {
315
         val client = IrcClientImpl(normalConfig)
317
         val client = IrcClientImpl(normalConfig)
316
         client.socketFactory = mockSocketFactory
318
         client.socketFactory = mockSocketFactory
489
     }
491
     }
490
 
492
 
491
     @Test
493
     @Test
494
+    @Deprecated("Tests deprecated method")
495
+    @RemoveIn("3.0.0")
492
     fun `defaults local nickname to profile`() {
496
     fun `defaults local nickname to profile`() {
493
         val client = IrcClientImpl(normalConfig)
497
         val client = IrcClientImpl(normalConfig)
494
         assertEquals(NICK, client.serverState.localNickname)
498
         assertEquals(NICK, client.serverState.localNickname)
495
     }
499
     }
496
 
500
 
501
+    @Test
502
+    fun `defaults local user to nickname in profile`() {
503
+        val client = IrcClientImpl(normalConfig)
504
+        assertEquals(User(NICK), client.localUser)
505
+    }
506
+
497
     @Test
507
     @Test
498
     fun `defaults server name to host name`() {
508
     fun `defaults server name to host name`() {
499
         val client = IrcClientImpl(normalConfig)
509
         val client = IrcClientImpl(normalConfig)
514
     @Test
524
     @Test
515
     fun `reset clears all state`() {
525
     fun `reset clears all state`() {
516
         with(IrcClientImpl(normalConfig)) {
526
         with(IrcClientImpl(normalConfig)) {
517
-            userState += User("acidBurn")
527
+            userState += User("zeroCool")
518
             channelState += ChannelState("#thegibson") { CaseMapping.Rfc }
528
             channelState += ChannelState("#thegibson") { CaseMapping.Rfc }
519
             serverState.serverName = "root.$HOST"
529
             serverState.serverName = "root.$HOST"
530
+            localUser.awayMessage = "Hacking the planet"
531
+
520
             reset()
532
             reset()
521
 
533
 
522
-            assertEquals(0, userState.count())
534
+            assertEquals(1, userState.count())
523
             assertEquals(0, channelState.count())
535
             assertEquals(0, channelState.count())
524
             assertEquals(HOST, serverState.serverName)
536
             assertEquals(HOST, serverState.serverName)
537
+            assertEquals(User("AcidBurn"), localUser)
525
         }
538
         }
526
     }
539
     }
527
 
540
 

+ 5
- 3
src/test/kotlin/com/dmdirc/ktirc/events/EventUtilsTest.kt View File

15
 internal class EventUtilsTest {
15
 internal class EventUtilsTest {
16
 
16
 
17
     private val fakeServerState = ServerState("", "")
17
     private val fakeServerState = ServerState("", "")
18
+    private val fakeLocalUser = User("")
18
     private val ircClient = mockk<IrcClient> {
19
     private val ircClient = mockk<IrcClient> {
19
         every { serverState } returns fakeServerState
20
         every { serverState } returns fakeServerState
20
         every { caseMapping } returns CaseMapping.Ascii
21
         every { caseMapping } returns CaseMapping.Ascii
22
+        every { localUser } returns fakeLocalUser
21
     }
23
     }
22
 
24
 
23
     @BeforeEach
25
     @BeforeEach
66
 
68
 
67
     @Test
69
     @Test
68
     fun `reply sends response to user when message is private`() {
70
     fun `reply sends response to user when message is private`() {
69
-        fakeServerState.localNickname = "zeroCool"
71
+        fakeLocalUser.nickname = "zeroCool"
70
         val message = MessageReceived(EventMetadata(TestConstants.time), User("acidBurn"), "Zerocool", "Hack the planet!")
72
         val message = MessageReceived(EventMetadata(TestConstants.time), User("acidBurn"), "Zerocool", "Hack the planet!")
71
 
73
 
72
         ircClient.reply(message, "OK")
74
         ircClient.reply(message, "OK")
97
 
99
 
98
     @Test
100
     @Test
99
     fun `reply sends response with message ID to user when message is private`() {
101
     fun `reply sends response with message ID to user when message is private`() {
100
-        fakeServerState.localNickname = "zeroCool"
102
+        fakeLocalUser.nickname = "zeroCool"
101
         val message = MessageReceived(EventMetadata(TestConstants.time, messageId = "abc123"), User("acidBurn"), "Zerocool", "Hack the planet!")
103
         val message = MessageReceived(EventMetadata(TestConstants.time, messageId = "abc123"), User("acidBurn"), "Zerocool", "Hack the planet!")
102
 
104
 
103
         ircClient.reply(message, "OK")
105
         ircClient.reply(message, "OK")
129
 
131
 
130
     @Test
132
     @Test
131
     fun `react sends response to user when message is private`() {
133
     fun `react sends response to user when message is private`() {
132
-        fakeServerState.localNickname = "zeroCool"
134
+        fakeLocalUser.nickname = "zeroCool"
133
         val message = MessageReceived(EventMetadata(TestConstants.time, messageId = "msgId"), User("acidBurn"), "Zerocool", "Hack the planet!")
135
         val message = MessageReceived(EventMetadata(TestConstants.time, messageId = "msgId"), User("acidBurn"), "Zerocool", "Hack the planet!")
134
 
136
 
135
         ircClient.react(message, ":P")
137
         ircClient.react(message, ":P")

+ 11
- 0
src/test/kotlin/com/dmdirc/ktirc/events/handlers/UserStateHandlerTest.kt View File

15
 
15
 
16
     private val fakeServerState = ServerState("", "")
16
     private val fakeServerState = ServerState("", "")
17
     private val fakeUserState = UserState { CaseMapping.Rfc }
17
     private val fakeUserState = UserState { CaseMapping.Rfc }
18
+    private val fakeLocalUser = User("?")
18
 
19
 
19
     private val ircClient = mockk<IrcClient> {
20
     private val ircClient = mockk<IrcClient> {
20
         every { serverState } returns fakeServerState
21
         every { serverState } returns fakeServerState
21
         every { userState } returns fakeUserState
22
         every { userState } returns fakeUserState
22
         every { isLocalUser(any<User>()) } answers { arg<User>(0).nickname == "zeroCool" }
23
         every { isLocalUser(any<User>()) } answers { arg<User>(0).nickname == "zeroCool" }
23
         every { isLocalUser(any<String>()) } answers { arg<String>(0) == "zeroCool" }
24
         every { isLocalUser(any<String>()) } answers { arg<String>(0) == "zeroCool" }
25
+        every { localUser } returns fakeLocalUser
24
     }
26
     }
25
 
27
 
26
     private val handler = UserStateHandler()
28
     private val handler = UserStateHandler()
269
         assertEquals("Hacking the planet", fakeUserState["acidBurn"]?.details?.awayMessage)
271
         assertEquals("Hacking the planet", fakeUserState["acidBurn"]?.details?.awayMessage)
270
     }
272
     }
271
 
273
 
274
+    @Test
275
+    fun `updates local nickname on server welcome`() {
276
+        fakeUserState += User("AcidBurn")
277
+
278
+        handler.processEvent(ircClient, ServerWelcome(EventMetadata(TestConstants.time), "the.gibson", "acidBurn2"))
279
+
280
+        assertEquals("acidBurn2", fakeLocalUser.nickname)
281
+    }
282
+
272
 }
283
 }

+ 7
- 0
src/test/kotlin/com/dmdirc/ktirc/model/UserTest.kt View File

53
         assertEquals("Hacking the planet", user2.awayMessage)
53
         assertEquals("Hacking the planet", user2.awayMessage)
54
     }
54
     }
55
 
55
 
56
+    @Test
57
+    fun `resets all fields`() {
58
+        val user = User("acidBurn", "acidB", "root.localhost", "acidBurn", "Libby", "Hacking the planet!")
59
+        user.reset("burn")
60
+        assertEquals(User("burn"), user)
61
+    }
62
+
56
 }
63
 }

Loading…
Cancel
Save