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 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package com.dmdirc.ktirc
  2. import com.dmdirc.ktirc.events.EventHandler
  3. import com.dmdirc.ktirc.events.IrcEvent
  4. import com.dmdirc.ktirc.events.ServerWelcome
  5. import com.dmdirc.ktirc.events.eventHandlers
  6. import com.dmdirc.ktirc.io.*
  7. import com.dmdirc.ktirc.messages.*
  8. import com.dmdirc.ktirc.model.*
  9. import kotlinx.coroutines.channels.map
  10. import kotlinx.coroutines.coroutineScope
  11. import kotlinx.coroutines.runBlocking
  12. import java.util.logging.Level
  13. import java.util.logging.LogManager
  14. interface IrcClient {
  15. suspend fun send(message: String)
  16. val serverState: ServerState
  17. val channelState: ChannelStateMap
  18. val caseMapping: CaseMapping
  19. get() = serverState.features[ServerFeature.ServerCaseMapping] ?: CaseMapping.Rfc
  20. fun isLocalUser(user: User): Boolean = caseMapping.areEquivalent(user.nickname, serverState.localNickname)
  21. }
  22. // TODO: How should alternative nicknames work?
  23. // TODO: Should IRC Client take a pool of servers and rotate through, or make the caller do that?
  24. // TODO: Should there be a default profile?
  25. class IrcClientImpl(private val server: Server, private val profile: Profile) : IrcClient {
  26. var socketFactory: (String, Int) -> LineBufferedSocket = ::KtorLineBufferedSocket
  27. override val serverState = ServerState(profile.initialNick)
  28. override val channelState = ChannelStateMap { caseMapping }
  29. private val messageHandler = MessageHandler(messageProcessors, eventHandlers + object : EventHandler {
  30. override suspend fun processEvent(client: IrcClient, event: IrcEvent) {
  31. when (event) {
  32. is ServerWelcome -> client.send(joinMessage("#mdbot"))
  33. }
  34. }
  35. })
  36. private val parser = MessageParser()
  37. private var socket: LineBufferedSocket? = null
  38. override suspend fun send(message: String) {
  39. socket?.sendLine(message)
  40. }
  41. suspend fun connect() {
  42. // TODO: Concurrency!
  43. check(socket == null)
  44. coroutineScope {
  45. with(socketFactory(server.host, server.port)) {
  46. socket = this
  47. connect()
  48. // TODO: CAP LS
  49. server.password?.let { pass -> sendLine(passwordMessage(pass)) }
  50. sendLine(nickMessage(profile.initialNick))
  51. // TODO: Send correct host
  52. sendLine(userMessage(profile.userName, "localhost", server.host, profile.realName))
  53. // TODO: This should be elsewhere
  54. messageHandler.processMessages(this@IrcClientImpl, readLines(this@coroutineScope).map { parser.parse(it) })
  55. }
  56. }
  57. }
  58. companion object {
  59. @JvmStatic
  60. fun main(args: Array<String>) {
  61. val rootLogger = LogManager.getLogManager().getLogger("")
  62. rootLogger.level = Level.FINEST
  63. for (h in rootLogger.handlers) {
  64. h.level = Level.FINEST
  65. }
  66. runBlocking {
  67. val client = IrcClientImpl(Server("irc.quakenet.org", 6667), Profile("KtIrc", "Kotlin!", "kotlin"))
  68. client.connect()
  69. }
  70. }
  71. }
  72. }