Last active
January 5, 2024 08:22
-
-
Save Fluf22/1f896f84ac9eb6a319b733189a757de2 to your computer and use it in GitHub Desktop.
Transfer notes from unzipped Google Takeout Keep folder to Nextcloud folder
This file contains hidden or 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 program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <https://www.gnu.org/licenses/>. | |
*/ | |
const fs = require("fs"); | |
const https = require("https"); | |
// How to use this script: execute `node send-notes-to-nextcloud.js` to see this message formatted | |
const printUsage = () => { | |
console.log("Usage:\nnode send-notes-to-nextcloud.js </e/ email> <password> [--replace=[true|false] --join-labels=[true|false] | --check=true]"); | |
return; | |
}; | |
// Get username and password from arguments, and store basic HTTP auth | |
const username = process.argv[2]; | |
const password = process.argv[3]; | |
const auth = `${username}:${password}`; | |
const url = `https://ecloud.global/index.php/apps/notes/api/v0.2/notes`; // /e/ Cloud API URL | |
const currDir = process.cwd(); // Get current directory path | |
const gkNotes = fs.readdirSync(currDir).filter(gkNote => gkNote.includes(".json")); // Get list of Google Keep notes | |
const checkOnly = process.argv.length === 5 && process.argv[4].includes("--check") && process.argv[4].split("=")[1] === "true"; // Is check only mode enabled | |
if (checkOnly) { | |
// Get all your Nextcloud notes | |
https.get(url, { auth }, (getResp) => { | |
let dataGet = ""; | |
// A chunk of data has been recieved. | |
getResp.on("data", (chunk) => { | |
dataGet += chunk; | |
}); | |
// The whole response has been received. Process the result. | |
getResp.on("end", () => { | |
// Compare the quantity of notes in your Google Keep folder and in your Nextcloud notes folder in the /e/ Cloud | |
console.log(`You have ${JSON.parse(dataGet).length} notes in your /e/ Cloud account, and ${gkNotes.length} notes in you Google Keep folder.`); | |
}); | |
}); | |
} else if (process.argv.length === 5 || process.argv.length === 6) { | |
const enableReplace = process.argv[4].includes("--replace") && process.argv[4].split("=")[1] === "true"; // Should I replace Notes already in Nextcloud | |
const joinLabels = process.argv[5].includes("--join-labels") && process.argv[5].split("=")[1] === "true"; // How to deal with multiple Google Keep categories | |
let failedTransfer = 0; // Track upload failures | |
// GET all your existing Nextcloud notes | |
https.get(url, { auth }, (getResp) => { | |
let dataGet = ""; | |
// A chunk of data has been recieved. | |
getResp.on("data", (chunk) => { | |
dataGet += chunk; | |
}); | |
// The whole response has been received. Process the result. | |
getResp.on("end", () => { | |
const ncNotes = JSON.parse(dataGet); // Parse the notes in a JSON format | |
for (let i = 0; i < gkNotes.length; ++i) { | |
// Initiate a request every 2 seconds to deal with /e/ Cloud API limitation | |
setTimeout(() => { | |
const gkNoteStr = fs.readFileSync(`${currDir}/${gkNotes[i]}`, "utf-8"); // Read the content of a Google Keep note | |
const gkNote = JSON.parse(gkNoteStr); // Parse it in a JSON format | |
// Translate the Google Keep note content in markdown to have compatible lists | |
const newNoteContent = Object.keys(gkNote).includes("textContent") ? gkNote.textContent : gkNote.listContent.map(item => (`- [${item.isChecked ? "x" : " "}] ${item.text}`)).join("\n"); | |
// Create the note title | |
const newNoteTitle = gkNote.title !== "" ? gkNote.title : newNoteContent.split(" ").slice(0, 3).join(" "); | |
// Create the new note object | |
const newNote = { | |
title: newNoteTitle, | |
content: newNoteTitle + "\n" + newNoteContent, | |
category: Object.keys(gkNote).includes("labels") ? (joinLabels ? gkNote.labels.map(item => item.name).join("/") : gkNote.labels[0].name) : "", | |
favorite: gkNote.isPinned | |
}; | |
// Store it in a string to be able to send it in a POST request body | |
const newNoteStr = JSON.stringify(newNote); | |
// Check if the note already exists in Nextcloud | |
const existingNcNote = ncNotes.find(ncNote => ncNote.title === newNoteTitle); | |
// If the --replace option has been set to true, update the note instead of creating a new one | |
if (enableReplace && existingNcNote !== undefined && existingNcNote.id !== undefined) { | |
const putReq = https.request(`${url}/${existingNcNote.id}`, { | |
method: "PUT", | |
auth, | |
data: JSON.stringify(newNote), | |
headers: { | |
"Content-Type": "application/json", | |
"Content-Length": Buffer.byteLength(newNoteStr) | |
} | |
}, (putResp) => { | |
let putData = ""; | |
// A chunk of data has been recieved. | |
putResp.on("data", (chunk) => { | |
putData += chunk; | |
}); | |
// The whole response has been received. Process the result. | |
putResp.on("end", () => { | |
try { | |
console.log("Replaced note: ", JSON.parse(putData).title); | |
} catch { | |
// Watch for failed transfer | |
console.log(`Something's fishy (failed transfer count: ${failedTransfer})`); | |
failedTransfer = failedTransfer + 1; | |
} | |
}); | |
}).on("error", (err) => { | |
console.log("Error during PUT request: " + err.message); | |
}); | |
putReq.write(newNoteStr); | |
putReq.end(); | |
} else { // ...otherwise, create a new note on Nextcloud | |
const postReq = https.request(url, { | |
method: "POST", | |
auth, | |
data: JSON.stringify(newNote), | |
headers: { | |
"Content-Type": "application/json", | |
"Content-Length": Buffer.byteLength(newNoteStr) | |
} | |
}, (postResp) => { | |
let postData = ""; | |
// A chunk of data has been recieved. | |
postResp.on("data", (chunk) => { | |
postData += chunk; | |
}); | |
// The whole response has been received. Process the result. | |
postResp.on("end", () => { | |
try { | |
console.log("Created note: ", JSON.parse(postData).title) | |
} catch { | |
// Watch for failed transfer | |
console.log(`Something's fishy (failed transfer count: ${failedTransfer})`); | |
failedTransfer = failedTransfer + 1; | |
} | |
}); | |
}).on("error", (err) => { | |
console.log("Error during POST request: " + err.message); | |
}); | |
postReq.write(newNoteStr); | |
postReq.end(); | |
} | |
}, 2000 * i); | |
} | |
}); | |
}).on("error", (err) => { | |
// Watch for request failure | |
console.log("Error during GET request: " + err.message); | |
}); | |
} else { | |
printUsage(); | |
return; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment