ScuttleBot

scuttlebot / pkg / sessionrelay / sessionrelay.go
Source Blame History 149 lines
24a217e… lmata 1 package sessionrelay
24a217e… lmata 2
24a217e… lmata 3 import (
24a217e… lmata 4 "context"
f3c383e… noreply 5 "encoding/json"
24a217e… lmata 6 "fmt"
24a217e… lmata 7 "net/http"
24a217e… lmata 8 "strings"
24a217e… lmata 9 "time"
24a217e… lmata 10 )
24a217e… lmata 11
24a217e… lmata 12 const (
24a217e… lmata 13 defaultRequestTimeout = 3 * time.Second
24a217e… lmata 14 defaultBufferSize = 512
24a217e… lmata 15 )
24a217e… lmata 16
24a217e… lmata 17 type Transport string
24a217e… lmata 18
24a217e… lmata 19 const (
24a217e… lmata 20 TransportHTTP Transport = "http"
24a217e… lmata 21 TransportIRC Transport = "irc"
24a217e… lmata 22 )
24a217e… lmata 23
24a217e… lmata 24 type Config struct {
24a217e… lmata 25 Transport Transport
24a217e… lmata 26 URL string
24a217e… lmata 27 Token string
24a217e… lmata 28 Channel string
1d3caa2… lmata 29 Channels []string
24a217e… lmata 30 Nick string
24a217e… lmata 31 HTTPClient *http.Client
24a217e… lmata 32 IRC IRCConfig
24a217e… lmata 33 }
24a217e… lmata 34
24a217e… lmata 35 type IRCConfig struct {
24a217e… lmata 36 Addr string
24a217e… lmata 37 Pass string
24a217e… lmata 38 AgentType string
24a217e… lmata 39 DeleteOnClose bool
a027855… noreply 40 // EnvelopeMode wraps outgoing messages in protocol.Envelope JSON.
a027855… noreply 41 // When true, agents in the channel can parse relay output as structured data.
a027855… noreply 42 EnvelopeMode bool
24a217e… lmata 43 }
24a217e… lmata 44
24a217e… lmata 45 type Message struct {
1d3caa2… lmata 46 At time.Time
1d3caa2… lmata 47 Channel string
1d3caa2… lmata 48 Nick string
1d3caa2… lmata 49 Text string
f64fe5f… noreply 50 MsgID string
24a217e… lmata 51 }
24a217e… lmata 52
24a217e… lmata 53 type Connector interface {
24a217e… lmata 54 Connect(ctx context.Context) error
24a217e… lmata 55 Post(ctx context.Context, text string) error
1d3caa2… lmata 56 PostTo(ctx context.Context, channel, text string) error
f3c383e… noreply 57 PostWithMeta(ctx context.Context, text string, meta json.RawMessage) error
f3c383e… noreply 58 PostToWithMeta(ctx context.Context, channel, text string, meta json.RawMessage) error
24a217e… lmata 59 MessagesSince(ctx context.Context, since time.Time) ([]Message, error)
24a217e… lmata 60 Touch(ctx context.Context) error
1d3caa2… lmata 61 JoinChannel(ctx context.Context, channel string) error
1d3caa2… lmata 62 PartChannel(ctx context.Context, channel string) error
1d3caa2… lmata 63 Channels() []string
1d3caa2… lmata 64 ControlChannel() string
24a217e… lmata 65 Close(ctx context.Context) error
24a217e… lmata 66 }
24a217e… lmata 67
24a217e… lmata 68 func New(cfg Config) (Connector, error) {
24a217e… lmata 69 cfg = withDefaults(cfg)
24a217e… lmata 70 if err := validateBaseConfig(cfg); err != nil {
24a217e… lmata 71 return nil, err
24a217e… lmata 72 }
24a217e… lmata 73
24a217e… lmata 74 switch cfg.Transport {
24a217e… lmata 75 case TransportHTTP:
24a217e… lmata 76 return newHTTPConnector(cfg), nil
24a217e… lmata 77 case TransportIRC:
24a217e… lmata 78 return newIRCConnector(cfg)
24a217e… lmata 79 default:
24a217e… lmata 80 return nil, fmt.Errorf("sessionrelay: unsupported transport %q", cfg.Transport)
24a217e… lmata 81 }
24a217e… lmata 82 }
24a217e… lmata 83
24a217e… lmata 84 func withDefaults(cfg Config) Config {
24a217e… lmata 85 if cfg.Transport == "" {
24a217e… lmata 86 cfg.Transport = TransportHTTP
24a217e… lmata 87 }
24a217e… lmata 88 if cfg.HTTPClient == nil {
24a217e… lmata 89 cfg.HTTPClient = &http.Client{Timeout: defaultRequestTimeout}
24a217e… lmata 90 }
24a217e… lmata 91 if cfg.IRC.AgentType == "" {
24a217e… lmata 92 cfg.IRC.AgentType = "worker"
24a217e… lmata 93 }
24a217e… lmata 94 cfg.Channel = normalizeChannel(cfg.Channel)
1d3caa2… lmata 95 cfg.Channels = normalizeChannels(cfg.Channel, cfg.Channels)
1d3caa2… lmata 96 if cfg.Channel == "" && len(cfg.Channels) > 0 {
1d3caa2… lmata 97 cfg.Channel = cfg.Channels[0]
1d3caa2… lmata 98 }
24a217e… lmata 99 cfg.Transport = Transport(strings.ToLower(string(cfg.Transport)))
24a217e… lmata 100 return cfg
24a217e… lmata 101 }
24a217e… lmata 102
24a217e… lmata 103 func validateBaseConfig(cfg Config) error {
1d3caa2… lmata 104 if cfg.Channel == "" || len(cfg.Channels) == 0 {
24a217e… lmata 105 return fmt.Errorf("sessionrelay: channel is required")
24a217e… lmata 106 }
24a217e… lmata 107 if cfg.Nick == "" {
24a217e… lmata 108 return fmt.Errorf("sessionrelay: nick is required")
24a217e… lmata 109 }
24a217e… lmata 110 return nil
24a217e… lmata 111 }
24a217e… lmata 112
24a217e… lmata 113 func normalizeChannel(channel string) string {
24a217e… lmata 114 channel = strings.TrimSpace(channel)
24a217e… lmata 115 if channel == "" {
24a217e… lmata 116 return ""
24a217e… lmata 117 }
24a217e… lmata 118 if strings.HasPrefix(channel, "#") {
24a217e… lmata 119 return channel
24a217e… lmata 120 }
24a217e… lmata 121 return "#" + channel
24a217e… lmata 122 }
24a217e… lmata 123
24a217e… lmata 124 func channelSlug(channel string) string {
24a217e… lmata 125 return strings.TrimPrefix(normalizeChannel(channel), "#")
1d3caa2… lmata 126 }
1d3caa2… lmata 127
1d3caa2… lmata 128 func normalizeChannels(primary string, channels []string) []string {
1d3caa2… lmata 129 seen := make(map[string]struct{}, len(channels)+1)
1d3caa2… lmata 130 out := make([]string, 0, len(channels)+1)
1d3caa2… lmata 131
1d3caa2… lmata 132 add := func(channel string) {
1d3caa2… lmata 133 channel = normalizeChannel(channel)
1d3caa2… lmata 134 if channel == "" {
1d3caa2… lmata 135 return
1d3caa2… lmata 136 }
1d3caa2… lmata 137 if _, ok := seen[channel]; ok {
1d3caa2… lmata 138 return
1d3caa2… lmata 139 }
1d3caa2… lmata 140 seen[channel] = struct{}{}
1d3caa2… lmata 141 out = append(out, channel)
1d3caa2… lmata 142 }
1d3caa2… lmata 143
1d3caa2… lmata 144 add(primary)
1d3caa2… lmata 145 for _, channel := range channels {
1d3caa2… lmata 146 add(channel)
1d3caa2… lmata 147 }
1d3caa2… lmata 148 return out
24a217e… lmata 149 }

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button