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,6 +8,8 @@ vNEXT (in development)
8 8
    * send(command, arguments) is available if no tags are to be sent
9 9
    * the line is built automatically (with spaces/' :' added appropriately)
10 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 13
  * (Internal) Added annotation to track removal of deprecated methods
12 14
 
13 15
 v0.9.0

+ 1
- 0
build.gradle.kts View File

@@ -43,6 +43,7 @@ dependencies {
43 43
     testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0")
44 44
     testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.0")
45 45
     testImplementation("org.junit.jupiter:junit-jupiter-params:5.4.0")
46
+    testImplementation("io.mockk:mockk:1.9")
46 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,6 +33,7 @@ internal data class IrcClientConfig(
33 33
  *
34 34
  * behaviour {
35 35
  *     requestModesOnJoin = true
36
+ *     alwaysEchoMessages = true
36 37
  * }
37 38
  *
38 39
  * sasl {
@@ -207,4 +208,5 @@ class SaslConfig {
207 208
 @IrcClientDsl
208 209
 class BehaviourConfig : ClientBehaviour {
209 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,6 +129,15 @@ interface ClientBehaviour {
129 129
     /** Whether or not to request channel modes when we join a channel. */
130 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,12 +52,14 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : IrcClient, C
52 52
     }
53 53
 
54 54
     override fun send(tags: Map<MessageTag, String>, command: String, vararg arguments: String) {
55
+        maybeEchoMessage(command, arguments)
55 56
         socket?.sendChannel?.offer(messageBuilder.build(tags, command, arguments))
56 57
                 ?: log.warning { "No send channel for command: $command" }
57 58
     }
58 59
 
59 60
     // TODO: This will become sendAsync and return a Deferred<IrcEvent>
60 61
     internal fun sendWithLabel(tags: Map<MessageTag, String>, command: String, vararg arguments: String) {
62
+        maybeEchoMessage(command, arguments)
61 63
         val tagseToSend = if (Capability.LabeledResponse in serverState.capabilities.enabledCapabilities) {
62 64
             tags + (MessageTag.Label to generateLabel(this))
63 65
         } else {
@@ -104,6 +106,19 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : IrcClient, C
104 106
     private fun emitEvent(event: IrcEvent) = messageHandler.handleEvent(this, event)
105 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 122
     internal fun reset() {
108 123
         serverState.reset()
109 124
         channelState.clear()

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

@@ -9,6 +9,7 @@ import com.dmdirc.ktirc.util.currentTimeProvider
9 9
 import com.dmdirc.ktirc.util.generateLabel
10 10
 import com.nhaarman.mockitokotlin2.*
11 11
 import io.ktor.util.KtorExperimentalAPI
12
+import io.mockk.*
12 13
 import kotlinx.coroutines.*
13 14
 import kotlinx.coroutines.channels.Channel
14 15
 import kotlinx.coroutines.channels.filter
@@ -55,10 +56,12 @@ internal class IrcClientImplTest {
55 56
         username = USER_NAME
56 57
     }
57 58
 
58
-    private val normalConfig = IrcClientConfig(ServerConfig().apply {
59
+    private val serverConfig = ServerConfig().apply {
59 60
         host = HOST
60 61
         port = PORT
61
-    }, profileConfig, BehaviourConfig(), null)
62
+    }
63
+
64
+    private val normalConfig = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig(), null)
62 65
 
63 66
     @BeforeEach
64 67
     fun setUp() {
@@ -190,6 +193,7 @@ internal class IrcClientImplTest {
190 193
     }
191 194
 
192 195
     @Test
196
+    @SuppressWarnings("deprecation")
193 197
     fun `sends text to socket`() = runBlocking {
194 198
         val client = IrcClientImpl(normalConfig)
195 199
         client.socketFactory = mockSocketFactory
@@ -211,6 +215,56 @@ internal class IrcClientImplTest {
211 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 268
     @Test
215 269
     fun `sends structured text to socket with tags`() = runBlocking {
216 270
         val client = IrcClientImpl(normalConfig)

Loading…
Cancel
Save