Created
September 8, 2024 09:34
-
-
Save xpcoffee/f1131c99db0fa1b4c77c88e5561bc09d to your computer and use it in GitHub Desktop.
Google Apps Script to add notes from emails to journal entries in a github repo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This script allows me to email myself notes and this appends them to my daily notes in my private note repo | |
/** | |
* Worlflow | |
*/ | |
function syncEmailNotes() { | |
const { content, noteMessages } = getNotesFromEmails() | |
if (content.length === 0) { | |
Logger.log("No notes found in inbox. Stopping.") | |
return | |
} else { | |
Logger.log("Found " + noteMessages.length + " notemails.") | |
} | |
appendToDailyNote("[email protected]", content) | |
archiveThreads(noteMessages) | |
} | |
/** | |
* Gmail | |
*/ | |
NOTE_SENDER_EMAIL = "<your-email>@gmail.com" | |
NOTE_TAG = "#notemail" | |
function getNotesFromEmails() { | |
const threads = GmailApp.getInboxThreads() | |
const noteMessages = threads.reduce(extractNotes, []) | |
let content = "" | |
for (let i = 0; i < noteMessages.length; i++) { | |
body = noteMessages[i].message.getPlainBody() | |
content += "\n\n" + splitBodyToBulletNotes(body) | |
} | |
return { content, noteMessages } | |
} | |
/** | |
* @param {string} body | |
*/ | |
function splitBodyToBulletNotes(body) { | |
return body.split("\n").filter(line => line.trim().length > 0).map(line => `- ${line.trim()} ${NOTE_TAG}`).join("\n") | |
} | |
/** | |
* returns both messsages and threads to allow threads to be archived later | |
* @param {{message: GmailApp.GmailMessage; thread: GmailApp.GmailThread}[]} acc message accumulator | |
* @param {GmailApp.GmailThread} thread | |
*/ | |
function extractNotes(acc, thread) { | |
Logger.log(thread.getFirstMessageSubject() + " " + thread.getMessages()[0].getFrom()) | |
if (!thread.getFirstMessageSubject().toLowerCase().includes("notes")) { | |
return acc | |
} | |
const message = thread.getMessages()[0] | |
if (!message.getFrom().includes(NOTE_SENDER_EMAIL)) { | |
return acc | |
} | |
acc.push({ message, thread }) | |
return acc | |
} | |
/** | |
* @param {{message: GmailApp.GmailMessage; thread: GmailApp.GmailThread}[]} threadMessages | |
*/ | |
function archiveThreads(threadMessages) { | |
for (let i = 0; i < threadMessages.length; i++) { | |
threadMessages[i].thread.moveToArchive() | |
} | |
} | |
/** | |
* Github | |
*/ | |
const REPO = "xpcoffee/notes" // private repo | |
const GIT_API_URL = `https://api.github.com/repos/${REPO}/contents/` | |
const REPO_COMMITER = { "name": "<your-commiter-name>", "email": "<your-comitter-email>@gmail.com" } | |
function appendToDailyNote(sender, newContent) { | |
const date = Utilities.formatDate(new Date(), "CET", "yyyy-MM-dd") | |
const dailyNoteFileName = `journal/${date}.md` | |
const url = GIT_API_URL + dailyNoteFileName | |
const fileResult = getFile(url) | |
switch (fileResult.status) { | |
case 200: | |
// append to file | |
const content = fileResult.content + "\n\n" + newContent | |
updateFile({ url, sha: fileResult.sha, content }) | |
break | |
case 404: | |
// create new file if it doesn't already exist | |
const noteHeader = `#${date}\n\n` | |
updateFile({ url, content: noteHeader + newContent }) | |
break | |
default: | |
Logger.error(fileResult) | |
return | |
} | |
Logger.log("Successfully updated " + dailyNoteFileName) | |
} | |
function getFile(url) { | |
Logger.log(`Fetching ${url}`) | |
var urlFetchOptions = { | |
"method": "GET", | |
"headers": { | |
"Accept": "Accept: application/vnd.github+json", | |
"Authorization": `Bearer ${getGitToken()}`, | |
"X-GitHub-Api-Version": "2022-11-28", | |
}, | |
"redirect": "follow", | |
"muteHttpExceptions": true | |
} | |
var resp = JSON.parse(UrlFetchApp.fetch(url, urlFetchOptions).getContentText()) | |
if (resp.status !== undefined && resp.status !== 200) { | |
return resp | |
} | |
const contentBlob = Utilities.base64Decode(resp.content, Utilities.Charset.UTF_8) | |
const content = Utilities.newBlob(contentBlob).getDataAsString() | |
return { | |
status: 200, | |
content, | |
sha: resp.sha | |
} | |
} | |
function updateFile({ url, sha, content }) { | |
// log out content for now just in case its useful in debugging edits over time. remove after we have confidence in script | |
Logger.log((sha ? `Updating ${url}` : `Creating ${url}`) + ` with ${content} -> ${Utilities.base64Encode(content)}`) | |
const body = { | |
"message": "Updating notes from emails", | |
"committer": REPO_COMMITER, | |
"content": Utilities.base64Encode(content), | |
} | |
if (sha) { | |
body.sha = sha | |
} | |
var urlFetchOptions = { | |
"method": "PUT", | |
"headers": { | |
"Accept": "Accept: application/vnd.github+json", | |
"Authorization": `Bearer ${getGitToken()}`, | |
"X-GitHub-Api-Version": "2022-11-28", | |
}, | |
"redirect": "follow", | |
"payload": JSON.stringify(body), | |
} | |
UrlFetchApp.fetch(url, urlFetchOptions) | |
} | |
function getGitToken() { | |
// generate new auth token for your repo at https://github.com/settings/tokens?type=beta | |
// save in apps script project settings | |
return PropertiesService.getScriptProperties().getProperty("GithubAuthToken") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment