Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding rich text block support #185

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- N/A


## [0.23.0] - 2024-04-07

### Added

- Added support for Rich Text Block via `Slack::BlockKit::Layout::RichText` (#185 by @GetOutOfMyBakery)

## [0.23.0] - 2023-08-07

### Added
Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ gem 'rspec', '~> 3'
gem 'rspec_junit_formatter', '~> 0.6'

gem 'codecov', '~> 0.6', require: false
gem 'debug', '~> 1.9', require: false
gem 'retest', '~> 1.13', require: false
gem 'rubocop', '~> 1', require: false
gem 'rubocop-rake', '~> 0.6', require: false
gem 'rubocop-rspec', '~> 2', require: false
Expand Down
32 changes: 32 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ GEM
codecov (0.6.0)
simplecov (>= 0.15, < 0.22)
coderay (1.1.3)
debug (1.9.1)
irb (~> 1.10)
reline (>= 0.3.8)
diff-lcs (1.5.1)
docile (1.4.0)
dotenv (2.8.1)
Expand All @@ -37,18 +40,41 @@ GEM
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
ffi (1.16.3)
io-console (0.7.2)
irb (1.12.0)
rdoc
reline (>= 0.4.2)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
method_source (1.0.0)
multipart-post (2.3.0)
observer (0.1.2)
parallel (1.22.1)
parser (3.1.2.0)
ast (~> 2.4.1)
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
psych (5.1.2)
stringio
racc (1.7.3)
rainbow (3.1.1)
rake (13.2.0)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rdoc (6.6.3.1)
psych (>= 4.0.0)
regexp_parser (2.3.1)
reline (0.4.3)
io-console (~> 0.5)
retest (1.13.2)
listen (~> 3.9)
observer (~> 0.1)
string-similarity (~> 2.1)
tty-option (~> 0.1)
rexml (3.2.5)
rspec (3.13.0)
rspec-core (~> 3.13.0)
Expand Down Expand Up @@ -88,21 +114,27 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.3)
string-similarity (2.1.0)
stringio (3.1.0)
tty-option (0.3.0)
unicode-display_width (2.1.0)
zeitwerk (2.6.13)

PLATFORMS
ruby
x86_64-darwin-19
x86_64-darwin-23
x86_64-linux

DEPENDENCIES
codecov (~> 0.6)
debug (~> 1.9)
dotenv (~> 2)
faraday (~> 1)
pry (~> 0.14)
racc
rake (~> 13)
retest (~> 1.13)
rspec (~> 3)
rspec_junit_formatter (~> 0.6)
rubocop (~> 1)
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ blocks = Slack::BlockKit.blocks do |b|
b.append(a_prebuilt_block)
end

body = { blocks: blocks.as_json }
GetOutOfMyBakery marked this conversation as resolved.
Show resolved Hide resolved
webhook_url = 'https://hooks.slack.com/services/your/webhook/url'
body = { blocks: blocks.as_json, text: 'New block message!' }

response = Faraday.post(
webhook_url,
Expand All @@ -81,8 +81,10 @@ This will create a message like this:

![example block message](https://git.io/fjDWR)

You can also check out the [`slackerduty`](https://github.com/CGA1123/slackerduty) project for some example,
[`Slackerduty::Alert`](https://github.com/CGA1123/slackerduty/blob/b33d708124ddf36d1432080ba7e16e66fefa6993/lib/slackerduty/alert.rb#L28-L34) and [`Slackerduty::Blocks`](https://github.com/CGA1123/slackerduty/blob/master/lib/slackerduty/blocks) may be helpful places to start.
See [`./examples`](./examples/), and [`./examples/README.md`](./examples/README.md), for more worked examples and guidance.

You can also check out the [`slackerduty`](https://github.com/CGA1123/slackerduty) project for some more examples,
[`Slackerduty::Alert`](https://github.com/CGA1123/slackerduty/blob/b33d708124ddf36d1432080ba7e16e66fefa6993/lib/slackerduty/alert.rb#L28-L34) and [`Slackerduty::Blocks`](https://github.com/CGA1123/slackerduty/blob/¦master/lib/slackerduty/blocks) may be helpful places to start.

## Contributing

Expand Down
18 changes: 18 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Examples

These example files demonstrate worked examples of how to use the gem.
They are written in a way that you can:
- send the messages to your own Slack workspace, when a valid Slack `webhook_url` has been provided (see `./config.rb`)
- copy and paste the output to Slack's [Block Builder Kit](https://app.slack.com/block-kit-builder) to validate the output without having to send a message to Slack

> [!NOTE]
> There are some values that need to be valid otherwise Slack, or the Block Builder Kit UI, will return a warning reporting the block output is invalid

## How to use
1. Update `./config.rb`
1. Run `bundle exec examples/simple_example.rb`

> [!TIP]
> When using [Block Builder Kit](https://app.slack.com/block-kit-builder) to validate the output, set `SUPPRESS_WEBHOOK_MESSAGE=true` to suppress lines that would invalid the output, e.g.:
> `SUPPRESS_WEBHOOK_MESSAGE=true examples/simple_example.rb | pbcopy` # on Mac
> `SUPPRESS_WEBHOOK_MESSAGE=true examples/simple_example.rb | xclip -selection clipboard` # on Linux
121 changes: 121 additions & 0 deletions examples/comprehensive_rich_text_blocks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/usr/bin/env ruby
GetOutOfMyBakery marked this conversation as resolved.
Show resolved Hide resolved
# frozen_string_literal: true

require_relative '../lib/slack-ruby-block-kit'
require_relative './config'
require_relative './example_helper'
require 'json'

# rubocop:disable Metrics/BlockLength
blocks = Slack::BlockKit.blocks do |b|
b.rich_text do |rt|
rt.rich_text_section do |rts|
rts.text(text: "Bold text\n", styles: ['bold'])
rts.text(text: "Italic text\n", styles: ['italic'])
rts.text(text: "strikethrough text\n", styles: ['strike'])
rts.text(text: "This is a code block!\n", styles: ['code'])
rts.text(
text: "This text has multiple styling\n",
styles: %w[
bold
italic
strike
]
)
end
end

b.divider

b.rich_text do |rt|
rt.rich_text_section do |rts|
rts.text(text: 'Tagging a user:')
rts.user(user_id: Config.user_id)
rts.text(text: "\nTagging a user with some styling:")
rts.user(user_id: Config.user_id, styles: %w[bold italic])
rts.text(text: "\n")
end
end

b.divider

b.rich_text do |rt|
rt.rich_text_section do |rts|
rts.text(text: 'Tagging a user group:')
rts.usergroup(usergroup_id: Config.usergroup_id)
rts.text(text: "\nTagging a user group with some styling:")
rts.usergroup(usergroup_id: Config.usergroup_id, styles: ['bold'])
rts.text(text: "\n")
end
end

b.divider

b.rich_text do |rt|
rt.rich_text_section do |rts|
rts.text(text: 'Channel tag: ')
rts.channel(channel_id: Config.channel_id)
rts.text(text: "\n")
end

rt.rich_text_section do |rts|
rts.text(text: 'Link to a great repo: ')
rts.emoji(name: 'point_right')
rts.link(url: 'https://github.com/CGA1123/slack-ruby-block-kit')
rts.emoji(name: 'point_left')
rts.text(text: "\n")
end

rt.rich_text_section do |rts|
rts.emoji(name: 'muscle')
rts.link(
url: 'https://github.com/CGA1123/slack-ruby-block-kit',
text: 'Link with alternative title',
unsafe: true,
styles: %w[
bold
italic
]
)
rts.emoji(name: 'muscle-two')
rts.text(text: "\n")
end
end

b.divider

b.rich_text do |rt|
rt.rich_text_list(style: 'bullet', indent: 1, offset: 1, border: 1) do |rtl|
rtl.rich_text_section do |rts|
rts.text(text: "Rich text\n", styles: %w[bold italic strike])
rts.text(text: "strikethrough text\n", styles: ['strike'])
end

rtl.rich_text_section do |rts|
rts.text(text: "strikethrough text\n", styles: ['strike'])
rts.text(text: "code block text\n", styles: ['code'])
end
end

rt.rich_text_list(style: 'ordered', indent: 2, offset: 2, border: 1) do |rtl|
rtl.rich_text_section do |rts|
rts.text(text: "Some text\n", styles: ['bold'])
rts.text(text: "Some more text\n", styles: ['strike'])
end
end

rt.rich_text_preformatted(border: 1) do |rtp|
rtp.text(text: 'Some text', styles: %w[bold italic])
end

rt.rich_text_quote(border: 1) do |rtq|
rtq.text(text: 'Some more text', styles: %w[bold strike])
end
end
end
# rubocop:enable Metrics/BlockLength

body = { blocks: blocks.as_json }
puts JSON.pretty_generate(body)

ExampleHelper.post_to_slack(body)
19 changes: 19 additions & 0 deletions examples/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

# When using Slack's Block Builder Kit, https://app.slack.com/block-kit-builder, there are some values that need
# to be valid otherwise the UI will return a warning and report the block as being invalid.

# Store and populate any other examples here, and use throughout 'examples/' scripts.

module Config
class << self
attr_accessor :channel_id, :user_id, :usergroup_id, :webhook_url
end

self.channel_id = 'CL9QH354H'
self.user_id = 'URLQ55EMB'
self.usergroup_id = 'SBR3Q0B2T'

# Uncomment and provide a valid webook to post to `examples/` scripts to Slack:
# self.webhook_url = 'https://hooks.slack.com/services/your/webhook/url'
end
22 changes: 22 additions & 0 deletions examples/example_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

# frozen_string_literal: true
require 'faraday'

module ExampleHelper
def self.print_output(line)
$stderr.puts line unless ENV.fetch('SUPPRESS_WEBHOOK_MESSAGE', nil) == 'true'
end

def self.post_to_slack(body)
if Config.webhook_url
print_output 'Posting to Slack ...'
Faraday.post(
Config.webhook_url,
body.to_json,
'Content-Type' => 'application/json'
)
else
print_output 'Set a "webhook_url" in "./config.rb" in order to test sending this message to Slack.'
end
end
end
33 changes: 33 additions & 0 deletions examples/simple_example.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require_relative '../lib/slack-ruby-block-kit'
require_relative './config'
require_relative './example_helper'
require 'json'

a_prebuilt_block = Slack::BlockKit::Layout::Section.new
text = Slack::BlockKit::Composition::Mrkdwn.new(text: ':wave: *hello*')
an_image = Slack::BlockKit::Element::Image.new(image_url: 'https://git.io/fjDW8', alt_text: 'a picture')
a_prebuilt_block.accessorise(an_image)
a_prebuilt_block.text = text

blocks = Slack::BlockKit.blocks do |b|
b.section do |s|
s.plain_text(text: 'Some plain text message!')
s.button(text: 'A button that is important', style: 'primary', action_id: 'id')
end

b.divider

b.context do |c|
c.mrkdwn(text: '_some italicised text for context_')
end

b.append(a_prebuilt_block)
end

body = { blocks: blocks.as_json }
puts JSON.pretty_generate(body)

ExampleHelper.post_to_slack(body)
26 changes: 26 additions & 0 deletions examples/simple_rich_text_blocks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require_relative '../lib/slack-ruby-block-kit'
require_relative './config'
require_relative './example_helper'
require 'json'

a_prebuilt_block = Slack::BlockKit::Layout::RichText.new
bold_rich_text_section = Slack::BlockKit::Layout::RichText::RichTextSection.new.text(text: 'Bold text', styles: ['bold'])
a_prebuilt_block.append(bold_rich_text_section)

italic_rich_text_section = Slack::BlockKit::Layout::RichText::RichTextSection.new.text(text: 'Italic text', styles: ['italic'])
a_prebuilt_block.append(italic_rich_text_section)

channel_rich_text = Slack::BlockKit::Layout::RichText::RichTextSection.new.channel(channel_id: Config.channel_id, styles: %w[bold italic])
a_prebuilt_block.append(channel_rich_text)

blocks = Slack::BlockKit.blocks do |b|
b.append(a_prebuilt_block)
end

body = { blocks: blocks.as_json }
puts JSON.pretty_generate(body)

ExampleHelper.post_to_slack(body)
8 changes: 8 additions & 0 deletions lib/slack/block_kit/blocks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ def image(url:, alt_text:, title: nil, block_id: nil, emoji: nil)
append(block)
end

def rich_text(block_id: nil)
block = Layout::RichText.new(block_id: block_id)

yield(block) if block_given?

append(block)
end

def video(alt_text:, thumbnail_url:, video_url:, title:, description:, **optional_args)
block = Layout::Video.new(
alt_text: alt_text,
Expand Down
Loading
Loading