mirai 消息变量替换工具类
-
使用 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
)即可。 -
感觉这样给 String 挂扩展而且还叫 replace 这种常用的名字不太好
-
replace效率稍微有些低 ,不如直接拼接
-
@helloworld123 这份代码主要是面向用户,比如在配置文件自定义机器人回复信息格式,让用户写 mirai code 似乎并不是很现实,替换变量是我目前想到对用户相对友好的办法