Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ buildUponDefaultConfig: true
style:
WildcardImport:
active: false
MaxLineLength:
active: true
maxLineLength: 150
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
kotlin = "2.3.20"
kotlin = "2.4.0-Beta1"
coroutines = "1.10.2"
serialization = "1.10.0"
ktor = "3.4.2"
Expand Down
4 changes: 4 additions & 0 deletions saltify-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ kotlin {
}
}

compilerOptions {
freeCompilerArgs.add("-Xcontext-parameters")
}

explicitApi()
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import org.ntqqrev.saltify.core.forward
import org.ntqqrev.saltify.core.node
import org.ntqqrev.saltify.core.text
import org.ntqqrev.saltify.dsl.SaltifyPlugin
import org.ntqqrev.saltify.entity.RegisteredCommandInfo
import org.ntqqrev.saltify.entity.RegisteredSubCommandInfo
import org.ntqqrev.saltify.entity.RegisteredCommand
import org.ntqqrev.saltify.entity.RegisteredSubCommand
import org.ntqqrev.saltify.extension.command
import org.ntqqrev.saltify.extension.respond

/**
Expand Down Expand Up @@ -49,7 +50,7 @@ public val commandHelp: SaltifyPlugin<Unit> = SaltifyPlugin("command-help") {

private fun buildCommandGroupText(
pluginName: String?,
commands: List<RegisteredCommandInfo>
commands: List<RegisteredCommand>
): String = buildString {
if (pluginName != null) {
appendLine("插件 $pluginName: ")
Expand Down Expand Up @@ -81,7 +82,7 @@ private fun buildCommandGroupText(
}.trimEnd()

private fun StringBuilder.appendSubCommand(
sub: RegisteredSubCommandInfo,
sub: RegisteredSubCommand,
parentPath: String,
depth: Int
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import kotlinx.coroutines.launch
import org.ntqqrev.milky.Event
import org.ntqqrev.milky.IncomingMessage
import org.ntqqrev.saltify.dsl.SaltifyPlugin
import org.ntqqrev.saltify.entity.env.event
import org.ntqqrev.saltify.extension.on
import org.ntqqrev.saltify.extension.plainText
import org.ntqqrev.saltify.model.EventConnectionState
import org.ntqqrev.saltify.model.SaltifyComponentType
Expand Down Expand Up @@ -53,8 +55,8 @@ public val defaultLogging: SaltifyPlugin<Unit> = SaltifyPlugin("default-logging"
}
}

// 收到消息日志
on<Event.MessageReceive> { event ->
// 消息日志
on<Event.MessageReceive> {
when (val data = event.data) {
is IncomingMessage.Group ->
logger.debug(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import org.ntqqrev.saltify.annotation.WithApiExtension
import org.ntqqrev.saltify.dsl.SaltifyPluginContext
import org.ntqqrev.saltify.dsl.config.SaltifyApplicationConfig
import org.ntqqrev.saltify.entity.InstalledPlugin
import org.ntqqrev.saltify.entity.RegisteredCommandInfo
import org.ntqqrev.saltify.entity.RegisteredCommand
import org.ntqqrev.saltify.exception.ApiCallException
import org.ntqqrev.saltify.model.EventConnectionState
import org.ntqqrev.saltify.model.EventConnectionType
Expand Down Expand Up @@ -100,7 +100,7 @@ public sealed class SaltifyApplication(internal val config: SaltifyApplicationCo

private val loadedPlugins = mutableListOf<SaltifyPluginContext>()

internal val commandRegistry: MutableList<RegisteredCommandInfo> = mutableListOf()
internal val commandRegistry: MutableList<RegisteredCommand> = mutableListOf()

@PublishedApi
internal val accessToken: String? = config.connection.accessToken
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,29 @@
package org.ntqqrev.saltify.dsl

import io.ktor.util.logging.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withTimeoutOrNull
import org.ntqqrev.milky.Event
import org.ntqqrev.milky.IncomingMessage
import org.ntqqrev.milky.OutgoingSegment
import org.ntqqrev.saltify.annotation.ContextParametersMigrationNeeded
import org.ntqqrev.saltify.annotation.SaltifyDsl
import org.ntqqrev.saltify.core.SaltifyApplication
import org.ntqqrev.saltify.core.recallGroupMessage
import org.ntqqrev.saltify.core.recallPrivateMessage
import org.ntqqrev.saltify.entity.SaltifyCommandRequirementContext
import org.ntqqrev.saltify.extension.respond
import org.ntqqrev.saltify.entity.CommandRequirementMatch
import org.ntqqrev.saltify.entity.env.EventEnvironment
import org.ntqqrev.saltify.model.CommandError
import org.ntqqrev.saltify.model.CommandRequirement
import org.ntqqrev.saltify.model.milky.SendMessageOutput
import kotlin.reflect.KClass
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

@SaltifyDsl
public class SaltifyCommandContext internal constructor() {
public var description: String = ""
public val parameter: SaltifyParameterBuilder = SaltifyParameterBuilder(this)

@PublishedApi
internal val parameters: MutableList<SaltifyCommandParamDef<*>> = mutableListOf()
internal val subCommands = mutableListOf<Pair<String, SaltifyCommandContext>>()
internal var executionBlock: (suspend SaltifyCommandExecutionContext.() -> Unit)? = null

/**
* 指令的描述信息。
*/
public var description: String = ""
internal var groupExecutionBlock: (suspend SaltifyCommandExecutionContext.() -> Unit)? = null
internal var privateExecutionBlock: (suspend SaltifyCommandExecutionContext.() -> Unit)? = null
internal var failureBlock: (suspend SaltifyCommandExecutionContext.(CommandError) -> Unit)? = null
internal var requirementBlock: (SaltifyCommandRequirementContext.() -> CommandRequirement)? = null
internal var executionBlock: (suspend CommandExecutionContext.() -> Unit)? = null
internal var groupExecutionBlock: (suspend CommandExecutionContext.() -> Unit)? = null
internal var privateExecutionBlock: (suspend CommandExecutionContext.() -> Unit)? = null
internal var failureBlock: (suspend CommandExecutionContext.(CommandError) -> Unit)? = null
internal var requirementBlock: (CommandRequirementMatch.() -> CommandRequirement)? = null

/**
* 注册一个子指令。
Expand All @@ -49,42 +35,42 @@ public class SaltifyCommandContext internal constructor() {
/**
* 定义指令执行要求。若不满足,静默返回。
*/
public fun require(block: SaltifyCommandRequirementContext.() -> CommandRequirement) {
public fun require(block: CommandRequirementMatch.() -> CommandRequirement) {
this.requirementBlock = block
}

/**
* 设置通用的指令执行逻辑。
*/
public fun onExecute(block: suspend SaltifyCommandExecutionContext.() -> Unit) {
public fun onExecute(block: suspend CommandExecutionContext.() -> Unit) {
executionBlock = block
}

/**
* 设置仅在群聊中触发的执行逻辑。优先级高于 [onExecute],定义后在群聊不会使用 [onExecute]。
*/
public fun onGroupExecute(block: suspend SaltifyCommandExecutionContext.() -> Unit) {
public fun onGroupExecute(block: suspend CommandExecutionContext.() -> Unit) {
groupExecutionBlock = block
}

/**
* 设置仅在私聊中触发的执行逻辑。优先级高于 [onExecute],定义后在群聊不会使用 [onExecute]。
* 设置仅在私聊中触发的执行逻辑。优先级高于 [onExecute],定义后在私聊不会使用 [onExecute]。
*/
public fun onPrivateExecute(block: suspend SaltifyCommandExecutionContext.() -> Unit) {
public fun onPrivateExecute(block: suspend CommandExecutionContext.() -> Unit) {
privateExecutionBlock = block
}

/**
* 当指令**解析**失败时执行的逻辑。
*/
public fun onFailure(block: suspend SaltifyCommandExecutionContext.(CommandError) -> Unit) {
public fun onFailure(block: suspend CommandExecutionContext.(CommandError) -> Unit) {
failureBlock = block
}
}

public class SaltifyParameterBuilder(@PublishedApi internal val context: SaltifyCommandContext) {
/**
* 定义一个指令参数。请搭配 [SaltifyCommandExecutionContext.value] 使用。
* 定义一个指令参数。请搭配 [CommandExecutionContext.value] 使用。
*
* @param isGreedy 是否是贪婪参数,即是否包含后面的所有剩余文本。
* @param transform 将原始文本转化为目标类型的函数。
Expand All @@ -99,12 +85,12 @@ public class SaltifyParameterBuilder(@PublishedApi internal val context: Saltify
}
}

public class SaltifyCommandExecutionContext(
public val client: SaltifyApplication,
public val event: Event.MessageReceive,
public class CommandExecutionContext(
public override val client: SaltifyApplication,
public override val event: Event.MessageReceive,
commandName: String,
private val argumentMap: Map<SaltifyCommandParamDef<*>, Any?>
) {
) : EventEnvironment<Event.MessageReceive>(event, client) {
public val logger: Logger = KtorSimpleLogger("Saltify/cmd:$commandName")

/**
Expand All @@ -113,49 +99,6 @@ public class SaltifyCommandExecutionContext(
@Suppress("UNCHECKED_CAST")
public val <T : Any> SaltifyCommandParamDef<T>.value: T
get() = (argumentMap[this] as? ParameterParseResult.Success<T>)?.value!!

/**
* 响应指令。
*/
public suspend fun respond(
block: MutableList<OutgoingSegment>.() -> Unit
): SendMessageOutput = event.respond(client, block)

/**
* 响应指令,并在指定延迟后撤回消息。
*/
@ContextParametersMigrationNeeded
public suspend inline fun respondWithRecall(
delay: Duration,
noinline block: MutableList<OutgoingSegment>.() -> Unit
) {
val output = respond(block)
delay(delay)
when (val data = event.data) {
is IncomingMessage.Group -> client.recallGroupMessage(data.peerId, output.messageSeq)
else -> client.recallPrivateMessage(data.peerId, output.messageSeq)
}
}

/**
* 获取由指令触发者发送的下一条消息事件。超时返回 null。
*/
public suspend fun awaitNextMessage(timeout: Duration = 30.seconds): Event.MessageReceive? {
val messageFlow = client.eventFlow.filterIsInstance<Event.MessageReceive>()

return withTimeoutOrNull(timeout) {
messageFlow.first { nextEvent ->
when (val contextData = event.data) {
is IncomingMessage.Group -> {
nextEvent.data is IncomingMessage.Group &&
(nextEvent.data as IncomingMessage.Group).group.groupId == contextData.group.groupId &&
nextEvent.senderId == event.senderId
}
else -> nextEvent.senderId == event.senderId
}
}
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,21 @@ package org.ntqqrev.saltify.dsl

import io.ktor.util.logging.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import org.ntqqrev.milky.Event
import org.ntqqrev.milky.IncomingMessage
import org.ntqqrev.milky.OutgoingSegment
import org.ntqqrev.saltify.annotation.ContextParametersMigrationNeeded
import org.ntqqrev.saltify.annotation.SaltifyDsl
import org.ntqqrev.saltify.core.SaltifyApplication
import org.ntqqrev.saltify.core.recallGroupMessage
import org.ntqqrev.saltify.core.recallPrivateMessage
import org.ntqqrev.saltify.core.text
import org.ntqqrev.saltify.entity.SaltifyBotConfig
import org.ntqqrev.saltify.extension.command
import org.ntqqrev.saltify.extension.on
import org.ntqqrev.saltify.extension.regex
import org.ntqqrev.saltify.extension.respond
import org.ntqqrev.saltify.model.milky.SendMessageOutput
import kotlin.time.Duration
import org.ntqqrev.saltify.entity.env.ApplicationEnvironment

@SaltifyDsl
public class SaltifyPluginContext internal constructor(
pluginName: String,
public val client: SaltifyApplication,
public override val client: SaltifyApplication,
@PublishedApi internal val pluginScope: CoroutineScope
) : CoroutineScope by pluginScope {
) : CoroutineScope by pluginScope, ApplicationEnvironment(client) {
public val logger: Logger = KtorSimpleLogger("Saltify/plugin:$pluginName")

internal val onStartHooks = mutableListOf<suspend () -> Unit>()
internal val onStopHooks = mutableListOf<() -> Unit>()

public val logger: Logger = KtorSimpleLogger("Saltify/plugin:$pluginName")

/**
* 插件被加载,即 [SaltifyApplication.Companion.invoke] 后执行的逻辑。
*/
Expand All @@ -45,64 +30,6 @@ public class SaltifyPluginContext internal constructor(
public fun onStop(block: () -> Unit) {
onStopHooks.add(block)
}

/**
* 注册一个事件监听器。
*/
public inline fun <reified T : Event> on(
crossinline block: suspend SaltifyApplication.(event: T) -> Unit
): Job = client.on(pluginScope, block)

/**
* 注册一个消息正则匹配监听器。
*/
public inline fun regex(
regex: String,
crossinline block: suspend SaltifyApplication.(
event: Event.MessageReceive,
matches: Sequence<MatchResult>
) -> Unit
): Job = client.regex(regex, pluginScope, block)

/**
* 注册一个指令。
*/
public fun command(
name: String,
prefix: String = SaltifyBotConfig.commandPrefix,
block: SaltifyCommandContext.() -> Unit
): Job = client.command(name, prefix, pluginScope, block)

/**
* 响应事件。
*/
public suspend fun Event.MessageReceive.respond(
block: MutableList<OutgoingSegment>.() -> Unit
): SendMessageOutput = respond(client, block)

/**
* 响应事件。这是用于返回纯文本的简写。
*/
@ContextParametersMigrationNeeded
public suspend fun Event.MessageReceive.respond(
text: Any?
): SendMessageOutput = respond { text(text.toString()) }

/**
* 响应事件,并在指定延迟后撤回消息。
*/
@ContextParametersMigrationNeeded
public suspend inline fun Event.MessageReceive.respondWithRecall(
delay: Duration,
noinline block: MutableList<OutgoingSegment>.() -> Unit
) {
val output = respond(block)
delay(delay)
when (data) {
is IncomingMessage.Group -> client.recallGroupMessage(peerId, output.messageSeq)
else -> client.recallPrivateMessage(peerId, output.messageSeq)
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.ntqqrev.saltify.entity

import org.ntqqrev.saltify.dsl.CommandExecutionContext

public class CommandRequirementMatch(
public val context: CommandExecutionContext
)
Loading