Skip to content

Commit

Permalink
feat:implement google-mail template
Browse files Browse the repository at this point in the history
fix: trigger_new_email.go file
  • Loading branch information
jaymesC committed Jul 26, 2024
1 parent d50f404 commit 9525d01
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 44 deletions.
1 change: 1 addition & 0 deletions internal/connectors/googlemail/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func NewConnector() (*sdk.ConnectorPlugin, error) {
NewGetMailByIDOperation(),
NewSendMailOperation(),
NewGetThreadOperation(),
NewSendTemplateMailOperation(),
NewListMailsOperation(),
},
})
Expand Down
6 changes: 0 additions & 6 deletions internal/connectors/googlemail/operation_send_email.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/base64"
"errors"
"fmt"
"regexp"
"strings"

"google.golang.org/api/gmail/v1"
Expand Down Expand Up @@ -126,11 +125,6 @@ func (c *SendMailOperation) Run(ctx *sdk.RunContext) (sdk.JSON, error) {
return "Message sent successfully!", nil
}

func isValidEmail(email string) bool {
emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`)
return emailRegex.MatchString(email)
}

func (c *SendMailOperation) Test(ctx *sdk.RunContext) (sdk.JSON, error) {
return c.Run(ctx)
}
Expand Down
184 changes: 184 additions & 0 deletions internal/connectors/googlemail/operation_send_email_template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package googlemail

import (
"context"
"encoding/base64"
"errors"
"fmt"
"strings"

"google.golang.org/api/gmail/v1"
"google.golang.org/api/option"

"github.com/wakflo/go-sdk/autoform"
sdk "github.com/wakflo/go-sdk/connector"
sdkcore "github.com/wakflo/go-sdk/core"
)

type sendTemplateMailOperationProps struct {
To string `json:"to"`
TemplateSubject string `json:"temp-subject"`
Subject string `json:"subject"`
Body string `json:"body"`
CC string `json:"cc"`
BCC string `json:"bcc"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
FromName string `json:"from"`
}

type SendTemplateMailOperation struct {
options *sdk.OperationInfo
}

func NewSendTemplateMailOperation() sdk.IOperation {
return &SendTemplateMailOperation{
options: &sdk.OperationInfo{
Name: "Send Template Email",
Description: "Send an personalized email.",
RequireAuth: true,
Auth: sharedAuth,
Input: map[string]*sdkcore.AutoFormSchema{
"temp-subject": autoform.NewShortTextField().
SetDisplayName(" Template title").
SetDescription("The template title.").
SetRequired(true).
Build(),
"subject": autoform.NewShortTextField().
SetDisplayName(" Subject").
SetDescription("The template subject you want to send.").
SetRequired(true).
Build(),
"to": autoform.NewShortTextField().
SetDisplayName(" To").
SetDescription("The receiver of the message address").
SetRequired(true).
Build(),
"from": autoform.NewShortTextField().
SetDisplayName(" From").
SetDescription("The sender of the message address").
SetRequired(false).
Build(),
"firstName": autoform.NewShortTextField().
SetDisplayName("First Name").
SetDescription("First name").
SetRequired(true).
Build(),
"lastName": autoform.NewShortTextField().
SetDisplayName("Last Name").
SetDescription("Last name").
SetRequired(false).
Build(),
},
ErrorSettings: sdkcore.StepErrorSettings{
ContinueOnError: false,
RetryOnError: false,
},
},
}
}

func (c *SendTemplateMailOperation) Run(ctx *sdk.RunContext) (sdk.JSON, error) {
if ctx.Auth.Token == nil {
return nil, errors.New("missing google auth token")
}

input := sdk.InputToType[sendTemplateMailOperationProps](ctx)
gmailService, err := gmail.NewService(context.Background(), option.WithTokenSource(*ctx.Auth.TokenSource))
if err != nil {
return nil, err
}

userProfile, err := gmailService.Users.GetProfile("me").Do()
if err != nil {
return nil, err
}
userEmail := userProfile.EmailAddress

if !isValidEmail(input.To) {
return nil, fmt.Errorf("invalid To email address: %s", input.To)
}

bccEmails := strings.Split(input.BCC, ",")
for _, email := range bccEmails {
email = strings.TrimSpace(email)
if email != "" && !isValidEmail(email) {
return nil, fmt.Errorf("invalid BCC email address: %s", email)
}
}

ccEmails := strings.Split(input.CC, ",")
for _, email := range ccEmails {
email = strings.TrimSpace(email)
if email != "" && !isValidEmail(email) {
return nil, fmt.Errorf("invalid CC email address: %s", email)
}
}

// Search for the template message
query := "subject:" + input.TemplateSubject
searchResult, err := gmailService.Users.Messages.List("me").Q(query).Do()
if err != nil {
return nil, errors.New("error searching for template")
}
if len(searchResult.Messages) == 0 {
return nil, errors.New("no template found with subject: " + input.TemplateSubject)
}

// Use the first matching message as the template
templateMsg, err := gmailService.Users.Messages.Get("me", searchResult.Messages[0].Id).Do()
if err != nil {
return nil, errors.New("error fetching template message")
}

// Decode the email body
var emailBody string
for _, part := range templateMsg.Payload.Parts {
if part.MimeType == "text/html" {
data, _ := base64.URLEncoding.DecodeString(part.Body.Data)
emailBody = string(data)
break
}
}
if emailBody == "" {
return nil, errors.New("no HTML body found in template message")
}

emailBody = strings.ReplaceAll(emailBody, "{{FirstName}}", input.FirstName)
emailBody = strings.ReplaceAll(emailBody, "{{LastName}}", input.LastName)

var fromField string
if input.FromName != "" {
fromField = fmt.Sprintf("%s <%s>", input.FromName, userEmail)
} else {
fromField = userEmail
}

// Use the modified template as the email body
message := &gmail.Message{
Raw: base64.URLEncoding.EncodeToString([]byte(
"From: " + fromField + "\r\n" +
"To: " + input.To + "\r\n" +
"Subject: " + input.TemplateSubject + "\r\n" +
"Content-Type: text/html; charset=UTF-8\r\n\r\n" +
emailBody)),
}

// Send the message
_, err = gmailService.Users.Messages.Send("me", message).Do()
if err != nil {
return nil, err
}

return map[string]interface{}{
"status": "Message sent successfully!",
}, nil
}

func (c *SendTemplateMailOperation) Test(ctx *sdk.RunContext) (sdk.JSON, error) {
return c.Run(ctx)
}

func (c *SendTemplateMailOperation) GetInfo() *sdk.OperationInfo {
return c.options
}
18 changes: 18 additions & 0 deletions internal/connectors/googlemail/shared.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package googlemail

import (
"regexp"

"google.golang.org/api/gmail/v1"

"github.com/wakflo/go-sdk/autoform"
sdkcore "github.com/wakflo/go-sdk/core"
)
Expand All @@ -19,3 +23,17 @@ var viewMailFormat = []*sdkcore.AutoFormSchema{
{Const: "raw", Title: "Raw"},
{Const: "metadata", Title: "Metadata"},
}

func isValidEmail(email string) bool {
emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`)
return emailRegex.MatchString(email)
}

func getHeader(headers []*gmail.MessagePartHeader, name string) string {
for _, header := range headers {
if header.Name == name {
return header.Value
}
}
return ""
}
67 changes: 29 additions & 38 deletions internal/connectors/googlemail/trigger_new_email.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package googlemail
import (
"context"
"errors"
"fmt"
"strings"
"time"

"google.golang.org/api/gmail/v1"
Expand All @@ -15,19 +13,11 @@ import (
sdkcore "github.com/wakflo/go-sdk/core"
)

type triggerNewEmailProps struct {
Subject string `json:"subject"`
From string `json:"from"`
MaxResults int `json:"maxResults"`
RecievedTime *time.Time `json:"receivedTime"`
RecievedTimeOp *string `json:"receivedTimeOp"`
}

type TriggerNewEmail struct {
options *sdk.TriggerInfo
}

func NewTriggerNewEmail() sdk.ITrigger {
func NewTriggerNewEmail() *TriggerNewEmail {
return &TriggerNewEmail{
options: &sdk.TriggerInfo{
Name: "New Email ",
Expand Down Expand Up @@ -67,55 +57,56 @@ func (t *TriggerNewEmail) Run(ctx *sdk.RunContext) (sdk.JSON, error) {
return nil, errors.New("missing google auth token")
}

input := sdk.InputToType[triggerNewEmailProps](ctx)
gmailService, err := gmail.NewService(context.Background(), option.WithTokenSource(*ctx.Auth.TokenSource))
if err != nil {
return nil, err
}

var qarr []string
if input.Subject != "" {
qarr = append(qarr, fmt.Sprintf("subject:%v", input.Subject))
}
if input.From != "" {
qarr = append(qarr, fmt.Sprintf("from:%v", input.From))
}
if input.RecievedTime != nil {
input.RecievedTime = ctx.Metadata.LastRun
}
if input.RecievedTime != nil {
op := ">"
if input.RecievedTimeOp != nil {
op = *input.RecievedTimeOp
}
qarr = append(qarr, fmt.Sprintf(`received time %v '%v'`, op, input.RecievedTime.UTC().Format("2006-01-02T15:04:05Z")))
var lastRunTime time.Time
if ctx.Metadata.LastRun != nil {
lastRunTime = *ctx.Metadata.LastRun
}

q := strings.Join(qarr, " ")
query := "after:" + lastRunTime.Format("2006/01/02")

req := gmailService.Users.Messages.List("me").
Q(q).MaxResults(int64(input.MaxResults))

res, err := req.Do()
messages, err := gmailService.Users.Messages.List("me").Q(query).Do()
if err != nil {
return nil, err
}

return res.Messages, nil
newEmails := make([]map[string]interface{}, 0, len(messages.Messages))
for _, msg := range messages.Messages {
email, err := gmailService.Users.Messages.Get("me", msg.Id).Do()
if err != nil {
continue
}

emailData := map[string]interface{}{
"id": email.Id,
"subject": getHeader(email.Payload.Headers, "Subject"),
"from": getHeader(email.Payload.Headers, "From"),
"date": getHeader(email.Payload.Headers, "Date"),
}
newEmails = append(newEmails, emailData)
}

return sdk.JSON(map[string]interface{}{
"new_emails": newEmails,
}), nil
}

func (t TriggerNewEmail) Test(ctx *sdk.RunContext) (sdk.JSON, error) {
func (t *TriggerNewEmail) Test(ctx *sdk.RunContext) (sdk.JSON, error) {
return t.Run(ctx)
}

func (t TriggerNewEmail) OnEnabled(ctx *sdk.RunContext) error {
func (t *TriggerNewEmail) OnEnabled(ctx *sdk.RunContext) error {
return nil
}

func (t TriggerNewEmail) OnDisabled(ctx *sdk.RunContext) error {
func (t *TriggerNewEmail) OnDisabled(ctx *sdk.RunContext) error {
return nil
}

func (t TriggerNewEmail) GetInfo() *sdk.TriggerInfo {
func (t *TriggerNewEmail) GetInfo() *sdk.TriggerInfo {
return t.options
}

0 comments on commit 9525d01

Please sign in to comment.