package slack import ( "context" "errors" "fmt" "log" "os" "os/exec" "rodleyserverbot/slack-bot/config" "strings" "github.com/slack-go/slack" "github.com/slack-go/slack/slackevents" "github.com/slack-go/slack/socketmode" ) func Start() { // Create a new client to slack by giving token // Set debug to true while developing // Also add a ApplicationToken option to the client client := slack.New( config.Config.SlackAppToken, slack.OptionDebug(false), slack.OptionAppLevelToken(config.Config.SlackAppToken)) // go-slack comes with a SocketMode package that we need to use that accepts a Slack client and outputs a Socket mode client instead socketClient := socketmode.New( client, socketmode.OptionDebug(false), // Option to set a custom logger socketmode.OptionLog(log.New(os.Stdout, "socketmode: ", log.Lshortfile|log.LstdFlags)), ) // Create a context that can be used to cancel goroutine ctx, cancel := context.WithCancel(context.Background()) // Make this cancel called properly in a real program , graceful shutdown etc defer cancel() go func(ctx context.Context, client *slack.Client, socketClient *socketmode.Client) { // Create a for loop that selects either the context cancellation or the events incomming for { select { // inscase context cancel is called exit the goroutine case <-ctx.Done(): log.Println("Shutting down socketmode listener") return case event := <-socketClient.Events: // We have a new Events, let's type switch the event // Add more use cases here if you want to listen to other events. switch event.Type { // handle EventInteractive events case socketmode.EventTypeInteractive: // fmt.Println("Recivimos un Interactive!", event) callback, ok := event.Data.(slack.InteractionCallback) if !ok { log.Printf("Could not type cast the event to the MessageAction: %v\n", event) continue } socketClient.Ack(*event.Request) switch callback.Type { case slack.InteractionTypeInteractionMessage: //fmt.Println("type:", callback.Type, "callBackId:", callback.CallbackID, "aactions[0]", *callback.ActionCallback.AttachmentActions[0]) action := *callback.ActionCallback.AttachmentActions[0] //fmt.Println("Value:", action.Value) var output string var err error switch action.Value { case "poweron": // fmt.Println("Encender!") output, err = executeAction(action.Value) case "poweroff": // fmt.Println("Apagar!") output, err = executeAction(action.Value) } if err == nil { finalMessage := fmt.Sprintf("Chi chi chi chi amo! Output: %s", output) replyToAction(callback.Channel.Name, finalMessage, client) } case slack.InteractionTypeMessageAction: //fmt.Println("is a message action?") case slack.InteractionTypeBlockActions: // See https://api.slack.com/apis/connections/socket-implement#button //fmt.Println("button clicked!") case slack.InteractionTypeShortcut: case slack.InteractionTypeViewSubmission: // See https://api.slack.com/apis/connections/socket-implement#modal case slack.InteractionTypeDialogSubmission: default: } // handle EventAPI events case socketmode.EventTypeEventsAPI: // The Event sent on the channel is not the same as the EventAPI events so we need to type cast it eventsAPIEvent, ok := event.Data.(slackevents.EventsAPIEvent) if !ok { log.Printf("Could not type cast the event to the EventsAPIEvent: %v\n", event) continue } // We need to send an Acknowledge to the slack server socketClient.Ack(*event.Request) // Now we have an Events API event, but this event type can in turn be many types, so we actually need another type switch // log.Println(eventsAPIEvent) err := handleEventMessage(eventsAPIEvent, client) if err != nil { // Replace with actual err handeling log.Fatal(err) } } } } }(ctx, client, socketClient) socketClient.Run() } // handleEventMessage will take an event and handle it properly based on the type of event func handleEventMessage(event slackevents.EventsAPIEvent, client *slack.Client) error { switch event.Type { // First we check if this is an CallbackEvent case slackevents.CallbackEvent: innerEvent := event.InnerEvent // fmt.Println("We received an event!", innerEvent) // Yet Another Type switch on the actual Data to see if its an AppMentionEvent switch ev := innerEvent.Data.(type) { case *slackevents.AppMentionEvent: // The application has been mentioned since this Event is a Mention event log.Println(ev) handleAppMentionEvent(ev, client) } default: return errors.New("unsupported event type") } return nil } func executeAction(actionName string) (string, error) { var output_str string switch actionName { case "poweron": cmd := exec.Command("/bin/bash", os.Getenv("POWERON_SCRIPT_PATH")) output, err := cmd.Output() if err != nil { fmt.Printf("error %s", err) return "", err } output_str = string(output) //fmt.Println("output:", output_str) // return output case "poweroff": cmd := exec.Command("/bin/bash", os.Getenv("POWEROFF_SCRIPT_PATH")) output, err := cmd.Output() if err != nil { fmt.Printf("error %s", err) return "", err } output_str = string(output) //fmt.Println("output:", output_str) } return output_str, nil } func replyToAction(channelName string, message string, client *slack.Client) error { attachment := slack.Attachment{} attachment.Text = message attachment.Color = "#4af030" _, _, err := client.PostMessage(channelName, slack.MsgOptionAttachments(attachment)) if err != nil { return fmt.Errorf("failed to post message: %w", err) } return nil } // handleAppMentionEvent is used to take care of the AppMentionEvent when the bot is mentioned func handleAppMentionEvent(event *slackevents.AppMentionEvent, client *slack.Client) error { // Grab the user name based on the ID of the one who mentioned the bot user, err := client.GetUserInfo(event.User) if err != nil { return err } // Check if the user said Hello to the bot text := strings.ToLower(event.Text) // Create the attachment and assigned based on the message attachment := slack.Attachment{} // Add Some default context like user who mentioned the bot // attachment.Fields = []slack.AttachmentField{ // { // Title: "Date", // Value: time.Now().String(), // }, { // Title: "Initializer", // Value: user.Name, // }, // } attachment.CallbackID = "server_action" if strings.Contains(text, "hello") { // Greet the user attachment.Text = fmt.Sprintf("Hello %s", user.Name) attachment.Pretext = "Greetings" attachment.Color = "#4af030" } else { // Send a message to the user attachment.Text = "Por el momento esto es lo que se hacer" attachment.Pretext = fmt.Sprintf("Como te puedo ayudar %s?", user.Name) attachment.Color = "#3d3d3d" powerOnAction := slack.AttachmentAction{} powerOnAction.Name = "poweron" powerOnAction.Text = "Encender" powerOnAction.Value = "poweron" powerOnAction.Type = "button" powerOffAction := slack.AttachmentAction{} powerOffAction.Name = "poweroff" powerOffAction.Text = "Apagar" powerOffAction.Value = "poweroff" powerOffAction.Type = "button" powerOffAction.Style = "danger" actions := []slack.AttachmentAction{powerOnAction, powerOffAction} //fmt.Println(actions) attachment.Actions = actions } // Send the message to the channel // The Channel is available in the event message _, _, err = client.PostMessage(event.Channel, slack.MsgOptionAttachments(attachment)) if err != nil { return fmt.Errorf("failed to post message: %w", err) } return nil }