Nim offers an smtp module, but it is a bit annoying to use out of the box. This blogpost hopes to be a mini-tutorial on the basics of how to use the smtp library and give developers best practices for handling outgoing email in ways that Google or iCloud will accept.
SMTP, or the Simple Mail Transfer Protocol is the backbone of how email works. It's a very simple line-based protocol, and there are wrappers for it in almost every programming language. Usage is pretty simple:
Unfortunately, the devil is truly in the details here. There are a few things that absolutely must be present in your emails in order for services like GMail to accept them. They are:
From
header specifying where the message was sent from1.0
here)text/plain
)For a more complete example, let's create a Mailer
type and a constructor:
# mailer.nim
import asyncdispatch, logging, smtp, strformat, strutils
type Mailer* = object
address: string
port: Port
myAddress: string
myName: string
username: string
password: string
proc newMailer*(address, port, myAddress, myName, username, password: string): Mailer =
result = Mailer(
address: address,
port: port.parseInt.Port,
myAddress: myAddress,
myName: myName,
username: username,
password: password,
)
And let's write a mail
method to send out email:
proc mail(m: Mailer, to, toName, subject, body: string) {.async.} =
let
toList = @[fmt"{toName} <{to}>"]
msg = createMessage(subject, body, toList, @[], [
("From", fmt"{m.myName} <{m.myAddress}"),
("MIME-Version", "1.0"),
("Content-Type", "text/plain"),
])
var client = newAsyncSmtp(useSsl = true)
await client.connect(m.address, m.port)
await client.auth(m.username, m.password)
await client.sendMail(m.myAddress, toList, $msg)
info "sent email to: ", to, " about: ", subject
await client.close()
Breaking this down, you can clearly see the parts of the SMTP connection as I
laid out before. The Mailer
creates a new transient SMTP connection,
authenticates with the remote server, sends the properly formatted email to
the server and then closes the connection cleanly.
If you want to test this code, I suggest testing it with a freely available
email provider that offers TLS/SSL-encrypted SMTP support. This also means that
you need to compile this code with --define: ssl
, so create config.nims
and
add the following:
--define: ssl
Here's a little wrapper using cligen:
when isMailModule:
import cligen, os
let
smtpAddress = getEnv("SMTP_ADDRESS")
smtpPort = getEnv("SMTP_PORT")
smtpMyAddress = getEnv("SMTP_MY_ADDRESS")
smtpMyName = getEnv("SMTP_MY_NAME")
smtpUsername = getEnv("SMTP_USERNAME")
smtpPassword = getEnv("SMTP_PASSWORD")
proc sendAnEmail(to, toName, subject, body: string) =
let m = newMailer(smtpAddress, smtpPort, smtpMyAddress, smtpMyName, smtpUsername, smtpPassword)
waitFor m.mail(to, toName, subject, body)
dispatch(sendAnEmail)
Usage is simple:
$ nim c -r mailer.nim --help
Usage:
sendAnEmail [required&optional-params]
Options(opt-arg sep :|=|spc):
-h, --help print this cligen-erated help
--help-syntax advanced: prepend,plurals,..
-t=, --to= string REQUIRED set to
--toName= string REQUIRED set toName
-s=, --subject= string REQUIRED set subject
-b=, --body= string REQUIRED set body
I hope this helps, this module is going to be used in my future post on how to create an application using Nim's Jester framework.
This article was posted on M08 28 2019. Facts and circumstances may have changed since publication. Please contact me before jumping to conclusions if something seems wrong or unclear.
Series: howto
Tags: nim
email
This post was WebMentioned at the following URLs:
The art for Mara was drawn by Selicre.
The art for Cadey was drawn by ArtZora Studios.