Browse Source

Simplify message building and add reply method

tags/v0.3.0
Chris Smith 5 years ago
parent
commit
dba2f07510

+ 4
- 1
CHANGELOG View File

@@ -1,6 +1,9 @@
1 1
 vNEXT (in development)
2 2
 
3
-  *
3
+  * Simplified how messages are constructed.
4
+    Instead of: client.send(joinMessage("#channel"))
5
+    Now use: client.sendJoin("#channel")
6
+  * Added reply utility to easily send replies to message events
4 7
 
5 8
 v0.2.1
6 9
 

+ 9
- 9
README.md View File

@@ -28,17 +28,17 @@ The main interface for interacting with KtIrc is the `IrcClientImpl` class. A
28 28
 simple bot might look like:
29 29
 
30 30
 ```kotlin
31
-val client = IrcClientImpl(Server("my.server.com", 6667), Profile("nick", "realName", "userName"))
32
-client.onEvent { event ->
33
-    when (event) {
34
-        is ServerWelcome ->
35
-            client.send(joinMessage("#ktirc"))
36
-        is MessageReceived ->
37
-            if (event.message == "!test")
38
-                client.send(privmsgMessage(event.target, "Test successful!"))
31
+with(IrcClientImpl(Server("my.server.com", 6667), Profile("nick", "realName", "userName"))) {
32
+    onEvent { event ->
33
+        when (event) {
34
+            is ServerWelcome -> sendJoin("#ktirc")
35
+            is MessageReceived ->
36
+                if (event.message == "!test")
37
+                    reply(event, "Test successful!")
38
+        }
39 39
     }
40
+    connect()
40 41
 }
41
-client.connect()
42 42
 ```
43 43
 
44 44
 ## Contributing

+ 1
- 6
src/itest/kotlin/com/dmdirc/ktirc/KtIrcIntegrationTest.kt View File

@@ -3,9 +3,6 @@ package com.dmdirc.ktirc
3 3
 import com.dmdirc.irctest.IrcLibraryTests
4 4
 import com.dmdirc.ktirc.model.Profile
5 5
 import com.dmdirc.ktirc.model.Server
6
-import kotlinx.coroutines.Dispatchers
7
-import kotlinx.coroutines.GlobalScope
8
-import kotlinx.coroutines.launch
9 6
 import org.junit.jupiter.api.TestFactory
10 7
 
11 8
 class KtIrcIntegrationTest {
@@ -17,9 +14,7 @@ class KtIrcIntegrationTest {
17 14
 
18 15
         override fun connect(nick: String, ident: String, realName: String, password: String?) {
19 16
             ircClient = IrcClientImpl(Server("localhost", 12321, password = password), Profile(nick, ident, realName))
20
-            GlobalScope.launch(Dispatchers.IO) {
21
-                ircClient.connect()
22
-            }
17
+            ircClient.connect()
23 18
         }
24 19
 
25 20
         override fun terminate() {

+ 13
- 12
src/main/kotlin/com/dmdirc/ktirc/IrcClient.kt View File

@@ -111,10 +111,10 @@ class IrcClientImpl(private val server: Server, private val profile: Profile) :
111 111
                 socket = this
112 112
                 connect()
113 113
                 sendLine("CAP LS 302")
114
-                server.password?.let { pass -> sendLine(passwordMessage(pass)) }
115
-                sendLine(nickMessage(profile.initialNick))
114
+                server.password?.let { pass -> sendPassword(pass) }
115
+                sendNickChange(profile.initialNick)
116 116
                 // TODO: Send correct host
117
-                sendLine(userMessage(profile.userName, "localhost", server.host, profile.realName))
117
+                sendUser(profile.userName, "localhost", server.host, profile.realName)
118 118
                 // TODO: This should be elsewhere
119 119
                 messageHandler.processMessages(this@IrcClientImpl, readLines(scope).map { parser.parse(it) })
120 120
             }
@@ -146,16 +146,17 @@ internal fun main() {
146 146
     }
147 147
 
148 148
     runBlocking {
149
-        val client = IrcClientImpl(Server("testnet.inspircd.org", 6667), Profile("KtIrc", "Kotlin!", "kotlin"))
150
-        client.onEvent { event ->
151
-            when (event) {
152
-                is ServerWelcome -> client.send(joinMessage("#ktirc"))
153
-                is MessageReceived ->
154
-                    if (event.message == "!test")
155
-                        client.send(privmsgMessage(event.target, "Test successful!"))
149
+        with(IrcClientImpl(Server("testnet.inspircd.org", 6667), Profile("KtIrc", "Kotlin!", "kotlin"))) {
150
+            onEvent { event ->
151
+                when (event) {
152
+                    is ServerWelcome -> sendJoin("#ktirc")
153
+                    is MessageReceived ->
154
+                        if (event.message == "!test")
155
+                            reply(event, "Test successful!")
156
+                }
156 157
             }
158
+            connect()
159
+            join()
157 160
         }
158
-        client.connect()
159
-        client.join()
160 161
     }
161 162
 }

+ 5
- 5
src/main/kotlin/com/dmdirc/ktirc/events/CapabilitiesHandler.kt View File

@@ -1,8 +1,8 @@
1 1
 package com.dmdirc.ktirc.events
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4
-import com.dmdirc.ktirc.messages.capabilityEndMessage
5
-import com.dmdirc.ktirc.messages.capabilityRequestMessage
4
+import com.dmdirc.ktirc.messages.sendCapabilityEnd
5
+import com.dmdirc.ktirc.messages.sendCapabilityRequest
6 6
 import com.dmdirc.ktirc.model.CapabilitiesNegotiationState
7 7
 import com.dmdirc.ktirc.model.CapabilitiesState
8 8
 import com.dmdirc.ktirc.model.Capability
@@ -30,12 +30,12 @@ internal class CapabilitiesHandler : EventHandler {
30 30
         with (client.serverState.capabilities) {
31 31
             if (advertisedCapabilities.keys.isEmpty()) {
32 32
                 negotiationState = CapabilitiesNegotiationState.FINISHED
33
-                client.send(capabilityEndMessage())
33
+                client.sendCapabilityEnd()
34 34
             } else {
35 35
                 negotiationState = CapabilitiesNegotiationState.AWAITING_ACK
36 36
                 advertisedCapabilities.keys.map { it.name }.let {
37 37
                     log.info { "Requesting capabilities: ${it.toList()}" }
38
-                    client.send(capabilityRequestMessage(it))
38
+                    client.sendCapabilityRequest(it)
39 39
                 }
40 40
             }
41 41
         }
@@ -47,7 +47,7 @@ internal class CapabilitiesHandler : EventHandler {
47 47
             log.info { "Acknowledged capabilities: ${capabilities.keys.map { it.name }.toList()}" }
48 48
             negotiationState = CapabilitiesNegotiationState.FINISHED
49 49
             enabledCapabilities.putAll(capabilities)
50
-            client.send(capabilityEndMessage())
50
+            client.sendCapabilityEnd()
51 51
         }
52 52
     }
53 53
 

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

@@ -1,6 +1,7 @@
1 1
 package com.dmdirc.ktirc.events
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.messages.sendMessage
4 5
 import com.dmdirc.ktirc.model.ServerFeature
5 6
 import com.dmdirc.ktirc.model.asUser
6 7
 
@@ -12,3 +13,11 @@ internal fun ChannelNamesReceived.toModesAndUsers(client: IrcClient) = sequence
12 13
         }
13 14
     }
14 15
 }.toList()
16
+
17
+fun IrcClient.reply(message: MessageReceived, response: String, prefixWithNickname: Boolean = false) {
18
+    if (caseMapping.areEquivalent(message.target, serverState.localNickname)) {
19
+        sendMessage(message.user.nickname, response)
20
+    } else {
21
+        sendMessage(message.target, if (prefixWithNickname) "${message.user.nickname}: $response" else response)
22
+    }
23
+}

+ 2
- 2
src/main/kotlin/com/dmdirc/ktirc/events/PingHandler.kt View File

@@ -1,13 +1,13 @@
1 1
 package com.dmdirc.ktirc.events
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4
-import com.dmdirc.ktirc.messages.pongMessage
4
+import com.dmdirc.ktirc.messages.sendPong
5 5
 
6 6
 internal class PingHandler : EventHandler {
7 7
 
8 8
     override fun processEvent(client: IrcClient, event: IrcEvent) {
9 9
         when (event) {
10
-            is PingReceived -> client.send(pongMessage(event.nonce))
10
+            is PingReceived -> client.sendPong(event.nonce)
11 11
         }
12 12
     }
13 13
 

+ 18
- 16
src/main/kotlin/com/dmdirc/ktirc/messages/MessageBuilders.kt View File

@@ -1,18 +1,20 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3
-/** Construct a message indicating the end of capability negotiation. */
4
-internal fun capabilityEndMessage() = "CAP END"
5
-/** Construct a message requesting the specified caps are enabled. */
6
-internal fun capabilityRequestMessage(capabilities: List<String>) = "CAP REQ :${capabilities.joinToString(" ")}"
7
-/** Construct a message to join the given channel. */
8
-fun joinMessage(channel: String) = "JOIN :$channel"
9
-/** Construct a message to change to the given nickname. */
10
-fun nickMessage(nick: String) = "NICK :$nick"
11
-/** Construct a message to provide the connection password to the server. */
12
-internal fun passwordMessage(password: String) = "PASS :$password"
13
-/** Construct a message to reply to a PING event. */
14
-internal fun pongMessage(nonce: ByteArray) = "PONG :${String(nonce)}"
15
-/** Construct a message to send a private message to a user or channel. */
16
-fun privmsgMessage(target: String, message: String) = "PRIVMSG $target :$message"
17
-/** Construct a message to register a user with the server. */
18
-internal fun userMessage(userName: String, localHostName: String, serverHostName: String, realName: String) = "USER $userName $localHostName $serverHostName :$realName"
3
+import com.dmdirc.ktirc.IrcClient
4
+
5
+/** Sends a message indicating the end of capability negotiation. */
6
+internal fun IrcClient.sendCapabilityEnd() = send("CAP END")
7
+/** Sends a message requesting the specified caps are enabled. */
8
+internal fun IrcClient.sendCapabilityRequest(capabilities: List<String>) = send("CAP REQ :${capabilities.joinToString(" ")}")
9
+/** Sends a request to join the given channel. */
10
+fun IrcClient.sendJoin(channel: String) = send("JOIN :$channel")
11
+/** Sends a request to change to the given nickname. */
12
+fun IrcClient.sendNickChange(nick: String) = send("NICK :$nick")
13
+/** Sends the connection password to the server. */
14
+internal fun IrcClient.sendPassword(password: String) = send("PASS :$password")
15
+/** Sends a response to a PING event. */
16
+internal fun IrcClient.sendPong(nonce: ByteArray) = send("PONG :${String(nonce)}")
17
+/** Sends a private message to a user or channel. */
18
+fun IrcClient.sendMessage(target: String, message: String) = send("PRIVMSG $target :$message")
19
+/** Sends a message to register a user with the server. */
20
+internal fun IrcClient.sendUser(userName: String, localHostName: String, serverHostName: String, realName: String) = send("USER $userName $localHostName $serverHostName :$realName")

+ 29
- 0
src/test/kotlin/com/dmdirc/ktirc/events/EventUtilsTest.kt View File

@@ -2,11 +2,14 @@ package com.dmdirc.ktirc.events
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.TestConstants
5
+import com.dmdirc.ktirc.io.CaseMapping
5 6
 import com.dmdirc.ktirc.model.ModePrefixMapping
6 7
 import com.dmdirc.ktirc.model.ServerFeature
7 8
 import com.dmdirc.ktirc.model.ServerState
9
+import com.dmdirc.ktirc.model.User
8 10
 import com.nhaarman.mockitokotlin2.doReturn
9 11
 import com.nhaarman.mockitokotlin2.mock
12
+import com.nhaarman.mockitokotlin2.verify
10 13
 import org.junit.jupiter.api.Assertions.assertEquals
11 14
 import org.junit.jupiter.api.BeforeEach
12 15
 import org.junit.jupiter.api.Test
@@ -16,6 +19,7 @@ internal class EventUtilsTest {
16 19
     private val serverState = ServerState("")
17 20
     private val ircClient = mock<IrcClient> {
18 21
         on { serverState } doReturn serverState
22
+        on { caseMapping } doReturn CaseMapping.Ascii
19 23
     }
20 24
 
21 25
     @BeforeEach
@@ -62,5 +66,30 @@ internal class EventUtilsTest {
62 66
         assertEquals("root.localhost", result[1].second.hostname)
63 67
     }
64 68
 
69
+    @Test
70
+    fun `reply sends response to user when message is private`() {
71
+        serverState.localNickname = "zeroCool"
72
+        val message = MessageReceived(TestConstants.time, User("acidBurn"), "Zerocool", "Hack the planet!")
73
+
74
+        ircClient.reply(message, "OK")
75
+        verify(ircClient).send("PRIVMSG acidBurn :OK")
76
+    }
77
+
78
+    @Test
79
+    fun `reply sends unprefixed response to user when message is in a channel`() {
80
+        val message = MessageReceived(TestConstants.time, User("acidBurn"), "#TheGibson", "Hack the planet!")
81
+
82
+        ircClient.reply(message, "OK")
83
+        verify(ircClient).send("PRIVMSG #TheGibson :OK")
84
+    }
85
+
86
+    @Test
87
+    fun `reply sends prefixed response to user when message is in a channel`() {
88
+        val message = MessageReceived(TestConstants.time, User("acidBurn"), "#TheGibson", "Hack the planet!")
89
+
90
+        ircClient.reply(message, "OK", prefixWithNickname = true)
91
+        verify(ircClient).send("PRIVMSG #TheGibson :acidBurn: OK")
92
+    }
93
+
65 94
 
66 95
 }

+ 37
- 9
src/test/kotlin/com/dmdirc/ktirc/messages/MessageBuildersTest.kt View File

@@ -1,32 +1,60 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3
-import org.junit.jupiter.api.Assertions.assertEquals
3
+import com.dmdirc.ktirc.IrcClient
4
+import com.nhaarman.mockitokotlin2.mock
5
+import com.nhaarman.mockitokotlin2.verify
4 6
 import org.junit.jupiter.api.Test
5 7
 
6 8
 internal class MessageBuildersTest {
7 9
 
10
+    private val mockClient = mock<IrcClient>()
11
+
8 12
     @Test
9
-    fun `CapabilityRequestMessage creates CAP REQ message with single argument`() = assertEquals("CAP REQ :a", capabilityRequestMessage(listOf("a")))
13
+    fun `CapabilityRequestMessage creates CAP REQ message with single argument`() {
14
+        mockClient.sendCapabilityRequest(listOf("a"))
15
+        verify(mockClient).send("CAP REQ :a")
16
+    }
10 17
 
11 18
     @Test
12
-    fun `CapabilityRequestMessage creates CAP REQ message with multiple args`() = assertEquals("CAP REQ :a b c", capabilityRequestMessage(listOf("a b c")))
19
+    fun `CapabilityRequestMessage creates CAP REQ message with multiple args`() {
20
+        mockClient.sendCapabilityRequest(listOf("a b c"))
21
+        verify(mockClient).send("CAP REQ :a b c")
22
+    }
13 23
 
14 24
     @Test
15
-    fun `JoinMessage creates correct JOIN message`() = assertEquals("JOIN :#Test123", joinMessage("#Test123"))
25
+    fun `JoinMessage creates correct JOIN message`() {
26
+        mockClient.sendJoin("#TheGibson")
27
+        verify(mockClient).send("JOIN :#TheGibson")
28
+    }
16 29
 
17 30
     @Test
18
-    fun `NickMessage creates correct NICK message`() = assertEquals("NICK :AcidBurn", nickMessage("AcidBurn"))
31
+    fun `NickMessage creates correct NICK message`() {
32
+        mockClient.sendNickChange("AcidBurn")
33
+        verify(mockClient).send("NICK :AcidBurn")
34
+    }
19 35
 
20 36
     @Test
21
-    fun `PasswordMessage creates correct PASS message`() = assertEquals("PASS :abcdef", passwordMessage("abcdef"))
37
+    fun `PasswordMessage creates correct PASS message`() {
38
+        mockClient.sendPassword("hacktheplanet")
39
+        verify(mockClient).send("PASS :hacktheplanet")
40
+    }
22 41
 
23 42
     @Test
24
-    fun `PongMessage creates correct PONG message`() = assertEquals("PONG :abcdef", pongMessage("abcdef".toByteArray()))
43
+    fun `PongMessage creates correct PONG message`() {
44
+        mockClient.sendPong("abcdef".toByteArray())
45
+        verify(mockClient).send("PONG :abcdef")
46
+    }
25 47
 
26 48
     @Test
27
-    fun `PrivmsgMessage creates correct PRIVMSG message`() = assertEquals("PRIVMSG acidBurn :Hack the planet!", privmsgMessage("acidBurn", "Hack the planet!"))
49
+    fun `PrivmsgMessage creates correct PRIVMSG message`() {
50
+        mockClient.sendMessage("acidBurn", "Hack the planet!")
51
+        verify(mockClient).send("PRIVMSG acidBurn :Hack the planet!")
52
+    }
28 53
 
29 54
     @Test
30
-    fun `UserMessage creates correct USER message`() = assertEquals("USER AcidBurn localhost gibson :Kate", userMessage("AcidBurn", "localhost", "gibson", "Kate"))
55
+    fun `UserMessage creates correct USER message`() {
56
+        mockClient.sendUser("AcidBurn", "localhost", "gibson", "Kate")
57
+        verify(mockClient).send("USER AcidBurn localhost gibson :Kate")
58
+    }
31 59
 
32 60
 }

Loading…
Cancel
Save