Skip to content

Commit

Permalink
build: 1.0.9
Browse files Browse the repository at this point in the history
  • Loading branch information
cssxsh committed Oct 15, 2022
1 parent 527513f commit 1b32bd8
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 39 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
* [Chat Command](https://github.com/project-mirai/chat-command)

会自动下载 [EhTagTranslation](https://github.com/EhTagTranslation/Database) 翻译词库
可以对接 [NaiFu](#NaiFu) 本地搭建
可以对接 [NaiFu](#NaiFu) 本地搭建
或者你可以 利用 [colab](https://colab.research.google.com/drive/1_Ma71L6uGbtt6UQyA3FjqW2lcZ5Bjck-#scrollTo=KZ88G-iWCTs7) 进行在线搭建
将搭建得到的 url 例如 `https://xxx.trycloudflare.com/` 填入 `config.yml``naifu_api` 配置项
注意 `trycloudflare.com` 可以不受墙的影响,所以不用配置代理

## 指令

Expand Down Expand Up @@ -45,7 +48,7 @@
## NaiFu

`naifu` 是基于 novelai 官方 web 端的修改版
相关信息可以看这 <https://www.bilibili.com/video/BV14e4y1E74X>
相关信息可以看这 <https://colab.research.google.com/drive/1_Ma71L6uGbtt6UQyA3FjqW2lcZ5Bjck-#scrollTo=KZ88G-iWCTs7>

## TODO

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
}

group = "xyz.cssxsh.mirai.novelai"
version = "1.0.8"
version = "1.0.9"

repositories {
mavenLocal()
Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/xyz/cssxsh/mirai/novelai/NovelAiHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ public object NovelAiHelper : KotlinPlugin(
JvmPluginDescription(
id = "xyz.cssxsh.mirai.plugin.novelai-helper",
name = "novelai-helper",
version = "1.0.8",
version = "1.0.9",
) {
author("cssxsh")
}
) {
public val client: NovelAiClient = NovelAiClient(config = NovelAiHelperConfig, default = true)
public val client: NovelAiClient = NovelAiClient(config = NovelAiHelperConfig)

public fun translate(word: String): String? {
for (translation in NovelAiHelperConfig.database.data) {
Expand All @@ -36,6 +36,7 @@ public object NovelAiHelper : KotlinPlugin(
NovelAiHelperConfig.reload()
NovelAiHelperConfig.save()
NovelAiCommand.register()
NovelAiFuCommand.register()
NovelAiLoginCommand.register()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package xyz.cssxsh.mirai.novelai.command

import io.ktor.client.call.*
import io.ktor.client.network.sockets.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.util.*
import io.ktor.utils.io.core.*
import kotlinx.coroutines.*
import kotlinx.serialization.json.*
import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.message.data.*
Expand All @@ -13,17 +16,72 @@ import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
import xyz.cssxsh.mirai.novelai.*
import xyz.cssxsh.mirai.novelai.data.*
import xyz.cssxsh.novelai.*
import xyz.cssxsh.novelai.ai.*
import kotlin.random.*

public object NovelAiFuCommand : SimpleCommand(
owner = NovelAiHelper,
"nai-fu", "naifu",
description = "生成图片"
) {
public val client: NovelAiClient = NovelAiClient(config = NovelAiHelperConfig, default = false)

private val random = Random(seed = System.currentTimeMillis())

private suspend fun colab(prompt: String, block: JsonObjectBuilder.() -> Unit = {}): AiGenerateImage {
val body = buildJsonObject {
put("prompt", prompt)
put("height", 768)
put("width", 512)
put("scale", 12)
put("n_samples", 1)
put("noise", 0.2)
put("sampler", "k_euler_ancestral")
put("steps", 28)
put("strength", 0.7)
put("uc", NovelAiHelperConfig.ban)
put("ucPreset", 0)

block.invoke(this)
}
val statement = NovelAiHelper.client.http.preparePost {
url {
takeFrom(NovelAiHelperConfig.local)
takeFrom("/generate-stream")
}
setBody(body)
contentType(ContentType.Application.Json)
}
val packet = with(statement) {
var cause: Exception? = null
while (kotlin.coroutines.coroutineContext.isActive) {
return@with try {
body<ByteReadPacket>()
} catch (exception: SocketTimeoutException) {
cause = exception
continue
} catch (exception: ConnectTimeoutException) {
cause = exception
continue
}
}
throw CancellationException("generate image timeout", cause)
}
var event = ""
var id = 0L
var data = ByteArray(0)
while (packet.canRead()) {
val key = packet.readUTF8UntilDelimiter(":")
packet.readByte()
when (key.trim()) {
"event" -> event = packet.readUTF8UntilDelimiter("\n").trim()
"id" -> id = packet.readUTF8UntilDelimiter("\n").trim().toLong()
"data" -> data = packet.decodeBase64Bytes().readBytes()
}
}

return AiGenerateImage(event, id, data)
}

@Handler
public suspend fun CommandSenderOnMessage<*>.handle(vararg tags: String) {
this as UserCommandSender
Expand All @@ -48,7 +106,7 @@ public object NovelAiFuCommand : SimpleCommand(
fromEvent.message.findIsInstance<Image>()?.let { source ->
try {
val url = source.queryUrl()
val response = client.http.get(url)
val response = NovelAiHelper.client.http.get(url)
val packet = response.body<ByteReadPacket>()
params["image"] = packet.encodeBase64()
} catch (cause: Exception) {
Expand All @@ -60,7 +118,7 @@ public object NovelAiFuCommand : SimpleCommand(
val seed = random.nextLong(0, 2L shl 32 - 1)
NovelAiHelper.logger.info(input.joinToString(", ", "generate image seed: $seed, tags: "))
val generate = try {
client.ai.generateImage(input = input.joinToString(",")) {
colab(prompt = input.joinToString(",")) {
put("seed", seed)
params.forEach { (key, value) ->
when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public object NovelAiHelperConfig : ReadOnlyPluginConfig("config"), NovelAiClien
override val image: ImageModel by value(ImageModel.SAFE_DIFFUSION)

@ValueName("naifu_api")
override val baseUrl: String by value("http://127.0.0.1:6969/")
public val local: String by value("http://127.0.0.1:6969/")

override var token: String = ""
get() {
Expand Down
7 changes: 2 additions & 5 deletions src/main/kotlin/xyz/cssxsh/novelai/NovelAiClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import xyz.cssxsh.novelai.subscription.*
import xyz.cssxsh.novelai.user.*
import java.net.*

public open class NovelAiClient(internal val config: NovelAiClientConfig, internal val default: Boolean = false) {
public open class NovelAiClient(internal val config: NovelAiClientConfig) {
public open val http: HttpClient = HttpClient(OkHttp) {
install(ContentNegotiation) {
json(json = Json)
Expand All @@ -39,8 +39,8 @@ public open class NovelAiClient(internal val config: NovelAiClientConfig, intern
HttpStatusCode.Conflict -> throw NovelAiApiException(error = response.body())
HttpStatusCode.InternalServerError -> throw NovelAiApiException(error = response.body())
}
if (response.status.value in 400..499) throw ClientRequestException(response, response.body())
if (response.status.value in 500..599) throw ServerResponseException(response, response.body())
if ((response.contentLength() ?: 0) == 0L) throw ServerResponseException(response, response.body())
}
}
Auth {
Expand All @@ -64,9 +64,6 @@ public open class NovelAiClient(internal val config: NovelAiClientConfig, intern
}
BrowserUserAgent()
ContentEncoding()
defaultRequest {
if (default) url("https://api.novelai.net") else url(config.baseUrl)
}
engine {
config {
dns(
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/xyz/cssxsh/novelai/NovelAiClientConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ public interface NovelAiClientConfig {
public val timeout: Long
public val image: ImageModel
public var token: String
public val baseUrl: String get() = "https://api.novelai.net"
public val ban: String get() = "lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry"
}
18 changes: 7 additions & 11 deletions src/main/kotlin/xyz/cssxsh/novelai/ai/AIController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class AIController(private val client: NovelAiClient) {
model = model,
parameters = buildJsonObject(block)
)
val response = client.http.post("/ai/generate") {
val response = client.http.post("https://api.novelai.net/ai/generate") {
setBody(body)
contentType(ContentType.Application.Json)
}
Expand All @@ -32,7 +32,7 @@ public class AIController(private val client: NovelAiClient) {
model = model,
parameters = buildJsonObject(block)
)
val response = client.http.post("/ai/generate-stream") {
val response = client.http.post("https://api.novelai.net/ai/generate-stream") {
setBody(body)
contentType(ContentType.Application.Json)
}
Expand Down Expand Up @@ -67,7 +67,7 @@ public class AIController(private val client: NovelAiClient) {
block.invoke(this)
}
)
val statement = client.http.preparePost("/ai/generate-image") {
val statement = client.http.preparePost("https://api.novelai.net/ai/generate-image") {
setBody(body)
contentType(ContentType.Application.Json)
}
Expand All @@ -89,40 +89,36 @@ public class AIController(private val client: NovelAiClient) {
var event = ""
var id = 0L
var data = ByteArray(0)
var key = ""
while (packet.canRead()) {
key = packet.readUTF8UntilDelimiter(":")
val key = packet.readUTF8UntilDelimiter(":")
packet.readByte()
when (key.trim()) {
"event" -> event = packet.readUTF8UntilDelimiter("\n").trim()
"id" -> id = packet.readUTF8UntilDelimiter("\n").trim().toLong()
"data" -> data = packet.decodeBase64Bytes().readBytes()
}
}
if (data.isEmpty()) {
throw NovelAiApiException(error = Json.decodeFromString(key))
}

return AiGenerateImage(event, id, data)
}

public suspend fun classify(): AiSequenceClassification {
val response = client.http.post("/ai/classify") {
val response = client.http.post("https://api.novelai.net/ai/classify") {
contentType(ContentType.Application.Json)
}
return response.body()
}

public suspend fun generateImageTags(model: String, prompt: String): AiRequestImageGenerationTags {
val response = client.http.get("/ai/generate-image/suggest-tags") {
val response = client.http.get("https://api.novelai.net/ai/generate-image/suggest-tags") {
parameter("model", model)
parameter("prompt", prompt)
}
return response.body()
}

public suspend fun generateVoice(text: String, seed: String): AiSequenceClassification {
val response = client.http.get("/ai/generate-voice") {
val response = client.http.get("https://api.novelai.net/ai/generate-voice") {
parameter("text", text)
parameter("seed", seed)
parameter("voice", "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import xyz.cssxsh.novelai.*
public class SubscriptionController(private val client: NovelAiClient) {
public suspend fun bind(id: String, processor: PaymentProcessor) {
val body = BindSubscriptionRequest(paymentProcessor = processor, subscriptionId = id)
val response = client.http.post("/user/subscription/bind") {
val response = client.http.post("https://api.novelai.net/user/subscription/bind") {
setBody(body)
contentType(ContentType.Application.Json)
}
Expand All @@ -17,7 +17,7 @@ public class SubscriptionController(private val client: NovelAiClient) {

public suspend fun change(plan: SubscriptionPlan) {
val body = ChangeSubscriptionPlanRequest(newSubscriptionPlan = plan)
val response = client.http.post("/user/subscription/change") {
val response = client.http.post("https://api.novelai.net/user/subscription/change") {
setBody(body)
contentType(ContentType.Application.Json)
}
Expand Down
22 changes: 11 additions & 11 deletions src/main/kotlin/xyz/cssxsh/novelai/user/UserController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class UserController(private val client: NovelAiClient) {
key = key,
recaptcha = recaptcha
)
val response = client.http.post("/user/register") {
val response = client.http.post("https://api.novelai.net/user/register") {
setBody(body)
contentType(ContentType.Application.Json)
}
Expand Down Expand Up @@ -57,7 +57,7 @@ public class UserController(private val client: NovelAiClient) {
val salt = blake2b(16, password.substring(0, 6) + email + "novelai_data_access_key")
val key = argon2(password, salt, 64).substring(0, 64)
val body = LoginRequest(key = key)
val response = client.http.post("/user/login") {
val response = client.http.post("https://api.novelai.net/user/login") {
setBody(body)
contentType(ContentType.Application.Json)
}
Expand All @@ -68,7 +68,7 @@ public class UserController(private val client: NovelAiClient) {

public suspend fun changeAccessKey(current: String, new: String, email: String): String {
val body = ChangeAccessKeyRequest(currentAccessKey = current, newAccessKey = new, newEmail = email)
val response = client.http.post("/user/change-access-key") {
val response = client.http.post("https://api.novelai.net/user/change-access-key") {
setBody(body)
contentType(ContentType.Application.Json)
}
Expand All @@ -79,7 +79,7 @@ public class UserController(private val client: NovelAiClient) {

public suspend fun sendEmailVerification(email: String) {
val body = EmailVerificationStartRequest(email = email)
val response = client.http.post("/user/resend-email-verification") {
val response = client.http.post("https://api.novelai.net/user/resend-email-verification") {
setBody(body)
contentType(ContentType.Application.Json)
}
Expand All @@ -88,41 +88,41 @@ public class UserController(private val client: NovelAiClient) {

public suspend fun verifyEmail(token: String) {
val body = EmailVerificationRequest(verificationToken = token)
val response = client.http.post("/user/verify-email") {
val response = client.http.post("https://api.novelai.net/user/verify-email") {
setBody(body)
contentType(ContentType.Application.Json)
}
response.body<ByteArray>()
}

public suspend fun information(): AccountInformation {
val response = client.http.get("/user/information")
val response = client.http.get("https://api.novelai.net/user/information")
return response.body()
}

public suspend fun data(): UserAccountData {
val response = client.http.get("/user/data")
val response = client.http.get("https://api.novelai.net/user/data")
return response.body()
}

public suspend fun priority(): Priority {
val response = client.http.get("/user/priority")
val response = client.http.get("https://api.novelai.net/user/priority")
return response.body()
}

public suspend fun giftkeys(): List<Any> {
val response = client.http.get("/user/giftkeys")
val response = client.http.get("https://api.novelai.net/user/giftkeys")
val data = response.body<GiftKeysResponse>()
return data.giftKeys
}

public suspend fun subscription(): Subscription {
val response = client.http.get("/user/subscription")
val response = client.http.get("https://api.novelai.net/user/subscription")
return response.body()
}

public suspend fun keystore(): Keystore {
val response = client.http.get("/user/keystore")
val response = client.http.get("https://api.novelai.net/user/keystore")
return response.body()
}
}

0 comments on commit 1b32bd8

Please sign in to comment.