You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IrcClient.kt 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package com.dmdirc.ktirc
  2. import com.dmdirc.ktirc.events.IrcEvent
  3. import com.dmdirc.ktirc.io.CaseMapping
  4. import com.dmdirc.ktirc.messages.sendJoin
  5. import com.dmdirc.ktirc.messages.tagMap
  6. import com.dmdirc.ktirc.model.*
  7. import com.dmdirc.ktirc.util.RemoveIn
  8. import kotlinx.coroutines.Deferred
  9. /**
  10. * Primary interface for interacting with KtIrc.
  11. */
  12. interface IrcClient {
  13. /**
  14. * Holds state relating to the current server, its features, and capabilities.
  15. */
  16. val serverState: ServerState
  17. /**
  18. * Holds the state for each channel we are currently joined to.
  19. */
  20. val channelState: ChannelStateMap
  21. /**
  22. * Holds the state for all known users (those in common channels).
  23. */
  24. val userState: UserState
  25. /**
  26. * Details of our user on IRC.
  27. */
  28. val localUser: User
  29. /**
  30. * The configured behaviour of the client.
  31. */
  32. val behaviour: ClientBehaviour
  33. /**
  34. * The current case-mapping of the server, defining how uppercase and lowercase nicks/channels/etc are mapped
  35. * to one another.
  36. *
  37. * This may change over the lifetime of an [IrcClient]. It should not be stored, and should be checked each
  38. * time it is needed.
  39. */
  40. val caseMapping: CaseMapping
  41. get() = serverState.features[ServerFeature.ServerCaseMapping] ?: CaseMapping.Rfc
  42. /**
  43. * Begins a connection attempt to the IRC server.
  44. *
  45. * This method will return immediately, and the attempt to connect will be executed in a coroutine on the
  46. * IO scheduler. To check the status of the connection, monitor events using [onEvent].
  47. */
  48. fun connect()
  49. /**
  50. * Disconnect immediately from the IRC server, without sending a QUIT.
  51. */
  52. fun disconnect()
  53. /**
  54. * Sends the given raw line to the IRC server, followed by a carriage return and line feed.
  55. *
  56. * Standard IRC messages can be constructed using the methods in [com.dmdirc.ktirc.messages]
  57. * such as [sendJoin].
  58. *
  59. * @param message The line to be sent to the IRC server.
  60. */
  61. @Deprecated("Use structured send instead", ReplaceWith("send(command, arguments)"))
  62. @RemoveIn("2.0.0")
  63. fun send(message: String)
  64. /**
  65. * Sends the given command to the IRC server.
  66. *
  67. * This should only be needed to send raw/custom commands; standard messages can be sent using the
  68. * extension methods in [com.dmdirc.ktirc.messages] such as [sendJoin].
  69. *
  70. * This method will return immediately; the message will be delivered by a coroutine. Messages
  71. * are guaranteed to be delivered in order when this method is called multiple times.
  72. *
  73. * @param tags The IRCv3 tags to prefix the message with, if any.
  74. * @param command The command to be sent.
  75. * @param arguments The arguments to the command.
  76. */
  77. fun send(tags: Map<MessageTag, String>, command: String, vararg arguments: String)
  78. /**
  79. * Sends the given command to the IRC server.
  80. *
  81. * This should only be needed to send raw/custom commands; standard messages can be sent using the
  82. * extension methods in [com.dmdirc.ktirc.messages] such as [sendJoin].
  83. *
  84. * This method will return immediately; the message will be delivered by a coroutine. Messages
  85. * are guaranteed to be delivered in order when this method is called multiple times.
  86. *
  87. * @param command The command to be sent.
  88. * @param arguments The arguments to the command.
  89. */
  90. fun send(command: String, vararg arguments: String) = send(emptyMap(), command, *arguments)
  91. /**
  92. * Registers a new handler for all events on this connection.
  93. *
  94. * All events are subclasses of [IrcEvent]; the idiomatic way to handle them is using a `when` statement:
  95. *
  96. * ```
  97. * client.onEvent {
  98. * when(it) {
  99. * is MessageReceived -> println(it.message)
  100. * }
  101. * }
  102. * ```
  103. *
  104. * *Note*: at present handlers cannot be removed; they last the lifetime of the [IrcClient].
  105. *
  106. * @param handler The method to call when a new event occurs.
  107. */
  108. fun onEvent(handler: (IrcEvent) -> Unit)
  109. /**
  110. * Utility method to determine if the given user is the one we are connected to IRC as. Should only be used after a
  111. * [com.dmdirc.ktirc.events.ServerReady] event has been received.
  112. */
  113. fun isLocalUser(user: User) = isLocalUser(user.nickname)
  114. /**
  115. * Utility method to determine if the given user is the one we are connected to IRC as. Should only be used after a
  116. * [com.dmdirc.ktirc.events.ServerReady] event has been received.
  117. */
  118. fun isLocalUser(nickname: String) = caseMapping.areEquivalent(nickname, localUser.nickname)
  119. /**
  120. * Determines if the given [target] appears to be a channel or not. Should only be used after a
  121. * [com.dmdirc.ktirc.events.ServerReady] event has been received.
  122. */
  123. fun isChannel(target: String) = target.isNotEmpty() && serverState.channelTypes.contains(target[0])
  124. }
  125. internal interface ExperimentalIrcClient : IrcClient {
  126. /**
  127. * Sends the given command to the IRC server, and waits for a response back.
  128. *
  129. * This should only be needed to send raw/custom commands; standard messages can be sent using the
  130. * extension methods in [com.dmdirc.ktirc.messages] such as [com.dmdirc.ktirc.messages.sendPartAsync].
  131. *
  132. * This method will return immediately. The returned [Deferred] will eventually be populated with
  133. * the server's response. If the server supports the labeled-responses capability, a label will
  134. * be added to the outgoing message to identify the correct response; otherwise the [matcher]
  135. * will be invoked on all incoming events to select the appropriate response.
  136. *
  137. * If the response times out, `null` will be supplied instead of an event.
  138. *
  139. * @param command The command to be sent.
  140. * @param arguments The arguments to the command.
  141. * @param matcher The matcher to use to find a matching event.
  142. * @return A deferred [IrcEvent]? that contains the server's response to the command.
  143. */
  144. fun sendAsync(command: String, arguments: Array<String>, matcher: (IrcEvent) -> Boolean) = sendAsync(tagMap(), command, arguments, matcher)
  145. /**
  146. * Sends the given command to the IRC server, and waits for a response back.
  147. *
  148. * This should only be needed to send raw/custom commands; standard messages can be sent using the
  149. * extension methods in [com.dmdirc.ktirc.messages] such as [com.dmdirc.ktirc.messages.sendPartAsync].
  150. *
  151. * This method will return immediately. The returned [Deferred] will eventually be populated with
  152. * the server's response. If the server supports the labeled-responses capability, a label will
  153. * be added to the outgoing message to identify the correct response; otherwise the [matcher]
  154. * will be invoked on all incoming events to select the appropriate response.
  155. *
  156. * If the response times out, `null` will be supplied instead of an event.
  157. *
  158. * @param tags The IRCv3 tags to prefix the message with, if any.
  159. * @param command The command to be sent.
  160. * @param arguments The arguments to the command.
  161. * @param matcher The matcher to use to find a matching event.
  162. * @return A deferred [IrcEvent]? that contains the server's response to the command.
  163. */
  164. fun sendAsync(tags: Map<MessageTag, String>, command: String, arguments: Array<String>, matcher: (IrcEvent) -> Boolean): Deferred<IrcEvent?>
  165. }
  166. /**
  167. * Defines the behaviour of an [IrcClient].
  168. */
  169. interface ClientBehaviour {
  170. /** Whether or not to request channel modes when we join a channel. */
  171. val requestModesOnJoin: Boolean
  172. /**
  173. * If enabled, all messages (`PRIVMSG`s) sent by the client will always be "echoed" back as a MessageReceived
  174. * event.
  175. *
  176. * This makes the behaviour consistent across ircds that support the echo-message capability and those that
  177. * don't. If disabled, messages will only be echoed back when the server supports the capability.
  178. */
  179. val alwaysEchoMessages: Boolean
  180. /**
  181. * If enabled, KtIRC will try to connect to IRC servers over IPv6 if they publish the appropriate DNS entries.
  182. *
  183. * Otherwise, KtIrc will prefer IPv4.
  184. */
  185. val preferIPv6: Boolean
  186. }
  187. /**
  188. * Constructs a new [IrcClient] using a configuration DSL.
  189. *
  190. * See [IrcClientConfigBuilder] for details of all options
  191. */
  192. @IrcClientDsl
  193. @Suppress("FunctionName")
  194. fun IrcClient(block: IrcClientConfigBuilder.() -> Unit): IrcClient =
  195. IrcClientImpl(IrcClientConfigBuilder().apply(block).build())