github.com/blend/go-sdk@v1.20220411.3/slack/webhook_sender.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package slack
     9  
    10  import (
    11  	"context"
    12  	"encoding/json"
    13  	"io"
    14  	"net/http"
    15  
    16  	"github.com/blend/go-sdk/ex"
    17  	"github.com/blend/go-sdk/r2"
    18  	"github.com/blend/go-sdk/webutil"
    19  )
    20  
    21  const (
    22  	// ErrNon200 is the exception class when a non-200 is returned from slack.
    23  	ErrNon200 ex.Class = "slack; non-200 status code returned from remote"
    24  )
    25  
    26  var (
    27  	_ Sender = (*WebhookSender)(nil)
    28  )
    29  
    30  // New creates a new webhook sender.
    31  func New(cfg Config) *WebhookSender {
    32  	return &WebhookSender{
    33  		Transport: new(http.Transport),
    34  		Config:    cfg,
    35  	}
    36  }
    37  
    38  // WebhookSender sends slack webhooks.
    39  type WebhookSender struct {
    40  	Transport       *http.Transport
    41  	RequestDefaults []r2.Option
    42  	Config          Config
    43  }
    44  
    45  // MessageDefaults returns default message options.
    46  func (whs WebhookSender) MessageDefaults() []MessageOption {
    47  	return []MessageOption{
    48  		OptMessageUsernameOrDefault(whs.Config.Username),
    49  		OptMessageChannelOrDefault(whs.Config.Channel),
    50  		OptMessageIconEmojiOrDefault(whs.Config.IconEmoji),
    51  		OptMessageIconURLOrDefault(whs.Config.IconURL),
    52  	}
    53  }
    54  
    55  func (whs WebhookSender) send(ctx context.Context, message Message) (*http.Response, error) {
    56  	message = ApplyMessageOptions(message, whs.MessageDefaults()...)
    57  	options := append(whs.RequestDefaults,
    58  		r2.OptPost(),
    59  		r2.OptTransport(whs.Transport),
    60  		r2.OptJSONBody(message),
    61  		r2.OptHeaderValue(webutil.HeaderContentType, "application/json"),
    62  	)
    63  	return r2.New(whs.Config.Webhook, options...).Do()
    64  }
    65  
    66  // Send sends a slack hook.
    67  func (whs WebhookSender) Send(ctx context.Context, message Message) error {
    68  	res, err := whs.send(ctx, message)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	defer res.Body.Close()
    73  
    74  	if statusCode := res.StatusCode; statusCode < http.StatusOK || statusCode > 299 {
    75  		contents, _ := io.ReadAll(res.Body)
    76  		return ex.New(ErrNon200, ex.OptMessage(string(contents)))
    77  	}
    78  	return nil
    79  }
    80  
    81  // SendAndReadResponse sends a slack hook and returns the deserialized response
    82  func (whs WebhookSender) SendAndReadResponse(ctx context.Context, message Message) (*PostMessageResponse, error) {
    83  	res, err := whs.send(ctx, message)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	defer res.Body.Close()
    88  
    89  	var contents PostMessageResponse
    90  	err = json.NewDecoder(res.Body).Decode(&contents)
    91  	if err != nil {
    92  		return nil, ex.New(err)
    93  	}
    94  	if statusCode := res.StatusCode; statusCode < http.StatusOK || statusCode > 299 {
    95  		return &contents, ex.New(ErrNon200, ex.OptMessagef("%#v", contents))
    96  	}
    97  	return &contents, nil
    98  }
    99  
   100  // PostMessage posts a basic message to a given channel.
   101  func (whs WebhookSender) PostMessage(channel, messageText string, options ...MessageOption) error {
   102  	message := Message{
   103  		Channel: channel,
   104  		Text:    messageText,
   105  	}
   106  	for _, option := range options {
   107  		option(&message)
   108  	}
   109  	return whs.Send(context.Background(), message)
   110  }
   111  
   112  // PostMessageAndReadResponse posts a basic message to a given channel and returns the deserialized response
   113  func (whs WebhookSender) PostMessageAndReadResponse(channel, messageText string, options ...MessageOption) (*PostMessageResponse, error) {
   114  	message := Message{
   115  		Channel: channel,
   116  		Text:    messageText,
   117  	}
   118  	for _, option := range options {
   119  		option(&message)
   120  	}
   121  	return whs.SendAndReadResponse(context.Background(), message)
   122  }
   123  
   124  // PostMessageContext posts a basic message to a given chanel with a given context.
   125  func (whs WebhookSender) PostMessageContext(ctx context.Context, channel, messageText string, options ...MessageOption) error {
   126  	message := Message{
   127  		Channel: channel,
   128  		Text:    messageText,
   129  	}
   130  	for _, option := range options {
   131  		option(&message)
   132  	}
   133  	return whs.Send(ctx, message)
   134  }
   135  
   136  // PostMessageAndReadResponseContext posts a basic message to a given channel and returns the deserialized response
   137  func (whs WebhookSender) PostMessageAndReadResponseContext(ctx context.Context, channel, messageText string, options ...MessageOption) (*PostMessageResponse, error) {
   138  	message := Message{
   139  		Channel: channel,
   140  		Text:    messageText,
   141  	}
   142  	for _, option := range options {
   143  		option(&message)
   144  	}
   145  	return whs.SendAndReadResponse(ctx, message)
   146  }