Skip to content

Instantly share code, notes, and snippets.

@astoiccoder
Last active March 27, 2024 22:49
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save astoiccoder/77577ea58eaf2a9b91cee8ca78c0a6f0 to your computer and use it in GitHub Desktop.
Save astoiccoder/77577ea58eaf2a9b91cee8ca78c0a6f0 to your computer and use it in GitHub Desktop.
reMarkable to Notion Sync
const GMAIL_LABEL_NAME = 'NotionToSync';
const SYNCED_LABEL = 'SyncedToNotion';
// if you set this, the attachments will be copied over to google drive and the links added to the notion page
const GDRIVE_FOLDER_ID = '';
const gmailToNotion = () => {
const label = GmailApp.getUserLabelByName(GMAIL_LABEL_NAME);
const successLabel = GmailApp.getUserLabelByName(SYNCED_LABEL);
label.getThreads(0, 20).forEach((thread) => {
const [message] = thread.getMessages().reverse();
postToNotion(message);
thread.removeLabel(label);
thread.addLabel(successLabel)
});
};
function getRichTextChunks(messageBody) {
let remainingString = messageBody;
const content = [];
while (remainingString.length > 0) {
// https://developers.notion.com/reference/request-limits#limits-for-property-values
if (remainingString.length <= 2000) {
content.push(getRichTextObjectForChunk(remainingString));
remainingString = '';
} else {
const maximalChunk = remainingString.substring(0, 2000);
const lastLineBreakInChunk = maximalChunk.lastIndexOf('\n');
const actualChunk = remainingString.substring(0, lastLineBreakInChunk);
content.push(getRichTextObjectForChunk(actualChunk));
remainingString = remainingString.substring(lastLineBreakInChunk + 1);
}
}
return content;
}
function getRichTextObjectForChunk(messageChunk) {
return {
type: 'text',
text: {
content: messageChunk
},
}
}
function getPdfBlocksForAttachments(message) {
var attachments = message.getAttachments();
var pdfBlocks = [];
for (var k = 0; k < attachments.length; k++) {
Logger.log('Message contains the attachment "%s" (%s bytes)', attachments[k].getName(), attachments[k].getSize());
var folder = DriveApp.getFolderById(GDRIVE_FOLDER_ID);
var ts = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyMMddHHmmss");
var createdFile = folder.createFile(attachments[k].copyBlob()).setName(attachments[k].getName() + ts);
pdfBlocks.push({
"object": "block",
"type": "paragraph",
"paragraph": {
"rich_text": [
{
"type": "text",
"text": {
"content": attachments[k].getName(),
"link": {
"url": createdFile.getUrl()
}
},
"annotations": {
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false,
"code": false,
"color": "default"
},
"plain_text": attachments[k].getName(),
"href": createdFile.getUrl()
}
],
"color": "default"
}
})
}
}
function postToNotion(message) {
const richTextChunks = getRichTextChunks(message.getPlainBody());
const pdfBlocks = GDRIVE_FOLDER_ID ? getPdfBlocksForAttachments(message) : [];
const url = 'https://api.notion.com/v1/pages';
const body = {
parent: {
type: "database_id",
database_id: "<Your Target Notion Database Id>",
},
icon: {
type: "emoji",
emoji: "📝"
},
children: [
...pdfBlocks,
{
object: 'block',
type: 'paragraph',
paragraph: {
rich_text: richTextChunks,
},
}
],
properties: {
Name: {
title: [
{
text: {
content: message.getSubject(),
},
},
],
}
}
}
UrlFetchApp.fetch(url, {
method: 'post',
contentType: "application/json",
muteHttpExceptions: false,
headers: {
Authorization: `Bearer <Your Notion Secret>`,
'Notion-Version': '2022-02-22'
},
payload: JSON.stringify(body)
});
}
@crushcup
Copy link

crushcup commented Mar 9, 2023

Hi @astoiccoder , thanks for pulling this all together. I can't wait to start using this to stay organized. Couple issues arose:

The PDF is being sent to my gmail, but the document does not make it to the notion, only the Subject. Only 1 page of the PDF gets sent from the remarkable to gmail, is there a way to send multiple pages at once?

Thanks again,

Edit: I was able to send multiple PDFs to gmail from the remarkable, but the content of the PDF is not being relayed to Notion, although the subject line appears.. very strange. Wondering if it is a setting filter or a change can be made to the script.

@astoiccoder
Copy link
Author

Hi @astoiccoder , thanks for pulling this all together. I can't wait to start using this to stay organized. Couple issues arose:

The PDF is being sent to my gmail, but the document does not make it to the notion, only the Subject. Only 1 page of the PDF gets sent from the remarkable to gmail, is there a way to send multiple pages at once?

Thanks again,

Hi @crushcup, thanks for reaching out! The remarkable changed the way it converts handwritten notes to text.
Before you always had to send it via email directly, but now conversation creates a new page in the current document containing the converted text. The send by email functionality lets you now choose in the buttom left corner if you want to send your pages as pdf attachments or as "Text in email".
So if you are converting text and want to sent it to your notion, you could choose the "Text in email" option and the script would still work as intended.

The tricky part comes with the pdf attachments (e.g. if you also want to send handwritten notes which would not be included with the "Text in email" option).
Notion unfortunately does not allow a file upload via API.
So I thought I could still make the pdfs show up on the page by storing the files to google drive with the script and then show them as an embed block on the notion page with the google drive integration, but adding a link_preview to a notion page is also not possible via API. Even in mention blocks it is not possible to add a link_preview.

The only thing I could find is now to copy the file to a google drive folder and then add the link to that google drive file to the notion page. But then of course it won't show a nice preview. If you want to have that, all you need to do is create a new google drive folder and copy the folder id from the url when you are inside the folder over to the latest version of the script.

Wish you a great weekend!

@doggiecuddles
Copy link

Hey @astoiccoder thank you so much for building this!

Everything looks good on my end but it is not showing up in notion

@astoiccoder
Copy link
Author

Hey @astoiccoder thank you so much for building this!

Everything looks good on my end but it is not showing up in notion

Hi @doggiecuddles,
A few things important to check could be:

  1. Did you use the "Text in email" option when sending the note from the remarkable?
  2. Are both labels ('NotionToSync' and 'SyncedToNotion') applied correctly on the emails that are being sent? That's a first indicator if the script is actually running through.
  3. Do you see any errors in the google apps script? (If the request to create the page in notion fails, it should show an error in the script - in order to check that you can also manually run the script after sending a fresh email or removing the 'SyncedToNotion' label on any prior email and run the script again)

@anthony88088
Copy link

hello! i'm struggling with this right now,
Exception: Request failed for https://api.notion.com returned code 401. Truncated server response: {"object":"error","status":401,"code":"unauthorized","message":"API token is invalid.","request_id":

getting the above response. nothing shows up in the database. i use convert to text and when emailing "text to email"

@samlee888
Copy link

Thanks for doing this @astoiccoder! I love notion and recently gotten my hands on Remarkable. Two truly amazing tools which will be even better if integrated OOTB.
Thanks for building this integration!

Using the latest code, I face a slightly different error.

"Exception: Request failed for https://api.notion.com returned code 400. Truncated server response: {"object":"error","status":400,"code":"validation_error","message":"body failed validation. Fix one:\nbody.parent.page_id should be defined, instea... (use muteHttpExceptions option to examine full response)"

Upon looking into this link "ramnes/notion-sdk-py#72", it seems like property values of the new page in the properties parameter must conform to the parent database's property schema. Any suggestion how to do this?

Thanks in advanced!


Exception: Request failed for https://api.notion.com/ returned code 400. Truncated server response: {"object":"error","status":400,"code":"validation_error","message":"body failed validation. Fix one:\nbody.parent.page_id should be defined, instea... (use muteHttpExceptions option to examine full response)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment