Skip to content

Instantly share code, notes, and snippets.

@seratch
Last active May 23, 2021 21:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seratch/6ab9139d15ad33c9b1e149327b5f14fa to your computer and use it in GitHub Desktop.
Save seratch/6ab9139d15ad33c9b1e149327b5f14fa to your computer and use it in GitHub Desktop.
Send Slack saved Items to Notion Database
plugins {
id("org.jetbrains.kotlin.jvm") version "1.5.0"
id("application")
}
repositories {
mavenCentral()
}
ext.slackBoltVersion = "1.8.0"
ext.notionSdkVersion = "0.1.9"
dependencies {
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.slack.api:bolt-jetty:${slackBoltVersion}")
implementation("com.github.seratch:notion-sdk-jvm-core:${notionSdkVersion}")
implementation("com.github.seratch:notion-sdk-jvm-httpclient:${notionSdkVersion}")
implementation("com.github.seratch:notion-sdk-jvm-slf4j:${notionSdkVersion}")
implementation("org.slf4j:slf4j-simple:1.7.30") // or logback-classic
}
application {
mainClassName = "MyAppKt" // add "Kt" suffix for main function source file
}
import com.slack.api.Slack
import com.slack.api.bolt.App
import com.slack.api.bolt.AppConfig
import com.slack.api.bolt.jetty.SlackAppServer
import com.slack.api.model.event.StarAddedEvent
import notion.api.v1.NotionClient
import notion.api.v1.http.JavaNetHttpClient
import notion.api.v1.logging.Slf4jLogger
import notion.api.v1.model.common.PropertyType
import notion.api.v1.model.common.RichTextLinkType
import notion.api.v1.model.pages.PageParent
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import notion.api.v1.model.pages.PageProperty as prop
fun main() {
System.setProperty("org.slf4j.simpleLogger.log.com.slack.api", "debug")
System.setProperty("org.slf4j.simpleLogger.log.notion.api", "debug")
// Bolt app
val app = App(
// Bot token scopes: users:read, team:read, channels:read/groups:read/mpim:read/im:read
// User token scopes: stars:read
AppConfig.builder()
.singleTeamBotToken(System.getenv("SLACK_BOT_TOKEN"))
.signingSecret(System.getenv("SLACK_SIGNING_SECRET"))
.build()
)
// The installer's user token
val installerUserToken = System.getenv("SLACK_USER_TOKEN")
val userAuthTest = Slack.getInstance().methods().authTest { it.token(installerUserToken) }
// This app works only for this user
val installerUserId = userAuthTest.userId
// Notion integration
val notionClient = NotionClient(
token = System.getenv("NOTION_TOKEN"),
httpClient = JavaNetHttpClient(),
logger = Slf4jLogger(),
)
val notionDatabaseId: String = findNotionDatabaseId(notionClient)
// Be sure to invite this app's bot user to the channel!
app.event(StarAddedEvent::class.java) { req, ctx ->
val item = req.event.item
// send only the messages saved by the app installer
if (req.event.user == installerUserId && item.message != null) {
app.executorService().submit {
val teamId = ctx.teamId
val team = ctx.client().teamInfo { it.team(teamId) }.team
val channelId = item.channel
val channel = ctx.client().conversationsInfo { it.channel(item.channel) }.channel
val permalink = ctx.client().chatGetPermalink {
it.channel(item.channel).messageTs(item.message.ts)
}.permalink
val epochMillis = java.lang.Long.parseLong(item.message.ts.split(".")[0] + "000")
val postedAt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), ZoneId.systemDefault())
.toOffsetDateTime().toString()
val permalinkLabel = "${team.domain}.slack.com / #${channel.name} / $postedAt"
val messageUserId = item.message.user
val userPermalink = "https://app.slack.com/client/$teamId/$channelId/user_profile/$messageUserId"
val user = ctx.client().usersInfo { it.user(messageUserId) }.user
val postedByLabel = "${user.realName} (@${user.name})"
val notionPageCreation = notionClient.createPage(
parent = PageParent.database(notionDatabaseId),
properties = mapOf(
"Message" to prop(
type = PropertyType.Title,
title = listOf(prop.RichText(
text = prop.RichText.Text(
content = permalinkLabel,
link = prop.RichText.Link(type = RichTextLinkType.Url, url = permalink)
),
plainText = permalinkLabel,
))
),
"Text" to prop(
type = PropertyType.RichText,
richText = listOf(prop.RichText(
text = prop.RichText.Text(content = item.message.text),
plainText = item.message.text,
))
),
"Posted By" to prop(
type = PropertyType.RichText,
richText = listOf(prop.RichText(
text = prop.RichText.Text(
content = postedByLabel,
link = prop.RichText.Link(type = RichTextLinkType.Url, url = userPermalink)
),
plainText = postedByLabel,
))
),
)
)
ctx.logger.info("Notion page created: {}", notionPageCreation)
}
}
ctx.ack()
}
val server = SlackAppServer(app)
server.start() // http://localhost:3000/slack/events
}
fun findNotionDatabaseId(notionClient: NotionClient): String {
var foundDatabaseId: String? = null
var currentCursor: String? = null
while (foundDatabaseId == null) {
val listResponse = notionClient.listDatabases(startCursor = currentCursor)
if (listResponse.results.isEmpty()) {
break
}
currentCursor = listResponse.nextCursor
// The integration should have the access to the database
foundDatabaseId = listResponse.results.find { d ->
d.title.any { it.plainText.contains("My Slack Saved Items") }
}?.id
}
if (foundDatabaseId == null) {
throw IllegalStateException(
"""Create a Notion database named "My Slack Saved Items"
|and invite your app to the database""".trimMargin()
)
}
return foundDatabaseId
}
_metadata:
major_version: 1
minor_version: 1
display_information:
name: send-slack-saved-items-to-notion
features:
app_home:
home_tab_enabled: false
messages_tab_enabled: false
messages_tab_read_only_enabled: true
bot_user:
display_name: Notion Bot
always_online: true
oauth_config:
scopes:
user:
- stars:read
bot:
- channels:read
- groups:read
- im:read
- mpim:read
- team:read
- users:read
settings:
event_subscriptions:
request_url: https://your-domain.ngrok.io/slack/events
user_events:
- star_added
org_deploy_enabled: false
socket_mode_enabled: false
is_hosted: false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment