123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- package com.dmdirc.ktirc
-
- import com.dmdirc.ktirc.events.*
- import com.dmdirc.ktirc.io.*
- import com.dmdirc.ktirc.messages.*
- import com.dmdirc.ktirc.model.*
- import kotlinx.coroutines.*
- import kotlinx.coroutines.channels.map
- import java.util.concurrent.atomic.AtomicBoolean
- import java.util.logging.Level
- import java.util.logging.LogManager
-
-
- interface IrcClient {
-
- fun send(message: String)
-
- val serverState: ServerState
- val channelState: ChannelStateMap
- val userState: UserState
-
- fun onEvent(handler: (IrcEvent) -> Unit)
-
- val caseMapping: CaseMapping
- get() = serverState.features[ServerFeature.ServerCaseMapping] ?: CaseMapping.Rfc
-
- fun isLocalUser(user: User): Boolean = caseMapping.areEquivalent(user.nickname, serverState.localNickname)
-
- }
-
- // TODO: How should alternative nicknames work?
- // TODO: Should IRC Client take a pool of servers and rotate through, or make the caller do that?
- // TODO: Should there be a default profile?
- class IrcClientImpl(private val server: Server, private val profile: Profile) : IrcClient {
-
- var socketFactory: (String, Int, Boolean) -> LineBufferedSocket = ::KtorLineBufferedSocket
-
- override val serverState = ServerState(profile.initialNick)
- override val channelState = ChannelStateMap { caseMapping }
- override val userState = UserState { caseMapping }
-
- private val messageHandler = MessageHandler(messageProcessors.toList(), eventHandlers.toMutableList())
-
- private val parser = MessageParser()
- private var socket: LineBufferedSocket? = null
-
- private val scope = CoroutineScope(Dispatchers.IO)
- private val connecting = AtomicBoolean(false)
-
- private var connectionJob: Job? = null
-
- override fun send(message: String) {
- scope.launch {
- socket?.sendLine(message)
- }
- }
-
- fun connect() {
- check(!connecting.getAndSet(true))
- connectionJob = scope.launch {
- with(socketFactory(server.host, server.port, server.tls)) {
- socket = this
- connect()
- sendLine("CAP LS 302")
- server.password?.let { pass -> sendLine(passwordMessage(pass)) }
- sendLine(nickMessage(profile.initialNick))
- // TODO: Send correct host
- sendLine(userMessage(profile.userName, "localhost", server.host, profile.realName))
- // TODO: This should be elsewhere
- messageHandler.processMessages(this@IrcClientImpl, readLines(scope).map { parser.parse(it) })
- }
- }
- }
-
- fun disconnect() {
- socket?.disconnect()
- }
-
- suspend fun join() {
- connectionJob?.join()
- }
-
- override fun onEvent(handler: (IrcEvent) -> Unit) {
- messageHandler.handlers.add(object : EventHandler {
- override fun processEvent(client: IrcClient, event: IrcEvent) {
- handler(event)
- }
- })
- }
- }
-
- fun main() {
- val rootLogger = LogManager.getLogManager().getLogger("")
- rootLogger.level = Level.FINEST
- for (h in rootLogger.handlers) {
- h.level = Level.FINEST
- }
-
- runBlocking {
- val client = IrcClientImpl(Server("testnet.inspircd.org", 6667), Profile("KtIrc", "Kotlin!", "kotlin"))
- client.onEvent { event ->
- when (event) {
- is ServerWelcome -> client.send(joinMessage("#ktirc"))
- is MessageReceived -> if (event.message == "!test") client.send(privmsgMessage(event.target, "Test successful!"))
- }
- }
- client.connect()
- client.join()
- }
- }
|