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