diff --git a/index.yaml b/index.yaml index 65fb7c20..cb26663a 100644 --- a/index.yaml +++ b/index.yaml @@ -1,4 +1,6 @@ tools: + sendgrid: + reference: ./sendgrid database: reference: ./database atlassian-jira: diff --git a/sendgrid/Makefile b/sendgrid/Makefile new file mode 100644 index 00000000..f648bc93 --- /dev/null +++ b/sendgrid/Makefile @@ -0,0 +1,3 @@ +.PHONY: build +build: + go build -o bin/gptscript-go-tool . diff --git a/sendgrid/cmd/send.go b/sendgrid/cmd/send.go new file mode 100644 index 00000000..ea332e4b --- /dev/null +++ b/sendgrid/cmd/send.go @@ -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 +} diff --git a/sendgrid/credential/tool.gpt b/sendgrid/credential/tool.gpt new file mode 100644 index 00000000..2fda069d --- /dev/null +++ b/sendgrid/credential/tool.gpt @@ -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 diff --git a/sendgrid/go.mod b/sendgrid/go.mod new file mode 100644 index 00000000..2df7ff4e --- /dev/null +++ b/sendgrid/go.mod @@ -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 +) diff --git a/sendgrid/go.sum b/sendgrid/go.sum new file mode 100644 index 00000000..c6f84970 --- /dev/null +++ b/sendgrid/go.sum @@ -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= diff --git a/sendgrid/main.go b/sendgrid/main.go new file mode 100644 index 00000000..a6758b31 --- /dev/null +++ b/sendgrid/main.go @@ -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 ") + 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) +} diff --git a/sendgrid/tool.gpt b/sendgrid/tool.gpt new file mode 100644 index 00000000..6684416f --- /dev/null +++ b/sendgrid/tool.gpt @@ -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