Refactoring
authorLanius Trolling <lanius@laniustrolling.dev>
Sun, 6 Oct 2024 20:06:58 +0000 (16:06 -0400)
committerLanius Trolling <lanius@laniustrolling.dev>
Sun, 6 Oct 2024 20:34:14 +0000 (16:34 -0400)
15 files changed:
.idea/misc.xml
src/jvmMain/kotlin/info/mechyrdia/auth/Sessions.kt
src/jvmMain/kotlin/info/mechyrdia/auth/nationstates.kt
src/jvmMain/kotlin/info/mechyrdia/data/Xml.kt
src/jvmMain/kotlin/info/mechyrdia/lore/ArticleListing.kt
src/jvmMain/kotlin/info/mechyrdia/lore/ArticleTitles.kt
src/jvmMain/kotlin/info/mechyrdia/lore/Fonts.kt
src/jvmMain/kotlin/info/mechyrdia/lore/ParserHtml.kt
src/jvmMain/kotlin/info/mechyrdia/lore/ParserPreprocess.kt
src/jvmMain/kotlin/info/mechyrdia/lore/ParserPreprocessMath.kt
src/jvmMain/kotlin/info/mechyrdia/lore/ParserTree.kt
src/jvmMain/kotlin/info/mechyrdia/robot/RobotService.kt
src/jvmMain/kotlin/info/mechyrdia/robot/RobotSse.kt
src/jvmMain/kotlin/info/mechyrdia/route/ResourceHandler.kt
src/jvmMain/kotlin/info/mechyrdia/route/ResourceTypes.kt

index 585a745f14f438cc3ad9a65da9c831ca22e24873..1ad7afe2ee65abc9378b34639a0c859d6a1bb5ae 100644 (file)
@@ -92,7 +92,6 @@
     </LinkMapSettings>
   </component>
   <component name="PWA">
-    <option name="enabled" value="true" />
     <option name="wasEnabledAtLeastOnce" value="true" />
   </component>
   <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
index e52174587d7932861c55568d265c26f933ff5791..5df063d9ebe864f1867125ad648e767e7250eda9 100644 (file)
@@ -52,7 +52,7 @@ fun ApplicationCall.createCsrfToken(targetRoute: String = request.origin.uri, ex
                currentUserSession = currentUserSession.let { sess ->
                        sess.copy(csrfTokens = sess.csrfTokens + (token to entry))
                }
-       } catch (ex: TooLateSessionSetException) {
+       } catch (_: TooLateSessionSetException) {
                // Yeah, this just happens on occasion. I don't want it to pollute the log files,
                // so we just ignore the exception itself and log the CSRF token that couldn't be
                // created, so we have some record in case this weirdness actually impacts a user.
index ad3713276fdb117193cf73a58c6249c32b0e6dd9..b7fab6df23f7b8135c08cfaca2b84de00f4a95c1 100644 (file)
@@ -13,7 +13,7 @@ val NSAPI: NationStates = DefaultNationStatesImpl("Mechyrdia Factbooks <samfranc
 suspend fun <Q : APIQuery<Q, R>, R> Q.executeSuspend(): R? = runInterruptible(Dispatchers.IO) {
        try {
                execute()
-       } catch (ex: NationStatesResourceNotFoundException) {
+       } catch (_: NationStatesResourceNotFoundException) {
                null
        }
 }
index 441b2194e0f625d365f040bd80f53948ecd29068..2e06317774ccb825a7d8cabeb6746629ae8a75ab 100644 (file)
@@ -1,6 +1,5 @@
 package info.mechyrdia.data
 
-import info.mechyrdia.lore.RssCategory
 import io.ktor.http.ContentType
 import io.ktor.http.HttpStatusCode
 import io.ktor.http.withCharsetIfNeeded
@@ -20,9 +19,6 @@ annotation class XmlTagMarker
 fun createXml(prettyPrint: Boolean = true): XmlTagConsumer<String> =
        createHTML(prettyPrint, xhtmlCompatible = true).xml()
 
-fun <O : Appendable> O.appendXml(prettyPrint: Boolean = true): XmlTagConsumer<O> =
-       appendHTML(prettyPrint, xhtmlCompatible = true).xml()
-
 @Suppress("UNCHECKED_CAST")
 fun <T, C : TagConsumer<T>> C.xml(): XmlTagConsumer<T> = if (this is XmlTagConsumer<*>)
        this as XmlTagConsumer<T>
@@ -31,7 +27,6 @@ else
 
 interface XmlTagConsumer<out R> : TagConsumer<R> {
        fun onTagDeclaration(version: String, standalone: Boolean?)
-       fun onTagDeclaration(standalone: Boolean?) = onTagDeclaration("1.0", standalone)
        
        override fun onTagEvent(tag: Tag, event: String, value: (Event) -> Unit) {
                tagEventsNotSupported()
index 1f19bb27bd815446cab0259100d8d3989c87bc23..aa49aee26d7fd0827462485204d1261ac698d9d1 100644 (file)
@@ -60,13 +60,6 @@ fun List<ArticleNode>.renderInto(list: UL, base: List<String> = emptyList(), for
 }
 
 suspend fun StoragePath.toFriendlyPageTitle() = ArticleTitleCache.get(this)
-       ?: ArticleTitle(
-               if (elements.size > 1)
-                       elements.lastOrNull()?.split('-')?.joinToString(separator = " ") { word ->
-                               word.lowercase().replaceFirstChar { it.titlecase() }
-                       }.orEmpty()
-               else TOC_TITLE
-       )
 
 suspend fun StoragePath.toFriendlyPathTitle(): String {
        val lorePath = elements.drop(1)
index 985e60bd1ef059d8134bc584d5b8d19138896c72..c0133a4ea2cd8b4e7ba55095587016a6d2b5b79c 100644 (file)
@@ -12,25 +12,26 @@ object ArticleTitleCache : FileDependentCache<ArticleTitle>() {
                        }.orEmpty()
                else TOC_TITLE
        
-       private val StoragePath.defaultCssProps: List<String>
-               get() = listOfNotNull(
-                       if (name.endsWith(".wip")) "opacity:0.675" else null,
-                       if (name.endsWith(".old")) "text-decoration:line-through" else null,
+       private val StoragePath.defaultCssProps: Map<String, Any>
+               get() = mapOfNotNull(
+                       if (name.endsWith(".wip")) "opacity" to "0.5" else null,
+                       if (name.endsWith(".old")) "text-decoration" to "line-through" else null,
                )
        
+       private fun Map<String, Any>.toStyleString() = map { (k, v) -> "$k:$v" }.joinToString(separator = ";")
+       
        override fun default(path: StoragePath): ArticleTitle {
-               return ArticleTitle(path.defaultTitle, path.defaultCssProps.joinToString(separator = ";"))
+               return ArticleTitle(path.defaultTitle, path.defaultCssProps.toStyleString())
        }
        
        override suspend fun processFile(path: StoragePath): ArticleTitle {
-               if (path !in StoragePath.articleDir)
-                       error("Invalid path for ArticleTitleCache /$path")
+               require(path in StoragePath.articleDir) { "Invalid path for ArticleTitleCache /$path" }
                
                val title = path.defaultTitle
                val cssProps = path.defaultCssProps
                
                val factbookAst = FactbookLoader.loadFactbook(path.elements.drop(1))
-                       ?: return ArticleTitle(title, cssProps.joinToString(separator = ";"))
+                       ?: return ArticleTitle(title, cssProps.toStyleString())
                
                val factbookTitle = factbookAst
                        .firstNotNullOfOrNull { node ->
@@ -40,10 +41,15 @@ object ArticleTitleCache : FileDependentCache<ArticleTitle>() {
                        ?.treeToText()
                        ?: title
                
-               val factbookCssProps = cssProps + listOfNotNull(
-                       if (factbookAst.any { it is ParserTreeNode.Tag && it.tag == "redirect" }) "font-style:italic" else null,
+               val factbookCssProps = cssProps + mapOfNotNull(
+                       if (factbookAst.any { it is ParserTreeNode.Tag && it.tag == "redirect" }) "font-style" to "italic" else null,
                )
                
-               return ArticleTitle(factbookTitle, factbookCssProps.joinToString(separator = ";"))
+               return ArticleTitle(factbookTitle, factbookCssProps.toStyleString())
        }
 }
+
+fun <K, V> mapOfNotNull(vararg pairs: Pair<K, V>?): Map<K, V> = pairs
+       .asSequence()
+       .filterNotNull()
+       .toMap(LinkedHashMap(pairs.size))
index 2a101b4b7d18f0dbf068f48fe3a45cf353fb7418..3d0125fcdad7fd8d9814760c52356e9961188634 100644 (file)
@@ -128,7 +128,7 @@ object MechyrdiaSansFont {
        private fun TTFFile.getGlyph(cp: Int): Int {
                return try {
                        unicodeToGlyph(cp)
-               } catch (ex: IOException) {
+               } catch (_: IOException) {
                        0
                }
        }
index f5f6255acfac78d35d6ed1657052f57a903c455d..76ed1401580aee269f7dfdb9fb37b25dcf278a69 100644 (file)
@@ -362,7 +362,6 @@ fun getSizeParam(tagParam: String?): Pair<Int?, Int?> = tagParam?.let { resoluti
 } ?: (null to null)
 
 fun getImageSizeStyleValue(width: Int?, height: Int?) = width?.let { "width: calc(var(--media-size-unit) * $it);" }.orEmpty() + height?.let { "height: calc(var(--media-size-unit) * $it);" }.orEmpty()
-fun getImageSizeAttributes(width: Int?, height: Int?) = " style=\"${getImageSizeStyleValue(width, height)}\""
 
 fun processTableCell(param: String?): Map<String, String> {
        val (width, height) = getSizeParam(param)
index 63f6d78078ddd92be31fda81ac0e2d25dab5f8b1..338a5df8dccf11d07691e37c7d2e8283367ff7b6 100644 (file)
@@ -2,8 +2,6 @@ package info.mechyrdia.lore
 
 import info.mechyrdia.JsonStorageCodec
 import info.mechyrdia.data.StoragePath
-import io.ktor.server.application.ApplicationCall
-import io.ktor.server.request.path
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.coroutineScope
@@ -11,7 +9,6 @@ import kotlinx.coroutines.joinAll
 import kotlinx.coroutines.launch
 import java.time.Instant
 import kotlin.math.roundToInt
-import java.util.function.Function as JFunction
 
 class PreProcessorContext private constructor(
        val variables: MutableMap<String, ParserTree>,
@@ -144,11 +141,10 @@ interface PreProcessorFunctionProvider : PreProcessorLexerTag {
        suspend fun provideFunction(param: String?): PreProcessorFunction?
        
        override suspend fun processTag(env: AsyncLexerTagEnvironment<PreProcessorContext, PreProcessorSubject>, param: String?, subNodes: ParserTree): PreProcessorSubject {
-               return param?.let { provideFunction(it) }.requireParam(tagName) {
+               return param?.let { provideFunction(it) }.requireParam(tagName) { func ->
                        val args = subNodes.asPreProcessorMap().mapValuesSuspend { _, value -> env.processTree(value) }
                        val ctx = PreProcessorContext(args, env.context)
                        
-                       val func = provideFunction(param) ?: return emptyList()
                        func.execute(PreProcessorUtils.withContext(env, ctx))
                }
        }
@@ -339,9 +335,9 @@ enum class PreProcessorTags(val type: PreProcessorLexerTag) {
                }
        }),
        FOR_EACH(PreProcessorLexerTag { env, param, subNodes ->
-               val itemToContext: JFunction<ParserTree, Map<String, ParserTree>> = if (param == null)
-                       JFunction(ParserTree::asPreProcessorMap)
-               else JFunction { mapOf(param to it) }
+               val itemToContext: (ParserTree) -> Map<String, ParserTree> = if (param == null)
+                       ParserTree::asPreProcessorMap
+               else ({ item: ParserTree -> mapOf(param to item) })
                
                val subTags = subNodes.filterIsInstance<ParserTreeNode.Tag>()
                val list = subTags.singleOrNull { it isTag "in" }?.subNodes
@@ -351,14 +347,14 @@ enum class PreProcessorTags(val type: PreProcessorLexerTag) {
                val body = subTags.singleOrNull { it isTag "do" }?.subNodes
                if (list != null && body != null)
                        list.mapSuspend { item ->
-                               PreProcessorUtils.processWithContext(env, env.context + itemToContext.apply(item), body)
+                               PreProcessorUtils.processWithContext(env, env.context + itemToContext(item), body)
                        }.flatten()
                else formatErrorToParserTree("Expected child tag [in] to take list input and child tag [do] to take loop body")
        }),
        MAP(PreProcessorLexerTag { env, param, subNodes ->
-               val itemToContext: JFunction<ParserTree, Map<String, ParserTree>> = if (param == null)
-                       JFunction(ParserTree::asPreProcessorMap)
-               else JFunction { mapOf(param to it) }
+               val itemToContext: (ParserTree) -> Map<String, ParserTree> = if (param == null)
+                       ParserTree::asPreProcessorMap
+               else ({ item: ParserTree -> mapOf(param to item) })
                
                val subTags = subNodes.filterIsInstance<ParserTreeNode.Tag>()
                val list = subTags.singleOrNull { it isTag "in" }?.subNodes
@@ -368,7 +364,7 @@ enum class PreProcessorTags(val type: PreProcessorLexerTag) {
                val body = subTags.singleOrNull { it isTag "do" }?.subNodes
                if (list != null && body != null)
                        list.mapSuspend { item ->
-                               ParserTreeNode.Tag("item", null, PreProcessorUtils.processWithContext(env, env.context + itemToContext.apply(item), body))
+                               ParserTreeNode.Tag("item", null, PreProcessorUtils.processWithContext(env, env.context + itemToContext(item), body))
                        }
                else formatErrorToParserTree("Expected child tag [in] to take list input and child tag [do] to take loop body")
        }),
index 72c2881c9da3dd24890a2bafe768882d0d971b90..89cfd44fe153cd05aea8ae7a2735afd7a0fe1e91 100644 (file)
@@ -89,17 +89,30 @@ fun interface PreProcessorMathUnaryOperator : PreProcessorFunction {
        fun calculate(input: Double): Double
 }
 
-fun interface PreProcessorMathBinaryOperator : PreProcessorFunction {
+interface PreProcessorBinaryFunction<T : Any> : PreProcessorFunction {
        override suspend fun execute(env: AsyncLexerTagEnvironment<PreProcessorContext, PreProcessorSubject>): ParserTree {
                val leftValue = env.processTree(env.context["left"])
                val rightValue = env.processTree(env.context["right"])
                
-               val left = leftValue.treeToNumberOrNull(String::toDoubleOrNull)
-               val right = rightValue.treeToNumberOrNull(String::toDoubleOrNull)
+               val left = leftValue.fromTreeOrNull()
+               val right = rightValue.fromTreeOrNull()
                
                if (left == null || right == null)
-                       return formatErrorToParserTree("Math operations require numerical inputs, got left = ${leftValue.unparse()} and right = ${rightValue.unparse()}")
+                       return formatErrorToParserTree("Received improper input for function: got left = ${leftValue.unparse()} and right = ${rightValue.unparse()}")
                
+               return calculateTree(left, right)
+       }
+       
+       fun ParserTree.fromTreeOrNull(): T?
+       fun calculateTree(left: T, right: T): ParserTree
+}
+
+fun interface PreProcessorMathBinaryFunction : PreProcessorBinaryFunction<Double> {
+       override fun ParserTree.fromTreeOrNull() = treeToNumberOrNull(String::toDoubleOrNull)
+}
+
+fun interface PreProcessorMathBinaryOperator : PreProcessorMathBinaryFunction {
+       override fun calculateTree(left: Double, right: Double): ParserTree {
                return calculate(left, right).numberToTree()
        }
        
@@ -120,34 +133,18 @@ fun interface PreProcessorMathVariadicOperator : PreProcessorFunction {
        fun calculate(args: List<Double>): Double
 }
 
-fun interface PreProcessorMathPredicate : PreProcessorFunction {
-       override suspend fun execute(env: AsyncLexerTagEnvironment<PreProcessorContext, PreProcessorSubject>): ParserTree {
-               val leftValue = env.processTree(env.context["left"])
-               val rightValue = env.processTree(env.context["right"])
-               
-               val left = leftValue.treeToNumberOrNull(String::toDoubleOrNull)
-               val right = rightValue.treeToNumberOrNull(String::toDoubleOrNull)
-               
-               if (left == null || right == null)
-                       return formatErrorToParserTree("Math operations require numerical inputs, got left = ${leftValue.unparse()} and right = ${rightValue.unparse()}")
-               
+fun interface PreProcessorMathPredicate : PreProcessorMathBinaryFunction {
+       override fun calculateTree(left: Double, right: Double): ParserTree {
                return calculate(left, right).booleanToTree()
        }
        
        fun calculate(left: Double, right: Double): Boolean
 }
 
-fun interface PreProcessorLogicBinaryOperator : PreProcessorFunction {
-       override suspend fun execute(env: AsyncLexerTagEnvironment<PreProcessorContext, PreProcessorSubject>): ParserTree {
-               val leftValue = env.processTree(env.context["left"])
-               val rightValue = env.processTree(env.context["right"])
-               
-               val left = leftValue.treeToBooleanOrNull()
-               val right = rightValue.treeToBooleanOrNull()
-               
-               if (left == null || right == null)
-                       return formatErrorToParserTree("Logical operations require boolean inputs, got left = ${leftValue.unparse()} and right = ${rightValue.unparse()}")
-               
+fun interface PreProcessorLogicBinaryOperator : PreProcessorBinaryFunction<Boolean> {
+       override fun ParserTree.fromTreeOrNull() = treeToBooleanOrNull()
+       
+       override fun calculateTree(left: Boolean, right: Boolean): ParserTree {
                return calculate(left, right).booleanToTree()
        }
        
index 8c0e1b35aae4d9d384ec16937ccd49467773337d..3ad2d0ba613f83c3b75163acea5d1916cd84575b 100644 (file)
@@ -16,7 +16,6 @@ infix fun ParserTreeNode.Tag.isTag(test: String) = tag.equals(test, ignoreCase =
 infix fun ParserTreeNode.Tag.isTag(test: Collection<String>) = test.any { tag.equals(it, ignoreCase = true) }
 
 infix fun ParserTreeNode.Tag.isNotTag(test: String) = !tag.equals(test, ignoreCase = true)
-infix fun ParserTreeNode.Tag.isNotTag(test: Collection<String>) = test.none { tag.equals(it, ignoreCase = true) }
 
 typealias ParserTree = List<ParserTreeNode>
 
index bcbb5255803c9a718418848cf3d8d583a14e7917..1c45ced336a68c0ef99b5bee3543516d914434ac 100644 (file)
@@ -441,7 +441,7 @@ class RobotService(
                \r
                suspend fun getInstance() = try {\r
                        instanceHolder.await()\r
-               } catch (ex: Exception) {\r
+               } catch (_: Exception) {\r
                        null\r
                }\r
                \r
index f6738040cba875efde5ff243430a85b680baaf17..cdb711f7b6c349e998e177ff92535fb1ba55edc7 100644 (file)
@@ -71,7 +71,7 @@ private suspend fun FlowCollector<ServerSentEvent>.receiveSse(response: HttpResp
                emit(builder.build())
 }
 
-suspend fun HttpClient.getSse(urlString: String, requestBuilder: suspend HttpRequestBuilder.() -> Unit): Flow<ServerSentEvent> {
+fun HttpClient.getSse(urlString: String, requestBuilder: suspend HttpRequestBuilder.() -> Unit): Flow<ServerSentEvent> {
        return flow {
                prepareGet(urlString) {
                        requestBuilder()
@@ -81,7 +81,7 @@ suspend fun HttpClient.getSse(urlString: String, requestBuilder: suspend HttpReq
        }
 }
 
-suspend fun HttpClient.postSse(urlString: String, requestBuilder: suspend HttpRequestBuilder.() -> Unit): Flow<ServerSentEvent> {
+fun HttpClient.postSse(urlString: String, requestBuilder: suspend HttpRequestBuilder.() -> Unit): Flow<ServerSentEvent> {
        return flow {
                preparePost(urlString) {
                        requestBuilder()
index a075fb7b5f58f22d3fe7b8ac2aa0639f4ece4403..d49326c2199b6b0a853b7d98bcca41d3f07241e6 100644 (file)
@@ -53,7 +53,7 @@ interface ResourceReceiver<P : Any> {
 }
 
 interface ResourceFilter {
-       suspend fun PipelineContext<Unit, ApplicationCall>.filterCall()
+       suspend fun ApplicationCall.filterCall()
 }
 
 inline fun <reified T : ResourceHandler> Route.get() {
index 341739cfdf02608dd428953e3f81411c2e223f10..725401024482f33738bc7ff6a80f8931307a2357 100644 (file)
@@ -82,19 +82,19 @@ val ErrorMessageAttributeKey = AttributeKey<String>("Mechyrdia.ErrorMessage")
 
 @Resource("/")
 class Root : ResourceHandler, ResourceFilter {
-       override suspend fun PipelineContext<Unit, ApplicationCall>.filterCall() {
-               call.request.cookies[ErrorMessageCookieName]?.let { call.attributes.put(ErrorMessageAttributeKey, it) }
+       override suspend fun ApplicationCall.filterCall() {
+               request.cookies[ErrorMessageCookieName]?.let { attributes.put(ErrorMessageAttributeKey, it) }
        }
        
        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-               filterCall()
+               call.filterCall()
                call.respondHtml(HttpStatusCode.OK, call.loreIntroPage())
        }
        
        @Resource("assets/{path...}")
        class AssetFile(val path: List<String>, val root: Root = Root()) : ResourceHandler {
                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                       with(root) { filterCall() }
+                       with(root) { call.filterCall() }
                        
                        call.respondAsset(StoragePath.assetDir / path)
                }
@@ -103,7 +103,7 @@ class Root : ResourceHandler, ResourceFilter {
        @Resource("lore/{path...}")
        class LorePage(val path: List<String>, val format: LoreArticleFormat = LoreArticleFormat.HTML, val root: Root = Root()) : ResourceHandler {
                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                       with(root) { filterCall() }
+                       with(root) { call.filterCall() }
                        
                        call.respondHtml(HttpStatusCode.OK, call.loreArticlePage(path, format))
                }
@@ -112,7 +112,7 @@ class Root : ResourceHandler, ResourceFilter {
        @Resource("map")
        class GalaxyMap(val root: Root = Root()) : ResourceHandler {
                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                       with(root) { filterCall() }
+                       with(root) { call.filterCall() }
                        
                        call.respondStoredFile(call.galaxyMapPage())
                }
@@ -121,7 +121,7 @@ class Root : ResourceHandler, ResourceFilter {
        @Resource("quote")
        class RandomQuote(val format: QuoteFormat = QuoteFormat.HTML, val root: Root = Root()) : ResourceHandler {
                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                       with(root) { filterCall() }
+                       with(root) { call.filterCall() }
                        
                        with(format) { call.respondQuote(randomQuote()) }
                }
@@ -130,7 +130,7 @@ class Root : ResourceHandler, ResourceFilter {
        @Resource("robots.txt")
        class RobotsTxt(val root: Root = Root()) : ResourceHandler {
                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                       with(root) { filterCall() }
+                       with(root) { call.filterCall() }
                        
                        call.respondStoredFile(StoragePath.Root / "robots.txt")
                }
@@ -139,7 +139,7 @@ class Root : ResourceHandler, ResourceFilter {
        @Resource("sitemap.xml")
        class SitemapXml(val root: Root = Root()) : ResourceHandler {
                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                       with(root) { filterCall() }
+                       with(root) { call.filterCall() }
                        
                        val sitemap = buildSitemap(call)
                        call.respondXml(contentType = ContentType.Application.Xml) {
@@ -151,7 +151,7 @@ class Root : ResourceHandler, ResourceFilter {
        @Resource("edits.rss")
        class RecentEditsRss(val root: Root = Root()) : ResourceHandler {
                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                       with(root) { filterCall() }
+                       with(root) { call.filterCall() }
                        
                        call.respondRss(generateRecentPageEdits(call))
                }
@@ -160,7 +160,7 @@ class Root : ResourceHandler, ResourceFilter {
        @Resource("comments.rss")
        class RecentCommentsRss(val limit: Int = 10, val root: Root = Root()) : ResourceHandler {
                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                       with(root) { filterCall() }
+                       with(root) { call.filterCall() }
                        
                        call.respondRss(call.recentCommentsRssFeedGenerator(limit))
                }
@@ -169,7 +169,7 @@ class Root : ResourceHandler, ResourceFilter {
        @Resource("preferences")
        class ClientPreferences(val root: Root = Root()) : ResourceHandler {
                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                       with(root) { filterCall() }
+                       with(root) { call.filterCall() }
                        
                        call.respondHtml(HttpStatusCode.OK, call.clientSettingsPage())
                }
@@ -177,14 +177,14 @@ class Root : ResourceHandler, ResourceFilter {
        
        @Resource("auth")
        class Auth(val root: Root = Root()) : ResourceFilter {
-               override suspend fun PipelineContext<Unit, ApplicationCall>.filterCall() {
+               override suspend fun ApplicationCall.filterCall() {
                        with(root) { filterCall() }
                }
                
                @Resource("login")
                class LoginPage(val auth: Auth = Auth()) : ResourceHandler {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                               with(auth) { filterCall() }
+                               with(auth) { call.filterCall() }
                                
                                call.respondHtml(HttpStatusCode.OK, call.loginPage())
                        }
@@ -193,7 +193,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("login")
                class LoginPost(val auth: Auth = Auth()) : ResourceReceiver<LoginPayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: LoginPayload) {
-                               with(auth) { filterCall() }
+                               with(auth) { call.filterCall() }
                                with(payload) { call.verifyCsrfToken() }
                                
                                call.loginRoute(payload.nation, payload.checksum, payload.tokenId)
@@ -203,7 +203,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("logout")
                class LogoutPost(val auth: Auth = Auth()) : ResourceReceiver<LogoutPayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: LogoutPayload) {
-                               with(auth) { filterCall() }
+                               with(auth) { call.filterCall() }
                                with(payload) { call.verifyCsrfToken() }
                                
                                call.logoutRoute()
@@ -212,16 +212,21 @@ class Root : ResourceHandler, ResourceFilter {
        }
        
        @Resource("nuke")
-       class Nuke(val root: Root = Root()) : ResourceHandler {
-               override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
+       class Nuke(val root: Root = Root()) : ResourceFilter, ResourceHandler {
+               override suspend fun ApplicationCall.filterCall() {
                        with(root) { filterCall() }
-                       
+               }
+               
+               override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
+                       call.filterCall()
                        call.respondHtml(HttpStatusCode.OK, call.robotPage())
                }
                
                @Resource("ws")
                class WS(val csrfToken: String? = null, val nuke: Nuke = Nuke()) : ResourceListener {
                        override suspend fun DefaultWebSocketServerSession.handleCall() {
+                               with(nuke) { call.filterCall() }
+                               
                                robotConversation(csrfToken)
                        }
                }
@@ -229,14 +234,14 @@ class Root : ResourceHandler, ResourceFilter {
        
        @Resource("comment")
        class Comments(val root: Root = Root()) : ResourceFilter {
-               override suspend fun PipelineContext<Unit, ApplicationCall>.filterCall() {
+               override suspend fun ApplicationCall.filterCall() {
                        with(root) { filterCall() }
                }
                
                @Resource("help")
                class HelpPage(val comments: Comments = Comments()) : ResourceHandler {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                               with(comments) { filterCall() }
+                               with(comments) { call.filterCall() }
                                
                                call.respondHtml(HttpStatusCode.OK, call.commentHelpPage())
                        }
@@ -245,7 +250,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("recent")
                class RecentPage(val limit: Int? = null, val comments: Comments = Comments()) : ResourceHandler {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                               with(comments) { filterCall() }
+                               with(comments) { call.filterCall() }
                                
                                call.respondHtml(HttpStatusCode.OK, call.recentCommentsPage(limit))
                        }
@@ -254,7 +259,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("new/{path...}")
                class NewPost(val path: List<String>, val comments: Comments = Comments()) : ResourceReceiver<NewCommentPayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: NewCommentPayload) {
-                               with(comments) { filterCall() }
+                               with(comments) { call.filterCall() }
                                with(payload) { call.verifyCsrfToken() }
                                
                                call.newCommentRoute(path, payload.comment)
@@ -264,7 +269,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("view/{id}")
                class ViewPage(val id: Id<Comment>, val comments: Comments = Comments()) : ResourceHandler {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                               with(comments) { filterCall() }
+                               with(comments) { call.filterCall() }
                                
                                call.viewCommentRoute(id)
                        }
@@ -273,7 +278,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("edit/{id}")
                class EditPost(val id: Id<Comment>, val comments: Comments = Comments()) : ResourceReceiver<EditCommentPayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: EditCommentPayload) {
-                               with(comments) { filterCall() }
+                               with(comments) { call.filterCall() }
                                with(payload) { call.verifyCsrfToken() }
                                
                                call.editCommentRoute(id, payload.comment)
@@ -283,7 +288,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("delete/{id}")
                class DeleteConfirmPage(val id: Id<Comment>, val comments: Comments = Comments()) : ResourceHandler {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                               with(comments) { filterCall() }
+                               with(comments) { call.filterCall() }
                                
                                call.respondHtml(HttpStatusCode.OK, call.deleteCommentPage(id))
                        }
@@ -292,7 +297,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("delete/{id}")
                class DeleteConfirmPost(val id: Id<Comment>, val comments: Comments = Comments()) : ResourceReceiver<DeleteCommentPayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: DeleteCommentPayload) {
-                               with(comments) { filterCall() }
+                               with(comments) { call.filterCall() }
                                with(payload) { call.verifyCsrfToken() }
                                
                                call.deleteCommentRoute(id)
@@ -302,19 +307,19 @@ class Root : ResourceHandler, ResourceFilter {
        
        @Resource("user")
        class User(val root: Root = Root()) : ResourceHandler, ResourceFilter {
-               override suspend fun PipelineContext<Unit, ApplicationCall>.filterCall() {
+               override suspend fun ApplicationCall.filterCall() {
                        with(root) { filterCall() }
                }
                
                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                       filterCall()
+                       call.filterCall()
                        call.currentUserPage()
                }
                
                @Resource("{id}")
                class ById(val id: Id<NationData>, val user: User = User()) : ResourceHandler {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                               with(user) { filterCall() }
+                               with(user) { call.filterCall() }
                                
                                call.respondHtml(HttpStatusCode.OK, call.userPage(id))
                        }
@@ -323,15 +328,15 @@ class Root : ResourceHandler, ResourceFilter {
        
        @Resource("admin")
        class Admin(val root: Root = Root()) : ResourceFilter {
-               override suspend fun PipelineContext<Unit, ApplicationCall>.filterCall() {
+               override suspend fun ApplicationCall.filterCall() {
                        with(root) { filterCall() }
-                       call.ownerNationOnly()
+                       ownerNationOnly()
                }
                
                @Resource("ban/{id}")
                class Ban(val id: Id<NationData>, val admin: Admin = Admin()) : ResourceReceiver<AdminBanUserPayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminBanUserPayload) {
-                               with(admin) { filterCall() }
+                               with(admin) { call.filterCall() }
                                with(payload) { call.verifyCsrfToken() }
                                
                                call.adminBanUserRoute(id)
@@ -341,7 +346,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("unban/{id}")
                class Unban(val id: Id<NationData>, val admin: Admin = Admin()) : ResourceReceiver<AdminUnbanUserPayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminUnbanUserPayload) {
-                               with(admin) { filterCall() }
+                               with(admin) { call.filterCall() }
                                with(payload) { call.verifyCsrfToken() }
                                
                                call.adminUnbanUserRoute(id)
@@ -350,19 +355,19 @@ class Root : ResourceHandler, ResourceFilter {
                
                @Resource("nuke")
                class NukeManagement(val admin: Admin = Admin()) : ResourceFilter, ResourceHandler {
-                       override suspend fun PipelineContext<Unit, ApplicationCall>.filterCall() {
+                       override suspend fun ApplicationCall.filterCall() {
                                with(admin) { filterCall() }
                        }
                        
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                               filterCall()
+                               call.filterCall()
                                call.respondHtml(HttpStatusCode.OK, call.robotManagementPage())
                        }
                        
                        @Resource("update")
                        class Update(val nukeManagement: NukeManagement = NukeManagement()) : ResourceReceiver<AdminNukeUpdatePayload> {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminNukeUpdatePayload) {
-                                       with(nukeManagement) { filterCall() }
+                                       with(nukeManagement) { call.filterCall() }
                                        with(payload) { call.verifyCsrfToken() }
                                        
                                        RobotService.getInstance()?.performMaintenance()
@@ -374,7 +379,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("reset")
                        class Reset(val nukeManagement: NukeManagement = NukeManagement()) : ResourceReceiver<AdminNukeResetPayload> {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminNukeResetPayload) {
-                                       with(nukeManagement) { filterCall() }
+                                       with(nukeManagement) { call.filterCall() }
                                        with(payload) { call.verifyCsrfToken() }
                                        
                                        RobotService.getInstance()?.reset()
@@ -386,14 +391,14 @@ class Root : ResourceHandler, ResourceFilter {
                
                @Resource("vfs")
                class Vfs(val admin: Admin = Admin()) : ResourceFilter {
-                       override suspend fun PipelineContext<Unit, ApplicationCall>.filterCall() {
+                       override suspend fun ApplicationCall.filterCall() {
                                with(admin) { filterCall() }
                        }
                        
                        @Resource("inline/{path...}")
                        class Inline(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceHandler {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        
                                        call.response.header(HttpHeaders.ContentDisposition, "inline")
                                        call.adminPreviewFile(StoragePath(path))
@@ -403,7 +408,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("download/{path...}")
                        class Download(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceHandler {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        
                                        call.response.header(HttpHeaders.ContentDisposition, "attachment; filename=\"${path.last()}\"")
                                        call.adminPreviewFile(StoragePath(path))
@@ -413,7 +418,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("view/{path...}")
                        class View(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceHandler {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        
                                        call.respondHtml(HttpStatusCode.OK, call.adminViewVfs(StoragePath(path)))
                                }
@@ -422,7 +427,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("webdav-token")
                        class WebDavTokenPage(val vfs: Vfs = Vfs()) : ResourceHandler {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        
                                        call.respondHtml(HttpStatusCode.OK, call.adminRequestWebDavToken())
                                }
@@ -431,7 +436,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("webdav-token")
                        class WebDavTokenPost(val vfs: Vfs = Vfs()) : ResourceReceiver<AdminVfsRequestWebDavTokenPayload> {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminVfsRequestWebDavTokenPayload) {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        with(payload) { call.verifyCsrfToken() }
                                        
                                        call.respondHtml(HttpStatusCode.Created, call.adminObtainWebDavToken())
@@ -441,7 +446,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("copy/{path...}")
                        class CopyPage(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceHandler {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        
                                        call.respondHtml(HttpStatusCode.OK, call.adminShowCopyFile(StoragePath(path)))
                                }
@@ -450,7 +455,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("copy/{path...}")
                        class CopyPost(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceReceiver<AdminVfsCopyFilePayload> {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminVfsCopyFilePayload) {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        with(payload) { call.verifyCsrfToken() }
                                        
                                        call.adminDoCopyFile(StoragePath(payload.from), StoragePath(path))
@@ -460,7 +465,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("upload/{path...}")
                        class Upload(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceReceiver<CsrfProtectedMultiPartPayload> {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: CsrfProtectedMultiPartPayload) {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        with(payload) { call.verifyCsrfToken() }
                                        
                                        val fileItem = payload.payload.filterIsInstance<PartData.FileItem>().singleOrNull()
@@ -473,7 +478,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("overwrite/{path...}")
                        class Overwrite(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceReceiver<CsrfProtectedMultiPartPayload> {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: CsrfProtectedMultiPartPayload) {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        with(payload) { call.verifyCsrfToken() }
                                        
                                        val fileItem = payload.payload.filterIsInstance<PartData.FileItem>().singleOrNull()
@@ -486,7 +491,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("delete/{path...}")
                        class DeleteConfirmPage(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceHandler {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        
                                        call.adminConfirmDeleteFile(StoragePath(path))
                                }
@@ -495,7 +500,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("delete/{path...}")
                        class DeleteConfirmPost(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceReceiver<AdminVfsDeleteFilePayload> {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminVfsDeleteFilePayload) {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        with(payload) { call.verifyCsrfToken() }
                                        
                                        call.adminDeleteFile(StoragePath(path))
@@ -505,7 +510,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("mkdir/{path...}")
                        class MkDir(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceReceiver<AdminVfsMkDirPayload> {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminVfsMkDirPayload) {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        with(payload) { call.verifyCsrfToken() }
                                        
                                        call.adminMakeDirectory(StoragePath(path), payload.directory)
@@ -515,7 +520,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("rmdir/{path...}")
                        class RmDirConfirmPage(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceHandler {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        
                                        call.adminConfirmRemoveDirectory(StoragePath(path))
                                }
@@ -524,7 +529,7 @@ class Root : ResourceHandler, ResourceFilter {
                        @Resource("rmdir/{path...}")
                        class RmDirConfirmPost(val path: List<String>, val vfs: Vfs = Vfs()) : ResourceReceiver<AdminVfsRmDirPayload> {
                                override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminVfsRmDirPayload) {
-                                       with(vfs) { filterCall() }
+                                       with(vfs) { call.filterCall() }
                                        with(payload) { call.verifyCsrfToken() }
                                        
                                        call.adminRemoveDirectory(StoragePath(path))
@@ -535,7 +540,7 @@ class Root : ResourceHandler, ResourceFilter {
        
        @Resource("utils")
        class Utils(val root: Root = Root()) : ResourceFilter {
-               override suspend fun PipelineContext<Unit, ApplicationCall>.filterCall() {
+               override suspend fun ApplicationCall.filterCall() {
                        with(root) { filterCall() }
                        
                        delay(250L)
@@ -544,7 +549,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("mechyrdia-sans")
                class MechyrdiaSans(val utils: Utils = Utils()) : ResourceReceiver<MechyrdiaSansPayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: MechyrdiaSansPayload) {
-                               with(utils) { filterCall() }
+                               with(utils) { call.filterCall() }
                                
                                val svgDoc = MechyrdiaSansFont.renderTextToSvg(payload.lines.joinToString(separator = "\n") { it.trim() }, payload.bold, payload.italic, payload.align)
                                call.respondXml(contentType = ContentType.Image.SVG) {
@@ -556,7 +561,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("tylan-lang")
                class TylanLanguage(val utils: Utils = Utils()) : ResourceReceiver<TylanLanguagePayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: TylanLanguagePayload) {
-                               with(utils) { filterCall() }
+                               with(utils) { call.filterCall() }
                                
                                call.respondText(TylanAlphabetFont.tylanToFontAlphabet(payload.lines.joinToString(separator = "\n")))
                        }
@@ -565,7 +570,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("pokhwal-lang")
                class PokhwalishLanguage(val utils: Utils = Utils()) : ResourceReceiver<PokhwalishLanguagePayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: PokhwalishLanguagePayload) {
-                               with(utils) { filterCall() }
+                               with(utils) { call.filterCall() }
                                
                                call.respondText(PokhwalishAlphabetFont.pokhwalToFontAlphabet(payload.lines.joinToString(separator = "\n")))
                        }
@@ -574,7 +579,7 @@ class Root : ResourceHandler, ResourceFilter {
                @Resource("preview-comment")
                class PreviewComment(val utils: Utils = Utils()) : ResourceReceiver<PreviewCommentPayload> {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: PreviewCommentPayload) {
-                               with(utils) { filterCall() }
+                               with(utils) { call.filterCall() }
                                
                                call.respondText(
                                        text = payload.lines.joinToString(separator = "\n").parseAs(ParserTree::toCommentHtml).toFragmentString(),