diff --git a/example/webui/agentpool.go b/example/webui/agentpool.go index 847f04e..083c82e 100644 --- a/example/webui/agentpool.go +++ b/example/webui/agentpool.go @@ -120,6 +120,7 @@ func (a *AgentPool) List() []string { const ( ConnectorTelegram = "telegram" + ConnectorSlack = "slack" ActionSearch = "search" ) @@ -153,7 +154,7 @@ type Connector interface { Start(a *Agent) } -var AvailableConnectors = []string{ConnectorTelegram} +var AvailableConnectors = []string{ConnectorTelegram, ConnectorSlack} func (a *AgentConfig) availableConnectors() []Connector { connectors := []Connector{} @@ -177,6 +178,8 @@ func (a *AgentConfig) availableConnectors() []Connector { } connectors = append(connectors, cc) + case ConnectorSlack: + connectors = append(connectors, connector.NewSlack(config)) } } return connectors diff --git a/example/webui/connector/slack.go b/example/webui/connector/slack.go new file mode 100644 index 0000000..2f08b35 --- /dev/null +++ b/example/webui/connector/slack.go @@ -0,0 +1,132 @@ +package connector + +import ( + "fmt" + "log" + "os" + + "github.com/mudler/local-agent-framework/agent" + + "github.com/slack-go/slack/socketmode" + + "github.com/slack-go/slack" + "github.com/slack-go/slack/slackevents" +) + +type Slack struct { + appToken string + botToken string +} + +func NewSlack(config map[string]string) *Slack { + return &Slack{ + appToken: config["appToken"], + botToken: config["botToken"], + } +} + +func (t *Slack) AgentResultCallback() func(state agent.ActionState) { + return func(state agent.ActionState) { + // Send the result to the bot + } +} + +func (t *Slack) AgentReasoningCallback() func(state agent.ActionCurrentState) bool { + return func(state agent.ActionCurrentState) bool { + // Send the reasoning to the bot + return true + } +} + +func (t *Slack) Start(a *agent.Agent) { + api := slack.New( + t.botToken, + slack.OptionDebug(true), + slack.OptionLog(log.New(os.Stdout, "api: ", log.Lshortfile|log.LstdFlags)), + slack.OptionAppLevelToken(t.appToken), + ) + + client := socketmode.New( + api, + socketmode.OptionDebug(true), + socketmode.OptionLog(log.New(os.Stdout, "socketmode: ", log.Lshortfile|log.LstdFlags)), + ) + go func() { + for evt := range client.Events { + switch evt.Type { + case socketmode.EventTypeConnecting: + fmt.Println("Connecting to Slack with Socket Mode...") + case socketmode.EventTypeConnectionError: + fmt.Println("Connection failed. Retrying later...") + case socketmode.EventTypeConnected: + fmt.Println("Connected to Slack with Socket Mode.") + case socketmode.EventTypeEventsAPI: + eventsAPIEvent, ok := evt.Data.(slackevents.EventsAPIEvent) + if !ok { + fmt.Printf("Ignored %+v\n", evt) + + continue + } + + fmt.Printf("Event received: %+v\n", eventsAPIEvent) + + client.Ack(*evt.Request) + + switch eventsAPIEvent.Type { + case slackevents.CallbackEvent: + innerEvent := eventsAPIEvent.InnerEvent + + b, err := api.AuthTest() + if err != nil { + fmt.Printf("Error getting auth test: %v", err) + } + + switch ev := innerEvent.Data.(type) { + case *slackevents.MessageEvent: + + if b.UserID == ev.User { + // Skip messages from ourselves + return + } + message := ev.Text + res := a.Ask( + agent.WithText(message), + ) + _, _, err = api.PostMessage(ev.Channel, + slack.MsgOptionText(res.Response, false), + slack.MsgOptionPostMessageParameters(slack.PostMessageParameters{LinkNames: 1})) + if err != nil { + fmt.Printf("Error posting message: %v", err) + } + case *slackevents.AppMentionEvent: + + if b.UserID == ev.User { + // Skip messages from ourselves + return + } + message := ev.Text + + res := a.Ask( + agent.WithText(message), + ) + + _, _, err = api.PostMessage(ev.Channel, + slack.MsgOptionText(res.Response, false), + slack.MsgOptionPostMessageParameters(slack.PostMessageParameters{LinkNames: 1})) + if err != nil { + fmt.Printf("Error posting message: %v", err) + } + case *slackevents.MemberJoinedChannelEvent: + fmt.Printf("user %q joined to channel %q", ev.User, ev.Channel) + } + default: + client.Debugf("unsupported Events API event received") + } + default: + fmt.Fprintf(os.Stderr, "Unexpected event type received: %s\n", evt.Type) + } + } + }() + + client.RunContext(a.Context()) +} diff --git a/go.mod b/go.mod index 66fe086..0432954 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,13 @@ toolchain go1.22.2 require ( github.com/donseba/go-htmx v1.8.0 - github.com/gofiber/fiber/v3 v3.0.0-20240405062939-c8c51ee78331 + github.com/go-telegram/bot v1.2.1 + github.com/gofiber/fiber/v2 v2.52.4 github.com/onsi/ginkgo/v2 v2.15.0 github.com/onsi/gomega v1.31.1 github.com/sap-nocops/duckduckgogo v0.0.0-20201102135645-176990152850 github.com/sashabaranov/go-openai v1.18.3 + github.com/slack-go/slack v0.12.5 github.com/valyala/fasthttp v1.52.0 ) @@ -20,17 +22,16 @@ require ( github.com/andybalholm/cascadia v1.1.0 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/go-telegram/bot v1.2.1 // indirect - github.com/gofiber/fiber/v2 v2.52.4 // indirect - github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect github.com/klauspost/compress v1.17.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect golang.org/x/net v0.21.0 // indirect diff --git a/go.sum b/go.sum index c52b9f1..419c853 100644 --- a/go.sum +++ b/go.sum @@ -18,20 +18,21 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-telegram/bot v1.2.1 h1:FkrixLCtMtPUQAN4plXdNElbhkdXkx2p68YPXKBruDg= github.com/go-telegram/bot v1.2.1/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM= +github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= +github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= -github.com/gofiber/fiber/v3 v3.0.0-20240405062939-c8c51ee78331 h1:kDxTNPKMIRz8q28+tJHL2p87Cjtmkfn/OsLfastmpaY= -github.com/gofiber/fiber/v3 v3.0.0-20240405062939-c8c51ee78331/go.mod h1:w7sdfTY0okjZ1oVH6rSOGvuACUIt0By1iK0HKUb3uqM= -github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co= -github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= @@ -54,7 +55,10 @@ github.com/sap-nocops/duckduckgogo v0.0.0-20201102135645-176990152850 h1:DsVS3HK github.com/sap-nocops/duckduckgogo v0.0.0-20201102135645-176990152850/go.mod h1:ur7dCshjxoPKHtsZgtb6n5gpOmzQNRQ5AT+yOLwaJxM= github.com/sashabaranov/go-openai v1.18.3 h1:dspFGkmZbhjg1059KhqLYSV2GaCiRIn+bOu50TlXUq8= github.com/sashabaranov/go-openai v1.18.3/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= +github.com/slack-go/slack v0.12.5 h1:ddZ6uz6XVaB+3MTDhoW04gG+Vc/M/X1ctC+wssy2cqs= +github.com/slack-go/slack v0.12.5/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -80,6 +84,7 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=