initial commit
This commit is contained in:
commit
4646cecd92
|
@ -0,0 +1,3 @@
|
|||
md2imap/mbox
|
||||
md2imap/*.imap
|
||||
md2imap/input/*
|
|
@ -0,0 +1,8 @@
|
|||
# cryptee-to-markdown
|
||||
A simple js script to convert your .uecd [Cryptee](https://github.com/cryptee) documents to markdown.
|
||||
|
||||
**Deprecated**: see delta2md instead
|
||||
|
||||
Usage:
|
||||
* Download .uecd files from the crypt.ee app.
|
||||
* call `node node-ui.js <inputUecdFolder>` or open `web-ui.html` in browser.
|
|
@ -0,0 +1,117 @@
|
|||
function getMarkDown(jsonInput)
|
||||
{
|
||||
function startLastWith(text)
|
||||
{
|
||||
temp = result.split('\n');
|
||||
temp[temp.length - 1] = text + temp[temp.length - 1];
|
||||
result = temp.join('\n');
|
||||
}
|
||||
|
||||
function dressUp(text, cloth)
|
||||
{
|
||||
return cloth + text + cloth;
|
||||
}
|
||||
|
||||
function createCodeBlocks()
|
||||
{
|
||||
temp = result.split('\n');
|
||||
var code = false;
|
||||
for (var i in temp)
|
||||
{
|
||||
if (temp[i].startsWith("~~~"))
|
||||
{
|
||||
if (!code)
|
||||
{
|
||||
code = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp[i] = temp[i].substr(3);
|
||||
}
|
||||
}
|
||||
else if (code)
|
||||
{
|
||||
temp[i] += "~~~";
|
||||
code = false;
|
||||
}
|
||||
}
|
||||
result = temp.join('\n');
|
||||
}
|
||||
|
||||
var data = JSON.parse(jsonInput);
|
||||
var result = "";
|
||||
var code = false;
|
||||
|
||||
for (var i in data.ops)
|
||||
{
|
||||
var elt = data.ops[i];
|
||||
var text = elt.insert;
|
||||
|
||||
if (text.image)
|
||||
{
|
||||
var alt = "";
|
||||
if (elt.attributes && elt.attributes.alt) alt = elt.attributes.alt;
|
||||
text = "![" + alt + "](" + text.image + ")";
|
||||
}
|
||||
if (text.divider)
|
||||
{
|
||||
text = '____\n';
|
||||
}
|
||||
|
||||
if (elt.attributes)
|
||||
{
|
||||
var attr = elt.attributes;
|
||||
if (attr.tag)
|
||||
{
|
||||
text = "#" + text;
|
||||
}
|
||||
if (attr.header)
|
||||
{
|
||||
startLastWith("#".repeat(attr.header) + " " );
|
||||
}
|
||||
if (attr.list == "bullet")
|
||||
{
|
||||
startLastWith("* ");
|
||||
}
|
||||
if (attr.list == "ordered")
|
||||
{
|
||||
startLastWith("1. ");
|
||||
}
|
||||
if (attr.list == "unchecked")
|
||||
{
|
||||
startLastWith("- [ ] ");
|
||||
}
|
||||
if (attr.list == "checked")
|
||||
{
|
||||
startLastWith("- [x] ");
|
||||
}
|
||||
if (attr.italic)
|
||||
{
|
||||
text = dressUp(text, "*");
|
||||
}
|
||||
if (attr.bold)
|
||||
{
|
||||
text = dressUp(text, "**");
|
||||
}
|
||||
if (attr["code-block"])
|
||||
{
|
||||
startLastWith("~~~");
|
||||
}
|
||||
if (attr.blockquote)
|
||||
{
|
||||
startLastWith("> ");
|
||||
}
|
||||
}
|
||||
result += text;
|
||||
}
|
||||
|
||||
// post processing
|
||||
createCodeBlocks();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof module !== "undefined")
|
||||
{
|
||||
module.exports.convert = getMarkDown;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
let getMarkDown = require("./cryptee-to-markdown.js");
|
||||
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
const folder = process.argv[2];
|
||||
const files = fs.readdirSync(folder);
|
||||
|
||||
for (var i in files)
|
||||
{
|
||||
const file = files[i];
|
||||
|
||||
if (file.indexOf('uecd') === -1) continue;
|
||||
console.log(file);
|
||||
|
||||
var data = fs.readFileSync( path.join(folder, file));
|
||||
|
||||
var md = getMarkDown.convert(data);
|
||||
|
||||
fs.writeFile(path.join(folder, file.replace(".uecd", ".md")), md, function (err)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="./cryptee-to-markdown.js"></script>
|
||||
<script>
|
||||
function convert()
|
||||
{
|
||||
var json = document.getElementById("input").value;
|
||||
var md =getMarkDown(json);
|
||||
console.log(md);
|
||||
document.getElementById("result").innerHTML = "<pre>" + md + "</pre>";
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<textarea id="input"></textarea>
|
||||
<button onclick="convert();">Convertir</button>
|
||||
<div id="result"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,231 @@
|
|||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
// parameters
|
||||
var inputFolder = "notes";
|
||||
var outputFolder = "result";
|
||||
var includeheaders = true;
|
||||
|
||||
var files = fs.readdirSync(inputFolder);
|
||||
|
||||
for (var i in files)
|
||||
{
|
||||
var file = files[i];
|
||||
if (file.indexOf(".uecd") === -1)
|
||||
{
|
||||
fs.copyFileSync(path.join(inputFolder, file), path.join(outputFolder, file));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var data = fs.readFileSync(path.join(inputFolder, file), 'utf8');
|
||||
var md = mdify(JSON.parse(data), file);
|
||||
fs.writeFileSync(path.join(outputFolder, file).replace(".uecd", ".md"), md);
|
||||
}
|
||||
}
|
||||
|
||||
function mdify(data, file)
|
||||
{
|
||||
var result = "";
|
||||
var tags = [];
|
||||
|
||||
for (var i in data.ops)
|
||||
{
|
||||
var op = data.ops[i];
|
||||
|
||||
if (op.insert.image)
|
||||
{
|
||||
// inline base64
|
||||
//result += "![](" + op.insert.image + ")";
|
||||
|
||||
//external
|
||||
var alt = op.attributes && op.attributes.alt ? op.attributes.alt : "";
|
||||
if (op.insert.image.indexOf("data:image") !== -1)
|
||||
{
|
||||
var buffer = Buffer.from(op.insert.image.replace(/^data:image\/jpeg;base64,/, ""), "base64");
|
||||
var imageFileName = file.replace(".uecd", i + ".jpeg");
|
||||
fs.writeFileSync(path.join(outputFolder, imageFileName), buffer);
|
||||
result += "![" + alt + "](" + imageFileName + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
result += "![" + alt + "](" + op.insert.image + ")";
|
||||
}
|
||||
}
|
||||
else if (!op.attributes)
|
||||
{
|
||||
if (op.insert.divider)
|
||||
{
|
||||
result += "---\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
result += op.insert;
|
||||
}
|
||||
}
|
||||
else if (op.attributes.italic)
|
||||
{
|
||||
result += "*" + op.insert + "*";
|
||||
}
|
||||
else if (op.attributes.bold)
|
||||
{
|
||||
result += "**" + op.insert + "**";
|
||||
}
|
||||
else if (op.attributes.underline)
|
||||
{
|
||||
result += "__" + op.insert + "__";
|
||||
}
|
||||
else if (op.attributes.strike)
|
||||
{
|
||||
result += "~~" + op.insert + "~~";
|
||||
}
|
||||
else if (op.attributes.tag)
|
||||
{
|
||||
//result += "#" + op.insert;
|
||||
tags.push(op.insert);
|
||||
}
|
||||
else if (op.attributes.header)
|
||||
{
|
||||
var result = result.split("\n");
|
||||
result[result.length - 1] = "#".repeat(op.attributes.header) + " " + result[result.length - 1];
|
||||
result = result.join("\n");
|
||||
result += op.insert;
|
||||
}
|
||||
else if (op.attributes.link)
|
||||
{
|
||||
if (op.insert == op.attributes.link)
|
||||
{
|
||||
result += op.insert;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += "[" + op.insert + "](" + op.attributes.link + ")";
|
||||
}
|
||||
}
|
||||
else if (op.attributes.code)
|
||||
{
|
||||
result += "`" + op.insert + "`";
|
||||
}
|
||||
else if (op.attributes.list == "bullet")
|
||||
{
|
||||
var result = result.split("\n");
|
||||
result[result.length - 1] = "\t".repeat(op.attributes.indent) + "* " + result[result.length - 1];
|
||||
result = result.join("\n");
|
||||
result += op.insert;
|
||||
}
|
||||
else if (op.attributes.list == "unchecked")
|
||||
{
|
||||
var result = result.split("\n");
|
||||
result[result.length - 1] = "\t".repeat(op.attributes.indent) + "[ ] " + result[result.length - 1];
|
||||
result = result.join("\n");
|
||||
result += op.insert;
|
||||
}
|
||||
else if (op.attributes.list == "checked")
|
||||
{
|
||||
var result = result.split("\n");
|
||||
result[result.length - 1] = "\t".repeat(op.attributes.indent) + "[x] " + result[result.length - 1];
|
||||
result = result.join("\n");
|
||||
result += op.insert;
|
||||
}
|
||||
else if (op.attributes.list == "ordered")
|
||||
{
|
||||
var result = result.split("\n");
|
||||
result[result.length - 1] = "\t".repeat(op.attributes.indent) + "1. " + result[result.length - 1];
|
||||
result = result.join("\n");
|
||||
result += op.insert;
|
||||
}
|
||||
else if (op.attributes.file)
|
||||
{
|
||||
result += "[[" + op.attributes.file.filetitle + "]]";
|
||||
|
||||
// traditional link
|
||||
//result += "[" + op.attributes.file.filetitle + "](" + op.attributes.file.filetitle + ".md)";
|
||||
}
|
||||
else if (op.attributes["code-block"])
|
||||
{
|
||||
var result = result.split("\n");
|
||||
result[result.length - 1] = "```" + result[result.length - 1] + "```";
|
||||
result = result.join("\n");
|
||||
result += op.insert.replace(/\n\n/g, "\n``````\n");;
|
||||
}
|
||||
else if (op.attributes.blockquote)
|
||||
{
|
||||
var result = result.split("\n");
|
||||
result[result.length - 1] = "> " + result[result.length - 1];
|
||||
result = result.join("\n");
|
||||
result += op.insert.replace(/\n\n/g, "\n> \n");
|
||||
}
|
||||
else
|
||||
{
|
||||
result += op.insert;
|
||||
}
|
||||
}
|
||||
|
||||
// Post processing
|
||||
|
||||
// remove nbsp, indent and trailing
|
||||
result = result.replace(/ /g, " ");
|
||||
result = result.replace(/\n[ ]*/g, "\n");
|
||||
result = result.replace(/[ ]*\n/g, "\n");
|
||||
|
||||
// insert line endings
|
||||
result = result.split('\n');
|
||||
var blockMarkers = ["* ", "1. ", "> ", "```", "["];
|
||||
for (var i = 0; i < result.length; i++)
|
||||
{
|
||||
var inBlock = false;
|
||||
for (var j in blockMarkers)
|
||||
{
|
||||
var block = blockMarkers[j];
|
||||
if (result[i].replace(/\t/g, "").startsWith(block) && result[i + 1].replace(/\t/g, "").startsWith(block))
|
||||
{
|
||||
inBlock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inBlock && Boolean(result[i + 1]))
|
||||
{
|
||||
result.splice(i + 1, 0, "");
|
||||
i++;
|
||||
}
|
||||
}
|
||||
result = result.join("\n");
|
||||
|
||||
// merge code blocks
|
||||
result = result.replace(/```\n```/g, "\n");
|
||||
result = result.replace(/```/g, "\n```\n");
|
||||
|
||||
// escape ~ in unix path
|
||||
result = result.replace(/~\//g, "\\~/");
|
||||
|
||||
// headers
|
||||
if (includeheaders)
|
||||
{
|
||||
var headers =
|
||||
{
|
||||
title: file.replace(".uecd", ""),
|
||||
tags: tags.join(", ")
|
||||
};
|
||||
|
||||
if (/[.|mars|mai|juin|août] 202\d /.test(file))
|
||||
{
|
||||
var index = file.indexOf("202");
|
||||
headers.title = headers.title.substring(index + 5);
|
||||
headers.date = file.substring(0, index + 4);
|
||||
}
|
||||
|
||||
var headersmd = "---\n";
|
||||
for (var i in headers)
|
||||
{
|
||||
headersmd += i + ": " + headers[i] + "\n";
|
||||
}
|
||||
headersmd += "---";
|
||||
|
||||
if (result[0] != "\n") headersmd += "\n";
|
||||
if (result[1] != "\n") headersmd += "\n";
|
||||
|
||||
result = headersmd + result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
# export-turtl-notes
|
||||
A simple python script to export your [turtl](https://github.com/turtl) notes.
|
||||
|
||||
What the script does:
|
||||
* Export your notes as markdown files.
|
||||
* Photos and files are also exported (without the associated text).
|
||||
* A new folder is created for each board, containg the files.
|
||||
* Tags are lost.
|
||||
|
||||
Usage:
|
||||
* Export your turtl profile from the turtl app
|
||||
* launch `python export-turtl-notes.py` next to turtl-backup.json
|
|
@ -0,0 +1,62 @@
|
|||
import base64
|
||||
import json
|
||||
import os
|
||||
|
||||
def findboard(id):
|
||||
for board in data['boards']:
|
||||
if id == board['id']:
|
||||
return board['title']
|
||||
return ''
|
||||
|
||||
def clean(text):
|
||||
return "".join(c if c.isalnum() else " " for c in text)
|
||||
|
||||
|
||||
f = open('turtl-backup.json', encoding='utf-8')
|
||||
data = json.load(f)
|
||||
|
||||
if not os.path.isdir('turtl'):
|
||||
os.mkdir('turtl')
|
||||
|
||||
for folder in data['boards']:
|
||||
path = os.path.join('turtl', folder['title'])
|
||||
if not os.path.isdir(path):
|
||||
os.mkdir(path)
|
||||
|
||||
untitledcounter = 1
|
||||
|
||||
for note in data['notes']:
|
||||
|
||||
type = note['type']
|
||||
title = note['title']
|
||||
if title == '':
|
||||
title = 'untitled' + str(untitledcounter)
|
||||
untitledcounter += 1
|
||||
|
||||
print(title)
|
||||
|
||||
if type == 'text':
|
||||
filename = clean(title) + '.md'
|
||||
with open(os.path.join('turtl', findboard(note['board_id']), filename), 'wb') as n:
|
||||
n.write(note['text'].encode('utf-8'))
|
||||
n.close()
|
||||
|
||||
if type == 'image' or type == 'file':
|
||||
file = note['file']
|
||||
filename = file['name']
|
||||
for fileref in data['files']:
|
||||
if fileref['id'] == note['id']:
|
||||
b64 = fileref['data']
|
||||
with open(os.path.join('turtl', findboard(note['board_id']), filename), 'wb') as n:
|
||||
n.write(base64.decodebytes(b64.encode('utf-8')))
|
||||
n.close()
|
||||
|
||||
if type == 'link':
|
||||
url = note['url']
|
||||
text = url + '\r\n' + note['text']
|
||||
filename = clean(title) + '.md'
|
||||
with open(os.path.join('turtl', findboard(note['board_id']), filename), 'wb') as n:
|
||||
n.write(text.encode('utf-8'))
|
||||
n.close()
|
||||
|
||||
f.close()
|
|
@ -0,0 +1,2 @@
|
|||
# export-wm3
|
||||
A js script to export your [writemonkey3](https://writemonkey.com/wm3/) documents as flat markdown files.
|
|
@ -0,0 +1,26 @@
|
|||
var fs = require('fs')
|
||||
|
||||
var filePath = process.argv[2];
|
||||
|
||||
var fileContent = fs.readFileSync(filePath, 'utf8');
|
||||
var data = fileContent.split("\n");
|
||||
var documents = {};
|
||||
for (var i in data)
|
||||
{
|
||||
if (!data[i]) continue;
|
||||
|
||||
var file = JSON.parse(data[i]);
|
||||
if (file.nam && file.txt && !file.del)
|
||||
{
|
||||
if (!documents[file.nam] || documents[file.nam].dtm < file.dtm)
|
||||
{
|
||||
documents[file.nam] = file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i in documents)
|
||||
{
|
||||
console.log(documents[i].nam);
|
||||
fs.writeFileSync(documents[i].nam + '.md', documents[i].txt, 'utf8');
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
from markdownify import markdownify
|
||||
import imaplib
|
||||
import email
|
||||
from email.header import decode_header
|
||||
import os
|
||||
|
||||
username = ""
|
||||
password = ""
|
||||
imapserver = ""
|
||||
|
||||
if not os.path.isdir('notes'):
|
||||
os.mkdir('notes')
|
||||
|
||||
def clean(text):
|
||||
return "".join(c if c.isalnum() else "_" for c in text)
|
||||
|
||||
# number of top emails to fetch
|
||||
N = 200
|
||||
|
||||
imap = imaplib.IMAP4_SSL(imapserver)
|
||||
imap.login(username, password)
|
||||
|
||||
status, messages = imap.select("Notes")
|
||||
|
||||
messages = int(messages[0])
|
||||
|
||||
for i in range(messages, messages-N, -1):
|
||||
res, msg = imap.fetch(str(i), "(RFC822)")
|
||||
for response in msg:
|
||||
if isinstance(response, tuple):
|
||||
msg = email.message_from_bytes(response[1])
|
||||
subject, encoding = decode_header(msg["Subject"])[0]
|
||||
if isinstance(subject, bytes):
|
||||
subject = subject.decode(encoding)
|
||||
|
||||
From, encoding = decode_header(msg.get("From"))[0]
|
||||
if isinstance(From, bytes):
|
||||
From = From.decode(encoding)
|
||||
|
||||
print(subject)
|
||||
filepath = clean(subject) + '.md'
|
||||
filepath = os.path.join('notes', filepath)
|
||||
|
||||
if msg.is_multipart():
|
||||
for part in msg.walk():
|
||||
content_type = part.get_content_type()
|
||||
content_disposition = str(part.get("Content-Disposition"))
|
||||
try:
|
||||
body = part.get_payload(decode=True).decode()
|
||||
except:
|
||||
pass
|
||||
|
||||
if content_type == "text/plain" and "attachment" not in content_disposition:
|
||||
open(filepath, "w").write(body)
|
||||
elif "attachment" in content_disposition:
|
||||
print('/!\\ attachment')
|
||||
else:
|
||||
content_type = msg.get_content_type()
|
||||
body = msg.get_payload(decode=True).decode()
|
||||
if content_type == "text/plain":
|
||||
open(filepath, "w").write(body)
|
||||
|
||||
if content_type == "text/html":
|
||||
md = markdownify(body)
|
||||
if md[:4] == 'html':
|
||||
md = md[4:]
|
||||
|
||||
open(filepath, "wb").write(md.encode('utf8'))
|
||||
|
||||
imap.close()
|
||||
imap.logout()
|
|
@ -0,0 +1,12 @@
|
|||
From email Wed Apr 10 08:45:21 2024
|
||||
From: name <email>
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
Date: Wed, 10 Apr 2024 02:45:21 -0400
|
||||
Reply-To: name <email>
|
||||
X-Uniform-Type-Identifier: com.apple.mail-note
|
||||
Subject: title
|
||||
X-Universally-Unique-Identifier: uid
|
||||
Status: RO
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
rm mbox
|
||||
for f in input/*.md
|
||||
do
|
||||
f=${f##*/}
|
||||
title=${f%.md}
|
||||
|
||||
uid=$(cat /proc/sys/kernel/random/uuid)
|
||||
cp imap-note-header "$title".imap
|
||||
sed -i "s/uid/${uid}/" "$title".imap
|
||||
sed -i "s/title/${title}/" "$title".imap
|
||||
sed -i "s/name/$1/" "$title".imap
|
||||
sed -i "s/email/$2/" "$title".imap
|
||||
|
||||
cat "input/$f" >> "$title".imap
|
||||
cat "$title".imap >> mbox
|
||||
printf '\n\n' >> mbox
|
||||
rm "$title".imap
|
||||
done
|
|
@ -0,0 +1,12 @@
|
|||
Tool to create a mbox file from a set of markdown files
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
md2imap.sh <name> <email>
|
||||
```
|
||||
|
||||
To import the mbox file to an imap server using mutt:
|
||||
* Open mbox file: `mutt -f mbox`
|
||||
* `T` then `.` to select all
|
||||
* `;` then `s` to save to =Notes folder
|
||||
* `U` then `.` to undelete mbox, if needed
|
Loading…
Reference in New Issue