Quick Thoughts with Script Kit and Notion

Altrim Beqiri

Altrim Beqiri /

Found out about Script Kit the past week. It's an awesome application that allows you to write node scripts to optimize your workflows. I’ve been playing with it for a few days by automating various flows and in this post I want to show you a script for posting quick thoughts on your Notion page.

I use Notion daily for my work and after I saw the tweet by John Lindquist's on the Quick Thoughts script I got inspired to write one that integrates with the Notion API.

John's Quick Thoughts script saves the notes locally in a .md file with the notes during the day split by a timestamp. I have implemented the same thing here, although instead of using a local file I am saving the notes on a Notion page.

In the video below you can see how the entire integration with Script Kit and Notion works.

To make the script work you will first need to create an integration in Notion. You can do that by going to the integrations page at https://www.notion.so/my-integrations and creating a new integration for your workspace. Make sure to get the token on that page since you will need to use it in the script.

Once you have the integration created you need to add that to the Notion page (database) you want the integration to work. You can probably tweak the script to use various Notion templates but for this demo I've used the following Notion template that you can duplicate here. When you have the template ready and the integration created you can connect the integration to the page by clicking on the share button on the top right corner of the page and selecting the integration on the list as shown in the image below.

Notion Integration

Now that we have the integration connected to our page we can use the script in the Script Kit. I am using TypeScript so I've set up the Script Kit to work by default with TypeScript. If you are familiar with Script Kit to create a new script run the prompt and type new notion-thoughts. This will create a new script and open your default editor to edit the script.

To interact with the Notion API I've used the official Notion JavaScript Client. You can find the entire notion thought script below. I've also added comments with references to the Notion APIs we use here so you can follow and perhaps tweak a few things to fit your use case.

import "@johnlindquist/kit";

// Menu: Notion Thoughts
// Description: Add quick thoughts to a notion journal page
// Author: Altrim Beqiri
// Twitter: @altrimbeqiri

/** @type {typeof import("@notionhq/client")} */
const { Client } = await npm("@notionhq/client");
/** @type {typeof import("date-fns")} */
const { format, parseISO, formatISO, isSameMinute } = await npm("date-fns");

// NOTION_URL=https://www.notion.so/{your_workspace}
const NOTION_URL = await env("NOTION_URL");

/**
 * To find the database ID check the following documentation under the _"Where can I find my database's ID?"_ section
 * https://developers.notion.com/docs/working-with-databases#adding-pages-to-a-database
 *
 * The URL uses the following format:
 * https://www.notion.so/{your_workspace}/b7bfd54495g558fe95281f7e33d4f178?v=...
 *                                       |--------- Database ID ----------|
 */
const DATABASE_ID = await env("NOTION_DATABASE_ID");

// NOTION_TOKEN={secret_token_from_the_integration_page}
const TOKEN = await env("NOTION_TOKEN");

// Initialize the notion client
const notion = new Client({
  auth: TOKEN,
});

/**
 * Create a heading two block
 *
 * @param content
 * @returns block object https://developers.notion.com/reference/block#heading-two-blocks
 */
const createHeadingBlock = (content: string) => {
  return {
    object: "block",
    type: "heading_2",
    heading_2: {
      text: [
        {
          type: "text",
          text: {
            content,
          },
        },
      ],
    },
  };
};

/**
 * Create a bulleted litst item block
 *
 * @param content
 * @returns block object https://developers.notion.com/reference/block#bulleted-list-item-blocks
 */
const createBulletItemBlock = (content: string) => {
  return {
    object: "block",
    type: "bulleted_list_item",
    bulleted_list_item: {
      text: [
        {
          type: "text",
          text: {
            content,
          },
        },
      ],
    },
  };
};

/**
 * Query the database by the name and today's date
 *
 * https://developers.notion.com/reference/post-database-query
 * @returns database object https://developers.notion.com/reference/database
 */
const queryDatabase = async () =>
  await notion.databases.query({
    database_id: DATABASE_ID,
    filter: {
      and: [
        {
          property: "Name",
          text: {
            contains: "Thoughts",
          },
        },
        {
          property: "Created",
          created_time: {
            equals: formatISO(new Date(), { representation: "date" }),
          },
        },
      ],
    },
    sorts: [
      {
        property: "Created",
        direction: "ascending",
      },
    ],
  });

/**
 * Create a new page in the database with today's date and a Daily tag
 *
 * https://developers.notion.com/reference/create-a-database
 * @returns https://developers.notion.com/reference/page
 */
const createPage = async () =>
  await notion.pages.create({
    parent: {
      database_id: DATABASE_ID,
    },
    icon: {
      type: "emoji",
      emoji: "📝",
    },
    properties: {
      Name: {
        title: [
          {
            text: {
              content: `${format(new Date(), "yyyy-MM-dd")} - Thoughts`,
            },
          },
        ],
      },
      Tags: {
        multi_select: [{ name: "Daily" }],
      },
    },
    children: [createHeadingBlock(`${format(new Date(), "HH:mm")}`)],
  });

const hasThoughtsForTheDay = (thoughts?: any[]) => thoughts && thoughts.length > 0;

// Query the database for the page that contains the "Thoughts" label and the today's date
const { results: database } = await queryDatabase();
// If we don't have a page for today we create a new one
const page = hasThoughtsForTheDay(database) ? database[0] : await createPage();

while (true) {
  const thought = await arg({
    placeholder: "Thought:",
    hint: `Type "open" to open journal in browser`,
  });

  // Will open the journal in a new tab in your default browser and exit the script
  if (thought === "open") {
    focusTab(`${NOTION_URL}/${DATABASE_ID}`);
    break;
  }

  // List all the children in the page
  const { results: children } = await notion.blocks.children.list({
    block_id: page.id,
    page_size: 42, // The number of items from the full list desired in the response. Maximum: 100
  });

  // Get last heading block we have on the page
  const headingBlock = [...children].reverse().find((obj: any) => obj.type === "heading_2");

  // Check if the last heading is not same time as the current time we need to create a new heading block
  const isSameTime = isSameMinute(parseISO(headingBlock?.created_time), new Date());
  if (!isSameTime) {
    await notion.blocks.children.append({
      block_id: page.id,
      children: [createHeadingBlock(format(new Date(), "HH:mm"))],
    });
  }

  // Append the item to the last heading block
  await notion.blocks.children.append({
    block_id: page.id,
    children: [createBulletItemBlock(thought)],
  });
}

Hope you find the script useful, and if you happen to tweak it and add cool stuff to it please let me know @altrimbeqiri on Twitter.

Happy Coding!