Files
JChatGPT/src/main/kotlin/tools/SendSingleMessageAgent.kt
jie65535 d2bdd273b2 Add reply-to-message capability with ID-addressable history
Lets the bot quote-reply to a specific message via an optional replyTo
on sendSingleMessage, and reworks history serialization so the LLM can
address messages and stop confusing quoted content with the quoter.

- Serialize each history line with a short [n] id; consecutive messages
  from the same sender continue under "[n]  └". A per-subject ReplyIndex
  maps [n] -> MessageRecord, kept alive with the context cache so ids
  stay continuous across cached turns.
- Replace inlined quote text with a reference: "↩[k]" when the quoted
  message is in-window, otherwise "↩(author:"snippet…")". This removes
  the ambiguity where A quoting B looked like A's own speech.
- Collapse forwarded messages to "[转发消息·N条:title]".
- sendSingleMessage accepts replyTo (the [n]); it resolves the record via
  MessageRecord.toMessageSource() and prepends a QuoteReply, falling back
  to a plain send with a note if the source is gone. ids may be null, so
  numbering still happens but such records can't be reply targets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 22:46:59 +08:00

61 lines
2.2 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package top.jie65535.mirai.tools
import com.aallam.openai.api.chat.Tool
import com.aallam.openai.api.core.Parameters
import kotlinx.serialization.json.*
import net.mamoe.mirai.event.events.MessageEvent
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.QuoteReply
import top.jie65535.mirai.JChatGPT
class SendSingleMessageAgent : BaseAgent(
tool = Tool.function(
name = "sendSingleMessage",
description = "发送一条消息适合发送一行以内的短句不支持Markdown",
parameters = Parameters.buildJsonObject {
put("type", "object")
putJsonObject("properties") {
putJsonObject("content") {
put("type", "string")
put("description", "消息内容")
}
putJsonObject("replyTo") {
put("type", "integer")
put("description", "可选。要引用回复的历史消息编号(即历史记录中每行行首的[n])。不需要回复具体某条消息时省略此参数。")
}
}
putJsonArray("required") {
add("content")
}
}
)
) {
override suspend fun execute(args: JsonObject?, event: MessageEvent): String {
requireNotNull(args)
val content = args.getValue("content").jsonPrimitive.content
val replyTo = args["replyTo"]?.jsonPrimitive?.intOrNull
val baseMsg = JChatGPT.toMessage(event.subject, content)
var note = ""
val message: Message = if (replyTo != null) {
val record = JChatGPT.lookupReplyTarget(event.subject.id, replyTo)
val source = try {
record?.toMessageSource()
} catch (e: Throwable) {
null
}
if (source != null) {
QuoteReply(source) + baseMsg
} else {
note = "(编号${replyTo}对应的消息已失效,未能引用,已直接发送)"
baseMsg
}
} else {
baseMsg
}
event.subject.sendMessage(message)
return "OK$note"
}
}