Creating a Self-Destruct Email Message: A Guide
Pablo Schaffner Bofill
Principal Software Engineer & AI Specialist | Startup Co-Founder | Expert in Python, Full-Stack Development, & Tech Leadership | 20+ Years in Tech
Let's take an enchanting journey through time and space, where old school cartoons meet cutting-edge tech. You'll recall that ever-so-eager inspector with his gadget-filled hat, right? The charming Inspector Gadget. His mission briefings came in self-destructing messages. How about we try and craft one such adventure in the real-world? Drum rolls, please!
Here's what we'll need to start our fun-filled escapade:
No worries if you're a greenhorn or a seasoned developer, this guide will be a rewarding endeavor for all. Buckle up, let's get started!
Setting Up Our Node.js Server
Start by installing Node.js and SQLite, if you haven't done so already. Once done, create a new Node.js project:
mkdir self-destruct-email && cd self-destruct-email
npm init -y
Our trusty server will need the following packages: express, nodemailer, sqlite, sqlite3, and cors. Install them using npm:
npm install express nodemailer sqlite sqlite3 cors
In the project root, create an index.js file and add the following code:
const express = require('express');
const nodemailer = require('nodemailer');
const cors = require('cors');
const { open } = require('sqlite');
const sqlite3 = require('sqlite3');
const app = express();
app.use(express.json());
app.use(cors());
let db;
(async () => {
? db = await open({
? ? filename: './emails.db',
? ? driver: sqlite3.Database
? });
? await db.exec('CREATE TABLE IF NOT EXISTS emails (id TEXT PRIMARY KEY, openedAt TEXT)');
})();
app.post('/send-email', async (req, res) => {
? let transporter = nodemailer.createTransport({
? ? service: 'gmail',
? ? auth: {
? ? ? user: '[email protected]',
? ? ? pass: 'yourpassword'
? ? }
? });
// Replace the placeholder text with the user's message
? const ampMarkupWithMessage = req.body.message.replace('Confidential message goes here', req.body.message);
?
let info = await transporter.sendMail({
? ? from: '"Fun Times ??" <[email protected]>',
? ? to: req.body.email,
? ? subject: "Self Destruct in 5 Minutes ?",
html: req.body.message,
? ? amp: req.body.message
? });
? res.json({ messageId: info.messageId });
});
app.get('/email-opened/:id', async (req, res) => {
? let email = await db.get('SELECT * FROM emails WHERE id = ?', req.params.id);
? if (!email) {
? ? await db.run('INSERT INTO emails (id, openedAt) VALUES (?, ?)', req.params.id, new Date());
? ? email = { id: req.params.id, openedAt: new Date() };
? }
? const ttl = 60 * 5;? // Destruct in 5 mins, change it as needed
? const currentTime = new Date();
? const timeElapsed = Math.floor((currentTime - new Date(email.openedAt)) / 1000);
? res.json([{ expired: timeElapsed > ttl }]);
});
app.listen(3000, () => {
? console.log('Server started on https://localhost:3000');
});
A Node.js server with SQLite, all set and ready! It's got an endpoint for sending an email and another for checking if an email has expired. Now, hold on to your horses as we dive into the magical world of AMP4Email!
Crafting Dynamic Emails with AMP4Email
Dynamic emails, you ask? Indeed! With AMP4Email, our emails can include components that can fetch data from our server, bind it to our email's elements, or even react to the user's actions! So let's draft one.
We'll use a Gmail account to send our emails. Remember to replace '[email protected]' and 'yourpassword' in the above code with your Gmail email and password. Also make sure you allow less secure apps for this Gmail account.
The amp property of our email will hold our AMP HTML:
<!doctype html>
<html ?4email>
<head>
? <meta charset="utf-8">
? <script async src="https://cdn.ampproject.org/v0.js"></script>
? <style amp4email-boilerplate>body{visibility:hidden}</style>
? <style amp-custom>
? ? .content {
? ? ? display: none;
? ? }
? ? .content:not(.expired) {
? ? ? display: block;
? ? }
? ? .expired-message {
? ? ? display: none;
? ? }
? ? .expired-message.visible {
? ? ? display: block;
? ? }
? </style>
? <script async custom-element="amp-list" src="https://cdn.ampproject.org/v0/amp-list-0.1.js"></script>
</head>
<body>
? <amp-list id="timer" layout="fixed-height" height="0" width="auto"
? ? ? ? ? ? src="https://localhost:3000/email-opened/${id}"
? ? ? ? ? ? single-item
? ? ? ? ? ? items=".">
? ? <template type="amp-mustache">
? ? ? <div class="content" [class]="expired ? 'content expired' : 'content'">
? ? ? ? Confidential message goes here.
? ? ? </div>
? ? ? <div class="expired-message" [class]="expired ? 'expired-message visible' : 'expired-message'">
? ? ? ? This message has self-destructed.
? ? ? </div>
? ? </template>
? </amp-list>
</body>
</html>
If this is your first time seeing an email with <script> tags, don't fret. It's all part of the magic of AMP4Email. When the email opens, the amp-list component fetches from our /email-opened/:id endpoint. If the TTL has passed, it changes the classes of our divs to show or hide the right message.
Bringing It All Together with React
Time to create a user-friendly interface for our email-sending server. Let's create a new React application:
npx create-react-app email-frontend
cd email-frontend
Then install the axios, react-draft-wysiwyg, and draftjs-to-html packages:
npm install axios react-draft-wysiwyg draftjs-to-html
Now, replace the contents of src/App.js with the following:
import React, { useState } from 'react';
import axios from 'axios';
import { Editor } from 'react-draft-wysiwyg';
import { EditorState, convertToRaw } from 'draft-js';
import draftToHtml from 'draftjs-to-html';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
function App() {
? const [editorState, setEditorState] = useState(EditorState.createEmpty());
? const [email, setEmail] = useState('');
? const sendEmail = async () => {
? ? const rawContentState = convertToRaw(editorState.getCurrentContent());
? ? const markup = draftToHtml(rawContentState);
? ? const response = await axios.post('https://localhost:3000/send-email', {
? ? ? email,
? ? ? message: markup
? ? });
? ? alert(`Email sent! Message ID: ${response.data.messageId}`);
? };
? return (
? ? <div className="App">
? ? ? <input type="text" placeholder="Recipient's email" value={email} onChange={e => setEmail(e.target.value)} />
? ? ? <Editor
? ? ? ? editorState={editorState}
? ? ? ? onEditorStateChange={setEditorState}
? ? ? />
? ? ? <button onClick={sendEmail}>Send Email</button>
? ? </div>
? );
}
export default App;
Our handy interface now has an input for the recipient's email address, a text editor for our confidential message, and a Send button.
Securing the Final Puzzle Piece: Node.js Magic
What's a tech adventure without some server-side wizardry? Now that our React application is ready to craft confidential messages, it's time for our Node.js server to work its charm.
Recall the AMP4Email template we created earlier with a placeholder text saying 'Confidential message goes here'? We'll now bring that into play. Instead of having the template on our client-side React application, we need to incorporate it into our server.
const emailTemplate = `
<!doctype html>
<html ?4email>
<head>
? <meta charset="utf-8">
? <script async src="https://cdn.ampproject.org/v0.js"></script>
? <style amp4email-boilerplate>body{visibility:hidden}</style>
? <style amp-custom>
? ? /* CSS rules go here */
? </style>
</head>
<body>
? <!-- The confidential message placeholder -->
? <p id="confidentialMsg">Confidential message goes here</p>
? <!-- The rest of the AMP4Email markup... -->
</body>
</html>
`;
module.exports = emailTemplate;
Pretty neat, right? Now, let's use this template in our /send-email route.
We're going to import the template and use the replace function to replace the 'Confidential message goes here' placeholder with the user's actual message. And voila! Our server becomes an expert at crafting self-destructing emails. Here's how you can do it:
const emailTemplate = require('./emailTemplate');
app.post('/send-email', async (req, res) => {
? // ... rest of your code ...
? // Replace the placeholder text with the user's message
? const ampMarkupWithMessage = emailTemplate.replace('Confidential message goes here', req.body.message);
? let info = await transporter.sendMail({
? ? from: '"Fun Times ??" <[email protected]>',
? ? to: req.body.email,
? ? subject: "Self Destruct in 5 Minutes ?",
html: req.body.message, //if amp is not supported, use html as it was
? ? amp: ampMarkupWithMessage
? });
? // ... rest of your code ...
});
With this final piece, our server pulls in the AMP4Email template, weaves in the confidential message, and sends it off as an AMP4Email.
Publishing your Project and Google Whitelist Registration
Before we take a final bow, there's an essential step you must be aware of. To make this project fly in the real-world internet skies, you must host it on a publicly accessible URL. This is because our email message needs to reach out to our server, and if it's tucked away in a local development environment, it's going to be like a carrier pigeon with no coordinates. So, whether you favor Heroku, Netlify, AWS, or any other platform, make sure you deploy your project there.
But wait, there's another caveat! You need to register the used 'from' email address with Google. Why, you ask? Because AMP4Email messages require the sender's email address to be whitelisted by Google. It's a necessary step to ensure the safe delivery of dynamic email content. You can register and check the specifics of this process in the Google AMP for Email Registration guide.
Time to test it out! Run your Node.js server with 'node index.js' and start your React app with 'npm start'. You can now send self-destructing emails. Pop open a fresh email, and after 5 minutes, the secret message will self-destruct! Mic drop!
This Inspector Gadget inspired adventure has been an absolute delight. Creating self-destructing emails using AMP4Email, Node.js, and SQLite isn't just about mimicking cartoons, it's about exploring the realms of possibility. It's a testament to the fact that with the right tools and a little imagination, the world of software engineering can be a thrilling escapade.
Until next time, happy coding!
Especialista en relacionamiento estratégico empresa - comunidad/ Ayudo a construir relaciones de confianza /Escucha activa / Innovación y creatividad
1 年Interesante artículo. No hay desafíos imposibles ....Vamos más allá.....
Liderazgo comercial | Marketing estratégico | Comunicación corporativa
1 年?Excelente Pablo! ?? muy interesantes tus artículos ??