draft

package
v1.0.17 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 22, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Index

Constants

View Source
const (
	LargeFileContainerIDPrefix = "large-file-area-"
	LargeFileItemID            = "large-file-item"
	LargeAttachmentTokenAttr   = "data-mail-token"
)

Well-known anchors for the large attachment HTML card generated by CLI and the desktop client. The HTML structure is:

<div id="large-file-area-{timestamp}" ...>
  <div>Title</div>
  <div id="large-file-item" ...>
    ... filename, size, <a data-mail-token="..."> ...
  </div>
  <div id="large-file-item" ...> ... </div>
</div>
View Source
const LargeAttachmentIDsHeader = "X-Lms-Large-Attachment-Ids"

LargeAttachmentIDsHeader is the header name CLI writes when creating or editing a draft. The value is base64-encoded JSON: [{"id":"<token>"}].

View Source
const QuoteWrapperClass = "history-quote-wrapper"

QuoteWrapperClass is the CSS class name used by Lark's mail composer for reply/forward quote blocks. Both +reply and +forward wrap the quoted original message in a <div> with this class. Exported so that mail_quote.go (the generator) and projection.go (the detector) share a single source of truth.

View Source
const ServerLargeAttachmentHeader = "X-Lark-Large-Attachment"

ServerLargeAttachmentHeader is the header name the mail server returns on readback. The value is base64-encoded JSON with richer metadata: [{"file_key":"<token>","file_name":"...","file_size":...}].

View Source
const SignatureWrapperClass = "lark-mail-signature"

SignatureWrapperClass is the CSS class for the mail signature container.

Variables

This section is empty.

Functions

func Apply

func Apply(dctx *DraftCtx, snapshot *DraftSnapshot, patch Patch) error

func BuildSignatureHTML added in v1.0.12

func BuildSignatureHTML(sigID, content string) string

BuildSignatureHTML wraps signature content in the standard signature container div. sigID is HTML-escaped to prevent attribute injection.

func ExtractSignatureBlock added in v1.0.16

func ExtractSignatureBlock(html string) string

ExtractSignatureBlock returns the signature block (including any preceding spacing that would be removed by RemoveSignatureHTML) from html. Returns "" when html has no signature.

Symmetric to RemoveSignatureHTML: RemoveSignatureHTML(html) + ExtractSignatureBlock(html) reconstitutes the original html.

func FindMatchingCloseDiv added in v1.0.12

func FindMatchingCloseDiv(html string, startPos int) int

FindMatchingCloseDiv finds the position after the closing </div> that matches the <div at startPos, tracking nesting depth.

func FindOrphanedCIDs added in v1.0.6

func FindOrphanedCIDs(html string, addedCIDs []string) []string

FindOrphanedCIDs returns CIDs from addedCIDs that are not referenced in the HTML body via <img src="cid:...">. These would appear as unexpected attachments when the email is sent.

func HTMLContainsLargeAttachment added in v1.0.16

func HTMLContainsLargeAttachment(html string) bool

HTMLContainsLargeAttachment reports whether the given HTML fragment contains a large attachment card container (`<div ... id="large-file-area-..."`). Used to detect whether a user-supplied set_body value already carries a card, in which case auto-preservation is skipped.

func InsertBeforeQuoteOrAppend added in v1.0.16

func InsertBeforeQuoteOrAppend(html, block string) string

InsertBeforeQuoteOrAppend inserts block into html right before the outermost quote wrapper (<div ... class="history-quote-wrapper">), or appends it to the end when no quote block is present. Matching uses quoteWrapperRe (an actual element with the class attribute), avoiding false positives from plain-text or code-snippet occurrences of the class name.

func IsLargeAttachmentHeader added in v1.0.16

func IsLargeAttachmentHeader(name string) bool

IsLargeAttachmentHeader returns true if the header name matches either the CLI-written or server-returned large attachment header.

func MustJSON

func MustJSON(v interface{}) string

func ParseLargeAttachmentItemsFromHTML added in v1.0.16

func ParseLargeAttachmentItemsFromHTML(htmlBody string) map[string]LargeAttachmentSummary

ParseLargeAttachmentItemsFromHTML walks the HTML body looking for large attachment card items (<div id="large-file-item">) and returns a map from token (data-mail-token attribute value) to filename + size.

The size is parsed best-effort from the displayed string (e.g. "25.0 MB"); it carries the precision of the formatted value and is not byte-exact.

func PlaceSignatureBeforeSystemTail added in v1.0.16

func PlaceSignatureBeforeSystemTail(html, sigBlock string) string

PlaceSignatureBeforeSystemTail is the single source of truth for signature placement. It removes any existing signature from html, then inserts sigBlock at the split point between the user-authored region and the system-managed tail (large attachment card or history quote wrapper, whichever comes first).

Used by both compose-time signature injection (mail/signature_compose.go) and edit-time insert_signature op (draft/patch.go), guaranteeing they produce a consistent HTML layout [user][sig][card?][quote?].

When sigBlock is empty, behaves as a simple "remove signature" on the HTML string level — note that callers needing MIME-part orphan cleanup should handle that separately.

func RemoveLargeFileItemFromHTML added in v1.0.16

func RemoveLargeFileItemFromHTML(htmlBody, token string) (string, bool)

RemoveLargeFileItemFromHTML parses the HTML, finds the large-file-item containing an <a> whose token matches (via data-mail-token attribute or href URL token= parameter), removes that item, and if the enclosing large-file-area container becomes empty, removes the container as well. Returns the updated HTML and a changed flag.

func RemoveSignatureHTML added in v1.0.12

func RemoveSignatureHTML(html string) string

RemoveSignatureHTML removes the signature block and its preceding spacing from HTML. Returns the HTML unchanged if no signature is found.

func Send

func Send(runtime *common.RuntimeContext, mailboxID, draftID, sendTime string) (map[string]interface{}, error)

func Serialize

func Serialize(snapshot *DraftSnapshot) (string, error)

func SignatureSpacing added in v1.0.12

func SignatureSpacing() string

SignatureSpacing returns the 2 empty-line divs placed before the signature, matching the structure generated by the Lark mail editor.

func SignatureSpacingRe added in v1.0.12

func SignatureSpacingRe() *regexp.Regexp

SignatureSpacingRe returns the compiled regex for signature spacing detection.

func SplitAtLargeAttachment added in v1.0.16

func SplitAtLargeAttachment(html string) (before, card, after string)

SplitAtLargeAttachment splits HTML into three pieces around the first large-file-area container: content before, the entire container block, and content after. If no container is present, returns (html, "", "").

Used by set_body / set_reply_body to preserve the large attachment card across body replacements.

func SplitAtQuote added in v1.0.12

func SplitAtQuote(html string) (body, quote string)

SplitAtQuote splits an HTML body into the user-authored content and the trailing reply/forward quote block. If no quote block is found, quote is empty and body is the original html unchanged.

func ValidateCIDReferences added in v1.0.6

func ValidateCIDReferences(html string, availableCIDs []string) error

ValidateCIDReferences checks that every cid: reference in the HTML body has a matching entry in availableCIDs. Returns an error for the first missing CID. Both sides are compared case-insensitively.

Types

type Address

type Address struct {
	Name    string `json:"name,omitempty"`
	Address string `json:"address"`
}

func (Address) String

func (a Address) String() string

type AttachmentTarget

type AttachmentTarget struct {
	PartID string `json:"part_id,omitempty"`
	CID    string `json:"cid,omitempty"`
	// Token selects a large attachment by its file token (registered via
	// the X-Lms-Large-Attachment-Ids header). Only valid for
	// remove_attachment; replace_inline/remove_inline operate on MIME
	// parts and do not accept Token.
	Token string `json:"token,omitempty"`
}

type DraftCtx added in v1.0.7

type DraftCtx struct {
	FIO fileio.FileIO
}

DraftCtx carries runtime dependencies for draft operations. It is separate from DraftSnapshot to keep the snapshot a pure data model.

type DraftProjection

type DraftProjection struct {
	Subject                 string                   `json:"subject"`
	To                      []Address                `json:"to,omitempty"`
	Cc                      []Address                `json:"cc,omitempty"`
	Bcc                     []Address                `json:"bcc,omitempty"`
	ReplyTo                 []Address                `json:"reply_to,omitempty"`
	InReplyTo               string                   `json:"in_reply_to,omitempty"`
	References              string                   `json:"references,omitempty"`
	BodyText                string                   `json:"body_text,omitempty"`
	BodyHTMLSummary         string                   `json:"body_html_summary,omitempty"`
	HasQuotedContent        bool                     `json:"has_quoted_content,omitempty"`
	HasSignature            bool                     `json:"has_signature,omitempty"`
	SignatureID             string                   `json:"signature_id,omitempty"`
	AttachmentsSummary      []PartSummary            `json:"attachments_summary,omitempty"`
	LargeAttachmentsSummary []LargeAttachmentSummary `json:"large_attachments_summary,omitempty"`
	InlineSummary           []PartSummary            `json:"inline_summary,omitempty"`
	Warnings                []string                 `json:"warnings,omitempty"`
}

func Project

func Project(snapshot *DraftSnapshot) DraftProjection

type DraftRaw

type DraftRaw struct {
	DraftID string
	RawEML  string
}

func GetRaw

func GetRaw(runtime *common.RuntimeContext, mailboxID, draftID string) (DraftRaw, error)

type DraftResult added in v1.0.16

type DraftResult struct {
	DraftID   string
	Reference string
}

func CreateWithRaw

func CreateWithRaw(runtime *common.RuntimeContext, mailboxID, rawEML string) (DraftResult, error)

func UpdateWithRaw

func UpdateWithRaw(runtime *common.RuntimeContext, mailboxID, draftID, rawEML string) (DraftResult, error)

type DraftSnapshot

type DraftSnapshot struct {
	DraftID string
	Headers []Header
	Body    *Part

	Subject    string
	From       []Address
	To         []Address
	Cc         []Address
	Bcc        []Address
	ReplyTo    []Address
	MessageID  string
	InReplyTo  string
	References string

	PrimaryTextPartID string
	PrimaryHTMLPartID string
}

func Parse

func Parse(raw DraftRaw) (*DraftSnapshot, error)
type Header struct {
	Name  string
	Value string
}

type LargeAttachmentSummary added in v1.0.16

type LargeAttachmentSummary struct {
	Token     string `json:"token"`
	FileName  string `json:"filename,omitempty"`
	SizeBytes int64  `json:"size_bytes,omitempty"`
}

LargeAttachmentSummary describes a single large attachment registered in the draft via the X-Lms-Large-Attachment-Ids header. Unlike normal attachments, large attachments have no MIME part — their existence is conveyed by the header plus an HTML card in the body.

func ParseLargeAttachmentSummariesFromHeader added in v1.0.16

func ParseLargeAttachmentSummariesFromHeader(headers []Header) []LargeAttachmentSummary

ParseLargeAttachmentSummariesFromHeader extracts full metadata from the large attachment header. Returns non-nil only when the server-format header (X-Lark-Large-Attachment) is found, since it carries file_name and file_size that the CLI-format header lacks.

type LocalImageRef added in v1.0.6

type LocalImageRef struct {
	FilePath string // original src value from the HTML
	CID      string // generated Content-ID
}

LocalImageRef represents a local image found in an HTML body that needs to be embedded as an inline MIME part.

func ResolveLocalImagePaths added in v1.0.6

func ResolveLocalImagePaths(html string) (string, []LocalImageRef, error)

ResolveLocalImagePaths scans HTML for <img src="local/path"> references, validates each path, generates CIDs, and returns the modified HTML with cid: URIs plus the list of local image references to embed as inline parts. This function handles only the HTML transformation; callers are responsible for embedding the actual file data (e.g., via emlbuilder.AddFileInline).

type Part

type Part struct {
	PartID string

	Headers               []Header
	MediaType             string
	MediaParams           map[string]string
	ContentDisposition    string
	ContentDispositionArg map[string]string
	ContentID             string
	TransferEncoding      string

	Children []*Part
	Body     []byte

	Preamble []byte
	Epilogue []byte

	RawEntity []byte
	Dirty     bool

	// EncodingProblem is set when the part's body could not be decoded as
	// declared (e.g. malformed base64, bad charset, unparseable Content-Type).
	// The part still contains usable data (raw bytes or fallback decode) and
	// can round-trip through RawEntity, but callers should treat Body as
	// potentially degraded.
	EncodingProblem bool
}

func FindHTMLBodyPart added in v1.0.16

func FindHTMLBodyPart(root *Part) *Part

FindHTMLBodyPart walks the MIME tree and returns the first text/html body part (skipping attachment-disposition parts), or nil when none exists.

func FindTextBodyPart added in v1.0.16

func FindTextBodyPart(root *Part) *Part

FindTextBodyPart walks the MIME tree and returns the first text/plain body part (skipping attachment-disposition parts), or nil when none exists.

func (*Part) Clone

func (p *Part) Clone() *Part

func (*Part) FileName

func (p *Part) FileName() string

func (*Part) IsMultipart

func (p *Part) IsMultipart() bool

type PartSummary

type PartSummary struct {
	PartID      string `json:"part_id"`
	FileName    string `json:"filename,omitempty"`
	ContentType string `json:"content_type,omitempty"`
	Disposition string `json:"disposition,omitempty"`
	CID         string `json:"cid,omitempty"`
}

type Patch

type Patch struct {
	Ops     []PatchOp    `json:"ops"`
	Options PatchOptions `json:"options,omitempty"`
}

func (Patch) Summary

func (p Patch) Summary() map[string]interface{}

func (Patch) Validate

func (p Patch) Validate() error

type PatchOp

type PatchOp struct {
	Op          string           `json:"op"`
	Value       string           `json:"value,omitempty"`
	Field       string           `json:"field,omitempty"`
	Address     string           `json:"address,omitempty"`
	Name        string           `json:"name,omitempty"`
	Addresses   []Address        `json:"addresses,omitempty"`
	BodyKind    string           `json:"body_kind,omitempty"`
	Selector    string           `json:"selector,omitempty"`
	Path        string           `json:"path,omitempty"`
	CID         string           `json:"cid,omitempty"`
	FileName    string           `json:"filename,omitempty"`
	ContentType string           `json:"content_type,omitempty"`
	Target      AttachmentTarget `json:"target,omitempty"`
	SignatureID string           `json:"signature_id,omitempty"`

	// RenderedSignatureHTML is set by the shortcut layer (not from JSON) after
	// fetching and interpolating the signature. The patch layer uses this
	// pre-rendered content for insert_signature ops.
	RenderedSignatureHTML string           `json:"-"`
	SignatureImages       []SignatureImage `json:"-"`
}

func (PatchOp) Validate

func (op PatchOp) Validate() error

type PatchOptions

type PatchOptions struct {
	RewriteEntireDraft        bool `json:"rewrite_entire_draft,omitempty"`
	AllowProtectedHeaderEdits bool `json:"allow_protected_header_edits,omitempty"`
}

type SignatureImage added in v1.0.12

type SignatureImage struct {
	CID         string
	ContentType string
	FileName    string
	Data        []byte
}

SignatureImage holds pre-downloaded image data for signature inline images. Populated by the shortcut layer, consumed by the patch layer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL