interact: support slack modal view request

This commit is contained in:
c9s 2022-01-21 00:45:43 +08:00
parent 16688be64d
commit 08241bffbd
4 changed files with 100 additions and 51 deletions

View File

@ -52,7 +52,11 @@ func (it *AuthInteract) Commands(interact *Interact) {
it.OneTimePasswordKey = key
}
interact.Command("/auth", "authorize", func(reply Reply, session Session) error {
reply.Message("Enter your authentication token")
reply.InputText("Authentication Token", TextField{
Label: "Authentication Token",
Name: "token",
PlaceHolder: "Enter Your Authentication Token",
})
session.SetAuthorizing(true)
return nil
}).Next(func(token string, reply Reply) error {

View File

@ -6,6 +6,28 @@ type Button struct {
Value string
}
type TextField struct {
// Name is the form field name
Name string
// Label is the field label
Label string
// PlaceHolder is the sample text in the text input
PlaceHolder string
}
type Option struct {
// Name is the form field name
Name string
// Label is the option label for display
Label string
// Value is the option value
Value string
}
type Reply interface {
// Send sends the message directly to the client's session
Send(message string)
@ -16,7 +38,12 @@ type Reply interface {
// AddButton adds the button to the reply
AddButton(text string, name, value string)
RequireTextInput(title, message string, textFields ...TextField)
InputText(prompt string, textFields ...TextField)
Choose(prompt string, options ...Option)
// Confirm shows the confirm dialog or confirm button in the user interface
// Confirm(prompt string)
// RemoveKeyboard hides the keyboard from the client user interface
RemoveKeyboard()

View File

@ -25,6 +25,8 @@ type SlackReply struct {
message string
buttons []Button
textInputModalViewRequest *slack.ModalViewRequest
}
func (reply *SlackReply) Send(message string) {
@ -39,10 +41,18 @@ func (reply *SlackReply) Send(message string) {
}
}
func (reply *SlackReply) Choose(prompt string, options ...Option) {
}
func (reply *SlackReply) Message(message string) {
reply.message = message
}
func (reply *SlackReply) InputText(prompt string, textFields ...TextField) {
reply.message = prompt
reply.textInputModalViewRequest = generateTextInputModalRequest(prompt, prompt, textFields...)
}
// RemoveKeyboard is not supported by Slack
func (reply *SlackReply) RemoveKeyboard() {}
@ -54,7 +64,15 @@ func (reply *SlackReply) AddButton(text string, name string, value string) {
})
}
func (reply *SlackReply) build() map[string]interface{} {
func (reply *SlackReply) build() interface{} {
if reply.textInputModalViewRequest != nil {
return reply.textInputModalViewRequest
}
if len(reply.message) > 0 {
return reply.message
}
var blocks []slack.Block
blocks = append(blocks, slack.NewSectionBlock(
@ -247,28 +265,37 @@ func (s *Slack) listen() {
}
log.Debugf("slash command received: %+v", slashCmd)
if responder, exists := s.commandResponders[slashCmd.Command]; exists {
responder, exists := s.commandResponders[slashCmd.Command]
if !exists {
log.Errorf("command %s does not exist", slashCmd.Command)
s.socket.Ack(*evt.Request)
continue
}
session := s.findSession(evt, slashCmd.UserID)
reply := s.newReply(session)
if err := responder(session, slashCmd.Text, reply); err != nil {
log.WithError(err).Errorf("responder returns error")
s.socket.Ack(*evt.Request)
continue
}
req := generateTextInputModalRequest("Authentication", "Please enter your code", TextField{
Label: "First Name",
Name: "first_name",
PlaceHolder: "Enter your first name",
})
// s.socket.Ack(*evt.Request, req)
if resp, err := s.client.OpenView(slashCmd.TriggerID, req); err != nil {
log.WithError(err).Error("view open error, resp: %+v", resp)
payload := reply.build()
if payload == nil {
log.Warnf("reply returns nil payload")
// ack with empty payload
s.socket.Ack(*evt.Request)
continue
}
payload := reply.build()
s.socket.Ack(*evt.Request, payload)
} else {
log.Errorf("command %s does not exist", slashCmd.Command)
switch o := payload.(type) {
case *slack.ModalViewRequest:
if resp, err := s.client.OpenView(slashCmd.TriggerID, *o); err != nil {
log.WithError(err).Error("view open error, resp: %+v", resp)
}
s.socket.Ack(*evt.Request)
default:
s.socket.Ack(*evt.Request, o)
}
default:
@ -301,18 +328,7 @@ func (s *Slack) Start(ctx context.Context) {
}
}
type TextField struct {
// Label is the field label
Label string
// Name is the form field name
Name string
// PlaceHolder is the sample text in the text input
PlaceHolder string
}
func generateTextInputModalRequest(title string, prompt string, textFields ...TextField) slack.ModalViewRequest {
func generateTextInputModalRequest(title string, prompt string, textFields ...TextField) *slack.ModalViewRequest {
// create a ModalViewRequest with a header and two inputs
titleText := slack.NewTextBlockObject("plain_text", title, false, false)
closeText := slack.NewTextBlockObject("plain_text", "Close", false, false)
@ -328,12 +344,12 @@ func generateTextInputModalRequest(title string, prompt string, textFields ...Te
}
for _, textField := range textFields {
firstNameText := slack.NewTextBlockObject("plain_text", textField.Label, false, false)
firstNamePlaceholder := slack.NewTextBlockObject("plain_text", textField.PlaceHolder, false, false)
firstNameElement := slack.NewPlainTextInputBlockElement(firstNamePlaceholder, textField.Name)
labelObject := slack.NewTextBlockObject("plain_text", textField.Label, false, false)
placeHolderObject := slack.NewTextBlockObject("plain_text", textField.PlaceHolder, false, false)
textInputObject := slack.NewPlainTextInputBlockElement(placeHolderObject, textField.Name)
// Notice that blockID is a unique identifier for a block
firstName := slack.NewInputBlock(textField.Name, firstNameText, firstNameElement)
blocks.BlockSet = append(blocks.BlockSet, firstName)
inputBlock := slack.NewInputBlock(textField.Name, labelObject, textInputObject)
blocks.BlockSet = append(blocks.BlockSet, inputBlock)
}
var modalRequest slack.ModalViewRequest
@ -342,5 +358,5 @@ func generateTextInputModalRequest(title string, prompt string, textFields ...Te
modalRequest.Close = closeText
modalRequest.Submit = submitText
modalRequest.Blocks = blocks
return modalRequest
return &modalRequest
}

View File

@ -57,11 +57,10 @@ type TelegramReply struct {
message string
menu *telebot.ReplyMarkup
buttons [][]telebot.Btn
buttons []telebot.Btn
set bool
}
func (r *TelegramReply) Send(message string) {
checkSendErr(r.bot.Send(r.session.Chat, message))
}
@ -71,8 +70,12 @@ func (r *TelegramReply) Message(message string) {
r.set = true
}
func (r *TelegramReply) RequireTextInput(title, message string, textFields ...TextField) {
r.message = message
func (r *TelegramReply) Choose(prompt string, options ...Option) {
}
func (r *TelegramReply) InputText(prompt string, textFields ...TextField) {
r.message = prompt
}
func (r *TelegramReply) RemoveKeyboard() {
@ -82,17 +85,16 @@ func (r *TelegramReply) RemoveKeyboard() {
func (r *TelegramReply) AddButton(text string, name string, value string) {
var button = r.menu.Text(text)
if len(r.buttons) == 0 {
r.buttons = append(r.buttons, []telebot.Btn{})
}
r.buttons[len(r.buttons)-1] = append(r.buttons[len(r.buttons)-1], button)
r.buttons = append(r.buttons, button)
r.set = true
}
func (r *TelegramReply) build() {
var rows []telebot.Row
for _, buttons := range r.buttons {
rows = append(rows, telebot.Row(buttons))
for _, button := range r.buttons {
rows = append(rows, telebot.Row{
button,
})
}
r.menu.Reply(rows...)
}