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,97 @@
|
||||
package adapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MessageHandler receives a normalized inbound message. It is the push-style
|
||||
// counterpart to ReadMessages: adapters call it when the platform delivers a
|
||||
// message asynchronously, so consumers do not have to run a polling loop.
|
||||
type MessageHandler func(ctx context.Context, msg UnifiedMessage)
|
||||
|
||||
// ChannelInfo describes a channel the adapter exposes, used by ListChannels.
|
||||
type ChannelInfo struct {
|
||||
ChannelID string
|
||||
ChannelType ChannelType
|
||||
ChannelName string
|
||||
GroupID string
|
||||
GroupName string
|
||||
}
|
||||
|
||||
// PlatformAdapter is the contract every cross-platform IM adapter implements.
|
||||
// Implementations translate between a native platform SDK/protocol and the
|
||||
// normalized UnifiedMessage/UnifiedResponse model. The API is deliberately
|
||||
// narrow so it is trivial to mock and so a new platform plugs in without
|
||||
// touching consumers.
|
||||
//
|
||||
// All I/O operations accept a context.Context per the project constraint and
|
||||
// should return promptly when the context is canceled.
|
||||
type PlatformAdapter interface {
|
||||
// Name returns the platform identifier (for example "telegram").
|
||||
Name() string
|
||||
// Config returns a snapshot of the adapter's current configuration as a
|
||||
// plain map, so callers can inspect or persist it without importing the
|
||||
// concrete type. Use DecodeConfig to read it into a typed struct.
|
||||
Config() map[string]any
|
||||
|
||||
// Connect establishes the platform session. It is idempotent.
|
||||
Connect(ctx context.Context) error
|
||||
// Disconnect tears down the platform session. It is idempotent.
|
||||
Disconnect(ctx context.Context) error
|
||||
// IsConnected reports whether the session is currently live.
|
||||
IsConnected() bool
|
||||
// HealthCheck verifies the session is usable, returning an error when not.
|
||||
HealthCheck(ctx context.Context) error
|
||||
|
||||
// ReadMessages runs a blocking read loop, emitting each normalized inbound
|
||||
// message to emit until ctx is canceled. Adapters that receive push
|
||||
// notifications should instead deliver through SetMessageHandler.
|
||||
ReadMessages(ctx context.Context, emit func(UnifiedMessage))
|
||||
|
||||
// SendMessage delivers a reply (potentially several messages) to the channel.
|
||||
SendMessage(ctx context.Context, resp UnifiedResponse) error
|
||||
// SendProactive pushes a message that was not triggered by a recent inbound
|
||||
// message. Whether that is permitted is governed by Capabilities.Proactive
|
||||
// and platform-specific limits (for example the weixin-ilink 24h window).
|
||||
SendProactive(ctx context.Context, msg UnifiedMessage) error
|
||||
// SetMessageHandler installs the callbacks receiving push-style inbound
|
||||
// messages. A nil fn clears the handler.
|
||||
SetMessageHandler(fn MessageHandler)
|
||||
|
||||
// Capabilities declares what the adapter supports.
|
||||
Capabilities() Capabilities
|
||||
// ListChannels returns the channels currently known to the adapter.
|
||||
ListChannels(ctx context.Context) ([]ChannelInfo, error)
|
||||
}
|
||||
|
||||
// Sentinel errors returned by adapters. Use errors.Is to test for them.
|
||||
var (
|
||||
// ErrNotConnected is returned when an operation requires a live session.
|
||||
ErrNotConnected = errors.New("adapter: not connected")
|
||||
// ErrUnsupported is returned when a requested capability is not available
|
||||
// (for example proactive push on a channel that does not allow it).
|
||||
ErrUnsupported = errors.New("adapter: operation not supported")
|
||||
// ErrClosed is returned when an operation is attempted on a closed adapter.
|
||||
ErrClosed = errors.New("adapter: closed")
|
||||
)
|
||||
|
||||
// DecodeConfig decodes a raw config map produced by Config into the struct
|
||||
// pointed to by v. It is a convenience for adapters and tests that model their
|
||||
// configuration as strongly typed structs. Unknown keys are ignored; missing
|
||||
// keys leave the corresponding zero value.
|
||||
func DecodeConfig(raw map[string]any, v any) error {
|
||||
if raw == nil {
|
||||
return errors.New("adapter: nil config map")
|
||||
}
|
||||
b, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adapter: encode config: %w", err)
|
||||
}
|
||||
if err := json.Unmarshal(b, v); err != nil {
|
||||
return fmt.Errorf("adapter: decode config into %T: %w", v, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user