Automating Grocery Lists with Notion and Telegram

TL;DR

Managing grocery lists manually was chaotic, so I automated the process using Notion and a Telegram bot. A TypeScript script connects to Notion’s API, gathers ingredients from selected recipes, and creates a shopping list automatically…


This content originally appeared on DEV Community and was authored by Alvaro Eduardo Falcón Morales

TL;DR

Managing grocery lists manually was chaotic, so I automated the process using Notion and a Telegram bot. A TypeScript script connects to Notion’s API, gathers ingredients from selected recipes, and creates a shopping list automatically. The bot allows me to generate lists and mark items as purchased with simple commands.

Introduction

My partner and I recently decided to eat healthier, which meant cooking more at home. She shared several recipe links, but when I went grocery shopping, I realized managing ingredients manually was overwhelming. Jumping between recipe links while trying to buy everything efficiently was frustrating.

That’s when I had an idea: automate the whole process!

The Notion Setup

To keep track of everything, I used Notion, where we already manage household tasks. I created several databases:

  • Ingredients – A list of all the ingredients we might need.
  • Recipes – Each recipe links to the necessary ingredients.
  • Shopping Lists – A database where each entry represents a shopping trip, containing a to-do list of ingredients to buy.

This setup made organizing ingredients easier, but I still had to manually transfer them from recipes to the shopping list. Not efficient enough!

Automating with TypeScript and Notion’s API

To fully automate the process, I wrote a TypeScript script that connects to Notion’s API. Here’s what it does:

1- Scans all recipes where a specific checkbox is enabled.

async function getRecipesToAdd() {
  const response = await notion.databases.query({
    database_id: RECIPES_DB_ID, // Your notion db ID, you can grab it from the url
    filter: {
      property: "Add to list?", // The checkbox we manually enable if we want recipes to be processed
      checkbox: {
        equals: true,
      },
    },
  });
  return response.results;
}

2- Extracts the required ingredients.

async function getIngredientsList(recipePage) {
  const relationArray = recipePage.properties["Ingredients"]?.relation;
  if (!relationArray || !relationArray.length) {
    return [];
  }

  const ingredientNames = [];

  for (const rel of relationArray) {
    const ingredientPageId = rel.id;

    const ingredientPage = await notion.pages.retrieve({
      page_id: ingredientPageId,
    });
    const nameProp = (ingredientPage as PageObjectResponse).properties[
      "Ingredient"
    ];
    let ingredientName = "Unnamed Ingredient";
    if (
      nameProp &&
      isTitleProperty(nameProp) &&
      nameProp.title &&
      nameProp.title[0]
    ) {
      ingredientName = nameProp.title[0].plain_text;
    }

    ingredientNames.push(ingredientName);
  }

  return ingredientNames;
}

3- Creates a new shopping list.

async function createShoppingListPage() {
  const todayStr = new Date().toISOString().slice(0, 10);
  const pageName = `Nueva lista ${todayStr}`;

  return await notion.pages.create({
    parent: { database_id: SHOPPING_LISTS_DB_ID },
    properties: {
      Name: {
        title: [{ type: "text", text: { content: pageName } }],
      },
      Fecha: {
        date: { start: todayStr },
      },
      Comprado: {
        checkbox: false,
      },
    },
  });
}

4- Populate the page with ingredients, we are going to use to-do blocks

async function appendIngredientChecklist(pageId, ingredients) {
  const children = ingredients.map((item) => ({
    object: "block",
    type: "to_do",
    to_do: {
      rich_text: [
        {
          type: "text",
          text: { content: item },
        },
      ],
      checked: false,
    },
  }));

  await notion.blocks.children.append({
    block_id: pageId,
    children,
  });
}

This meant that with a simple selection of recipes, my shopping list would be generated instantly.

Running the Script via Telegram

I didn’t want to manually run the script on my computer every time, so I integrated it with Telegram:

  • I built a Telegram bot using telegraf, that triggers the script with a command.
  • The bot automatically compiles the grocery list inside Notion.
bot.command("import", async (ctx) => {
  const isValid = await checkUserValid(ctx.from.username, ctx); // Just a check making sure my partner and I are the only ones allowed to use certain commands
  if (!isValid) {
    return;
  }
  await ctx.reply("Importing ingredients...");
  return buildShoppingList(ctx);
});
  • A second command lists pending shopping lists.
export async function listShoppingLists(context: Context) {
  await context.reply("Loading shopping list...");
  const response = await notion.databases.query({
    database_id: process.env.SHOPPING_LIST_DB_ID,
    filter: {
      property: "Bought",
      checkbox: {
        equals: false,
      },
    },
  });

  if (response.results.length > 0) {
    await context.reply(
      `${context.from.first_name}, elements pending to buy:`,
    );
  } else {
    return context.reply("No elements to add.");
  }

  for (const page of response.results) {
    const allIngredients: string[] = [];
    const ingredients = await notion.blocks.children.list({
      block_id: page.id,
    });
    allIngredients.push(
      ...ingredients.results
        .map((ingredient: BlockObjectResponse) => {
          if (ingredient.type === "to_do") {
            const checked = ingredient.to_do.checked ? "" : "🔲";
            return `${checked} ${ingredient.to_do.rich_text[0].plain_text}`;
          }
          return null;
        })
        .filter(Boolean),
    );
    if (allIngredients.length > 0) {
      const inlineKeyboard = Markup.inlineKeyboard([
        Markup.button.callback(
          "Mark as bought",
          `markPurchased:${page.id}`,
        ),
      ]);
      const nameProp = (page as PageObjectResponse).properties["Name"];
      if (isTitleProperty(nameProp)) {
        await context.reply(`${nameProp.title[0].plain_text}:`);
      }
      await context.reply(allIngredients.join("\n"), inlineKeyboard);
    }
  }
}
  • A button allows marking a list as purchased (added in the code above) and its handler.
bot.action(/markPurchased:(.+)/, async (ctx) => {
  const isValid = await checkUserValid(ctx.from.username, ctx);
  if (!isValid) {
    return;
  }
  const pageId = ctx.match[1];
  await markAsPurchased(ctx, pageId);
});  

Now, with a quick /import command, I get my groceries organized effortlessly.

Next Steps

I initially built this for personal use, but it was also a great way to experiment with Notion’s API. Some ideas for future improvements:

  • Recipe scaling: Adjust ingredient quantities dynamically based on portions.
  • Multiple store support: Categorize ingredients by where to buy them.
  • Something not related to groceries!: Maybe a library and a local search on Telegram to know if you have a certain book?

What Would You Add?

This has been a fun side project, but I’d love to hear ideas for making it even better. Would you find something like this useful? Let me know in the comments!


This content originally appeared on DEV Community and was authored by Alvaro Eduardo Falcón Morales


Print Share Comment Cite Upload Translate Updates
APA

Alvaro Eduardo Falcón Morales | Sciencx (2025-02-21T12:17:50+00:00) Automating Grocery Lists with Notion and Telegram. Retrieved from https://www.scien.cx/2025/02/21/automating-grocery-lists-with-notion-and-telegram/

MLA
" » Automating Grocery Lists with Notion and Telegram." Alvaro Eduardo Falcón Morales | Sciencx - Friday February 21, 2025, https://www.scien.cx/2025/02/21/automating-grocery-lists-with-notion-and-telegram/
HARVARD
Alvaro Eduardo Falcón Morales | Sciencx Friday February 21, 2025 » Automating Grocery Lists with Notion and Telegram., viewed ,<https://www.scien.cx/2025/02/21/automating-grocery-lists-with-notion-and-telegram/>
VANCOUVER
Alvaro Eduardo Falcón Morales | Sciencx - » Automating Grocery Lists with Notion and Telegram. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/02/21/automating-grocery-lists-with-notion-and-telegram/
CHICAGO
" » Automating Grocery Lists with Notion and Telegram." Alvaro Eduardo Falcón Morales | Sciencx - Accessed . https://www.scien.cx/2025/02/21/automating-grocery-lists-with-notion-and-telegram/
IEEE
" » Automating Grocery Lists with Notion and Telegram." Alvaro Eduardo Falcón Morales | Sciencx [Online]. Available: https://www.scien.cx/2025/02/21/automating-grocery-lists-with-notion-and-telegram/. [Accessed: ]
rf:citation
» Automating Grocery Lists with Notion and Telegram | Alvaro Eduardo Falcón Morales | Sciencx | https://www.scien.cx/2025/02/21/automating-grocery-lists-with-notion-and-telegram/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.