Mail to PDF - initial commit
						commit
						1b44541a1e
					
				@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					# Mail-to-pdf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Skrypt konwertujący pliki .eml do plików PDF, razem z załącznikami w postaci zdjęć.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Wymagania
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- zx
 | 
				
			||||||
 | 
					- wkhtmltopdf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Sposób użycia
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`./mail-to-pdf.mjs plik.eml`
 | 
				
			||||||
@ -0,0 +1,120 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/zx --quiet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { resolve, basename } from "path";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const pwd = (await $`pwd`).stdout.replace("\n", "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$`echo $PWD`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const path = resolve(process.env.PWD, process.argv.slice(-1)[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mail_body = (await $`mu view ${path} -o sexp`).stdout.replace(
 | 
				
			||||||
 | 
					  /\\"/g,
 | 
				
			||||||
 | 
					  '"'
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let html = mail_body.match(/\<html\>.*\<\/html\>/gis)[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const attachments = (await $`mu extract ${path}`).stdout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getSimpleHeader(body, header_name) {
 | 
				
			||||||
 | 
					  const regexp = new RegExp(`:${header_name} (.*)\n`);
 | 
				
			||||||
 | 
					  return body.match(regexp)[1];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function formatAddress(sexp) {
 | 
				
			||||||
 | 
					  sexp = sexp
 | 
				
			||||||
 | 
					    .replace(/(^\(\(|\)\)$)/g, "")
 | 
				
			||||||
 | 
					    .replace(/"? \. "/, " <")
 | 
				
			||||||
 | 
					    .replace(/"$/, ">")
 | 
				
			||||||
 | 
					    .replace(/^"/, "")
 | 
				
			||||||
 | 
					    .replace(/^nil <(.*)>$/, "$1");
 | 
				
			||||||
 | 
					  return sexp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function escape(str) {
 | 
				
			||||||
 | 
					  return str.replace(/</g, "<");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const to_download = attachments
 | 
				
			||||||
 | 
					  .split("\n")
 | 
				
			||||||
 | 
					  .slice(1)
 | 
				
			||||||
 | 
					  .filter((line) => line.includes("image"))
 | 
				
			||||||
 | 
					  .map((line) => {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      filename: line.match(/\d+ (.*) image\//)[1],
 | 
				
			||||||
 | 
					      mime: line.match(/image\/[^ ]+/)[0],
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const files = await Promise.all(
 | 
				
			||||||
 | 
					  to_download.map(async ({ filename, mime }) => {
 | 
				
			||||||
 | 
					    await $`mu extract --overwrite --target-dir=/tmp ${path} ${filename}`;
 | 
				
			||||||
 | 
					    const base64 = (await $`base64 -w 0 < ${`/tmp/${filename}`}`).stdout;
 | 
				
			||||||
 | 
					    return { filename, base64, mime };
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const attachments_items = files
 | 
				
			||||||
 | 
					  .map(
 | 
				
			||||||
 | 
					    ({ filename, mime, base64 }) => /* HTML */ `<li>
 | 
				
			||||||
 | 
					      ${filename}<img style="width: 100%" src="data:${mime};base64,${base64}" />
 | 
				
			||||||
 | 
					    </li>`
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  .join("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					html = html.replace(
 | 
				
			||||||
 | 
					  /<\/body>/i,
 | 
				
			||||||
 | 
					  /* HTML */ `<hr/><h3>Załączniki:</h3>
 | 
				
			||||||
 | 
					    <ul>
 | 
				
			||||||
 | 
					      ${attachments_items}
 | 
				
			||||||
 | 
					    </ul></body>`
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const subject = getSimpleHeader(mail_body, "subject").slice(1, -1); // slice to remove "
 | 
				
			||||||
 | 
					const from = formatAddress(getSimpleHeader(mail_body, "from"));
 | 
				
			||||||
 | 
					const to = formatAddress(getSimpleHeader(mail_body, "to"));
 | 
				
			||||||
 | 
					const date = await formatDate(getSimpleHeader(mail_body, "date"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// console.log({ subject, from, to, date });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					html = html.replace(
 | 
				
			||||||
 | 
					  /<body>/i,
 | 
				
			||||||
 | 
					  `<body>` +
 | 
				
			||||||
 | 
					    /* HTML */ `<h1>${subject}</h1>
 | 
				
			||||||
 | 
					      <table>
 | 
				
			||||||
 | 
					        <tr>
 | 
				
			||||||
 | 
					          <th>Temat wiadomości:</th>
 | 
				
			||||||
 | 
					          <td>${subject}</td>
 | 
				
			||||||
 | 
					        </tr>
 | 
				
			||||||
 | 
					        <tr>
 | 
				
			||||||
 | 
					          <th>Wysłano:</th>
 | 
				
			||||||
 | 
					          <td>${escape(date)}</td>
 | 
				
			||||||
 | 
					        </tr>
 | 
				
			||||||
 | 
					        <tr>
 | 
				
			||||||
 | 
					          <th>Od:</th>
 | 
				
			||||||
 | 
					          <td>${escape(from)}</td>
 | 
				
			||||||
 | 
					        </tr>
 | 
				
			||||||
 | 
					        <tr>
 | 
				
			||||||
 | 
					          <th>Do:</th>
 | 
				
			||||||
 | 
					          <td>${escape(to)}</td>
 | 
				
			||||||
 | 
					        </tr>
 | 
				
			||||||
 | 
					      </table>`
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const output = (await $`mktemp --suffix=.html`).stdout.replace("\n", "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					await fs.writeFile(output, html);
 | 
				
			||||||
 | 
					await $`wkhtmltopdf ${output} ${`${basename(path)}.pdf`}`;
 | 
				
			||||||
 | 
					// console.log(pwd + "output.html");
 | 
				
			||||||
 | 
					// console.log(mail_body);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function formatDate(sexp) {
 | 
				
			||||||
 | 
					  const parsed = (await $`emacsclient --eval ${`(decode-time '${sexp})`}`)
 | 
				
			||||||
 | 
					    .stdout;
 | 
				
			||||||
 | 
					  const [, minutes, hours, date, month, year] = parsed.split(" ");
 | 
				
			||||||
 | 
					  return `${year}-${month.padStart(2, "0")}-${date} ${hours}:${minutes.padStart(
 | 
				
			||||||
 | 
					    2,
 | 
				
			||||||
 | 
					    "0"
 | 
				
			||||||
 | 
					  )}`;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
					Loading…
					
					
				
		Reference in New Issue