Documentation
¶
Overview ¶
Package chatmsg holds the shared, read-only projection helpers for DingTalk message-list responses (list_individual_chat_message, list_conversation_message_v2, search_at_me_message, search_messages_by_keyword, list_topic_replies, …). Several shortcuts reshape those raw responses into a clean speaker/text/time list; centralising the fiddly bits here keeps them consistent and fixed in one place:
- Sender: the display name lives under the bare "sender" key, forwarded entries carry the literal string "null", and some responses nest the speaker in a {name:…} object — all handled here.
- Text: out-of-office auto-replies / cards arrive as raw rich-content JSON, and card/robot messages arrive as undecryptable ciphertext; CleanText renders the former to readable text and marks the latter, WITHOUT ever rewriting ordinary text that merely contains a JSON fragment.
- Forwarded: a forwarded chat record ("聊天记录") hides its real per-message bodies in forwardMessages while the top-level content is a lossy summary.
Index ¶
- Constants
- func ApplyMessagePagination(payload, data map[string]any, messages []map[string]any, direction string)
- func ApplyPagination(payload, data map[string]any)
- func CleanText(s string) string
- func ConversationID(m map[string]any) any
- func CreateTime(m map[string]any) any
- func Forwarded(m map[string]any, project func(map[string]any) map[string]any) []map[string]any
- func IsEncrypted(s string) bool
- func MessageID(m map[string]any) any
- func MessageType(m map[string]any) any
- func NewMessageListPayload(messages []map[string]any) map[string]any
- func Pagination(data map[string]any) map[string]any
- func ProjectMessageV1(m map[string]any, includeReactions bool) map[string]any
- func QuotedMessage(m map[string]any) map[string]any
- func Reactions(m map[string]any) map[string]any
- func Resources(m map[string]any) []map[string]any
- func ResourcesDeep(m map[string]any) []map[string]any
- func Sender(m map[string]any) any
- func SenderID(m map[string]any) any
- func SenderType(m map[string]any) any
- func StableMessageID(message map[string]any) string
- func Text(m map[string]any) any
- func ThreadID(m map[string]any) any
- func UpdateTime(m map[string]any) any
- type MessageResultContract
Constants ¶
const MessageListContractVersion = "im.message-list.v1"
MessageListContractVersion identifies the additive, compatibility-preserving public envelope shared by message list/search/mget/thread projections.
Variables ¶
This section is empty.
Functions ¶
func ApplyMessagePagination ¶
func ApplyMessagePagination(payload, data map[string]any, messages []map[string]any, direction string)
ApplyMessagePagination publishes message-list completeness without claiming the lower response's nextCursor is a valid CLI input. DingTalk's executable message-list contract paginates with the boundary message createTime, so the resume object uses exactly that accepted parameter.
func ApplyPagination ¶
ApplyPagination carries lower-layer completeness facts into a projected Shortcut payload. It intentionally preserves cursor values only in the command output (where callers need them to continue); audit reports redact those values and retain only their presence.
func CleanText ¶
CleanText makes a message body human-readable WITHOUT ever rewriting ordinary text. It only transforms a body that is a genuine DingTalk structured message:
- Encrypted card/robot ciphertext (base64 + "||v||t||len" trailer) → a clear "[加密消息]" marker instead of the raw base64.
- A rich-content card (out-of-office auto-reply, link/preview card, …) whose lines include at least one recognised rich-content block → the readable text extracted from those blocks, with the card's decorative JSON lines and "empty" placeholders dropped.
Crucially, if NO line is a recognised rich-content block (e.g. ordinary text that merely embeds a `{"approved":false}` fragment), the original string is returned verbatim — a JSON line is never silently dropped.
func ConversationID ¶
ConversationID preserves the stable conversation identity carried by list and search responses.
func CreateTime ¶
CreateTime reads a message's create/send time under whichever candidate key is present, returning the raw value.
func Forwarded ¶
Forwarded projects the nested messages of a forwarded chat record. The caller supplies its own per-message projection so each command keeps its own row shape; project is applied recursively, so multi-level forwards expand too.
func IsEncrypted ¶
IsEncrypted reports whether a message body is a raw DingTalk encrypted-message ciphertext: a base64 blob (DingTalk wraps it across several lines) followed by the "||v||t||len" trailer. It is intentionally strict — both the trailer and a pure-base64 body are required — so ordinary text (CJK, punctuation, …) never trips it.
func MessageID ¶
MessageID preserves the stable message identity needed by follow-up reply, reaction, resource and deduplication operations.
func MessageType ¶
MessageType preserves the lower message type when present.
func NewMessageListPayload ¶ added in v1.0.57
NewMessageListPayload initializes the common result ledger before a caller adds pagination or resource-download facts.
func Pagination ¶
Pagination extracts hasMore/nextCursor from the response root or a common result/data envelope. When hasMore is present it also emits the explicit inverse "complete", making truncation hard for an Agent to overlook.
func ProjectMessageV1 ¶ added in v1.0.57
ProjectMessageV1 is the single compatibility-preserving core projection for list, search, mget, @me, and thread readers. Public wrappers may retain legacy aliases such as time or msgType, but the underlying identity, context, reaction, quote, forward, and resource semantics come from here.
func QuotedMessage ¶
QuotedMessage projects one level of quoted/replied-to context. It is deliberately non-recursive: a reply chain may be arbitrarily deep or even cyclic after gateway reshaping, while an Agent primarily needs the quoted message's stable identity, speaker, readable body and time.
func Reactions ¶
Reactions normalises DingTalk's inline emotionReplyList into one compact, Agent-friendly block. Unlike Lark, DingTalk already returns these reactions with message-list responses, so this projection performs no extra network request.
Output shape:
"reactions": {
"counts": [{"emoji": "赞", "count": 3}],
"details": [{"emoji": "赞", "replyUsers": ["..."]}]
}
func Resources ¶
Resources extracts actionable media and drive-file references from both structured message fields and the textual mediaId/fileId notation returned by older DingTalk message APIs. Every reference publishes the exact Shortcut arguments already known from the message, plus ready=false and missing fields when a follow-up lookup is still required. This shared shape is used by list, search, mget, quoted messages and thread replies.
func ResourcesDeep ¶ added in v1.0.56
ResourcesDeep returns resources from a message and each nested quoted, replied-to or forwarded message. Every nested resource is projected from the child message that owns it, so its download arguments never reuse the parent message ID. A missing child conversation ID inherits the enclosing conversation because quoted and forwarded records often omit that duplicate field.
func Sender ¶
Sender reads a message's speaker display name, tolerating common sender-name keys. The message-list responses carry the display name under the bare "sender" key (verified live), so it is probed first; the remaining aliases and the *Id fallbacks keep the projection resilient to other shapes. The literal string "null" (forwarded entries) and the empty string are treated as absent, and a nested {name:…} sender object yields its display name rather than the raw object.
func SenderID ¶ added in v1.0.57
SenderID preserves the stable sender identity without replacing the legacy scalar sender display field. Nested sender records and both userId families are accepted because list/search/mget currently expose different shapes.
func SenderType ¶ added in v1.0.57
SenderType returns only an explicitly published lower sender type. It does not guess that every sender identity is a user because bot/system messages can share the same generic senderId key.
func StableMessageID ¶ added in v1.0.57
StableMessageID returns the normalized message identity used for cross-page deduplication. An empty value means the lower response did not publish a stable identity; callers must keep that row rather than guessing.
func Text ¶
Text reads a message's textual content (tolerating common text keys and one level of nesting) and runs it through CleanText.
func ThreadID ¶
ThreadID preserves the stable topic/thread identity needed to continue from a message-list result into the thread-replies command.
func UpdateTime ¶
UpdateTime reads an edited message's update time. Gateways sometimes echo createTime as updateTime even when the message was never edited; omit that duplicate so Agents do not infer a nonexistent edit.
Types ¶
type MessageResultContract ¶ added in v1.0.57
MessageResultContract is the reviewed additive contract shared by message list, search, mget, @me and thread projections. Keep this descriptor small: Runtime owns the values, while Skill references and policy checks consume a copy of these field names so prose cannot silently invent another result shape.
func CurrentMessageResultContract ¶ added in v1.0.57
func CurrentMessageResultContract() MessageResultContract
CurrentMessageResultContract returns defensive copies so callers cannot mutate the process-wide reviewed descriptor.