Files
gocryptotrader/communications/smsglobal/smsglobal.go
Ryan O'Hara-Reid d636049fb2 exchanges: Initial context propagation (#744)
* gct: phase one context awareness pass

* exchanges: context propagation pass

* common/requester: force context requirement

* gctcli/exchanges: linter fix

* rpcserver: fix test using dummy rpc server

* backtester: fix comments

* grpc: add correct cancel and timeout for commands

* rpcserver_test: add comment on dummy server

* common: deprecated SendHTTPGetRequest

* linter: fix

* linter: turn on no context check

* apichecker: fix context linter issue

* binance: use param context

* common: remove checks as this gets executed before main

* common: change mutex to RW as clients can be used by multiple go routines.

* common: remove init and JIT default client. Unexport global variables and add protection.

* common: Add comments

* bithumb: after dinner mints fix
2021-09-11 13:52:07 +10:00

198 lines
5.2 KiB
Go

// Package smsglobal allows bulk messaging to a desired recipient list
package smsglobal
import (
"context"
"errors"
"flag"
"net/http"
"net/url"
"strings"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/communications/base"
"github.com/thrasher-corp/gocryptotrader/log"
)
const (
smsGlobalAPIURL = "https://www.smsglobal.com/http-api.php"
)
var (
errSMSNotSent = errors.New("SMSGlobal message not sent")
errContactNotFound = errors.New("SMSGlobal error contact not found")
)
// SMSGlobal is the overarching type across this package
type SMSGlobal struct {
base.Base
Contacts []Contact
Username string
Password string
SendFrom string
}
// Setup takes in a SMSGlobal configuration, sets username, password and
// and recipient list
func (s *SMSGlobal) Setup(cfg *base.CommunicationsConfig) {
s.Name = cfg.SMSGlobalConfig.Name
s.Enabled = cfg.SMSGlobalConfig.Enabled
s.Verbose = cfg.SMSGlobalConfig.Verbose
s.Username = cfg.SMSGlobalConfig.Username
s.Password = cfg.SMSGlobalConfig.Password
s.SendFrom = cfg.SMSGlobalConfig.From
var contacts []Contact
for x := range cfg.SMSGlobalConfig.Contacts {
contacts = append(contacts,
Contact{
Name: cfg.SMSGlobalConfig.Contacts[x].Name,
Number: cfg.SMSGlobalConfig.Contacts[x].Number,
Enabled: cfg.SMSGlobalConfig.Contacts[x].Enabled,
},
)
log.Debugf(log.CommunicationMgr, "SMSGlobal: SMS Contact: %s. Number: %s. Enabled: %v\n",
cfg.SMSGlobalConfig.Contacts[x].Name,
cfg.SMSGlobalConfig.Contacts[x].Number,
cfg.SMSGlobalConfig.Contacts[x].Enabled)
}
s.Contacts = contacts
}
// IsConnected returns whether or not the connection is connected
func (s *SMSGlobal) IsConnected() bool {
return s.Connected
}
// Connect connects to the service
func (s *SMSGlobal) Connect() error {
s.Connected = true
return nil
}
// PushEvent pushes an event to a contact list via SMS
func (s *SMSGlobal) PushEvent(event base.Event) error {
return s.SendMessageToAll(event.Message)
}
// GetEnabledContacts returns how many SMS contacts are enabled in the
// contact list
func (s *SMSGlobal) GetEnabledContacts() int {
counter := 0
for x := range s.Contacts {
if s.Contacts[x].Enabled {
counter++
}
}
return counter
}
// GetContactByNumber returns a contact with supplied number
func (s *SMSGlobal) GetContactByNumber(number string) (Contact, error) {
for x := range s.Contacts {
if s.Contacts[x].Number == number {
return s.Contacts[x], nil
}
}
return Contact{}, errContactNotFound
}
// GetContactByName returns a contact with supplied name
func (s *SMSGlobal) GetContactByName(name string) (Contact, error) {
for x := range s.Contacts {
if strings.EqualFold(s.Contacts[x].Name, name) {
return s.Contacts[x], nil
}
}
return Contact{}, errContactNotFound
}
// AddContact checks to see if a contact exists and adds them if it doesn't
func (s *SMSGlobal) AddContact(contact Contact) error {
if contact.Name == "" || contact.Number == "" {
return errors.New("SMSGlobal AddContact() error - nothing to add")
}
if s.ContactExists(contact) {
return errors.New("SMSGlobal AddContact() error - contact already exists")
}
s.Contacts = append(s.Contacts, contact)
return nil
}
// ContactExists checks to see if a contact exists
func (s *SMSGlobal) ContactExists(contact Contact) bool {
for x := range s.Contacts {
if s.Contacts[x].Number == contact.Number && strings.EqualFold(s.Contacts[x].Name, contact.Name) {
return true
}
}
return false
}
// RemoveContact removes a contact if it exists
func (s *SMSGlobal) RemoveContact(contact Contact) error {
if !s.ContactExists(contact) {
return errors.New("SMSGlobal RemoveContact() error - contact does not exist")
}
for x := range s.Contacts {
if s.Contacts[x].Name == contact.Name && s.Contacts[x].Number == contact.Number {
s.Contacts = append(s.Contacts[:x], s.Contacts[x+1:]...)
return nil
}
}
return errors.New("SMSGlobal RemoveContact() error - contact already removed")
}
// SendMessageToAll sends a message to all enabled contacts in cfg
func (s *SMSGlobal) SendMessageToAll(message string) error {
for x := range s.Contacts {
if s.Contacts[x].Enabled {
if s.Verbose {
log.Debugf(log.CommunicationMgr, "SMSGlobal: Sending SMS to %s. Number: %s. Message: %s [From: %s]\n",
s.Contacts[x].Name, s.Contacts[x].Number, message, s.SendFrom)
}
err := s.SendMessage(s.Contacts[x].Number, message)
if err != nil {
return err
}
}
}
return nil
}
// SendMessage sends a message to an individual contact
func (s *SMSGlobal) SendMessage(to, message string) error {
if flag.Lookup("test.v") != nil {
return nil
}
values := url.Values{}
values.Set("action", "sendsms")
values.Set("user", s.Username)
values.Set("password", s.Password)
values.Set("from", s.SendFrom)
values.Set("to", to)
values.Set("text", message)
headers := make(map[string]string)
headers["Content-Type"] = "application/x-www-form-urlencoded"
resp, err := common.SendHTTPRequest(context.TODO(),
http.MethodPost,
smsGlobalAPIURL,
headers,
strings.NewReader(values.Encode()),
s.Verbose)
if err != nil {
return err
}
if !strings.Contains(string(resp), "OK: 0; Sent queued message") {
return errSMSNotSent
}
return nil
}