emlbuilder: add LMSReplyType method for reply/forward type header

Add LMSReplyType method to the EML builder that sets the
X-LMS-Reply-Type header. This allows reply, reply-all, and forward
shortcuts to tag the EML with REPLY or FORWARD so that downstream
services can label the original message accordingly.

Call LMSReplyType("REPLY") in mail_reply.go and mail_reply_all.go,
and LMSReplyType("FORWARD") in mail_forward.go, immediately after
the existing LMSReplyToMessageID call.
This commit is contained in:
xzcong0820
2026-06-26 22:03:07 +08:00
parent 8a268aa2d2
commit c0091b3ed9
5 changed files with 69 additions and 0 deletions

View File

@@ -57,6 +57,10 @@ import (
"github.com/larksuite/cli/shortcuts/mail/filecheck"
)
// lmsReplyTypeHeaderKey is the MIME header name for the reply type
// ("REPLY" or "FORWARD"), read by data-access to populate BodyExtra.ReplyType.
const lmsReplyTypeHeaderKey = "X-LMS-Reply-Type"
// MaxEMLSize is the maximum allowed raw EML size in bytes.
const MaxEMLSize = 25 * 1024 * 1024 // 25 MB
@@ -90,6 +94,7 @@ type Builder struct {
inReplyTo string // raw value, without angle brackets
references string // space-separated list of message IDs, with angle brackets
lmsReplyToMessageID string // Lark internal message_id of the original message
lmsReplyType string // "REPLY" or "FORWARD", written as X-LMS-Reply-Type
textBody []byte
htmlBody []byte
calendarBody []byte
@@ -395,6 +400,22 @@ func (b Builder) LMSReplyToMessageID(id string) Builder {
return b
}
// LMSReplyType sets the reply type header X-LMS-Reply-Type.
// t must be "REPLY" or "FORWARD"; other values are silently ignored.
// This header is written unconditionally when set, independent of In-Reply-To.
// Returns an error builder if t contains CR, LF, or other dangerous characters.
func (b Builder) LMSReplyType(t string) Builder {
if b.err != nil {
return b
}
if err := validateHeaderValue(t); err != nil {
b.err = err
return b
}
b.lmsReplyType = t
return b
}
// References sets the References header value verbatim.
// Typically a space-separated list of message IDs including angle brackets,
// e.g. "<id1@host> <id2@host>".
@@ -732,6 +753,9 @@ func (b Builder) Build() ([]byte, error) {
for _, kv := range b.extraHeaders {
writeHeader(&buf, kv[0], kv[1])
}
if b.lmsReplyType != "" {
writeHeader(&buf, lmsReplyTypeHeaderKey, b.lmsReplyType)
}
// ── Body ───────────────────────────────────────────────────────────────────
// Full MIME hierarchy (outer layers only present when needed):

View File

@@ -285,6 +285,48 @@ func TestBuild_LMSReplyToMessageID_NotWrittenWithoutInReplyTo(t *testing.T) {
}
}
// TestBuild_LMSReplyType verifies LMS reply type header writing.
func TestBuild_LMSReplyType(t *testing.T) {
raw, err := New().
From("", "alice@example.com").
To("", "bob@example.com").
Subject("Re: hello").
Date(fixedDate).
LMSReplyType("REPLY").
TextBody([]byte("my reply")).
Build()
if err != nil {
t.Fatal(err)
}
eml := string(raw)
got := headerValue(eml, "X-LMS-Reply-Type")
if got != "REPLY" {
t.Errorf("X-LMS-Reply-Type: got %q, want REPLY", got)
}
}
// TestBuild_LMSReplyType_Forward verifies LMSReplyType with FORWARD value.
func TestBuild_LMSReplyType_Forward(t *testing.T) {
raw, err := New().
From("", "alice@example.com").
To("", "bob@example.com").
Subject("Fw: hello").
Date(fixedDate).
LMSReplyType("FORWARD").
TextBody([]byte("fw body")).
Build()
if err != nil {
t.Fatal(err)
}
eml := string(raw)
got := headerValue(eml, "X-LMS-Reply-Type")
if got != "FORWARD" {
t.Errorf("X-LMS-Reply-Type: got %q, want FORWARD", got)
}
}
// ── Disposition-Notification-To (read receipt) ───────────────────────────────
// TestBuild_DispositionNotificationTo verifies build disposition notification to.

View File

@@ -256,6 +256,7 @@ var MailForward = common.Shortcut{
}
if messageId != "" {
bld = bld.LMSReplyToMessageID(messageId)
bld = bld.LMSReplyType("FORWARD")
}
useHTML := !plainText && (bodyIsHTML(body) || bodyIsHTML(orig.bodyRaw) || sigResult != nil)
if strings.TrimSpace(inlineFlag) != "" && !useHTML {

View File

@@ -269,6 +269,7 @@ var MailReply = common.Shortcut{
}
if messageId != "" {
bld = bld.LMSReplyToMessageID(messageId)
bld = bld.LMSReplyType("REPLY")
}
var autoResolvedPaths []string
var composedHTMLBody string

View File

@@ -278,6 +278,7 @@ var MailReplyAll = common.Shortcut{
}
if messageId != "" {
bld = bld.LMSReplyToMessageID(messageId)
bld = bld.LMSReplyType("REPLY")
}
var autoResolvedPaths []string
var composedHTMLBody string