Repository: slack-go/slack
Pull request #1249 (2023-12-20 10:14:54)

Add block element type FileInput
Make [file_input](https://api.slack.com/reference/block-kit/block-elements#file_input) element is supported.

> Related to https://github.com/slack-go/slack/issues/1246

##### Pull Request Guidelines

These are recommendations for pull requests.
They are strictly guidelines to help manage expectations.

##### PR preparation

Run `make pr-prep` from the root of the repository to run formatting, linting and tests.

> All `passed`

##### Should this be an issue instead

- [ ] is it a convenience method? (no new functionality, streamlines some use case)
- [ ] exposes a previously private type, const, method, etc.
- [ ] is it application specific (caching, retry logic, rate limiting, etc)
- [ ] is it performance related.

##### API changes

* Add `slack.NewFileInputBlockElement()` to create the file input element. 
* Add `Files` field to `BlockAction`

###### Examples of API changes that do not meet guidelines:

N/A


Patch:
diff --git a/block.go b/block.go
index c28377a..2351908 100644
--- a/block.go
+++ b/block.go
@@ -18,7 +18,6 @@ const (
 	MBTInput    MessageBlockType = "input"
 	MBTHeader   MessageBlockType = "header"
 	MBTRichText MessageBlockType = "rich_text"
-	MBTVideo    MessageBlockType = "video"
 )
 
 // Block defines an interface all block types should implement
@@ -40,6 +39,7 @@ type BlockAction struct {
 	Type                  ActionType          `json:"type"`
 	Text                  TextBlockObject     `json:"text"`
 	Value                 string              `json:"value"`
+	Files                 []File              `json:"files"`
 	ActionTs              string              `json:"action_ts"`
 	SelectedOption        OptionBlockObject   `json:"selected_option"`
 	SelectedOptions       []OptionBlockObject `json:"selected_options"`
diff --git a/block_conv.go b/block_conv.go
index 57fd85c..cc84d7a 100644
--- a/block_conv.go
+++ b/block_conv.go
@@ -2,6 +2,7 @@ package slack
 
 import (
 	"encoding/json"
+	"errors"
 	"fmt"
 )
 
@@ -65,12 +66,8 @@ func (b *Blocks) UnmarshalJSON(data []byte) error {
 			block = &InputBlock{}
 		case "rich_text":
 			block = &RichTextBlock{}
-		case "rich_text_input":
-			block = &RichTextBlock{}
 		case "section":
 			block = &SectionBlock{}
-		case "video":
-			block = &VideoBlock{}
 		default:
 			block = &UnknownBlock{}
 		}
@@ -117,8 +114,6 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error {
 		e = &DateTimePickerBlockElement{}
 	case "plain_text_input":
 		e = &PlainTextInputBlockElement{}
-	case "rich_text_input":
-		e = &RichTextInputBlockElement{}
 	case "email_text_input":
 		e = &EmailTextInputBlockElement{}
 	case "url_text_input":
@@ -135,8 +130,10 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error {
 		e = &RadioButtonsBlockElement{}
 	case "number_input":
 		e = &NumberInputBlockElement{}
+	case "file_input":
+		e = &FileInputBlockElement{}
 	default:
-		return fmt.Errorf("unsupported block element type %v", s.TypeVal)
+		return errors.New("unsupported block element type")
 	}
 
 	if err := json.Unmarshal(a.Element, e); err != nil {
@@ -201,8 +198,6 @@ func (b *BlockElements) UnmarshalJSON(data []byte) error {
 			blockElement = &DateTimePickerBlockElement{}
 		case "plain_text_input":
 			blockElement = &PlainTextInputBlockElement{}
-		case "rich_text_input":
-			blockElement = &RichTextInputBlockElement{}
 		case "email_text_input":
 			blockElement = &EmailTextInputBlockElement{}
 		case "url_text_input":
@@ -305,12 +300,6 @@ func (a *Accessory) UnmarshalJSON(data []byte) error {
 			return err
 		}
 		a.PlainTextInputElement = element.(*PlainTextInputBlockElement)
-	case "rich_text_input":
-		element, err := unmarshalBlockElement(r, &RichTextInputBlockElement{})
-		if err != nil {
-			return err
-		}
-		a.RichTextInputElement = element.(*RichTextInputBlockElement)
 	case "radio_buttons":
 		element, err := unmarshalBlockElement(r, &RadioButtonsBlockElement{})
 		if err != nil {
@@ -442,7 +431,7 @@ func (e *ContextElements) UnmarshalJSON(data []byte) error {
 
 			e.Elements = append(e.Elements, elem.(*ImageBlockElement))
 		default:
-			return fmt.Errorf("unsupported context element type %v", contextElementType)
+			return errors.New("unsupported context element type")
 		}
 	}
 
diff --git a/block_element.go b/block_element.go
index fba00c5..1077db7 100644
--- a/block_element.go
+++ b/block_element.go
@@ -12,10 +12,10 @@ const (
 	METDatetimepicker MessageElementType = "datetimepicker"
 	METPlainTextInput MessageElementType = "plain_text_input"
 	METRadioButtons   MessageElementType = "radio_buttons"
-	METRichTextInput  MessageElementType = "rich_text_input"
 	METEmailTextInput MessageElementType = "email_text_input"
 	METURLTextInput   MessageElementType = "url_text_input"
 	METNumber         MessageElementType = "number_input"
+	METFileInput      MessageElementType = "file_input"
 
 	MixedElementImage MixedElementType = "mixed_image"
 	MixedElementText  MixedElementType = "mixed_text"
@@ -52,7 +52,6 @@ type Accessory struct {
 	DatePickerElement          *DatePickerBlockElement
 	TimePickerElement          *TimePickerBlockElement
 	PlainTextInputElement      *PlainTextInputBlockElement
-	RichTextInputElement       *RichTextInputBlockElement
 	RadioButtonsElement        *RadioButtonsBlockElement
 	SelectElement              *SelectBlockElement
 	MultiSelectElement         *MultiSelectBlockElement
@@ -75,8 +74,6 @@ func NewAccessory(element BlockElement) *Accessory {
 		return &Accessory{TimePickerElement: element.(*TimePickerBlockElement)}
 	case *PlainTextInputBlockElement:
 		return &Accessory{PlainTextInputElement: element.(*PlainTextInputBlockElement)}
-	case *RichTextInputBlockElement:
-		return &Accessory{RichTextInputElement: element.(*RichTextInputBlockElement)}
 	case *RadioButtonsBlockElement:
 		return &Accessory{RadioButtonsElement: element.(*RadioButtonsBlockElement)}
 	case *SelectBlockElement:
@@ -181,12 +178,6 @@ func (s *ButtonBlockElement) WithConfirm(confirm *ConfirmationBlockObject) *Butt
 	return s
 }
 
-// WithURL adds a URL for the button to link to and returns the modified ButtonBlockElement
-func (s *ButtonBlockElement) WithURL(url string) *ButtonBlockElement {
-	s.URL = url
-	return s
-}
-
 // NewButtonBlockElement returns an instance of a new button element to be used within a block
 func NewButtonBlockElement(actionID, value string, text *TextBlockObject) *ButtonBlockElement {
 	return &ButtonBlockElement{
@@ -519,32 +510,6 @@ func NewPlainTextInputBlockElement(placeholder *TextBlockObject, actionID string
 	}
 }
 
-// RichTextInputBlockElement creates a field where allows users to enter formatted text
-// in a WYSIWYG composer, offering the same messaging writing experience as in Slack
-// More Information: https://api.slack.com/reference/block-kit/block-elements#rich_text_input
-type RichTextInputBlockElement struct {
-	Type                 MessageElementType    `json:"type"`
-	ActionID             string                `json:"action_id,omitempty"`
-	Placeholder          *TextBlockObject      `json:"placeholder,omitempty"`
-	InitialValue         string                `json:"initial_value,omitempty"`
-	DispatchActionConfig *DispatchActionConfig `json:"dispatch_action_config,omitempty"`
-	FocusOnLoad          bool                  `json:"focus_on_load,omitempty"`
-}
-
-// ElementType returns the type of the Element
-func (s RichTextInputBlockElement) ElementType() MessageElementType {
-	return s.Type
-}
-
-// NewRichTextInputBlockElement returns an instance of a rich-text input element
-func NewRichTextInputBlockElement(placeholder *TextBlockObject, actionID string) *RichTextInputBlockElement {
-	return &RichTextInputBlockElement{
-		Type:        METRichTextInput,
-		ActionID:    actionID,
-		Placeholder: placeholder,
-	}
-}
-
 // CheckboxGroupsBlockElement defines an element which allows users to choose
 // one or more items from a list of possible options.
 //
@@ -627,3 +592,40 @@ func NewNumberInputBlockElement(placeholder *TextBlockObject, actionID string, i
 		IsDecimalAllowed: isDecimalAllowed,
 	}
 }
+
+// FileInputBlockElement creates a field where a user can upload a file.
+//
+// File input elements are currently only available in modals.
+//
+// More Information: https://api.slack.com/reference/block-kit/block-elements#file_input
+type FileInputBlockElement struct {
+	Type      MessageElementType `json:"type"`
+	ActionID  string             `json:"action_id,omitempty"`
+	FileTypes []string           `json:"filetypes,omitempty"`
+	MaxFiles  int                `json:"max_files,omitempty"`
+}
+
+// ElementType returns the type of the Element
+func (s FileInputBlockElement) ElementType() MessageElementType {
+	return s.Type
+}
+
+// NewFileInputBlockElement returns an instance of a file input element
+func NewFileInputBlockElement(actionID string) *FileInputBlockElement {
+	return &FileInputBlockElement{
+		Type:     METFileInput,
+		ActionID: actionID,
+	}
+}
+
+// WithFileTypes sets the file types that can be uploaded
+func (s *FileInputBlockElement) WithFileTypes(fileTypes ...string) *FileInputBlockElement {
+	s.FileTypes = fileTypes
+	return s
+}
+
+// WithMaxFiles sets the maximum number of files that can be uploaded
+func (s *FileInputBlockElement) WithMaxFiles(maxFiles int) *FileInputBlockElement {
+	s.MaxFiles = maxFiles
+	return s
+}
diff --git a/block_rich_text.go b/block_rich_text.go
index aea1597..01e5cdb 100644
--- a/block_rich_text.go
+++ b/block_rich_text.go
@@ -40,9 +40,6 @@ func (e *RichTextBlock) UnmarshalJSON(b []byte) error {
 		switch s.Type {
 		case RTESection:
 			elem = &RichTextSection{}
-		case RTEList:
-			elem = &RichTextList{}
-
 		default:
 			elems = append(elems, &RichTextUnknown{
 				Type: s.Type,
@@ -95,88 +92,12 @@ func (u RichTextUnknown) RichTextElementType() RichTextElementType {
 	return u.Type
 }
 
-type RichTextListElementType string
-
-const (
-	RTEListOrdered RichTextListElementType = "ordered"
-	RTEListBullet  RichTextListElementType = "bullet"
-)
-
-type RichTextList struct {
-	Type     RichTextElementType     `json:"type"`
-	Elements []RichTextElement       `json:"elements"`
-	Style    RichTextListElementType `json:"style"`
-	Indent   int                     `json:"indent"`
-}
-
-// NewRichTextList returns a new rich text list element.
-func NewRichTextList(style RichTextListElementType, indent int, elements ...RichTextElement) *RichTextList {
-	return &RichTextList{
-		Type:     RTEList,
-		Elements: elements,
-		Style:    style,
-		Indent:   indent,
-	}
-}
-
-// ElementType returns the type of the Element
-func (s RichTextList) RichTextElementType() RichTextElementType {
-	return s.Type
-}
-
-func (e *RichTextList) UnmarshalJSON(b []byte) error {
-	var raw struct {
-		RawElements []json.RawMessage       `json:"elements"`
-		Style       RichTextListElementType `json:"style"`
-		Indent      int                     `json:"indent"`
-	}
-	if string(b) == "{}" {
-		return nil
-	}
-	if err := json.Unmarshal(b, &raw); err != nil {
-		return err
-	}
-	elems := make([]RichTextElement, 0, len(raw.RawElements))
-	for _, r := range raw.RawElements {
-		var s struct {
-			Type RichTextElementType `json:"type"`
-		}
-		if err := json.Unmarshal(r, &s); err != nil {
-			return err
-		}
-		var elem RichTextElement
-		switch s.Type {
-		case RTESection:
-			elem = &RichTextSection{}
-		case RTEList:
-			elem = &RichTextList{}
-		default:
-			elems = append(elems, &RichTextUnknown{
-				Type: s.Type,
-				Raw:  string(r),
-			})
-			continue
-		}
-		if err := json.Unmarshal(r, elem); err != nil {
-			return err
-		}
-		elems = append(elems, elem)
-	}
-	*e = RichTextList{
-		Type:     RTEList,
-		Elements: elems,
-		Style:    raw.Style,
-		Indent:   raw.Indent,
-	}
-	return nil
-}
-
 type RichTextSection struct {
 	Type     RichTextElementType      `json:"type"`
 	Elements []RichTextSectionElement `json:"elements"`
 }
 
-// RichTextElementType returns the type of the Element
+// ElementType returns the type of the Element
 func (s RichTextSection) RichTextElementType() RichTextElementType {
 	return s.Type
 }
diff --git a/block_video.go b/block_video.go
deleted file mode 100644
index 322c614..0000000
--- a/block_video.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package slack
-
-// VideoBlock defines data required to display a video as a block element
-//
-// More Information: https://api.slack.com/reference/block-kit/blocks#video
-type VideoBlock struct {
-	Type            MessageBlockType `json:"type"`
-	VideoURL        string           `json:"video_url"`
-	ThumbnailURL    string           `json:"thumbnail_url"`
-	AltText         string           `json:"alt_text"`
-	Title           *TextBlockObject `json:"title"`
-	BlockID         string           `json:"block_id,omitempty"`
-	TitleURL        string           `json:"title_url,omitempty"`
-	AuthorName      string           `json:"author_name,omitempty"`
-	ProviderName    string           `json:"provider_name,omitempty"`
-	ProviderIconURL string           `json:"provider_icon_url,omitempty"`
-	Description     *TextBlockObject `json:"description,omitempty"`
-}
-
-// BlockType returns the type of the block
-func (s VideoBlock) BlockType() MessageBlockType {
-	return s.Type
-}
-
-// NewVideoBlock returns an instance of a new Video Block type
-func NewVideoBlock(videoURL, thumbnailURL, altText, blockID string, title *TextBlockObject) *VideoBlock {
-	return &VideoBlock{
-		Type:         MBTVideo,
-		VideoURL:     videoURL,
-		ThumbnailURL: thumbnailURL,
-		AltText:      altText,
-		BlockID:      blockID,
-		Title:        title,
-	}
-}
-
-// WithAuthorName sets the author name for the VideoBlock
-func (s *VideoBlock) WithAuthorName(authorName string) *VideoBlock {
-	s.AuthorName = authorName
-	return s
-}
-
-// WithTitleURL sets the title URL for the VideoBlock
-func (s *VideoBlock) WithTitleURL(titleURL string) *VideoBlock {
-	s.TitleURL = titleURL
-	return s
-}
-
-// WithDescription sets the description for the VideoBlock
-func (s *VideoBlock) WithDescription(description *TextBlockObject) *VideoBlock {
-	s.Description = description
-	return s
-}
-
-// WithProviderIconURL sets the provider icon URL for the VideoBlock
-func (s *VideoBlock) WithProviderIconURL(providerIconURL string) *VideoBlock {
-	s.ProviderIconURL = providerIconURL
-	return s
-}
-
-// WithProviderName sets the provider name for the VideoBlock
-func (s *VideoBlock) WithProviderName(providerName string) *VideoBlock {
-	s.ProviderName = providerName
-	return s
-}
diff --git a/conversation.go b/conversation.go
index 787a961..1a63c53 100644
--- a/conversation.go
+++ b/conversation.go
@@ -22,7 +22,6 @@ type Conversation struct {
 	IsIM               bool     `json:"is_im"`
 	IsExtShared        bool     `json:"is_ext_shared"`
 	IsOrgShared        bool     `json:"is_org_shared"`
-	IsGlobalShared     bool     `json:"is_global_shared"`
 	IsPendingExtShared bool     `json:"is_pending_ext_shared"`
 	IsPrivate          bool     `json:"is_private"`
 	IsMpIM             bool     `json:"is_mpim"`
@@ -31,9 +30,6 @@ type Conversation struct {
 	NumMembers         int      `json:"num_members"`
 	Priority           float64  `json:"priority"`
 	User               string   `json:"user"`
-	ConnectedTeamIDs   []string `json:"connected_team_ids,omitempty"`
-	SharedTeamIDs      []string `json:"shared_team_ids,omitempty"`
-	InternalTeamIDs    []string `json:"internal_team_ids,omitempty"`
 
 	// TODO support pending_shared
 	// TODO support previous_names
diff --git a/examples/blocks/README.md b/examples/blocks/README.md
index 2e5d81f..588f248 100644
--- a/examples/blocks/README.md
+++ b/examples/blocks/README.md
@@ -53,7 +53,7 @@ To preview this block on the builder website, you should copy just the contents
 The first example demonstrates usage of Sections, Fields and Action buttons.  You can view the [Approval Example](https://api.slack.com/tools/block-kit-builder?blocks=%5B%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22You%20have%20a%20new%20request%3A%5Cn*%3CfakeLink.toEmployeeProfile.com%7CFred%20Enriquez%20-%20New%20device%20request%3E*%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22fields%22%3A%20%5B%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%09%22text%22%3A%20%22*Type%3A*%5CnComputer%20(laptop)%22%0A%09%09%09%7D%2C%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%09%22text%22%3A%20%22*When%3A*%5CnSubmitted%20Aut%2010%22%0A%09%09%09%7D%2C%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%09%22text%22%3A%20%22*Last%20Update%3A*%5CnMar%2010%2C%202015%20(3%20years%2C%205%20months)%22%0A%09%09%09%7D%2C%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%09%22text%22%3A%20%22*Reason%3A*%5CnAll%20vowel%20keys%20aren%27t%20working.%22%0A%09%09%09%7D%2C%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%09%22text%22%3A%20%22*Specs%3A*%5Cn%5C%22Cheetah%20Pro%2015%5C%22%20-%20Fast%2C%20really%20fast%5C%22%22%0A%09%09%09%7D%0A%09%09%5D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22actions%22%2C%0A%09%09%22elements%22%3A%20%5B%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22button%22%2C%0A%09%09%09%09%22text%22%3A%20%7B%0A%09%09%09%09%09%22type%22%3A%20%22plain_text%22%2C%0A%09%09%09%09%09%22emoji%22%3A%20true%2C%0A%09%09%09%09%09%22text%22%3A%20%22Approve%22%0A%09%09%09%09%7D%2C%0A%09%09%09%09%22value%22%3A%20%22click_me_123%22%0A%09%09%09%7D%2C%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22button%22%2C%0A%09%09%09%09%22text%22%3A%20%7B%0A%09%09%09%09%09%22type%22%3A%20%22plain_text%22%2C%0A%09%09%09%09%09%22emoji%22%3A%20true%2C%0A%09%09%09%09%09%22text%22%3A%20%22Deny%22%0A%09%09%09%09%7D%2C%0A%09%09%09%09%22value%22%3A%20%22click_me_123%22%0A%09%09%09%7D%0A%09%09%5D%0A%09%7D%0A%5D) on the block kit builder website. This example can be generated with the function named `exampleOne`.
 
 #### Example 2 - Approval - With Images
-The second example adds additional complexity by introducing images as accessories to main blocks of text. You can view this [Approval Example with Images](https://api.slack.com/tools/block-kit-builder?blocks=%5B%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22You%20have%20a%20new%20request%3A%5Cn*%3Cgoogle.com%7CFred%20Enriquez%20-%20Time%20Off%20request%3E*%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22*Type%3A*%5CnPaid%20time%20off%5Cn*When%3A*%5CnAug%2010-Aug%2013%5Cn*Hours%3A*%2016.0%20(2%20days)%5Cn*Remaining%20balance%3A*%2032.0%20hours%20(4%20days)%5Cn*Comments%3A*%20%5C%22Family%20in%20town%2C%20going%20camping!%5C%22%22%0A%09%09%7D%2C%0A%09%09%22accessory%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22image%22%2C%0A%09%09%09%22image_url%22%3A%20%22https%3A%2F%2Fapi.slack.com%2Fimg%2Fblocks%2Fbkb_template_images%2FapprovalsNewDevice.png%22%2C%0A%09%09%09%22alt_text%22%3A%20%22computer%20thumbnail%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22actions%22%2C%0A%09%09%22elements%22%3A%20%5B%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22button%22%2C%0A%09%09%09%09%22text%22%3A%20%7B%0A%09%09%09%09%09%22type%22%3A%20%22plain_text%22%2C%0A%09%09%09%09%09%22emoji%22%3A%20true%2C%0A%09%09%09%09%09%22text%22%3A%20%22Approve%22%0A%09%09%09%09%7D%2C%0A%09%09%09%09%22value%22%3A%20%22click_me_123%22%0A%09%09%09%7D%2C%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22button%22%2C%0A%09%09%09%09%22text%22%3A%20%7B%0A%09%09%09%09%09%22type%22%3A%20%22plain_text%22%2C%0A%09%09%09%09%09%22emoji%22%3A%20true%2C%0A%09%09%09%09%09%22text%22%3A%20%22Deny%22%0A%09%09%09%09%7D%2C%0A%09%09%09%09%22value%22%3A%20%22click_me_123%22%0A%09%09%09%7D%0A%09%09%5D%0A%09%7D%0A%5D) on the block kit builder website. This example can be generated with the function named `exampleTwo`.
+The secoond example adds additional complexity by introducing images as accessories to main blocks of text. You can view this [Approval Example with Images](https://api.slack.com/tools/block-kit-builder?blocks=%5B%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22You%20have%20a%20new%20request%3A%5Cn*%3Cgoogle.com%7CFred%20Enriquez%20-%20Time%20Off%20request%3E*%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22*Type%3A*%5CnPaid%20time%20off%5Cn*When%3A*%5CnAug%2010-Aug%2013%5Cn*Hours%3A*%2016.0%20(2%20days)%5Cn*Remaining%20balance%3A*%2032.0%20hours%20(4%20days)%5Cn*Comments%3A*%20%5C%22Family%20in%20town%2C%20going%20camping!%5C%22%22%0A%09%09%7D%2C%0A%09%09%22accessory%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22image%22%2C%0A%09%09%09%22image_url%22%3A%20%22https%3A%2F%2Fapi.slack.com%2Fimg%2Fblocks%2Fbkb_template_images%2FapprovalsNewDevice.png%22%2C%0A%09%09%09%22alt_text%22%3A%20%22computer%20thumbnail%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22actions%22%2C%0A%09%09%22elements%22%3A%20%5B%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22button%22%2C%0A%09%09%09%09%22text%22%3A%20%7B%0A%09%09%09%09%09%22type%22%3A%20%22plain_text%22%2C%0A%09%09%09%09%09%22emoji%22%3A%20true%2C%0A%09%09%09%09%09%22text%22%3A%20%22Approve%22%0A%09%09%09%09%7D%2C%0A%09%09%09%09%22value%22%3A%20%22click_me_123%22%0A%09%09%09%7D%2C%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22button%22%2C%0A%09%09%09%09%22text%22%3A%20%7B%0A%09%09%09%09%09%22type%22%3A%20%22plain_text%22%2C%0A%09%09%09%09%09%22emoji%22%3A%20true%2C%0A%09%09%09%09%09%22text%22%3A%20%22Deny%22%0A%09%09%09%09%7D%2C%0A%09%09%09%09%22value%22%3A%20%22click_me_123%22%0A%09%09%09%7D%0A%09%09%5D%0A%09%7D%0A%5D) on the block kit builder website. This example can be generated with the function named `exampleTwo`.
 
 #### Example 3 - Notifications
 This example shows how to add actions to your block that will trigger an interactive message to your application.  You can view the rendered example for [Notifications](https://api.slack.com/tools/block-kit-builder?blocks=%5B%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22plain_text%22%2C%0A%09%09%09%22emoji%22%3A%20true%2C%0A%09%09%09%22text%22%3A%20%22Looks%20like%20you%20have%20a%20scheduling%20conflict%20with%20this%20event%3A%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22divider%22%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22*%3CfakeLink.toUserProfiles.com%7CIris%20%2F%20Zelda%201-1%3E*%5CnTuesday%2C%20January%2021%204%3A00-4%3A30pm%5CnBuilding%202%20-%20Havarti%20Cheese%20(3)%5Cn2%20guests%22%0A%09%09%7D%2C%0A%09%09%22accessory%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22image%22%2C%0A%09%09%09%22image_url%22%3A%20%22https%3A%2F%2Fapi.slack.com%2Fimg%2Fblocks%2Fbkb_template_images%2Fnotifications.png%22%2C%0A%09%09%09%22alt_text%22%3A%20%22calendar%20thumbnail%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22context%22%2C%0A%09%09%22elements%22%3A%20%5B%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22image%22%2C%0A%09%09%09%09%22image_url%22%3A%20%22https%3A%2F%2Fapi.slack.com%2Fimg%2Fblocks%2Fbkb_template_images%2FnotificationsWarningIcon.png%22%2C%0A%09%09%09%09%22alt_text%22%3A%20%22notifications%20warning%20icon%22%0A%09%09%09%7D%2C%0A%09%09%09%7B%0A%09%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%09%22text%22%3A%20%22*Conflicts%20with%20Team%20Huddle%3A%204%3A15-4%3A30pm*%22%0A%09%09%09%7D%0A%09%09%5D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22divider%22%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22*Propose%20a%20new%20time%3A*%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22*Today%20-%204%3A30-5pm*%5CnEveryone%20is%20available%3A%20%40iris%2C%20%40zelda%22%0A%09%09%7D%2C%0A%09%09%22accessory%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22button%22%2C%0A%09%09%09%22text%22%3A%20%7B%0A%09%09%09%09%22type%22%3A%20%22plain_text%22%2C%0A%09%09%09%09%22emoji%22%3A%20true%2C%0A%09%09%09%09%22text%22%3A%20%22Choose%22%0A%09%09%09%7D%2C%0A%09%09%09%22value%22%3A%20%22click_me_123%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22*Tomorrow%20-%204-4%3A30pm*%5CnEveryone%20is%20available%3A%20%40iris%2C%20%40zelda%22%0A%09%09%7D%2C%0A%09%09%22accessory%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22button%22%2C%0A%09%09%09%22text%22%3A%20%7B%0A%09%09%09%09%22type%22%3A%20%22plain_text%22%2C%0A%09%09%09%09%22emoji%22%3A%20true%2C%0A%09%09%09%09%22text%22%3A%20%22Choose%22%0A%09%09%09%7D%2C%0A%09%09%09%22value%22%3A%20%22click_me_123%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22*Tomorrow%20-%206-6%3A30pm*%5CnSome%20people%20aren%27t%20available%3A%20%40iris%2C%20~%40zelda~%22%0A%09%09%7D%2C%0A%09%09%22accessory%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22button%22%2C%0A%09%09%09%22text%22%3A%20%7B%0A%09%09%09%09%22type%22%3A%20%22plain_text%22%2C%0A%09%09%09%09%22emoji%22%3A%20true%2C%0A%09%09%09%09%22text%22%3A%20%22Choose%22%0A%09%09%09%7D%2C%0A%09%09%09%22value%22%3A%20%22click_me_123%22%0A%09%09%7D%0A%09%7D%2C%0A%09%7B%0A%09%09%22type%22%3A%20%22section%22%2C%0A%09%09%22text%22%3A%20%7B%0A%09%09%09%22type%22%3A%20%22mrkdwn%22%2C%0A%09%09%09%22text%22%3A%20%22*%3Cfakelink.ToMoreTimes.com%7CShow%20more%20times%3E*%22%0A%09%09%7D%0A%09%7D%0A%5D) on the block builder website. Refer to the function `exampleThree` for details on how this block can be generated.
diff --git a/interactions.go b/interactions.go
index 8c64147..e362caa 100644
--- a/interactions.go
+++ b/interactions.go
@@ -33,30 +33,29 @@ const (
 
 // InteractionCallback is sent from slack when a user interactions with a button or dialog.
 type InteractionCallback struct {
-	Type                InteractionType         `json:"type"`
-	Token               string                  `json:"token"`
-	CallbackID          string                  `json:"callback_id"`
-	ResponseURL         string                  `json:"response_url"`
-	TriggerID           string                  `json:"trigger_id"`
-	ActionTs            string                  `json:"action_ts"`
-	Team                Team                    `json:"team"`
-	Channel             Channel                 `json:"channel"`
-	User                User                    `json:"user"`
-	OriginalMessage     Message                 `json:"original_message"`
-	Message             Message                 `json:"message"`
-	Name                string                  `json:"name"`
-	Value               string                  `json:"value"`
-	MessageTs           string                  `json:"message_ts"`
-	AttachmentID        string                  `json:"attachment_id"`
-	ActionCallback      ActionCallbacks         `json:"actions"`
-	View                View                    `json:"view"`
-	ActionID            string                  `json:"action_id"`
-	APIAppID            string                  `json:"api_app_id"`
-	BlockID             string                  `json:"block_id"`
-	Container           Container               `json:"container"`
-	Enterprise          Enterprise              `json:"enterprise"`
-	IsEnterpriseInstall bool                    `json:"is_enterprise_install"`
-	WorkflowStep        InteractionWorkflowStep `json:"workflow_step"`
+	Type            InteractionType         `json:"type"`
+	Token           string                  `json:"token"`
+	CallbackID      string                  `json:"callback_id"`
+	ResponseURL     string                  `json:"response_url"`
+	TriggerID       string                  `json:"trigger_id"`
+	ActionTs        string                  `json:"action_ts"`
+	Team            Team                    `json:"team"`
+	Channel         Channel                 `json:"channel"`
+	User            User                    `json:"user"`
+	OriginalMessage Message                 `json:"original_message"`
+	Message         Message                 `json:"message"`
+	Name            string                  `json:"name"`
+	Value           string                  `json:"value"`
+	MessageTs       string                  `json:"message_ts"`
+	AttachmentID    string                  `json:"attachment_id"`
+	ActionCallback  ActionCallbacks         `json:"actions"`
+	View            View                    `json:"view"`
+	ActionID        string                  `json:"action_id"`
+	APIAppID        string                  `json:"api_app_id"`
+	BlockID         string                  `json:"block_id"`
+	Container       Container               `json:"container"`
+	Enterprise      Enterprise              `json:"enterprise"`
+	WorkflowStep    InteractionWorkflowStep `json:"workflow_step"`
 	DialogSubmissionCallback
 	ViewSubmissionCallback
 	ViewClosedCallback
diff --git a/slackevents/inner_events.go b/slackevents/inner_events.go
index cb7229e..fb1ab13 100644
--- a/slackevents/inner_events.go
+++ b/slackevents/inner_events.go
@@ -28,9 +28,6 @@ type AppMentionEvent struct {
 
 	// BotID is filled out when a bot triggers the app_mention event
 	BotID string `json:"bot_id,omitempty"`
-
-	// When the app is mentioned in the edited message
-	Edited *Edited `json:"edited,omitempty"`
 }
 
 // AppHomeOpenedEvent Your Slack app home was opened.
@@ -270,7 +267,6 @@ type MessageEvent struct {
 	Upload bool   `json:"upload"`
 	Files  []File `json:"files"`
 
-	Blocks      slack.Blocks       `json:"blocks,omitempty"`
 	Attachments []slack.Attachment `json:"attachments,omitempty"`
 
 	// Root is the message that was broadcast to the channel when the SubType is


Test patch:
diff --git a/block_element_test.go b/block_element_test.go
index 5c766e4..84cfc85 100644
--- a/block_element_test.go
+++ b/block_element_test.go
@@ -43,16 +43,6 @@ func TestWithStyleForButtonElement(t *testing.T) {
 
 }
 
-func TestWithURLForButtonElement(t *testing.T) {
-
-	btnTxt := NewTextBlockObject("plain_text", "Next 2 Results", false, false)
-	btnElement := NewButtonBlockElement("test", "click_me_123", btnTxt)
-
-	btnElement.WithURL("https://foo.bar")
-	assert.Equal(t, btnElement.URL, "https://foo.bar")
-
-}
-
 func TestNewOptionsSelectBlockElement(t *testing.T) {
 
 	testOptionText := NewTextBlockObject("plain_text", "Option One", false, false)
@@ -158,12 +148,6 @@ func TestNewPlainTextInputBlockElement(t *testing.T) {
 
 }
 
-func TestNewRichTextInputBlockElement(t *testing.T) {
-	richTextInputElement := NewRichTextInputBlockElement(nil, "test")
-	assert.Equal(t, string(richTextInputElement.Type), "rich_text_input")
-	assert.Equal(t, richTextInputElement.ActionID, "test")
-}
-
 func TestNewEmailTextInputBlockElement(t *testing.T) {
 	emailTextInputElement := NewEmailTextInputBlockElement(nil, "example@example.com")
 
@@ -231,3 +215,19 @@ func TestNewNumberInputBlockElement(t *testing.T) {
 	assert.Equal(t, numberInputElement.IsDecimalAllowed, true)
 
 }
+
+func TestNewFileInputBlockElement(t *testing.T) {
+
+	fileInputElement := NewFileInputBlockElement("test")
+
+	assert.Equal(t, string(fileInputElement.Type), "file_input")
+	assert.Equal(t, fileInputElement.ActionID, "test")
+
+	fileInputElement.WithFileTypes("jpg", "png")
+	assert.Equal(t, len(fileInputElement.FileTypes), 2)
+	assert.Contains(t, fileInputElement.FileTypes, "jpg")
+	assert.Contains(t, fileInputElement.FileTypes, "png")
+
+	fileInputElement.WithMaxFiles(10)
+	assert.Equal(t, fileInputElement.MaxFiles, 10)
+}
diff --git a/block_rich_text_test.go b/block_rich_text_test.go
index 2788c97..270dde9 100644
--- a/block_rich_text_test.go
+++ b/block_rich_text_test.go
@@ -36,12 +36,11 @@ func TestRichTextBlock_UnmarshalJSON(t *testing.T) {
 		err      error
 	}{
 		{
-			[]byte(`{"elements":[{"type":"rich_text_unknown"},{"type":"rich_text_section"},{"type":"rich_text_list"}]}`),
+			[]byte(`{"elements":[{"type":"rich_text_unknown"},{"type":"rich_text_section"}]}`),
 			RichTextBlock{
 				Elements: []RichTextElement{
 					&RichTextUnknown{Type: RTEUnknown, Raw: `{"type":"rich_text_unknown"}`},
 					&RichTextSection{Type: RTESection, Elements: []RichTextSectionElement{}},
-					&RichTextList{Type: RTEList, Elements: []RichTextElement{}},
 				},
 			},
 			nil,
@@ -118,84 +117,3 @@ func TestRichTextSection_UnmarshalJSON(t *testing.T) {
 		}
 	}
 }
-
-func TestRichTextList_UnmarshalJSON(t *testing.T) {
-	cases := []struct {
-		raw      []byte
-		expected RichTextList
-		err      error
-	}{
-		{
-			[]byte(`{"style":"ordered","elements":[{"type":"rich_text_unknown","value":10},{"type":"rich_text_section","elements":[{"type":"text","text":"hi"}]}]}`),
-			RichTextList{
-				Type:  RTEList,
-				Style: RTEListOrdered,
-				Elements: []RichTextElement{
-					&RichTextUnknown{Type: RTEUnknown, Raw: `{"type":"rich_text_unknown","value":10}`},
-					&RichTextSection{
-						Type: RTESection,
-						Elements: []RichTextSectionElement{
-							&RichTextSectionTextElement{Type: RTSEText, Text: "hi"},
-						},
-					},
-				},
-			},
-			nil,
-		},
-		{
-			[]byte(`{"style":"ordered","elements":[{"type":"rich_text_list","style":"bullet","elements":[{"type":"rich_text_section","elements":[{"type":"text","text":"hi"}]}]}]}`),
-			RichTextList{
-				Type:  RTEList,
-				Style: RTEListOrdered,
-				Elements: []RichTextElement{
-					&RichTextList{
-						Type:  RTEList,
-						Style: RTEListBullet,
-						Elements: []RichTextElement{
-							&RichTextSection{
-								Type: RTESection,
-								Elements: []RichTextSectionElement{
-									&RichTextSectionTextElement{Type: RTSEText, Text: "hi"},
-								},
-							},
-						},
-					},
-				},
-			},
-			nil,
-		},
-		{
-			[]byte(`{"type": "rich_text_list","elements":[]}`),
-			RichTextList{
-				Type:     RTEList,
-				Elements: []RichTextElement{},
-			},
-			nil,
-		},
-		{
-			[]byte(`{"type": "rich_text_list","elements":[],"indent":2}`),
-			RichTextList{
-				Type:     RTEList,
-				Indent:   2,
-				Elements: []RichTextElement{},
-			},
-			nil,
-		},
-	}
-	for _, tc := range cases {
-		var actual RichTextList
-		err := json.Unmarshal(tc.raw, &actual)
-		if err != nil {
-			if tc.err == nil {
-				t.Errorf("unexpected error: %s", err)
-			}
-			t.Errorf("expected error is %s, but got %s", tc.err, err)
-		}
-		if tc.err != nil {
-			t.Errorf("expected to raise an error %s", tc.err)
-		}
-		if diff := deep.Equal(actual, tc.expected); diff != nil {
-			t.Errorf("actual value does not match expected one\n%s", diff)
-		}
-	}
-}
diff --git a/block_video_test.go b/block_video_test.go
deleted file mode 100644
index d4b791f..0000000
--- a/block_video_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package slack
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestNewVideoBlock(t *testing.T) {
-
-	videoTitle := NewTextBlockObject("plain_text", "VideoTitle", false, false)
-	videoBlock := NewVideoBlock(
-		"https://example.com/example.mp4",
-		"https://example.com/thumbnail.png",
-		"alternative text", "blockID", videoTitle)
-
-	assert.Equal(t, string(videoBlock.Type), "video")
-	assert.Equal(t, videoBlock.Title.Type, "plain_text")
-	assert.Equal(t, videoBlock.BlockID, "blockID")
-	assert.Contains(t, videoBlock.Title.Text, "VideoTitle")
-	assert.Contains(t, videoBlock.VideoURL, "example.mp4")
-
-}
diff --git a/slacktest/data.go b/slacktest/data.go
index 31d9a08..c9a6971 100644
--- a/slacktest/data.go
+++ b/slacktest/data.go
@@ -35,11 +35,11 @@ var okWebResponse = slack.SlackResponse{
 	Ok: true,
 }
 
-var defaultOkJSON = `
+var defaultOkJSON = fmt.Sprintf(`
 	{
 		"ok": true
 	}
-	`
+	`)
 
 var defaultChannelsListJSON = fmt.Sprintf(`
 	{
diff --git a/slacktest/server_test.go b/slacktest/server_test.go
index 45f6208..ac42444 100644
--- a/slacktest/server_test.go
+++ b/slacktest/server_test.go
@@ -48,7 +48,7 @@ func TestBotDirectMessageBotHandler(t *testing.T) {
 	s := NewTestServer()
 	go s.Start()
 	s.SendDirectMessageToBot("some text")
-	expectedMsg := "some text"
+	expectedMsg := fmt.Sprintf("some text")
 	time.Sleep(2 * time.Second)
 	assert.True(t, s.SawOutgoingMessage(expectedMsg))
 	s.Stop()