3 changed files with 243 additions and 236 deletions
@ -1,240 +1,7 @@ |
|||||
package main |
package main |
||||
|
|
||||
import ( |
import "rodleyserverbot/slack-bot/internal/slack" |
||||
"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 main() { |
func main() { |
||||
// Create a new client to slack by giving token
|
slack.Start() |
||||
// 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 |
|
||||
} |
} |
||||
|
|||||
@ -0,0 +1,240 @@ |
|||||
|
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 |
||||
|
} |
||||
Loading…
Reference in new issue