diff --git a/twitter.go b/twitter.go index e8dfc62..14f346d 100644 --- a/twitter.go +++ b/twitter.go @@ -5,6 +5,7 @@ import ( "regexp" "strings" "time" + "unicode/utf8" "github.com/dghubble/go-twitter/twitter" "github.com/dghubble/oauth1" @@ -12,6 +13,9 @@ import ( "gorm.io/gorm" ) +// maximum number of characters a tweet can support +const tweetMaxSize = 280 + // Tweet to store relationship between a Product and a Twitter notification type Tweet struct { gorm.Model @@ -107,11 +111,8 @@ func formatPrice(value float64, currency string) string { // NotifyWhenAvailable create a Twitter status for announcing that a product is available // implements the Notifier interface func (c *TwitterNotifier) NotifyWhenAvailable(shopName string, productName string, productPrice float64, productCurrency string, productURL string) error { - // format message - formattedPrice := formatPrice(productPrice, productCurrency) hashtags := c.buildHashtags(productName) - message := fmt.Sprintf("%s: %s for %s is available at %s %s", shopName, productName, formattedPrice, productURL, hashtags) - + message := formatAvailableTweet(shopName, productName, productPrice, productCurrency, productURL, hashtags) // create thread tweetID, err := c.createTweet(message) if err != nil { @@ -129,6 +130,22 @@ func (c *TwitterNotifier) NotifyWhenAvailable(shopName string, productName strin return nil } +func formatAvailableTweet(shopName string, productName string, productPrice float64, productCurrency string, productURL string, hashtags string) string { + // format message + formattedPrice := formatPrice(productPrice, productCurrency) + message := fmt.Sprintf("%s: %s for %s is available at %s %s", shopName, productName, formattedPrice, productURL, hashtags) + + // truncate tweet if too big + if utf8.RuneCountInString(message) > tweetMaxSize { + // maximum tweet size - other characters - additional "…" to say product name has been truncated + productNameSize := tweetMaxSize - utf8.RuneCountInString(fmt.Sprintf("%s: for %s is available at %s %s", shopName, formattedPrice, productURL, hashtags)) - 1 + format := fmt.Sprintf("%%s: %%.%ds… for %%s is available at %%s %%s", productNameSize) + message = fmt.Sprintf(format, shopName, productName, formattedPrice, productURL, hashtags) + } + + return message +} + // NotifyWhenNotAvailable create a Twitter status replying to the NotifyWhenAvailable status to say it's over // implements the Notifier interface func (c *TwitterNotifier) NotifyWhenNotAvailable(productURL string, duration time.Duration) error { diff --git a/twitter_test.go b/twitter_test.go index db3af41..a2a985b 100644 --- a/twitter_test.go +++ b/twitter_test.go @@ -79,3 +79,46 @@ func TestFormatPrice(t *testing.T) { }) } } + +/* +func formatAvailableTweet(shopName string, productName string, productPrice float64, productCurrency string, productURL string, hashtags string) string { + // format message + formattedPrice := formatPrice(productPrice, productCurrency) + message := fmt.Sprintf("%s: %s for %s is available at %s %s", shopName, productName, formattedPrice, productURL, hashtags) + + // truncate tweet if too big + if utf8.RuneCountInString(message) > tweetMaxSize { + // maximum tweet size - other characters - additional "…" to say product name has been truncated + productNameSize := tweetMaxSize - utf8.RuneCountInString(fmt.Sprintf("%s: for %s is available at %s %s", shopName, formattedPrice, productURL, hashtags)) - 1 + format := fmt.Sprintf("%%s: %%.%ds… for %%s is available at %%s %%s", productNameSize) + message = fmt.Sprintf(format, shopName, productName, formattedPrice, productURL, hashtags) + } + + return message +} +*/ + +func TestFormatAvailableTweet(t *testing.T) { + tests := []struct { + shopName string + productName string + productPrice float64 + productCurrency string + productURL string + hashtags string + expected string + }{ + {"shop.com", "my awesome product", 999.99, "USD", "https://shop.com/awesome", "#awesome #product", "shop.com: my awesome product for $999.99 is available at https://shop.com/awesome #awesome #product"}, + {"shop.com", "my awesome product with very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long name", 999.99, "USD", "https://shop.com/awesome", "#awesome #product", "shop.com: my awesome product with very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very… for $999.99 is available at https://shop.com/awesome #awesome #product"}, + } + for i, tc := range tests { + t.Run(fmt.Sprintf("TestFormatAvailableTweet#%d", i), func(t *testing.T) { + got := formatAvailableTweet(tc.shopName, tc.productName, tc.productPrice, tc.productCurrency, tc.productURL, tc.hashtags) + if got != tc.expected { + t.Errorf("for %s, got '%s', want '%s'", tc.productName, got, tc.expected) + } else { + t.Logf("for %s, got '%s', want '%s'", tc.productName, got, tc.expected) + } + }) + } +}