Add ability to delay text message sending

This commit is contained in:
Eli Ribble 2026-01-26 16:10:30 +00:00
parent ab105e16e8
commit adc99e8871
No known key found for this signature in database
24 changed files with 2028 additions and 28 deletions

View file

@ -10,14 +10,21 @@ import (
"strings"
"time"
"github.com/Gleipnir-Technology/nidus-sync/config"
"github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/db/enums"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
"github.com/aarondl/opt/omit"
"github.com/nyaruka/phonenumbers"
"github.com/rs/zerolog/log"
"github.com/stephenafamo/bob/types/pgtypes"
)
func StoreSources() error {
ctx := context.TODO()
src := phonenumbers.Format(&config.PhoneNumberReport, phonenumbers.E164)
return ensureInDB(ctx, src)
}
func convertToPGData(data map[string]string) pgtypes.HStore {
result := pgtypes.HStore{}
for k, v := range data {
@ -26,6 +33,21 @@ func convertToPGData(data map[string]string) pgtypes.HStore {
return result
}
func delayMessage(ctx context.Context, source string, destination string, content string, type_ enums.CommsTextjobtype) error {
job, err := models.CommsTextJobs.Insert(&models.CommsTextJobSetter{
Content: omit.From(content),
Created: omit.From(time.Now()),
Destination: omit.From(destination),
//ID:
Type: omit.From(type_),
}).One(ctx, db.PGInstance.BobDB)
if err != nil {
return fmt.Errorf("Failed to add delayed text job: %w", err)
}
log.Info().Int32("id", job.ID).Msg("Created delayed text job")
return nil
}
func ensureInDB(ctx context.Context, destination string) (err error) {
_, err = models.FindCommsPhone(ctx, db.PGInstance.BobDB, destination)
if err != nil {
@ -58,6 +80,17 @@ func insertTextLog(ctx context.Context, content string, destination string, sour
return err
}
func isSubscribed(ctx context.Context, destination string) (bool, error) {
phone, err := models.FindCommsPhone(ctx, db.PGInstance.BobDB, destination)
if err != nil {
if err.Error() == "sql: no rows in result set" {
return false, nil
}
return false, fmt.Errorf("Failed to find phone number %s: %w", destination, err)
}
return phone.IsSubscribed, nil
}
func generatePublicId(t enums.CommsMessagetypeemail, m map[string]string) string {
if m == nil || len(m) == 0 {
// Return hash of empty string for empty maps

31
comms/text/initial.go Normal file
View file

@ -0,0 +1,31 @@
package text
import (
"context"
"fmt"
"github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/db/enums"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
)
func ensureInitialText(ctx context.Context, src string, dst string) error {
//
origin := enums.CommsTextoriginWebsiteAction
rows, err := models.CommsTextLogs.Query(
models.SelectWhere.CommsTextLogs.Destination.EQ(dst),
models.SelectWhere.CommsTextLogs.IsWelcome.EQ(true),
).All(ctx, db.PGInstance.BobDB)
if err != nil {
return fmt.Errorf("Failed to query text logs: %w", err)
}
if len(rows) > 0 {
return nil
}
content := "Welcome to Report Mosquitoes Online. We received your request and want to confirm text updates. Reply YES to continue. Reply STOP at any time to unsubscribe"
err = sendText(ctx, src, dst, content, origin)
if err != nil {
return fmt.Errorf("Failed to send initial confirmation: %w", err)
}
return nil
}

View file

@ -48,9 +48,24 @@ func sendReportSubscription(ctx context.Context, job Job) error {
return fmt.Errorf("job is not for report subscription confirmation")
}
err := sendText(ctx, j.src, j.dst, j.content(), enums.CommsTextoriginWebsiteAction)
sub, err := isSubscribed(ctx, job.destination())
if err != nil {
return fmt.Errorf("Failed to send report subscription confirmation: %w", err)
return fmt.Errorf("Failed to check if subscribed: %w", err)
}
if !sub {
err = sendText(ctx, j.source(), j.destination(), j.content(), enums.CommsTextoriginWebsiteAction)
if err != nil {
return fmt.Errorf("Failed to send report subscription confirmation: %w", err)
}
} else {
err = delayMessage(ctx, j.source(), j.destination(), j.content(), enums.CommsTextjobtypeReportConfirmation)
if err != nil {
return fmt.Errorf("Failed to delay report subscription message: %w", err)
}
err := ensureInitialText(ctx, j.source(), j.destination())
if err != nil {
return fmt.Errorf("Failed to ensure initial text has been sent: %w", err)
}
}
return nil
}

View file

@ -19,14 +19,12 @@ func ParsePhoneNumber(input string) (*E164, error) {
return phonenumbers.Parse(input, "US")
}
func sendText(ctx context.Context, source E164, destination E164, message string, origin enums.CommsTextorigin) error {
src := phonenumbers.Format(&source, phonenumbers.E164)
dest := phonenumbers.Format(&destination, phonenumbers.E164)
err := ensureInDB(ctx, dest)
func sendText(ctx context.Context, source string, destination string, message string, origin enums.CommsTextorigin) error {
err := ensureInDB(ctx, destination)
if err != nil {
return fmt.Errorf("Failed to ensure text message destination is in the DB: %w", err)
}
err = insertTextLog(ctx, message, dest, src, origin)
err = insertTextLog(ctx, message, destination, source, origin)
if err != nil {
return fmt.Errorf("Failed to insert text message in the DB: %w", err)
}
@ -36,16 +34,16 @@ func sendText(ctx context.Context, source E164, destination E164, message string
params.SetMessagingServiceSid(config.TwilioMessagingServiceSID)
params.SetBody(message)
params.SetTo(dest)
params.SetTo(destination)
resp, err := client.Api.CreateMessage(params)
if err != nil {
return fmt.Errorf("Failed to create message to %s: %w", dest, err)
return fmt.Errorf("Failed to create message to %s: %w", destination, err)
} else {
if resp.Body != nil {
log.Info().Str("dest", dest).Str("body", *resp.Body).Msg("Text message response")
log.Info().Str("dest", destination).Str("body", *resp.Body).Msg("Text message response")
} else {
log.Info().Str("dest", dest).Msg("Text message response is nil")
log.Info().Str("dest", destination).Msg("Text message response is nil")
}
}
return nil