소스 검색

Add rudimentary PRIVMSG handling

tags/v0.1.0
Chris Smith 5 년 전
부모
커밋
36c7acfe2a

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

1
 package com.dmdirc.ktirc
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
 import com.dmdirc.ktirc.io.*
4
 import com.dmdirc.ktirc.io.*
8
 import com.dmdirc.ktirc.messages.*
5
 import com.dmdirc.ktirc.messages.*
9
 import com.dmdirc.ktirc.model.*
6
 import com.dmdirc.ktirc.model.*
24
     val caseMapping: CaseMapping
21
     val caseMapping: CaseMapping
25
         get() = serverState.features[ServerFeature.ServerCaseMapping] ?: CaseMapping.Rfc
22
         get() = serverState.features[ServerFeature.ServerCaseMapping] ?: CaseMapping.Rfc
26
 
23
 
24
+    var eventHandler: EventHandler?
25
+
27
     fun isLocalUser(user: User): Boolean = caseMapping.areEquivalent(user.nickname, serverState.localNickname)
26
     fun isLocalUser(user: User): Boolean = caseMapping.areEquivalent(user.nickname, serverState.localNickname)
28
 
27
 
29
 }
28
 }
38
     override val serverState = ServerState(profile.initialNick)
37
     override val serverState = ServerState(profile.initialNick)
39
     override val channelState = ChannelStateMap { caseMapping }
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
     private val parser = MessageParser()
49
     private val parser = MessageParser()
50
     private var socket: LineBufferedSocket? = null
50
     private var socket: LineBufferedSocket? = null
81
             }
81
             }
82
 
82
 
83
             runBlocking {
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
                 client.connect()
93
                 client.connect()
86
             }
94
             }
87
         }
95
         }

+ 3
- 0
src/main/kotlin/com/dmdirc/ktirc/events/Events.kt 파일 보기

31
 
31
 
32
 /** Raised when the entirety of the channel's member list has been received. */
32
 /** Raised when the entirety of the channel's member list has been received. */
33
 data class ChannelNamesFinished(val channel: String) : IrcEvent()
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 파일 보기

7
 import kotlinx.coroutines.channels.ReceiveChannel
7
 import kotlinx.coroutines.channels.ReceiveChannel
8
 import kotlinx.coroutines.channels.consumeEach
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
     private val log by logger()
12
     private val log by logger()
13
 
13
 

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/ISupportProcessor.kt 파일 보기

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

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/JoinProcessor.kt 파일 보기

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

+ 1
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/MessageBuilders.kt 파일 보기

4
 fun nickMessage(nick: String) = "NICK :$nick"
4
 fun nickMessage(nick: String) = "NICK :$nick"
5
 fun passwordMessage(password: String) = "PASS :$password"
5
 fun passwordMessage(password: String) = "PASS :$password"
6
 fun pongMessage(nonce: ByteArray) = "PONG :${String(nonce)}"
6
 fun pongMessage(nonce: ByteArray) = "PONG :${String(nonce)}"
7
+fun privmsgMessage(target: String, message: String) = "PRIVMSG $target :$message"
7
 fun userMessage(userName: String, localHostName: String, serverHostName: String, realName: String) = "USER $userName $localHostName $serverHostName :$realName"
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 파일 보기

23
         NamesProcessor(),
23
         NamesProcessor(),
24
         PartProcessor(),
24
         PartProcessor(),
25
         PingProcessor(),
25
         PingProcessor(),
26
+        PrivmsgProcessor(),
26
         WelcomeProcessor()
27
         WelcomeProcessor()
27
 )
28
 )

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/NamesProcessor.kt 파일 보기

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

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/PartProcessor.kt 파일 보기

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

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/PingProcessor.kt 파일 보기

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

+ 15
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/PrivmsgProcessor.kt 파일 보기

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 파일 보기

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

+ 61
- 0
src/test/kotlin/com/dmdirc/ktirc/IrcClientTest.kt 파일 보기

1
 package com.dmdirc.ktirc
1
 package com.dmdirc.ktirc
2
 
2
 
3
+import com.dmdirc.ktirc.events.EventHandler
4
+import com.dmdirc.ktirc.events.ServerWelcome
3
 import com.dmdirc.ktirc.io.CaseMapping
5
 import com.dmdirc.ktirc.io.CaseMapping
4
 import com.dmdirc.ktirc.io.LineBufferedSocket
6
 import com.dmdirc.ktirc.io.LineBufferedSocket
5
 import com.dmdirc.ktirc.model.Profile
7
 import com.dmdirc.ktirc.model.Profile
8
 import com.dmdirc.ktirc.model.User
10
 import com.dmdirc.ktirc.model.User
9
 import com.nhaarman.mockitokotlin2.*
11
 import com.nhaarman.mockitokotlin2.*
10
 import kotlinx.coroutines.channels.Channel
12
 import kotlinx.coroutines.channels.Channel
13
+import kotlinx.coroutines.launch
11
 import kotlinx.coroutines.runBlocking
14
 import kotlinx.coroutines.runBlocking
12
 import org.junit.jupiter.api.Assertions.*
15
 import org.junit.jupiter.api.Assertions.*
13
 import org.junit.jupiter.api.Test
16
 import org.junit.jupiter.api.Test
34
         on { invoke(HOST, PORT) } doReturn mockSocket
37
         on { invoke(HOST, PORT) } doReturn mockSocket
35
     }
38
     }
36
 
39
 
40
+    private val mockEventHandler = mock<EventHandler>()
41
+
37
     @Test
42
     @Test
38
     fun `IrcClientImpl uses socket factory to create a new socket on connect`() {
43
     fun `IrcClientImpl uses socket factory to create a new socket on connect`() {
39
         runBlocking {
44
         runBlocking {
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
     @Test
160
     @Test
100
     fun `IrcClient gets case mapping from server features`() {
161
     fun `IrcClient gets case mapping from server features`() {
101
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
162
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))

+ 3
- 3
src/test/kotlin/com/dmdirc/ktirc/io/MessageHandlerTest.kt 파일 보기

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

+ 25
- 0
src/test/kotlin/com/dmdirc/ktirc/messages/PrivmsgProcessorTest.kt 파일 보기

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…
취소
저장