Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ require (
github.com/ttacon/libphonenumber v1.2.1 // indirect
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/net v0.49.0 // indirect
golang.org/x/oauth2 v0.34.0 // indirect
golang.org/x/oauth2 v0.34.0
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
Expand Down
40 changes: 40 additions & 0 deletions service/gmail/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Package gmail implements a Gmail notifier, allowing plain text emails to be sent
to multiple recipients through the Gmail API using OAuth2 credentials.

Usage:

package main

import (
"context"
"log"

"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/gmail"
"golang.org/x/oauth2"
)

func main() {
notifier := notify.New()

token := &oauth2.Token{AccessToken: "ACCESS_TOKEN"}
tokenSource := oauth2.StaticTokenSource(token)

gmailService, err := gmail.New(tokenSource, "sender@example.com")
if err != nil {
log.Fatal(err)
}

gmailService.AddReceivers("alice@example.com", "bob@example.com")

notifier.UseServices(gmailService)

_ = notifier.Send(
context.Background(),
"Hello!",
"I am a bot written in Go!",
)
}
*/
package gmail
74 changes: 74 additions & 0 deletions service/gmail/gmail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package gmail

import (
"context"
"encoding/base64"
"fmt"
"net/mail"
"strings"

"golang.org/x/oauth2"
googleGmail "google.golang.org/api/gmail/v1"
"google.golang.org/api/option"
)

// Gmail struct holds necessary data to communicate with the Gmail API.
type Gmail struct {
client *googleGmail.Service
senderAddr string
recipients []string
}

// New returns a new instance of a Gmail notification service.
// tokenSource provides OAuth2 credentials for the Gmail API.
// senderAddr is the email address to send from (typically the authenticated user's email).
func New(tokenSource oauth2.TokenSource, senderAddr string) (*Gmail, error) {
srv, err := googleGmail.NewService(context.Background(), option.WithTokenSource(tokenSource))
if err != nil {
return nil, fmt.Errorf("create gmail service: %w", err)
}

return &Gmail{
client: srv,
senderAddr: senderAddr,
recipients: []string{},
}, nil
}

// AddReceivers takes email addresses and adds them to the internal recipient list.
func (g *Gmail) AddReceivers(emails ...string) {
g.recipients = append(g.recipients, emails...)
}

// Send takes a message subject and body and sends them to all recipients via Gmail API.
func (g Gmail) Send(ctx context.Context, subject, message string) error {
for _, recipient := range g.recipients {
select {
case <-ctx.Done():
return ctx.Err()
default:
from := mail.Address{Address: g.senderAddr}
to := mail.Address{Address: recipient}

header := fmt.Sprintf(
"From: %s\r\nTo: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=\"UTF-8\"\r\n\r\n%s",
from.String(),
to.String(),
subject,
message,
)

raw := base64.URLEncoding.EncodeToString([]byte(header))
// Gmail API uses URL-safe base64 without padding.
raw = strings.TrimRight(raw, "=")

gmailMessage := &googleGmail.Message{Raw: raw}
_, err := g.client.Users.Messages.Send("me", gmailMessage).Context(ctx).Do()
if err != nil {
return fmt.Errorf("send email to %q: %w", recipient, err)
}
}
}

return nil
}
56 changes: 56 additions & 0 deletions service/gmail/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Gmail Usage

Ensure that you have already navigated to your GOPATH and installed the following packages:

* `go get -u github.com/nikoksr/notify`
* `go get golang.org/x/oauth2`
* `go get google.golang.org/api/gmail/v1`

## Steps for Gmail API

These are general and very high level instructions

1. Create or select a Google Cloud project
2. Enable the Gmail API for that project
3. Configure an OAuth consent screen
4. Create OAuth 2.0 credentials for your application
5. Request a token with a Gmail sending scope such as `https://www.googleapis.com/auth/gmail.send`
6. Use the authenticated email address as the sender address
7. Now you should be good to use the code below

## Sample Code

```go
package main

import (
"context"
"log"

"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/gmail"
"golang.org/x/oauth2"
)

func main() {
notifier := notify.New()

token := &oauth2.Token{AccessToken: "ACCESS_TOKEN"}
tokenSource := oauth2.StaticTokenSource(token)

gmailService, err := gmail.New(tokenSource, "sender@example.com")
if err != nil {
log.Fatal(err)
}

gmailService.AddReceivers("alice@example.com", "bob@example.com")

notifier.UseServices(gmailService)

_ = notifier.Send(
context.Background(),
"Hello!",
"I am a bot written in Go!",
)
}
```
Loading