diff --git a/src/main/java/icu/samnyan/aqua/net/Safety.kt b/src/main/java/icu/samnyan/aqua/net/Safety.kt new file mode 100644 index 00000000..5e5492f9 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/net/Safety.kt @@ -0,0 +1,71 @@ +package icu.samnyan.aqua.net + +import ext.HTTP +import ext.async +import ext.toJson +import icu.samnyan.aqua.net.games.BaseEntity +import io.ktor.client.call.* +import io.ktor.client.request.* +import io.ktor.http.* +import jakarta.persistence.Entity +import kotlinx.serialization.Serializable +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.context.annotation.Configuration +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Service + +@Configuration +@ConfigurationProperties(prefix = "aqua-net.openai") +class OpenAIConfig { + var apiKey: String = "" +} + +@Entity +class AquaNetSafety : BaseEntity() { + var content: String = "" + var safe: Boolean = false +} + +interface AquaNetSafetyRepo : JpaRepository { + fun findByContent(content: String): AquaNetSafety? +} + +@Serializable +data class OpenAIResp( + val id: String, + val model: String, + val results: List +) + +@Serializable +data class OpenAIMod( + val flagged: Boolean, + val categories: Map, + val categoryScores: Map, +) + +@Service +class AquaNetSafetyService( + val safety: AquaNetSafetyRepo, + val openAIConfig: OpenAIConfig +) { + suspend fun isSafe(content: String): Boolean { + if (content.isBlank()) return true + + async { safety.findByContent(content) }?.let { return it.safe } + + // Query OpenAI + HTTP.post("https://api.openai.com/v1/moderations") { + header("Authorization", "Bearer ${openAIConfig.apiKey}") + header("Content-Type", "application/json") + setBody(mapOf("input" to content).toJson()) + }.let { + if (!it.status.isSuccess()) return true + val body = it.body>() + return AquaNetSafety().apply { + this.content = content + this.safe = !body.results.first().flagged + }.also { safety.save(it) }.safe + } + } +} diff --git a/src/main/resources/db/migration/mariadb/V1000_16__safety.sql b/src/main/resources/db/migration/mariadb/V1000_16__safety.sql new file mode 100644 index 00000000..7b59d158 --- /dev/null +++ b/src/main/resources/db/migration/mariadb/V1000_16__safety.sql @@ -0,0 +1,8 @@ +CREATE TABLE aqua_net_safety +( + id BIGINT AUTO_INCREMENT NOT NULL, + content VARCHAR(255) NOT NULL, + safe BIT(1) NOT NULL, + CONSTRAINT pk_aqua_net_safety PRIMARY KEY (id), + CONSTRAINT uq_aqua_net_safety_content UNIQUE (content) +); \ No newline at end of file