diff --git a/config/app.go b/config/app.go index 7fcfa38..879be12 100644 --- a/config/app.go +++ b/config/app.go @@ -1,28 +1,30 @@ package config import ( + "encoding/csv" "fmt" + "io" "log" "os" - "encoding/csv" "strings" - "io" + "github.com/joho/godotenv" ) type ConfigEntries struct { SlackAuthToken string - SlackAppToken string - Actions []ActionScript + SlackAppToken string + Actions []ActionScript } type ActionScript struct { - Name string + Name string DisplayName string - Path string + Path string } var Config ConfigEntries + // var Actions []ActionScript func init() { @@ -38,6 +40,10 @@ func init() { Config.SlackAuthToken = os.Getenv("SLACK_AUTH_TOKEN") Config.SlackAppToken = os.Getenv("SLACK_APP_TOKEN") actionsLoaded := initalizeDefinedActions() + if actionsLoaded == 0 { + log.Println("No actions found in the environment variables") + os.Exit(1) + } log.Println("Finished loading config. Actions found:", actionsLoaded) log.Println("Actions:", Config.Actions) } @@ -47,13 +53,17 @@ func init() { // `Name,Display Name,Path` // For example: // `poweron,Encender Server,/some/script.sh` +// +// Each action name must be unique func initalizeDefinedActions() int { log.Println("initalizeDefinedActions") - var count = 1 + var count = 0 var value string var exists bool + + actionNames := make(map[string]bool) for { - envVarName := fmt.Sprintf("BOT_ACTION_%d", count) + envVarName := fmt.Sprintf("BOT_ACTION_%d", count+1) log.Println("Checking for env var:", envVarName) value, exists = os.LookupEnv(envVarName) if !exists { @@ -61,6 +71,21 @@ func initalizeDefinedActions() int { } log.Println("Action found:", value) action := actionStringToActionScript(value) + + exists = actionNames[action.Name] + if exists { + log.Println("The action", action.Name, "is already defined.") + os.Exit(1) + } else { + actionNames[action.Name] = true + } + + _, err := os.Stat(action.Path) + if err != nil { + log.Println("The specified path", action.Path, " does not exist.", err) + os.Exit(1) + } + // Actions = append(Actions, action) Config.Actions = append(Config.Actions, action) count++ @@ -68,7 +93,7 @@ func initalizeDefinedActions() int { return count } -func actionStringToActionScript(actionString string) ActionScript{ +func actionStringToActionScript(actionString string) ActionScript { var action ActionScript var csvArr []string var err error diff --git a/internal/.DS_Store b/internal/.DS_Store new file mode 100644 index 0000000..ed9df2d Binary files /dev/null and b/internal/.DS_Store differ diff --git a/internal/slack/handler.go b/internal/slack/handler.go index 7ff57fc..6a829b9 100644 --- a/internal/slack/handler.go +++ b/internal/slack/handler.go @@ -6,9 +6,10 @@ import ( "fmt" "log" "os" - "time" "os/exec" "rodleyserverbot/slack-bot/config" + "time" + "github.com/slack-go/slack" "github.com/slack-go/slack/slackevents" "github.com/slack-go/slack/socketmode" @@ -50,7 +51,7 @@ func Start() { // handle EventInteractive events case socketmode.EventTypeInteractive: callback, ok := event.Data.(slack.InteractionCallback) - + if !ok { log.Printf("Could not type cast the event to the MessageAction: %v\n", event) continue @@ -58,7 +59,9 @@ func Start() { socketClient.Ack(*event.Request) switch callback.Type { case slack.InteractionTypeInteractionMessage: - action := *callback.ActionCallback.AttachmentActions[0] + case slack.InteractionTypeMessageAction: + case slack.InteractionTypeBlockActions: + action := *callback.ActionCallback.BlockActions[0] var output string var err error @@ -68,13 +71,14 @@ func Start() { finalMessage := fmt.Sprintf("Resultado*\n```\n%s\n```", output) replyToAction(callback.Channel.ID, finalMessage, client) - handleAppMessagedEvent(nil, callback.Channel.ID, client) + err := handleAppMessagedEvent(nil, callback.Channel.ID, client) + if err != nil { + log.Println("Error handling event:", err) + } + } else { + log.Println("Error executing action", err) } - case slack.InteractionTypeMessageAction: - - case slack.InteractionTypeBlockActions: - // See https://api.slack.com/apis/connections/socket-implement#button - + case slack.InteractionTypeShortcut: case slack.InteractionTypeViewSubmission: // See https://api.slack.com/apis/connections/socket-implement#modal @@ -97,6 +101,7 @@ func Start() { if err != nil { // Replace with actual err handeling // log.Fatal(err) + log.Println("Error handling event:", err) } default: } @@ -120,10 +125,16 @@ func handleEventMessage(event slackevents.EventsAPIEvent, client *slack.Client) case *slackevents.AppMentionEvent: // The application has been mentioned since this Event is a Mention event //log.Println("App Mentioned!", ev) - handleAppMentionEvent(ev, client) + err := handleAppMentionEvent(ev, client) + if err != nil { + return fmt.Errorf("failed to handle app mention event: %w", err) + } case *slackevents.MessageEvent: //log.Println("MessageEvent!", ev) - handleAppMessagedEvent(ev, ev.Channel, client) + err := handleAppMessagedEvent(ev, ev.Channel, client) + if err != nil { + return fmt.Errorf("failed to handle app message event: %w", err) + } default: } default: @@ -188,37 +199,65 @@ func replyToAction(channelName string, message string, client *slack.Client) err return nil } -func getAttachmentButtons() []slack.AttachmentAction{ +func getAttachmentButtons() []slack.AttachmentAction { var actions []slack.AttachmentAction for _, action := range config.Config.Actions { slackAction := slack.AttachmentAction{} slackAction.Name = action.Name slackAction.Value = action.Name - slackAction.Text = action.DisplayName + slackAction.Text = action.DisplayName slackAction.Type = "button" - actions=append(actions, slackAction) + actions = append(actions, slackAction) } return actions } -func handleAppMessagedEvent(event *slackevents.MessageEvent, channel string,client *slack.Client) error { +func buildResponseMessage() (slack.MsgOption, error) { + var blocks []slack.Block + + sectionBlock := slack.NewSectionBlock( + slack.NewTextBlockObject("mrkdwn", "Esto es lo que se hacer:", false, false), + nil, + nil, + ) + blocks = append(blocks, sectionBlock) + + var buttons []slack.BlockElement + // Slack only allows 5 buttons per action block + for i, action := range config.Config.Actions { + if i > 0 && i%5 == 0 { + actionBlock := slack.NewActionBlock("", buttons...) + blocks = append(blocks, actionBlock) + buttons = []slack.BlockElement{} + } + buttonElement := slack.NewButtonBlockElement(action.Name, action.Name, slack.NewTextBlockObject("plain_text", action.DisplayName, false, false)) + buttons = append(buttons, buttonElement) + } + + if len(buttons) > 0 { + actionBlock := slack.NewActionBlock("", buttons...) + blocks = append(blocks, actionBlock) + } + + message := slack.MsgOptionBlocks(blocks...) + return message, nil + +} + +func handleAppMessagedEvent(event *slackevents.MessageEvent, channel string, client *slack.Client) error { if event != nil && event.BotID != "" { // We're not interested in messages from ourselves or other bots return nil } - attachment := slack.Attachment{} var err error - attachment.CallbackID = "server_action" - // 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?") - attachment.Color = "#3d3d3d" - actions := getAttachmentButtons() - attachment.Actions = actions + message, err := buildResponseMessage() + if err != nil { + return fmt.Errorf("failed to build response message: %w", err) + } // Send the message to the channel // The Channel is available in the event message - _, _, err = client.PostMessage(channel, slack.MsgOptionAttachments(attachment)) + _, _, err = client.PostMessage(channel, message) if err != nil { return fmt.Errorf("failed to post message: %w", err) } @@ -234,7 +273,7 @@ func handleAppMentionEvent(event *slackevents.AppMentionEvent, client *slack.Cli // 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?") + attachment.Pretext = "Como te puedo ayudar?" attachment.Color = "#3d3d3d" actions := getAttachmentButtons() attachment.Actions = actions