Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
koddr committed Aug 30, 2021
1 parent b1f7e4c commit 948bc2c
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 4 deletions.
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
max_line_length = 100
trim_trailing_whitespace = true
insert_final_newline = true

[{Dockerfile,*.yml,*.yaml,*.json}]
indent_style = tab
indent_size = 2
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
*.dylib

# Test binary, built with `go test -c`
tmp/
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
vendor/
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"semi": false,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100
}
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]
Copyright 2021 Vic Shóstak <vic@shostak.dev> (https://shostak.dev)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# go-email-sender
Simple (but useful) email sender for Go. Support HTML templates and attachments.
# 📮 Go Email Sender

Simple (but useful) email sender written with pure Go `v1.17`. Support HTML templates and attachments.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/koddr/go-email-sender

go 1.17
57 changes: 57 additions & 0 deletions parse_email_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package sender

import (
"os"
"testing"
)

func TestParseTemplate(t *testing.T) {
// Create folder and file for running tests.
_ = os.Mkdir("tmp", 0700)
file, err := os.OpenFile("tmp/test.html", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0700)
if err != nil {
t.Errorf("Sender.parseTemplate() error = %v", err)
return
}
_, err = file.Write([]byte("<h1>Hello, World!</h1>"))
if err != nil {
t.Errorf("Sender.parseTemplate() error = %v", err)
return
}
defer file.Close()

// Test cases.
type args struct {
file string
data interface{}
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "successfully parsing the template file",
args: args{
file: "tmp/test.html",
data: nil,
},
want: "<h1>Hello, World!</h1>",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseTemplate(tt.args.file, tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("Sender.parseTemplate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Sender.parseTemplate() = %v, want %v", got, tt.want)
}
os.RemoveAll("tmp") // remove temp folder
})
}
}
19 changes: 19 additions & 0 deletions parse_template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package sender

import (
"bytes"
"html/template"
)

// ParseTemplate ...
func ParseTemplate(file string, data interface{}) (string, error) {
tmpl, errParseFiles := template.ParseFiles(file)
if errParseFiles != nil {
return "", errParseFiles
}
buffer := new(bytes.Buffer)
if errExecute := tmpl.Execute(buffer, data); errExecute != nil {
return "", errExecute
}
return buffer.String(), nil
}
88 changes: 88 additions & 0 deletions sender.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package sender

import (
"bytes"
"fmt"
"mime/quotedprintable"
"net/smtp"
"strings"
)

// Sender struct for describe auth data for sending emails.
type Sender struct {
Login string
Password string
SMTPServer string
SMTPServerPort int
}

// NewEmailSender func for create new email sender.
// Includes login, password, SMTP server and port.
func NewEmailSender(login, password, server string, port int) *Sender {
return &Sender{login, password, server, port}
}

// SendHTMLEmail func for send email with given HTML template and data.
// If template is empty string, it will send with default template.
func (s *Sender) SendHTMLEmail(template string, dest []string, subject string, data interface{}) error {
if template == "" {
template = "templates/default.html"
}
tmpl, errParseTemplate := ParseTemplate(template, data)
if errParseTemplate != nil {
return errParseTemplate
}
body := s.writeEmail(dest, "text/html", subject, tmpl)
s.sendEmail(dest, subject, body)
return nil
}

// SendPlainEmail func for send plain text email with data.
func (s *Sender) SendPlainEmail(dest []string, subject, data string) error {
body := s.writeEmail(dest, "text/plain", subject, data)
s.sendEmail(dest, subject, body)
return nil
}

// writeEmail method for prepare email header and body to send.
func (s *Sender) writeEmail(dest []string, contentType, subject, body string) string {
// Define variables.
var message string
var header map[string]string
var encodedMessage bytes.Buffer

// Create header.
header["From"] = s.Login
header["To"] = strings.Join(dest, ",")
header["Subject"] = subject
header["MIME-Version"] = "1.0"
header["Content-Type"] = fmt.Sprintf("%s; charset=\"utf-8\"", contentType)
header["Content-Transfer-Encoding"] = "quoted-printable"
header["Content-Disposition"] = "inline"

// Add header values to the message.
for key, value := range header {
message += fmt.Sprintf("%s:%s\r\n", key, value)
}

// Create writer for make encoding the message.
result := quotedprintable.NewWriter(&encodedMessage)
result.Write([]byte(body))
result.Close()

// Return the encoded message string.
message += "\r\n" + encodedMessage.String()
return message
}

// sendEmail method for send email to destination addresses with subject and body.
func (s *Sender) sendEmail(dest []string, subject, body string) error {
if err := smtp.SendMail(
fmt.Sprintf("%s:%d", s.SMTPServer, s.SMTPServerPort),
smtp.PlainAuth("", s.Login, s.Password, s.SMTPServer),
s.Login, dest, []byte(body),
); err != nil {
return err
}
return nil
}
71 changes: 71 additions & 0 deletions templates/default.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>✨ The default email from example.com</title>
<style type="text/css">
body {
margin: 0 auto;
padding: 0;
min-width: 100%;
color: #333333;
font-family: sans-serif;
}
table {
margin: 50px 0 50px 0;
}
code {
color: #003253;
font-family: monospace;
}
.header {
height: 40px;
text-align: center;
text-transform: uppercase;
font-size: 24px;
font-weight: bold;
}
.content {
height: 100px;
font-size: 18px;
line-height: 30px;
}
.footer {
text-align: center;
height: 40px;
font-size: 14px;
}
.footer a {
color: #777777;
text-decoration: none;
font-style: normal;
}
</style>
</head>
<body bgcolor="#FAFAFA">
<table bgcolor="#FFFFFF" width="100%" border="0" cellspacing="0" cellpadding="0">
<tr class="header">
<td style="padding: 40px">✨ The default email</td>
</tr>
<tr class="content">
<td style="padding: 10px">
<p>
<strong>Hi,</strong><br />
This is the default email from
<a href="https://github.com/koddr/go-email-sender" target="_blank" rel="noreferrer"
>koddr/go-email-sender</a
>
</p>
<p>Please specify your HTML template in the <code>SendHTMLEmail</code> method.</p>
</td>
</tr>
<tr class="footer">
<td style="padding: 40px">
👨‍💻 <a href="https://github.com/koddr" target="_blank" rel="noreferrer">@koddr</a>
</td>
</tr>
</table>
</body>
</html>

0 comments on commit 948bc2c

Please sign in to comment.