diff --git a/config.sample.toml b/config.sample.toml index 3e2a87b..7d143c7 100644 --- a/config.sample.toml +++ b/config.sample.toml @@ -13,4 +13,13 @@ config = ''' "message_type": "", "sender_id": "" } +''' + +[messenger.ses] +config = ''' +{ + "access_key": "", + "secret_key": "", + "region": "", +} ''' \ No newline at end of file diff --git a/go.mod b/go.mod index 9fad21a..978fe2c 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-chi/chi v1.5.4 github.com/knadh/koanf v1.4.0 github.com/knadh/listmonk v1.1.0 + github.com/knadh/smtppool v0.4.0 github.com/spf13/pflag v1.0.5 ) diff --git a/go.sum b/go.sum index bbd57d2..12e2cba 100644 --- a/go.sum +++ b/go.sum @@ -86,6 +86,8 @@ github.com/knadh/koanf v1.4.0/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO4 github.com/knadh/listmonk v1.1.0 h1:X5qIOKuyC3OxfKGf82C1yTIMHwsjWxvf1gzn2ulbXOo= github.com/knadh/listmonk v1.1.0/go.mod h1:XlnRvP4GDUZXZNq0+Yl8SqComqpZ9VoS3ZAzdakaiHs= github.com/knadh/smtppool v0.2.1/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA= +github.com/knadh/smtppool v0.4.0 h1:335iXPwZ6katJVhauV4O6e8uPvvPmO6YLrfDQhb6UvE= +github.com/knadh/smtppool v0.4.0/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA= github.com/knadh/stuffbin v1.1.0/go.mod h1:yVCFaWaKPubSNibBsTAJ939q2ABHudJQxRWZWV5yh+4= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= diff --git a/messenger/ses.go b/messenger/ses.go new file mode 100644 index 0000000..a70ab71 --- /dev/null +++ b/messenger/ses.go @@ -0,0 +1,130 @@ +package messenger + +import ( + "encoding/json" + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ses" + "github.com/knadh/smtppool" +) + +const ( + ContentTypeHTML = "html" + ContentTypePlain = "plain" +) + +type sesCfg struct { + AccessKey string `json:"access_key"` + SecretKey string `json:"secret_key"` + Region string `json:"region"` + Log bool `json:"log"` +} + +type sesMessenger struct { + cfg sesCfg + client *ses.SES +} + +func (s sesMessenger) Name() string { + return "ses" +} + +// Push sends the sms through pinpoint API. +func (s sesMessenger) Push(msg Message) error { + // convert attachments to smtppool.Attachments + a := make([]smtppool.Attachment, 0, len(msg.Attachments)) + for i := 0; i < len(msg.Attachments); i++ { + a[i] = smtppool.Attachment{ + Filename: msg.Attachments[i].Name, + Header: msg.Attachments[i].Header, + Content: msg.Attachments[i].Content, + } + } + + email := smtppool.Email{ + From: msg.From, + To: msg.To, + Subject: msg.Subject, + Sender: msg.From, + Headers: msg.Headers, + Attachments: a, + } + + switch { + case msg.ContentType == ContentTypePlain: + email.Text = msg.Body + case msg.ContentType == ContentTypeHTML: + email.HTML = msg.Body + } + + emailB, err := email.Bytes() + if err != nil { + return err + } + + to := make([]*string, 0, len(msg.To)) + for i := 0; i < len(msg.To); i++ { + to = append(to, &msg.To[i]) + } + + input := &ses.SendRawEmailInput{ + Source: &msg.From, + Destinations: to, + RawMessage: &ses.RawMessage{ + Data: emailB, + }, + } + + out, err := s.client.SendRawEmail(input) + if err != nil { + return err + } + + if s.cfg.Log { + log.Printf("successfully sent email to %s: %#+v", msg.Subscriber.Email, out) + } + + return nil +} + +func (s sesMessenger) Flush() error { + return nil +} + +func (s sesMessenger) Close() error { + return nil +} + +// NewAWSSES creates new instance of pinpoint +func NewAWSSES(cfg []byte) (Messenger, error) { + var c sesCfg + if err := json.Unmarshal(cfg, &c); err != nil { + return nil, err + } + + if c.Region == "" { + return nil, fmt.Errorf("invalid region") + } + if c.AccessKey == "" { + return nil, fmt.Errorf("invalid access_key") + } + if c.SecretKey == "" { + return nil, fmt.Errorf("invalid secret_key") + } + + sess := session.Must(session.NewSession()) + svc := ses.New(sess, + aws.NewConfig(). + WithCredentials(credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, "")). + WithRegion(c.Region), + ) + + return sesMessenger{ + client: svc, + cfg: c, + }, nil +}