Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slash Command returns "bad_error_message" #1267

Closed
talaniz opened this issue Feb 21, 2024 · 7 comments
Closed

Slash Command returns "bad_error_message" #1267

talaniz opened this issue Feb 21, 2024 · 7 comments

Comments

@talaniz
Copy link

talaniz commented Feb 21, 2024

What happened

I'm following this tutorial for creating a Slack bot in socket mode. I've created a very basic "hello" command and reinstalled the app. However, when I run the code and attempt the command "/hello reader" per the tutorial, I get "/hello failed with the error "dispatch_failed"" instead of processing the command. I tried adding a default case to the switch statement and it returns ****** Received Event: {error_bad_message 0x1400029e5d0 <nil>}, which appears to come from the slack-go library.

Sample websocket message

socketmode: 2024/02/21 15:20:38 socket_mode_managed_conn.go:378: Received WebSocket message: {"envelope_id":"276bcbd8-cdba-48f6-95f6-6680f75728b4","payload":
{"token":"TOKEN","team_id":"T015M2ZDW58","team_domain":"DOMAIN","channel_id":"C015T89B4AY","channel_name":"general","user_id":"U015ZMBAL1X"
,"user_name":"USER.NAME","command":"\/hello","text":"reader","api_app_id":"A015T8ETJ92",
"is_enterprise_install":"false","response_url":"https:\/\/hooks.slack.com\/commands\/T015M2ZDW58\/6681237517090\/hY68y9aB79puxzLfT1id5c5f","trigger_id":"6704023023520.1191101472178.1810964e79c5559c22273228f710f5f7"},
"type":"slash_commands","accepts_response_payload":true}

Expected behavior

It looks like the code should process the command correctly.

Steps to reproduce

Follow the tutorial to create the slash command (code below).

reproducible code

package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/joho/godotenv"
	"github.com/slack-go/slack"
	"github.com/slack-go/slack/slackevents"
	"github.com/slack-go/slack/socketmode"
)

func handleAppMentionEvent(event *slackevents.AppMentionEvent, client *slack.Client) error {

	user, err := client.GetUserInfo(event.User)
	if err != nil {
		return err
	}
	text := strings.ToLower(event.Text)

	attachment := slack.Attachment{}
	attachment.Fields = []slack.AttachmentField{
		{
			Title: "Date",
			Value: time.Now().String(),
		}, {
			Title: "Initializer",
			Value: user.Name,
		},
	}
	if strings.Contains(text, "hello") {
		attachment.Text = fmt.Sprintf("Hello %s", user.Name)
		attachment.Pretext = "Greetings"
		attachment.Color = "#4af030"
	} else {
		attachment.Text = fmt.Sprintf("How can I help you %s?", user.Name)
		attachment.Pretext = "How can I be of service"
		attachment.Color = "#3d3d3d"
	}
	_, _, err = client.PostMessage(event.Channel, slack.MsgOptionAttachments(attachment))
	if err != nil {
		return fmt.Errorf("failed to post message: %w", err)
	}
	return nil
}

func handleHelloCommand(command slack.SlashCommand, client *slack.Client) error {
	attachment := slack.Attachment{}
	attachment.Fields = []slack.AttachmentField{
		{
			Title: "Date",
			Value: time.Now().String(),
		}, {
			Title: "Initializer",
			Value: command.UserName,
		},
	}
	attachment.Text = fmt.Sprintf("Hello %s", command.Text)
	attachment.Color = "#4af030"

	_, _, err := client.PostMessage(command.ChannelID, slack.MsgOptionAttachments(attachment))
	if err != nil {
		return fmt.Errorf("failed to post message: %w", err)
	}
	return nil
}

// handleSlashCommand handles slash commands by routing them to the appropriate function
func handleSlashCommand(command slack.SlashCommand, client *slack.Client) error {
	log.Println("Received slash command", command)
	switch command.Command {
	case "/hello":
		return handleHelloCommand(command, client)
	}
	return nil
}

// handleEventMessage will take an event and handle itproperly based on the event type
func handleEventMessage(event slackevents.EventsAPIEvent, client *slack.Client) error {
	switch event.Type {

	case slackevents.CallbackEvent:
		innerEvent := event.InnerEvent
		switch ev := innerEvent.Data.(type) {
		case *slackevents.AppMentionEvent:
			err := handleAppMentionEvent(ev, client)
			if err != nil {
				return err
			}
		}
	default:
		return errors.New("unsupported event type")
	}
	return nil
}

func main() {
	godotenv.Load(".env")

	token := os.Getenv("SLACK_BOT_TOKEN")
	appToken := os.Getenv("SLACK_APP_TOKEN")

	client := slack.New(token, slack.OptionDebug(true), slack.OptionAppLevelToken(appToken))
	sockentClient := socketmode.New(
		client,
		socketmode.OptionDebug(true),
		socketmode.OptionLog(log.New(os.Stdout, "socketmode: ", log.Lshortfile|log.LstdFlags)),
	)

	// Implement a graceful shutdown here
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	go func(ctx context.Context, client *slack.Client, socketClient *socketmode.Client) {
		for {
			select {
			case <-ctx.Done():
				log.Println("Shutting down socketmode listener")
				return
			case event := <-socketClient.Events:
				switch event.Type {
				// Slack events
				case socketmode.EventTypeEventsAPI:
					eventsApiEvent, ok := event.Data.(slackevents.EventsAPIEvent)
					if !ok {
						log.Printf("Could not type case the event to the EventsAPIEvent: %v\n", event)
						continue
					}
					log.Println("Received API event: ", event.Type)
					socketClient.Ack(*event.Request)
					err := handleEventMessage(eventsApiEvent, client)
					if err != nil {
						log.Fatal(err)
					}
				// Slash command
				case socketmode.EventTypeSlashCommand:
					command, ok := event.Data.(slack.SlashCommand)
					if !ok {
						log.Printf("Could not type case the message to a SlashCommand: %v\n", command)
						continue
					}
					log.Println("Received slash event: ", command)
					socketClient.Ack(*event.Request)
					err := handleSlashCommand(command, client)
					if err != nil {
						log.Fatal(err)
					}
				default:
					log.Println("****** Received Event: ", event)
				}
			}
		}
	}(ctx, client, sockentClient)
	sockentClient.Run()
}

manifest.yaml

Versions

  • Go: 1.21.0
  • slack-go/slack: v.0.12.4
@rohithkakarla30
Copy link

rohithkakarla30 commented Feb 22, 2024

It worked for me if i change the value of struct SlashCommand to

type SlashCommand struct {
	Token               string `json:"token"`
	TeamID              string `json:"team_id"`
	TeamDomain          string `json:"team_domain"`
	EnterpriseID        string `json:"enterprise_id,omitempty"`
	EnterpriseName      string `json:"enterprise_name,omitempty"`
	IsEnterpriseInstall bool   `json:"is_enterprise_install,string"`
	ChannelID           string `json:"channel_id"`
	ChannelName         string `json:"channel_name"`
	UserID              string `json:"user_id"`
	UserName            string `json:"user_name"`
	Command             string `json:"command"`
	Text                string `json:"text"`
	ResponseURL         string `json:"response_url"`
	TriggerID           string `json:"trigger_id"`
	APIAppID            string `json:"api_app_id"`
}

Which should not be the case. but not sure.
Changed value IsEnterpriseInstall bool json:"is_enterprise_install" -> IsEnterpriseInstall bool json:"is_enterprise_install,string"
Reference to line
https://github.com/slack-go/slack/blob/v0.12.4/slash.go#L14

@nvjustdev
Copy link

Happened to me too

@SeniorPomidorro
Copy link

Same problem, rollback to v.0.12.3 is helped

slack-go/slack: v.0.12.4
go1.21.5 darwin/arm64

Event Data:

{parsing slash command: json: cannot unmarshal string into Go struct field SlashCommand.is_enterprise_install of type bool {"envelope_id":"XXXXX","payload":{"token":"XXXXX","team_id":"XXXXX","team_domain":"XXXX","channel_id":"XXXX","channel_name":"lolkek","user_id":"U0XXXXX","user_name":"XXXXX","command":"\/pollsettings","text":"","api_app_id":"XXXXX","is_enterprise_install":"false","response_url":"https:\/\/hooks.slack.com\/commands\/XXXXX\/XXXXXX\/XXXXXX","trigger_id":"XXXXXXX"},"type":"slash_commands","accepts_response_payload":true}}

Code sample:

func (s *Service) eventRouter(ctx context.Context, e socketmode.Event) error {

switch e.Type {
case socketmode.EventTypeSlashCommand:
	s.slack.Ack(e.Request)
	e := e.Data.(slack.SlashCommand)

	switch e.Command {
	case "/test":
		return s.poll.CreateModalView(ctx, &e)
	case "/pollsettings":
		return s.poll.CreatePollSettingsModal(ctx, &e)
	case "/pepefast":
		return s.poll.CreateFast(ctx, &e)
	}

case socketmode.EventTypeInteractive:
	s.slack.Ack(e.Request)
	e := e.Data.(slack.InteractionCallback)

	switch {
	case e.Type == slack.InteractionTypeViewSubmission && (e.View.CallbackID == "poll_modal_view" || e.View.CallbackID == "settings_modal_view"),
		e.Type == slack.InteractionTypeBlockActions && e.View.CallbackID == "settings_modal_view",
		e.Type == slack.InteractionTypeInteractionMessage && strings.Contains(e.CallbackID, "poll"):
		return s.poll.Interactions(ctx, &e)

	}
case socketmode.EventTypeErrorBadMessage:
	fmt.Printf("%s", e.Data)
default:
	if e.Type != "connecting" && e.Type != "connected" && e.Type != "hello" {
		return errors.New(fmt.Sprintf("UNKNOW EVENT TYPE %s", e.Type))
	}
}

return nil

}

@parsley42
Copy link
Member

Sorry about that; probably a straight-forward fix, somebody care to work up a PR? I added a comment to the PR where this was introduced.

@talaniz
Copy link
Author

talaniz commented Feb 24, 2024

Looks like there's one in that already addresses the comments on the original commit #1266

@parsley42
Copy link
Member

Just published v0.12.5 which deals w/ bool or string. Thanks to @kpaulisse.

@talaniz
Copy link
Author

talaniz commented Feb 26, 2024

I've tested in v0.12.5, looks like all is working. Thanks everyone!

@talaniz talaniz closed this as completed Feb 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants