1package mailgun
2
3import (
4	"encoding/json"
5	"fmt"
6	"strconv"
7)
8
9// A mailing list may have one of three membership modes.
10// ReadOnly specifies that nobody, including Members,
11// may send messages to the mailing list.
12// Messages distributed on such lists come from list administrator accounts only.
13// Members specifies that only those who subscribe to the mailing list may send messages.
14// Everyone specifies that anyone and everyone may both read and submit messages
15// to the mailing list, including non-subscribers.
16const (
17	ReadOnly = "readonly"
18	Members  = "members"
19	Everyone = "everyone"
20)
21
22// Mailing list members have an attribute that determines if they've subscribed to the mailing list or not.
23// This attribute may be used to filter the results returned by GetSubscribers().
24// All, Subscribed, and Unsubscribed provides a convenient and readable syntax for specifying the scope of the search.
25var (
26	All          *bool = nil
27	Subscribed   *bool = &yes
28	Unsubscribed *bool = &no
29)
30
31// yes and no are variables which provide us the ability to take their addresses.
32// Subscribed and Unsubscribed are pointers to these booleans.
33//
34// We use a pointer to boolean as a kind of trinary data type:
35// if nil, the relevant data type remains unspecified.
36// Otherwise, its value is either true or false.
37var (
38	yes bool = true
39	no  bool = false
40)
41
42// A List structure provides information for a mailing list.
43//
44// AccessLevel may be one of ReadOnly, Members, or Everyone.
45type List struct {
46	Address      string `json:"address",omitempty"`
47	Name         string `json:"name",omitempty"`
48	Description  string `json:"description",omitempty"`
49	AccessLevel  string `json:"access_level",omitempty"`
50	CreatedAt    string `json:"created_at",omitempty"`
51	MembersCount int    `json:"members_count",omitempty"`
52}
53
54// A Member structure represents a member of the mailing list.
55// The Vars field can represent any JSON-encodable data.
56type Member struct {
57	Address    string                 `json:"address,omitempty"`
58	Name       string                 `json:"name,omitempty"`
59	Subscribed *bool                  `json:"subscribed,omitempty"`
60	Vars       map[string]interface{} `json:"vars,omitempty"`
61}
62
63// GetLists returns the specified set of mailing lists administered by your account.
64func (mg *MailgunImpl) GetLists(limit, skip int, filter string) (int, []List, error) {
65	r := newHTTPRequest(generatePublicApiUrl(mg, listsEndpoint))
66	r.setClient(mg.Client())
67	r.setBasicAuth(basicAuthUser, mg.ApiKey())
68	p := newUrlEncodedPayload()
69	if limit != DefaultLimit {
70		p.addValue("limit", strconv.Itoa(limit))
71	}
72	if skip != DefaultSkip {
73		p.addValue("skip", strconv.Itoa(skip))
74	}
75	if filter != "" {
76		p.addValue("address", filter)
77	}
78	var envelope struct {
79		Items      []List `json:"items"`
80		TotalCount int    `json:"total_count"`
81	}
82	response, err := makeRequest(r, "GET", p)
83	if err != nil {
84		return -1, nil, err
85	}
86	err = response.parseFromJSON(&envelope)
87	return envelope.TotalCount, envelope.Items, err
88}
89
90// CreateList creates a new mailing list under your Mailgun account.
91// You need specify only the Address and Name members of the prototype;
92// Description, and AccessLevel are optional.
93// If unspecified, Description remains blank,
94// while AccessLevel defaults to Everyone.
95func (mg *MailgunImpl) CreateList(prototype List) (List, error) {
96	r := newHTTPRequest(generatePublicApiUrl(mg, listsEndpoint))
97	r.setClient(mg.Client())
98	r.setBasicAuth(basicAuthUser, mg.ApiKey())
99	p := newUrlEncodedPayload()
100	if prototype.Address != "" {
101		p.addValue("address", prototype.Address)
102	}
103	if prototype.Name != "" {
104		p.addValue("name", prototype.Name)
105	}
106	if prototype.Description != "" {
107		p.addValue("description", prototype.Description)
108	}
109	if prototype.AccessLevel != "" {
110		p.addValue("access_level", prototype.AccessLevel)
111	}
112	response, err := makePostRequest(r, p)
113	if err != nil {
114		return List{}, err
115	}
116	var l List
117	err = response.parseFromJSON(&l)
118	return l, err
119}
120
121// DeleteList removes all current members of the list, then removes the list itself.
122// Attempts to send e-mail to the list will fail subsequent to this call.
123func (mg *MailgunImpl) DeleteList(addr string) error {
124	r := newHTTPRequest(generatePublicApiUrl(mg, listsEndpoint) + "/" + addr)
125	r.setClient(mg.Client())
126	r.setBasicAuth(basicAuthUser, mg.ApiKey())
127	_, err := makeDeleteRequest(r)
128	return err
129}
130
131// GetListByAddress allows your application to recover the complete List structure
132// representing a mailing list, so long as you have its e-mail address.
133func (mg *MailgunImpl) GetListByAddress(addr string) (List, error) {
134	r := newHTTPRequest(generatePublicApiUrl(mg, listsEndpoint) + "/" + addr)
135	r.setClient(mg.Client())
136	r.setBasicAuth(basicAuthUser, mg.ApiKey())
137	response, err := makeGetRequest(r)
138	if err != nil {
139		return List{}, err
140	}
141
142	var envelope struct {
143		List `json:"list"`
144	}
145	err = response.parseFromJSON(&envelope)
146	return envelope.List, err
147}
148
149// UpdateList allows you to change various attributes of a list.
150// Address, Name, Description, and AccessLevel are all optional;
151// only those fields which are set in the prototype will change.
152//
153// Be careful!  If changing the address of a mailing list,
154// e-mail sent to the old address will not succeed.
155// Make sure you account for the change accordingly.
156func (mg *MailgunImpl) UpdateList(addr string, prototype List) (List, error) {
157	r := newHTTPRequest(generatePublicApiUrl(mg, listsEndpoint) + "/" + addr)
158	r.setClient(mg.Client())
159	r.setBasicAuth(basicAuthUser, mg.ApiKey())
160	p := newUrlEncodedPayload()
161	if prototype.Address != "" {
162		p.addValue("address", prototype.Address)
163	}
164	if prototype.Name != "" {
165		p.addValue("name", prototype.Name)
166	}
167	if prototype.Description != "" {
168		p.addValue("description", prototype.Description)
169	}
170	if prototype.AccessLevel != "" {
171		p.addValue("access_level", prototype.AccessLevel)
172	}
173	var l List
174	response, err := makePutRequest(r, p)
175	if err != nil {
176		return l, err
177	}
178	err = response.parseFromJSON(&l)
179	return l, err
180}
181
182// GetMembers returns the list of members belonging to the indicated mailing list.
183// The s parameter can be set to one of three settings to help narrow the returned data set:
184// All indicates that you want both Members and unsubscribed members alike, while
185// Subscribed and Unsubscribed indicate you want only those eponymous subsets.
186func (mg *MailgunImpl) GetMembers(limit, skip int, s *bool, addr string) (int, []Member, error) {
187	r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, addr))
188	r.setClient(mg.Client())
189	r.setBasicAuth(basicAuthUser, mg.ApiKey())
190	p := newUrlEncodedPayload()
191	if limit != DefaultLimit {
192		p.addValue("limit", strconv.Itoa(limit))
193	}
194	if skip != DefaultSkip {
195		p.addValue("skip", strconv.Itoa(skip))
196	}
197	if s != nil {
198		p.addValue("subscribed", yesNo(*s))
199	}
200	var envelope struct {
201		TotalCount int      `json:"total_count"`
202		Items      []Member `json:"items"`
203	}
204	response, err := makeRequest(r, "GET", p)
205	if err != nil {
206		return -1, nil, err
207	}
208	err = response.parseFromJSON(&envelope)
209	return envelope.TotalCount, envelope.Items, err
210}
211
212// GetMemberByAddress returns a complete Member structure for a member of a mailing list,
213// given only their subscription e-mail address.
214func (mg *MailgunImpl) GetMemberByAddress(s, l string) (Member, error) {
215	r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, l) + "/" + s)
216	r.setClient(mg.Client())
217	r.setBasicAuth(basicAuthUser, mg.ApiKey())
218	response, err := makeGetRequest(r)
219	if err != nil {
220		return Member{}, err
221	}
222	var envelope struct {
223		Member Member `json:"member"`
224	}
225	err = response.parseFromJSON(&envelope)
226	return envelope.Member, err
227}
228
229// CreateMember registers a new member of the indicated mailing list.
230// If merge is set to true, then the registration may update an existing Member's settings.
231// Otherwise, an error will occur if you attempt to add a member with a duplicate e-mail address.
232func (mg *MailgunImpl) CreateMember(merge bool, addr string, prototype Member) error {
233	vs, err := json.Marshal(prototype.Vars)
234	if err != nil {
235		return err
236	}
237
238	r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, addr))
239	r.setClient(mg.Client())
240	r.setBasicAuth(basicAuthUser, mg.ApiKey())
241	p := newFormDataPayload()
242	p.addValue("upsert", yesNo(merge))
243	p.addValue("address", prototype.Address)
244	p.addValue("name", prototype.Name)
245	p.addValue("vars", string(vs))
246	if prototype.Subscribed != nil {
247		p.addValue("subscribed", yesNo(*prototype.Subscribed))
248	}
249	_, err = makePostRequest(r, p)
250	return err
251}
252
253// UpdateMember lets you change certain details about the indicated mailing list member.
254// Address, Name, Vars, and Subscribed fields may be changed.
255func (mg *MailgunImpl) UpdateMember(s, l string, prototype Member) (Member, error) {
256	r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, l) + "/" + s)
257	r.setClient(mg.Client())
258	r.setBasicAuth(basicAuthUser, mg.ApiKey())
259	p := newFormDataPayload()
260	if prototype.Address != "" {
261		p.addValue("address", prototype.Address)
262	}
263	if prototype.Name != "" {
264		p.addValue("name", prototype.Name)
265	}
266	if prototype.Vars != nil {
267		vs, err := json.Marshal(prototype.Vars)
268		if err != nil {
269			return Member{}, err
270		}
271		p.addValue("vars", string(vs))
272	}
273	if prototype.Subscribed != nil {
274		p.addValue("subscribed", yesNo(*prototype.Subscribed))
275	}
276	response, err := makePutRequest(r, p)
277	if err != nil {
278		return Member{}, err
279	}
280	var envelope struct {
281		Member Member `json:"member"`
282	}
283	err = response.parseFromJSON(&envelope)
284	return envelope.Member, err
285}
286
287// DeleteMember removes the member from the list.
288func (mg *MailgunImpl) DeleteMember(member, addr string) error {
289	r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, addr) + "/" + member)
290	r.setClient(mg.Client())
291	r.setBasicAuth(basicAuthUser, mg.ApiKey())
292	_, err := makeDeleteRequest(r)
293	return err
294}
295
296// CreateMemberList registers multiple Members and non-Member members to a single mailing list
297// in a single round-trip.
298// u indicates if the existing members should be updated or duplicates should be updated.
299// Use All to elect not to provide a default.
300// The newMembers list can take one of two JSON-encodable forms: an slice of strings, or
301// a slice of Member structures.
302// If a simple slice of strings is passed, each string refers to the member's e-mail address.
303// Otherwise, each Member needs to have at least the Address field filled out.
304// Other fields are optional, but may be set according to your needs.
305func (mg *MailgunImpl) CreateMemberList(u *bool, addr string, newMembers []interface{}) error {
306	r := newHTTPRequest(generateMemberApiUrl(mg, listsEndpoint, addr) + ".json")
307	r.setClient(mg.Client())
308	r.setBasicAuth(basicAuthUser, mg.ApiKey())
309	p := newFormDataPayload()
310	if u != nil {
311		p.addValue("upsert", yesNo(*u))
312	}
313	bs, err := json.Marshal(newMembers)
314	if err != nil {
315		return err
316	}
317	fmt.Println(string(bs))
318	p.addValue("members", string(bs))
319	_, err = makePostRequest(r, p)
320	return err
321}
322