CompositeCommand.kt 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /*
  2. * Copyright 2019-2022 Mamoe Technologies and contributors.
  3. *
  4. * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
  5. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
  6. *
  7. * https://github.com/mamoe/mirai/blob/dev/LICENSE
  8. */
  9. package net.mamoe.mirai.console.command
  10. import net.mamoe.mirai.console.command.descriptor.*
  11. import net.mamoe.mirai.console.command.java.JCompositeCommand
  12. import net.mamoe.mirai.console.compiler.common.ResolveContext
  13. import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
  14. import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER
  15. import net.mamoe.mirai.console.internal.command.CommandReflector
  16. import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver
  17. import net.mamoe.mirai.console.permission.Permission
  18. import net.mamoe.mirai.console.util.ConsoleExperimentalApi
  19. import kotlin.annotation.AnnotationRetention.RUNTIME
  20. import kotlin.annotation.AnnotationTarget.FUNCTION
  21. /**
  22. * 复合指令. 指令注册时候会通过反射构造指令解析器.
  23. *
  24. * Java 示例查看 [JCompositeCommand].
  25. *
  26. * Kotlin 示例:
  27. * ```
  28. * @OptIn(ConsoleExperimentalAPI::class)
  29. * object MyCompositeCommand : CompositeCommand(
  30. * MyPluginMain, "manage", // "manage" 是主指令名
  31. * description = "示例指令", permission = MyCustomPermission,
  32. * // prefixOptional = true // 还有更多参数可填, 此处忽略
  33. * ) {
  34. *
  35. * // [参数智能解析]
  36. * //
  37. * // 在控制台执行 "/manage <群号>.<群员> <持续时间>",
  38. * // 或在聊天群内发送 "/manage <@一个群员> <持续时间>",
  39. * // 或在聊天群内发送 "/manage <目标群员的群名> <持续时间>",
  40. * // 或在聊天群内发送 "/manage <目标群员的账号> <持续时间>"
  41. * // 时调用这个函数
  42. * @SubCommand // 表示这是一个子指令,使用函数名作为子指令名称
  43. * suspend fun CommandSender.mute(target: Member, duration: Int) { // 通过 /manage mute <target> <duration> 调用
  44. * sendMessage("/manage mute 被调用了, 参数为: $target, $duration")
  45. *
  46. * val result = kotlin.runCatching {
  47. * target.mute(duration).toString()
  48. * }.getOrElse {
  49. * it.stackTraceToString()
  50. * } // 失败时返回堆栈信息
  51. *
  52. * sendMessage("结果: $result")
  53. * }
  54. *
  55. * @SubCommand
  56. * suspend fun SystemCommandSender.foo() {
  57. * // 使用 SystemCommandSender 作为接收者,表示指令只能由系统(控制台或其他插件)执行。
  58. * // 当用户尝试在聊天环境执行时将会收到错误提示。
  59. * }
  60. *
  61. * @SubCommand("list", "查看列表") // 可以设置多个子指令名。此时函数名会被忽略。
  62. * suspend fun CommandSender.list() { // 执行 "/manage list" 时调用这个函数
  63. * sendMessage("/manage list 被调用了")
  64. * }
  65. *
  66. * @SubCommand
  67. * suspend fun CommandContext.repeat() {
  68. * // 使用 CommandContext 作为参数,可以获得触发指令的原消息链 originalMessage,其中包含 MessageMetadata。
  69. * sender.sendMessage(originalMessage)
  70. * }
  71. *
  72. * // 支持 Image 类型, 需在聊天中执行此指令.
  73. * @SubCommand
  74. * suspend fun UserCommandSender.test(image: Image) { // 执行 "/manage test <一张图片>" 时调用这个函数
  75. * // 由于 Image 类型消息只可能在聊天环境,可以直接使用 UserCommandSender。
  76. * sendMessage("/manage image 被调用了, 图片是 ${image.imageId}")
  77. * }
  78. * }
  79. * ```
  80. *
  81. * @see buildCommandArgumentContext
  82. */
  83. public abstract class CompositeCommand(
  84. @ResolveContext(RESTRICTED_CONSOLE_COMMAND_OWNER) owner: CommandOwner,
  85. @ResolveContext(COMMAND_NAME) primaryName: String,
  86. @ResolveContext(COMMAND_NAME) vararg secondaryNames: String,
  87. description: String = "no description available",
  88. parentPermission: Permission = owner.parentPermission,
  89. overrideContext: CommandArgumentContext = EmptyCommandArgumentContext,
  90. ) : Command, AbstractCommand(owner, primaryName, secondaryNames = secondaryNames, description, parentPermission),
  91. CommandArgumentContextAware {
  92. private val reflector by lazy { CommandReflector(this, CompositeCommandSubCommandAnnotationResolver) }
  93. @ExperimentalCommandDescriptors
  94. public final override val overloads: List<@JvmWildcard CommandSignatureFromKFunction> by lazy {
  95. reflector.findSubCommands().also {
  96. reflector.validate(it)
  97. }
  98. }
  99. /**
  100. * 自动根据带有 [SubCommand] 注解的函数签名生成 [usage]. 也可以被覆盖.
  101. */
  102. public override val usage: String by lazy {
  103. @OptIn(ExperimentalCommandDescriptors::class)
  104. reflector.generateUsage(overloads)
  105. }
  106. /**
  107. * 智能参数解析环境
  108. */ // open since 2.12
  109. public override val context: CommandArgumentContext = CommandArgumentContext.Builtins + overrideContext
  110. /**
  111. * 标记一个函数为子指令, 当 [value] 为空时使用函数名.
  112. * @param value 子指令名
  113. */
  114. @Retention(RUNTIME)
  115. @Target(FUNCTION)
  116. protected annotation class SubCommand(
  117. @ResolveContext(COMMAND_NAME) vararg val value: String = [],
  118. )
  119. /** 指令描述 */
  120. @Retention(RUNTIME)
  121. @Target(FUNCTION)
  122. protected annotation class Description(val value: String)
  123. /** 参数名, 将参与构成 [usage] */
  124. @ConsoleExperimentalApi("Classname might change")
  125. @Retention(RUNTIME)
  126. @Target(AnnotationTarget.VALUE_PARAMETER)
  127. protected annotation class Name(val value: String)
  128. }