mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-27 09:15:15 +00:00
interact: improve slack session loading and block sets rendering
This commit is contained in:
parent
a9610a3e74
commit
22404da392
|
@ -70,13 +70,23 @@ func (reply *SlackReply) build() interface{} {
|
||||||
return reply.textInputModalViewRequest
|
return reply.textInputModalViewRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var blocks slack.Blocks
|
||||||
|
|
||||||
if len(reply.message) > 0 {
|
if len(reply.message) > 0 {
|
||||||
return reply.message
|
blocks.BlockSet = append(blocks.BlockSet, slack.NewSectionBlock(
|
||||||
|
&slack.TextBlockObject{
|
||||||
|
Type: slack.MarkdownType,
|
||||||
|
Text: reply.message,
|
||||||
|
},
|
||||||
|
nil, // fields
|
||||||
|
nil, // accessory
|
||||||
|
// slack.SectionBlockOptionBlockID(reply.uuid),
|
||||||
|
))
|
||||||
|
|
||||||
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
var blocks []slack.Block
|
blocks.BlockSet = append(blocks.BlockSet, slack.NewSectionBlock(
|
||||||
|
|
||||||
blocks = append(blocks, slack.NewSectionBlock(
|
|
||||||
&slack.TextBlockObject{
|
&slack.TextBlockObject{
|
||||||
Type: slack.MarkdownType,
|
Type: slack.MarkdownType,
|
||||||
Text: reply.message,
|
Text: reply.message,
|
||||||
|
@ -102,13 +112,10 @@ func (reply *SlackReply) build() interface{} {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
blocks = append(blocks, slack.NewActionBlock(reply.uuid, buttons...))
|
blocks.BlockSet = append(blocks.BlockSet, slack.NewActionBlock(reply.uuid, buttons...))
|
||||||
}
|
}
|
||||||
|
|
||||||
var payload = map[string]interface{}{
|
return blocks
|
||||||
"blocks": blocks,
|
|
||||||
}
|
|
||||||
return payload
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SlackSession struct {
|
type SlackSession struct {
|
||||||
|
@ -122,9 +129,10 @@ type SlackSession struct {
|
||||||
questions map[string]interface{}
|
questions map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSlackSession(userID string) *SlackSession {
|
func NewSlackSession(userID, channelID string) *SlackSession {
|
||||||
return &SlackSession{
|
return &SlackSession{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
|
ChannelID: channelID,
|
||||||
questions: make(map[string]interface{}),
|
questions: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,52 +248,92 @@ func (s *Slack) listen() {
|
||||||
switch callback.Type {
|
switch callback.Type {
|
||||||
case slack.InteractionTypeBlockActions:
|
case slack.InteractionTypeBlockActions:
|
||||||
// See https://api.slack.com/apis/connections/socket-implement#button
|
// See https://api.slack.com/apis/connections/socket-implement#button
|
||||||
log.Debugf("button clicked!")
|
log.Debugf("InteractionTypeBlockActions: %+v", callback)
|
||||||
// TODO: check and find what's the response handler for the reply
|
|
||||||
// we need to find the session first,
|
|
||||||
// and then look up the state, call the function to transit the state with the given value
|
|
||||||
|
|
||||||
case slack.InteractionTypeShortcut:
|
case slack.InteractionTypeShortcut:
|
||||||
|
log.Debugf("InteractionTypeShortcut: %+v", callback)
|
||||||
|
|
||||||
case slack.InteractionTypeViewSubmission:
|
case slack.InteractionTypeViewSubmission:
|
||||||
// See https://api.slack.com/apis/connections/socket-implement#modal
|
// See https://api.slack.com/apis/connections/socket-implement#modal
|
||||||
log.Debugf("InteractionTypeViewSubmission: state: %s", toJson(callback.View.State))
|
log.Debugf("[slack] InteractionTypeViewSubmission: %+v", callback)
|
||||||
|
var values = simplifyStateValues(callback.View.State)
|
||||||
|
|
||||||
|
if len(values) > 1 {
|
||||||
|
log.Warnf("[slack] more than 1 values received from the modal view submission, the value choosen from the state values might be incorrect")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugln(toJson(values))
|
||||||
|
if inputValue, ok := takeOneValue(values); ok {
|
||||||
|
session := s.loadSession(evt, callback.User.ID, callback.Channel.ID)
|
||||||
|
|
||||||
|
if !session.authorizing && !session.Authorized {
|
||||||
|
log.Warn("[slack] telegram is set to private mode, skipping message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reply := s.newReply(session)
|
||||||
|
if s.textMessageResponder != nil {
|
||||||
|
if err := s.textMessageResponder(session, inputValue, reply); err != nil {
|
||||||
|
log.WithError(err).Errorf("[slack] response handling error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the modal view by sending a null payload
|
||||||
|
s.socket.Ack(*evt.Request)
|
||||||
|
|
||||||
|
// build the response
|
||||||
|
response := reply.build()
|
||||||
|
|
||||||
|
log.Debugln("response payload", toJson(response))
|
||||||
|
switch response := response.(type) {
|
||||||
|
case slack.Blocks:
|
||||||
|
payload = map[string]interface{}{
|
||||||
|
"response_action": "clear",
|
||||||
|
// "errors": { "ticket-due-date": "You may not select a due date in the past" },
|
||||||
|
"blocks": response.BlockSet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case slack.InteractionTypeDialogSubmission:
|
case slack.InteractionTypeDialogSubmission:
|
||||||
|
log.Debugf("[slack] InteractionTypeDialogSubmission: %+v", callback)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
log.Debugf("[slack] unexpected callback type: %+v", callback)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.socket.Ack(*evt.Request, payload)
|
s.socket.Ack(*evt.Request, payload)
|
||||||
|
|
||||||
case socketmode.EventTypeHello:
|
case socketmode.EventTypeHello:
|
||||||
log.Debugf("hello command received: %+v", evt)
|
log.Debugf("[slack] hello command received: %+v", evt)
|
||||||
|
|
||||||
case socketmode.EventTypeSlashCommand:
|
case socketmode.EventTypeSlashCommand:
|
||||||
slashCmd, ok := evt.Data.(slack.SlashCommand)
|
slashCmd, ok := evt.Data.(slack.SlashCommand)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Debugf("ignored %+v", evt)
|
log.Debugf("[slack] ignored %+v", evt)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("slash command received: %+v", slashCmd)
|
log.Debugf("[slack] slash command received: %+v", slashCmd)
|
||||||
responder, exists := s.commandResponders[slashCmd.Command]
|
responder, exists := s.commandResponders[slashCmd.Command]
|
||||||
if !exists {
|
if !exists {
|
||||||
log.Errorf("command %s does not exist", slashCmd.Command)
|
log.Errorf("[slack] command %s does not exist", slashCmd.Command)
|
||||||
s.socket.Ack(*evt.Request)
|
s.socket.Ack(*evt.Request)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
session := s.findSession(evt, slashCmd.UserID)
|
session := s.loadSession(evt, slashCmd.UserID, slashCmd.ChannelID)
|
||||||
reply := s.newReply(session)
|
reply := s.newReply(session)
|
||||||
if err := responder(session, slashCmd.Text, reply); err != nil {
|
if err := responder(session, slashCmd.Text, reply); err != nil {
|
||||||
log.WithError(err).Errorf("responder returns error")
|
log.WithError(err).Errorf("[slack] responder returns error")
|
||||||
s.socket.Ack(*evt.Request)
|
s.socket.Ack(*evt.Request)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := reply.build()
|
payload := reply.build()
|
||||||
if payload == nil {
|
if payload == nil {
|
||||||
log.Warnf("reply returns nil payload")
|
log.Warnf("[slack] reply returns nil payload")
|
||||||
// ack with empty payload
|
// ack with empty payload
|
||||||
s.socket.Ack(*evt.Request)
|
s.socket.Ack(*evt.Request)
|
||||||
continue
|
continue
|
||||||
|
@ -293,8 +341,8 @@ func (s *Slack) listen() {
|
||||||
|
|
||||||
switch o := payload.(type) {
|
switch o := payload.(type) {
|
||||||
case *slack.ModalViewRequest:
|
case *slack.ModalViewRequest:
|
||||||
if resp, err := s.client.OpenView(slashCmd.TriggerID, *o); err != nil {
|
if resp, err := s.socket.OpenView(slashCmd.TriggerID, *o); err != nil {
|
||||||
log.WithError(err).Error("view open error, resp: %+v", resp)
|
log.WithError(err).Error("[slack] view open error, resp: %+v", resp)
|
||||||
}
|
}
|
||||||
s.socket.Ack(*evt.Request)
|
s.socket.Ack(*evt.Request)
|
||||||
default:
|
default:
|
||||||
|
@ -302,17 +350,17 @@ func (s *Slack) listen() {
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Debugf("unexpected event type received: %s", evt.Type)
|
log.Debugf("[slack] unexpected event type received: %s", evt.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Slack) findSession(evt socketmode.Event, userID string) *SlackSession {
|
func (s *Slack) loadSession(evt socketmode.Event, userID, channelID string) *SlackSession {
|
||||||
if session, ok := s.sessions[userID]; ok {
|
if session, ok := s.sessions[userID]; ok {
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
session := NewSlackSession(userID)
|
session := NewSlackSession(userID, channelID)
|
||||||
s.sessions[userID] = session
|
s.sessions[userID] = session
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
@ -367,6 +415,30 @@ func generateTextInputModalRequest(title string, prompt string, textFields ...Te
|
||||||
return &modalRequest
|
return &modalRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simplifyStateValues simplifies the multi-layer structured values into just name=value mapping
|
||||||
|
func simplifyStateValues(state *slack.ViewState) map[string]string {
|
||||||
|
var values = make(map[string]string)
|
||||||
|
|
||||||
|
if state == nil {
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
for blockID, fields := range state.Values {
|
||||||
|
_ = blockID
|
||||||
|
for fieldName, fieldValues := range fields {
|
||||||
|
values[fieldName] = fieldValues.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func takeOneValue(values map[string]string) (string, bool) {
|
||||||
|
for _, v := range values {
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
func toJson(v interface{}) string {
|
func toJson(v interface{}) string {
|
||||||
o, err := json.MarshalIndent(v, "", " ")
|
o, err := json.MarshalIndent(v, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user