Custom domain emailing

Earlier this year, I set up my robotics club’s website to be able to send and receive emails from its domain. For example, [email protected] or [email protected].

The problem with a shared email account is that most people don’t check it very often because of the hassle of logging in. People check chat messages more often, so being updated with email messages in a chat client leads to quicker responses.

So to make team emails more accessible, I made the Spartabots website send a webhook to a private Discord channel when an email is received.

Screenshot of the Discord embed webhook with email information.
The webhook embed works!

Create a Mailgun route

I used Mailgun’s free tier to collect email sent to *@sparabots. I made a new Mailgun route that forwards all emails to the /new-incoming-email endpoint on the spartabots.org server. The server takes the email data, formats it, and sends it to Discord.

Screenshot of the Mailgun route actions.
Mailgun route to forward all incoming emails

PHP code on the server

Then, the webserver needs to handle the incoming POST request. This short function sends a JSON message to a Discord webhook using cURL.

post('/new-incoming-email', function() {
	$webhook_url = 'https://discordapp.com/api/webhooks/.../...';

    $webhook_content = '**From**: ' . $_POST['from'] .
                       "\n**To**: " .$_POST['recipient'] .
	                   "\n**Subject**: " . $_POST['subject'] .
                       "\n**Content**: " . $_POST['stripped-text'];
    $webhook_data = json_encode(array('content' => $webhook_content),
                    JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $webhook_url,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $webhook_data,
        CURLOPT_HTTPHEADER => [
            'Length' => strlen($hook_object),
            'Content-Type' => 'application/json'
        ]
    ]);
    curl_exec($ch);
    curl_close($ch);
});

This code will produce a non-embed webhook that simply lists out the email information.

New Email

From: Vishal Devireddy <[email protected]>
To: [email protected]
Subject: Does this webhook work?
Content: This is a test to make sure Mailgun routes incoming email through the spartabots website and the website successfully sends Discord a webhook. Hello, I’m another line of text!

Fancy embeds

Discord supports sending rich embeds, which are sleek gray boxes with formatted lines and fields. The embed field is an array of Embed objects, as defined by the Discord docs. There are other fields that could be set, but I chose to only include the most relevant information.

post('/new-incoming-email', function() {
	$webhook_url = 'https://discordapp.com/api/webhooks/.../...';

    send_discord_email_webhook($_POST['from'], $_POST['recipient'], $_POST['subject'],
                               $_POST['stripped-text'], $webhook_url);
});

function send_discord_email_webhook($sender_email, $recipient_email, $email_subject,
                                    $email_content, $webhook_url) {
    $webhook_data = json_encode([
        'content' => '',
        "embeds" => [[
			'type' => 'rich',
			'timestamp' => date('c', time()),
			'footer' => [
				'text' => 'Received'
			],
			'fields' => [
				[
					'name' => 'From',
					'value' => $sender_email,
					'inline' => true
				], [
					'name' => 'To',
					'value' => $recipient_email,
					'inline' => true
				], [
					'name' => 'Subject',
					'value' => $email_subject,
					'inline' => true
				], [
					'name' => 'Content',
					'value' => $email_content,
					'inline' => false
				]
			]
		]]
    ], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL => $webhook_url,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $webhook_data,
        CURLOPT_HTTPHEADER => [
            'Length' => strlen($webhook_data),
            'Content-Type' => 'application/json'
        ]
    ]);
    curl_exec($ch);
    curl_close($ch);
}