Browse Source

Add rudimentary PRIVMSG handling

tags/v0.1.0
Chris Smith 5 years ago
parent
commit
36c7acfe2a

+ 19
- 11
src/main/kotlin/com/dmdirc/ktirc/IrcClient.kt View File

@@ -1,9 +1,6 @@
1 1
 package com.dmdirc.ktirc
2 2
 
3
-import com.dmdirc.ktirc.events.EventHandler
4
-import com.dmdirc.ktirc.events.IrcEvent
5
-import com.dmdirc.ktirc.events.ServerWelcome
6
-import com.dmdirc.ktirc.events.eventHandlers
3
+import com.dmdirc.ktirc.events.*
7 4
 import com.dmdirc.ktirc.io.*
8 5
 import com.dmdirc.ktirc.messages.*
9 6
 import com.dmdirc.ktirc.model.*
@@ -24,6 +21,8 @@ interface IrcClient {
24 21
     val caseMapping: CaseMapping
25 22
         get() = serverState.features[ServerFeature.ServerCaseMapping] ?: CaseMapping.Rfc
26 23
 
24
+    var eventHandler: EventHandler?
25
+
27 26
     fun isLocalUser(user: User): Boolean = caseMapping.areEquivalent(user.nickname, serverState.localNickname)
28 27
 
29 28
 }
@@ -38,13 +37,14 @@ class IrcClientImpl(private val server: Server, private val profile: Profile) :
38 37
     override val serverState = ServerState(profile.initialNick)
39 38
     override val channelState = ChannelStateMap { caseMapping }
40 39
 
41
-    private val messageHandler = MessageHandler(messageProcessors, eventHandlers + object : EventHandler {
42
-        override suspend fun processEvent(client: IrcClient, event: IrcEvent) {
43
-            when (event) {
44
-                is ServerWelcome -> client.send(joinMessage("#mdbot"))
45
-            }
40
+    override var eventHandler: EventHandler? = null
41
+        set(value) {
42
+            field?.let { messageHandler.handlers.remove(it) }
43
+            field = value
44
+            field?.let { messageHandler.handlers.add(it) }
46 45
         }
47
-    })
46
+
47
+    private val messageHandler = MessageHandler(messageProcessors.toList(), eventHandlers.toMutableList())
48 48
 
49 49
     private val parser = MessageParser()
50 50
     private var socket: LineBufferedSocket? = null
@@ -81,7 +81,15 @@ class IrcClientImpl(private val server: Server, private val profile: Profile) :
81 81
             }
82 82
 
83 83
             runBlocking {
84
-                val client = IrcClientImpl(Server("irc.quakenet.org", 6667), Profile("KtIrc", "Kotlin!", "kotlin"))
84
+                val client = IrcClientImpl(Server("uk.quakenet.org", 6667), Profile("KtIrc", "Kotlin!", "kotlin"))
85
+                client.eventHandler = object : EventHandler {
86
+                    override suspend fun processEvent(client: IrcClient, event: IrcEvent) {
87
+                        when (event) {
88
+                            is ServerWelcome -> client.send(joinMessage("#mdbot"))
89
+                            is MessageReceived -> if (event.message == "!test") client.send(privmsgMessage(event.target, "Test successful!"))
90
+                        }
91
+                    }
92
+                }
85 93
                 client.connect()
86 94
             }
87 95
         }

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

@@ -31,3 +31,6 @@ data class ChannelNamesReceived(val channel: String, val names: List<String>) :
31 31
 
32 32
 /** Raised when the entirety of the channel's member list has been received. */
33 33
 data class ChannelNamesFinished(val channel: String) : IrcEvent()
34
+
35
+/** Raised when a message is received. */
36
+data class MessageReceived(val user: User, val target: String, val message: String) : IrcEvent()

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

@@ -7,7 +7,7 @@ import com.dmdirc.ktirc.util.logger
7 7
 import kotlinx.coroutines.channels.ReceiveChannel
8 8
 import kotlinx.coroutines.channels.consumeEach
9 9
 
10
-class MessageHandler(private val processors: Collection<MessageProcessor>, private val handlers: Collection<EventHandler>) {
10
+class MessageHandler(private val processors: List<MessageProcessor>, val handlers: MutableList<EventHandler>) {
11 11
 
12 12
     private val log by logger()
13 13
 

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

@@ -9,7 +9,7 @@ import com.dmdirc.ktirc.model.serverFeatures
9 9
 import com.dmdirc.ktirc.util.logger
10 10
 import kotlin.reflect.KClass
11 11
 
12
-class ISupportProcessor : MessageProcessor {
12
+internal class ISupportProcessor : MessageProcessor {
13 13
 
14 14
     private val log by logger()
15 15
 

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

@@ -4,7 +4,7 @@ import com.dmdirc.ktirc.events.ChannelJoined
4 4
 import com.dmdirc.ktirc.io.IrcMessage
5 5
 import com.dmdirc.ktirc.model.asUser
6 6
 
7
-class JoinProcessor : MessageProcessor {
7
+internal class JoinProcessor : MessageProcessor {
8 8
 
9 9
     override val commands = arrayOf("JOIN")
10 10
 

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

@@ -4,4 +4,5 @@ fun joinMessage(channel: String) = "JOIN :$channel"
4 4
 fun nickMessage(nick: String) = "NICK :$nick"
5 5
 fun passwordMessage(password: String) = "PASS :$password"
6 6
 fun pongMessage(nonce: ByteArray) = "PONG :${String(nonce)}"
7
+fun privmsgMessage(target: String, message: String) = "PRIVMSG $target :$message"
7 8
 fun userMessage(userName: String, localHostName: String, serverHostName: String, realName: String) = "USER $userName $localHostName $serverHostName :$realName"

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

@@ -23,5 +23,6 @@ val messageProcessors = setOf(
23 23
         NamesProcessor(),
24 24
         PartProcessor(),
25 25
         PingProcessor(),
26
+        PrivmsgProcessor(),
26 27
         WelcomeProcessor()
27 28
 )

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

@@ -4,7 +4,7 @@ import com.dmdirc.ktirc.events.ChannelNamesFinished
4 4
 import com.dmdirc.ktirc.events.ChannelNamesReceived
5 5
 import com.dmdirc.ktirc.io.IrcMessage
6 6
 
7
-class NamesProcessor : MessageProcessor {
7
+internal class NamesProcessor : MessageProcessor {
8 8
 
9 9
     override val commands = arrayOf("353", "366")
10 10
 

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

@@ -4,7 +4,7 @@ import com.dmdirc.ktirc.events.ChannelParted
4 4
 import com.dmdirc.ktirc.io.IrcMessage
5 5
 import com.dmdirc.ktirc.model.asUser
6 6
 
7
-class PartProcessor : MessageProcessor {
7
+internal class PartProcessor : MessageProcessor {
8 8
 
9 9
     override val commands = arrayOf("PART")
10 10
 

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

@@ -3,7 +3,7 @@ package com.dmdirc.ktirc.messages
3 3
 import com.dmdirc.ktirc.events.PingReceived
4 4
 import com.dmdirc.ktirc.io.IrcMessage
5 5
 
6
-class PingProcessor : MessageProcessor {
6
+internal class PingProcessor : MessageProcessor {
7 7
 
8 8
     override val commands = arrayOf("PING")
9 9
 

+ 15
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/PrivmsgProcessor.kt View File

@@ -0,0 +1,15 @@
1
+package com.dmdirc.ktirc.messages
2
+
3
+import com.dmdirc.ktirc.events.MessageReceived
4
+import com.dmdirc.ktirc.io.IrcMessage
5
+import com.dmdirc.ktirc.model.asUser
6
+
7
+internal class PrivmsgProcessor : MessageProcessor {
8
+
9
+    override val commands = arrayOf("PRIVMSG")
10
+
11
+    override fun process(message: IrcMessage) = message.prefix?.let {
12
+        listOf(MessageReceived(it.asUser(), String(message.params[0]), String(message.params[1])))
13
+    } ?: emptyList()
14
+
15
+}

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

@@ -3,7 +3,7 @@ package com.dmdirc.ktirc.messages
3 3
 import com.dmdirc.ktirc.events.ServerWelcome
4 4
 import com.dmdirc.ktirc.io.IrcMessage
5 5
 
6
-class WelcomeProcessor : MessageProcessor {
6
+internal class WelcomeProcessor : MessageProcessor {
7 7
 
8 8
     override val commands = arrayOf("001")
9 9
 

+ 61
- 0
src/test/kotlin/com/dmdirc/ktirc/IrcClientTest.kt View File

@@ -1,5 +1,7 @@
1 1
 package com.dmdirc.ktirc
2 2
 
3
+import com.dmdirc.ktirc.events.EventHandler
4
+import com.dmdirc.ktirc.events.ServerWelcome
3 5
 import com.dmdirc.ktirc.io.CaseMapping
4 6
 import com.dmdirc.ktirc.io.LineBufferedSocket
5 7
 import com.dmdirc.ktirc.model.Profile
@@ -8,6 +10,7 @@ import com.dmdirc.ktirc.model.ServerFeature
8 10
 import com.dmdirc.ktirc.model.User
9 11
 import com.nhaarman.mockitokotlin2.*
10 12
 import kotlinx.coroutines.channels.Channel
13
+import kotlinx.coroutines.launch
11 14
 import kotlinx.coroutines.runBlocking
12 15
 import org.junit.jupiter.api.Assertions.*
13 16
 import org.junit.jupiter.api.Test
@@ -34,6 +37,8 @@ internal class IrcClientImplTest {
34 37
         on { invoke(HOST, PORT) } doReturn mockSocket
35 38
     }
36 39
 
40
+    private val mockEventHandler = mock<EventHandler>()
41
+
37 42
     @Test
38 43
     fun `IrcClientImpl uses socket factory to create a new socket on connect`() {
39 44
         runBlocking {
@@ -96,6 +101,62 @@ internal class IrcClientImplTest {
96 101
         }
97 102
     }
98 103
 
104
+    @Test
105
+    fun `IrcClientImpl sends events to provided event handler`() {
106
+        runBlocking {
107
+            val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
108
+            client.socketFactory = mockSocketFactory
109
+            client.eventHandler = mockEventHandler
110
+
111
+            launch {
112
+                readLineChannel.send(":the.gibson 001 acidBurn :Welcome to the IRC!".toByteArray())
113
+                readLineChannel.close()
114
+            }
115
+
116
+            client.connect()
117
+
118
+            verify(mockEventHandler).processEvent(client, ServerWelcome("acidBurn"))
119
+        }
120
+    }
121
+
122
+    @Test
123
+    fun `IrcClientImpl removes old event handlers when new one is added`() {
124
+        runBlocking {
125
+            val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
126
+            client.socketFactory = mockSocketFactory
127
+            client.eventHandler = mockEventHandler
128
+            client.eventHandler = mock()
129
+
130
+            launch {
131
+                readLineChannel.send(":the.gibson 001 acidBurn :Welcome to the IRC!".toByteArray())
132
+                readLineChannel.close()
133
+            }
134
+
135
+            client.connect()
136
+
137
+            verify(mockEventHandler, never()).processEvent(client, ServerWelcome("acidBurn"))
138
+        }
139
+    }
140
+
141
+    @Test
142
+    fun `IrcClientImpl removes old event handlers when it is set to null`() {
143
+        runBlocking {
144
+            val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
145
+            client.socketFactory = mockSocketFactory
146
+            client.eventHandler = mockEventHandler
147
+            client.eventHandler = null
148
+
149
+            launch {
150
+                readLineChannel.send(":the.gibson 001 acidBurn :Welcome to the IRC!".toByteArray())
151
+                readLineChannel.close()
152
+            }
153
+
154
+            client.connect()
155
+
156
+            verify(mockEventHandler, never()).processEvent(client, ServerWelcome("acidBurn"))
157
+        }
158
+    }
159
+
99 160
     @Test
100 161
     fun `IrcClient gets case mapping from server features`() {
101 162
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))

+ 3
- 3
src/test/kotlin/com/dmdirc/ktirc/io/MessageHandlerTest.kt View File

@@ -24,7 +24,7 @@ internal class MessageHandlerTest {
24 24
 
25 25
     @Test
26 26
     fun `MessageHandler passes message on to correct processor`() = runBlocking {
27
-        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), emptyList())
27
+        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), mutableListOf())
28 28
         val message = IrcMessage(null, null, "JOIN", emptyList())
29 29
 
30 30
         with(Channel<IrcMessage>(1)) {
@@ -39,7 +39,7 @@ internal class MessageHandlerTest {
39 39
 
40 40
     @Test
41 41
     fun `MessageHandler reads multiple messages`() = runBlocking {
42
-        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), emptyList())
42
+        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), mutableListOf())
43 43
         val joinMessage = IrcMessage(null, null, "JOIN", emptyList())
44 44
         val nickMessage = IrcMessage(null, null, "NICK", emptyList())
45 45
         val otherMessage = IrcMessage(null, null, "OTHER", emptyList())
@@ -63,7 +63,7 @@ internal class MessageHandlerTest {
63 63
     fun `MessageHandler invokes all event handler with all returned events`() = runBlocking {
64 64
         val eventHandler1 = mock<EventHandler>()
65 65
         val eventHandler2 = mock<EventHandler>()
66
-        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), listOf(eventHandler1, eventHandler2))
66
+        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), mutableListOf(eventHandler1, eventHandler2))
67 67
         val joinMessage = IrcMessage(null, null, "JOIN", emptyList())
68 68
         whenever(joinProcessor.process(any())).thenReturn(listOf(ServerConnected, ServerWelcome("abc")))
69 69
 

+ 25
- 0
src/test/kotlin/com/dmdirc/ktirc/messages/PrivmsgProcessorTest.kt View File

@@ -0,0 +1,25 @@
1
+package com.dmdirc.ktirc.messages
2
+
3
+import com.dmdirc.ktirc.events.MessageReceived
4
+import com.dmdirc.ktirc.io.IrcMessage
5
+import com.dmdirc.ktirc.model.User
6
+import org.junit.jupiter.api.Assertions
7
+import org.junit.jupiter.api.Test
8
+
9
+internal class PrivmsgProcessorTest {
10
+
11
+    @Test
12
+    fun `PrivsgProcessor raises message received event`() {
13
+        val events = PrivmsgProcessor().process(
14
+                IrcMessage(null, "acidburn!libby@root.localhost".toByteArray(), "PRIVMSG", listOf("#crashandburn".toByteArray(), "hack the planet!".toByteArray())))
15
+        Assertions.assertEquals(1, events.size)
16
+        Assertions.assertEquals(MessageReceived(User("acidburn", "libby", "root.localhost"), "#crashandburn", "hack the planet!"), events[0])
17
+    }
18
+
19
+    @Test
20
+    fun `PrivsgProcessor does nothing if prefix missing`() {
21
+        val events = PrivmsgProcessor().process(
22
+                IrcMessage(null, null, "PRIVMSG", listOf("#crashandburn".toByteArray(), "hack the planet!".toByteArray())))
23
+        Assertions.assertEquals(0, events.size)
24
+    }
25
+}

Loading…
Cancel
Save