Skip to content

Commit

Permalink
feat: Support thread_ts parameter to post the new message as a thread…
Browse files Browse the repository at this point in the history
… reply to another

Closes #187
  • Loading branch information
ArnaudRinquin committed Apr 22, 2024
1 parent 53b162f commit 032e1fb
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 28 deletions.
104 changes: 82 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ For examples on how to leverage this in your workflows, check out the [example w

This package has three different techniques to send data to Slack:

1) Send data to Slack's Workflow Builder (requires a paid Slack instance).
2) Send data via a Slack app to post to a specific channel (use an existing custom app or create a new one).
3) Send data via a Slack Incoming Webhook URL (use an existing custom app or create a new one).
1. Send data to Slack's Workflow Builder (requires a paid Slack instance).
2. Send data via a Slack app to post to a specific channel (use an existing custom app or create a new one).
3. Send data via a Slack Incoming Webhook URL (use an existing custom app or create a new one).

The recommended way to use this action is with Slack's Workflow Builder (if you're on a paid Slack plan).

Expand All @@ -29,10 +29,10 @@ you will need to define expected variables in the payload the webhook will recei

#### Setup

* [Create a Slack workflow webhook][create-webhook].
* Copy the webhook URL (`https://hooks.slack.com/workflows/....`) and [add it as a secret in your repo settings][repo-secret] named `SLACK_WEBHOOK_URL`.
* Add a step to your GitHub action to send data to your Webhook.
* Configure your Slack workflow to use variables from the incoming payload from the GitHub Action. You can select where you want to post the data and how you want to format it in Slack's workflow builder interface.
- [Create a Slack workflow webhook][create-webhook].
- Copy the webhook URL (`https://hooks.slack.com/workflows/....`) and [add it as a secret in your repo settings][repo-secret] named `SLACK_WEBHOOK_URL`.
- Add a step to your GitHub action to send data to your Webhook.
- Configure your Slack workflow to use variables from the incoming payload from the GitHub Action. You can select where you want to post the data and how you want to format it in Slack's workflow builder interface.

#### Usage

Expand Down Expand Up @@ -62,6 +62,7 @@ or
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
```
or
> If the `payload` is provided it will take preference over `payload-file-path`
Expand Down Expand Up @@ -97,11 +98,11 @@ By creating a new Slack app or using an existing one, this approach allows your

#### Setup

* [Create a Slack App][apps] for your workspace (alternatively use an existing app you have already created and installed).
* Add the [`chat:write`](https://api.slack.com/scopes/chat:write) bot scope under **OAuth & Permissions**.
* Install the app to your workspace.
* Copy the app's Bot Token from the **OAuth & Permissions** page and [add it as a secret in your repo settings][repo-secret] named `SLACK_BOT_TOKEN`.
* Invite the bot user into the channel you wish to post messages to (`/invite @bot_user_name`).
- [Create a Slack App][apps] for your workspace (alternatively use an existing app you have already created and installed).
- Add the [`chat:write`](https://api.slack.com/scopes/chat:write) bot scope under **OAuth & Permissions**.
- Install the app to your workspace.
- Copy the app's Bot Token from the **OAuth & Permissions** page and [add it as a secret in your repo settings][repo-secret] named `SLACK_BOT_TOKEN`.
- Invite the bot user into the channel you wish to post messages to (`/invite @bot_user_name`).

#### Usage

Expand All @@ -115,7 +116,7 @@ Add this Action as a [step][job-step] to your project's GitHub Action Workflow f
# Slack channel id, channel name, or user id to post message.
# See also: https://api.slack.com/methods/chat.postMessage#channels
# You can pass in multiple channels to post to by providing a comma-delimited list of channel IDs.
channel-id: 'CHANNEL_ID,ANOTHER_CHANNEL_ID'
channel-id: "CHANNEL_ID,ANOTHER_CHANNEL_ID"
# For posting a simple plain text message
slack-message: "GitHub build result: ${{ job.status }}\n${{ github.event.pull_request.html_url || github.event.head_commit.url }}"
env:
Expand All @@ -131,7 +132,7 @@ Using JSON payload for constructing a message is also available:
with:
# Slack channel id, channel name, or user id to post message.
# See also: https://api.slack.com/methods/chat.postMessage#channels
channel-id: 'CHANNEL_ID'
channel-id: "CHANNEL_ID"
# For posting a rich message using Block Kit
payload: |
{
Expand Down Expand Up @@ -209,6 +210,65 @@ Please note that **the message update step does not accept a channel name.** Set
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
```

#### Post as a reply to a message

If you would like to log the real-time updates, you can post new messages as thread replies to the message your build job posted in the subsequent steps. In order to do this, the steps after the first message posting can have `thread-ts: ${{ steps.slack.outputs.ts }}` in their settings. With this, the step creates a new message in the thread of the already posted channel message.

Please note that **the reply message step does not accept a channel name.** Set a channel ID for the steps for the actions that update messages.

```yaml
- id: slack
uses: slackapi/slack-github-action@v1.26.0
with:
# The following message update step does not accept a channel name.
# Setting a channel ID here for consistency is highly recommended.
channel-id: "CHANNEL_ID"
payload: |
{
"text": "Deployment started (In Progress)",
"attachments": [
{
"pretext": "Deployment started",
"color": "dbab09",
"fields": [
{
"title": "Status",
"short": true,
"value": "In Progress"
}
]
}
]
}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
- uses: slackapi/slack-github-action@v1.26.0
with:
# Unlike the step posting a new message, this step does not accept a channel name.
# Please use a channel ID, not a name here.
channel-id: "CHANNEL_ID"
thread-ts: ${{ steps.slack.outputs.ts }}
payload: |
{
"text": "Deployment finished (Completed)",
"attachments": [
{
"pretext": "Deployment finished",
"color": "28a745",
"fields": [
{
"title": "Status",
"short": true,
"value": "Completed"
}
]
}
]
}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
```

### Technique 3: Slack Incoming Webhook

This approach allows your GitHub Actions job to post a message to a Slack channel or direct message by utilizing [Incoming Webhooks](https://api.slack.com/messaging/webhooks).
Expand All @@ -217,11 +277,11 @@ Incoming Webhooks conform to the same rules and functionality as any of Slack's

#### Setup

* [Create a Slack App][apps] for your workspace (alternatively use an existing app you have already created and installed).
* Add the [`incoming-webhook`](https://api.slack.com/scopes/incoming-webhook) bot scope under **OAuth & Permissions**.
* Install the app to your workspace (you will select a channel to notify).
* Activate and create a new webhook under **Incoming Webhooks**.
* Copy the Webhook URL from the Webhook you just generated [add it as a secret in your repo settings][repo-secret] named `SLACK_WEBHOOK_URL`.
- [Create a Slack App][apps] for your workspace (alternatively use an existing app you have already created and installed).
- Add the [`incoming-webhook`](https://api.slack.com/scopes/incoming-webhook) bot scope under **OAuth & Permissions**.
- Install the app to your workspace (you will select a channel to notify).
- Activate and create a new webhook under **Incoming Webhooks**.
- Copy the Webhook URL from the Webhook you just generated [add it as a secret in your repo settings][repo-secret] named `SLACK_WEBHOOK_URL`.

#### Usage

Expand Down Expand Up @@ -258,12 +318,12 @@ If you need to use a proxy to connect with Slack, you can use the `HTTPS_PROXY`
id: slack
uses: slackapi/slack-github-action@v1.26.0
with:
channel-id: 'CHANNEL_ID'
slack-message: 'This message was sent through a proxy'
channel-id: "CHANNEL_ID"
slack-message: "This message was sent through a proxy"
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
# Set the HTTPS_PROXY environment variable to whatever your policy requires
HTTPS_PROXY: 'http://proxy.example.org:8080'
HTTPS_PROXY: "http://proxy.example.org:8080"
```

## Contributing
Expand Down
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ inputs:
update-ts: # The timestamp of a previous message posted to update it instead of posting a new message
description: 'The timestamp of a previous message posted. It will update the existing message instead of posting a new message'
required: false
thread-ts: # The timestamp of a previous message posted to post a new message as a thread reply to the existing message
description: 'The timestamp of a previous message posted. It will post the new message as a thread reply to the existing message'
required: false
outputs:
time: # id of output
description: 'The time'
Expand Down
23 changes: 17 additions & 6 deletions src/slack-send.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,25 @@ module.exports = async function slackSend(core) {
}

if (message.length > 0 || payload) {
const ts = core.getInput('update-ts');
const updateTs = core.getInput('update-ts');
const threadTs = core.getInput('thread-ts');
await Promise.all(channelIds.split(',').map(async (channelId) => {
if (ts) {
// update message
webResponse = await web.chat.update({ ts, channel: channelId.trim(), text: message, ...(payload || {}) });
if (updateTs) {
// update message
webResponse = await web.chat.update({
ts: updateTs,
channel: channelId.trim(),
text: message,
...(payload || {}),
});
} else {
// post message
webResponse = await web.chat.postMessage({ channel: channelId.trim(), text: message, ...(payload || {}) });
// post message
webResponse = await web.chat.postMessage({
thread_ts: threadTs,
channel: channelId.trim(),
text: message,
...(payload || {}),
});
}
}));
} else {
Expand Down
16 changes: 16 additions & 0 deletions test/slack-send-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ describe('slack-send', () => {
assert.equal(chatArgs.text, 'who let the dogs out?', 'Correct message provided to postMessage');
});

it('should send a reply-message using the postMessage API', async () => {
fakeCore.getInput.withArgs('slack-message').returns('who let the dogs out?');
fakeCore.getInput.withArgs('channel-id').returns('C123456');
fakeCore.getInput.withArgs('thread-ts').returns('123123');
await slackSend(fakeCore);
assert.equal(fakeCore.setOutput.firstCall.firstArg, 'ts', 'Output name set to ts');
assert.equal(fakeCore.setOutput.secondCall.firstArg, 'thread_ts', 'Output name set to thread_ts');
assert(fakeCore.setOutput.secondCall.lastArg.length > 0, 'Time output a non-zero-length string');
assert.equal(fakeCore.setOutput.lastCall.firstArg, 'time', 'Output name set to time');
assert(fakeCore.setOutput.lastCall.lastArg.length > 0, 'Time output a non-zero-length string');
const chatArgs = ChatStub.postMessage.lastCall.firstArg;
assert.equal(chatArgs.channel, 'C123456', 'Correct channel provided to postMessage');
assert.equal(chatArgs.thread_ts, '123123', 'Correct thread_ts provided to postMessage');
assert.equal(chatArgs.text, 'who let the dogs out?', 'Correct message provided to postMessage');
});

it('should send a message using the update API', async () => {
fakeCore.getInput.withArgs('slack-message').returns('who let the dogs out?');
fakeCore.getInput.withArgs('channel-id').returns('C123456');
Expand Down

0 comments on commit 032e1fb

Please sign in to comment.