Files
2018-04-25 13:16:36 +02:00

260 lines
6.9 KiB
Go

package notificationservice
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"bitbucket.org/nemt/nemt-portal-api/application/entitymapping"
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
"bitbucket.org/nemt/nemt-portal-api/domain"
"bitbucket.org/nemt/nemt-portal-api/domain/contract"
"bitbucket.org/nemt/nemt-portal-api/domain/service"
"bitbucket.org/nemt/nemt-portal-api/infra/config"
)
const (
twilioMessageURL = "https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json"
twilioNumbersURL = "https://api.twilio.com/2010-04-01/Accounts/%s/AvailablePhoneNumbers/US/Local.json"
twiliActualNumbersURL = "https://api.twilio.com/2010-04-01/Accounts/%s/IncomingPhoneNumbers.json"
)
// twilioService holds methods to twillio application service
type twilioService struct {
svc *service.Service
mapEntity *entitymapping.Mapper
cfg *config.Config
urlMessage string
urlNumber string
urlActualNumber string
cache contract.CacheManager
}
// newTwilioService returns a twilioService instance
func newTwilioService(svc *service.Service, mapper *entitymapping.Mapper, cfg *config.Config, cache contract.CacheManager) *twilioService {
return &twilioService{
svc: svc,
mapEntity: mapper,
cfg: cfg,
urlMessage: fmt.Sprintf(twilioMessageURL, cfg.Twilio.Account),
urlNumber: fmt.Sprintf(twilioNumbersURL, cfg.Twilio.Account),
urlActualNumber: fmt.Sprintf(twiliActualNumbersURL, cfg.Twilio.Account),
cache: cache,
}
}
func (s twilioService) GetProxyNumber(to string) (viewmodel.ProxyNumber, error) {
key := "proxy" + to
var number viewmodel.ProxyNumber
err := s.cache.GetStruct(key, &number)
if err != nil || err == domain.ErrCacheMiss {
return number, err
} else {
return number, nil
}
}
func (s twilioService) GetAvaliableNumbers(to string) (viewmodel.ListNumbers, error) {
if to != "" {
if strings.Contains(to, "+1") {
to = strings.Replace(to, "+1", "", -1)
}
area := to[0:3]
msgData := url.Values{}
msgData.Set("PhoneNumber", area)
client := &http.Client{}
req, _ := http.NewRequest("GET", s.urlActualNumber+"?"+msgData.Encode(), bytes.NewBuffer([]byte{}))
req.SetBasicAuth(s.cfg.Twilio.Account, s.cfg.Twilio.Token)
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error Twilio: ", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
var data viewmodel.ListNumbers
decoder := json.NewDecoder(resp.Body)
err := decoder.Decode(&data)
if err != nil {
return viewmodel.ListNumbers{}, err
}
return data, nil
} else {
return viewmodel.ListNumbers{}, errors.New(resp.Status)
}
}
return viewmodel.ListNumbers{}, nil
}
func (s twilioService) GetAvaliableNewNumbers(to string) (viewmodel.ListNumbers, error) {
if to != "" {
if strings.Contains(to, "+1") {
to = strings.Replace(to, "+1", "", -1)
}
area := to[0:3]
msgData := url.Values{}
msgData.Set("AreaCode", area)
client := &http.Client{}
req, _ := http.NewRequest("GET", s.urlNumber+"?"+msgData.Encode(), bytes.NewBuffer([]byte{}))
req.SetBasicAuth(s.cfg.Twilio.Account, s.cfg.Twilio.Token)
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error Twilio: ", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
var data viewmodel.ListNumbers
decoder := json.NewDecoder(resp.Body)
err := decoder.Decode(&data)
if err != nil {
return viewmodel.ListNumbers{}, err
}
return data, nil
} else {
return viewmodel.ListNumbers{}, errors.New(resp.Status)
}
}
return viewmodel.ListNumbers{}, nil
}
func (s twilioService) SaveNewNumber(number viewmodel.Number) (viewmodel.Number, error) {
phoneNumber := number.PhoneNumber
if strings.Contains(phoneNumber, "+1") {
phoneNumber = strings.Replace(phoneNumber, "+1", "", -1)
}
area := phoneNumber[0:3]
msgData := url.Values{}
msgData.Set("PhoneNumber", number.PhoneNumber)
msgData.Set("AreaCode", area)
msgData.Set("FriendlyName", number.FriendlyName)
msgData.Set("SmsApplicationSid", s.cfg.Twilio.TwiMLSID)
msgDataReader := *strings.NewReader(msgData.Encode())
client := &http.Client{}
req, _ := http.NewRequest("POST", s.urlActualNumber, &msgDataReader)
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.SetBasicAuth(s.cfg.Twilio.Account, s.cfg.Twilio.Token)
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error Twilio: ", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
decoder := json.NewDecoder(resp.Body)
err := decoder.Decode(&number)
if err != nil {
return viewmodel.Number{}, err
}
return number, nil
} else {
return viewmodel.Number{}, errors.New(resp.Status)
}
}
func (s twilioService) GetValidNumber(from string, to string) (viewmodel.Number, error) {
list, err := s.GetAvaliableNumbers(to)
if err != nil {
return viewmodel.Number{}, err
}
if len(list.IncomingPhones) > 0 {
return list.IncomingPhones[0], nil
}
list, err = s.GetAvaliableNewNumbers(to)
if err != nil {
return viewmodel.Number{}, err
}
if len(list.AvailableNumbers) > 0 {
number, err := s.SaveNewNumber(list.AvailableNumbers[0])
if err != nil {
fmt.Println("Error on GetValidNumber: ", err.Error())
return viewmodel.Number{}, err
}
return number, nil
} else {
return viewmodel.Number{}, errors.New("No new number available")
}
}
func (s twilioService) GetNumber(from string, to string) (string, error) {
number, err := s.GetValidNumber(from, to)
if err != nil {
return from, err
}
return number.PhoneNumber, nil
}
func (s twilioService) SendSMS(from string, to string, message string) error {
if from == "" {
return errors.New("TwilioService.SendSMS: from parameter is mandatory")
}
if to == "" {
return errors.New("TwilioService.SendSMS: to parameter is mandatory")
}
if message == "" {
return errors.New("TwilioService.SendSMS: message parameter is mandatory")
}
senderNumber, err := s.GetNumber(from, to)
if err != nil {
return err
}
msgData := url.Values{}
msgData.Set("To", to)
msgData.Set("From", senderNumber)
msgData.Set("Body", message)
msgDataReader := *strings.NewReader(msgData.Encode())
client := &http.Client{}
req, _ := http.NewRequest("POST", s.urlMessage, &msgDataReader)
req.SetBasicAuth(s.cfg.Twilio.Account, s.cfg.Twilio.Token)
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error Twilio: ", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
var data map[string]interface{}
decoder := json.NewDecoder(resp.Body)
err := decoder.Decode(&data)
if err != nil {
return err
}
} else {
fmt.Println("Error on Twilio Response: ", resp.Status)
return errors.New(resp.Status)
}
return nil
}