Repository: slack-go/slack
Pull request #1319 (2024-09-09 09:05:38)

feat: Add support for unicode parameter in emoji type of rich text blocks
This PR adds support for the `unicode` parameter to the `RichTextSectionEmojiElement` struct for rich text blocks. While this parameter is not officially documented in Slack's API, it is present in the JSON payload of actual Slack messages and represents the Unicode code point of the emoji.
https://api.slack.com/reference/block-kit/blocks#emoji-element-type

For example, a rich text block with an emoji can include the unicode field like this:

```json
"blocks": [
    {
      "type": "rich_text",
      "block_id": "xxxxx",
      "elements": [
        {
          "type": "rich_text_section",
          "elements": [
            {
              "type": "emoji",
              "name": "+1",
              "unicode": "1f44d"
            }
          ]
        }
      ]
    }
  ]
```

The unicode parameter behaves similarly to the skin-tone parameter, which is also undocumented but has already been included in the structure. This PR aligns the handling of unicode in the same way to ensure emojis are fully supported in Slack message payloads.

Please review, and feel free to provide feedback if any adjustments are needed. Thank you!



##### 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.

##### 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

Since API changes have to be maintained they undergo a more detailed review and are more likely to require changes.

- no tests, if you're adding to the API include at least a single test of the happy case.
- If you can accomplish your goal without changing the API, then do so.
- dependency changes. updates are okay. adding/removing need justification.

###### Examples of API changes that do not meet guidelines:
- in library cache for users. caches are use case specific.
- Convenience methods for Sending Messages, update, post, ephemeral, etc. consider opening an issue instead.



Patch:
diff --git a/block_element.go b/block_element.go
index a2b755b..06bffe3 100644
--- a/block_element.go
+++ b/block_element.go
@@ -527,7 +527,7 @@ type RichTextInputBlockElement struct {
 	Type                 MessageElementType    `json:"type"`
 	ActionID             string                `json:"action_id,omitempty"`
 	Placeholder          *TextBlockObject      `json:"placeholder,omitempty"`
-	InitialValue         string                `json:"initial_value,omitempty"`
+	InitialValue         *RichTextBlock        `json:"initial_value,omitempty"`
 	DispatchActionConfig *DispatchActionConfig `json:"dispatch_action_config,omitempty"`
 	FocusOnLoad          bool                  `json:"focus_on_load,omitempty"`
 }
diff --git a/block_rich_text.go b/block_rich_text.go
index b6a4b4c..c6eb0b1 100644
--- a/block_rich_text.go
+++ b/block_rich_text.go
@@ -341,6 +341,7 @@ type RichTextSectionEmojiElement struct {
 	Type     RichTextSectionElementType `json:"type"`
 	Name     string                     `json:"name"`
 	SkinTone int                        `json:"skin_tone"`
+	Unicode  string                     `json:"unicode,omitempty"`
 	Style    *RichTextSectionTextStyle  `json:"style,omitempty"`
 }
 
@@ -414,16 +415,22 @@ func NewRichTextSectionUserGroupElement(usergroupID string) *RichTextSectionUser
 type RichTextSectionDateElement struct {
 	Type      RichTextSectionElementType `json:"type"`
 	Timestamp JSONTime                   `json:"timestamp"`
+	Format    string                     `json:"format"`
+	URL       *string                    `json:"url,omitempty"`
+	Fallback  *string                    `json:"fallback,omitempty"`
 }
 
 func (r RichTextSectionDateElement) RichTextSectionElementType() RichTextSectionElementType {
 	return r.Type
 }
 
-func NewRichTextSectionDateElement(timestamp int64) *RichTextSectionDateElement {
+func NewRichTextSectionDateElement(timestamp int64, format string, url *string, fallback *string) *RichTextSectionDateElement {
 	return &RichTextSectionDateElement{
 		Type:      RTSEDate,
 		Timestamp: JSONTime(timestamp),
+		Format:    format,
+		URL:       url,
+		Fallback:  fallback,
 	}
 }
 
diff --git a/chat.go b/chat.go
index 18f8e93..96843d6 100644
--- a/chat.go
+++ b/chat.go
@@ -377,6 +377,7 @@ func (t responseURLSender) BuildRequestContext(ctx context.Context) (*http.Reque
 	req, err := jsonReq(ctx, t.endpoint, Msg{
 		Text:            t.values.Get("text"),
 		Timestamp:       t.values.Get("ts"),
+		ThreadTimestamp: t.values.Get("thread_ts"),
 		Attachments:     t.attachments,
 		Blocks:          t.blocks,
 		Metadata:        t.metadata,


Test patch:
diff --git a/block_rich_text_test.go b/block_rich_text_test.go
index a9f04b7..dc4a0cf 100644
--- a/block_rich_text_test.go
+++ b/block_rich_text_test.go
@@ -167,13 +167,14 @@ func TestRichTextSection_UnmarshalJSON(t *testing.T) {
 		err      error
 	}{
 		{
-			[]byte(`{"elements":[{"type":"unknown","value":10},{"type":"text","text":"hi"},{"type":"date","timestamp":1636961629}]}`),
+			[]byte(`{"elements":[{"type":"unknown","value":10},{"type":"text","text":"hi"},{"type":"date","timestamp":1636961629,"format":"{date_short_pretty}"},{"type":"date","timestamp":1636961629,"format":"{date_short_pretty}","url":"https://example.com","fallback":"default"}]}`),
 			RichTextSection{
 				Type: RTESection,
 				Elements: []RichTextSectionElement{
 					&RichTextSectionUnknownElement{Type: RTSEUnknown, Raw: `{"type":"unknown","value":10}`},
 					&RichTextSectionTextElement{Type: RTSEText, Text: "hi"},
-					&RichTextSectionDateElement{Type: RTSEDate, Timestamp: JSONTime(1636961629)},
+					&RichTextSectionDateElement{Type: RTSEDate, Timestamp: JSONTime(1636961629), Format: "{date_short_pretty}"},
+					&RichTextSectionDateElement{Type: RTSEDate, Timestamp: JSONTime(1636961629), Format: "{date_short_pretty}", URL: strp("https://example.com"), Fallback: strp("default")},
 				},
 			},
 			nil,
@@ -186,6 +187,16 @@ func TestRichTextSection_UnmarshalJSON(t *testing.T) {
 			},
 			nil,
 		},
+		{
+			[]byte(`{"type": "rich_text_section","elements":[{"type": "emoji","name": "+1","unicode": "1f44d-1f3fb","skin_tone": 2}]}`),
+			RichTextSection{
+				Type: RTESection,
+				Elements: []RichTextSectionElement{
+					&RichTextSectionEmojiElement{Type: RTSEEmoji, Name: "+1", Unicode: "1f44d-1f3fb", SkinTone: 2},
+				},
+			},
+			nil,
+		},
 	}
 	for _, tc := range cases {
 		var actual RichTextSection
@@ -361,3 +372,5 @@ func TestRichTextQuote_Marshal(t *testing.T) {
 		}
 	})
 }
+
+func strp(in string) *string { return &in }