Comments (1)
Can confirm this issue is still present. If you add a block, then the update works great, as intended, but you can't update using text.
I've done my best to recreate some reproducible code - but you may need to tweak it a little:
package chatbot
import (
"fmt"
"github.com/slack-go/slack"
"github.com/stretchr/testify/assert"
"testing"
)
var slackClient *slack.Client
// ErrChannelNotFound can be used with errors.Is to determine if the channel
// doesn't exist
var ErrChannelNotFound = errors.New("channel_not_found")
func setup(t *testing.T) {
t.Helper()
slackClient = slack.New(slackBotToken,
slack.OptionAppLevelToken(slackAppToken))
}
// ChatMessage is an abstraction, designed to make chat input/output more accessible from other components
// You should instantiate this by using NewChatMessage()
type ChatMessage struct {
ShortMessage string `bson:"shortMessage" json:"shortMessage"`
ChannelName string `bson:"channelName" json:"channelName"`
SuccessResponse string `bson:"successResponse" json:"successResponse"`
FailureResponse string `bson:"failureResponse" json:"failureResponse"`
// AtMentionUser is the user to mention in the message, e.g. topher.sterling
// - there should not be an @prefix
AtMentionUser string `bson:"atMentionUser" json:"atMentionUser"`
Metadata map[string]interface{} `bson:"metadata" json:"metadata"`
Blocks []CFChatbotBlock `bson:"-" json:"-"`
}
func NewChatMessage(shortMessage, channelName string) *ChatMessage {
c := &ChatMessage{
ChannelName: channelName,
ShortMessage: shortMessage,
}
if len(shortMessage) > 0 {
c.AddTitle(shortMessage)
}
return c
}
type CFChatbotBlock struct {
slack.Block
}
// slackBlocks converts the ChatMessage's blocks to a slice of slack.Blocks
func (c *ChatMessage) slackBlocks() []slack.Block {
blocks := make([]slack.Block, len(c.Blocks))
for i := range c.Blocks {
blocks[i] = c.Blocks[i].Block
}
if len(c.AtMentionUser) > 0 {
// We're supposed to be able to reply with a user ID - but we can't. So... we'll have to get the user info
// userInfo, err := bot.SlackClient.GetUserInfo(c.AtMentionUser)
// if err != nil {
// bot.log.WithError(err).Error("Could not get user info")
// } else {
// bot.log.WithField("user", userInfo).Info("Found user info")
// }
// Prepend the @mention to the message
c.Blocks = append([]CFChatbotBlock{
{Block: slack.NewSectionBlock(
slack.NewTextBlockObject(
slack.MarkdownType,
fmt.Sprintf("FYI <@%s>?", c.AtMentionUser),
false, false,
),
nil,
nil,
),
},
}, c.Blocks...)
}
return blocks
}
// Send Posts the message to Slack and saves it to the DB.
// It returns an error if the message could not be sent or saved.
func (c *ChatMessage) Send() (msgID string, err error) {
_, msgID, err = slackClient.PostMessage(c.ChannelName,
slack.MsgOptionEnableLinkUnfurl(),
slack.MsgOptionBlocks(
c.slackBlocks()...,
),
slack.MsgOptionMetadata(slack.SlackMetadata{
EventType: "cf-emitting-chat-message",
EventPayload: c.Metadata,
}),
slack.MsgOptionUsername(fmt.Sprintf("CF %s Chat Bot", "DEV")),
)
if err != nil {
if IsChannelNotFound(err) {
err = ErrChannelNotFound
}
return msgID, fmt.Errorf("could not send message to Slack channel '%s': %w", c.ChannelName, err)
}
return msgID, nil
}
// CompleteInteraction allows you to mark a slack interaction as "completed"
func CompleteInteraction(channelName, msgId, result, completedByUser string) (err error) {
channelID, _, err := ChannelNameToID(channelName)
if err != nil {
return fmt.Errorf("unable to complete slack interaction out of band: %w", err)
}
text := fmt.Sprintf("Thank you for submitting your answer of '%s' %s! (We realize this is a thread - slack does *not* like updating the original message without a user initiated action).", result, completedByUser)
// Note - will post a thread reply
_, _, _, err = slackClient.UpdateMessage(channelID,
msgId,
slack.MsgOptionText(text, false),
slack.MsgOptionAsUser(false),
slack.MsgOptionUsername(fmt.Sprintf("CF %s Chat Bot", consts.ActualCFEnv)))
if err != nil {
return fmt.Errorf("unable to complete slack interaction out of band: %w", err)
}
return nil
}
// ChannelNameToID converts a channel name to a channel ID - a note that this
// will call the Slack API to get the list of channels. Caching has been removed for this example.
func ChannelNameToID(channelName string) (channelID string, isChannelMember bool, err error) {
// Remove the # if it's there
channelName = strings.TrimPrefix(channelName, "#")
// There was a cache here - removing to make it easier
channelNamesToIDs, err := ListChannels()
if err != nil {
return "", false, err
}
channelID, ok := channelNamesToIDs[channelName]
if !ok {
return "", false, ErrChannelNotFound
}
return channelID, false, nil
}
// ListChannels returns a map of channel names to channel IDs for all public and
// private channels. These channels are not auto-joined. Caching has been removed for this example.
func ListChannels() (channelNamesToIDs map[string]string, err error) {
channels, _, err := slackClient.GetConversations(&slack.GetConversationsParameters{
ExcludeArchived: true,
Limit: 1000,
Types: []string{"public_channel", "private_channel"},
})
if err != nil {
return nil, err
}
channelNamesToIDs = make(map[string]string)
for i := range channels {
channelNamesToIDs[channels[i].Name] = channels[i].ID
}
return channelNamesToIDs, nil
}
// TestCompleteInteraction is a test which should first send a message, then
// subsequently update that message. We are able to get this test to sort of work
// by updating the slack.MsgOptionTS(msgId) - but that's not ideal as it doesn't allow us to
// mark the interaction as complete.
// We also can send using ephemeral messages, but that's not ideal either, as we want the message
// to persist until the interaction is either completed from with-in Slack or the
// external system completes the interaction.
func TestCompleteInteraction(t *testing.T) {
is := assert.New(t)
setup(t)
msg := NewChatMessage("My initial message", "cf-chatbot-test-")
// msg.AddControl("How are you?", formtypes.ControlTypeButton, "Good", "Bad")
msgId, err := msg.Send()
is.NoError(err, "Could not send chat message")
is.NoError(CompleteInteraction(msg.ChannelName, msgId, "Good", "Topher Sterling"))
}
Note that changing the UpdateMessage code to use blocks rather than slack.MsgOptionText(text, false)
fixes the issue and the update succeeds. If you swap the CompleteInteration/UpdateMessage call for this, then you'll see it work:
_, _, _, err = slackClient.UpdateMessage(channelID,
msgId,
slack.MsgOptionBlocks(slack.NewSectionBlock(
slack.NewTextBlockObject(
"mrkdwn", text, false, false),
nil, nil)),
slack.MsgOptionAsUser(false),
slack.MsgOptionUsername(fmt.Sprintf("CF %s Chat Bot", consts.ActualCFEnv)))
So perhaps the SDK could convert the MsgOptionText
to a single MsgOptionBlocks
?
from slack.
Related Issues (20)
- Websocket authentication failures since 2023-09-19
- unfurl_links does not work again
- video block HOT 2
- Support is_read_only in slack.Conversation
- ---
- NewHeaderBlock does not want to send message
- Post Webhook method keeps returning dial tcp timeout (ACL issue?) HOT 1
- Support rich text input
- Add support Custom profile fields HOT 1
- Apply retryable logic to all APIs
- Fix SocketMode support in slacktest
- Support file_input elements on modals HOT 3
- Webhook Sending Functions Seem to Use the Regular JSON Encoder Which Escapes HTML Characters Unnecessarily HOT 1
- Get Block data from webhook payload
- request for a release cut HOT 2
- Release 0.21.4 breaks slash commands in socket mode
- Slash Command returns "bad_error_message" HOT 7
- file_input feature support in slack-go HOT 2
- Unable to render buttons using slack.Attachment HOT 2
- Receiving no payload on websocket based events HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from slack.