MiraiForum

    • 注册
    • 登录
    • 搜索
    • 热门
    • 最新
    • 未解决
    • 标签
    • 群组
    • 友情链接

    mirai 消息变量替换工具类

    技术交流板块
    kotlin jvm mirai-core util
    3
    4
    457
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • MrXiaoM
      MrXiaoM 童心未泯 最后由 编辑

      使用 kotlin 编写,但有 java 兼容。

      import kotlinx.coroutines.Dispatchers
      import kotlinx.coroutines.runBlocking
      import kotlinx.coroutines.withContext
      import net.mamoe.mirai.contact.Contact
      import net.mamoe.mirai.message.data.*
      import net.mamoe.mirai.utils.ExternalResource
      import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
      import java.net.URL
      
      /**
      * 消息助手
      * @author MrXiaoM
      */
      object MessageHelper {
         /**
          * 参见 [String.replace]
          * @author MrXiaoM
          */
         @JvmStatic
         fun replace(s: String, replacements: Map<String, SingleMessage>): MessageChain =
             runBlocking { s.replace(replacements) }
      }
      
      /**
      * 消息助手预处理消息接口
      * @author MrXiaoM
      */
      interface PrepareMessage : SingleMessage {
         /**
          * 该方法将会在该消息需要被处理时调用以生成消息。
          * 有时,用户可能会在单条消息多次使用同一变量,建议储存生成结果以便复用。
          * @author MrXiaoM
          */
         suspend fun generateMessage(): SingleMessage
         override fun contentToString(): String = ""
      }
      
      /**
      * 适用于 MessageHelper.replace 的预上传图片
      * @author MrXiaoM
      * @param contact 要上传到的联系人
      * @param handler 需要上传时用于生成 ExternalResource 的处理器,自行编写处理器时应自觉 toAutoCloseable()
      * @param failedText 上传失败时的代替文本
      */
      class PrepareUploadImage(
         private val contact: Contact,
         private val handler: suspend () -> ExternalResource?,
         private val failedText: String = ""
      ) : PrepareMessage {
         private var generated: SingleMessage? = null
         override suspend fun generateMessage(): SingleMessage {
             generated?.also { return it }
             return try {
                 contact.uploadImage(handler()!!).also { generated = it }
             } catch (_: Throwable) {
                 PlainText(failedText)
             }
         }
         override fun contentToString(): String = failedText
         override fun toString(): String = "PrepareUploadImage(contact=$contact)"
         companion object {
             /**
              * 生成预下载图片消息。
              * 图片将会在 replace 时发现需要用到该变量时下载。
              * @author MrXiaoM
              * @param contact 要上传到的联系人
              * @param link 图片下载链接
              * @param failedText 上传失败时的返回消息
              */
             @JvmStatic
             fun fromURL(contact: Contact, link: String, failedText: String = ""): PrepareUploadImage =
                 PrepareUploadImage(contact, {
                     try {
                         withContext(Dispatchers.IO) {
                             URL(link).openConnection().also { it.connect() }.getInputStream()
                         }.toExternalResource().toAutoCloseable()
                     } catch (t: Throwable) {
                         t.printStackTrace()
                         null
                     }
                 }, failedText)
      
             /**
              * 生成预下载图片消息。
              *
              * 参见 [fromURL]
              * @param link 要下载图片的链接
              * @param failedText 上传失败时的返回消息
              */
             @JvmStatic
             fun Contact.prepareUploadImage(link: String, failedText: String = ""): PrepareUploadImage =
                 fromURL(this, link, failedText)
      
             /**
              * 生成预下载头像图片消息。
              *
              * 参见 [prepareUploadImage]
              * @param failedText 上传失败时的返回消息
              */
             @JvmStatic
             fun Contact.prepareUploadAvatarImage(failedText: String = ""): PrepareUploadImage =
                 prepareUploadImage(avatarUrl, failedText)
         }
      }
      
      /**
      * 将字符串作为字符串模板,替换其中的变量为 SingleMessage 来生成 MessageChain
      *
      * 使用示例: @用户并发送他的头像
      * ```kotlin
      * val member = event.sender
      * "\$at 你好,你的头像是\n \$pic".replace(mapOf("at" to At(member), "pic" to member.prepareUploadAvatarImage()))
      * ```
      *
      * 在配置文件或者在 java 中不需要用 \ 将 $ 转义,这会对用户自定义机器人发送的消息的体验更加友好
      * @author MrXiaoM
      * @param replacements 变量对照表
      */
      suspend fun String.replace(replacements: Map<String, SingleMessage>): MessageChain {
         if (!this.contains("\$") || replacements.isEmpty()) return PlainText(this).toMessageChain()
         val message = MessageChainBuilder()
         val s = this.split("\$").toMutableList()
         message.add(s.removeAt(0))
         s.forEach { text ->
             var isOriginal = true
             for ((k, m) in replacements) {
                 if (!text.startsWith(k)) continue
                 if (m is PrepareMessage) message.add(m.generateMessage())
                 else message.add(m)
      
                 message.add(text.substring(k.length))
                 isOriginal = false
                 break
             }
             if (isOriginal) message.add("\$$text")
         }
         return message.build()
      }
      

      刚学 kt 不久,代码有点烂,但是能用就行。以下是使用例子,均会生成 @人间工作p 你好,2431208142

      // kotlin
      val msg = "\$at 你好,\$qq".replace(mapOf("qq" to PlainText(sender.id), "at" to At(sender)))
      
      // java
      Map<String, SingleMessage> replacements = new HashMap<>();
      replacements.put("qq", new PlainText(String.valueOf(sender.getId())));
      replacements.put("at", new At(sender.getId()));
      MessageChain msg = MessageHelper.replace("$at 你好,$qq", replacements);
      

      若要使用这份代码,你只需要在源代码中保留作者信息(即保留 @author MrXiaoM)即可。

      1 条回复 最后回复 回复 引用 0
      • Him188
        Him188 最后由 编辑

        感觉这样给 String 挂扩展而且还叫 replace 这种常用的名字不太好

        1 条回复 最后回复 回复 引用 0
        • helloworld123
          helloworld123 最后由 编辑

          replace效率稍微有些低 ,不如直接拼接

          MrXiaoM 1 条回复 最后回复 回复 引用 0
          • MrXiaoM
            MrXiaoM 童心未泯 @helloworld123 最后由 编辑

            @helloworld123 这份代码主要是面向用户,比如在配置文件自定义机器人回复信息格式,让用户写 mirai code 似乎并不是很现实,替换变量是我目前想到对用户相对友好的办法

            1 条回复 最后回复 回复 引用 0
            • 1 / 1
            • First post
              Last post
            Powered by Mamoe Technologies & NodeBB | 友情链接 | 服务监控 | Contact