Skip to content

Channels Go API ​

The channels package provides the adapter interface, message types, and channel manager for multi-platform messaging.

Package: github.com/openparallax/openparallax/internal/channels

ChannelAdapter Interface ​

go
type ChannelAdapter interface {
    Name() string
    Start(ctx context.Context) error
    Stop() error
    SendMessage(chatID string, message *ChannelMessage) error
    IsConfigured() bool
}

Name ​

go
func (a *Adapter) Name() string

Returns the adapter identifier: "telegram", "whatsapp", "discord", "slack", "signal", or "teams".

Start ​

go
func (a *Adapter) Start(ctx context.Context) error

Begins listening for incoming messages. This method blocks until ctx is canceled or a fatal error occurs. On Telegram, this runs a long-polling loop. On WhatsApp, this starts a webhook HTTP server. On Discord, this opens a WebSocket connection to the Gateway.

Stop ​

go
func (a *Adapter) Stop() error

Gracefully shuts down the adapter. Closes connections, stops HTTP servers, and releases resources.

SendMessage ​

go
func (a *Adapter) SendMessage(chatID string, message *ChannelMessage) error

Sends a response back to the platform. The chatID is platform-specific: a phone number for WhatsApp/Signal, a chat ID for Telegram, a channel ID for Discord.

IsConfigured ​

go
func (a *Adapter) IsConfigured() bool

Returns true if the adapter has valid configuration (API tokens present, required fields set). The Manager only registers adapters that are configured.

ChannelMessage ​

go
type ChannelMessage struct {
    Text        string
    Attachments []ChannelAttachment
    ReplyToID   string
    Format      MessageFormat
}
FieldDescription
TextThe message text content
AttachmentsFiles or images to include
ReplyToIDPlatform-specific message ID for threading
FormatHow the text should be rendered: FormatPlain, FormatMarkdown, or FormatHTML

ChannelAttachment ​

go
type ChannelAttachment struct {
    Filename string
    Path     string
    MimeType string
}
FieldDescription
FilenameDisplay name of the file
PathLocal filesystem path to the file
MimeTypeMIME type of the file

MessageFormat ​

go
const (
    FormatPlain    MessageFormat = iota  // Plain text
    FormatMarkdown                       // Markdown
    FormatHTML                           // HTML
)

Manager ​

go
type Manager struct { ... }

Manages adapter lifecycle and routes messages to the engine pipeline.

NewManager ​

go
func NewManager(eng *engine.Engine, log *logging.Logger) *Manager

Creates a channel manager connected to the engine.

Register ​

go
func (m *Manager) Register(adapter ChannelAdapter)

Adds an adapter to the manager. Only registers adapters where IsConfigured() returns true. Logs a warning for unconfigured adapters.

StartAll ​

go
func (m *Manager) StartAll(ctx context.Context)

Starts all registered adapters in separate goroutines. Each adapter runs with retry logic: up to 5 attempts with 30-second delays between retries. If an adapter fails all retry attempts, it is logged and abandoned (other adapters continue running).

StopAll ​

go
func (m *Manager) StopAll()

Gracefully stops all registered adapters.

HandleMessage ​

go
func (m *Manager) HandleMessage(ctx context.Context, adapterName, chatID, content string, mode types.SessionMode) (string, error)

Routes an incoming message to the engine pipeline and returns the full response text. This is the primary method called by adapters when they receive a message.

The method:

  1. Gets or creates a session for the adapterName:chatID pair
  2. Generates a unique message ID
  3. Creates a responseCollector (an EventSender that aggregates streaming events)
  4. Calls engine.ProcessMessageForWeb() with the collector
  5. Returns the complete response text

ResetSession ​

go
func (m *Manager) ResetSession(adapterName, chatID string)

Creates a new session for a chat. Called when the user sends /new on any platform.

AdapterCount ​

go
func (m *Manager) AdapterCount() int

Returns the number of registered adapters.

SplitMessage ​

go
func SplitMessage(text string, maxLen int) []string

Splits a long message at natural boundaries (paragraph breaks, then word boundaries) to respect platform message length limits. If the text is within maxLen, it is returned as a single-element slice.

go
parts := channels.SplitMessage(response, 2000) // Discord limit
for _, part := range parts {
    _ = adapter.SendMessage(chatID, &channels.ChannelMessage{Text: part})
}

MaxMessageLen ​

go
func MaxMessageLen(platform string) int

Returns the maximum message length for a platform:

PlatformMax Length
"telegram"4,096
"whatsapp"4,096
"discord"2,000
default4,096

EventSender Implementation ​

The responseCollector is an internal EventSender that aggregates pipeline events into a response string:

go
type responseCollector struct {
    mu       sync.Mutex
    text     strings.Builder
    response string
    done     bool
}

func (r *responseCollector) SendEvent(event *engine.PipelineEvent) error {
    switch event.Type {
    case engine.EventLLMToken:
        r.text.WriteString(event.LLMToken.Text)
    case engine.EventResponseComplete:
        r.response = event.ResponseComplete.Content
        r.done = true
    }
    return nil
}

The collector accumulates streaming text tokens and extracts the final response from the response_complete event. The fullResponse() method returns the ResponseComplete content if available, falling back to the accumulated text.