mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
425b570683
@ -1,6 +1,6 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Bintray Publish
|
||||
name: Dev Tag Publishing
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the master branch
|
||||
@ -9,9 +9,6 @@ on:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- '*-dev*'
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
63
.github/workflows/ReleasePublishing.yml
vendored
Normal file
63
.github/workflows/ReleasePublishing.yml
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Bintray Publish
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the master branch
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Gradle clean
|
||||
run: ./gradlew clean
|
||||
- name: Gradle build
|
||||
run: ./gradlew build # if test's failed, don't publish
|
||||
- name: Check keys
|
||||
run: ./gradlew
|
||||
:mirai-console:ensureBintrayAvailable
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
- name: Gradle :mirai-console:fillBuildConstants
|
||||
run: ./gradlew
|
||||
:mirai-console:fillBuildConstants
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
- name: Gradle :mirai-console:bintrayUpload
|
||||
run: ./gradlew
|
||||
:mirai-console:bintrayUpload
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
- name: Gradle :mirai-console-terminal:bintrayUpload
|
||||
run: ./gradlew
|
||||
:mirai-console-terminal:bintrayUpload
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
- name: Gradle :mirai-console-compiler-common:bintrayUpload
|
||||
run: ./gradlew
|
||||
:mirai-console-compiler-common:bintrayUpload
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
- name: Gradle :mirai-console-intellij:bintrayUpload
|
||||
run: ./gradlew
|
||||
:mirai-console-intellij:bintrayUpload
|
||||
-Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }}
|
||||
-Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||
- name: Publish Gradle plugin
|
||||
run: ./gradlew
|
||||
:mirai-console-gradle:publishPlugins
|
||||
-Dgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }} -Pgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }}
|
||||
-Dgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }} -Pgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }}
|
@ -143,7 +143,7 @@ public interface MiraiConsole : CoroutineScope {
|
||||
return when (password) {
|
||||
is ByteArray -> Bot(id, password, config)
|
||||
is String -> Bot(id, password, config)
|
||||
else -> null!!
|
||||
else -> throw IllegalArgumentException("Bad password type: `${password.javaClass.name}`. Require ByteArray or String")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,22 +20,38 @@ import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser.Com
|
||||
import net.mamoe.mirai.console.command.descriptor.PermissionIdValueArgumentParser
|
||||
import net.mamoe.mirai.console.command.descriptor.PermitteeIdValueArgumentParser
|
||||
import net.mamoe.mirai.console.command.descriptor.buildCommandArgumentContext
|
||||
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl.allRegisteredCommands
|
||||
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig
|
||||
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.*
|
||||
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.PasswordKind.PLAIN
|
||||
import net.mamoe.mirai.console.internal.permission.BuiltInPermissionService
|
||||
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.internal.util.runIgnoreException
|
||||
import net.mamoe.mirai.console.permission.Permission
|
||||
import net.mamoe.mirai.console.permission.Permission.Companion.parentsWithSelf
|
||||
import net.mamoe.mirai.console.permission.PermissionService
|
||||
import net.mamoe.mirai.console.permission.PermissionService.Companion.cancel
|
||||
import net.mamoe.mirai.console.permission.PermissionService.Companion.findCorrespondingPermissionOrFail
|
||||
import net.mamoe.mirai.console.permission.PermissionService.Companion.getPermittedPermissions
|
||||
import net.mamoe.mirai.console.permission.PermissionService.Companion.permit
|
||||
import net.mamoe.mirai.console.permission.PermitteeId
|
||||
import net.mamoe.mirai.console.plugin.name
|
||||
import net.mamoe.mirai.console.plugin.version
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
||||
import net.mamoe.mirai.event.events.EventCancelledException
|
||||
import net.mamoe.mirai.message.nextMessageOrNull
|
||||
import net.mamoe.mirai.utils.secondsToMillis
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.lang.management.MemoryUsage
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.math.floor
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
||||
@ -205,9 +221,19 @@ public object BuiltInCommands {
|
||||
@SubCommand("permittedPermissions", "pp", "grantedPermissions", "gp")
|
||||
public suspend fun CommandSender.permittedPermissions(
|
||||
@Name("被许可人 ID") target: PermitteeId,
|
||||
@Name("包括重复") all: Boolean = false,
|
||||
) {
|
||||
val grantedPermissions = target.getPermittedPermissions()
|
||||
sendMessage(grantedPermissions.joinToString("\n") { it.id.toString() })
|
||||
var grantedPermissions = target.getPermittedPermissions().toList()
|
||||
if (!all) {
|
||||
grantedPermissions = grantedPermissions.filter { thisPerm ->
|
||||
grantedPermissions.none { other -> thisPerm.parentsWithSelf.drop(1).any { it == other } }
|
||||
}
|
||||
}
|
||||
if (grantedPermissions.isEmpty()) {
|
||||
sendMessage("${target.asString()} 未被授予任何权限. 使用 `${CommandManager.commandPrefix}permission grant` 给予权限.")
|
||||
} else {
|
||||
sendMessage(grantedPermissions.joinToString("\n") { it.id.toString() })
|
||||
}
|
||||
}
|
||||
|
||||
@Description("查看所有权限列表")
|
||||
@ -216,4 +242,203 @@ public object BuiltInCommands {
|
||||
sendMessage(PermissionService.INSTANCE.getRegisteredPermissions().joinToString("\n") { it.id.toString() })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public object AutoLoginCommand : CompositeCommand(
|
||||
ConsoleCommandOwner, "autoLogin", "自动登录",
|
||||
description = "自动登录设置",
|
||||
overrideContext = buildCommandArgumentContext {
|
||||
ConfigurationKey::class with ConfigurationKey.Parser
|
||||
}
|
||||
), BuiltInCommandInternal {
|
||||
@Description("查看自动登录账号列表")
|
||||
@SubCommand
|
||||
public suspend fun CommandSender.list() {
|
||||
sendMessage(buildString {
|
||||
for (account in AutoLoginConfig.accounts) {
|
||||
if (account.account == "123456") continue
|
||||
append("- ")
|
||||
append("账号: ")
|
||||
append(account.account)
|
||||
appendLine()
|
||||
append(" 密码: ")
|
||||
append(account.password.value)
|
||||
appendLine()
|
||||
|
||||
if (account.configuration.isNotEmpty()) {
|
||||
appendLine(" 配置:")
|
||||
for ((key, value) in account.configuration) {
|
||||
append(" $key = $value")
|
||||
}
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Description("添加自动登录")
|
||||
@SubCommand
|
||||
public suspend fun CommandSender.add(account: Long, password: String, passwordKind: PasswordKind = PLAIN) {
|
||||
val accountStr = account.toString()
|
||||
if (AutoLoginConfig.accounts.any { it.account == accountStr }) {
|
||||
sendMessage("已有相同账号在自动登录配置中. 请先删除该账号.")
|
||||
return
|
||||
}
|
||||
AutoLoginConfig.accounts.add(AutoLoginConfig.Account(accountStr, Password(passwordKind, password)))
|
||||
sendMessage("已成功添加 '$account'.")
|
||||
}
|
||||
|
||||
@Description("清除所有配置")
|
||||
@SubCommand
|
||||
public suspend fun CommandSender.clear() {
|
||||
AutoLoginConfig.accounts.clear()
|
||||
sendMessage("已清除所有自动登录配置.")
|
||||
}
|
||||
|
||||
@Description("删除一个账号")
|
||||
@SubCommand
|
||||
public suspend fun CommandSender.remove(account: Long) {
|
||||
val accountStr = account.toString()
|
||||
if (AutoLoginConfig.accounts.removeIf { it.account == accountStr }) {
|
||||
sendMessage("已成功删除 '$account'.")
|
||||
return
|
||||
}
|
||||
sendMessage("账号 '$account' 未配置自动登录.")
|
||||
}
|
||||
|
||||
@Description("设置一个账号的一个配置项")
|
||||
@SubCommand
|
||||
public suspend fun CommandSender.setConfig(account: Long, configKey: ConfigurationKey, value: String) {
|
||||
val accountStr = account.toString()
|
||||
|
||||
val oldAccount = AutoLoginConfig.accounts.find { it.account == accountStr } ?: kotlin.run {
|
||||
sendMessage("未找到账号 $account.")
|
||||
return
|
||||
}
|
||||
|
||||
if (value.isEmpty()) return removeConfig(account, configKey)
|
||||
|
||||
val newAccount = oldAccount.copy(configuration = oldAccount.configuration.toMutableMap().apply {
|
||||
put(configKey, value)
|
||||
})
|
||||
|
||||
AutoLoginConfig.accounts.remove(oldAccount)
|
||||
AutoLoginConfig.accounts.add(newAccount)
|
||||
|
||||
sendMessage("成功修改 '$account' 的配置 '$configKey' 为 '$value'")
|
||||
}
|
||||
|
||||
@Description("删除一个账号的一个配置项")
|
||||
@SubCommand
|
||||
public suspend fun CommandSender.removeConfig(account: Long, configKey: ConfigurationKey) {
|
||||
val accountStr = account.toString()
|
||||
|
||||
val oldAccount = AutoLoginConfig.accounts.find { it.account == accountStr } ?: kotlin.run {
|
||||
sendMessage("未找到账号 $account.")
|
||||
return
|
||||
}
|
||||
|
||||
val newAccount = oldAccount.copy(configuration = oldAccount.configuration.toMutableMap().apply {
|
||||
remove(configKey)
|
||||
})
|
||||
|
||||
AutoLoginConfig.accounts.remove(oldAccount)
|
||||
AutoLoginConfig.accounts.add(newAccount)
|
||||
|
||||
sendMessage("成功删除 '$account' 的配置 '$configKey'.")
|
||||
}
|
||||
}
|
||||
|
||||
public object StatusCommand : SimpleCommand(
|
||||
ConsoleCommandOwner, "status", "states", "状态",
|
||||
description = "获取 Mirai Console 运行状态"
|
||||
), BuiltInCommandInternal {
|
||||
@Handler
|
||||
public suspend fun CommandSender.handle() {
|
||||
sendMessage(buildString {
|
||||
val buildDateFormatted =
|
||||
MiraiConsoleBuildConstants.buildDate.atZone(ZoneId.systemDefault())
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||
|
||||
append("Running MiraiConsole v${MiraiConsoleBuildConstants.versionConst}, built on ").append(buildDateFormatted)
|
||||
.append(".\n")
|
||||
append(MiraiConsoleImplementationBridge.frontEndDescription.render()).append("\n\n")
|
||||
append("Permission Service: ").append(
|
||||
if (PermissionService.INSTANCE is BuiltInPermissionService) {
|
||||
"Built In Permission Service"
|
||||
} else {
|
||||
val plugin = PermissionServiceProvider.providerPlugin
|
||||
if (plugin == null) {
|
||||
PermissionService.INSTANCE.toString()
|
||||
} else {
|
||||
"${plugin.name} v${plugin.version}"
|
||||
}
|
||||
}
|
||||
)
|
||||
append("\n\n")
|
||||
|
||||
append("Plugins: ")
|
||||
if (PluginManagerImpl.resolvedPlugins.isEmpty()) {
|
||||
append("<none>")
|
||||
} else {
|
||||
PluginManagerImpl.resolvedPlugins.joinTo(this) { plugin ->
|
||||
"${plugin.name} v${plugin.version}"
|
||||
}
|
||||
}
|
||||
append("\n\n")
|
||||
val memoryMXBean = ManagementFactory.getMemoryMXBean()
|
||||
|
||||
append("Object Pending Finalization Count: ")
|
||||
.append(memoryMXBean.objectPendingFinalizationCount)
|
||||
.append("\n")
|
||||
|
||||
append(" Heap Memory: ")
|
||||
renderMemoryUsage(memoryMXBean.heapMemoryUsage)
|
||||
append("\nNon-Heap Memory: ")
|
||||
renderMemoryUsage(memoryMXBean.nonHeapMemoryUsage)
|
||||
})
|
||||
}
|
||||
|
||||
private const val MEM_B = 1024L
|
||||
private const val MEM_KB = 1024L shl 10
|
||||
private const val MEM_MB = 1024L shl 20
|
||||
private const val MEM_GB = 1024L shl 30
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun StringBuilder.appendDouble(number: Double): StringBuilder =
|
||||
append(floor(number * 100) / 100)
|
||||
|
||||
private fun StringBuilder.renderMemoryUsageNumber(num: Long) {
|
||||
when {
|
||||
num == -1L -> {
|
||||
append(num)
|
||||
}
|
||||
num < MEM_B -> {
|
||||
append(num).append("B")
|
||||
}
|
||||
num < MEM_KB -> {
|
||||
appendDouble(num / 1024.0).append("KB")
|
||||
}
|
||||
num < MEM_MB -> {
|
||||
appendDouble((num ushr 10) / 1024.0).append("MB")
|
||||
}
|
||||
else -> {
|
||||
appendDouble((num ushr 20) / 1024.0).append("GB")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun StringBuilder.renderMemoryUsage(usage: MemoryUsage) {
|
||||
append("(committed / init / used / max) [")
|
||||
renderMemoryUsageNumber(usage.committed)
|
||||
append(", ")
|
||||
renderMemoryUsageNumber(usage.init)
|
||||
append(", ")
|
||||
renderMemoryUsageNumber(usage.used)
|
||||
append(", ")
|
||||
renderMemoryUsageNumber(usage.max)
|
||||
append("]")
|
||||
}
|
||||
}
|
||||
}
|
@ -14,18 +14,14 @@ package net.mamoe.mirai.console.command
|
||||
import net.mamoe.mirai.console.command.descriptor.*
|
||||
import net.mamoe.mirai.console.command.parse.CommandCall
|
||||
import net.mamoe.mirai.console.command.parse.CommandValueArgument
|
||||
import net.mamoe.mirai.console.command.resolve.InterceptedReason
|
||||
import net.mamoe.mirai.console.command.resolve.ResolvedCommandCall
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* 指令的执行返回
|
||||
*
|
||||
* 注意: 现阶段
|
||||
*
|
||||
* @see CommandExecuteStatus
|
||||
*/
|
||||
@ConsoleExperimentalApi("Not yet implemented")
|
||||
@ExperimentalCommandDescriptors
|
||||
public sealed class CommandExecuteResult {
|
||||
/** 指令执行时发生的错误 (如果有) */
|
||||
@ -97,6 +93,21 @@ public sealed class CommandExecuteResult {
|
||||
public override val resolvedCall: ResolvedCommandCall? get() = null
|
||||
}
|
||||
|
||||
/** 没有匹配的指令 */
|
||||
public class Intercepted(
|
||||
/** 解析的 [CommandCall] (如果匹配到) */
|
||||
public override val call: CommandCall?,
|
||||
/** 解析的 [ResolvedCommandCall] (如果匹配到) */
|
||||
public override val resolvedCall: ResolvedCommandCall?,
|
||||
/** 尝试执行的指令 (如果匹配到) */
|
||||
public override val command: Command?,
|
||||
/** 拦截原因 */
|
||||
public val reason: InterceptedReason,
|
||||
) : Failure() {
|
||||
/** 指令执行时发生的错误, 总是 `null` */
|
||||
public override val exception: Nothing? get() = null
|
||||
}
|
||||
|
||||
/** 权限不足 */
|
||||
public class PermissionDenied(
|
||||
/** 尝试执行的指令 */
|
||||
|
@ -14,9 +14,7 @@
|
||||
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
@ -24,11 +22,9 @@ import net.mamoe.mirai.console.command.CommandSender.Companion.asCommandSender
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.asMemberCommandSender
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.asTempCommandSender
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.toCommandSender
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandArgumentParserException
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.internal.data.castOrNull
|
||||
import net.mamoe.mirai.console.internal.data.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.plugin.rootCauseOrSelf
|
||||
import net.mamoe.mirai.console.permission.AbstractPermitteeId
|
||||
import net.mamoe.mirai.console.permission.Permittee
|
||||
import net.mamoe.mirai.console.permission.PermitteeId
|
||||
@ -47,7 +43,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
/**
|
||||
* 指令发送者.
|
||||
*
|
||||
* 只有 [CommandSender] 才能 [执行指令][CommandManager.execute]
|
||||
* 只有 [CommandSender] 才能 [执行指令][CommandManager.executeCommand]
|
||||
*
|
||||
* ## 获得指令发送者
|
||||
* - [MessageEvent.toCommandSender]
|
||||
@ -74,7 +70,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
* - [AbstractUserCommandSender] 代表用户
|
||||
* - [ConsoleCommandSender] 代表控制台
|
||||
*
|
||||
* 二级子类, 当指令由插件 [主动执行][CommandManager.execute] 时, 插件应使用 [toCommandSender] 或 [asCommandSender], 因此,
|
||||
* 二级子类, 当指令由插件 [主动执行][CommandManager.executeCommand] 时, 插件应使用 [toCommandSender] 或 [asCommandSender], 因此,
|
||||
* - 若在群聊环境, 对应 [CommandSender] 为 [MemberCommandSender]
|
||||
* - 若在私聊环境, 对应 [CommandSender] 为 [FriendCommandSender]
|
||||
* - 若在临时会话环境, 对应 [CommandSender] 为 [TempCommandSender]
|
||||
@ -173,9 +169,6 @@ public interface CommandSender : CoroutineScope, Permittee {
|
||||
@JvmBlockingBridge
|
||||
public suspend fun sendMessage(message: String): MessageReceipt<Contact>?
|
||||
|
||||
@ConsoleExperimentalApi("This is unstable and might get changed")
|
||||
public suspend fun catchExecutionException(e: Throwable)
|
||||
|
||||
public companion object {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -274,154 +267,54 @@ public sealed class AbstractCommandSender : CommandSender, CoroutineScope {
|
||||
public abstract override val subject: Contact?
|
||||
public abstract override val user: User?
|
||||
public abstract override fun toString(): String
|
||||
|
||||
@ConsoleExperimentalApi("This is unstable and might get changed")
|
||||
override suspend fun catchExecutionException(e: Throwable) {
|
||||
if (this is CommandSenderOnMessage<*>) {
|
||||
val cause = e.rootCauseOrSelf
|
||||
|
||||
// TODO: 2020/10/17
|
||||
// CommandArgumentParserException 作为 IllegalCommandArgumentException 不会再进入此函数
|
||||
// 已在
|
||||
// - [console] CommandManagerImpl.commandListener
|
||||
// - [terminal] ConsoleThread.kt
|
||||
// 处理
|
||||
|
||||
val message = cause
|
||||
.takeIf { it is CommandArgumentParserException }?.message
|
||||
?: "${cause::class.simpleName.orEmpty()}: ${cause.message}"
|
||||
|
||||
// TODO: 2020/8/30 优化 net.mamoe.mirai.console.command.CommandSender.catchExecutionException
|
||||
|
||||
sendMessage(message) // \n\n60 秒内发送 stacktrace 查看堆栈信息
|
||||
this@AbstractCommandSender.launch(CoroutineName("stacktrace delayer from command")) {
|
||||
if (fromEvent.nextMessageOrNull(60_000) {
|
||||
it.message.contentEquals("stacktrace") || it.message.contentEquals("stack")
|
||||
} != null) {
|
||||
sendMessage(e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sendMessage(e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 [this] 为 [AbstractCommandSender] 时返回.
|
||||
*
|
||||
* 正常情况下, 所有 [CommandSender] 都应该继承 [AbstractCommandSender]
|
||||
*
|
||||
* 在需要类型智能转换等情况时可使用此函数.
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若函数正常返回, Kotlin 编译器认为 [this] 是 [AbstractCommandSender] 实例并执行智能类型转换.
|
||||
*
|
||||
* @return `this`
|
||||
*/
|
||||
public fun CommandSender.checkIsAbstractCommandSender(): AbstractCommandSender {
|
||||
contract {
|
||||
returns() implies (this@checkIsAbstractCommandSender is AbstractCommandSender)
|
||||
}
|
||||
check(this is AbstractCommandSender) { "A CommandSender must extend AbstractCommandSender" }
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 [this] 为 [AbstractUserCommandSender] 时返回.
|
||||
*
|
||||
* 正常情况下, 所有 [UserCommandSender] 都应该继承 [AbstractUserCommandSender]
|
||||
*
|
||||
* 在需要类型智能转换等情况时可使用此函数.
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若函数正常返回, Kotlin 编译器认为 [this] 是 [AbstractUserCommandSender] 实例并执行智能类型转换.
|
||||
*
|
||||
* @return `this`
|
||||
*/
|
||||
public fun UserCommandSender.checkIsAbstractUserCommandSender(): AbstractUserCommandSender {
|
||||
contract {
|
||||
returns() implies (this@checkIsAbstractUserCommandSender is AbstractUserCommandSender)
|
||||
}
|
||||
check(this is AbstractUserCommandSender) { "A UserCommandSender must extend AbstractUserCommandSender" }
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 [this] 为 [ConsoleCommandSender] 时返回 `true`
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若返回 `true`, Kotlin 编译器认为 [this] 是 [ConsoleCommandSender] 实例并执行智能类型转换.
|
||||
* - 若返回 `false`, Kotlin 编译器认为 [this] 是 [UserCommandSender] 实例并执行智能类型转换.
|
||||
*/
|
||||
public fun CommandSender.isConsole(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isConsole is ConsoleCommandSender)
|
||||
returns(false) implies (this@isConsole is UserCommandSender)
|
||||
}
|
||||
this.checkIsAbstractCommandSender()
|
||||
return this is ConsoleCommandSender
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 [this] 不为 [ConsoleCommandSender], 即为 [UserCommandSender] 时返回 `true`.
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若返回 `true`, Kotlin 编译器认为 [this] 是 [UserCommandSender] 实例并执行智能类型转换.
|
||||
* - 若返回 `false`, Kotlin 编译器认为 [this] 是 [ConsoleCommandSender] 实例并执行智能类型转换.
|
||||
* 当 [this] 不为 [ConsoleCommandSender] 时返回 `true`
|
||||
*/
|
||||
public fun CommandSender.isNotConsole(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isNotConsole is UserCommandSender)
|
||||
returns(false) implies (this@isNotConsole is ConsoleCommandSender)
|
||||
returns(true) implies (this@isNotConsole !is ConsoleCommandSender)
|
||||
}
|
||||
this.checkIsAbstractCommandSender()
|
||||
return this !is ConsoleCommandSender
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 [this] 为 [UserCommandSender] 时返回 `true`
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若返回 `true`, Kotlin 编译器认为 [this] 是 [UserCommandSender] 实例并执行智能类型转换.
|
||||
* - 若返回 `false`, Kotlin 编译器认为 [this] 是 [ConsoleCommandSender] 实例并执行智能类型转换.
|
||||
*/
|
||||
public fun CommandSender.isUser(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isUser is UserCommandSender)
|
||||
returns(false) implies (this@isUser is ConsoleCommandSender)
|
||||
}
|
||||
this.checkIsAbstractCommandSender()
|
||||
return this is UserCommandSender
|
||||
}
|
||||
|
||||
/**
|
||||
* 当 [this] 不为 [UserCommandSender], 即为 [ConsoleCommandSender] 时返回 `true`
|
||||
*
|
||||
* ### 契约
|
||||
* 本函数定义契约,
|
||||
* - 若返回 `true`, Kotlin 编译器认为 [this] 是 [ConsoleCommandSender] 实例并执行智能类型转换.
|
||||
* - 若返回 `false`, Kotlin 编译器认为 [this] 是 [UserCommandSender] 实例并执行智能类型转换.
|
||||
*/
|
||||
public fun CommandSender.isNotUser(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isNotUser is ConsoleCommandSender)
|
||||
returns(false) implies (this@isNotUser is UserCommandSender)
|
||||
}
|
||||
this.checkIsAbstractCommandSender()
|
||||
return this !is UserCommandSender
|
||||
}
|
||||
|
||||
/**
|
||||
* 折叠 [AbstractCommandSender] 的两种可能性.
|
||||
* 折叠 [AbstractCommandSender] 的可能性.
|
||||
*
|
||||
* - 当 [this] 为 [ConsoleCommandSender] 时执行 [ifIsConsole]
|
||||
* - 当 [this] 为 [UserCommandSender] 时执行 [ifIsUser]
|
||||
* - 否则执行 [otherwise]
|
||||
*
|
||||
* ### 示例
|
||||
* ```
|
||||
@ -438,20 +331,23 @@ public fun CommandSender.isNotUser(): Boolean {
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* @return [ifIsConsole] 或 [ifIsUser] 执行结果.
|
||||
* @return [ifIsConsole], [ifIsUser] 或 [otherwise] 执行结果.
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun <R> CommandSender.fold(
|
||||
ifIsConsole: ConsoleCommandSender.() -> R,
|
||||
ifIsUser: UserCommandSender.() -> R,
|
||||
otherwise: CommandSender.() -> R = { error("CommandSender ${this::class.qualifiedName} is not supported") },
|
||||
): R {
|
||||
contract {
|
||||
callsInPlace(ifIsConsole, InvocationKind.AT_MOST_ONCE)
|
||||
callsInPlace(ifIsUser, InvocationKind.AT_MOST_ONCE)
|
||||
callsInPlace(otherwise, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
return when (val sender = this.checkIsAbstractCommandSender()) {
|
||||
return when (val sender = this) {
|
||||
is ConsoleCommandSender -> ifIsConsole(sender)
|
||||
is AbstractUserCommandSender -> ifIsUser(sender)
|
||||
is UserCommandSender -> ifIsUser(sender)
|
||||
else -> otherwise(sender)
|
||||
}
|
||||
}
|
||||
|
||||
@ -477,7 +373,7 @@ public inline fun <R> UserCommandSender.foldContext(
|
||||
callsInPlace(inGroup, InvocationKind.AT_MOST_ONCE)
|
||||
callsInPlace(inPrivate, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
return when (val sender = this.checkIsAbstractUserCommandSender()) {
|
||||
return when (val sender = this) {
|
||||
is MemberCommandSender -> inGroup(sender)
|
||||
else -> inPrivate(sender)
|
||||
}
|
||||
@ -603,7 +499,7 @@ public sealed class AbstractUserCommandSender : UserCommandSender, AbstractComma
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个 [好友][Friend] 执行指令, 但不一定是通过私聊方式, 也有可能是由插件在代码直接执行 ([CommandManager.execute])
|
||||
* 代表一个 [好友][Friend] 执行指令, 但不一定是通过私聊方式, 也有可能是由插件在代码直接执行 ([CommandManager.executeCommand])
|
||||
* @see FriendCommandSenderOnMessage 代表一个真实的 [好友][Friend] 主动在私聊消息执行指令
|
||||
*/
|
||||
public open class FriendCommandSender internal constructor(
|
||||
@ -622,7 +518,7 @@ public open class FriendCommandSender internal constructor(
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个 [群员][Member] 执行指令, 但不一定是通过群内发消息方式, 也有可能是由插件在代码直接执行 ([CommandManager.execute])
|
||||
* 代表一个 [群员][Member] 执行指令, 但不一定是通过群内发消息方式, 也有可能是由插件在代码直接执行 ([CommandManager.executeCommand])
|
||||
* @see MemberCommandSenderOnMessage 代表一个真实的 [群员][Member] 主动在群内发送消息执行指令.
|
||||
*/
|
||||
public open class MemberCommandSender internal constructor(
|
||||
@ -644,7 +540,7 @@ public open class MemberCommandSender internal constructor(
|
||||
}
|
||||
|
||||
/**
|
||||
* 代表一个 [群员][Member] 通过临时会话执行指令, 但不一定是通过私聊方式, 也有可能是由插件在代码直接执行 ([CommandManager.execute])
|
||||
* 代表一个 [群员][Member] 通过临时会话执行指令, 但不一定是通过私聊方式, 也有可能是由插件在代码直接执行 ([CommandManager.executeCommand])
|
||||
* @see TempCommandSenderOnMessage 代表一个 [群员][Member] 主动在临时会话发送消息执行指令
|
||||
*/
|
||||
public open class TempCommandSender internal constructor(
|
||||
|
@ -71,7 +71,7 @@ public interface CommandArgumentContext {
|
||||
*
|
||||
* @see EmptyCommandArgumentContext
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmField // public static final CommandArgumentContext EMPTY;
|
||||
public val EMPTY: CommandArgumentContext = EmptyCommandArgumentContext
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import net.mamoe.mirai.console.command.descriptor.AbstractCommandValueParameter.
|
||||
import net.mamoe.mirai.console.command.descriptor.AbstractCommandValueParameter.UserDefinedType.Companion.createRequired
|
||||
import net.mamoe.mirai.console.command.descriptor.ArgumentAcceptance.Companion.isAcceptable
|
||||
import net.mamoe.mirai.console.command.parse.CommandValueArgument
|
||||
import net.mamoe.mirai.console.command.resolve.ResolvedCommandValueArgument
|
||||
import net.mamoe.mirai.console.internal.data.classifierAsKClass
|
||||
import net.mamoe.mirai.console.internal.data.classifierAsKClassOrNull
|
||||
import net.mamoe.mirai.console.internal.data.typeOf0
|
||||
@ -52,13 +53,31 @@ public abstract class AbstractCommandParameter<T> : CommandParameter<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherited instances must be [AbstractCommandValueParameter]
|
||||
* Inherited instances must be [AbstractCommandValueParameter].
|
||||
*
|
||||
* ### Implementation details
|
||||
*
|
||||
* [CommandValueParameter] should:
|
||||
* - implement [equals], [hashCode] since used in [ResolvedCommandValueArgument].
|
||||
* - implement [toString] to produce user-friendly textual representation of this parameter with type info.
|
||||
*
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface CommandValueParameter<T : Any?> : CommandParameter<T> {
|
||||
|
||||
public val isVararg: Boolean
|
||||
|
||||
/**
|
||||
* Checks whether this [CommandValueParameter] accepts [argument].
|
||||
*
|
||||
* An [argument] can be accepted if:
|
||||
* - [CommandValueArgument.type] is subtype of, or equals to [CommandValueParameter.type] (nullability considered), or
|
||||
* - [CommandValueArgument.typeVariants] produces
|
||||
*
|
||||
* @return `true` if [argument] may be accepted through any approach mentioned above.
|
||||
*
|
||||
* @see accepting
|
||||
*/
|
||||
public fun accepts(argument: CommandValueArgument, commandArgumentContext: CommandArgumentContext?): Boolean =
|
||||
accepting(argument, commandArgumentContext).isAcceptable
|
||||
|
||||
@ -208,6 +227,8 @@ public sealed class AbstractCommandValueParameter<T> : CommandValueParameter<T>,
|
||||
public override val isVararg: Boolean,
|
||||
public override val type: KType,
|
||||
) : AbstractCommandValueParameter<T>() {
|
||||
override fun toString(): String = super.toString()
|
||||
|
||||
init {
|
||||
requireNotNull(type.classifierAsKClassOrNull()) {
|
||||
"type.classifier must be KClass."
|
||||
@ -237,7 +258,5 @@ public sealed class AbstractCommandValueParameter<T> : CommandValueParameter<T>,
|
||||
* Extended by [CommandValueArgumentParser]
|
||||
*/
|
||||
@ConsoleExperimentalApi
|
||||
public abstract class Extended<T> : AbstractCommandValueParameter<T>() {
|
||||
abstract override fun toString(): String
|
||||
}
|
||||
public abstract class Extended<T> : AbstractCommandValueParameter<T>() // For implementer: take care of toString()
|
||||
}
|
@ -58,9 +58,9 @@ public abstract class AbstractCommandSignature : CommandSignature {
|
||||
override fun toString(): String {
|
||||
val receiverParameter = receiverParameter
|
||||
return if (receiverParameter == null) {
|
||||
"CommandSignatureVariant(${valueParameters.joinToString()})"
|
||||
"CommandSignature(${valueParameters.joinToString()})"
|
||||
} else {
|
||||
"CommandSignatureVariant($receiverParameter, ${valueParameters.joinToString()})"
|
||||
"CommandSignature($receiverParameter, ${valueParameters.joinToString()})"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,10 @@ import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.CompositeCommand
|
||||
import net.mamoe.mirai.console.command.SimpleCommand
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser.Companion.map
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser.Companion.parse
|
||||
import net.mamoe.mirai.console.permission.PermissionId
|
||||
import net.mamoe.mirai.console.permission.PermitteeId
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import kotlin.contracts.InvocationKind
|
||||
@ -45,6 +48,9 @@ import kotlin.contracts.contract
|
||||
* - [User]: [ExistingUserValueArgumentParser]
|
||||
* - [Contact]: [ExistingContactValueArgumentParser]
|
||||
*
|
||||
* - [PermitteeId]: [PermitteeIdValueArgumentParser]
|
||||
* - [PermissionId]: [PermissionIdValueArgumentParser]
|
||||
*
|
||||
*
|
||||
* @see SimpleCommand 简单指令
|
||||
* @see CompositeCommand 复合指令
|
||||
@ -143,6 +149,9 @@ public abstract class AbstractCommandValueArgumentParser<T : Any> : CommandValue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see CommandValueArgumentParser.map
|
||||
*/
|
||||
public class MappingCommandValueArgumentParser<T : Any, R : Any>(
|
||||
private val original: CommandValueArgumentParser<T>,
|
||||
private val mapper: MappingCommandValueArgumentParser<T, R>.(T) -> R,
|
||||
|
@ -9,8 +9,6 @@
|
||||
|
||||
package net.mamoe.mirai.console.command.descriptor
|
||||
|
||||
import net.mamoe.mirai.console.command.parse.CommandCall
|
||||
import net.mamoe.mirai.console.command.parse.CommandCallParser
|
||||
import net.mamoe.mirai.console.command.parse.CommandValueArgument
|
||||
import net.mamoe.mirai.console.internal.data.castOrNull
|
||||
import net.mamoe.mirai.console.internal.data.kClassQualifiedName
|
||||
@ -19,9 +17,18 @@ import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* Implicit type variant specified by [CommandCallParser].
|
||||
* Intrinsic variant of an [CommandValueArgument].
|
||||
*
|
||||
* [TypeVariant] is not necessary for all [CommandCall]s.
|
||||
* The *intrinsic* reveals the independent conversion property of this type.
|
||||
* Conversion with [TypeVariant] is out of any contextual resource,
|
||||
* except the [output type][TypeVariant.outType] declared by the [TypeVariant] itself.
|
||||
*
|
||||
*
|
||||
* [TypeVariant] is not necessary for all [CommandValueArgument]s.
|
||||
*
|
||||
* @param OutType the type this [TypeVariant] can map a argument [Message] to .
|
||||
*
|
||||
* @see CommandValueArgument.typeVariants
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface TypeVariant<out OutType> {
|
||||
@ -31,17 +38,22 @@ public interface TypeVariant<out OutType> {
|
||||
public val outType: KType
|
||||
|
||||
/**
|
||||
* Maps an [valueArgument] to [outType]
|
||||
*
|
||||
* @see CommandValueArgument.value
|
||||
*/
|
||||
public fun mapValue(valueParameter: Message): OutType
|
||||
public fun mapValue(valueArgument: Message): OutType
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* Creates a [TypeVariant] with reified [OutType].
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@JvmSynthetic
|
||||
public inline operator fun <reified OutType> invoke(crossinline block: (valueParameter: Message) -> OutType): TypeVariant<OutType> {
|
||||
return object : TypeVariant<OutType> {
|
||||
override val outType: KType = typeOf<OutType>()
|
||||
override fun mapValue(valueParameter: Message): OutType = block(valueParameter)
|
||||
override fun mapValue(valueArgument: Message): OutType = block(valueArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,20 +63,20 @@ public interface TypeVariant<out OutType> {
|
||||
public object MessageContentTypeVariant : TypeVariant<MessageContent> {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override val outType: KType = typeOf<MessageContent>()
|
||||
override fun mapValue(valueParameter: Message): MessageContent =
|
||||
valueParameter.castOrNull<MessageContent>() ?: error("Accepts MessageContent only but given ${valueParameter.kClassQualifiedName}")
|
||||
override fun mapValue(valueArgument: Message): MessageContent =
|
||||
valueArgument.castOrNull<MessageContent>() ?: error("Accepts MessageContent only but given ${valueArgument.kClassQualifiedName}")
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public object MessageChainTypeVariant : TypeVariant<MessageChain> {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override val outType: KType = typeOf<MessageChain>()
|
||||
override fun mapValue(valueParameter: Message): MessageChain = valueParameter.asMessageChain()
|
||||
override fun mapValue(valueArgument: Message): MessageChain = valueArgument.asMessageChain()
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public object ContentStringTypeVariant : TypeVariant<String> {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override val outType: KType = typeOf<String>()
|
||||
override fun mapValue(valueParameter: Message): String = valueParameter.content
|
||||
override fun mapValue(valueArgument: Message): String = valueArgument.content
|
||||
}
|
||||
|
@ -12,29 +12,45 @@
|
||||
package net.mamoe.mirai.console.command.parse
|
||||
|
||||
import net.mamoe.mirai.console.command.Command
|
||||
import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
|
||||
import net.mamoe.mirai.console.command.resolve.CommandCallResolver
|
||||
import net.mamoe.mirai.console.command.resolve.ResolvedCommandCall
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
|
||||
/**
|
||||
* Unresolved [CommandCall].
|
||||
*
|
||||
* ### Implementation details
|
||||
* [CommandCall] should be _immutable_,
|
||||
* meaning all of its properties must be *pure* and should be implemented as an immutable property, or delegated by a lazy initializer.
|
||||
*
|
||||
* @see CommandCallParser
|
||||
* @see CommandCallResolver
|
||||
*
|
||||
* @see ResolvedCommandCall
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface CommandCall {
|
||||
/**
|
||||
* The [CommandSender] responsible to this call.
|
||||
*/
|
||||
public val caller: CommandSender
|
||||
|
||||
/**
|
||||
* One of callee [Command]'s [Command.allNames]
|
||||
* One of callee [Command]'s [Command.allNames].
|
||||
*
|
||||
* Generally [CommandCallResolver] use [calleeName] to find target [Command] registered in [CommandManager]
|
||||
*/
|
||||
public val calleeName: String
|
||||
|
||||
/**
|
||||
* Explicit value arguments
|
||||
* Explicit value arguments parsed from raw [MessageChain] or implicit ones deduced by the [CommandCallResolver].
|
||||
*/
|
||||
public val valueArguments: List<CommandValueArgument>
|
||||
|
||||
// maybe add contextual arguments, i.e. from MessageMetadata
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
|
@ -42,6 +42,12 @@ public interface CommandValueArgument : CommandArgument {
|
||||
* [MessageChain] is vararg
|
||||
*/
|
||||
public val value: Message
|
||||
|
||||
/**
|
||||
* Intrinsic variants of this argument.
|
||||
*
|
||||
* @see TypeVariant
|
||||
*/
|
||||
public val typeVariants: List<TypeVariant<*>>
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ import net.mamoe.mirai.message.data.asMessageChain
|
||||
@ExperimentalCommandDescriptors
|
||||
public object BuiltInCommandCallResolver : CommandCallResolver {
|
||||
override fun resolve(call: CommandCall): CommandResolveResult {
|
||||
val callee = CommandManager.matchCommand(call.calleeName) ?: return CommandResolveResult(null)
|
||||
val callee = CommandManager.matchCommand(call.calleeName) ?: return CommandResolveResult(CommandExecuteResult.UnresolvedCommand(call))
|
||||
|
||||
val valueArguments = call.valueArguments
|
||||
val context = callee.safeCast<CommandArgumentContextAware>()?.context
|
||||
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("unused", "NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.console.command.resolve
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
|
||||
import net.mamoe.mirai.console.command.parse.CommandCall
|
||||
import net.mamoe.mirai.console.command.parse.CommandCallParser
|
||||
import net.mamoe.mirai.console.extensions.CommandCallInterceptorProvider
|
||||
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
|
||||
import net.mamoe.mirai.console.internal.util.UNREACHABLE_CLAUSE
|
||||
import net.mamoe.mirai.console.util.safeCast
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import org.jetbrains.annotations.Contract
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
|
||||
/**
|
||||
* 指令解析和调用拦截器. 用于在指令各解析阶段拦截或转换调用.
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface CommandCallInterceptor {
|
||||
/**
|
||||
* 在指令[语法解析][CommandCallParser]前调用.
|
||||
*
|
||||
* @return `null` 表示未处理
|
||||
*/
|
||||
public fun interceptBeforeCall(
|
||||
message: Message,
|
||||
caller: CommandSender,
|
||||
): InterceptResult<Message>? = null
|
||||
|
||||
/**
|
||||
* 在指令[语法解析][CommandCallParser]后调用.
|
||||
*
|
||||
* @return `null` 表示未处理
|
||||
*/
|
||||
public fun interceptCall(
|
||||
call: CommandCall,
|
||||
): InterceptResult<CommandCall>? = null
|
||||
|
||||
/**
|
||||
* 在指令[调用解析][CommandCallResolver]后调用.
|
||||
*
|
||||
* @return `null` 表示未处理
|
||||
*/
|
||||
public fun interceptResolvedCall(
|
||||
call: ResolvedCommandCall,
|
||||
): InterceptResult<ResolvedCommandCall>? = null
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 使用 [CommandCallInterceptor] 依次调用 [interceptBeforeCall].
|
||||
* 在第一个拦截时返回拦截原因, 在所有 [CommandCallInterceptor] 都处理完成后返回结果 [Message]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun Message.intercepted(caller: CommandSender): InterceptResult<Message> {
|
||||
GlobalComponentStorage.run {
|
||||
return CommandCallInterceptorProvider.foldExtensions(this@intercepted) { acc, ext ->
|
||||
val intercepted = ext.instance.interceptBeforeCall(acc, caller)
|
||||
intercepted?.fold(
|
||||
onIntercepted = { return intercepted },
|
||||
otherwise = { it }
|
||||
) ?: acc
|
||||
}.let { InterceptResult(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 [CommandCallInterceptor] 依次调用 [interceptBeforeCall].
|
||||
* 在第一个拦截时返回拦截原因, 在所有 [CommandCallInterceptor] 都处理完成后返回结果 [CommandCall]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun CommandCall.intercepted(): InterceptResult<CommandCall> {
|
||||
GlobalComponentStorage.run {
|
||||
return CommandCallInterceptorProvider.foldExtensions(this@intercepted) { acc, ext ->
|
||||
val intercepted = ext.instance.interceptCall(acc)
|
||||
intercepted?.fold(
|
||||
onIntercepted = { return intercepted },
|
||||
otherwise = { it }
|
||||
) ?: acc
|
||||
}.let { InterceptResult(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 [CommandCallInterceptor] 依次调用 [interceptBeforeCall].
|
||||
* 在第一个拦截时返回拦截原因, 在所有 [CommandCallInterceptor] 都处理完成后返回结果 [ResolvedCommandCall]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun ResolvedCommandCall.intercepted(): InterceptResult<ResolvedCommandCall> {
|
||||
GlobalComponentStorage.run {
|
||||
return CommandCallInterceptorProvider.foldExtensions(this@intercepted) { acc, ext ->
|
||||
val intercepted = ext.instance.interceptResolvedCall(acc)
|
||||
intercepted?.fold(
|
||||
onIntercepted = { return intercepted },
|
||||
otherwise = { it }
|
||||
) ?: acc
|
||||
}.let { InterceptResult(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [CommandCallInterceptor] 拦截结果
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public class InterceptResult<T> internal constructor(
|
||||
private val _value: Any?,
|
||||
@Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any?,
|
||||
) {
|
||||
/**
|
||||
* 构造一个 [InterceptResult], 以 [value] 继续处理后续指令执行.
|
||||
*/
|
||||
public constructor(value: T) : this(value as Any?, null)
|
||||
|
||||
/**
|
||||
* 构造一个 [InterceptResult], 以 [原因][reason] 中断指令执行.
|
||||
*/
|
||||
public constructor(reason: InterceptedReason) : this(reason as Any?, null)
|
||||
|
||||
@get:Contract(pure = true)
|
||||
public val value: T?
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
get() {
|
||||
val value = this._value
|
||||
return if (value is InterceptedReason) null else value as T
|
||||
}
|
||||
|
||||
@get:Contract(pure = true)
|
||||
public val reason: InterceptedReason?
|
||||
get() = this._value.safeCast()
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public inline fun <T, R> InterceptResult<T>.fold(
|
||||
onIntercepted: (reason: InterceptedReason) -> R,
|
||||
otherwise: (call: T) -> R,
|
||||
): R {
|
||||
contract {
|
||||
callsInPlace(onIntercepted, InvocationKind.AT_MOST_ONCE)
|
||||
callsInPlace(otherwise, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
value?.let(otherwise)
|
||||
reason?.let(onIntercepted)
|
||||
UNREACHABLE_CLAUSE
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public inline fun <T : R, R> InterceptResult<T>.getOrElse(onIntercepted: (reason: InterceptedReason) -> R): R {
|
||||
contract { callsInPlace(onIntercepted, InvocationKind.AT_MOST_ONCE) }
|
||||
reason?.let(onIntercepted)
|
||||
return value!!
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个 [InterceptedReason]
|
||||
*
|
||||
* @see InterceptedReason.create
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
@JvmSynthetic
|
||||
public inline fun InterceptedReason(message: String): InterceptedReason = InterceptedReason.create(message)
|
||||
|
||||
/**
|
||||
* 拦截原因
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface InterceptedReason {
|
||||
public val message: String
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 创建一个 [InterceptedReason]
|
||||
*/
|
||||
public fun create(message: String): InterceptedReason = InterceptedReasonData(message)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCommandDescriptors::class)
|
||||
@Serializable
|
||||
private data class InterceptedReasonData(override val message: String) : InterceptedReason
|
@ -32,22 +32,35 @@ public class CommandResolveResult private constructor(
|
||||
public val failure: CommandExecuteResult.Failure?
|
||||
get() = value.safeCast()
|
||||
|
||||
public inline fun <R> fold(
|
||||
onSuccess: (ResolvedCommandCall?) -> R,
|
||||
onFailure: (CommandExecuteResult.Failure) -> R,
|
||||
): R {
|
||||
contract {
|
||||
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
|
||||
callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
failure?.let(onFailure)?.let { return it }
|
||||
return call.let(onSuccess)
|
||||
}
|
||||
|
||||
public constructor(call: ResolvedCommandCall?) : this(call as Any?)
|
||||
public constructor(call: ResolvedCommandCall) : this(call as Any?)
|
||||
public constructor(failure: CommandExecuteResult.Failure) : this(failure as Any)
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public inline fun <R> CommandResolveResult.fold(
|
||||
onSuccess: (ResolvedCommandCall?) -> R,
|
||||
onFailure: (CommandExecuteResult.Failure) -> R,
|
||||
): R {
|
||||
contract {
|
||||
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
|
||||
callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
failure?.let(onFailure)?.let { return it }
|
||||
return call.let(onSuccess)
|
||||
}
|
||||
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public inline fun CommandResolveResult.getOrElse(
|
||||
onFailure: (CommandExecuteResult.Failure) -> ResolvedCommandCall?,
|
||||
): ResolvedCommandCall {
|
||||
contract {
|
||||
callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
failure?.let(onFailure)?.let { return it }
|
||||
return call!!
|
||||
}
|
||||
|
||||
/**
|
||||
* The resolver converting a [CommandCall] into [ResolvedCommandCall] based on registered []
|
||||
*
|
||||
|
@ -24,10 +24,17 @@ import net.mamoe.mirai.console.util.cast
|
||||
/**
|
||||
* The resolved [CommandCall].
|
||||
*
|
||||
* ### Implementation details
|
||||
* [ResolvedCommandCall] should be _immutable_,
|
||||
* meaning all of its properties must be *pure* and should be implemented as an immutable property, or delegated by a lazy initializer.
|
||||
*
|
||||
* @see ResolvedCommandCallImpl
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface ResolvedCommandCall {
|
||||
/**
|
||||
* The [CommandSender] responsible to this call.
|
||||
*/
|
||||
public val caller: CommandSender
|
||||
|
||||
/**
|
||||
@ -48,7 +55,7 @@ public interface ResolvedCommandCall {
|
||||
/**
|
||||
* Resolved value arguments arranged mapping the [CommandSignature.valueParameters] by index.
|
||||
*
|
||||
* **Implementation details**: Lazy calculation.
|
||||
* **Default implementation details**: Lazy calculation.
|
||||
*/
|
||||
@ConsoleExperimentalApi
|
||||
public val resolvedValueArguments: List<ResolvedCommandValueArgument<*>>
|
||||
@ -56,18 +63,30 @@ public interface ResolvedCommandCall {
|
||||
public companion object
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolved [CommandValueParameter] for [ResolvedCommandCall.resolvedValueArguments]
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public data class ResolvedCommandValueArgument<T>(
|
||||
val parameter: CommandValueParameter<T>,
|
||||
/**
|
||||
* Argument value expected by the [parameter]
|
||||
*/
|
||||
val value: T,
|
||||
)
|
||||
|
||||
// Don't move into companion, compilation error
|
||||
/**
|
||||
* Invoke this resolved call.
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public suspend inline fun ResolvedCommandCall.call() {
|
||||
return this@call.calleeSignature.call(this@call)
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation.
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public class ResolvedCommandCallImpl(
|
||||
override val caller: CommandSender,
|
||||
|
@ -31,13 +31,15 @@ import kotlinx.serialization.SerialInfo
|
||||
* map:
|
||||
* a: b
|
||||
* ```
|
||||
*
|
||||
* @see net.mamoe.yamlkt.Comment
|
||||
*/
|
||||
@SerialInfo
|
||||
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
public annotation class ValueDescription(
|
||||
/**
|
||||
* 将会被 [String.trimIndent] 处理.
|
||||
* 将会被 [String.trimIndent] 处理
|
||||
*/
|
||||
val value: String,
|
||||
)
|
@ -23,7 +23,7 @@ package net.mamoe.mirai.console.data
|
||||
* 将被保存为配置 (YAML 作为示例):
|
||||
* ```yaml
|
||||
* AccountPluginData:
|
||||
* map:
|
||||
* info:
|
||||
* a: b
|
||||
* ```
|
||||
*
|
||||
|
@ -11,6 +11,7 @@ package net.mamoe.mirai.console.extension
|
||||
|
||||
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
|
||||
import net.mamoe.mirai.console.command.parse.CommandCallParser
|
||||
import net.mamoe.mirai.console.command.resolve.CommandCallInterceptor
|
||||
import net.mamoe.mirai.console.command.resolve.CommandCallResolver
|
||||
import net.mamoe.mirai.console.extensions.*
|
||||
import net.mamoe.mirai.console.internal.extension.AbstractConcurrentComponentStorage
|
||||
@ -128,4 +129,19 @@ public class PluginComponentStorage(
|
||||
@OverloadResolutionByLambdaReturnType
|
||||
public fun contributeCommandCallParser(provider: CommandCallResolverProvider): Unit =
|
||||
contribute(CommandCallResolverProvider, plugin, provider)
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
/** 注册一个 [CommandCallInterceptorProvider] */
|
||||
@ExperimentalCommandDescriptors
|
||||
@OverloadResolutionByLambdaReturnType
|
||||
public fun contributeCommandCallInterceptor(lazyInstance: () -> CommandCallInterceptor): Unit =
|
||||
contribute(CommandCallInterceptorProvider, plugin, CommandCallInterceptorProviderImplLazy(lazyInstance))
|
||||
|
||||
/** 注册一个 [CommandCallInterceptorProvider] */
|
||||
@ExperimentalCommandDescriptors
|
||||
@JvmName("contributeCommandCallInterceptorProvider")
|
||||
@OverloadResolutionByLambdaReturnType
|
||||
public fun contributeCommandCallParser(provider: CommandCallInterceptorProvider): Unit =
|
||||
contribute(CommandCallInterceptorProvider, plugin, provider)
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package net.mamoe.mirai.console.extensions
|
||||
|
||||
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
|
||||
import net.mamoe.mirai.console.command.resolve.CommandCallInterceptor
|
||||
import net.mamoe.mirai.console.extension.AbstractInstanceExtensionPoint
|
||||
import net.mamoe.mirai.console.extension.InstanceExtension
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface CommandCallInterceptorProvider : InstanceExtension<CommandCallInterceptor> {
|
||||
public companion object ExtensionPoint :
|
||||
AbstractInstanceExtensionPoint<CommandCallInterceptorProvider, CommandCallInterceptor>(CommandCallInterceptorProvider::class)
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public class CommandCallInterceptorProviderImpl(override val instance: CommandCallInterceptor) : CommandCallInterceptorProvider
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public class CommandCallInterceptorProviderImplLazy(initializer: () -> CommandCallInterceptor) : CommandCallInterceptorProvider {
|
||||
override val instance: CommandCallInterceptor by lazy(initializer)
|
||||
}
|
@ -11,8 +11,11 @@ package net.mamoe.mirai.console.extensions
|
||||
|
||||
import net.mamoe.mirai.console.extension.AbstractSingletonExtensionPoint
|
||||
import net.mamoe.mirai.console.extension.SingletonExtension
|
||||
import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
|
||||
import net.mamoe.mirai.console.internal.permission.BuiltInPermissionService
|
||||
import net.mamoe.mirai.console.permission.PermissionService
|
||||
import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
|
||||
/**
|
||||
* [权限服务][PermissionService] 提供器.
|
||||
@ -21,7 +24,16 @@ import net.mamoe.mirai.console.permission.PermissionService
|
||||
*/
|
||||
public interface PermissionServiceProvider : SingletonExtension<PermissionService<*>> {
|
||||
public companion object ExtensionPoint :
|
||||
AbstractSingletonExtensionPoint<PermissionServiceProvider, PermissionService<*>>(PermissionServiceProvider::class, BuiltInPermissionService)
|
||||
AbstractSingletonExtensionPoint<PermissionServiceProvider, PermissionService<*>>(PermissionServiceProvider::class, BuiltInPermissionService) {
|
||||
@ConsoleExperimentalApi
|
||||
public val providerPlugin: Plugin? by lazy {
|
||||
GlobalComponentStorage.run {
|
||||
val instance = PermissionService.INSTANCE
|
||||
if (instance is BuiltInPermissionService) return@lazy null
|
||||
PermissionServiceProvider.getExtensions().find { it.extension.instance === instance }?.plugin
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,8 +14,8 @@ import java.time.Instant
|
||||
|
||||
internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
|
||||
@JvmStatic
|
||||
val buildDate: Instant = Instant.ofEpochSecond(1605147625)
|
||||
const val versionConst: String = "1.0-RC2-dev-4"
|
||||
val buildDate: Instant = Instant.ofEpochSecond(1606185513)
|
||||
const val versionConst: String = "1.0.1-dev-1"
|
||||
|
||||
@JvmStatic
|
||||
val version: SemVersion = SemVersion(versionConst)
|
||||
|
@ -29,8 +29,9 @@ import net.mamoe.mirai.console.extensions.PermissionServiceProvider
|
||||
import net.mamoe.mirai.console.extensions.PostStartupExtension
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.internal.command.CommandConfig
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig
|
||||
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.PasswordKind.MD5
|
||||
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.PasswordKind.PLAIN
|
||||
import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope
|
||||
import net.mamoe.mirai.console.internal.data.builtins.LoggerConfig
|
||||
import net.mamoe.mirai.console.internal.data.castOrNull
|
||||
@ -41,6 +42,7 @@ import net.mamoe.mirai.console.internal.logging.MiraiConsoleLogger
|
||||
import net.mamoe.mirai.console.internal.permission.BuiltInPermissionService
|
||||
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.internal.util.autoHexToBytes
|
||||
import net.mamoe.mirai.console.internal.util.runIgnoreException
|
||||
import net.mamoe.mirai.console.logging.LoggerController
|
||||
import net.mamoe.mirai.console.permission.PermissionService
|
||||
import net.mamoe.mirai.console.permission.PermissionService.Companion.permit
|
||||
@ -49,6 +51,7 @@ import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
import net.mamoe.mirai.console.plugin.jvm.AbstractJvmPlugin
|
||||
import net.mamoe.mirai.console.plugin.loader.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.name
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.console.util.ConsoleInput
|
||||
import net.mamoe.mirai.console.util.SemVersion
|
||||
@ -184,24 +187,24 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
phase `load PermissionService`@{
|
||||
mainLogger.verbose { "Loading PermissionService..." }
|
||||
|
||||
PermissionServiceProvider.selectedInstance // init
|
||||
|
||||
PermissionService.INSTANCE.let { ps ->
|
||||
if (ps is BuiltInPermissionService) {
|
||||
ConsoleDataScope.addAndReloadConfig(ps.config)
|
||||
mainLogger.verbose { "Reloaded PermissionService settings." }
|
||||
} else {
|
||||
mainLogger.info { "Loaded PermissionService from plugin ${PermissionServiceProvider.providerPlugin?.name}" }
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleCommandSender.permit(RootPermission)
|
||||
runIgnoreException<UnsupportedOperationException> { ConsoleCommandSender.permit(RootPermission) }
|
||||
}
|
||||
|
||||
phase `prepare commands`@{
|
||||
mainLogger.verbose { "Loading built-in commands..." }
|
||||
BuiltInCommands.registerAll()
|
||||
mainLogger.verbose { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" }
|
||||
mainLogger.info { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" }
|
||||
CommandManager
|
||||
CommandManagerImpl.commandListener // start
|
||||
// CommandManagerImpl.commandListener // start
|
||||
}
|
||||
|
||||
phase `enable plugins`@{
|
||||
@ -218,20 +221,44 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
|
||||
phase `auto-login bots`@{
|
||||
runBlocking {
|
||||
for ((id, password) in AutoLoginConfig.plainPasswords.filterNot { it.key == 123456654321L }) {
|
||||
mainLogger.info { "Auto-login $id" }
|
||||
MiraiConsole.addBot(id, password).alsoLogin()
|
||||
val accounts = AutoLoginConfig.accounts.toList()
|
||||
for (account in accounts) {
|
||||
val id = kotlin.runCatching {
|
||||
account.account.toLong()
|
||||
}.getOrElse {
|
||||
error("Bad auto-login account: '${account.account}'")
|
||||
}
|
||||
if (id == 123456L) continue
|
||||
fun BotConfiguration.configBot() {
|
||||
mainLogger.info { "Auto-login ${account.account}" }
|
||||
|
||||
account.configuration[AutoLoginConfig.Account.ConfigurationKey.protocol]
|
||||
?.let { protocol ->
|
||||
this.protocol = runCatching {
|
||||
BotConfiguration.MiraiProtocol.valueOf(protocol.toString())
|
||||
}.getOrElse {
|
||||
throw IllegalArgumentException(
|
||||
"Bad auto-login config value for `protocol` for account $id",
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
when (account.password.kind) {
|
||||
PLAIN -> {
|
||||
MiraiConsole.addBot(id, account.password.value, BotConfiguration::configBot).alsoLogin()
|
||||
}
|
||||
MD5 -> {
|
||||
val md5 = kotlin.runCatching {
|
||||
account.password.value.autoHexToBytes()
|
||||
}.getOrElse {
|
||||
error("Bad auto-login md5: '${account.password.value}' for account $id")
|
||||
}
|
||||
MiraiConsole.addBot(id, md5, BotConfiguration::configBot).alsoLogin()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ((id, password) in AutoLoginConfig.md5Passwords.filterNot { it.key == 123456654321L }) {
|
||||
mainLogger.info { "Auto-login $id" }
|
||||
val x = runCatching {
|
||||
password.autoHexToBytes()
|
||||
}.getOrElse {
|
||||
error("Bad auto-login md5: '$password'")
|
||||
}
|
||||
MiraiConsole.addBot(id, x).alsoLogin()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,28 +10,23 @@
|
||||
package net.mamoe.mirai.console.internal.command
|
||||
|
||||
import kotlinx.atomicfu.locks.withLock
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.command.*
|
||||
import net.mamoe.mirai.console.command.Command.Companion.allNames
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.findDuplicate
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.toCommandSender
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandArgumentParserException
|
||||
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
|
||||
import net.mamoe.mirai.console.command.parse.CommandCallParser.Companion.parseCommandCall
|
||||
import net.mamoe.mirai.console.command.resolve.CommandCallInterceptor.Companion.intercepted
|
||||
import net.mamoe.mirai.console.command.resolve.CommandCallResolver.Companion.resolve
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.command.resolve.getOrElse
|
||||
import net.mamoe.mirai.console.internal.util.ifNull
|
||||
import net.mamoe.mirai.console.permission.PermissionService.Companion.testPermission
|
||||
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
|
||||
import net.mamoe.mirai.event.Listener
|
||||
import net.mamoe.mirai.event.subscribeAlways
|
||||
import net.mamoe.mirai.message.MessageEvent
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.asMessageChain
|
||||
import net.mamoe.mirai.message.data.content
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.SimpleLogger
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
@OptIn(ExperimentalCommandDescriptors::class)
|
||||
@ -63,47 +58,6 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiCons
|
||||
}
|
||||
return optionalPrefixCommandMap[commandName.toLowerCase()]
|
||||
}
|
||||
|
||||
internal val commandListener: Listener<MessageEvent> by lazy {
|
||||
subscribeAlways(
|
||||
coroutineContext = CoroutineExceptionHandler { _, throwable ->
|
||||
logger.error(throwable)
|
||||
},
|
||||
concurrency = Listener.ConcurrencyKind.CONCURRENT,
|
||||
priority = Listener.EventPriority.HIGH
|
||||
) {
|
||||
val sender = this.toCommandSender()
|
||||
|
||||
when (val result = executeCommand(sender, message)) {
|
||||
is CommandExecuteResult.PermissionDenied -> {
|
||||
if (!result.command.prefixOptional || message.content.startsWith(CommandManager.commandPrefix)) {
|
||||
if (MiraiConsoleImplementationBridge.loggerController.shouldLog("console.debug", SimpleLogger.LogPriority.DEBUG)) {
|
||||
sender.sendMessage("权限不足")
|
||||
}
|
||||
intercept()
|
||||
}
|
||||
}
|
||||
is CommandExecuteResult.IllegalArgument -> {
|
||||
result.exception.message?.let { sender.sendMessage(it) }
|
||||
intercept()
|
||||
}
|
||||
is CommandExecuteResult.Success -> {
|
||||
intercept()
|
||||
}
|
||||
is CommandExecuteResult.ExecutionFailed -> {
|
||||
sender.catchExecutionException(result.exception)
|
||||
intercept()
|
||||
}
|
||||
is CommandExecuteResult.UnmatchedSignature,
|
||||
is CommandExecuteResult.UnresolvedCommand,
|
||||
-> {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///// IMPL
|
||||
|
||||
|
||||
@ -173,15 +127,29 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiCons
|
||||
// Don't move into CommandManager, compilation error / VerifyError
|
||||
@OptIn(ExperimentalCommandDescriptors::class)
|
||||
internal suspend fun executeCommandImpl(
|
||||
message: Message,
|
||||
message0: Message,
|
||||
caller: CommandSender,
|
||||
checkPermission: Boolean,
|
||||
): CommandExecuteResult {
|
||||
val call = message.asMessageChain().parseCommandCall(caller) ?: return CommandExecuteResult.UnresolvedCommand(null)
|
||||
val resolved = call.resolve().fold(
|
||||
onSuccess = { it },
|
||||
onFailure = { return it }
|
||||
) ?: return CommandExecuteResult.UnresolvedCommand(call)
|
||||
val message = message0
|
||||
.intercepted(caller)
|
||||
.getOrElse { return CommandExecuteResult.Intercepted(null, null, null, it) }
|
||||
|
||||
val call = message.asMessageChain()
|
||||
.parseCommandCall(caller)
|
||||
.ifNull { return CommandExecuteResult.UnresolvedCommand(null) }
|
||||
.let { raw ->
|
||||
raw.intercepted()
|
||||
.getOrElse { return CommandExecuteResult.Intercepted(raw, null, null, it) }
|
||||
}
|
||||
|
||||
val resolved = call
|
||||
.resolve()
|
||||
.getOrElse { return it }
|
||||
.let { raw ->
|
||||
raw.intercepted()
|
||||
.getOrElse { return CommandExecuteResult.Intercepted(call, raw, null, it) }
|
||||
}
|
||||
|
||||
val command = resolved.callee
|
||||
|
||||
|
@ -7,31 +7,76 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("EXPOSED_SUPER_CLASS")
|
||||
|
||||
package net.mamoe.mirai.console.internal.data.builtins
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser
|
||||
import net.mamoe.mirai.console.command.descriptor.InternalCommandValueArgumentParserExtensions
|
||||
import net.mamoe.mirai.console.data.AutoSavePluginConfig
|
||||
import net.mamoe.mirai.console.data.ValueDescription
|
||||
import net.mamoe.mirai.console.data.value
|
||||
import net.mamoe.mirai.console.internal.util.md5
|
||||
import net.mamoe.mirai.console.internal.util.toUHexString
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.yamlkt.Comment
|
||||
import net.mamoe.yamlkt.YamlDynamicSerializer
|
||||
|
||||
internal object AutoLoginConfig : AutoSavePluginConfig("AutoLogin") {
|
||||
@ValueDescription(
|
||||
"""
|
||||
账号和明文密码列表
|
||||
"""
|
||||
)
|
||||
val plainPasswords: MutableMap<Long, String> by value(mutableMapOf(123456654321L to "example"))
|
||||
@ConsoleExperimentalApi
|
||||
@ValueDescription("自动登录配置")
|
||||
public object AutoLoginConfig : AutoSavePluginConfig("AutoLogin") {
|
||||
|
||||
|
||||
@ValueDescription(
|
||||
"""
|
||||
账号和 MD5 密码列表
|
||||
"""
|
||||
)
|
||||
val md5Passwords: MutableMap<Long, String> by value(
|
||||
mutableMapOf(
|
||||
123456654321L to "example".toByteArray().md5().toUHexString()
|
||||
@Serializable
|
||||
public data class Account(
|
||||
@Comment("账号, 现只支持 QQ 数字账号")
|
||||
val account: String,
|
||||
val password: Password,
|
||||
@Comment("""
|
||||
账号配置. 可用配置列表 (注意大小写):
|
||||
"protocol": "ANDROID_PHONE" / "ANDROID_PAD" / "ANDROID_WATCH"
|
||||
""")
|
||||
val configuration: Map<ConfigurationKey, @Serializable(with = YamlDynamicSerializer::class) Any> = mapOf(),
|
||||
) {
|
||||
@Serializable
|
||||
public data class Password(
|
||||
@Comment("密码种类, 可选 PLAIN 或 MD5")
|
||||
val kind: PasswordKind,
|
||||
@Comment("密码内容, PLAIN 时为密码文本, MD5 时为 16 进制")
|
||||
val value: String,
|
||||
)
|
||||
)
|
||||
|
||||
@Suppress("EnumEntryName")
|
||||
@Serializable
|
||||
public enum class ConfigurationKey {
|
||||
protocol,
|
||||
|
||||
;
|
||||
|
||||
public object Parser : CommandValueArgumentParser<ConfigurationKey>, InternalCommandValueArgumentParserExtensions<ConfigurationKey>() {
|
||||
override fun parse(raw: String, sender: CommandSender): ConfigurationKey {
|
||||
val key = values().find { it.name.equals(raw, ignoreCase = true) }
|
||||
if (key != null) return key
|
||||
illegalArgument("未知配置项, 可选值: ${values().joinToString()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
public enum class PasswordKind {
|
||||
PLAIN,
|
||||
MD5;
|
||||
|
||||
public object Parser : CommandValueArgumentParser<ConfigurationKey>, InternalCommandValueArgumentParserExtensions<ConfigurationKey>() {
|
||||
override fun parse(raw: String, sender: CommandSender): ConfigurationKey {
|
||||
val key = ConfigurationKey.values().find { it.name.equals(raw, ignoreCase = true) }
|
||||
if (key != null) return key
|
||||
illegalArgument("未知配置项, 可选值: ${ConfigurationKey.values().joinToString()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public val accounts: MutableList<Account> by value(mutableListOf(
|
||||
Account("123456", Account.Password(Account.PasswordKind.PLAIN, "pwd"), mapOf(Account.ConfigurationKey.protocol to "ANDROID_PHONE"))
|
||||
))
|
||||
}
|
@ -154,7 +154,7 @@ internal abstract class AbstractConcurrentComponentStorage : ComponentStorage {
|
||||
internal inline fun <T : Extension> ExtensionPoint<T>.useExtensions(block: (extension: T, plugin: Plugin?) -> Unit): Unit =
|
||||
withExtensions(block)
|
||||
|
||||
val instances: MutableMap<ExtensionPoint<*>, MutableSet<ExtensionRegistry<*>>> = ConcurrentHashMap()
|
||||
private val instances: MutableMap<ExtensionPoint<*>, MutableSet<ExtensionRegistry<*>>> = ConcurrentHashMap()
|
||||
override fun <T : Extension> contribute(
|
||||
extensionPoint: ExtensionPoint<T>,
|
||||
plugin: Plugin,
|
||||
|
@ -7,12 +7,14 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmName("CommonUtils")
|
||||
@file:JvmName("CommonUtils") // maintain binary compatibility
|
||||
|
||||
package net.mamoe.mirai.console.internal.util
|
||||
|
||||
import io.github.karlatemp.caller.StackFrame
|
||||
import net.mamoe.mirai.console.internal.plugin.BuiltInJvmPluginLoaderImpl
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
internal inline fun <reified E : Throwable, R> runIgnoreException(block: () -> R): R? {
|
||||
try {
|
||||
@ -38,3 +40,35 @@ internal fun StackFrame.findLoader(): ClassLoader? {
|
||||
BuiltInJvmPluginLoaderImpl.classLoaders.firstOrNull { it.findClass(className, true) != null }
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
@kotlin.internal.LowPriorityInOverloadResolution
|
||||
internal inline fun <T : Any> T?.ifNull(block: () -> T): T {
|
||||
contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) }
|
||||
return this ?: block()
|
||||
}
|
||||
|
||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated("Useless ifNull on not null value.")
|
||||
@JvmName("ifNull1")
|
||||
internal inline fun <T : Any> T.ifNull(block: () -> T): T = this
|
||||
|
||||
@PublishedApi
|
||||
internal inline fun assertionError(message: () -> String = { "Reached an unexpected branch." }): Nothing {
|
||||
contract { callsInPlace(message, InvocationKind.EXACTLY_ONCE) }
|
||||
throw AssertionError(message())
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal inline fun assertUnreachable(message: () -> String = { "Reached an unexpected branch." }): Nothing {
|
||||
contract { callsInPlace(message, InvocationKind.EXACTLY_ONCE) }
|
||||
throw AssertionError(message())
|
||||
}
|
||||
|
||||
@MarkerUnreachableClause
|
||||
@PublishedApi
|
||||
internal inline val UNREACHABLE_CLAUSE: Nothing
|
||||
get() = assertUnreachable()
|
||||
|
||||
@DslMarker
|
||||
private annotation class MarkerUnreachableClause
|
@ -18,7 +18,7 @@ public abstract class AbstractLoggerController : LoggerController {
|
||||
|
||||
protected open fun shouldLog(
|
||||
priority: LogPriority,
|
||||
settings: LogPriority
|
||||
settings: LogPriority,
|
||||
): Boolean = settings <= priority
|
||||
|
||||
protected abstract fun getPriority(identity: String?): LogPriority
|
||||
|
@ -17,4 +17,4 @@ package net.mamoe.mirai.console.permission
|
||||
public class PermissionRegistryConflictException(
|
||||
public val newInstance: Permission,
|
||||
public val existingInstance: Permission,
|
||||
) : Exception("Conflicted Permission registry. new: $newInstance, existing: $existingInstance")
|
||||
) : Exception("Conflicting Permission registry. new: $newInstance, existing: $existingInstance")
|
@ -16,6 +16,7 @@ import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import net.mamoe.mirai.console.compiler.common.ResolveContext
|
||||
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.PLUGIN_ID
|
||||
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.VERSION_REQUIREMENT
|
||||
import net.mamoe.mirai.console.internal.data.map
|
||||
import net.mamoe.mirai.console.util.SemVersion
|
||||
|
||||
@ -37,7 +38,7 @@ public data class PluginDependency @JvmOverloads constructor(
|
||||
*
|
||||
* @see SemVersion.Requirement
|
||||
*/
|
||||
public val versionRequirement: String? = null,
|
||||
@ResolveContext(VERSION_REQUIREMENT) public val versionRequirement: String? = null,
|
||||
/**
|
||||
* 若为 `false`, 插件在找不到此依赖时也能正常加载.
|
||||
*/
|
||||
@ -46,6 +47,7 @@ public data class PluginDependency @JvmOverloads constructor(
|
||||
init {
|
||||
kotlin.runCatching {
|
||||
PluginDescription.checkPluginId(id)
|
||||
if (versionRequirement != null) SemVersion.parseRangeRequirement(versionRequirement)
|
||||
}.getOrElse {
|
||||
throw IllegalArgumentException(it)
|
||||
}
|
||||
@ -63,7 +65,10 @@ public data class PluginDependency @JvmOverloads constructor(
|
||||
|
||||
public override fun toString(): String = buildString {
|
||||
append(id)
|
||||
versionRequirement?.let(::append)
|
||||
versionRequirement?.let {
|
||||
append(':')
|
||||
append(it)
|
||||
}
|
||||
if (isOptional) {
|
||||
append('?')
|
||||
}
|
||||
@ -78,8 +83,12 @@ public data class PluginDependency @JvmOverloads constructor(
|
||||
public fun parseFromString(string: String): PluginDependency {
|
||||
require(string.isNotEmpty()) { "string is empty." }
|
||||
val optional = string.endsWith('?')
|
||||
val (id, version) = string.removeSuffix("?").let {
|
||||
it.substringBeforeLast(':') to it.substringAfterLast(':', "")
|
||||
val (id, version) = string.removeSuffix("?").let { rule ->
|
||||
if (rule.contains(':')) {
|
||||
rule.substringBeforeLast(':') to rule.substringAfterLast(':')
|
||||
} else {
|
||||
rule to null
|
||||
}
|
||||
}
|
||||
return PluginDependency(id, version, optional)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.*
|
||||
import net.mamoe.mirai.console.internal.util.findLoader
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDependency
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.console.util.SemVersion
|
||||
import net.mamoe.yamlkt.Yaml
|
||||
|
||||
@ -34,35 +35,6 @@ import net.mamoe.yamlkt.Yaml
|
||||
*/
|
||||
public interface JvmPluginDescription : PluginDescription {
|
||||
public companion object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Deprecated(
|
||||
"Use top-level function instead",
|
||||
ReplaceWith("JvmPluginDescription(id, version, block)", "net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription"),
|
||||
DeprecationLevel.ERROR
|
||||
)
|
||||
@JvmName("create")
|
||||
public inline fun invoke(
|
||||
@ResolveContext(PLUGIN_ID) id: String,
|
||||
@ResolveContext(SEMANTIC_VERSION) version: String,
|
||||
@ResolveContext(PLUGIN_NAME) name: String = id,
|
||||
block: JvmPluginDescriptionBuilder.() -> Unit = {},
|
||||
): JvmPluginDescription = error("Shouldn't be called")
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Deprecated(
|
||||
"Use top-level function instead",
|
||||
ReplaceWith("JvmPluginDescription(id, version, block)", "net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription"),
|
||||
DeprecationLevel.ERROR
|
||||
)
|
||||
@JvmName("create")
|
||||
@JvmSynthetic
|
||||
public inline fun invoke(
|
||||
@ResolveContext(PLUGIN_ID) id: String,
|
||||
version: SemVersion,
|
||||
@ResolveContext(PLUGIN_NAME) name: String = id,
|
||||
block: JvmPluginDescriptionBuilder.() -> Unit = {},
|
||||
): JvmPluginDescription = error("Shouldn't be called")
|
||||
|
||||
/**
|
||||
* 从 [pluginClassloader] 读取资源文件 [filename] 并以 YAML 格式解析为 [SimpleJvmPluginDescription]
|
||||
*
|
||||
@ -71,6 +43,7 @@ public interface JvmPluginDescription : PluginDescription {
|
||||
*/
|
||||
// @JvmOverloads // compiler error
|
||||
@JvmStatic
|
||||
@ConsoleExperimentalApi
|
||||
public fun loadFromResource(
|
||||
filename: String = "plugin.yml",
|
||||
pluginClassloader: ClassLoader = CallerFinder.getCaller()?.findLoader() ?: error("Cannot find caller classloader, please specify manually."),
|
||||
@ -245,7 +218,7 @@ public class JvmPluginDescriptionBuilder(
|
||||
*
|
||||
* @see JvmPluginDescription
|
||||
*/
|
||||
@Serializable
|
||||
@Serializable // Keep this file in public API files. Might turn to `public` in the future.
|
||||
internal data class SimpleJvmPluginDescription
|
||||
@JvmOverloads constructor(
|
||||
override val id: String,
|
||||
|
@ -181,8 +181,8 @@ internal class TestCommand {
|
||||
@Test
|
||||
fun `composite command descriptors`() {
|
||||
val overloads = TestCompositeCommand.overloads
|
||||
assertEquals("CommandSignatureVariant(<mute>, seconds: Int = ...)", overloads[0].toString())
|
||||
assertEquals("CommandSignatureVariant(<mute>, target: Long, seconds: Int)", overloads[1].toString())
|
||||
assertEquals("CommandSignature(<mute>, seconds: Int = ...)", overloads[0].toString())
|
||||
assertEquals("CommandSignature(<mute>, target: Long, seconds: Int)", overloads[1].toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -11,12 +11,12 @@
|
||||
|
||||
object Versions {
|
||||
const val core = "1.3.3"
|
||||
const val console = "1.0-RC2-dev-6"
|
||||
const val console = "1.0.1-dev-1"
|
||||
const val consoleGraphical = "0.0.7"
|
||||
const val consoleTerminal = console
|
||||
|
||||
const val kotlinCompiler = "1.4.20-RC"
|
||||
const val kotlinStdlib = "1.4.10"
|
||||
const val kotlinCompiler = "1.4.20"
|
||||
const val kotlinStdlib = "1.4.20"
|
||||
|
||||
const val kotlinIntellijPlugin = "1.4.20-RC-IJ2020.2-1" // -release
|
||||
const val intellij = "2020.2.1"
|
||||
@ -34,7 +34,7 @@ object Versions {
|
||||
const val blockingBridge = "1.4.1"
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
const val yamlkt = "0.7.1"
|
||||
const val yamlkt = "0.7.3"
|
||||
|
||||
const val intellijGradlePlugin = "0.4.16"
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ console 由后端和前端一起工作. 使用时必须选择一个前端.
|
||||
|
||||
| 版本类型 | 版本号 |
|
||||
|:------:|:------------------------------:|
|
||||
| 稳定 | - |
|
||||
| 预览 | 1.0-RC-1 |
|
||||
| 稳定 | 1.0.0 |
|
||||
| 预览 | - |
|
||||
| 开发 | [![Version]][Bintray Download] |
|
||||
|
||||
## 配置项目
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Mirai Console 前端开发文档。
|
||||
|
||||
[`MiraiConsole`]: ../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
|
||||
[`MiraiConsole`]: ../backend/mirai-console/src/MiraiConsole.kt
|
||||
|
||||
## 实现前端
|
||||
|
||||
@ -10,10 +10,8 @@ Mirai Console 前端开发文档。
|
||||
|
||||
在 `build.gradle` 或 `build.gradle.kts` 添加:
|
||||
```kotlin
|
||||
kotlin {
|
||||
sourceSets.all {
|
||||
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.console.ConsoleFrontEndImplementation")
|
||||
}
|
||||
kotlin.sourceSets.all {
|
||||
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.console.ConsoleFrontEndImplementation")
|
||||
}
|
||||
```
|
||||
|
||||
@ -22,9 +20,9 @@ kotlin {
|
||||
|
||||
### 实现 Mirai Console
|
||||
|
||||
[`MiraiConsole`] 是后端的公开对象,由 [MiraiConsoleImplementationBridge](../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/MiraiConsoleImplementationBridge.kt) 代理,与前端链接。
|
||||
[`MiraiConsole`] 是后端的公开对象,由 [MiraiConsoleImplementationBridge](../backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt) 代理,与前端链接。
|
||||
|
||||
前端需要实现 [MiraiConsoleImplementation.kt](../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt)。
|
||||
前端需要实现 [MiraiConsoleImplementation.kt](../backend/mirai-console/src/MiraiConsoleImplementation)。
|
||||
|
||||
由于实现前端需要一定的技术能力,相信实现者都能理解源码内注释。
|
||||
|
||||
@ -32,4 +30,4 @@ kotlin {
|
||||
|
||||
通过 `public fun MiraiConsoleImplementation.start()`。
|
||||
|
||||
[MiraiConsoleImplementation.kt: Line 161](../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt#L161)
|
||||
[MiraiConsoleImplementation.kt: Line 161](../backend/mirai-console/src/MiraiConsoleImplementation.kt#L161)
|
||||
|
@ -194,7 +194,7 @@ public final class JExample extends JavaPlugin {
|
||||
由 Console 初始化(仅在某些静态初始化不可用的情况下使用):
|
||||
```java
|
||||
public final class JExample extends JavaPlugin {
|
||||
private static final JExample instance;
|
||||
private static JExample instance;
|
||||
public static JExample getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
38
docs/Run.md
38
docs/Run.md
@ -22,12 +22,34 @@ Mirai Console 可以独立启动,也可以被嵌入到某个应用中。
|
||||
|
||||
只有 mirai-console 前端才有入口点 `main` 方法。目前只有一个 terminal 前端可用。
|
||||
|
||||
#### 从JCenter下载模块
|
||||
|
||||
mirai 在版本发布时会将发布的构建存放与 [mirai-bintray-repo]。
|
||||
|
||||
- mirai-core 会提供 [mirai-core-all]
|
||||
- mirai-console 与其各个模块都会提供 `-all` 的 Shadowed 构建
|
||||
|
||||
```shell script
|
||||
# 注: 自行更换对应版本号
|
||||
|
||||
# Download Mirai Core All
|
||||
|
||||
curl -L https://maven.aliyun.com/repository/public/net/mamoe/mirai-core-all/1.3.3/mirai-core-all-1.3.3-all.jar -o mirai-core-all-1.3.3.jar
|
||||
|
||||
# Download Mirai Console All
|
||||
|
||||
curl -L https://maven.aliyun.com/repository/public/net/mamoe/mirai-console/1.0.0/mirai-console-1.0.0-all.jar -o mirai-console-1.0.0.jar
|
||||
|
||||
# Download Mirai Console Terminal
|
||||
|
||||
curl -L https://maven.aliyun.com/repository/public/net/mamoe/mirai-console-terminal/1.0.0/mirai-console-terminal-1.0.0-all.jar -o mirai-console-terminal-1.0.0.jar
|
||||
|
||||
```
|
||||
|
||||
### 启动 mirai-console-terminal 前端
|
||||
|
||||
mirai 在版本发布时会同时发布打包依赖的 Shadow JAR,存放在 [mirai-repo]。
|
||||
|
||||
1. 在 [mirai-repo] 下载如下三个模块的最新版本文件并放到一个文件夹内 (如 `libs`):
|
||||
- mirai-core-qqandroid
|
||||
1. 下载如下三个模块的最新版本文件并放到一个文件夹内 (如 `libs`)(详见 [下载模块](#从JCenter下载模块)):
|
||||
- mirai-core-all
|
||||
- mirai-console
|
||||
- mirai-console-terminal
|
||||
|
||||
@ -51,6 +73,7 @@ pause
|
||||
Linux:
|
||||
```shell script
|
||||
#!/usr/bin/env bash
|
||||
echo -e '\033]2;Mirai Console\007'
|
||||
java -cp "./libs/*" net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader $*
|
||||
```
|
||||
|
||||
@ -59,11 +82,14 @@ java -cp "./libs/*" net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader
|
||||
#### mirai-console-terminal 前端参数
|
||||
使用 `./start-mirai-console --help` 查看 mirai-console-terminal 支持的启动参数
|
||||
|
||||
[mirai-repo]: https://github.com/project-mirai/mirai-repo/tree/master/shadow
|
||||
|
||||
|
||||
### 启动 mirai-console-pure 前端
|
||||
|
||||
与启动 `mirai-console-terminal` 前端大体相同
|
||||
- 下载 `mirai-console-terminal` 改成下载 `mirai-console-pure`
|
||||
- 启动入口从 `net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader` 改成 `net.mamoe.mirai.console.pure.MiraiConsolePureLoader`
|
||||
|
||||
|
||||
[mirai-repo]: https://github.com/project-mirai/mirai-repo/tree/master/shadow
|
||||
[mirai-bintray-repo]: https://bintray.com/him188moe/mirai
|
||||
[mirai-core-all]: https://bintray.com/him188moe/mirai/mirai-core-all
|
||||
|
@ -103,48 +103,38 @@ val lineReader: LineReader by lazy {
|
||||
val terminal: Terminal = run {
|
||||
if (ConsoleTerminalSettings.noConsole) return@run NoConsole
|
||||
|
||||
val dumb = System.getProperty("java.class.path")
|
||||
.contains("idea_rt.jar") || System.getProperty("mirai.idea") !== null || System.getenv("mirai.idea") !== null
|
||||
|
||||
runCatching {
|
||||
TerminalBuilder.builder()
|
||||
.dumb(dumb)
|
||||
.paused(true)
|
||||
.build()
|
||||
.let { terminal ->
|
||||
if (terminal is AbstractWindowsTerminal) {
|
||||
val pumpField = runCatching {
|
||||
AbstractWindowsTerminal::class.java.getDeclaredField("pump").also {
|
||||
it.isAccessible = true
|
||||
}
|
||||
}.onFailure { err ->
|
||||
err.printStackTrace()
|
||||
return@let terminal.also { it.resume() }
|
||||
}.getOrThrow()
|
||||
var response = terminal
|
||||
terminal.setOnClose {
|
||||
response = NoConsole
|
||||
TerminalBuilder.builder()
|
||||
.name("Mirai Console")
|
||||
.system(true)
|
||||
.jansi(true)
|
||||
.dumb(true)
|
||||
.paused(true)
|
||||
.build()
|
||||
.let { terminal ->
|
||||
if (terminal is AbstractWindowsTerminal) {
|
||||
val pumpField = runCatching {
|
||||
AbstractWindowsTerminal::class.java.getDeclaredField("pump").also {
|
||||
it.isAccessible = true
|
||||
}
|
||||
terminal.resume()
|
||||
val pumpThread = pumpField[terminal] as? Thread ?: return@let NoConsole
|
||||
@Suppress("ControlFlowWithEmptyBody")
|
||||
while (pumpThread.state == Thread.State.NEW);
|
||||
Thread.sleep(1000)
|
||||
terminal.setOnClose(null)
|
||||
return@let response
|
||||
}.onFailure { err ->
|
||||
err.printStackTrace()
|
||||
return@let terminal.also { it.resume() }
|
||||
}.getOrThrow()
|
||||
var response = terminal
|
||||
terminal.setOnClose {
|
||||
response = NoConsole
|
||||
}
|
||||
terminal.resume()
|
||||
terminal
|
||||
val pumpThread = pumpField[terminal] as? Thread ?: return@let NoConsole
|
||||
@Suppress("ControlFlowWithEmptyBody")
|
||||
while (pumpThread.state == Thread.State.NEW);
|
||||
Thread.sleep(1000)
|
||||
terminal.setOnClose(null)
|
||||
return@let response
|
||||
}
|
||||
}.recoverCatching {
|
||||
TerminalBuilder.builder()
|
||||
.jansi(true)
|
||||
.build()
|
||||
}.recoverCatching {
|
||||
TerminalBuilder.builder()
|
||||
.system(true)
|
||||
.build()
|
||||
}.getOrThrow()
|
||||
terminal.resume()
|
||||
terminal
|
||||
}
|
||||
}
|
||||
|
||||
private object ConsoleFrontEndDescImpl : MiraiConsoleFrontEndDescription {
|
||||
|
@ -10,6 +10,6 @@
|
||||
package net.mamoe.mirai.console.gradle
|
||||
|
||||
internal object VersionConstants {
|
||||
const val CONSOLE_VERSION = "1.0-RC2-dev-6" // value is written here automatically during build
|
||||
const val CONSOLE_VERSION = "1.0.1-dev-1" // value is written here automatically during build
|
||||
const val CORE_VERSION = "1.3.3" // value is written here automatically during build
|
||||
}
|
@ -14,14 +14,17 @@ import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.progress.impl.CancellationCheck.Companion.runWithCancellationCheck
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import net.mamoe.mirai.console.compiler.common.resolve.AUTO_SERVICE
|
||||
import net.mamoe.mirai.console.compiler.common.resolve.PLUGIN_FQ_NAME
|
||||
import net.mamoe.mirai.console.intellij.diagnostics.fix.ConfigurePluginMainServiceFix
|
||||
import net.mamoe.mirai.console.intellij.resolve.allSuperNames
|
||||
import net.mamoe.mirai.console.intellij.resolve.hasAnnotation
|
||||
import org.jetbrains.kotlin.idea.debugger.readAction
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.idea.util.module
|
||||
import org.jetbrains.kotlin.idea.util.rootManager
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.referenceExpressionVisitor
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
import org.jetbrains.kotlin.psi.classOrObjectVisitor
|
||||
import java.util.*
|
||||
|
||||
/*
|
||||
@ -39,19 +42,20 @@ class PluginMainServiceNotConfiguredInspection : AbstractKotlinInspection() {
|
||||
}
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||
return referenceExpressionVisitor visitor@{ referenceExpr ->
|
||||
val ktClass = referenceExpr.resolveMiraiPluginDeclaration() ?: return@visitor
|
||||
val fqName = ktClass.fqName?.asString() ?: return@visitor
|
||||
return classOrObjectVisitor visitor@{ element ->
|
||||
if (element !is KtObjectDeclaration) return@visitor
|
||||
if (element.allSuperNames.none { it == PLUGIN_FQ_NAME }) return@visitor
|
||||
val fqName = element.fqName?.asString() ?: return@visitor
|
||||
|
||||
val found = isServiceConfiguredWithAutoService(ktClass)
|
||||
|| isServiceConfiguredWithResource(ktClass, fqName)
|
||||
val found = isServiceConfiguredWithAutoService(element)
|
||||
|| isServiceConfiguredWithResource(element, fqName)
|
||||
|
||||
if (!found) {
|
||||
holder.registerProblem(
|
||||
ktClass.nameIdentifier ?: ktClass.identifyingElement ?: ktClass,
|
||||
element.nameIdentifier ?: element.identifyingElement ?: element,
|
||||
"插件主类服务未配置",
|
||||
ProblemHighlightType.WARNING,
|
||||
ConfigurePluginMainServiceFix(ktClass)
|
||||
ConfigurePluginMainServiceFix(element)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -9,19 +9,16 @@
|
||||
|
||||
package net.mamoe.mirai.console.intellij.diagnostics
|
||||
|
||||
import com.intellij.util.castSafelyTo
|
||||
import net.mamoe.mirai.console.compiler.common.castOrNull
|
||||
import net.mamoe.mirai.console.compiler.common.resolve.PLUGIN_FQ_NAME
|
||||
import net.mamoe.mirai.console.compiler.common.resolve.parents
|
||||
import net.mamoe.mirai.console.intellij.resolve.allSuperNames
|
||||
import net.mamoe.mirai.console.intellij.resolve.getResolvedCall
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||
import org.jetbrains.kotlin.psi.KtUserType
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
|
||||
@ -44,12 +41,3 @@ fun KtTypeReference.isReferencing(fqName: FqName): Boolean {
|
||||
val KtTypeReference.referencedUserType: KtUserType? get() = this.typeElement.castOrNull()
|
||||
|
||||
fun KtTypeReference.resolveReferencedType() = referencedUserType?.referenceExpression?.mainReference?.resolve()
|
||||
|
||||
fun KtReferenceExpression.resolveMiraiPluginDeclaration(): KtClassOrObject? {
|
||||
val main =
|
||||
parents.filterIsInstance<KtClassOrObject>().firstOrNull() ?: return null
|
||||
val kotlinPluginClass =
|
||||
resolve().castSafelyTo<KtConstructor<*>>()?.parent?.castSafelyTo<KtClass>() ?: return null
|
||||
if (kotlinPluginClass.allSuperNames.none { it == PLUGIN_FQ_NAME }) return null
|
||||
return main
|
||||
}
|
||||
|
@ -15,16 +15,17 @@ import com.intellij.codeInsight.daemon.LineMarkerProvider
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||
import com.intellij.psi.PsiElement
|
||||
import net.mamoe.mirai.console.compiler.common.resolve.PLUGIN_FQ_NAME
|
||||
import net.mamoe.mirai.console.intellij.Icons
|
||||
import net.mamoe.mirai.console.intellij.diagnostics.resolveMiraiPluginDeclaration
|
||||
import net.mamoe.mirai.console.intellij.resolve.allSuperNames
|
||||
import net.mamoe.mirai.console.intellij.resolve.getElementForLineMark
|
||||
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
|
||||
class PluginMainLineMarkerProvider : LineMarkerProvider {
|
||||
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {
|
||||
if (element !is KtReferenceExpression) return null
|
||||
val main = element.resolveMiraiPluginDeclaration() ?: return null
|
||||
return Info(getElementForLineMark(main))
|
||||
if (element !is KtObjectDeclaration) return null
|
||||
if (element.allSuperNames.any { it == PLUGIN_FQ_NAME }) return Info(getElementForLineMark(element))
|
||||
return null
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
|
Loading…
Reference in New Issue
Block a user