bcclient

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 23, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

README

babelconnect-sdk-go

The Go SDK for babelconnect-server — a server-authoritative "dumb renderer" client: it mirrors the agent's state and exposes typed intents over gRPC, with a pluggable WebRTC media leg.

go get github.com/babelforce/babelconnect-sdk-go

Module github.com/babelforce/babelconnect-sdk-go, package bcclient.

What it does

babelconnect-server is state-authoritative: it owns the per-agent AgentView and streams a snapshot + entity-level patches over the gRPC Agent.Session. This SDK:

  • opens the session and keeps a StateCache mirror of the AgentView, applying snapshot/patches mechanically — no domain logic, so your UI is a pure function of the state;
  • exposes typed intent senders (PlaceCall, Answer, Hangup, Mute, Hold, SendDigits, SetDisplayAs, SetPresence, Transfer, the conference intents, and SMS SendSms/SetConversationOpen/MarkConversationRead) plus the unary fetches GetHistory/GetSmsThread/GetPhonebook;
  • drives a pluggable Media leg (WebRTC). The default SyntheticMediaFactory is cgo-free — it answers in PCMA, streams A-law silence, and counts inbound RTP (two-way media is verifiable, but nothing is heard). Supply your own Media for real mic/speaker audio — no control/state code changes.

Your UI binds to Cache().Subscribe(...) and dispatches intents. That's the whole contract.

Usage

// PasswordGrant is a convenience for local tools/tests; in production, bring your own token.
token, _ := bcclient.PasswordGrant(ctx, "https://login.example.com", user, pass)

cli, _ := bcclient.Dial(ctx, bcclient.Options{
    Addr: "agent.example.com:7090", Token: token, AutoAnswer: true,
})
defer cli.Close()

cli.Subscribe(func(v *bcv1.AgentView) { render(v) }) // UI = f(state)
cli.Register("webrtc")
cli.PlaceCall("+1990001000", "+1990002000", "+1990003000")

AutoAnswer applies to outbound only; an inbound call rings until you Answer (accept) or Hangup (reject).

License

Apache-2.0.

Documentation

Overview

Package bcclient is the Go SDK for babelconnect-server — the reference implementation of a babelconnect "dumb renderer" client. It opens the Agent.Session stream, keeps a local StateCache mirror of the server's AgentView (applying snapshot + patches mechanically), exposes typed intent senders (PlaceCall/Answer/Hangup/Mute/...), and drives a pluggable WebRTC Media leg. UI code binds to the cache and dispatches intents — no call/agent state logic lives in the client. It is the seed of babelconnect-sdk-{ts,dart}.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PasswordGrant

func PasswordGrant(ctx context.Context, oauthBase, user, pass string) (string, error)

PasswordGrant exchanges an agent email/password for a bearer token via an OAuth password-grant endpoint. It's a convenience for local tools and tests; production clients obtain a token through their own login and pass it as Options.Token.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a live babelconnect session.

func Dial

func Dial(ctx context.Context, opts Options) (*Client, error)

Dial opens the session and starts mirroring server state. The returned Client is ready to Register and place/answer calls.

func (*Client) ActiveCall

func (c *Client) ActiveCall() *bcv1.CallState

ActiveCall returns the first call currently in the view, or nil.

func (*Client) AddConferenceMember

func (c *Client) AddConferenceMember(agentID, number string) error

AddConferenceMember invites a participant — exactly one of agentID / number.

func (*Client) Answer

func (c *Client) Answer(callID string) error

Answer manually answers a RINGING call by id (no-op under AutoAnswer once the call has already been answered).

func (*Client) Cache

func (c *Client) Cache() *StateCache

Cache exposes the state mirror (View / Subscribe).

func (*Client) Close

func (c *Client) Close() error

Close tears down all media legs and the connection.

func (*Client) EndConference

func (c *Client) EndConference() error

EndConference tears down the whole conference (moderator only).

func (*Client) FlagRecording

func (c *Client) FlagRecording(callID string) error

FlagRecording toggles the flagged mark on the call's recording.

func (*Client) GetHistory

func (c *Client) GetHistory(ctx context.Context, max, page int32) ([]*bcv1.CallRecord, error)

GetHistory fetches a page of the agent's recent calls (newest first) via the unary Agent.GetHistory RPC — the on-demand reference data behind the History view. It mirrors the Dart SDK's getHistory. The bearer token (held in opts) rides as request metadata, since unary calls don't inherit the stream context.

func (*Client) GetPhonebook

func (c *Client) GetPhonebook(ctx context.Context, max, page int32, query string) ([]*bcv1.PhonebookEntry, error)

GetPhonebook re-pulls the agent's merged contacts + recent numbers (the Contacts tab) via the unary Agent.GetPhonebook RPC — an on-demand refresh of the register-time AgentInfo.phonebook snapshot. query filters by label/number. Mirrors the Dart SDK's getPhonebook.

func (*Client) GetSmsThread

func (c *Client) GetSmsThread(ctx context.Context, conversationID string, max, page int32) ([]*bcv1.SmsMessage, error)

GetSmsThread fetches the messages of one SMS conversation (the chat thread), oldest first, via the unary Agent.GetSmsThread RPC — on-demand reference data like GetHistory. Mirrors the Dart SDK's getSmsThread.

func (*Client) Hangup

func (c *Client) Hangup(callID string) error

func (*Client) Hold

func (c *Client) Hold(callID string, on bool) error

func (*Client) HoldConferenceMember

func (c *Client) HoldConferenceMember(memberID string, on bool) error

HoldConferenceMember holds/unholds an individual member (moderator only).

func (*Client) KickConferenceMember

func (c *Client) KickConferenceMember(memberID string) error

KickConferenceMember removes a member (moderator only).

func (*Client) LeaveConference

func (c *Client) LeaveConference() error

LeaveConference hangs up only the agent's own leg.

func (*Client) MarkConversationRead

func (c *Client) MarkConversationRead(conversationID string) error

MarkConversationRead clears the unread count on an SMS conversation.

func (*Client) Mute

func (c *Client) Mute(callID string, on bool) error

func (*Client) MuteConferenceMember

func (c *Client) MuteConferenceMember(memberID string, on bool) error

MuteConferenceMember mutes/unmutes an individual member (moderator only).

func (*Client) PlaceCall

func (c *Client) PlaceCall(to, displayAsTo, displayAsFrom string, record bool) error

func (*Client) Register

func (c *Client) Register(capabilities ...string) error

func (*Client) ResetLineStatus

func (c *Client) ResetLineStatus() error

ResetLineStatus clears a blocked line (busy/unreachable/declined).

func (*Client) SendDigits

func (c *Client) SendDigits(callID, digits string) error

func (*Client) SendSms

func (c *Client) SendSms(to, text, from string, session map[string]string) error

SendSms sends an SMS. `from` may be empty (the server picks the agent's default SMS number); `session` carries opaque CTI/embedding correlation keys. The sent conversation flows back via state (sms_upsert). Equivalent to the SendSms RPC.

func (*Client) SetAgentNumber

func (c *Client) SetAgentNumber(number string) error

SetAgentNumber sets the external phone number the agent's calls bridge to.

func (*Client) SetConversationOpen

func (c *Client) SetConversationOpen(conversationID string, open bool) error

SetConversationOpen opens (reopens) or closes (resolves) an SMS conversation. The server flips SmsConversation.open optimistically and re-confirms via state.

func (*Client) SetDisplayAs

func (c *Client) SetDisplayAs(number string) error

func (*Client) SetPresence

func (c *Client) SetPresence(name string) error

SetPresence switches the agent's presence (the selector): "available" or a configured pause reason (see AgentInfo.PresenceOptions).

func (*Client) SetRecordingTags

func (c *Client) SetRecordingTags(callID string, tags []string) error

SetRecordingTags replaces the tags on the call's recording.

func (*Client) SetWebrtc

func (c *Client) SetWebrtc(on bool) error

SetWebrtc enables/disables the browser phone (WebRTC).

func (*Client) StartConference

func (c *Client) StartConference(hold bool) error

StartConference opens a conference around the active call (`hold` holds the current party while the first member is dialed).

func (*Client) StartRecording

func (c *Client) StartRecording(callID string) error

StartRecording begins recording the call.

func (*Client) Stats

func (c *Client) Stats() (sent, received int64)

Stats sums RTP packets sent/received across all live media legs.

func (*Client) StopRecording

func (c *Client) StopRecording(callID string) error

StopRecording ends the call's recording.

func (*Client) Subscribe

func (c *Client) Subscribe(fn func(*bcv1.AgentView))

Subscribe registers a render callback (fired on every state update).

func (*Client) Transfer

func (c *Client) Transfer(callID, to, agentID, applicationID string, warm bool) error

Transfer hands the active call to a target — exactly one of `to` (number), agentID, or applicationID. `warm` selects attended (conference) vs blind/cold.

func (*Client) View

func (c *Client) View() *bcv1.AgentView

View is a convenience for c.Cache().View().

func (*Client) WrapUpCancel

func (c *Client) WrapUpCancel() error

WrapUpCancel ends after-call-work early.

func (*Client) WrapUpExtend

func (c *Client) WrapUpExtend(seconds int32) error

WrapUpExtend adds `seconds` to the after-call-work countdown.

type Media

type Media interface {
	// Answer consumes the server's SDP offer and returns the client's SDP answer,
	// starting audio. Called once per call.
	Answer(ctx context.Context, offer string) (answer string, err error)
	// Stats reports RTP packets sent/received so far (for headless verification).
	Stats() (sent, received int64)
	// Close tears down the peer (idempotent).
	Close() error
}

Media is the client's WebRTC leg for one call. The SDK is media-agnostic: it hands the server's SDP offer to a Media, gets back an answer, and lets the Media own audio. This is the seam where a real client swaps in mic/speaker (flutter_webrtc on the apps, or a malgo/portaudio backend on Go desktop) without touching the control/state code.

func SyntheticMediaFactory

func SyntheticMediaFactory(callID string) (Media, error)

SyntheticMediaFactory is the default, cgo-free backend used by headless tools and the local client until real audio is wired: it answers in PCMA, streams A-law silence outbound, and counts inbound RTP (so two-way media is still verifiable against the echoserver). No microphone or speaker is touched.

type MediaFactory

type MediaFactory func(callID string) (Media, error)

MediaFactory builds the Media for a given call id.

type Options

type Options struct {
	Addr  string // babelconnect-server gRPC address, e.g. "agent.example.com:7090"
	Token string // bearer token (see PasswordGrant)

	// Media builds the per-call WebRTC leg. Defaults to SyntheticMediaFactory
	// (cgo-free silence + RTP counting) when nil.
	Media MediaFactory

	// AutoAnswer answers RINGING calls automatically — the right behaviour for
	// the outbound dialer (the agent answers their own leg). Set false to drive
	// answering manually via Answer(callID).
	AutoAnswer bool

	// OnError surfaces out-of-band server notices (command rejections). Optional.
	OnError func(*bcv1.Error)
	// OnGap is called when a state-update seq gap is detected (the SDK keeps
	// applying, but a real client should reconnect for a fresh snapshot). Optional.
	OnGap func()
}

Options configures a Client.

type StateCache

type StateCache struct {
	// contains filtered or unexported fields
}

StateCache is the client-side mirror of the server-authoritative AgentView. It applies StateUpdate snapshots and entity-level patches mechanically — there is NO domain logic here, by design: the server reduces, the client renders. This is the same reducer the server runs, in reverse, and is meant to be identical (and trivial) across the go/ts/dart SDKs so they cannot drift.

func (*StateCache) Apply

func (sc *StateCache) Apply(u *bcv1.StateUpdate) (gap bool)

Apply folds one snapshot or patch into the view and notifies listeners. It returns true if a seq gap was detected (a patch whose seq is not exactly the previous seq + 1), which means the caller should resubscribe for a fresh snapshot. Error updates are handled by the client, not here.

func (*StateCache) Subscribe

func (sc *StateCache) Subscribe(fn func(*bcv1.AgentView))

Subscribe registers a render callback, invoked with a fresh view copy on every update (and once immediately with the current view).

func (*StateCache) View

func (sc *StateCache) View() *bcv1.AgentView

View returns a deep copy of the current view (safe to read/hold).

Jump to

Keyboard shortcuts

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