Browse Source

Add always echo messages behaviour

tags/v0.10.0
Chris Smith 5 years ago
parent
commit
be2ed37acf

+ 2
- 0
CHANGELOG View File

8
    * send(command, arguments) is available if no tags are to be sent
8
    * send(command, arguments) is available if no tags are to be sent
9
    * the line is built automatically (with spaces/' :' added appropriately)
9
    * the line is built automatically (with spaces/' :' added appropriately)
10
    * send(line) is deprecated and will be removed after v1.0.0
10
    * send(line) is deprecated and will be removed after v1.0.0
11
+ * Add 'alwaysEchoMessages' behaviour that makes it easier to deal with servers
12
+   that don't support the echo message capability
11
  * (Internal) Added annotation to track removal of deprecated methods
13
  * (Internal) Added annotation to track removal of deprecated methods
12
 
14
 
13
 v0.9.0
15
 v0.9.0

+ 1
- 0
build.gradle.kts View File

43
     testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0")
43
     testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0")
44
     testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.0")
44
     testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.0")
45
     testImplementation("org.junit.jupiter:junit-jupiter-params:5.4.0")
45
     testImplementation("org.junit.jupiter:junit-jupiter-params:5.4.0")
46
+    testImplementation("io.mockk:mockk:1.9")
46
     testRuntime("org.junit.jupiter:junit-jupiter-engine:5.4.0")
47
     testRuntime("org.junit.jupiter:junit-jupiter-engine:5.4.0")
47
 }
48
 }
48
 
49
 

+ 2
- 0
src/main/kotlin/com/dmdirc/ktirc/Dsl.kt View File

33
  *
33
  *
34
  * behaviour {
34
  * behaviour {
35
  *     requestModesOnJoin = true
35
  *     requestModesOnJoin = true
36
+ *     alwaysEchoMessages = true
36
  * }
37
  * }
37
  *
38
  *
38
  * sasl {
39
  * sasl {
207
 @IrcClientDsl
208
 @IrcClientDsl
208
 class BehaviourConfig : ClientBehaviour {
209
 class BehaviourConfig : ClientBehaviour {
209
     override var requestModesOnJoin = false
210
     override var requestModesOnJoin = false
211
+    override var alwaysEchoMessages = false
210
 }
212
 }

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

129
     /** Whether or not to request channel modes when we join a channel. */
129
     /** Whether or not to request channel modes when we join a channel. */
130
     val requestModesOnJoin: Boolean
130
     val requestModesOnJoin: Boolean
131
 
131
 
132
+    /**
133
+     * If enabled, all messages (`PRIVMSG`s) sent by the client will always be "echoed" back as a MessageReceived
134
+     * event.
135
+     *
136
+     * This makes the behaviour consistent across ircds that support the echo-message capability and those that
137
+     * don't. If disabled, messages will only be echoed back when the server supports the capability.
138
+     */
139
+    val alwaysEchoMessages: Boolean
140
+
132
 }
141
 }
133
 
142
 
134
 /**
143
 /**

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

52
     }
52
     }
53
 
53
 
54
     override fun send(tags: Map<MessageTag, String>, command: String, vararg arguments: String) {
54
     override fun send(tags: Map<MessageTag, String>, command: String, vararg arguments: String) {
55
+        maybeEchoMessage(command, arguments)
55
         socket?.sendChannel?.offer(messageBuilder.build(tags, command, arguments))
56
         socket?.sendChannel?.offer(messageBuilder.build(tags, command, arguments))
56
                 ?: log.warning { "No send channel for command: $command" }
57
                 ?: log.warning { "No send channel for command: $command" }
57
     }
58
     }
58
 
59
 
59
     // TODO: This will become sendAsync and return a Deferred<IrcEvent>
60
     // TODO: This will become sendAsync and return a Deferred<IrcEvent>
60
     internal fun sendWithLabel(tags: Map<MessageTag, String>, command: String, vararg arguments: String) {
61
     internal fun sendWithLabel(tags: Map<MessageTag, String>, command: String, vararg arguments: String) {
62
+        maybeEchoMessage(command, arguments)
61
         val tagseToSend = if (Capability.LabeledResponse in serverState.capabilities.enabledCapabilities) {
63
         val tagseToSend = if (Capability.LabeledResponse in serverState.capabilities.enabledCapabilities) {
62
             tags + (MessageTag.Label to generateLabel(this))
64
             tags + (MessageTag.Label to generateLabel(this))
63
         } else {
65
         } else {
104
     private fun emitEvent(event: IrcEvent) = messageHandler.handleEvent(this, event)
106
     private fun emitEvent(event: IrcEvent) = messageHandler.handleEvent(this, event)
105
     private fun sendPasswordIfPresent() = config.server.password?.let(this::sendPassword)
107
     private fun sendPasswordIfPresent() = config.server.password?.let(this::sendPassword)
106
 
108
 
109
+    private fun maybeEchoMessage(command: String, arguments: Array<out String>) {
110
+        // TODO: Is this the best place to do it? It'd be nicer to actually build the message and
111
+        //       reflect the raw line back through all the processors etc.
112
+        if (command == "PRIVMSG" && behaviour.alwaysEchoMessages && !serverState.capabilities.enabledCapabilities.contains(Capability.EchoMessages)) {
113
+            emitEvent(MessageReceived(
114
+                    EventMetadata(currentTimeProvider()),
115
+                    userState[serverState.localNickname]?.details ?: User(serverState.localNickname),
116
+                    arguments[0],
117
+                    arguments[1]
118
+            ))
119
+        }
120
+    }
121
+
107
     internal fun reset() {
122
     internal fun reset() {
108
         serverState.reset()
123
         serverState.reset()
109
         channelState.clear()
124
         channelState.clear()

+ 56
- 2
src/test/kotlin/com/dmdirc/ktirc/IrcClientImplTest.kt View File

9
 import com.dmdirc.ktirc.util.generateLabel
9
 import com.dmdirc.ktirc.util.generateLabel
10
 import com.nhaarman.mockitokotlin2.*
10
 import com.nhaarman.mockitokotlin2.*
11
 import io.ktor.util.KtorExperimentalAPI
11
 import io.ktor.util.KtorExperimentalAPI
12
+import io.mockk.*
12
 import kotlinx.coroutines.*
13
 import kotlinx.coroutines.*
13
 import kotlinx.coroutines.channels.Channel
14
 import kotlinx.coroutines.channels.Channel
14
 import kotlinx.coroutines.channels.filter
15
 import kotlinx.coroutines.channels.filter
55
         username = USER_NAME
56
         username = USER_NAME
56
     }
57
     }
57
 
58
 
58
-    private val normalConfig = IrcClientConfig(ServerConfig().apply {
59
+    private val serverConfig = ServerConfig().apply {
59
         host = HOST
60
         host = HOST
60
         port = PORT
61
         port = PORT
61
-    }, profileConfig, BehaviourConfig(), null)
62
+    }
63
+
64
+    private val normalConfig = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig(), null)
62
 
65
 
63
     @BeforeEach
66
     @BeforeEach
64
     fun setUp() {
67
     fun setUp() {
190
     }
193
     }
191
 
194
 
192
     @Test
195
     @Test
196
+    @SuppressWarnings("deprecation")
193
     fun `sends text to socket`() = runBlocking {
197
     fun `sends text to socket`() = runBlocking {
194
         val client = IrcClientImpl(normalConfig)
198
         val client = IrcClientImpl(normalConfig)
195
         client.socketFactory = mockSocketFactory
199
         client.socketFactory = mockSocketFactory
211
         assertLineReceived("testing 123 456")
215
         assertLineReceived("testing 123 456")
212
     }
216
     }
213
 
217
 
218
+    @Test
219
+    fun `echoes message event when behaviour is set and cap is unsupported`() = runBlocking {
220
+        val config = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig().apply { alwaysEchoMessages = true }, null)
221
+        val client = IrcClientImpl(config)
222
+        client.socketFactory = mockSocketFactory
223
+
224
+        val slot = slot<MessageReceived>()
225
+        val mockkEventHandler = mockk<(IrcEvent) -> Unit>(relaxed = true)
226
+        every { mockkEventHandler(capture(slot)) } just Runs
227
+
228
+        client.onEvent(mockkEventHandler)
229
+        client.connect()
230
+
231
+        client.send("PRIVMSG", "#thegibson", "Mess with the best, die like the rest")
232
+
233
+        assertTrue(slot.isCaptured)
234
+        val event = slot.captured
235
+        assertEquals("#thegibson", event.target)
236
+        assertEquals("Mess with the best, die like the rest", event.message)
237
+        assertEquals(NICK, event.user.nickname)
238
+        assertEquals(TestConstants.time, event.metadata.time)
239
+    }
240
+
241
+    @Test
242
+    fun `does not echo message event when behaviour is set and cap is supported`() = runBlocking {
243
+        val config = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig().apply { alwaysEchoMessages = true }, null)
244
+        val client = IrcClientImpl(config)
245
+        client.socketFactory = mockSocketFactory
246
+        client.serverState.capabilities.enabledCapabilities[Capability.EchoMessages] = ""
247
+        client.connect()
248
+
249
+        client.onEvent(mockEventHandler)
250
+        client.send("PRIVMSG", "#thegibson", "Mess with the best, die like the rest")
251
+
252
+        verify(mockEventHandler, never()).invoke(isA<MessageReceived>())
253
+    }
254
+
255
+    @Test
256
+    fun `does not echo message event when behaviour is unset`() = runBlocking {
257
+        val config = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig().apply { alwaysEchoMessages = false }, null)
258
+        val client = IrcClientImpl(config)
259
+        client.socketFactory = mockSocketFactory
260
+        client.connect()
261
+
262
+        client.onEvent(mockEventHandler)
263
+        client.send("PRIVMSG", "#thegibson", "Mess with the best, die like the rest")
264
+
265
+        verify(mockEventHandler, never()).invoke(isA<MessageReceived>())
266
+    }
267
+
214
     @Test
268
     @Test
215
     fun `sends structured text to socket with tags`() = runBlocking {
269
     fun `sends structured text to socket with tags`() = runBlocking {
216
         val client = IrcClientImpl(normalConfig)
270
         val client = IrcClientImpl(normalConfig)

Loading…
Cancel
Save