diff --git a/config/config.go b/config/config.go index 322a34df..5a156b51 100644 --- a/config/config.go +++ b/config/config.go @@ -14,6 +14,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/currency" "github.com/thrasher-/gocryptotrader/portfolio" + "github.com/thrasher-/gocryptotrader/smsglobal" ) // Constants declared here are filename strings and test strings @@ -66,11 +67,7 @@ type SMSGlobalConfig struct { Enabled bool Username string Password string - Contacts []struct { - Name string - Number string - Enabled bool - } + Contacts []smsglobal.Contact } // Post holds the bot configuration data diff --git a/events/event_test.go b/events/event_test.go index a57ef5d0..93bf68c0 100644 --- a/events/event_test.go +++ b/events/event_test.go @@ -3,11 +3,31 @@ package events import ( "testing" + "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/currency/pair" "github.com/thrasher-/gocryptotrader/exchanges/ticker" + "github.com/thrasher-/gocryptotrader/smsglobal" ) +var ( + loaded = false +) + +func testSetup(t *testing.T) { + if !loaded { + cfg := config.GetConfig() + err := cfg.LoadConfig("") + if err != nil { + t.Fatalf("Test failed. Failed to load config %s", err) + } + smsglobal.New(cfg.SMS.Username, cfg.SMS.Password, cfg.Name, cfg.SMS.Contacts) + loaded = true + } +} + func TestAddEvent(t *testing.T) { + testSetup(t) + pair := pair.NewCurrencyPair("BTC", "USD") eventID, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest) if err != nil && eventID != 0 { @@ -36,6 +56,8 @@ func TestAddEvent(t *testing.T) { } func TestRemoveEvent(t *testing.T) { + testSetup(t) + pair := pair.NewCurrencyPair("BTC", "USD") eventID, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest) if err != nil && eventID != 0 { @@ -50,6 +72,8 @@ func TestRemoveEvent(t *testing.T) { } func TestGetEventCounter(t *testing.T) { + testSetup(t) + pair := pair.NewCurrencyPair("BTC", "USD") one, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest) if err != nil { @@ -87,6 +111,8 @@ func TestGetEventCounter(t *testing.T) { } func TestExecuteAction(t *testing.T) { + testSetup(t) + pair := pair.NewCurrencyPair("BTC", "USD") one, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest) if err != nil { @@ -127,11 +153,12 @@ func TestExecuteAction(t *testing.T) { if !RemoveEvent(one) { t.Error("Test Failed. ExecuteAction: Error, error removing event") } - // More tests when ExecuteAction is expanded } func TestEventToString(t *testing.T) { + testSetup(t) + pair := pair.NewCurrencyPair("BTC", "USD") one, err := AddEvent("ANX", "price", ">,==", pair, "SPOT", actionTest) if err != nil { @@ -149,6 +176,8 @@ func TestEventToString(t *testing.T) { } func TestCheckCondition(t *testing.T) { + testSetup(t) + // Test invalid currency pair newPair := pair.NewCurrencyPair("A", "B") one, err := AddEvent("ANX", "price", ">=,10", newPair, "SPOT", actionTest) @@ -219,6 +248,8 @@ func TestCheckCondition(t *testing.T) { } func TestIsValidEvent(t *testing.T) { + testSetup(t) + err := IsValidEvent("ANX", "price", ">,==", actionTest) if err != nil { t.Errorf("Test Failed. IsValidEvent: %s", err) @@ -253,6 +284,8 @@ func TestIsValidEvent(t *testing.T) { } func TestCheckEvents(t *testing.T) { + testSetup(t) + pair := pair.NewCurrencyPair("BTC", "USD") _, err := AddEvent("ANX", "price", ">=,10", pair, "SPOT", actionTest) if err != nil { @@ -263,17 +296,21 @@ func TestCheckEvents(t *testing.T) { } func TestIsValidExchange(t *testing.T) { - boolean := IsValidExchange("ANX", configPathTest) + testSetup(t) + + boolean := IsValidExchange("ANX") if !boolean { t.Error("Test Failed. IsValidExchange: Error, incorrect Exchange") } - boolean = IsValidExchange("OBTUSE", configPathTest) + boolean = IsValidExchange("OBTUSE") if boolean { t.Error("Test Failed. IsValidExchange: Error, incorrect return") } } func TestIsValidCondition(t *testing.T) { + testSetup(t) + boolean := IsValidCondition(">") if !boolean { t.Error("Test Failed. IsValidCondition: Error, incorrect Condition") @@ -301,6 +338,8 @@ func TestIsValidCondition(t *testing.T) { } func TestIsValidAction(t *testing.T) { + testSetup(t) + boolean := IsValidAction("sms") if !boolean { t.Error("Test Failed. IsValidAction: Error, incorrect Action") @@ -316,6 +355,8 @@ func TestIsValidAction(t *testing.T) { } func TestIsValidItem(t *testing.T) { + testSetup(t) + boolean := IsValidItem("price") if !boolean { t.Error("Test Failed. IsValidItem: Error, incorrect Item") diff --git a/events/events.go b/events/events.go index e14a11c3..e6b1a57f 100644 --- a/events/events.go +++ b/events/events.go @@ -23,7 +23,6 @@ const ( actionSMSNotify = "SMS" actionConsolePrint = "CONSOLE_PRINT" actionTest = "ACTION_TEST" - configPathTest = config.ConfigTestFile ) var ( @@ -107,12 +106,12 @@ func (e *Event) ExecuteAction() bool { action := common.SplitStrings(e.Action, ",") if action[0] == actionSMSNotify { message := fmt.Sprintf("Event triggered: %s", e.String()) + s := smsglobal.SMSGlobal if action[1] == "ALL" { - smsglobal.SMSSendToAll(message, config.Cfg) + s.SendMessageToAll(message) } else { - smsglobal.SMSNotify(smsglobal.SMSGetNumberByName(action[1], - config.Cfg.SMS), message, config.Cfg, - ) + contact, _ := s.GetContactByName(action[1]) + s.SendMessage(contact.Number, message) } } } else { @@ -188,12 +187,7 @@ func IsValidEvent(Exchange, Item, Condition, Action string) error { Item = common.StringToUpper(Item) Action = common.StringToUpper(Action) - configPath := "" - if Action == actionTest { - configPath = configPathTest - } - - if !IsValidExchange(Exchange, configPath) { + if !IsValidExchange(Exchange) { return errExchangeDisabled } @@ -218,10 +212,12 @@ func IsValidEvent(Exchange, Item, Condition, Action string) error { return errInvalidAction } - cfg := config.GetConfig() - if action[1] != "ALL" && smsglobal.SMSGetNumberByName( - action[1], cfg.SMS) == smsglobal.ErrSMSContactNotFound { - return errInvalidAction + if action[1] != "ALL" { + s := smsglobal.SMSGlobal + _, err := s.GetContactByName(action[1]) + if err != nil { + return errInvalidAction + } } } else { if Action != actionConsolePrint && Action != actionTest { @@ -254,14 +250,9 @@ func CheckEvents() { } // IsValidExchange validates the exchange -func IsValidExchange(Exchange, configPath string) bool { +func IsValidExchange(Exchange string) bool { Exchange = common.StringToUpper(Exchange) - cfg := config.GetConfig() - if len(cfg.Exchanges) == 0 { - cfg.LoadConfig(configPath) - } - for _, x := range cfg.Exchanges { if x.Name == Exchange && x.Enabled { return true diff --git a/main.go b/main.go index 67b86f96..967b20af 100644 --- a/main.go +++ b/main.go @@ -64,6 +64,7 @@ type ExchangeMain struct { // overarching type across this code base. type Bot struct { config *config.Config + smsglobal *smsglobal.Base portfolio *portfolio.Base exchange ExchangeMain exchanges []exchange.IBotExchange @@ -115,14 +116,16 @@ func main() { log.Fatal(err) } + AdjustGoMaxProcs() log.Printf("Bot '%s' started.\n", bot.config.Name) log.Printf("Fiat display currency: %s.", bot.config.FiatDisplayCurrency) - AdjustGoMaxProcs() if bot.config.SMS.Enabled { + bot.smsglobal = smsglobal.New(bot.config.SMS.Username, bot.config.SMS.Password, + bot.config.Name, bot.config.SMS.Contacts) log.Printf( "SMS support enabled. Number of SMS contacts %d.\n", - smsglobal.GetEnabledSMSContacts(bot.config.SMS), + bot.smsglobal.GetEnabledContacts(), ) } else { log.Println("SMS support disabled.") diff --git a/smsglobal/smsglobal.go b/smsglobal/smsglobal.go index 55fa03ca..d5cc5aab 100644 --- a/smsglobal/smsglobal.go +++ b/smsglobal/smsglobal.go @@ -3,66 +3,150 @@ package smsglobal import ( "errors" "flag" - "log" "net/url" "strings" "github.com/thrasher-/gocryptotrader/common" - "github.com/thrasher-/gocryptotrader/config" ) const ( - smsGlobalAPIURL = "http://www.smsglobal.com/http-api.php" + smsGlobalAPIURL = "https://www.smsglobal.com/http-api.php" // ErrSMSContactNotFound is a general error code for "SMS Contact not found." ErrSMSContactNotFound = "SMS Contact not found." errSMSNotSent = "SMS message not sent." ) -// GetEnabledSMSContacts returns how many SMS contacts are enabled in the -// contacts list. -func GetEnabledSMSContacts(smsCfg config.SMSGlobalConfig) int { +// vars for the SMS global package +var ( + SMSGlobal *Base +) + +// Contact struct stores information related to a SMSGlobal contact +type Contact struct { + Name string `json:"name"` + Number string `json:"number"` + Enabled bool `json:"enabled"` +} + +// Base struct stores information related to the SMSGlobal package +type Base struct { + Contacts []Contact `json:"contacts"` + Username string `json:"username"` + Password string `json:"password"` + SendFrom string `json:"send_from"` +} + +// New initalises the SMSGlobal var +func New(username, password, sendFrom string, contacts []Contact) *Base { + if username == "" || password == "" || sendFrom == "" || len(contacts) == 0 { + return nil + } + + var goodContacts []Contact + for x := range contacts { + if contacts[x].Name != "" || contacts[x].Number != "" { + goodContacts = append(goodContacts, contacts[x]) + } + } + + SMSGlobal = &Base{ + Contacts: goodContacts, + Username: username, + Password: password, + SendFrom: sendFrom, + } + return SMSGlobal +} + +// GetEnabledContacts returns how many SMS contacts are enabled in the +// contact list +func (s *Base) GetEnabledContacts() int { counter := 0 - for _, contact := range smsCfg.Contacts { - if contact.Enabled { + for x := range s.Contacts { + if s.Contacts[x].Enabled { counter++ } } return counter } -// SMSSendToAll sends a message to all enabled contacts in cfg -func SMSSendToAll(message string, cfg config.Config) { - for _, contact := range cfg.SMS.Contacts { - if contact.Enabled && len(contact.Number) == 10 { - err := SMSNotify(contact.Number, message, cfg) - if err != nil { - log.Printf("Unable to send SMS to %s.\n", contact.Name) - } +// GetContactByNumber returns a contact with supplied number +func (s *Base) GetContactByNumber(number string) (Contact, error) { + for x := range s.Contacts { + if s.Contacts[x].Number == number { + return s.Contacts[x], nil + } + } + return Contact{}, errors.New(ErrSMSContactNotFound) +} + +// GetContactByName returns a contact with supplied name +func (s *Base) GetContactByName(name string) (Contact, error) { + for x := range s.Contacts { + if common.StringToLower(s.Contacts[x].Name) == common.StringToLower(name) { + return s.Contacts[x], nil + } + } + return Contact{}, errors.New(ErrSMSContactNotFound) +} + +// AddContact checks to see if a contact exists and adds them if it doesn't +func (s *Base) AddContact(contact Contact) { + if contact.Name == "" || contact.Number == "" { + return + } + + if s.ContactExists(contact) { + return + } + + s.Contacts = append(s.Contacts, contact) +} + +// ContactExists checks to see if a contact exists +func (s *Base) ContactExists(contact Contact) bool { + for x := range s.Contacts { + if s.Contacts[x].Number == contact.Number && common.StringToLower(s.Contacts[x].Name) == common.StringToLower(contact.Name) { + return true + } + } + return false +} + +// RemoveContact removes a contact if it exists +func (s *Base) RemoveContact(contact Contact) { + if !s.ContactExists(contact) { + return + } + + 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 } } } -// SMSGetNumberByName returns contact number by supplied name -func SMSGetNumberByName(name string, smsCfg config.SMSGlobalConfig) string { - for _, contact := range smsCfg.Contacts { - if common.StringToUpper(contact.Name) == common.StringToUpper(name) { - return contact.Number +// SendMessageToAll sends a message to all enabled contacts in cfg +func (s *Base) SendMessageToAll(message string) { + for x := range s.Contacts { + if s.Contacts[x].Enabled { + s.SendMessage(s.Contacts[x].Name, message) } } - return ErrSMSContactNotFound } -// SMSNotify sends a message to an individual contact -func SMSNotify(to, message string, cfg config.Config) error { +// SendMessage sends a message to an individual contact +func (s *Base) SendMessage(to, message string) error { if flag.Lookup("test.v") != nil { return nil } values := url.Values{} values.Set("action", "sendsms") - values.Set("user", cfg.SMS.Username) - values.Set("password", cfg.SMS.Password) - values.Set("from", cfg.Name) + values.Set("user", s.Username) + values.Set("password", s.Password) + values.Set("from", s.SendFrom) values.Set("to", to) values.Set("text", message) diff --git a/smsglobal/smsglobal_test.go b/smsglobal/smsglobal_test.go index 4cd57a17..8fcd287f 100644 --- a/smsglobal/smsglobal_test.go +++ b/smsglobal/smsglobal_test.go @@ -1,71 +1,138 @@ package smsglobal import ( + "log" "testing" - - "github.com/thrasher-/gocryptotrader/config" ) -func TestGetEnabledSMSContacts(t *testing.T) { - cfg := config.GetConfig() - err := cfg.LoadConfig(config.ConfigTestFile) - if err != nil { - t.Errorf( - "Test Failed. GetEnabledSMSContacts: Function return is incorrect with, %s.", - err, - ) +func TestNew(t *testing.T) { + result := New("", "", "", nil) + if result != nil { + t.Error("Test failed. New: Expected nil result") } - numberOfContacts := GetEnabledSMSContacts(cfg.SMS) - if numberOfContacts != len(cfg.SMS.Contacts) { - t.Errorf( - "Test Failed. GetEnabledSMSContacts: Function return is incorrect with, %d.", - numberOfContacts, - ) + + contact := Contact{Name: "bob", Number: "1234", Enabled: true} + var contacts []Contact + contacts = append(contacts, contact) + + result = New("bob", "pw", "Skynet", contacts) + if !result.ContactExists(contact) { + t.Error("Test failed. New: Expected contact not found") } } -func TestSMSSendToAll(t *testing.T) { - cfg := config.GetConfig() - err := cfg.LoadConfig(config.ConfigTestFile) - if err != nil { - t.Errorf( - "Test Failed. SMSSendToAll: \nFunction return is incorrect with, %s.", - err, - ) - } - SMSSendToAll("SMSGLOBAL Test - SMSSENDTOALL", *cfg) -} +func TestGetEnabledContacts(t *testing.T) { + contact := Contact{Name: "bob", Number: "1234", Enabled: true} + var contacts []Contact + contacts = append(contacts, contact) + result := New("bob", "pw", "Skynet", contacts) -func TestSMSGetNumberByName(t *testing.T) { - cfg := config.GetConfig() - err := cfg.LoadConfig(config.ConfigTestFile) - if err != nil { - t.Errorf( - "Test Failed. SMSGetNumberByName: Function return is incorrect with, %s.", - err, - ) - } - number := SMSGetNumberByName("StyleGherkin", cfg.SMS) - if number == "" { - t.Error("Test Failed. SMSNotify Error: No number, name not found.") - } - number = SMSGetNumberByName("testy", cfg.SMS) - if number == "" { - t.Error("Test Failed. SMSNotify Error: No number, name not found.") + expected := 1 + actual := result.GetEnabledContacts() + if expected != actual { + t.Errorf("Test failed. TestGetEnabledContacts expected %d, got %d", + expected, actual) } } -func TestSMSNotify(t *testing.T) { - cfg := config.GetConfig() - err := cfg.LoadConfig(config.ConfigTestFile) +func TestGetContactByNumber(t *testing.T) { + contact := Contact{Name: "bob", Number: "1234", Enabled: true} + var contacts []Contact + contacts = append(contacts, contact) + result := New("bob", "pw", "Skynet", contacts) + + actual, err := result.GetContactByNumber(contact.Number) if err != nil { - t.Errorf( - "Test Failed. SMSNotify: \nFunction return is incorrect with, %s.", - err, - ) + t.Fatalf("Test failed. TestGetContactByNumber: %s", err) + } + + if actual.Name != contact.Name && actual.Number != contact.Number && actual.Enabled != contact.Enabled { + t.Fatal("Test failed. TestGetContactByNumber: Incorrect values") + } + + _, err = result.GetContactByNumber("ASDASDASD") + if err == nil { + t.Fatal("Test failed. TestGetContactByNumber: Returned nil err on non-existant number") } - // err2 := SMSNotify("+61312112718", "teststring", *cfg) - // if err2 != nil { - // t.Error("Test Failed. SMSNotify: \nError: ", err2) - // } +} + +func TestGetContactByName(t *testing.T) { + contact := Contact{Name: "bob", Number: "1234", Enabled: true} + var contacts []Contact + contacts = append(contacts, contact) + result := New("bob", "pw", "Skynet", contacts) + + actual, err := result.GetContactByName(contact.Name) + if err != nil { + t.Fatalf("Test failed. TestGetContactByName: %s", err) + } + + if actual.Name != contact.Name && actual.Number != contact.Number && actual.Enabled != contact.Enabled { + t.Fatal("Test failed. TestGetContactByName: Incorrect values") + } + + _, err = result.GetContactByName("ASDASDASD") + if err == nil { + t.Fatal("Test failed. TestGetContactByName: Returned nil err on non-existant number") + } +} + +func TestAddContact(t *testing.T) { + contact := Contact{Name: "bob", Number: "1234", Enabled: true} + var contacts []Contact + contacts = append(contacts, contact) + result := New("bob", "pw", "Skynet", contacts) + + // Test adding same contact + result.AddContact(contact) + if result.GetEnabledContacts() > 1 { + t.Fatal("Test failed. TestAddContact: Incorrect values") + } + + invalidContact := Contact{Name: "", Number: "", Enabled: true} + result.AddContact(invalidContact) + if result.GetEnabledContacts() > 1 { + t.Fatal("Test failed. TestAddContact: Incorrect values") + } + + newContact := Contact{Name: "newContact", Number: "12345", Enabled: true} + result.AddContact(newContact) + if result.GetEnabledContacts() != 2 { + t.Fatal("Test failed. TestAddContact: Incorrect values") + } +} + +func TestRemoveContact(t *testing.T) { + contact := Contact{Name: "bob", Number: "1234", Enabled: true} + var contacts []Contact + contacts = append(contacts, contact) + result := New("bob", "pw", "Skynet", contacts) + + result.RemoveContact(Contact{Name: "blah", Number: "1234"}) + if result.GetEnabledContacts() != 1 { + t.Fatal("Test failed. TestRemoveContact: Incorrect values") + } + + result.RemoveContact(contact) + if result.GetEnabledContacts() != 0 { + t.Fatal("Test failed. TestRemoveContact: Incorrect values") + } +} + +func TestSendMessageToAll(t *testing.T) { + contact := Contact{Name: "bob", Number: "1234", Enabled: true} + var contacts []Contact + contacts = append(contacts, contact) + result := New("bob", "pw", "Skynet", contacts) + result.SendMessageToAll("hello world") +} + +func TestSendMessage(t *testing.T) { + contact := Contact{Name: "bob", Number: "1234", Enabled: true} + var contacts []Contact + contacts = append(contacts, contact) + result := New("bob", "pw", "Skynet", contacts) + err := result.SendMessage(contact.Number, "hello world") + log.Println(err) + t.Log(err) }