Skip to content

Commit df8aa84

Browse files
committed
feat: add Slack notification provider and update related documentation
- Introduced Slack support for sending deployment notifications via incoming webhooks. - Added new Slack provider with message formatting and sending logic. - Updated environment configuration to include SLACK_WEBHOOK_URL. - Enhanced documentation to include Slack setup instructions and features. - Revised existing documentation to reflect the addition of Slack as a notification option.
1 parent f6839c6 commit df8aa84

File tree

17 files changed

+569
-34
lines changed

17 files changed

+569
-34
lines changed

apps/fumadocs/content/docs/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ A lightweight webhook relay that sends Vercel deployment notifications wherever
2323
| Provider | Format |
2424
|----------|--------|
2525
| [Discord](/docs/providers/discord) | Rich embeds with colors and fields |
26+
| [Slack](/docs/providers/slack) | Block Kit messages via webhooks |
2627
| [Telegram](/docs/providers/telegram) | HTML messages via Bot API |
2728

2829
## Quick Start

apps/fumadocs/content/docs/installation/environment.mdx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ Add the variables for at least one provider. See the [Providers](/docs/providers
4646
description: 'Discord webhook URL',
4747
type: 'string',
4848
},
49-
DISCORD_WEBHOOK_USERNAME: {
50-
description: 'Custom bot username',
51-
type: 'string',
52-
default: 'Versend',
53-
},
54-
DISCORD_WEBHOOK_AVATAR_URL: {
55-
description: 'Custom bot avatar URL',
49+
}}
50+
/>
51+
52+
**Slack:**
53+
54+
<TypeTable
55+
type={{
56+
SLACK_WEBHOOK_URL: {
57+
description: 'Slack incoming webhook URL',
5658
type: 'string',
5759
},
5860
}}

apps/fumadocs/content/docs/providers/discord.mdx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,6 @@ Add these to your Vercel project:
4545
type: 'string',
4646
required: true,
4747
},
48-
DISCORD_WEBHOOK_USERNAME: {
49-
description: 'Custom bot username',
50-
type: 'string',
51-
default: 'Versend',
52-
},
53-
DISCORD_WEBHOOK_AVATAR_URL: {
54-
description: 'Custom bot avatar URL',
55-
type: 'string',
56-
},
5748
}}
5849
/>
5950

apps/fumadocs/content/docs/providers/index.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ Versend supports multiple notification providers. Configure one or many — noti
1313
| Provider | Status | Description |
1414
|----------|--------|-------------|
1515
| [Discord](/docs/providers/discord) | ✅ Stable | Rich embeds with colors, fields, and links |
16+
| [Slack](/docs/providers/slack) | ✅ Stable | Block Kit messages via incoming webhooks |
1617
| [Telegram](/docs/providers/telegram) | ✅ Stable | HTML-formatted messages via Bot API |
1718

1819
## How It Works
1920

2021
Each provider is **auto-enabled** when its required environment variables are set:
2122

2223
- **Discord**: Set `DISCORD_WEBHOOK_URL` → Discord enabled
24+
- **Slack**: Set `SLACK_WEBHOOK_URL` → Slack enabled
2325
- **Telegram**: Set `TELEGRAM_BOT_TOKEN` + `TELEGRAM_CHAT_ID` → Telegram enabled
2426

2527
Enable as many as you need.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"title": "Providers",
3-
"pages": ["index", "discord", "telegram"]
3+
"pages": ["index", "discord", "slack", "telegram"]
44
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
title: Slack
3+
description: Send deployment notifications to Slack
4+
icon: Hash
5+
---
6+
7+
import { Step, Steps } from 'fumadocs-ui/components/steps';
8+
import { TypeTable } from 'fumadocs-ui/components/type-table';
9+
10+
# Slack Provider
11+
12+
Send Block Kit-formatted notifications to any Slack channel via incoming webhooks.
13+
14+
## Features
15+
16+
- Rich Block Kit formatting with sections, fields, and buttons
17+
- Clickable links to deployments, commits, and projects
18+
- Branch, commit SHA, and commit message display
19+
- Build error details for failed deployments
20+
- Automatic retry with backoff for rate limits
21+
22+
## Setup
23+
24+
<Steps>
25+
<Step>
26+
27+
### Create a Slack App
28+
29+
1. Go to [api.slack.com/apps](https://api.slack.com/apps) and click **Create New App**
30+
2. Choose **From scratch** and name it (e.g., `Versend`)
31+
3. Select the workspace where you want notifications
32+
33+
</Step>
34+
<Step>
35+
36+
### Enable Incoming Webhooks
37+
38+
1. In your app settings, go to **Incoming Webhooks**
39+
2. Toggle **Activate Incoming Webhooks** to On
40+
3. Click **Add New Webhook to Workspace**
41+
4. Select the channel for notifications
42+
5. Copy the **Webhook URL**
43+
44+
</Step>
45+
<Step>
46+
47+
### Add Environment Variables
48+
49+
Add this to your Vercel project:
50+
51+
<TypeTable
52+
type={{
53+
SLACK_WEBHOOK_URL: {
54+
description: 'The incoming webhook URL you just copied',
55+
type: 'string',
56+
required: true,
57+
},
58+
}}
59+
/>
60+
61+
</Step>
62+
<Step>
63+
64+
### Redeploy
65+
66+
Redeploy your Versend instance to apply the changes.
67+
68+
</Step>
69+
</Steps>
70+
71+
## Example Output
72+
73+
A successful deployment notification looks like:
74+
75+
```
76+
✅ Deployment succeeded
77+
78+
**myapp** deployed to **production**
79+
80+
🌿 Branch 📝 Commit ⚙️ Environment
81+
`main` `abc1234` production
82+
83+
Commit Message:
84+
feat: add new feature
85+
86+
[View Deployment]
87+
```
88+
89+
The message includes clickable buttons and uses Slack's Block Kit formatting for a clean, structured layout.
90+

apps/web/src/env.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ export const env = createEnv({
77
WEBHOOK_INTEGRATION_SECRET: string(),
88
// Discord
99
DISCORD_WEBHOOK_URL: string().url().optional(),
10-
DISCORD_WEBHOOK_USERNAME: string().optional(),
11-
DISCORD_WEBHOOK_AVATAR_URL: string().url().optional(),
1210
// Telegram
1311
TELEGRAM_BOT_TOKEN: string().optional(),
1412
TELEGRAM_CHAT_ID: string().optional(),
13+
// Slack
14+
SLACK_WEBHOOK_URL: string().url().optional(),
1515
// Rate limiting
1616
UPSTASH_REDIS_REST_URL: string().url().optional(),
1717
UPSTASH_REDIS_REST_TOKEN: string().optional(),
@@ -20,10 +20,9 @@ export const env = createEnv({
2020
runtimeEnv: {
2121
WEBHOOK_INTEGRATION_SECRET: process.env.WEBHOOK_INTEGRATION_SECRET,
2222
DISCORD_WEBHOOK_URL: process.env.DISCORD_WEBHOOK_URL,
23-
DISCORD_WEBHOOK_USERNAME: process.env.DISCORD_WEBHOOK_USERNAME,
24-
DISCORD_WEBHOOK_AVATAR_URL: process.env.DISCORD_WEBHOOK_AVATAR_URL,
2523
TELEGRAM_BOT_TOKEN: process.env.TELEGRAM_BOT_TOKEN,
2624
TELEGRAM_CHAT_ID: process.env.TELEGRAM_CHAT_ID,
25+
SLACK_WEBHOOK_URL: process.env.SLACK_WEBHOOK_URL,
2726
UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL,
2827
UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN,
2928
},

apps/web/src/providers/discord/client.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { type Embed, Webhook } from "@vermaysha/discord-webhook";
22

33
import { env } from "@/env";
4-
import { DEFAULT_AVATAR_URL } from "./consts";
4+
import { BOT_AVATAR, BOT_NAME } from "./consts";
55

66
const RETRY_CONFIG = {
77
maxRetries: 3,
@@ -15,8 +15,8 @@ export async function sendEmbed(embed: Embed): Promise<void> {
1515
}
1616

1717
const hook = new Webhook(env.DISCORD_WEBHOOK_URL);
18-
hook.setUsername(env.DISCORD_WEBHOOK_USERNAME || "Versend");
19-
hook.setAvatarUrl(env.DISCORD_WEBHOOK_AVATAR_URL || DEFAULT_AVATAR_URL);
18+
hook.setUsername(BOT_NAME);
19+
hook.setAvatarUrl(BOT_AVATAR);
2020
hook.addEmbed(embed);
2121

2222
await sendWithRetry(hook);

apps/web/src/providers/discord/consts.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { WebhookType } from "@/schemas/vercel";
22
import type { StateProperty } from "./types";
33

4-
export const DEFAULT_AVATAR_URL =
5-
"https://assets.vercel.com/image/upload/front/favicon/vercel/180x180.png" as const;
4+
export const BOT_NAME = "Versend";
5+
export const BOT_AVATAR =
6+
"https://assets.vercel.com/image/upload/front/favicon/vercel/180x180.png";
67

78
export const COLORS = {
89
PROMOTED: 0xd9_98_e3,

apps/web/src/providers/discord/formatter.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Embed } from "@vermaysha/discord-webhook";
22

3-
import { env } from "@/env";
43
import type { Deployment, Links, VercelWebhook } from "@/schemas/vercel";
5-
import { DEFAULT_AVATAR_URL, EMOJIS, getStateProperty } from "./consts";
4+
import { BOT_AVATAR, EMOJIS, getStateProperty } from "./consts";
65

76
type EmbedCreator = (webhook: VercelWebhook) => Embed;
87

@@ -75,7 +74,7 @@ function createGenericEmbed(webhook: VercelWebhook): Embed {
7574
);
7675
embed.setFooter({
7776
text: `Event ID: ${webhook.id}`,
78-
icon_url: env.DISCORD_WEBHOOK_AVATAR_URL || DEFAULT_AVATAR_URL,
77+
icon_url: BOT_AVATAR,
7978
});
8079

8180
return embed;
@@ -94,7 +93,7 @@ function setBasicProperties(
9493
embed.setTimestamp();
9594
embed.setAuthor({
9695
name: "versend.io",
97-
icon_url: env.DISCORD_WEBHOOK_AVATAR_URL || DEFAULT_AVATAR_URL,
96+
icon_url: BOT_AVATAR,
9897
url: "https://versend.io",
9998
});
10099
}
@@ -181,7 +180,7 @@ function addDeploymentFields(
181180
if (deployment.id) {
182181
embed.setFooter({
183182
text: `Deployment ID: ${deployment.id}`,
184-
icon_url: env.DISCORD_WEBHOOK_AVATAR_URL || DEFAULT_AVATAR_URL,
183+
icon_url: BOT_AVATAR,
185184
});
186185
}
187186
}

0 commit comments

Comments
 (0)