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)
});
}
@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