Apply ktlint standard formatting

This commit is contained in:
2026-09-13 21:26:25 +01:00
parent aa1e862201
commit e8fc28a239
15 changed files with 192 additions and 142 deletions
@@ -2,8 +2,8 @@ package com.shr4pnel.ferretirc
import com.shr4pnel.ferretirc.irc.Server
import com.shr4pnel.ferretirc.irc.util.Helpers
import com.shr4pnel.ferretirc.net.messages.ClientMessage
import com.shr4pnel.ferretirc.net.Connection
import com.shr4pnel.ferretirc.net.messages.ClientMessage
import com.shr4pnel.ferretirc.net.messages.ServerMessage
import io.github.oshai.kotlinlogging.KotlinLoggingConfiguration
import kotlinx.coroutines.CoroutineScope
@@ -64,15 +64,23 @@ class IrcClient(hostname: String, port: Int, enableLogging: Boolean = true) {
suspend inline fun <reified T : ServerMessage> waitForNext(noinline predicate: (T) -> Boolean = { true }): T =
connection.reader.waitForNext(T::class, predicate)
suspend fun register(nick: String, realName: String? = null, password: String? = null) {
val messages = buildList {
if (!password.isNullOrEmpty()) add(ClientMessage.Pass(password))
add(ClientMessage.Cap.LS())
add(ClientMessage.Nick(nick))
if (!realName.isNullOrEmpty()) add(ClientMessage.User(nick, realName))
else add(ClientMessage.User(nick, nick))
add(ClientMessage.Cap.END())
}
suspend fun register(
nick: String,
realName: String? = null,
password: String? = null,
) {
val messages =
buildList {
if (!password.isNullOrEmpty()) add(ClientMessage.Pass(password))
add(ClientMessage.Cap.LS())
add(ClientMessage.Nick(nick))
if (!realName.isNullOrEmpty()) {
add(ClientMessage.User(nick, realName))
} else {
add(ClientMessage.User(nick, nick))
}
add(ClientMessage.Cap.END())
}
queueMessages(*messages.toTypedArray())
}
}
}
@@ -1,5 +1,3 @@
package com.shr4pnel.ferretirc.irc
class IRCChannel(val chanName: String, val clientCount: Int, val topic: String) {
}
class IRCChannel(val chanName: String, val clientCount: Int, val topic: String)
@@ -20,4 +20,4 @@ sealed class Feature(key: String) {
// Indicates the maximum length of a nickname that a client may use. Other clients on the network may have nicknames longer than this.
data class MAXNICKLEN(val max: Int) : Feature("MAXNICKLEN")
}
}
@@ -9,7 +9,11 @@ import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.onSubscription
import kotlinx.coroutines.flow.takeWhile
class Server(private val msgBuffer: SharedFlow<ServerMessage>, private val outgoingMessages: Channel<ClientMessage>, private val scope: CoroutineScope) {
class Server(
private val msgBuffer: SharedFlow<ServerMessage>,
private val outgoingMessages: Channel<ClientMessage>,
private val scope: CoroutineScope,
) {
lateinit var features: ISupportFeatures
private set
@@ -31,6 +35,5 @@ class Server(private val msgBuffer: SharedFlow<ServerMessage>, private val outgo
}
fun fetchFeatures() {
}
}
}
@@ -4,4 +4,4 @@ class Helpers {
companion object LoggingConfig {
var enableLogging = true
}
}
}
@@ -22,4 +22,4 @@ class Connection(val hostname: String, val port: Int, val scope: CoroutineScope)
writer.start()
return Server(reader.sharedMessageBuffer, writer.outgoingMessages, scope)
}
}
}
@@ -18,6 +18,7 @@ class MessageParser(val incoming: Channel<String>) {
abstract class Command(open val command: String, open val parameters: List<String>) {
abstract fun toServerMessage(): ServerMessage
fun getTrailingParameterIndex() = parameters.indexOfFirst { it.startsWith(":") }
fun getTrailingParameterString() =
@@ -35,41 +36,47 @@ class MessageParser(val incoming: Channel<String>) {
fun getNonTrailingParameters() = getNonTrailingParameterString().split(" ")
class NamedCommand(override val command: String, override val parameters: List<String>) : Command(command, parameters) {
override fun toServerMessage() = when (command.uppercase()) {
"PONG" -> ServerMessage.Pong(getTrailingParameterString().removePrefix(":"))
"MODE" -> ServerMessage.Mode(
parameters.first(),
parameters.last().removePrefix(":")
) // TODO MODE, CHANMODE, LOCALMODE
override fun toServerMessage() =
when (command.uppercase()) {
"PONG" -> ServerMessage.Pong(getTrailingParameterString().removePrefix(":"))
"MODE" ->
ServerMessage.Mode(
parameters.first(),
parameters.last().removePrefix(":"),
) // TODO MODE, CHANMODE, LOCALMODE
"NOTICE" -> ServerMessage.Notice(
getNonTrailingParameters(),
getTrailingParameterString()
)
"NOTICE" ->
ServerMessage.Notice(
getNonTrailingParameters(),
getTrailingParameterString(),
)
"PRIVMSG" -> ServerMessage.PrivMsg(
getNonTrailingParameters(),
getTrailingParameterString()
)
"PRIVMSG" ->
ServerMessage.PrivMsg(
getNonTrailingParameters(),
getTrailingParameterString(),
)
else -> {
ServerMessage.UNIMPLEMENTED("$command ${parameters.joinToString(" ")}")
else -> {
ServerMessage.UNIMPLEMENTED("$command ${parameters.joinToString(" ")}")
}
}
}
}
class NumericCommand(override val command: String, override val parameters: List<String>) : Command(command, parameters) {
override fun toServerMessage() = when (command.toInt()) {
5 -> ServerMessage.Numeric.RPL_ISUPPORT(getNonTrailingParameterString())
321 -> ServerMessage.Numeric.RPL_LISTSTART()
322 -> ServerMessage.Numeric.RPL_LIST(
parameters[1],
parameters[2].toInt(),
getTrailingParameterString()
)
323 -> ServerMessage.Numeric.RPL_LISTEND()
else -> ServerMessage.UNIMPLEMENTED(parameters.toString())
}
override fun toServerMessage() =
when (command.toInt()) {
5 -> ServerMessage.Numeric.RPL_ISUPPORT(getNonTrailingParameterString())
321 -> ServerMessage.Numeric.RPL_LISTSTART()
322 ->
ServerMessage.Numeric.RPL_LIST(
parameters[1],
parameters[2].toInt(),
getTrailingParameterString(),
)
323 -> ServerMessage.Numeric.RPL_LISTEND()
else -> ServerMessage.UNIMPLEMENTED(parameters.toString())
}
}
}
@@ -99,21 +106,31 @@ class MessageParser(val incoming: Channel<String>) {
val tokens = commandList.toMutableList()
// remove tags from strlist if present
tags = if (tokens.first().startsWith("@")) {
tokens.removeFirst()
} else null
tags =
if (tokens.first().startsWith("@")) {
tokens.removeFirst()
} else {
null
}
// remove client prefix (nick&opt hostname) if present
prefix = if (tokens.first().startsWith(":")) {
tokens.removeFirst()
} else null
prefix =
if (tokens.first().startsWith(":")) {
tokens.removeFirst()
} else {
null
}
// get command e.g. PING
val commandStr = tokens.removeFirst()
// Check if command is a numeric (RPL/ERR) or named
command = if (commandStr.toIntOrNull() == null) Command.NamedCommand(commandStr, tokens)
else Command.NumericCommand(commandStr, tokens)
command =
if (commandStr.toIntOrNull() == null) {
Command.NamedCommand(commandStr, tokens)
} else {
Command.NumericCommand(commandStr, tokens)
}
return command.toServerMessage()
}
@@ -122,11 +139,10 @@ class MessageParser(val incoming: Channel<String>) {
}
suspend fun start() {
val builder = MessageBuilder()
for (msg in incoming) {
logger.trace { "Receiving: $msg" }
incomingParsedMessages.send(builder.build(msg))
}
}
}
}
@@ -27,7 +27,6 @@ class MessageReader(socket: Socket, scope: CoroutineScope) : MessageIO(socket, s
val parser = MessageParser(incomingMessages)
private val logger = if (Helpers.enableLogging) KotlinLogging.logger {} else KotlinLogging.logger(NOPLogger.NOP_LOGGER)
init {
scope.launch {
parser.start()
@@ -49,7 +48,10 @@ class MessageReader(socket: Socket, scope: CoroutineScope) : MessageIO(socket, s
}
}
suspend fun <T : ServerMessage> waitForNext(kClass: KClass<T>, predicate: (T) -> Boolean = { true }): T {
suspend fun <T : ServerMessage> waitForNext(
kClass: KClass<T>,
predicate: (T) -> Boolean = { true },
): T {
return messageBuffer
.filter {
kClass.isInstance(it)
@@ -57,4 +59,4 @@ class MessageReader(socket: Socket, scope: CoroutineScope) : MessageIO(socket, s
kClass.cast(it)
}.first(predicate)
}
}
}
@@ -16,13 +16,14 @@ class MessageWriter(socket: Socket, scope: CoroutineScope) : MessageIO(socket, s
private lateinit var send: ByteWriteChannel
private val logger = if (Helpers.enableLogging) KotlinLogging.logger {} else KotlinLogging.logger(NOPLogger.NOP_LOGGER)
override fun start() = scope.launch {
send = socket.openWriteChannel()
override fun start() =
scope.launch {
send = socket.openWriteChannel()
for (msg in outgoingMessages) {
logger.debug { "Sending: ${msg.toWireIntermediate()}" }
send.writeFully(msg.toWire())
send.flush()
for (msg in outgoingMessages) {
logger.debug { "Sending: ${msg.toWireIntermediate()}" }
send.writeFully(msg.toWire())
send.flush()
}
}
}
}
@@ -55,7 +55,9 @@ sealed class ClientMessage(val strName: String) {
class Nick(val nickname: String) : ClientMessage("NICK") {
init {
require(nickname.length < 10) { "\"$nickname\" exceeds IRCs maximum nickname length of 9" }
require(!nickname.startsWith(":") && !nickname.startsWith("#")) { "\"$nickname\" may not begin with : or #" } // TODO THIS SHOULD BLACKLIST ALL PREFIXES NAMED IN CHANTYPES PARAMETER
require(!nickname.startsWith(":") && !nickname.startsWith("#")) {
"\"$nickname\" may not begin with : or #"
} // TODO THIS SHOULD BLACKLIST ALL PREFIXES NAMED IN CHANTYPES PARAMETER
require(!nickname.contains(" ")) { "$nickname may not contain a space" }
}
@@ -97,9 +99,12 @@ sealed class ClientMessage(val strName: String) {
class PrivMsg(val target: IRCChannel, val message: String) : ClientMessage("PRIVMSG") {
override val params: kotlin.collections.List<String>
get() = listOf(target.chanName) // TODO RPL_ISUPPORT REQUIREMENTS IN FUN EG LINELEN STATUSMSG - ALSO ADD FUCKING STUPID STATUSMSG & SUPPORT FOR MASKS. FUCK ME
get() =
listOf(
target.chanName,
) // TODO RPL_ISUPPORT REQUIREMENTS IN FUN EG LINELEN STATUSMSG - ALSO ADD FUCKING STUPID STATUSMSG & SUPPORT FOR MASKS. FUCK ME
override val trailing: String
get() = message
}
}
}
@@ -7,5 +7,6 @@ import kotlinx.coroutines.channels.Channel
abstract class MessageIO(val socket: Socket, val scope: CoroutineScope) {
val incomingMessages = Channel<String>(Channel.BUFFERED)
val outgoingMessages = Channel<ClientMessage>(Channel.BUFFERED)
abstract fun start(): Any
}
}
@@ -5,16 +5,24 @@ package com.shr4pnel.ferretirc.net.messages
*/
sealed class ServerMessage {
data class Cap(val placeholder: String? = null) : ServerMessage()
data class Pong(val token: String? = null) : ServerMessage()
data class Mode(val operatorName: String, val pMask: String) : ServerMessage()
data class PrivMsg(val targets: List<String>, val message: String) : ServerMessage()
data class Notice(val targets: List<String>, val message: String) : ServerMessage()
data class UNIMPLEMENTED(val msg: String) : ServerMessage()
sealed class Numeric(val number: Short) : ServerMessage() {
data class RPL_ISUPPORT(val keypairs: String): Numeric(5)
data class RPL_ISUPPORT(val keypairs: String) : Numeric(5)
class RPL_LISTSTART : Numeric(321)
data class RPL_LIST(val chanName: String, val clientCount: Int, val topic: String) : Numeric(322)
class RPL_LISTEND : Numeric(323)
}
}
}