mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
e10b1e1fd4
Add outbound media sending capability so the agent can publish media attachments (images, files, audio, video) through channels via the bus. - Add MediaPart and OutboundMediaMessage types to bus - Add PublishOutboundMedia/SubscribeOutboundMedia bus methods - Add MediaSender interface discovered via type assertion by Manager - Add media dispatch/worker in Manager with shared retry logic - Extend ToolResult with Media field and MediaResult constructor - Publish outbound media from agent loop on tool results - Implement SendMedia for Telegram, Discord, Slack, LINE, OneBot, WeCom
109 lines
2.3 KiB
Go
109 lines
2.3 KiB
Go
package bus
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync/atomic"
|
|
)
|
|
|
|
// ErrBusClosed is returned when publishing to a closed MessageBus.
|
|
var ErrBusClosed = errors.New("message bus closed")
|
|
|
|
type MessageBus struct {
|
|
inbound chan InboundMessage
|
|
outbound chan OutboundMessage
|
|
outboundMedia chan OutboundMediaMessage
|
|
done chan struct{}
|
|
closed atomic.Bool
|
|
}
|
|
|
|
func NewMessageBus() *MessageBus {
|
|
return &MessageBus{
|
|
inbound: make(chan InboundMessage, 100),
|
|
outbound: make(chan OutboundMessage, 100),
|
|
outboundMedia: make(chan OutboundMediaMessage, 100),
|
|
done: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
func (mb *MessageBus) PublishInbound(ctx context.Context, msg InboundMessage) error {
|
|
if mb.closed.Load() {
|
|
return ErrBusClosed
|
|
}
|
|
select {
|
|
case mb.inbound <- msg:
|
|
return nil
|
|
case <-mb.done:
|
|
return ErrBusClosed
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
|
|
func (mb *MessageBus) ConsumeInbound(ctx context.Context) (InboundMessage, bool) {
|
|
select {
|
|
case msg, ok := <-mb.inbound:
|
|
return msg, ok
|
|
case <-mb.done:
|
|
return InboundMessage{}, false
|
|
case <-ctx.Done():
|
|
return InboundMessage{}, false
|
|
}
|
|
}
|
|
|
|
func (mb *MessageBus) PublishOutbound(ctx context.Context, msg OutboundMessage) error {
|
|
if mb.closed.Load() {
|
|
return ErrBusClosed
|
|
}
|
|
select {
|
|
case mb.outbound <- msg:
|
|
return nil
|
|
case <-mb.done:
|
|
return ErrBusClosed
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
|
|
func (mb *MessageBus) SubscribeOutbound(ctx context.Context) (OutboundMessage, bool) {
|
|
select {
|
|
case msg, ok := <-mb.outbound:
|
|
return msg, ok
|
|
case <-mb.done:
|
|
return OutboundMessage{}, false
|
|
case <-ctx.Done():
|
|
return OutboundMessage{}, false
|
|
}
|
|
}
|
|
|
|
func (mb *MessageBus) PublishOutboundMedia(ctx context.Context, msg OutboundMediaMessage) error {
|
|
if mb.closed.Load() {
|
|
return ErrBusClosed
|
|
}
|
|
select {
|
|
case mb.outboundMedia <- msg:
|
|
return nil
|
|
case <-mb.done:
|
|
return ErrBusClosed
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
|
|
func (mb *MessageBus) SubscribeOutboundMedia(ctx context.Context) (OutboundMediaMessage, bool) {
|
|
select {
|
|
case msg, ok := <-mb.outboundMedia:
|
|
return msg, ok
|
|
case <-mb.done:
|
|
return OutboundMediaMessage{}, false
|
|
case <-ctx.Done():
|
|
return OutboundMediaMessage{}, false
|
|
}
|
|
}
|
|
|
|
func (mb *MessageBus) Close() {
|
|
if mb.closed.CompareAndSwap(false, true) {
|
|
close(mb.done)
|
|
}
|
|
}
|