Apply linting and clearer log messages

This commit is contained in:
2026-10-05 12:50:15 +01:00
parent 96d0c084b1
commit 357405aba5
@@ -1,6 +1,8 @@
package com.shr4pnel.ferretirc.net
import com.shr4pnel.ferretirc.net.messages.ServerMessage
import com.shr4pnel.ferretirc.net.messages.ServerMessage.*
import com.shr4pnel.ferretirc.net.messages.ServerMessage.Numeric.*
import io.github.oshai.kotlinlogging.KotlinLogging
import io.github.oshai.kotlinlogging.slf4j.logger
import kotlinx.coroutines.channels.Channel
@@ -18,7 +20,10 @@ class MessageParser(val incoming: Channel<String>, enableLogging: Boolean = fals
abstract class Command(open val command: String, open val parameters: List<String>) {
abstract fun toServerMessage(): ServerMessage
fun getTrailingParameterIndex() = parameters.indexOfFirst { it.startsWith(":") }
fun getTrailingParameterIndex(): Int {
val index = parameters.indexOfFirst { it.startsWith(":") }
return if (index != -1) index else 0
}
fun getTrailingParameterString() =
parameters
@@ -26,48 +31,57 @@ class MessageParser(val incoming: Channel<String>, enableLogging: Boolean = fals
.joinToString(" ")
.removePrefix(":")
fun getNonTrailingParameterString() =
fun getNonTrailingParameterString(startIndex: Int = 0) =
parameters
.subList(0, getTrailingParameterIndex())
.subList(startIndex, getTrailingParameterIndex())
.joinToString(" ")
.removePrefix(":")
fun getNonTrailingParameters() = getNonTrailingParameterString().split(" ")
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(":"))
// TODO MODE, CHANMODE, LOCALMODE
"MODE" ->
ServerMessage.Mode(
parameters.first(),
"PONG" -> Pong(getTrailingParameterString().removePrefix(":"))
"MODE" -> Mode(
parameters.first(), // TODO MODE, CHANMODE, LOCALMODE
parameters.last().removePrefix(":"),
)
"NOTICE" ->
ServerMessage.Notice(
"NOTICE" -> Notice(
getNonTrailingParameters(),
getTrailingParameterString(),
)
"PRIVMSG" ->
ServerMessage.PrivMsg(
"PRIVMSG" -> PrivMsg(
getNonTrailingParameters(),
getTrailingParameterString(),
)
else -> ServerMessage.UNIMPLEMENTED("$command ${parameters.joinToString(" ")}")
"JOIN" -> Join(getTrailingParameterString())
"CAP" -> Cap(getTrailingParameterString())
"PING" -> Ping(getTrailingParameterString())
"ERROR" -> Error(getTrailingParameterString())
else -> UNIMPLEMENTED("$command ${getTrailingParameterString()}")
}
}
class NumericCommand(override val command: String, override val parameters: List<String>) : Command(command, parameters) {
private enum class RPL(val underlying: Int) {
WELCOME(1),
YOURHOST(2),
CREATED(3),
MYINFO(4),
ISUPPORT(5),
BOUNCE(10),
STATSCOMMANDS(212),
ENDOFSTATS(219),
LISTSTART(321),
LIST(322),
LISTEND(323),
TOPIC(332),
TOPICWHOTIME(333),
NAMREPLY(353),
ENDOFNAMES(366),
NONE(0xbeef), ;
companion object {
@@ -79,38 +93,31 @@ class MessageParser(val incoming: Channel<String>, enableLogging: Boolean = fals
override fun toServerMessage() =
when (RPL from command) {
RPL.ISUPPORT -> ServerMessage.Numeric.RPL_ISUPPORT(getNonTrailingParameterString())
RPL.LISTSTART -> ServerMessage.Numeric.RPL_LISTSTART()
RPL.LIST -> ServerMessage.Numeric.RPL_LIST(
RPL.WELCOME -> RPL_WELCOME(getTrailingParameterString())
RPL.YOURHOST -> RPL_YOURHOST(getTrailingParameterString())
RPL.CREATED -> RPL_CREATED(getTrailingParameterString())
RPL.MYINFO -> RPL_MYINFO(getTrailingParameterString())
RPL.BOUNCE -> UNIMPLEMENTED(parameters.toString())
RPL.ISUPPORT -> RPL_ISUPPORT(getNonTrailingParameterString(1).split(" "))
RPL.LISTSTART -> RPL_LISTSTART
RPL.LIST -> RPL_LIST(
parameters[1],
parameters[2].toInt(),
getTrailingParameterString(),
)
RPL.LISTEND -> ServerMessage.Numeric.RPL_LISTEND()
RPL.NONE -> ServerMessage.UNIMPLEMENTED(parameters.toString())
RPL.LISTEND -> RPL_LISTEND
RPL.STATSCOMMANDS -> UNIMPLEMENTED(parameters.toString())
RPL.ENDOFSTATS -> UNIMPLEMENTED(parameters.toString())
RPL.TOPIC -> RPL_TOPIC(getTrailingParameterString())
RPL.TOPICWHOTIME -> UNIMPLEMENTED(parameters.toString())
RPL.NAMREPLY -> UNIMPLEMENTED(parameters.toString())
RPL.ENDOFNAMES -> RPL_ENDOFNAMES
RPL.NONE -> UNIMPLEMENTED(parameters.toString())
}
}
}
/**
* Messages have this format, as rough ABNF:
*
* message ::= ['@' <tags> SPACE] [':' <source> SPACE] <command> <parameters> <crlf>
* SPACE ::= %x20 *( %x20 ) ; space character(s)
* crlf ::= %x0D %x0A ; "carriage return" "linefeed"
*
* The specific parts of an IRC message are:
*
* tags: Optional metadata on a message, starting with ('@', 0x40).
*
* source: Optional note of where the message came from, starting with (':', 0x3A).
*
* command: The specific command this message represents.
*
* parameters: If it exists, data relevant to this specific command.
*
* ===================
*
* Convert line from server into tokens split on strings and remove elements that belong
* to different segments of the IRC message e.g. tags, prefixes etc
*/
@@ -118,27 +125,24 @@ class MessageParser(val incoming: Channel<String>, enableLogging: Boolean = fals
val tokens = commandList.toMutableList()
// remove tags from strlist if present
tags =
if (tokens.first().startsWith("@")) {
tags = if (tokens.first().startsWith("@")) {
tokens.removeFirst()
} else {
null
}
// remove client prefix (nick&opt hostname) if present
prefix =
if (tokens.first().startsWith(":")) {
prefix = if (tokens.first().startsWith(":")) {
tokens.removeFirst()
} else {
null
}
// get command e.g. PING
// get command e.g. PING, 005
val commandStr = tokens.removeFirst()
// Check if command is a numeric (RPL/ERR) or named
command =
if (commandStr.toIntOrNull() == null) {
command = if (commandStr.toIntOrNull() == null) {
Command.NamedCommand(commandStr, tokens)
} else {
Command.NumericCommand(commandStr, tokens)
@@ -153,8 +157,14 @@ class MessageParser(val incoming: Channel<String>, enableLogging: Boolean = fals
suspend fun start() {
val builder = MessageBuilder()
for (msg in incoming) {
logger.debug { "Receiving: $msg" }
incomingParsedMessages.send(builder.build(msg))
logger.trace { "Receiving: $msg" }
val parsed = builder.build(msg)
if (parsed is UNIMPLEMENTED) {
logger.warn { "[S->C]: Left unparsed: $msg" }
} else {
logger.debug { "[S->C]: Got $parsed" }
}
incomingParsedMessages.send(parsed)
}
}
}