Last active
July 15, 2021 10:31
-
-
Save andyleejordan/327ab637f182f8f19a77572b3f9e71a5 to your computer and use it in GitHub Desktop.
Google Scripts web app to receive MailGun webhooks
This file contains 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
// Hook location: https://app.mailgun.com/app/webhooks | |
var API_KEY = "key-abcd1234..."; | |
var EMAIL = "[email protected]"; | |
// Naive function to email a posted event from a MailGun web hook. | |
// `doPost(e)` signature: https://developers.google.com/apps-script/guides/web#deploying | |
// `parameter` schema: https://mailgun-documentation.readthedocs.io/en/latest/user_manual.html#webhooks | |
// Events API: https://documentation.mailgun.com/en/latest/api-events.html#api-events | |
function doPost(e) { | |
// The output is not consumed, but required by the Google Scripts API. | |
var output = ContentService.createTextOutput("Post received, thanks!"); | |
var params = e["parameter"]; | |
var params_str = JSON.stringify(params, null, 2); | |
Logger.log("Processing: " + params_str); | |
if (!verify(API_KEY, params["token"], params["timestamp"], params["signature"])) { | |
MailApp.sendEmail(EMAIL, "MailGun verification failed!", params_str); | |
// There is no way to return a specific exit code; so this still returns 200. | |
return output; | |
}; | |
var subject = "MailGun " + params["event"] + " a message!"; | |
var body = params; | |
try { | |
var headers = JSON.stringify(JSON.parse(params["message-headers"]), null, 2); | |
body = | |
"Domain: " + params["domain"] + "\n" + | |
"Description: " + params["description"] + "\n" + | |
"Recipient: " + params["recipient"] + "\n" + | |
"Message ID: " + params["Message-Id"] + "\n" + | |
"Headers: " + headers + "\n"; | |
} catch (e) { | |
body += "\n" + e.message; | |
} | |
Logger.log("Body: " + body); | |
MailApp.sendEmail(EMAIL, subject, body); | |
return output; | |
} | |
// Needs API_KEY replaced with this to test. | |
var test_key = "key-4847b6d360af1cc52d7261693b609789"; // Yes, this key has been revoked. | |
var test_token = "4379a5189aab3b606083a4e4fba0b99064e88ce76ea8c2a6df"; | |
var test_timestamp = 1499125194; | |
var test_signature = "5ad3b5393dd4c0795f8b381e2616646b25d12847b5c1b870b2f45a4f1e010d29"; | |
function testDoPost() { | |
var parameters = { | |
"token": test_token, | |
"timestamp": test_timestamp, | |
"signature": test_signature, | |
"event": "dropped", | |
"recipient": "[email protected]", | |
"domain": "name.com", | |
"description": "This is a test POST.", | |
"message-headers": "[[\"Sender\", \"[email protected]\"], [\"X-Mailgun-Sending-Ip\", \"198.61.254.26\"]]" | |
}; | |
var e = { | |
"parameter": parameters, | |
"parameters": parameters | |
}; | |
doPost(e); | |
} | |
// Returns true if the message was verified. | |
function verify(key, token, timestamp, signature) { | |
var computed = toHexString(Utilities.computeHmacSha256Signature(timestamp + token, key)); | |
Logger.log("Computed: " + computed); | |
Logger.log("Signature: " + signature); | |
return computed == signature; | |
} | |
// Converts a byte[] to a hex string. | |
// https://stackoverflow.com/a/34310051 | |
// https://en.wikipedia.org/wiki/Hexadecimal | |
function toHexString(bytes) { | |
return bytes.map(function(byte) { | |
return ('0' + (byte & 0xFF).toString(16)).slice(-2); | |
}).join(''); | |
} | |
// Used in conjunction with logs to test `verify()`. | |
function testVerify() { | |
return verify(test_key, test_token, test_timestamp, test_signature); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment