feat(channel): 跨平台 IM 接入抽象契约(UnifiedMessage/PlatformAdapter/Capabilities/Actor 身份)
This commit is contained in:
1 parent
a21176ec0a
commit
c52ddfd9c7
8 files changed
+767
No files matched your search
@@ -0,0 +1,161 @@
|
||||
// Package adapter defines the cross-platform IM channel adaptation contract for
|
||||
// Cyrene v2. It declares the normalized message model, the per-platform adapter
|
||||
// interface, the capability declaration, and the strongly typed identity (actor)
|
||||
// metadata that the channel layer uses for owner detection and group-chat
|
||||
// isolation.
|
||||
//
|
||||
// The contract is intentionally platform-neutral: concrete adapters (qq-obv11,
|
||||
// weixin-ilink, telegram, webhook) are implemented behind this interface in
|
||||
// milestone M6, not here. The design authority is DevDocs/design/08-渠道接入.md
|
||||
// (sections 0-2) and DevDocs/design/02-会话与上下文模型.md (section 3).
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ChannelType classifies where a message originated or is destined.
|
||||
type ChannelType string
|
||||
|
||||
const (
|
||||
// ChannelDirect is a one-to-one private conversation.
|
||||
ChannelDirect ChannelType = "direct"
|
||||
// ChannelGroup is a group / room conversation.
|
||||
ChannelGroup ChannelType = "group"
|
||||
)
|
||||
|
||||
// Valid reports whether ct is one of the known channel kinds.
|
||||
func (ct ChannelType) Valid() bool {
|
||||
switch ct {
|
||||
case ChannelDirect, ChannelGroup:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ContentType classifies the primary payload of a message.
|
||||
type ContentType string
|
||||
|
||||
const (
|
||||
// ContentText is a plain-text chat message.
|
||||
ContentText ContentType = "text"
|
||||
// ContentImage is an image attachment.
|
||||
ContentImage ContentType = "image"
|
||||
// ContentVoice is a voice/audio clip.
|
||||
ContentVoice ContentType = "voice"
|
||||
// ContentFile is an arbitrary file attachment.
|
||||
ContentFile ContentType = "file"
|
||||
// ContentAction is a non-chat action signal (typing, join/leave, recall).
|
||||
ContentAction ContentType = "action"
|
||||
)
|
||||
|
||||
// Valid reports whether ct is one of the known content kinds.
|
||||
func (ct ContentType) Valid() bool {
|
||||
switch ct {
|
||||
case ContentText, ContentImage, ContentVoice, ContentFile, ContentAction:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MediaKind classifies an individual media attachment. It is broader than
|
||||
// ContentType so attachments can also carry video or other payloads.
|
||||
type MediaKind string
|
||||
|
||||
const (
|
||||
// MediaImage is an image file.
|
||||
MediaImage MediaKind = "image"
|
||||
// MediaVoice is an audio/voice file.
|
||||
MediaVoice MediaKind = "voice"
|
||||
// MediaVideo is a video file.
|
||||
MediaVideo MediaKind = "video"
|
||||
// MediaFile is an arbitrary file.
|
||||
MediaFile MediaKind = "file"
|
||||
)
|
||||
|
||||
// Media is a single attachment carried by a UnifiedMessage. URL and Data are
|
||||
// mutually exclusive representations: prefer URL for remote media to
|
||||
// keep payloads out of memory.
|
||||
type Media struct {
|
||||
// Kind classifies the attachment.
|
||||
Kind MediaKind
|
||||
// URL is the remote location of the media when not inlined.
|
||||
URL string
|
||||
// Name is the original file name, when known.
|
||||
Name string
|
||||
// Size is the media size in bytes.
|
||||
Size int64
|
||||
// MIMEType is the media content type, when known.
|
||||
MIMEType string
|
||||
// Data carries inlined bytes for small, locally produced payloads.
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Mention is a reference to a user inside a message body.
|
||||
type Mention struct {
|
||||
UserID string
|
||||
UserName string
|
||||
}
|
||||
|
||||
// UnifiedMessage is the normalized, platform-neutral representation of an IM
|
||||
// event or inbound message. Every channel adapter converts its native payload
|
||||
// into UnifiedMessage so the runtime can reason about them uniformly.
|
||||
//
|
||||
// SenderID/SenderName are kept per design/08 section 0; Sender carries the
|
||||
// strongly typed identity metadata (Role, group flags) used for owner detection
|
||||
// and group isolation. The strategy engine (M6) decides access from Sender, not
|
||||
// from the free-text SenderName.
|
||||
type UnifiedMessage struct {
|
||||
Platform string
|
||||
ChannelID string
|
||||
ChannelType ChannelType
|
||||
SenderID string
|
||||
SenderName string
|
||||
Sender Actor
|
||||
Content string
|
||||
ContentType ContentType
|
||||
Media []Media
|
||||
Mentions []Mention
|
||||
ReplyToID string
|
||||
ReplyToText string
|
||||
GroupID string
|
||||
GroupName string
|
||||
ThreadID string
|
||||
MessageID string
|
||||
Timestamp time.Time
|
||||
// RawData is the opaque native payload the message was converted from.
|
||||
RawData []byte
|
||||
}
|
||||
|
||||
// UnifiedResponse is the normalized outbound payload for SendMessage. It may
|
||||
// carry several messages so an adapter can burst a reply.
|
||||
type UnifiedResponse struct {
|
||||
// Messages are the reply messages to deliver.
|
||||
Messages []UnifiedMessage
|
||||
// ReplyTo is an optional context identifier the messages reply to.
|
||||
ReplyTo string
|
||||
// Hint is an optional human hint surfaced by the runtime.
|
||||
Hint string
|
||||
}
|
||||
|
||||
// Validate reports whether the message is well-formed for downstream handling.
|
||||
// It checks the enum fields and requires a group id on group messages.
|
||||
func (m UnifiedMessage) Validate() error {
|
||||
if !m.ChannelType.Valid() {
|
||||
return fmt.Errorf("adapter: invalid channel type %q", m.ChannelType)
|
||||
}
|
||||
if !m.ContentType.Valid() {
|
||||
return fmt.Errorf("adapter: invalid content type %q", m.ContentType)
|
||||
}
|
||||
if m.ChannelType == ChannelGroup && m.GroupID == "" {
|
||||
return errors.New("adapter: group message missing group id")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsGroup reports whether the message belongs to a group conversation.
|
||||
func (m UnifiedMessage) IsGroup() bool { return m.ChannelType == ChannelGroup }
|
||||
Reference in new issue
Block a user