2026-04-26 08:05:10 +00:00
|
|
|
package channels
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"github.com/sipeed/picoclaw/pkg/bus"
|
|
|
|
|
runtimeevents "github.com/sipeed/picoclaw/pkg/events"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func channelTypeForEvent(m *Manager, channelName string) string {
|
|
|
|
|
if m == nil || m.config == nil {
|
|
|
|
|
return channelName
|
|
|
|
|
}
|
|
|
|
|
if bc := m.config.Channels.Get(channelName); bc != nil && bc.Type != "" {
|
|
|
|
|
return bc.Type
|
|
|
|
|
}
|
|
|
|
|
return channelName
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Manager) publishChannelEvent(
|
|
|
|
|
kind runtimeevents.Kind,
|
|
|
|
|
channelName string,
|
|
|
|
|
scope runtimeevents.Scope,
|
|
|
|
|
severity runtimeevents.Severity,
|
|
|
|
|
payload any,
|
|
|
|
|
) {
|
|
|
|
|
if m == nil || m.runtimeEvents == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if scope.Channel == "" {
|
|
|
|
|
scope.Channel = channelName
|
|
|
|
|
}
|
2026-04-27 05:09:03 +00:00
|
|
|
m.runtimeEvents.PublishNonBlocking(runtimeevents.Event{
|
2026-04-26 08:05:10 +00:00
|
|
|
Kind: kind,
|
|
|
|
|
Source: runtimeevents.Source{Component: "channel", Name: channelName},
|
|
|
|
|
Scope: scope,
|
|
|
|
|
Severity: severity,
|
|
|
|
|
Payload: payload,
|
2026-04-27 05:09:03 +00:00
|
|
|
Attrs: channelEventAttrs(payload),
|
2026-04-26 08:05:10 +00:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 05:09:03 +00:00
|
|
|
func channelEventAttrs(payload any) map[string]any {
|
|
|
|
|
switch payload := payload.(type) {
|
|
|
|
|
case ChannelLifecyclePayload:
|
|
|
|
|
attrs := map[string]any{}
|
|
|
|
|
setAttrString(attrs, "type", payload.Type)
|
|
|
|
|
setAttrString(attrs, "error", payload.Error)
|
|
|
|
|
return attrs
|
|
|
|
|
case ChannelOutboundPayload:
|
|
|
|
|
attrs := map[string]any{}
|
|
|
|
|
if payload.Media {
|
|
|
|
|
attrs["media"] = payload.Media
|
|
|
|
|
}
|
|
|
|
|
if payload.ContentLen > 0 {
|
|
|
|
|
attrs["content_len"] = payload.ContentLen
|
|
|
|
|
}
|
|
|
|
|
if len(payload.MessageIDs) > 0 {
|
|
|
|
|
attrs["message_ids_count"] = len(payload.MessageIDs)
|
|
|
|
|
}
|
|
|
|
|
setAttrString(attrs, "reply_to_message_id", payload.ReplyToMessageID)
|
|
|
|
|
setAttrString(attrs, "error", payload.Error)
|
|
|
|
|
if payload.Retries > 0 {
|
|
|
|
|
attrs["retries"] = payload.Retries
|
|
|
|
|
}
|
|
|
|
|
return attrs
|
|
|
|
|
default:
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func setAttrString(attrs map[string]any, key, value string) {
|
|
|
|
|
if value != "" {
|
|
|
|
|
attrs[key] = value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-26 08:05:10 +00:00
|
|
|
func (m *Manager) publishOutboundSent(
|
|
|
|
|
channelName string,
|
|
|
|
|
msg bus.OutboundMessage,
|
|
|
|
|
messageIDs []string,
|
|
|
|
|
) {
|
|
|
|
|
m.publishChannelEvent(
|
|
|
|
|
runtimeevents.KindChannelMessageOutboundSent,
|
|
|
|
|
channelName,
|
|
|
|
|
scopeFromOutboundContext(msg.Context),
|
|
|
|
|
runtimeevents.SeverityInfo,
|
|
|
|
|
ChannelOutboundPayload{
|
|
|
|
|
ContentLen: len([]rune(msg.Content)),
|
|
|
|
|
MessageIDs: append([]string(nil), messageIDs...),
|
|
|
|
|
ReplyToMessageID: msg.ReplyToMessageID,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Manager) publishOutboundQueued(
|
|
|
|
|
channelName string,
|
|
|
|
|
msg bus.OutboundMessage,
|
|
|
|
|
) {
|
|
|
|
|
m.publishChannelEvent(
|
|
|
|
|
runtimeevents.KindChannelMessageOutboundQueued,
|
|
|
|
|
channelName,
|
|
|
|
|
scopeFromOutboundContext(msg.Context),
|
|
|
|
|
runtimeevents.SeverityInfo,
|
|
|
|
|
ChannelOutboundPayload{
|
|
|
|
|
ContentLen: len([]rune(msg.Content)),
|
|
|
|
|
ReplyToMessageID: msg.ReplyToMessageID,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Manager) publishOutboundFailed(
|
|
|
|
|
channelName string,
|
|
|
|
|
msg bus.OutboundMessage,
|
|
|
|
|
err error,
|
|
|
|
|
media bool,
|
|
|
|
|
) {
|
|
|
|
|
payload := ChannelOutboundPayload{
|
|
|
|
|
Media: media,
|
|
|
|
|
ContentLen: len([]rune(msg.Content)),
|
|
|
|
|
ReplyToMessageID: msg.ReplyToMessageID,
|
|
|
|
|
Retries: maxRetries,
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
payload.Error = err.Error()
|
|
|
|
|
}
|
|
|
|
|
m.publishChannelEvent(
|
|
|
|
|
runtimeevents.KindChannelMessageOutboundFailed,
|
|
|
|
|
channelName,
|
|
|
|
|
scopeFromOutboundContext(msg.Context),
|
|
|
|
|
runtimeevents.SeverityError,
|
|
|
|
|
payload,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Manager) publishOutboundMediaSent(
|
|
|
|
|
channelName string,
|
|
|
|
|
msg bus.OutboundMediaMessage,
|
|
|
|
|
messageIDs []string,
|
|
|
|
|
) {
|
|
|
|
|
m.publishChannelEvent(
|
|
|
|
|
runtimeevents.KindChannelMessageOutboundSent,
|
|
|
|
|
channelName,
|
|
|
|
|
scopeFromOutboundContext(msg.Context),
|
|
|
|
|
runtimeevents.SeverityInfo,
|
|
|
|
|
ChannelOutboundPayload{
|
|
|
|
|
Media: true,
|
|
|
|
|
MessageIDs: append([]string(nil), messageIDs...),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Manager) publishOutboundMediaQueued(
|
|
|
|
|
channelName string,
|
|
|
|
|
msg bus.OutboundMediaMessage,
|
|
|
|
|
) {
|
|
|
|
|
m.publishChannelEvent(
|
|
|
|
|
runtimeevents.KindChannelMessageOutboundQueued,
|
|
|
|
|
channelName,
|
|
|
|
|
scopeFromOutboundContext(msg.Context),
|
|
|
|
|
runtimeevents.SeverityInfo,
|
|
|
|
|
ChannelOutboundPayload{Media: true},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Manager) publishOutboundMediaFailed(
|
|
|
|
|
channelName string,
|
|
|
|
|
msg bus.OutboundMediaMessage,
|
|
|
|
|
err error,
|
|
|
|
|
) {
|
|
|
|
|
payload := ChannelOutboundPayload{
|
|
|
|
|
Media: true,
|
|
|
|
|
Retries: maxRetries,
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
payload.Error = err.Error()
|
|
|
|
|
}
|
|
|
|
|
m.publishChannelEvent(
|
|
|
|
|
runtimeevents.KindChannelMessageOutboundFailed,
|
|
|
|
|
channelName,
|
|
|
|
|
scopeFromOutboundContext(msg.Context),
|
|
|
|
|
runtimeevents.SeverityError,
|
|
|
|
|
payload,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func scopeFromOutboundContext(ctx bus.InboundContext) runtimeevents.Scope {
|
|
|
|
|
return runtimeevents.Scope{
|
|
|
|
|
Channel: ctx.Channel,
|
|
|
|
|
Account: ctx.Account,
|
|
|
|
|
ChatID: ctx.ChatID,
|
|
|
|
|
TopicID: ctx.TopicID,
|
|
|
|
|
SpaceID: ctx.SpaceID,
|
|
|
|
|
SpaceType: ctx.SpaceType,
|
|
|
|
|
ChatType: ctx.ChatType,
|
|
|
|
|
SenderID: ctx.SenderID,
|
|
|
|
|
MessageID: ctx.MessageID,
|
|
|
|
|
}
|
|
|
|
|
}
|