Add slack connector
This commit is contained in:
@@ -120,6 +120,7 @@ func (a *AgentPool) List() []string {
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ConnectorTelegram = "telegram"
|
ConnectorTelegram = "telegram"
|
||||||
|
ConnectorSlack = "slack"
|
||||||
ActionSearch = "search"
|
ActionSearch = "search"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -153,7 +154,7 @@ type Connector interface {
|
|||||||
Start(a *Agent)
|
Start(a *Agent)
|
||||||
}
|
}
|
||||||
|
|
||||||
var AvailableConnectors = []string{ConnectorTelegram}
|
var AvailableConnectors = []string{ConnectorTelegram, ConnectorSlack}
|
||||||
|
|
||||||
func (a *AgentConfig) availableConnectors() []Connector {
|
func (a *AgentConfig) availableConnectors() []Connector {
|
||||||
connectors := []Connector{}
|
connectors := []Connector{}
|
||||||
@@ -177,6 +178,8 @@ func (a *AgentConfig) availableConnectors() []Connector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connectors = append(connectors, cc)
|
connectors = append(connectors, cc)
|
||||||
|
case ConnectorSlack:
|
||||||
|
connectors = append(connectors, connector.NewSlack(config))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return connectors
|
return connectors
|
||||||
|
|||||||
132
example/webui/connector/slack.go
Normal file
132
example/webui/connector/slack.go
Normal file
@@ -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())
|
||||||
|
}
|
||||||
9
go.mod
9
go.mod
@@ -6,11 +6,13 @@ toolchain go1.22.2
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/donseba/go-htmx v1.8.0
|
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/ginkgo/v2 v2.15.0
|
||||||
github.com/onsi/gomega v1.31.1
|
github.com/onsi/gomega v1.31.1
|
||||||
github.com/sap-nocops/duckduckgogo v0.0.0-20201102135645-176990152850
|
github.com/sap-nocops/duckduckgogo v0.0.0-20201102135645-176990152850
|
||||||
github.com/sashabaranov/go-openai v1.18.3
|
github.com/sashabaranov/go-openai v1.18.3
|
||||||
|
github.com/slack-go/slack v0.12.5
|
||||||
github.com/valyala/fasthttp v1.52.0
|
github.com/valyala/fasthttp v1.52.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,17 +22,16 @@ require (
|
|||||||
github.com/andybalholm/cascadia v1.1.0 // indirect
|
github.com/andybalholm/cascadia v1.1.0 // indirect
|
||||||
github.com/go-logr/logr v1.3.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-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/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||||
github.com/google/uuid v1.6.0 // 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/klauspost/compress v1.17.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // 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/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
golang.org/x/net v0.21.0 // indirect
|
golang.org/x/net v0.21.0 // indirect
|
||||||
|
|||||||
13
go.sum
13
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-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 h1:FkrixLCtMtPUQAN4plXdNElbhkdXkx2p68YPXKBruDg=
|
||||||
github.com/go-telegram/bot v1.2.1/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM=
|
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 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
||||||
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
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 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
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 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
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/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 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
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/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 h1:dspFGkmZbhjg1059KhqLYSV2GaCiRIn+bOu50TlXUq8=
|
||||||
github.com/sashabaranov/go-openai v1.18.3/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
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/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.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 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
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/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 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
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 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
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=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
|||||||
Reference in New Issue
Block a user