2015-12-01 21:05:24 +00:00
|
|
|
package mailchimp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"regexp"
|
|
|
|
"sync"
|
2016-02-29 16:52:58 +00:00
|
|
|
"time"
|
2015-12-01 21:05:24 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
reports_endpoint string = "/3.0/reports"
|
|
|
|
reports_endpoint_campaign string = "/3.0/reports/%s"
|
|
|
|
)
|
|
|
|
|
|
|
|
var mailchimp_datacenter = regexp.MustCompile("[a-z]+[0-9]+$")
|
|
|
|
|
|
|
|
type ChimpAPI struct {
|
|
|
|
Transport http.RoundTripper
|
|
|
|
Debug bool
|
|
|
|
|
|
|
|
sync.Mutex
|
|
|
|
|
|
|
|
url *url.URL
|
|
|
|
}
|
|
|
|
|
|
|
|
type ReportsParams struct {
|
|
|
|
Count string
|
|
|
|
Offset string
|
|
|
|
SinceSendTime string
|
|
|
|
BeforeSendTime string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *ReportsParams) String() string {
|
|
|
|
v := url.Values{}
|
|
|
|
if p.Count != "" {
|
|
|
|
v.Set("count", p.Count)
|
|
|
|
}
|
|
|
|
if p.Offset != "" {
|
|
|
|
v.Set("offset", p.Offset)
|
|
|
|
}
|
|
|
|
if p.BeforeSendTime != "" {
|
|
|
|
v.Set("before_send_time", p.BeforeSendTime)
|
|
|
|
}
|
|
|
|
if p.SinceSendTime != "" {
|
|
|
|
v.Set("since_send_time", p.SinceSendTime)
|
|
|
|
}
|
|
|
|
return v.Encode()
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewChimpAPI(apiKey string) *ChimpAPI {
|
|
|
|
u := &url.URL{}
|
|
|
|
u.Scheme = "https"
|
|
|
|
u.Host = fmt.Sprintf("%s.api.mailchimp.com", mailchimp_datacenter.FindString(apiKey))
|
|
|
|
u.User = url.UserPassword("", apiKey)
|
|
|
|
return &ChimpAPI{url: u}
|
|
|
|
}
|
|
|
|
|
|
|
|
type APIError struct {
|
|
|
|
Status int `json:"status"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
Title string `json:"title"`
|
|
|
|
Detail string `json:"detail"`
|
|
|
|
Instance string `json:"instance"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e APIError) Error() string {
|
|
|
|
return fmt.Sprintf("ERROR %v: %v. See %v", e.Status, e.Title, e.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
func chimpErrorCheck(body []byte) error {
|
|
|
|
var e APIError
|
|
|
|
json.Unmarshal(body, &e)
|
|
|
|
if e.Title != "" || e.Status != 0 {
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *ChimpAPI) GetReports(params ReportsParams) (ReportsResponse, error) {
|
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
a.url.Path = reports_endpoint
|
|
|
|
|
|
|
|
var response ReportsResponse
|
|
|
|
rawjson, err := runChimp(a, params)
|
|
|
|
if err != nil {
|
|
|
|
return response, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(rawjson, &response)
|
|
|
|
if err != nil {
|
|
|
|
return response, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *ChimpAPI) GetReport(campaignID string) (Report, error) {
|
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
a.url.Path = fmt.Sprintf(reports_endpoint_campaign, campaignID)
|
|
|
|
|
|
|
|
var response Report
|
|
|
|
rawjson, err := runChimp(a, ReportsParams{})
|
|
|
|
if err != nil {
|
|
|
|
return response, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(rawjson, &response)
|
|
|
|
if err != nil {
|
|
|
|
return response, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func runChimp(api *ChimpAPI, params ReportsParams) ([]byte, error) {
|
2016-02-29 16:52:58 +00:00
|
|
|
client := &http.Client{
|
|
|
|
Transport: api.Transport,
|
|
|
|
Timeout: time.Duration(4 * time.Second),
|
|
|
|
}
|
2015-12-01 21:05:24 +00:00
|
|
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
req, err := http.NewRequest("GET", api.url.String(), &b)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
req.URL.RawQuery = params.String()
|
|
|
|
req.Header.Set("User-Agent", "Telegraf-MailChimp-Plugin")
|
|
|
|
if api.Debug {
|
2016-09-30 21:37:56 +00:00
|
|
|
log.Printf("D! Request URL: %s", req.URL.String())
|
2015-12-01 21:05:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := client.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if api.Debug {
|
2016-09-30 21:37:56 +00:00
|
|
|
log.Printf("D! Response Body:%s", string(body))
|
2015-12-01 21:05:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err = chimpErrorCheck(body); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return body, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type ReportsResponse struct {
|
|
|
|
Reports []Report `json:"reports"`
|
|
|
|
TotalItems int `json:"total_items"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Report struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
CampaignTitle string `json:"campaign_title"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
EmailsSent int `json:"emails_sent"`
|
|
|
|
AbuseReports int `json:"abuse_reports"`
|
|
|
|
Unsubscribed int `json:"unsubscribed"`
|
|
|
|
SendTime string `json:"send_time"`
|
|
|
|
|
|
|
|
TimeSeries []TimeSerie
|
|
|
|
Bounces Bounces `json:"bounces"`
|
|
|
|
Forwards Forwards `json:"forwards"`
|
|
|
|
Opens Opens `json:"opens"`
|
|
|
|
Clicks Clicks `json:"clicks"`
|
|
|
|
FacebookLikes FacebookLikes `json:"facebook_likes"`
|
|
|
|
IndustryStats IndustryStats `json:"industry_stats"`
|
|
|
|
ListStats ListStats `json:"list_stats"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Bounces struct {
|
|
|
|
HardBounces int `json:"hard_bounces"`
|
|
|
|
SoftBounces int `json:"soft_bounces"`
|
|
|
|
SyntaxErrors int `json:"syntax_errors"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Forwards struct {
|
|
|
|
ForwardsCount int `json:"forwards_count"`
|
|
|
|
ForwardsOpens int `json:"forwards_opens"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Opens struct {
|
|
|
|
OpensTotal int `json:"opens_total"`
|
|
|
|
UniqueOpens int `json:"unique_opens"`
|
|
|
|
OpenRate float64 `json:"open_rate"`
|
|
|
|
LastOpen string `json:"last_open"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type Clicks struct {
|
|
|
|
ClicksTotal int `json:"clicks_total"`
|
|
|
|
UniqueClicks int `json:"unique_clicks"`
|
|
|
|
UniqueSubscriberClicks int `json:"unique_subscriber_clicks"`
|
|
|
|
ClickRate float64 `json:"click_rate"`
|
|
|
|
LastClick string `json:"last_click"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type FacebookLikes struct {
|
|
|
|
RecipientLikes int `json:"recipient_likes"`
|
|
|
|
UniqueLikes int `json:"unique_likes"`
|
|
|
|
FacebookLikes int `json:"facebook_likes"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type IndustryStats struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
OpenRate float64 `json:"open_rate"`
|
|
|
|
ClickRate float64 `json:"click_rate"`
|
|
|
|
BounceRate float64 `json:"bounce_rate"`
|
|
|
|
UnopenRate float64 `json:"unopen_rate"`
|
|
|
|
UnsubRate float64 `json:"unsub_rate"`
|
|
|
|
AbuseRate float64 `json:"abuse_rate"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type ListStats struct {
|
|
|
|
SubRate float64 `json:"sub_rate"`
|
|
|
|
UnsubRate float64 `json:"unsub_rate"`
|
|
|
|
OpenRate float64 `json:"open_rate"`
|
|
|
|
ClickRate float64 `json:"click_rate"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type TimeSerie struct {
|
|
|
|
TimeStamp string `json:"timestamp"`
|
|
|
|
EmailsSent int `json:"emails_sent"`
|
|
|
|
UniqueOpens int `json:"unique_opens"`
|
|
|
|
RecipientsClick int `json:"recipients_click"`
|
|
|
|
}
|