diff --git a/.gitea/workflows/dockerfiles.yml b/.gitea/workflows/dockerfiles.yml index e079f33..37c98d0 100644 --- a/.gitea/workflows/dockerfiles.yml +++ b/.gitea/workflows/dockerfiles.yml @@ -11,6 +11,12 @@ jobs: RUNNER_TOOL_CACHE: /toolcache REGISTRY: tuxpa.in IMAGE_ROOT: a/wynn + strategy: + matrix: + options: + name: ts + context: ./ts + file: ./ts/Dockerfile steps: - name: Checkout uses: actions/checkout@v4 @@ -25,13 +31,13 @@ jobs: - name: build and push typescript uses: docker/build-push-action@v6 with: - context: ./ts - file: ./ts/Dockerfile + context: ${{ matrix.options.context }} + file: ${{ matrix.options.file }} platforms: linux/amd64 push: true - cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_ROOT }}/ts:cache - cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_ROOT }}/ts:cache,mode=max,image-manifest=true,oci-mediatypes=true,type=registry + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_ROOT }}/${{matrix.options.name}}:cache + cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_ROOT }}/${{matrix.options.name}}:cache,mode=max,image-manifest=true,oci-mediatypes=true,type=registry tags: | - ${{env.REGISTRY}}/${{env.IMAGE_ROOT}}/ts:${{ gitea.ref_name }}, - ${{env.REGISTRY}}/${{env.IMAGE_ROOT}}/ts:${{ gitea.head_ref || gitea.ref_name }} + ${{env.REGISTRY}}/${{env.IMAGE_ROOT}}/${{matrix.options.name}}:${{ gitea.ref_name }}, + ${{env.REGISTRY}}/${{env.IMAGE_ROOT}}/${{matrix.options.name}}:${{ gitea.head_ref || gitea.ref_name }} diff --git a/java/clientmod/.gitignore b/java/clientmod/.gitignore new file mode 100644 index 0000000..d5f737e --- /dev/null +++ b/java/clientmod/.gitignore @@ -0,0 +1,119 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ +runs/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/java/clientmod/LICENSE.txt b/java/clientmod/LICENSE.txt new file mode 100644 index 0000000..9e85bcc --- /dev/null +++ b/java/clientmod/LICENSE.txt @@ -0,0 +1,2 @@ +Copyright (c) 2025 +All rights reserved. diff --git a/java/clientmod/build.gradle.kts b/java/clientmod/build.gradle.kts new file mode 100644 index 0000000..f2261e5 --- /dev/null +++ b/java/clientmod/build.gradle.kts @@ -0,0 +1,116 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") version "2.1.10" + kotlin("plugin.serialization") version "2.1.10" + id("fabric-loom") version "1.10.1" + id("maven-publish") +} + +version = project.property("mod_version") as String +group = project.property("maven_group") as String + +val ktor_version = "3.1.1" + +base { + archivesName.set(project.property("archives_base_name") as String) +} + +val targetJavaVersion = 21 +java { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +loom { + splitEnvironmentSourceSets() + + mods { + register("wynnreporter") { + sourceSet("main") + sourceSet("client") + } + } +} + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft("com.mojang:minecraft:${project.property("minecraft_version")}") + mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2") + modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}") + modImplementation("net.fabricmc:fabric-language-kotlin:${project.property("kotlin_loader_version")}") + + modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}") + + + // non minecraft-related dependencies + implementation("io.ktor:ktor-client-core:$ktor_version") + implementation("io.ktor:ktor-client-cio:$ktor_version") + implementation("io.ktor:ktor-client-resources:$ktor_version") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0") +} + +tasks.processResources { + inputs.property("version", project.version) + inputs.property("minecraft_version", project.property("minecraft_version")) + inputs.property("loader_version", project.property("loader_version")) + filteringCharset = "UTF-8" + + filesMatching("fabric.mod.json") { + expand( + "version" to project.version, + "minecraft_version" to project.property("minecraft_version"), + "loader_version" to project.property("loader_version"), + "kotlin_loader_version" to project.property("kotlin_loader_version") + ) + } +} + +tasks.withType().configureEach { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + options.encoding = "UTF-8" + options.release.set(targetJavaVersion) +} + +tasks.withType().configureEach { + compilerOptions.jvmTarget.set(JvmTarget.fromTarget(targetJavaVersion.toString())) +} + +tasks.jar { + from("LICENSE") { + rename { "${it}_${project.base.archivesName}" } + } +} + +// configure the maven publication +publishing { + publications { + create("mavenJava") { + artifactId = project.property("archives_base_name") as String + from(components["java"]) + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/java/clientmod/gradle.properties b/java/clientmod/gradle.properties new file mode 100644 index 0000000..27e3f17 --- /dev/null +++ b/java/clientmod/gradle.properties @@ -0,0 +1,15 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.21.4 +yarn_mappings=1.21.4+build.8 +loader_version=0.16.10 +kotlin_loader_version=1.13.1+kotlin.2.1.10 +# Mod Properties +mod_version=1.0-SNAPSHOT +maven_group=org.tux +archives_base_name=clientmod +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.118.0+1.21.4 diff --git a/java/clientmod/gradle/wrapper/gradle-wrapper.properties b/java/clientmod/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..56c13af --- /dev/null +++ b/java/clientmod/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip diff --git a/java/clientmod/settings.gradle.kts b/java/clientmod/settings.gradle.kts new file mode 100644 index 0000000..05eb23d --- /dev/null +++ b/java/clientmod/settings.gradle.kts @@ -0,0 +1,8 @@ +pluginManagement { + repositories { + maven("https://maven.fabricmc.net/") { + name = "Fabric" + } + gradlePluginPortal() + } +} diff --git a/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/WynnreporterClient.kt b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/WynnreporterClient.kt new file mode 100644 index 0000000..7d7ea09 --- /dev/null +++ b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/WynnreporterClient.kt @@ -0,0 +1,49 @@ +package org.tux.wynnreporter.client + +import client.kotlin.org.tux.wynnreporter.client.singleton.InventoryReader +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import net.fabricmc.api.ClientModInitializer +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents +import net.minecraft.client.MinecraftClient +import org.tux.wynnreporter.Wynnreporter +import org.tux.wynnreporter.client.processors.InventoryProcessor +import org.tux.wynnreporter.client.structs.InventoryPayload + +class WynnreporterClient : ClientModInitializer { + + private val inventoryPayloadChannel = Channel() + private val inventoryReader: InventoryReader = InventoryReader(inventoryPayloadChannel) + + + @OptIn(DelicateCoroutinesApi::class) + override fun onInitializeClient() { + Wynnreporter.LOGGER.info("initializing client") + ClientTickEvents.END_CLIENT_TICK.register(this::onEndClientTick) + val inventoryProcessor = InventoryProcessor(inventoryPayloadChannel) + // can you tell i write golang lol + runBlocking { + GlobalScope.launch{ + inventoryProcessor.start() + } + } + ClientLifecycleEvents.CLIENT_STARTED.register { client -> + Wynnreporter.LOGGER.info("client starting") + Wynnreporter.LOGGER.info("client started") + } + ClientLifecycleEvents.CLIENT_STOPPING.register { client -> + Wynnreporter.LOGGER.info("client stopping") + inventoryPayloadChannel.close() + Wynnreporter.LOGGER.info("client stopped") + } + } + + fun onEndClientTick(client: MinecraftClient?) { + inventoryReader.handle(client) + } + +} diff --git a/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/processors/InventoryProcessor.kt b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/processors/InventoryProcessor.kt new file mode 100644 index 0000000..05720e2 --- /dev/null +++ b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/processors/InventoryProcessor.kt @@ -0,0 +1,16 @@ +package org.tux.wynnreporter.client.processors + +import kotlinx.coroutines.channels.Channel +import org.tux.wynnreporter.Wynnreporter +import org.tux.wynnreporter.client.structs.InventoryPayload + +class InventoryProcessor ( + val invChannel: Channel +){ + suspend fun start() { + for(item in invChannel) { + Wynnreporter.LOGGER.info("got item {}", item) + } + Wynnreporter.LOGGER.info("inventory payload channel closed. exiting") + } +} \ No newline at end of file diff --git a/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/readers/InventoryReader.kt b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/readers/InventoryReader.kt new file mode 100644 index 0000000..71da2c3 --- /dev/null +++ b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/readers/InventoryReader.kt @@ -0,0 +1,69 @@ +package client.kotlin.org.tux.wynnreporter.client.singleton + +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.onFailure +import net.minecraft.client.MinecraftClient +import net.minecraft.client.gui.screen.Screen +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen +import org.tux.wynnreporter.Wynnreporter +import org.tux.wynnreporter.client.structs.InventoryPayload +import org.tux.wynnreporter.client.structs.InventoryPayloadItem +import java.util.* + + +class InventoryReader( + val invChannel: Channel +){ + private var previousScreen: Screen? = null + + private fun tick(client: MinecraftClient, screen: Screen) { + val containerScreen = screen as? GenericContainerScreen ?: return + val inventory = containerScreen.screenHandler.inventory + if(inventory.isEmpty) { + return + } + + val itemList = mutableListOf() + // iterate over size + for(i in 0 until inventory.size()) { + val stack= inventory.getStack(i) + if(stack.isEmpty) { + continue + } + val item = stack.item; + val components = stack.components + val componentMap = mutableMapOf() + components.forEach { component -> + componentMap[component.type.toString()] = component.value.toString() + } + // create a copy to send to the channel so that we dont block main thread :) + itemList.add(InventoryPayloadItem( + item.name.toString(), + stack.count, + componentMap, + )) + } + val payload = InventoryPayload( + client.player?.uuid ?: UUID(0,0), + containerScreen.title.toString(), + itemList, + ) + invChannel.trySend(payload).onFailure { cause -> + Wynnreporter.LOGGER.warn("failed to send inventory payload", cause) + }; + } + + fun handle(client: MinecraftClient?) { + val screen: Screen? = client?.currentScreen + // see if the screen changed + if (screen == null || screen == previousScreen) { + return + } + this.tick(client, screen) + // screen didnt change, run callback + this.previousScreen = screen + } + + + +} \ No newline at end of file diff --git a/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/services/ApiClient.kt b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/services/ApiClient.kt new file mode 100644 index 0000000..80c826d --- /dev/null +++ b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/services/ApiClient.kt @@ -0,0 +1,31 @@ +package org.tux.wynnreporter.client.services +import io.ktor.client.* +import io.ktor.client.engine.cio.* +import io.ktor.client.request.* +import io.ktor.client.plugins.* +import io.ktor.client.plugins.resources.* +import io.ktor.client.plugins.resources.Resources +import io.ktor.resources.* +import java.util.UUID + + + +class ApiClient(val rootUrl: String) { + + val client = HttpClient(CIO) { + expectSuccess = true + defaultRequest { + url(rootUrl) + } + install(Resources) + } + + suspend fun RequestChallenge(uuid: UUID) { + val response = client.get { + url("/api/v1/ingame/challenge") + } + + + + } +} diff --git a/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/services/ApiClientSpec/ApiV1.kt b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/services/ApiClientSpec/ApiV1.kt new file mode 100644 index 0000000..33f3021 --- /dev/null +++ b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/services/ApiClientSpec/ApiV1.kt @@ -0,0 +1,12 @@ +package org.tux.wynnreporter.client.services.ApiClientSpec + +import io.ktor.resources.* + +@Resource("/api/v1") +class ApiV1 { + @Resource("ingame") + class Ingame { + @Resource("challenge") + class Challenge(val parent:) + } +} diff --git a/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/services/ApiClientSpec/Responses.kt b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/services/ApiClientSpec/Responses.kt new file mode 100644 index 0000000..c08e12d --- /dev/null +++ b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/services/ApiClientSpec/Responses.kt @@ -0,0 +1,6 @@ +package org.tux.wynnreporter.client.services.ApiClientSpec + +import kotlinx.serialization.Serializable + +@Serializable +data class GetChallengeResponse() diff --git a/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/structs/InventoryPayload.kt b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/structs/InventoryPayload.kt new file mode 100644 index 0000000..6aa30ac --- /dev/null +++ b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/structs/InventoryPayload.kt @@ -0,0 +1,8 @@ +package org.tux.wynnreporter.client.structs +import java.util.UUID + +data class InventoryPayload( + val opener: UUID, + val name: String, + val items: List, + ) diff --git a/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/structs/InventoryPayloadItem.kt b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/structs/InventoryPayloadItem.kt new file mode 100644 index 0000000..a2607ef --- /dev/null +++ b/java/clientmod/src/client/kotlin/org/tux/wynnreporter/client/structs/InventoryPayloadItem.kt @@ -0,0 +1,7 @@ +package org.tux.wynnreporter.client.structs + +data class InventoryPayloadItem( + val itemName: String, + val stackSize: Int, + val components: Map, +) diff --git a/java/clientmod/src/client/resources/wynnreporter.client.mixins.json b/java/clientmod/src/client/resources/wynnreporter.client.mixins.json new file mode 100644 index 0000000..c0bed8d --- /dev/null +++ b/java/clientmod/src/client/resources/wynnreporter.client.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "org.tux.wynnreporter.mixin.client", + "compatibilityLevel": "JAVA_21", + "client": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/java/clientmod/src/main/kotlin/org/tux/wynnreporter/Wynnreporter.kt b/java/clientmod/src/main/kotlin/org/tux/wynnreporter/Wynnreporter.kt new file mode 100644 index 0000000..3d9f64d --- /dev/null +++ b/java/clientmod/src/main/kotlin/org/tux/wynnreporter/Wynnreporter.kt @@ -0,0 +1,18 @@ +package org.tux.wynnreporter + +import net.fabricmc.api.ModInitializer + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class Wynnreporter : ModInitializer { + + companion object { + private const val MOD_ID: String = "wynnreporter" + val LOGGER: Logger = LoggerFactory.getLogger(MOD_ID) + } + + override fun onInitialize() { + LOGGER.info("initializing wynnreporter") + } +} diff --git a/java/clientmod/src/main/resources/fabric.mod.json b/java/clientmod/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..fae2026 --- /dev/null +++ b/java/clientmod/src/main/resources/fabric.mod.json @@ -0,0 +1,33 @@ +{ + "schemaVersion": 1, + "id": "wynnreporter", + "version": "${version}", + "name": "wynnreporter", + "description": "", + "authors": [], + "contact": {}, + "license": "All-Rights-Reserved", + "icon": "assets/wynnreporter/icon.png", + "environment": "client", + "entrypoints": { + "client": [ + "org.tux.wynnreporter.client.WynnreporterClient" + ], + "main": [ + "org.tux.wynnreporter.Wynnreporter" + ] + }, + "mixins": [ + "wynnreporter.mixins.json", + { + "config": "wynnreporter.client.mixins.json", + "environment": "client" + } + ], + "depends": { + "fabricloader": ">=${loader_version}", + "fabric-language-kotlin": ">=${kotlin_loader_version}", + "fabric": "*", + "minecraft": "${minecraft_version}" + } +} diff --git a/java/clientmod/src/main/resources/wynnreporter.mixins.json b/java/clientmod/src/main/resources/wynnreporter.mixins.json new file mode 100644 index 0000000..ebc3227 --- /dev/null +++ b/java/clientmod/src/main/resources/wynnreporter.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "org.tux.wynnreporter.mixin", + "compatibilityLevel": "JAVA_21", + "mixins": [ + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/ts/.yarn/install-state.gz b/ts/.yarn/install-state.gz index ea23992..056125a 100644 Binary files a/ts/.yarn/install-state.gz and b/ts/.yarn/install-state.gz differ diff --git a/ts/package.json b/ts/package.json index 442487c..be1057d 100644 --- a/ts/package.json +++ b/ts/package.json @@ -18,9 +18,11 @@ "@temporalio/common": "^1.11.7", "@temporalio/worker": "^1.11.7", "@temporalio/workflow": "^1.11.7", + "@ts-rest/core": "https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/core?feat-standard-schema", + "@ts-rest/fastify": "https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/fastify?feat-standard-schema", "@types/node": "^22.13.4", "any-date-parser": "^2.0.3", - "arktype": "2.0.4", + "arktype": "2.1.1", "axios": "^1.7.9", "axios-cache-interceptor": "^1.6.2", "chrono-node": "^2.7.8", diff --git a/ts/src/activities/guild.ts b/ts/src/activities/guild.ts index 6583555..f975f06 100644 --- a/ts/src/activities/guild.ts +++ b/ts/src/activities/guild.ts @@ -60,7 +60,6 @@ export async function update_guild({ created = EXCLUDED.created ` const {total, ...rest} = parsed.members - for(const [rank_name, rank] of Object.entries(rest)){ for(const [userName, member] of Object.entries(rank)) { await sql`insert into wynn_guild_members @@ -71,7 +70,6 @@ export async function update_guild({ joined_at = EXCLUDED.joined_at, contributed = EXCLUDED.contributed ` - await sql`insert into minecraft_user (uid, name, server) values (${member.uuid}, ${userName}, ${member.server}) on conflict (uid) do update set diff --git a/ts/src/apiserver/contract.ts b/ts/src/apiserver/contract.ts new file mode 100644 index 0000000..fb4be6c --- /dev/null +++ b/ts/src/apiserver/contract.ts @@ -0,0 +1,50 @@ +import { initContract } from "@ts-rest/core/src"; +import { type } from "arktype"; + +const con = initContract(); + + +const ingameauth = con.router({ + challenge: { + description: "generate a challenge for the client to solve", + method: "GET", + path: "/challenge", + responses: { + 200: type({ + challenge: "string.uuid", + }), + }, + query: type({ + uuid: "string.uuid", + }), + }, + solve: { + description: "attempt to solve the challenge and get the token for the challenge", + method: "POST", + path: "/solve", + body: type({ + challenge: "string.uuid", + uuid: "string.uuid", + }), + responses: { + 200: type({ + success: "true", + challenge: "string.uuid", + uuid: "string.uuid", + }), + 401: type({ + success: "false", + reason: "string", + }), + }, + } +}, {pathPrefix: "/ingame"}) + +export const api = con.router({ + "ingameauth": ingameauth, +}, {pathPrefix: "/api/v1"}) + +export const contract = con.router({ + api: api +}) + diff --git a/ts/src/bot/botevent/handler.ts b/ts/src/bot/botevent/handler.ts index 05317bd..c298f3b 100644 --- a/ts/src/bot/botevent/handler.ts +++ b/ts/src/bot/botevent/handler.ts @@ -1,7 +1,8 @@ -import {bot} from "#/bot" +import {Bot, BotType} from "#/bot" import { ActivityTypes, ApplicationCommandOptionTypes, InteractionTypes } from "discordeno" import { InteractionType, MuxHandler, SlashHandler } from "./types" import { uuid4 } from "@temporalio/workflow" +import { c } from "#/di" export const slashHandler = async (interaction: InteractionType, rootHandler: SlashHandler) => { @@ -58,6 +59,7 @@ export const events = (rootHandler: SlashHandler) => {return { return }, ready: async ({shardId}) => { + const bot = await c.getAsync(Bot) await bot.gateway.editShardStatus(shardId, { status: 'online', activities: [ @@ -71,4 +73,4 @@ export const events = (rootHandler: SlashHandler) => {return { ], }) } -} as typeof bot.events} +} as BotType['events']} diff --git a/ts/src/bot/botevent/types.ts b/ts/src/bot/botevent/types.ts index e6ae460..2bd9865 100644 --- a/ts/src/bot/botevent/types.ts +++ b/ts/src/bot/botevent/types.ts @@ -1,7 +1,8 @@ -import {bot} from "#/bot" +import {BotType} from "#/bot" -export type BotEventsType = typeof bot.events -export type InteractionHandler = NonNullable + +export type BotEventsType = BotType['events'] +export type InteractionHandler = NonNullable export type InteractionType = Parameters[0] export type MuxHandler = (interaction: InteractionType, params?: T) => Promise diff --git a/ts/src/bot/index.ts b/ts/src/bot/index.ts index 03eb373..570e79c 100644 --- a/ts/src/bot/index.ts +++ b/ts/src/bot/index.ts @@ -1,4 +1,6 @@ import { config } from "#/config"; +import { c } from "#/di"; +import { InjectionToken } from "@needle-di/core"; import {createBot, Intents} from "discordeno" @@ -45,28 +47,16 @@ export const createBotWithToken = (token: string) => createBot({ } }) -export const bot = createBot({ - intents: intents.reduce((acc, curr) => acc | curr, Intents.Guilds), - token: config.DISCORD_TOKEN || "", - desiredProperties: { - interaction: { - id: true, - data: true, - type: true, - token: true, - message: true, - channelId: true, - channel: true, - guildId: true, - guild: true, - user: true, - member: true, - }, - message: { - id: true, - member: true, - guildId: true, - }, - } +export type BotType = ReturnType +export const Bot = new InjectionToken("DISCORD_BOT") +c.bind({ + provide: Bot, + async: true, + useFactory: async () => { + let token = config.DISCORD_TOKEN + if(!token) { + throw new Error('no discord token found. bot cant start'); + } + return createBotWithToken(token) + }, }) - diff --git a/ts/src/cmd/bot.ts b/ts/src/cmd/bot.ts index 5128875..86802f7 100644 --- a/ts/src/cmd/bot.ts +++ b/ts/src/cmd/bot.ts @@ -4,7 +4,7 @@ import { Command } from 'clipanion'; // di import "#/services/pg" import { DISCORD_GUILD_ID } from '#/constants'; -import { bot } from '#/bot'; +import { Bot } from '#/bot'; import { events } from '#/bot/botevent/handler'; import { SlashCommandHandler } from '#/bot/botevent/slash_commands'; import { c } from '#/di'; @@ -18,6 +18,7 @@ export class BotCommand extends Command { throw new Error('no discord token found. bot cant start'); } const rootHandler = await c.getAsync(SlashCommandHandler) + const bot = await c.getAsync(Bot) bot.events = events(rootHandler.root()) console.log('registring slash commands'); await bot.rest.upsertGuildApplicationCommands(DISCORD_GUILD_ID, rootHandler.commands()).catch(console.error) diff --git a/ts/src/cmd/worker.ts b/ts/src/cmd/worker.ts index 322574b..f7b7625 100644 --- a/ts/src/cmd/worker.ts +++ b/ts/src/cmd/worker.ts @@ -4,11 +4,8 @@ import { c } from '#/di'; import { runMigrations } from '#/services/pg/migrations'; // di -import "#/services/pg" import "#/services/temporal" - import { NativeConnection, Worker } from '@temporalio/worker'; -import { config } from '#/config'; import * as activities from '../activities'; import path from 'path'; import { Client, ScheduleNotFoundError, ScheduleOptions, ScheduleOverlapPolicy } from '@temporalio/client'; @@ -16,6 +13,9 @@ import { workflowSyncAllGuilds, workflowSyncGuilds, workflowSyncOnline, workflow import { PG } from '#/services/pg'; +import { config } from '#/config'; + + const schedules: ScheduleOptions[] = [ { diff --git a/ts/yarn.lock b/ts/yarn.lock index d87894c..26e8d9c 100644 --- a/ts/yarn.lock +++ b/ts/yarn.lock @@ -5,19 +5,19 @@ __metadata: version: 8 cacheKey: 10c0 -"@ark/schema@npm:0.39.0": - version: 0.39.0 - resolution: "@ark/schema@npm:0.39.0" +"@ark/schema@npm:0.42.0": + version: 0.42.0 + resolution: "@ark/schema@npm:0.42.0" dependencies: - "@ark/util": "npm:0.39.0" - checksum: 10c0/8a279ac688218937ca5585670e6eb8f3bac9c3ccf5f1868e3223ff9f1edabe713e76b30715510606f154e8f0208cd2e1b6551fd947f6cfae309bd7a65cf6c315 + "@ark/util": "npm:0.42.0" + checksum: 10c0/d0e93fc456d0ee835ad6fa70317202992d7ede0f847506ad61d7317ce9677f96731f2e2ab1380f8e372b1b3f6e0a95c76305a81eb0abf02184ee90fea719ac75 languageName: node linkType: hard -"@ark/util@npm:0.39.0": - version: 0.39.0 - resolution: "@ark/util@npm:0.39.0" - checksum: 10c0/0e5a580e0e238ef457c07aa68f9382ae34fae8ced93dddd5c3402bce9898b8372e2a2022327b9d234eab8adc7d9a347e731721d329049dad17be37c9257b05f8 +"@ark/util@npm:0.42.0": + version: 0.42.0 + resolution: "@ark/util@npm:0.42.0" + checksum: 10c0/7639c66aa876c475d66f41c8767e4977958293c5516e36cbd6541ce98421b091d6ec97455522e7a0361cfca5383991e4fbc40ccc3fd9bf701a0ac227561cccbc languageName: node linkType: hard @@ -746,6 +746,30 @@ __metadata: languageName: node linkType: hard +"@ts-rest/core@https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/core?feat-standard-schema": + version: 3.52.0 + resolution: "@ts-rest/core@https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/core?feat-standard-schema" + peerDependencies: + "@types/node": ^18.18.7 || >=20.8.4 + zod: ^3.24.0 + peerDependenciesMeta: + "@types/node": + optional: true + zod: + optional: true + checksum: 10c0/fba55ca7d1a5161d3ec1af850c9c98d34efd1b4373ad8ca4108737c2d75fa11ef033da6e9cb657cd5078465c09ff6761c029053f9c59022075b8d3e128f298a5 + languageName: node + linkType: hard + +"@ts-rest/fastify@https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/fastify?feat-standard-schema": + version: 3.52.0 + resolution: "@ts-rest/fastify@https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/fastify?feat-standard-schema" + peerDependencies: + fastify: ^4.0.0 + checksum: 10c0/8e8a31fda5a49c4fc976962df29129a24ad3c9c4896af38a6deb95bf60e36943c29bef56ff601bb63cab883d845095c7793ae266e92876cf74a7a05d238ba00f + languageName: node + linkType: hard + "@types/eslint-scope@npm:^3.7.7": version: 3.7.7 resolution: "@types/eslint-scope@npm:3.7.7" @@ -1116,13 +1140,13 @@ __metadata: languageName: node linkType: hard -"arktype@npm:2.0.4": - version: 2.0.4 - resolution: "arktype@npm:2.0.4" +"arktype@npm:2.1.1": + version: 2.1.1 + resolution: "arktype@npm:2.1.1" dependencies: - "@ark/schema": "npm:0.39.0" - "@ark/util": "npm:0.39.0" - checksum: 10c0/2bff7326ffebedee74ada3456147a3adb833df939db0c48cb9a78e6aa433592cc1f1877999a2ed0559f1021b70ab1dfbdfa3a6eee2b52d0a5dd68e0249471acf + "@ark/schema": "npm:0.42.0" + "@ark/util": "npm:0.42.0" + checksum: 10c0/6e416a61bbd6890fb589be222bf69b86b79145cda1a025fb87b6a78818629669a7cc8aadd6261b8d99e74d43e8858d959fcacdebd222341f97e73eefa3a39498 languageName: node linkType: hard @@ -1176,10 +1200,12 @@ __metadata: "@temporalio/common": "npm:^1.11.7" "@temporalio/worker": "npm:^1.11.7" "@temporalio/workflow": "npm:^1.11.7" + "@ts-rest/core": "https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/core?feat-standard-schema" + "@ts-rest/fastify": "https://gitpkg.vercel.app/aidant/ts-rest/libs/ts-rest/fastify?feat-standard-schema" "@types/node": "npm:^22.13.4" "@types/object-hash": "npm:^3" any-date-parser: "npm:^2.0.3" - arktype: "npm:2.0.4" + arktype: "npm:2.1.1" axios: "npm:^1.7.9" axios-cache-interceptor: "npm:^1.6.2" barrelsby: "npm:^2.8.1"