-
Notifications
You must be signed in to change notification settings - Fork 0
/
gomap.go
125 lines (114 loc) · 3.98 KB
/
gomap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package gomap
import (
"fmt"
"time"
"github.com/cwinters8/gomap/client"
"github.com/cwinters8/gomap/objects/emails"
"github.com/cwinters8/gomap/objects/mailboxes"
)
type Client struct {
*client.Client
Drafts *mailboxes.Mailbox
Sent *mailboxes.Mailbox
}
// NewClient creates a new JMAP mail client that can be used for interacting
// with the JMAP mail server specified with jmapSessionURL.
//
// Commonly, draftsMailbox should be "Drafts" and sentMailbox should be "Sent".
// These arguments are available in case customization is necessary.
// You can use the DefaultDrafts and DefaultSent constants for convenience.
func NewClient(jmapSessionURL, bearerToken, draftsMailbox, sentMailbox string) (*Client, error) {
client, err := client.NewClient(jmapSessionURL, bearerToken)
if err != nil {
return nil, fmt.Errorf("failed to instantiate jmap client: %w", err)
}
drafts, err := mailboxes.GetMailboxByName(client, draftsMailbox)
if err != nil {
return nil, fmt.Errorf("failed to retrieve drafts mailbox: %w", err)
}
sent, err := mailboxes.GetMailboxByName(client, sentMailbox)
if err != nil {
return nil, fmt.Errorf("failed to retrieve sent mailbox: %w", err)
}
c := Client{client, drafts, sent}
return &c, nil
}
// SendEmail sends an email using the provided arguments.
//
// Addresses in from must be owned by the account authenticated with the Client,
// otherwise the email will fail to send.
//
// Setting isHTML to true will set the body type attribute to HTML instead of plaintext.
// This works best if body is a string that has been output from executing an html/template.
func (c *Client) SendEmail(from, to Addresses, subject, body string, isHTML bool) error {
bodyType := emails.TextPlain
if isHTML {
bodyType = emails.TextHTML
}
email, err := emails.NewEmail([]string{c.Drafts.ID}, from, to, subject, body, bodyType)
if err != nil {
return fmt.Errorf("failed to instantiate new email: %w", err)
}
if err := emails.Set(c.Client, []*emails.Email{email}); err != nil {
return fmt.Errorf("email set request failure: %w", err)
}
if _, err := email.Submit(c.Client, c.Drafts.ID, c.Sent.ID); err != nil {
return fmt.Errorf("failed to submit email: %w", err)
}
return nil
}
// GetEmails retrieves emails based on the provided filter.
// It will continue to query until the first of maxCount or timeout has been reached.
//
// maxCount is used as a metric for breaking out of the query loop,
// not a hard limit on the number of emails returned.
func (c *Client) GetEmails(filter *Filter, maxCount int, timeout time.Duration) ([]*emails.Email, error) {
var emailIDs []string
f := emails.Filter(*filter)
end := time.Now().Add(timeout)
for time.Now().Compare(end) < 1 {
newIDs, err := emails.Query(c.Client, &f)
if err != nil {
return nil, fmt.Errorf("failed to query for emails: %w", err)
}
if len(newIDs) > 0 {
emailIDs = append(emailIDs, newIDs...)
}
if len(emailIDs) >= maxCount {
break
}
}
if len(emailIDs) == 0 {
return nil, fmt.Errorf("email IDs matching provided filter not found")
}
found, notFound, err := emails.GetEmails(c.Client, emailIDs)
if err != nil {
return nil, fmt.Errorf("failed to retrieve emails: %w", err)
}
if len(notFound) > 0 {
fmt.Printf("Warning: Unable to retrieve emails with IDs %v\n", notFound)
}
return found, nil
}
// GetMailbox retrieves a mailbox with the matching name.
func (c *Client) GetMailbox(name string) (*mailboxes.Mailbox, error) {
return mailboxes.GetMailboxByName(c.Client, name)
}
// NewAddress is a convenience function for creating a new *emails.Address
func NewAddress(name, email string) *emails.Address {
return &emails.Address{Name: name, Email: email}
}
const (
DefaultDrafts = "Drafts"
DefaultSent = "Sent"
)
type Addresses []*emails.Address
type Filter emails.Filter
// NewAddresses creates a new Addresses slice that can be used for sending emails
func NewAddresses(addresses ...*emails.Address) Addresses {
addr := Addresses{}
if len(addresses) > 0 {
addr = append(addr, addresses...)
}
return addr
}