...
使用 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)即可。