소스 검색

Simplify message building and add reply method

tags/v0.3.0
Chris Smith 5 년 전
부모
커밋
dba2f07510

+ 4
- 1
CHANGELOG 파일 보기

1
 vNEXT (in development)
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
 v0.2.1
8
 v0.2.1
6
 
9
 

+ 9
- 9
README.md 파일 보기

28
 simple bot might look like:
28
 simple bot might look like:
29
 
29
 
30
 ```kotlin
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
 ## Contributing
44
 ## Contributing

+ 1
- 6
src/itest/kotlin/com/dmdirc/ktirc/KtIrcIntegrationTest.kt 파일 보기

3
 import com.dmdirc.irctest.IrcLibraryTests
3
 import com.dmdirc.irctest.IrcLibraryTests
4
 import com.dmdirc.ktirc.model.Profile
4
 import com.dmdirc.ktirc.model.Profile
5
 import com.dmdirc.ktirc.model.Server
5
 import com.dmdirc.ktirc.model.Server
6
-import kotlinx.coroutines.Dispatchers
7
-import kotlinx.coroutines.GlobalScope
8
-import kotlinx.coroutines.launch
9
 import org.junit.jupiter.api.TestFactory
6
 import org.junit.jupiter.api.TestFactory
10
 
7
 
11
 class KtIrcIntegrationTest {
8
 class KtIrcIntegrationTest {
17
 
14
 
18
         override fun connect(nick: String, ident: String, realName: String, password: String?) {
15
         override fun connect(nick: String, ident: String, realName: String, password: String?) {
19
             ircClient = IrcClientImpl(Server("localhost", 12321, password = password), Profile(nick, ident, realName))
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
         override fun terminate() {
20
         override fun terminate() {

+ 13
- 12
src/main/kotlin/com/dmdirc/ktirc/IrcClient.kt 파일 보기

111
                 socket = this
111
                 socket = this
112
                 connect()
112
                 connect()
113
                 sendLine("CAP LS 302")
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
                 // TODO: Send correct host
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
                 // TODO: This should be elsewhere
118
                 // TODO: This should be elsewhere
119
                 messageHandler.processMessages(this@IrcClientImpl, readLines(scope).map { parser.parse(it) })
119
                 messageHandler.processMessages(this@IrcClientImpl, readLines(scope).map { parser.parse(it) })
120
             }
120
             }
146
     }
146
     }
147
 
147
 
148
     runBlocking {
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 파일 보기

1
 package com.dmdirc.ktirc.events
1
 package com.dmdirc.ktirc.events
2
 
2
 
3
 import com.dmdirc.ktirc.IrcClient
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
 import com.dmdirc.ktirc.model.CapabilitiesNegotiationState
6
 import com.dmdirc.ktirc.model.CapabilitiesNegotiationState
7
 import com.dmdirc.ktirc.model.CapabilitiesState
7
 import com.dmdirc.ktirc.model.CapabilitiesState
8
 import com.dmdirc.ktirc.model.Capability
8
 import com.dmdirc.ktirc.model.Capability
30
         with (client.serverState.capabilities) {
30
         with (client.serverState.capabilities) {
31
             if (advertisedCapabilities.keys.isEmpty()) {
31
             if (advertisedCapabilities.keys.isEmpty()) {
32
                 negotiationState = CapabilitiesNegotiationState.FINISHED
32
                 negotiationState = CapabilitiesNegotiationState.FINISHED
33
-                client.send(capabilityEndMessage())
33
+                client.sendCapabilityEnd()
34
             } else {
34
             } else {
35
                 negotiationState = CapabilitiesNegotiationState.AWAITING_ACK
35
                 negotiationState = CapabilitiesNegotiationState.AWAITING_ACK
36
                 advertisedCapabilities.keys.map { it.name }.let {
36
                 advertisedCapabilities.keys.map { it.name }.let {
37
                     log.info { "Requesting capabilities: ${it.toList()}" }
37
                     log.info { "Requesting capabilities: ${it.toList()}" }
38
-                    client.send(capabilityRequestMessage(it))
38
+                    client.sendCapabilityRequest(it)
39
                 }
39
                 }
40
             }
40
             }
41
         }
41
         }
47
             log.info { "Acknowledged capabilities: ${capabilities.keys.map { it.name }.toList()}" }
47
             log.info { "Acknowledged capabilities: ${capabilities.keys.map { it.name }.toList()}" }
48
             negotiationState = CapabilitiesNegotiationState.FINISHED
48
             negotiationState = CapabilitiesNegotiationState.FINISHED
49
             enabledCapabilities.putAll(capabilities)
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 파일 보기

1
 package com.dmdirc.ktirc.events
1
 package com.dmdirc.ktirc.events
2
 
2
 
3
 import com.dmdirc.ktirc.IrcClient
3
 import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.messages.sendMessage
4
 import com.dmdirc.ktirc.model.ServerFeature
5
 import com.dmdirc.ktirc.model.ServerFeature
5
 import com.dmdirc.ktirc.model.asUser
6
 import com.dmdirc.ktirc.model.asUser
6
 
7
 
12
         }
13
         }
13
     }
14
     }
14
 }.toList()
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 파일 보기

1
 package com.dmdirc.ktirc.events
1
 package com.dmdirc.ktirc.events
2
 
2
 
3
 import com.dmdirc.ktirc.IrcClient
3
 import com.dmdirc.ktirc.IrcClient
4
-import com.dmdirc.ktirc.messages.pongMessage
4
+import com.dmdirc.ktirc.messages.sendPong
5
 
5
 
6
 internal class PingHandler : EventHandler {
6
 internal class PingHandler : EventHandler {
7
 
7
 
8
     override fun processEvent(client: IrcClient, event: IrcEvent) {
8
     override fun processEvent(client: IrcClient, event: IrcEvent) {
9
         when (event) {
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 파일 보기

1
 package com.dmdirc.ktirc.messages
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 파일 보기

2
 
2
 
3
 import com.dmdirc.ktirc.IrcClient
3
 import com.dmdirc.ktirc.IrcClient
4
 import com.dmdirc.ktirc.TestConstants
4
 import com.dmdirc.ktirc.TestConstants
5
+import com.dmdirc.ktirc.io.CaseMapping
5
 import com.dmdirc.ktirc.model.ModePrefixMapping
6
 import com.dmdirc.ktirc.model.ModePrefixMapping
6
 import com.dmdirc.ktirc.model.ServerFeature
7
 import com.dmdirc.ktirc.model.ServerFeature
7
 import com.dmdirc.ktirc.model.ServerState
8
 import com.dmdirc.ktirc.model.ServerState
9
+import com.dmdirc.ktirc.model.User
8
 import com.nhaarman.mockitokotlin2.doReturn
10
 import com.nhaarman.mockitokotlin2.doReturn
9
 import com.nhaarman.mockitokotlin2.mock
11
 import com.nhaarman.mockitokotlin2.mock
12
+import com.nhaarman.mockitokotlin2.verify
10
 import org.junit.jupiter.api.Assertions.assertEquals
13
 import org.junit.jupiter.api.Assertions.assertEquals
11
 import org.junit.jupiter.api.BeforeEach
14
 import org.junit.jupiter.api.BeforeEach
12
 import org.junit.jupiter.api.Test
15
 import org.junit.jupiter.api.Test
16
     private val serverState = ServerState("")
19
     private val serverState = ServerState("")
17
     private val ircClient = mock<IrcClient> {
20
     private val ircClient = mock<IrcClient> {
18
         on { serverState } doReturn serverState
21
         on { serverState } doReturn serverState
22
+        on { caseMapping } doReturn CaseMapping.Ascii
19
     }
23
     }
20
 
24
 
21
     @BeforeEach
25
     @BeforeEach
62
         assertEquals("root.localhost", result[1].second.hostname)
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 파일 보기

1
 package com.dmdirc.ktirc.messages
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
 import org.junit.jupiter.api.Test
6
 import org.junit.jupiter.api.Test
5
 
7
 
6
 internal class MessageBuildersTest {
8
 internal class MessageBuildersTest {
7
 
9
 
10
+    private val mockClient = mock<IrcClient>()
11
+
8
     @Test
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
     @Test
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
     @Test
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
     @Test
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
     @Test
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
     @Test
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
     @Test
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
     @Test
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…
취소
저장