Skip to content

Commit

Permalink
Merge pull request #5 from takoba/improve-dx
Browse files Browse the repository at this point in the history
improve Developer eXperience
  • Loading branch information
takoba committed Feb 3, 2022
2 parents 3831545 + d328928 commit 6a798af
Show file tree
Hide file tree
Showing 4 changed files with 1,184 additions and 87 deletions.
50 changes: 50 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"root": true,
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:import/errors",
"plugin:import/typescript",
"plugin:prettier/recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"import",
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-unused-vars": "error",
"no-unused-vars": "off",
"sort-imports": "off",
"import/order": [
"error",
{
"alphabetize": {
"order": "asc"
}
}
],
"prettier/prettier": [
"error",
{
"printWidth": 120,
"semi": false,
"singleQuote": true
}
]
},
"overrides": []
}
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
"devDependencies": {
"@types/node": "^17.0.13",
"@types/url-parse": "^1.4.7",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"eslint": "^8.8.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-prettier": "^4.0.0",
"prettier": "^2.5.1",
"ts-node": "^10.4.0",
"typescript": "^4.5.5"
},
Expand Down
187 changes: 103 additions & 84 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Client } from '@notionhq/client'
import {App, ContextBlock, DividerBlock, KnownBlock, SectionBlock} from '@slack/bolt'
import { URL } from 'url'
import {GetDatabaseResponse, GetPageResponse} from "@notionhq/client/build/src/api-endpoints"
import { Client } from '@notionhq/client'
import { GetBlockResponse, GetDatabaseResponse, GetPageResponse } from '@notionhq/client/build/src/api-endpoints'
import { App, Context, ContextBlock, DividerBlock, KnownBlock, MessageEvent, SayFn, SectionBlock } from '@slack/bolt'

const app = new App({
token: process.env.SLACK_BOT_TOKEN,
Expand All @@ -12,96 +12,115 @@ const notion = new Client({
auth: process.env.NOTION_TOKEN,
})


app.message(/(https?:\/\/(www\.)?notion\.so\/[A-z0-9\-_]+\/[A-z0-9\-_#?=&;]+)/, async ({ message, context, say}) => {
console.debug("DEBUG: dump `message`", message)
console.debug("DEBUG: dump `context`", context)

const url = context.matches[0].replace(/&/, '&')

const parsedUrl = new URL(url)
const parsePageIdFromPathname = (pathname: string) => pathname.replace(/^\/[0-9A-z\-_]+\//, '').split('-').splice(-1)[0]
const page_id: string = parsedUrl.searchParams.get('p') !== null
? parsedUrl.searchParams.get('p') ?? parsePageIdFromPathname(parsedUrl.pathname)
: parsePageIdFromPathname(parsedUrl.pathname)
const block_id = parsedUrl.hash.slice(1) || null

let page, database;
try {
page = await notion.pages.retrieve({ page_id })
console.debug("DEBUG: dump `notion.pages.retrieve()`", { page_id }, page)
} catch (error) {
console.error("ERROR: failed notion.pages.retrieve()", { error })

app.message(
/(https?:\/\/(www\.)?notion\.so\/[A-z0-9\-_]+\/[A-z0-9\-_#?=&;]+)/,
async ({ message, context, say }: { message: MessageEvent; context: Context; say: SayFn }) => {
console.debug('DEBUG: dump `message`', message)
console.debug('DEBUG: dump `context`', context)

const url = context.matches[0].replace(/&/, '&')

const parsedUrl = new URL(url)
const parsePageIdFromPathname = (pathname: string) =>
pathname
.replace(/^\/[0-9A-z\-_]+\//, '')
.split('-')
.splice(-1)[0]
const page_id: string =
parsedUrl.searchParams.get('p') !== null
? parsedUrl.searchParams.get('p') ?? parsePageIdFromPathname(parsedUrl.pathname)
: parsePageIdFromPathname(parsedUrl.pathname)
const block_id = parsedUrl.hash.slice(1) || null

let page, database
try {
database = await notion.databases.retrieve({ database_id: page_id })
console.debug("DEBUG: dump `notion.databases.retrieve()`", { database_id: page_id }, database)
page = await notion.pages.retrieve({ page_id })
console.debug('DEBUG: dump `notion.pages.retrieve()`', { page_id }, page)
} catch (error) {
console.error("ERROR: failed notion.databases.retrieve()", { error })
throw error
}
}

const block = block_id ? await notion.blocks.retrieve({ block_id }) : null
console.debug("DEBUG: dump `notion.blocks.retrieve()`", { block_id }, block)

const object = page || database as GetPageResponse | GetDatabaseResponse

const title = ((obj: GetPageResponse | GetDatabaseResponse) => {
if (obj.object === 'page') {
// @ts-ignore
return Object.values(obj.properties).find(elem => elem.id === 'title')?.title.find(elem => elem.plain_text !== undefined)?.plain_text
} else if (obj.object === 'database') {
// @ts-ignore
return obj.title.find(elem => elem.type === 'text')?.plain_text
console.error('ERROR: failed notion.pages.retrieve()', { error })

try {
database = await notion.databases.retrieve({ database_id: page_id })
console.debug('DEBUG: dump `notion.databases.retrieve()`', { database_id: page_id }, database)
} catch (error) {
console.error('ERROR: failed notion.databases.retrieve()', { error })
throw error
}
}

// @ts-ignore
throw new Error(`Unexpected object. obj.object: ${obj.object}`)
})(object)
const block: GetBlockResponse | { [key: string]: string | object | boolean } | null = block_id
? await notion.blocks.retrieve({ block_id })
: null
console.debug('DEBUG: dump `notion.blocks.retrieve()`', { block_id }, block)

const object = page || (database as GetPageResponse | GetDatabaseResponse)

const title = ((obj: GetPageResponse | GetDatabaseResponse) => {
if (obj.object === 'page') {
return (('properties' in obj ? obj.properties : []) as { type: string; plain_text: string }[]).find(
(elem) => elem.type === 'title'
)?.plain_text
} else if (obj.object === 'database') {
return (
'title' in obj
? obj.title
: (('title' in obj.properties ? obj.properties.title : []) as { type: string; plain_text: string }[])
).find((elem) => elem.type === 'text')?.plain_text
}

const blocks: KnownBlock[] = []
blocks.push({
type: "section",
text: {
type: "mrkdwn",
text: `*<${url}|${title}>*`,
},
} as SectionBlock)
throw new Error(`Unexpected object. obj.object: ${'object' in obj && (obj as { object: string }).object}`)
})(object)

if (block !== null) {
const blocks: KnownBlock[] = []
blocks.push({
"type": "section",
"text": {
"type": "mrkdwn",
// @ts-ignore
"text": `${block[block.type].text.map(obj => obj.plain_text).join(' ') || 'content is unloaeded :cry:'}`,
}
type: 'section',
text: {
type: 'mrkdwn',
text: `*<${url}|${title}>*`,
},
} as SectionBlock)
}
blocks.push({
"type": "divider"
} as DividerBlock)
blocks.push({
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Posted by <https://github.com/takoba/notion-opener|notion-opener>"
}
]
} as ContextBlock)

await say({
// @ts-ignore
thread_ts: message.thread_ts ?? null,
attachments: [{
blocks,
fallback: `attachment failure. <${url}>`
}]
})
})

if (block !== null) {
blocks.push({
type: 'section',
text: {
type: 'mrkdwn',
text: `${
(
('type' in block &&
(block.type as string) in block &&
(block[block.type as string] as { text: { plain_text: string }[] })) || { text: [] }
)?.text
.map((obj) => obj.plain_text)
.join(' ') || 'content is unloaeded :cry:'
}`,
},
} as SectionBlock)
}
blocks.push({
type: 'divider',
} as DividerBlock)
blocks.push({
type: 'context',
elements: [
{
type: 'mrkdwn',
text: 'Posted by <https://github.com/takoba/notion-opener|notion-opener>',
},
],
} as ContextBlock)

await say({
thread_ts: 'thread_ts' in message ? message.thread_ts : undefined,
attachments: [
{
blocks,
fallback: `attachment failure. <${url}>`,
},
],
})
}
)
;(async () => {
await app.start(process.env.PORT || 3000)
console.info(`INFO: Bolt app is running!`, { app })
Expand Down

0 comments on commit 6a798af

Please sign in to comment.