Skip to content

Commit

Permalink
feat: add sendgrid bundle for sending emails
Browse files Browse the repository at this point in the history
Signed-off-by: Nick Hale <[email protected]>
  • Loading branch information
njhale committed Dec 31, 2024
1 parent 4d411df commit 4e12496
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 0 deletions.
2 changes: 2 additions & 0 deletions index.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
tools:
sendgrid:
reference: ./sendgrid
database:
reference: ./database
atlassian-jira:
Expand Down
3 changes: 3 additions & 0 deletions sendgrid/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.PHONY: build
build:
go build -o bin/gptscript-go-tool .
90 changes: 90 additions & 0 deletions sendgrid/cmd/send.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package cmd

import (
"context"
"fmt"
"os"
"strings"

"github.com/sendgrid/sendgrid-go"
"github.com/sendgrid/sendgrid-go/helpers/mail"
)

func Send(ctx context.Context, to, subject, textBody, htmlBody string) (string, error) {
// Retrieve and trim environment variables
sendGridAPIKey := strings.TrimSpace(os.Getenv("SENDGRID_API_KEY"))
if sendGridAPIKey == "" {
return "", fmt.Errorf("SENDGRID_API_KEY is not set")
}

fromEmail := strings.TrimSpace(os.Getenv("OBOT_NO_REPLY_EMAIL"))
if fromEmail == "" {
return "", fmt.Errorf("OBOT_NO_REPLY_EMAIL is not set")
}

to = strings.TrimSpace(to)
subject = strings.TrimSpace(subject)
textBody = strings.TrimSpace(textBody)
htmlBody = strings.TrimSpace(htmlBody)

if to == "" {
return "", fmt.Errorf("recipient email (to) is required")
}

// Split comma-delimited recipients and trim each email
toEmails := strings.Split(to, ",")
for i := range toEmails {
toEmails[i] = strings.TrimSpace(toEmails[i])
}

// Remove any empty emails after trimming
var validEmails []string
for _, email := range toEmails {
if email != "" {
validEmails = append(validEmails, email)
}
}

if len(validEmails) < 1 {
return "", fmt.Errorf("no valid recipient emails provided")
}

if subject == "" {
return "", fmt.Errorf("subject is required")
}

if textBody == "" && htmlBody == "" {
return "", fmt.Errorf("either textBody or htmlBody is required")
}

from := mail.NewEmail("Obot", fromEmail)
personalization := mail.NewPersonalization()

for _, email := range validEmails {
personalization.AddTos(mail.NewEmail("", email))
}

// Create email message
message := mail.NewV3Mail()
message.SetFrom(from)
message.Subject = subject
message.AddPersonalizations(personalization)
if textBody != "" {
message.AddContent(mail.NewContent("text/plain", textBody))
}
if htmlBody != "" {
message.AddContent(mail.NewContent("text/html", htmlBody))
}

client := sendgrid.NewSendClient(sendGridAPIKey)
response, err := client.SendWithContext(ctx, message)
if err != nil {
return "", fmt.Errorf("failed to send email: %w", err)
}

if response.StatusCode < 200 || response.StatusCode >= 300 {
return "", fmt.Errorf("failed to send email: status code %d, body: %s", response.StatusCode, response.Body)
}

return fmt.Sprintf("email sent successfully with status code: %d", response.StatusCode), nil
}
6 changes: 6 additions & 0 deletions sendgrid/credential/tool.gpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

Name: SendGrid API Key
Share Credential: github.com/gptscript-ai/credential as sendgrid
with "Please enter your SendGrid API key" as message and
token as field and
SENDGRID_API_KEY as env
11 changes: 11 additions & 0 deletions sendgrid/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/obot-platform/tools/sendgrid

go 1.23.4

require github.com/sendgrid/sendgrid-go v3.16.0+incompatible

require (
github.com/sendgrid/rest v2.6.9+incompatible // indirect
github.com/stretchr/testify v1.10.0 // indirect
golang.org/x/net v0.33.0 // indirect
)
14 changes: 14 additions & 0 deletions sendgrid/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
github.com/sendgrid/sendgrid-go v3.16.0+incompatible h1:i8eE6IMkiCy7vusSdacHHSBUpXyTcTXy/Rl9N9aZ/Qw=
github.com/sendgrid/sendgrid-go v3.16.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
52 changes: 52 additions & 0 deletions sendgrid/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"context"
"fmt"
"os"

"github.com/obot-platform/tools/sendgrid/cmd"
)

func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: gptscript-go-tool <command>")
os.Exit(1)
}
command := os.Args[1]

// Run the requested command
var (
result string
err error
ctx = context.Background()
)
switch command {
case "sendEmail":
// Retrieve environment variables for sending email
to := os.Getenv("TO")
subject := os.Getenv("SUBJECT")
textBody := os.Getenv("TEXT_BODY")
htmlBody := os.Getenv("HTML_BODY")

// Validate required fields
if to == "" || subject == "" || (textBody == "" && htmlBody == "") {
fmt.Println("Missing required environment variables: TO, SUBJECT, and at least one of TEXT_BODY or HTML_BODY")
os.Exit(1)
}

// Call the Send function
result, err = cmd.Send(ctx, to, subject, textBody, htmlBody)

default:
fmt.Printf("Unknown command: %s\n", command)
os.Exit(1)
}

if err != nil {
fmt.Println(err)
os.Exit(1)
}

fmt.Print(result)
}
31 changes: 31 additions & 0 deletions sendgrid/tool.gpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Name: SendGrid
Metadata: category: Capability
Metadata: icon: https://cdn.jsdelivr.net/npm/@phosphor-icons/core@2/assets/duotone/envelope-duotone.svg
Share Tools: Send Email

---

Name: Send Email
Description: Send an email using SendGrid.
Share Context: Send Email Context
Credentials: ./credential/tool.gpt
Param: to: A comma-delimited list of email addresses to send the email to.
Param: subject: The subject of the email.
Param: html_body: (optional) The HTML body of the email.
Param: text_body: (optional) The plain text body of the email.

#!${GPTSCRIPT_TOOL_DIR}/bin/gptscript-go-tool sendEmail

---
Name: Send Email Context
Type: context

# START INSTRUCTIONS: Send Email tool

The Send Email tool allows you to send emails using SendGrid.

- At least one of `html_body` or `text_body` must be provided.
- If both `html_body` and `text_body` are provided, both will be included in the email.
- Don't sign the email unless otherwise asked to do so.

# END INSTRUCTIONS: Send Email tool

0 comments on commit 4e12496

Please sign in to comment.