Skip to content

Commit

Permalink
update docs and fix headers
Browse files Browse the repository at this point in the history
  • Loading branch information
javoeria committed May 12, 2023
1 parent 04a0470 commit efe4b91
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 43 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Dart

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: dart-lang/setup-dart@v1

- name: Install dependencies
run: dart pub get

- name: Verify formatting
run: dart format --output=none --set-exit-if-changed .

- name: Analyze project source
run: dart analyze

- name: Run tests
run: dart test
8 changes: 3 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# Files and directories created by pub
.dart_tool/
.packages
# Remove the following pattern if you wish to check in your lock file

# Omit commiting pubspec.lock for library packages:
# https://dart.dev/guides/libraries/private-files#pubspeclock
pubspec.lock

# Conventional directory for build outputs
build/

# Directory created by dartdoc
doc/api/

.vscode/
.idea/
*.iml
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.3.0

* Update blocks and documentation.
* Fix issue on Flutter Web ([#2](https://github.com/javoeria/slack-dart/issues/2)).

## 1.2.0

* Add layout blocks support.
Expand Down
77 changes: 58 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,69 @@
# slack_notifier

A simple wrapper to post messages from external sources into Slack.
Dart wrapper for posting messages to Slack using Incoming Webhooks.

[![Build Status](https://travis-ci.com/javoeria/slack-dart.svg?branch=master)](https://travis-ci.com/javoeria/slack-dart)
[![pub package](https://img.shields.io/pub/v/slack_notifier.svg)](https://pub.dartlang.org/packages/slack_notifier)
[![Build Status](https://github.com/javoeria/slack-dart/actions/workflows/dart.yml/badge.svg?branch=master)](https://github.com/javoeria/slack-dart/actions/workflows/dart.yml)
[![pub package](https://img.shields.io/pub/v/slack_notifier.svg)](https://pub.dev/packages/slack_notifier)

## Usage
## Getting Started

Incoming Webhooks are a simple way to post messages from apps into Slack. Creating an Incoming Webhook gives you a unique URL to which you send a JSON payload with the message text and some options. You can use all the usual [formatting](https://api.slack.com/reference/surfaces/formatting) and [layout blocks](https://api.slack.com/messaging/composing/layouts) with Incoming Webhooks to make the messages stand out.

To get the WEBHOOK_URL you need:

To use this package, your workspace needs to have a Webhook URL.
If you don't have it, follow the instructions [here](https://api.slack.com/messaging/webhooks).
1. Create a Slack app (if you don't have one already)
2. Enable Incoming Webhooks
3. Create an Incoming Webhook
4. Use your Incoming Webhook URL to post a message

Read more about webhooks [here](https://api.slack.com/messaging/webhooks).

## Usage

The `SlackNotifier` class has only one method, with optional parameters to customize your messages.
It supports [block kit](https://api.slack.com/reference/block-kit).
This method posts a message to a public channel, private channel, or direct message/IM channel.

```dart
Future<String> send(
String text, {
String? channel, // Channel, private group, or IM channel name to send message to.
String? iconEmoji, // Emoji to use as the icon for this message.
String? iconUrl, // URL to an image to use as the icon for this message.
String? username, // Set your bot's user name.
List<Block>? blocks, // Blocks that can be combined to create interactive messages.
List<Attachment>? attachments, // Attachments that can be added as secondary content.
});
final slack = SlackNotifier('WEBHOOK_URL');
slack.send(
'Hello world',
channel: 'general',
iconEmoji: ':chart_with_upwards_trend:',
iconUrl: 'https://picsum.photos/48/48',
username: 'My Bot',
blocks: [SectionBlock(text: 'Hello world')],
attachments: [Attachment(pretext: 'pre-hello', text: 'text-world')],
);
```

And the message will look like this:
The usage of the `text` field changes depending on whether you're using `blocks`. If you are using `blocks`, this is used as a fallback string to display in notifications. If you aren't, this is the main body text of the message. It can be formatted as plain text, or with `mrkdwn`.

![example](https://a.slack-edge.com/80588/img/integrations/incoming_webhook_example1.png)
## Blocks

Blocks are a series of components that can be combined to create visually rich and compellingly interactive messages. [Block Kit](https://api.slack.com/reference/block-kit) can make your app's communication clearer while also giving you consistent opportunity to interact with and assist users.

- `ActionsBlock` A block that is used to hold multiple interactive elements.
- `ContextBlock` Used for contextual info, which can include both images and text.
- `DividerBlock` A content divider used to visually separate pieces of info inside of a message.
- `FileBlock` Used with remote files to display info about the attached files.
- `HeaderBlock` A larger-sized text block used as a header.
- `ImageBlock` A simple image block, designed to make those cat photos really pop.
- `InputBlock` A block that collects information from users in a multitude of ways.
- `SectionBlock` Display text or combine text with interactive elements and images.
- `VideoBlock` A block designed to display those cool cat videos.

Individual blocks can be stacked together to create complex visual layouts.

```dart
var blocks = [
HeaderBlock(text: 'Onboarding'),
SectionBlock(text: 'Example message for engaging new users.'),
DividerBlock(),
SectionBlock(text: "Hey there :wave: I'm *TaskBot*. I'm here to help you create and manage tasks in Slack."),
ImageBlock(
imageUrl: 'https://api.slack.com/img/blocks/bkb_template_images/onboardingComplex.jpg',
altText: 'image1',
title: 'image1',
),
];
slack.send('Onboarding', channel: 'general', blocks: blocks);
```
83 changes: 76 additions & 7 deletions lib/src/block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class ActionsBlock extends Block {
ActionsBlock({required this.elements});

/// An array of interactive element objects - buttons, select menus, overflow menus, or date pickers.
/// There is a maximum of 5 elements in each action block.
/// There is a maximum of 25 elements in each action block.
final List<Map> elements;

@override
Expand Down Expand Up @@ -96,7 +96,7 @@ class InputBlock extends Block {
/// Maximum length for this field is 2000 characters.
final String label;

/// An plain-text input element, a checkbox element, a radio button element, a select menu element, a multi-select menu element, or a datepicker.
/// A plain-text input element, a checkbox element, a radio button element, a select menu element, a multi-select menu element, or a datepicker.
final Map element;

/// An optional hint that appears below an input element in a lighter grey.
Expand All @@ -121,14 +121,14 @@ class SectionBlock extends Block {

/// The text for the block.
/// Maximum length for this field is 3000 characters.
/// This field is not required if a valid array of `fields` objects is provided instead.
/// This field is not required if a valid array of `fields` is provided instead.
final String? text;

/// Required if no `text` is provided.
/// Any text objects included with `fields` will be rendered in a compact format that allows for 2 columns of side-by-side text.
/// Any text included with `fields` will be rendered in a compact format that allows for 2 columns of side-by-side text.
/// Maximum number of items is 10.
/// Maximum length for the `text` in each item is 2000 characters.
final List<Map>? fields;
/// Maximum length for each item is 2000 characters.
final List<String>? fields;

/// One of the available element objects.
final Map? accessory;
Expand All @@ -138,9 +138,78 @@ class SectionBlock extends Block {
var block = {};
block['type'] = 'section';
if (text != null) block['text'] = {'type': 'mrkdwn', 'text': text};
if (fields != null) block['fields'] = fields;
if (fields != null) {
block['fields'] =
fields!.map((f) => {'type': 'mrkdwn', 'text': f}).toList();
}
if (accessory != null) block['accessory'] = accessory;

return block;
}
}

/// A `video` block is designed to embed videos in all app surfaces (e.g. link unfurls, messages, modals, App Home) - anywhere you can put blocks.
class VideoBlock extends Block {
VideoBlock({
this.altText,
this.authorName,
this.description,
this.providerIconUrl,
this.providerName,
required this.title,
this.titleUrl,
required this.thumbnailUrl,
required this.videoUrl,
});

/// A tooltip for the video.
final String? altText;

/// Author name to be displayed.
/// Must be less than 50 characters.
final String? authorName;

/// Description for video in plain text format.
final String? description;

/// Icon for the video provider.
final String? providerIconUrl;

/// The originating application or domain of the video.
final String? providerName;

/// Video title in plain text format.
/// Must be less than 200 characters.
final String title;

/// Hyperlink for the title text.
/// Must correspond to the non-embeddable URL for the video.
/// Must go to an HTTPS URL.
final String? titleUrl;

/// The thumbnail image URL.
final String thumbnailUrl;

/// The URL to be embedded.
/// Must match any existing unfurl domains within the app and point to a HTTPS URL.
final String videoUrl;

@override
toMap() {
var block = {};
block['type'] = 'video';
block['alt_text'] = altText ?? title;
if (authorName != null) block['author_name'] = authorName;
if (description != null) {
block['description'] = {'type': 'plain_text', 'text': description};
}
if (providerIconUrl != null) block['provider_icon_url'] = providerIconUrl;
if (providerName != null) block['provider_name'] = providerName;
block['title'] = {'type': 'plain_text', 'text': title};
if (titleUrl != null) block['title_url'] = titleUrl;
block['thumbnail_url'] = thumbnailUrl;
block['video_url'] = videoUrl;

return block;
}
}
9 changes: 6 additions & 3 deletions lib/src/slack_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import 'block.dart';
class SlackNotifier {
SlackNotifier(this.token);

/// Webhook URL that is specific to a single user and a single channel.
/// Authentication token bearing required scopes.
final String token;

/// Send message to your slack workspace.
/// Sends a message to a channel.
///
/// One of these arguments is required to describe the content of the message.
/// If `attachments` or `blocks` are included, `text` will be used as fallback text for notifications only.
Future<String> send(
String text, {
String? channel,
Expand Down Expand Up @@ -38,7 +41,7 @@ class SlackNotifier {

var response = await http.post(
Uri.parse(webhookUrl),
headers: {'Content-Type': 'application/json'},
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: jsonEncode(body),
);
return response.body;
Expand Down
10 changes: 5 additions & 5 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
name: slack_notifier
description: A simple wrapper to post messages from external sources into Slack.
version: 1.2.0
description: A simple wrapper for posting messages to Slack using Incoming Webhooks.
version: 1.3.0
homepage: https://github.com/javoeria/slack-dart

environment:
sdk: ">=2.12.0 <3.0.0"

dependencies:
http: ^0.13.4
http: ^0.13.5

dev_dependencies:
lints: ^1.0.1
test: ^1.20.1
lints: ^2.0.1
test: ^1.24.1
33 changes: 29 additions & 4 deletions test/slack_notifier_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,26 @@ void main() {
});

test('send plain message', () async {
var response = await slack.send('test', channel: 'test');
var response = await slack.send(
'Hello world',
channel: 'test',
iconEmoji: ':chart_with_upwards_trend:',
iconUrl: 'https://picsum.photos/48/48',
username: 'My Bot',
);
expect(response, 'ok');
});

test('send block message', () async {
var block = HeaderBlock(text: 'test');
var block = SectionBlock(text: 'Hello world');
var response = await slack.send('', channel: 'test', blocks: [block]);
expect(response, 'ok');
});

test('send attachment message', () async {
var attachment = Attachment(title: 'title');
var attachment = Attachment(pretext: 'pre-hello', text: 'text-world');
var response =
await slack.send('test', channel: 'test', attachments: [attachment]);
await slack.send('', channel: 'test', attachments: [attachment]);
expect(response, 'ok');
});

Expand All @@ -41,4 +47,23 @@ void main() {
var response = await SlackNotifier('T/B/X').send('test');
expect(response, 'no_team');
});

test('send multiple blocks', () async {
var blocks = [
HeaderBlock(text: 'Onboarding'),
SectionBlock(text: 'Example message for engaging new users.'),
DividerBlock(),
SectionBlock(
text:
"Hey there :wave: I'm *TaskBot*. I'm here to help you create and manage tasks in Slack."),
ImageBlock(
imageUrl:
'https://api.slack.com/img/blocks/bkb_template_images/onboardingComplex.jpg',
altText: 'image1',
title: 'image1',
),
];
var response = await slack.send('test', channel: 'test', blocks: blocks);
expect(response, 'ok');
});
}

0 comments on commit efe4b91

Please sign in to comment.