Skip to content

Commit

Permalink
Merge pull request #116 from planetscale/slack-improve
Browse files Browse the repository at this point in the history
Slack improve
  • Loading branch information
mscoutermarsh authored Mar 9, 2024
2 parents 0a400bb + a022ec7 commit 5ecacd3
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 110 deletions.
13 changes: 6 additions & 7 deletions doc/slack_setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

- [Create a Slack app](https://api.slack.com/apps/new) if you don't have one already
- Pick a name, choose a workspace to associate your app with, and then click **Create App**
- Select the **Incoming Webhooks** feature and click the **Activate Incoming Webhooks** toggle to switch it on
- Click **Add New Webhook to Workspace**
- Pick the channel the app will post to and then click to **Authorize** your app
- You should see a new entry under the Webhook URLs for Your Workspace section

See the [Slack API docs](https://api.slack.com/messaging/webhooks) for more detailed instructions on creating Slack apps.
- Go to "OAuth and Permissions"
- Add the `chat:write` permission to the app
- Create an OAuth token for the app, this will be used as `SLACK_TOKEN`
- By default it will post to #general. In Slack, you must invite your new bot to the channel so that it can post.

- **Set environment variables in `.env`**
- Set `ENABLE_SLACK_POSTING` to **true**
- Set `SLACK_WEBHOOK_URL` to the Webhook URL from your Slack app setting
- Set `SLACK_TOKEN` to the OAuth token for the app.
- Optionally, set `SLACK_CHANNEL`
3 changes: 2 additions & 1 deletion env/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const serverEnv = {
CLOUDINARY_API_KEY: cloudinaryParser({ allowEmpty: true, default: '' }),
CLOUDINARY_API_SECRET: cloudinaryParser({ allowEmpty: true, default: '' }),
ENABLE_SLACK_POSTING: bool({ default: false }),
SLACK_WEBHOOK_URL: slackParser({ allowEmpty: true, default: '' }),
SLACK_TOKEN: slackParser({ allowEmpty: true, default: '' }),
SLACK_CHANNEL: slackParser({ allowEmpty: true, default: '#general' }),
}),
}
93 changes: 55 additions & 38 deletions lib/slack.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { serverEnv } from '@/env/server'
import type { Post } from '@prisma/client'
import { markdownToBlocks } from '@instantish/mack'
import { marked } from 'marked'
import { WebClient } from '@slack/web-api'

export async function postToSlackIfEnabled({
post,
Expand All @@ -10,46 +10,63 @@ export async function postToSlackIfEnabled({
post: Post
authorName: string
}) {
if (serverEnv.ENABLE_SLACK_POSTING && serverEnv.SLACK_WEBHOOK_URL) {
if (serverEnv.ENABLE_SLACK_POSTING && serverEnv.SLACK_TOKEN) {
const tokens = marked.lexer(post.content)
const summaryToken = tokens.find((token) => {
return (
token.type === 'paragraph' ||
token.type === 'html' ||
token.type === 'image'
)
})
const summaryBlocks = summaryToken
? await markdownToBlocks(summaryToken.raw)
: []
return fetch(serverEnv.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*<${serverEnv.NEXT_APP_URL}/post/${post.id}|${post.title}>*`,
},
const summary = summarize(tokens)

const web = new WebClient(serverEnv.SLACK_TOKEN)
return await web.chat.postMessage({
channel: serverEnv.SLACK_CHANNEL,
text: `*<${serverEnv.NEXT_APP_URL}/post/${post.id}|${post.title}>*`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*<${serverEnv.NEXT_APP_URL}/post/${post.id}|${post.title}>*`,
},
summaryBlocks[0],
{ type: 'divider' },
{
type: 'context',
elements: [
{
type: 'plain_text',
text: authorName,
emoji: true,
},
],
},
{
type: 'section',
text: {
type: 'mrkdwn',
text: summary,
},
],
}),
},
{ type: 'divider' },
{
type: 'context',
elements: [
{
type: 'plain_text',
text: authorName,
emoji: true,
},
],
},
],
})
}
}

const MAX_CHARS = 250

function summarize(tokens: marked.Token[]) {
let summary = ''
let charCount = 0

for (const token of tokens) {
if (token.type === 'paragraph') {
const text = token.text || ''
const remainingChars = MAX_CHARS - charCount
summary += ' ' + text.substring(0, remainingChars)
charCount += text.length

if (charCount >= MAX_CHARS) {
break
}
}
}

return summary
}
Loading

0 comments on commit 5ecacd3

Please sign in to comment.