1
0
mirror of https://github.com/nikoksr/notify.git synced 2024-11-28 08:39:13 +02:00

feat(service): Add Plivo service

Added support for Plivo as a backend service with unit tests and
documentation.

Signed-off-by: Prashanth Pai <mail@ppai.me>
This commit is contained in:
Prashanth Pai 2021-02-06 14:30:42 +05:30
parent 77cea7b546
commit 67b2d011b8
8 changed files with 319 additions and 3 deletions

View File

@ -64,10 +64,11 @@ _ = notifier.Send(
- *Discord* - *Discord*
- *Email* - *Email*
- *Microsoft Teams* - *Microsoft Teams*
- *Slack* - *Plivo*
- *Twitter*
- *Telegram*
- *Pushbullet* - *Pushbullet*
- *Slack*
- *Telegram*
- *Twitter*
## Roadmap <a id="roadmap"></a> ## Roadmap <a id="roadmap"></a>

4
go.mod
View File

@ -9,9 +9,13 @@ require (
github.com/dghubble/go-twitter v0.0.0-20201011215211-4b180d0cc78d github.com/dghubble/go-twitter v0.0.0-20201011215211-4b180d0cc78d
github.com/dghubble/oauth1 v0.7.0 github.com/dghubble/oauth1 v0.7.0
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/google/go-querystring v1.0.0 // indirect
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/plivo/plivo-go v5.5.1+incompatible
github.com/sirupsen/logrus v1.7.0 // indirect
github.com/slack-go/slack v0.8.0 github.com/slack-go/slack v0.8.0
github.com/stretchr/testify v1.7.0
github.com/technoweenie/multipartstreamer v1.0.1 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a golang.org/x/sync v0.0.0-20201207232520-09787c993a3a

5
go.sum
View File

@ -29,10 +29,15 @@ github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/plivo/plivo-go v5.5.1+incompatible h1:LtZaUNHjSrNzBCHAe/IdDBnLGlyZB+WX18Dr+dnlVzE=
github.com/plivo/plivo-go v5.5.1+incompatible/go.mod h1:OhnI9crdl6O+D94Lp1lvuwJoA3KUH39J6IM+j3HwCBE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/slack-go/slack v0.8.0 h1:ANyLY5KHLV+MxLJDQum2IuHTLwbCbDtaWY405X1EU9U= github.com/slack-go/slack v0.8.0 h1:ANyLY5KHLV+MxLJDQum2IuHTLwbCbDtaWY405X1EU9U=
github.com/slack-go/slack v0.8.0/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM= github.com/slack-go/slack v0.8.0/go.mod h1:FGqNzJBmxIsZURAxh2a8D21AnOVvvXZvGligs4npPUM=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=

49
service/plivo/README.md Normal file
View File

@ -0,0 +1,49 @@
# Plivo
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/nikoksr/notify/service/plivo)
## Prerequisites
You will need to have a [Plivo](https://www.plivo.com/) account and the
following things:
1. `Auth ID` and `Auth Token` from Plivo [console](https://console.plivo.com/dashboard/).
1. An active rented Plivo [phone number](https://console.plivo.com/active-phone-numbers/).
## Usage
```go
package main
import (
"log"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/plivo"
)
func main() {
plivoSvc, err := plivo.New(
&plivo.ClientOptions{
AuthID: "<Your-Plivo-Auth-Id>",
AuthToken: "<Your-Plivo-Auth-Token>",
}, &plivo.MessageOptions{
Source: "<Your-Plivo-Source-Number>",
})
if err != nil {
log.Fatalf("plivo.New() failed: %s", err.Error())
}
plivoSvc.AddReceivers("Destination1")
notifier := notify.New()
notifier.UseServices(plivoSvc)
err = notifier.Send("subject", "message")
if err != nil {
log.Fatalf("notifier.Send() failed: %s", err.Error())
}
log.Printf("notification sent")
}
```

41
service/plivo/doc.go Normal file
View File

@ -0,0 +1,41 @@
/*
Package plivo provides message notification integration for Plivo.
Usage:
package main
import (
"log"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/plivo"
)
func main() {
plivoSvc, err := plivo.New(
&plivo.ClientOptions{
AuthID: "<Your-Plivo-Auth-Id>",
AuthToken: "<Your-Plivo-Auth-Token>",
}, &plivo.MessageOptions{
Source: "<Your-Plivo-Source-Number>",
})
if err != nil {
log.Fatalf("plivo.New() failed: %s", err.Error())
}
plivoSvc.AddReceivers("Destination1")
notifier := notify.New()
notifier.UseServices(plivoSvc)
err = notifier.Send("subject", "message")
if err != nil {
log.Fatalf("notifier.Send() failed: %s", err.Error())
}
log.Printf("notification sent")
}
*/
package plivo

View File

@ -0,0 +1,36 @@
// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
package plivo
import (
plivo "github.com/plivo/plivo-go"
mock "github.com/stretchr/testify/mock"
)
// mockPlivoMsgClient is an autogenerated mock type for the plivoMsgClient type
type mockPlivoMsgClient struct {
mock.Mock
}
// Create provides a mock function with given fields: _a0
func (_m *mockPlivoMsgClient) Create(_a0 plivo.MessageCreateParams) (*plivo.MessageCreateResponseBody, error) {
ret := _m.Called(_a0)
var r0 *plivo.MessageCreateResponseBody
if rf, ok := ret.Get(0).(func(plivo.MessageCreateParams) *plivo.MessageCreateResponseBody); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*plivo.MessageCreateResponseBody)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(plivo.MessageCreateParams) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

102
service/plivo/plivo.go Normal file
View File

@ -0,0 +1,102 @@
package plivo
import (
"fmt"
"net/http"
"strings"
plivo "github.com/plivo/plivo-go"
)
// ClientOptions allow you to configure a Plivo SDK client.
type ClientOptions struct {
AuthID string // If empty, env variable PLIVO_AUTH_ID will be used
AuthToken string // If empty, env variable PLIVO_AUTH_TOKEN will be used
// Optional
HTTPClient *http.Client // Bring Your Own Client
}
// MessageOptions allow you to configure options for sending a message.
type MessageOptions struct {
Source string // a Plivo source phone number or a Plivo Powerpack UUID
// Optional
CallbackURL string // URL to which status update callbacks for the message should be sent
CallbackMethod string // The HTTP method to be used when calling CallbackURL - GET or POST(default)
}
// plivoMsgClient abstracts Plivo SDK for writing unit tests
type plivoMsgClient interface {
Create(plivo.MessageCreateParams) (*plivo.MessageCreateResponseBody, error)
}
// Service is a Plivo client
type Service struct {
client plivoMsgClient
mopts MessageOptions
destinations []string
}
// New creates a new instance of plivo service.
func New(cOpts *ClientOptions, mOpts *MessageOptions) (*Service, error) {
if cOpts == nil {
return nil, fmt.Errorf("client-options cannot be nil")
}
if mOpts == nil {
return nil, fmt.Errorf("message-options cannot be nil")
}
if mOpts.Source == "" {
return nil, fmt.Errorf("source cannot be empty")
}
client, err := plivo.NewClient(
cOpts.AuthID,
cOpts.AuthToken,
&plivo.ClientOptions{
HttpClient: cOpts.HTTPClient,
},
)
if err != nil {
return nil, fmt.Errorf("plivo.NewClient() failed: %w", err)
}
return &Service{
client: client.Messages,
mopts: *mOpts,
}, nil
}
// AddReceivers adds the given destination phone numbers to the notifier.
func (s *Service) AddReceivers(phoneNumbers ...string) {
s.destinations = append(s.destinations, phoneNumbers...)
}
// Send sends a SMS via Plivo to all previously added receivers.
func (s *Service) Send(subject, message string) error {
text := subject + "\n" + message
var dst string
switch len(s.destinations) {
case 0:
return fmt.Errorf("no receivers added")
case 1:
dst = s.destinations[0]
default:
// multiple destinations, use bulk message syntax
// see: https://www.plivo.com/docs/sms/api/message#bulk-messaging
dst = strings.Join(s.destinations, "<")
}
_, err := s.client.Create(plivo.MessageCreateParams{
Dst: dst,
Text: text,
Src: s.mopts.Source,
URL: s.mopts.CallbackURL,
Method: s.mopts.CallbackMethod,
})
return err
}

View File

@ -0,0 +1,78 @@
package plivo
import (
"errors"
"testing"
plivo "github.com/plivo/plivo-go"
"github.com/stretchr/testify/require"
)
func TestNew(t *testing.T) {
assert := require.New(t)
// nil ClientOptions
svc, err := New(nil, &MessageOptions{})
assert.NotNil(err)
assert.Nil(svc)
// nil MessageOptions
svc, err = New(&ClientOptions{}, nil)
assert.NotNil(err)
assert.Nil(svc)
// empty source
svc, err = New(&ClientOptions{}, &MessageOptions{})
assert.NotNil(err)
assert.Nil(svc)
// success
svc, err = New(&ClientOptions{}, &MessageOptions{Source: "12345"})
assert.Nil(err)
assert.NotNil(svc)
}
func TestAddReceivers(t *testing.T) {
assert := require.New(t)
svc, err := New(&ClientOptions{}, &MessageOptions{Source: "12345"})
assert.Nil(err)
assert.NotNil(svc)
nums := []string{"1", "2", "3", "4", "5"}
svc.AddReceivers(nums...)
assert.Equal(svc.destinations, nums)
}
func TestSend(t *testing.T) {
assert := require.New(t)
svc, err := New(&ClientOptions{}, &MessageOptions{Source: "12345"})
assert.Nil(err)
assert.NotNil(svc)
// no receivers added
err = svc.Send("message", "test")
assert.NotNil(err)
// test plivo client returning error
mockClient := new(mockPlivoMsgClient)
mockClient.On("Create", plivo.MessageCreateParams{Src: "12345", Dst: "67890", Text: "message\ntest"}).
Return(nil, errors.New("some error"))
svc.client = mockClient
svc.AddReceivers("67890")
err = svc.Send("message", "test")
assert.NotNil(err)
mockClient.AssertExpectations(t)
// test success and multiple receivers
mockClient = new(mockPlivoMsgClient)
mockClient.On("Create", plivo.MessageCreateParams{Src: "12345", Dst: "67890<09876", Text: "message\ntest"}).
Return(nil, nil)
svc.client = mockClient
svc.AddReceivers("09876")
err = svc.Send("message", "test")
assert.Nil(err)
mockClient.AssertExpectations(t)
}