208 lines
7.7 KiB
Go
208 lines
7.7 KiB
Go
package text
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/Gleipnir-Technology/bob"
|
|
"github.com/Gleipnir-Technology/nidus-sync/comms/text"
|
|
"github.com/Gleipnir-Technology/nidus-sync/config"
|
|
"github.com/aarondl/opt/omit"
|
|
"github.com/aarondl/opt/omitnull"
|
|
//"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/Gleipnir-Technology/nidus-sync/platform/background"
|
|
"github.com/Gleipnir-Technology/nidus-sync/platform/event"
|
|
"github.com/Gleipnir-Technology/nidus-sync/platform/types"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
func ensureInitialText(ctx context.Context, txn bob.Executor, dst types.E164) error {
|
|
rows, err := models.CommsTextLogs.Query(
|
|
models.SelectWhere.CommsTextLogs.Destination.EQ(dst.PhoneString()),
|
|
models.SelectWhere.CommsTextLogs.IsWelcome.EQ(true),
|
|
).All(ctx, txn)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to query text logs: %w", err)
|
|
}
|
|
if len(rows) > 0 {
|
|
return nil
|
|
}
|
|
return sendInitialText(ctx, txn, dst)
|
|
}
|
|
func insertTextLog(ctx context.Context, txn bob.Executor, destination types.E164, source types.E164, origin enums.CommsTextorigin, content string, is_welcome bool, is_visible_to_llm bool) (l *models.CommsTextLog, err error) {
|
|
l, err = models.CommsTextLogs.Insert(&models.CommsTextLogSetter{
|
|
//ID:
|
|
Content: omit.From(content),
|
|
Created: omit.From(time.Now()),
|
|
Destination: omit.From(destination.PhoneString()),
|
|
IsVisibleToLLM: omit.From(is_visible_to_llm),
|
|
IsWelcome: omit.From(is_welcome),
|
|
Origin: omit.From(origin),
|
|
Source: omit.From(source.PhoneString()),
|
|
TwilioSid: omitnull.FromPtr[string](nil),
|
|
TwilioStatus: omit.From(""),
|
|
}).One(ctx, txn)
|
|
|
|
return l, err
|
|
}
|
|
func resendInitialText(ctx context.Context, txn bob.Executor, dst types.E164) error {
|
|
phone, err := models.FindCommsPhone(ctx, txn, dst.PhoneString())
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to find phone %s: %w", dst, err)
|
|
}
|
|
err = phone.Update(ctx, txn, &models.CommsPhoneSetter{
|
|
Status: omit.From(enums.CommsPhonestatustypeUnconfirmed),
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to clear subscription on phone %s: %w", dst, err)
|
|
}
|
|
return nil
|
|
}
|
|
func sendInitialText(ctx context.Context, txn bob.Executor, dst types.E164) error {
|
|
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 := sendTextDirect(ctx, txn, enums.CommsTextoriginWebsiteAction, dst.PhoneString(), content, false, true)
|
|
if err != nil {
|
|
return fmt.Errorf("send text: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Begin the process of sending the text message, but only get as far as adding it to
|
|
// the database, then let the backend finish sending.
|
|
func sendTextBegin(ctx context.Context, txn bob.Executor, user_id *int32, report_id *int32, destination types.E164, content string, type_ enums.CommsTextjobtype) (*int32, error) {
|
|
err := EnsureInDB(ctx, txn, destination)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to ensure text message destination is in the DB: %w", err)
|
|
}
|
|
job, err := models.CommsTextJobs.Insert(&models.CommsTextJobSetter{
|
|
Content: omit.From(content),
|
|
CreatorID: omitnull.FromPtr(user_id),
|
|
Created: omit.From(time.Now()),
|
|
Destination: omit.From(destination.PhoneString()),
|
|
//ID:
|
|
ReportID: omitnull.FromPtr(report_id),
|
|
Source: omit.From(enums.CommsTextjobsourceRmo),
|
|
Type: omit.From(type_),
|
|
}).One(ctx, txn)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to add delayed text job: %w", err)
|
|
}
|
|
err = background.NewTextSend(ctx, txn, job.ID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("new background job: %w", err)
|
|
}
|
|
return &job.ID, nil
|
|
}
|
|
func sendTextCommandResponse(ctx context.Context, txn bob.Executor, dst types.E164, content string) error {
|
|
_, err := sendTextDirect(ctx, txn, enums.CommsTextoriginCommandResponse, dst.PhoneString(), content, false, false)
|
|
return err
|
|
}
|
|
func sendTextComplete(ctx context.Context, txn bob.Executor, job *models.CommsTextJob) error {
|
|
dst, err := ParsePhoneNumber(job.Destination)
|
|
if err != nil {
|
|
return fmt.Errorf("parse phone: %w", err)
|
|
}
|
|
var origin enums.CommsTextorigin
|
|
switch job.Type {
|
|
case enums.CommsTextjobtypeReportConfirmation:
|
|
origin = enums.CommsTextoriginWebsiteAction
|
|
case enums.CommsTextjobtypeReportMessage:
|
|
origin = enums.CommsTextoriginDistrict
|
|
default:
|
|
return fmt.Errorf("incomplete switch: %s", string(job.Type))
|
|
}
|
|
status, err := phoneStatus(ctx, *dst)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to check if subscribed: %w", err)
|
|
}
|
|
log.Debug().Str("phone status", string(status)).Str("destination", job.Destination).Send()
|
|
switch status {
|
|
case enums.CommsPhonestatustypeUnconfirmed:
|
|
err := ensureInitialText(ctx, txn, *dst)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to ensure initial text has been sent: %w", err)
|
|
}
|
|
return nil
|
|
//case enums.CommsPhonestatustypeOkToSend:
|
|
// allow to drop through
|
|
case enums.CommsPhonestatustypeStopped:
|
|
resendInitialText(ctx, txn, *dst)
|
|
return nil
|
|
}
|
|
text_log, err := sendTextDirect(ctx, txn, origin, job.Destination, job.Content, true, false)
|
|
if err != nil {
|
|
return fmt.Errorf("send text direct: %w", err)
|
|
}
|
|
err = job.Update(ctx, txn, &models.CommsTextJobSetter{
|
|
Completed: omitnull.From(time.Now()),
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("update job: %w", err)
|
|
}
|
|
if job.ReportID.IsValue() {
|
|
creator_id := job.CreatorID.MustGet()
|
|
report_id := job.ReportID.MustGet()
|
|
log.Debug().Int32("creator", creator_id).Int32("report_id", report_id).Msg("Creating report entries for text message")
|
|
_, err := models.ReportTexts.Insert(&models.ReportTextSetter{
|
|
CreatorID: omit.From(creator_id),
|
|
ReportID: omit.From(report_id),
|
|
TextLogID: omit.From(text_log.ID),
|
|
}).One(ctx, txn)
|
|
if err != nil {
|
|
return fmt.Errorf("insert report_text: %w", err)
|
|
}
|
|
models.PublicreportReportLogs.Insert(&models.PublicreportReportLogSetter{
|
|
Created: omit.From(time.Now()),
|
|
EmailLogID: omitnull.FromPtr[int32](nil),
|
|
// ID
|
|
ReportID: omit.From(report_id),
|
|
TextLogID: omitnull.From(text_log.ID),
|
|
Type: omit.From(enums.PublicreportReportlogtypeMessageText),
|
|
UserID: omitnull.From(creator_id),
|
|
}).One(ctx, txn)
|
|
report, err := models.FindPublicreportReport(ctx, txn, report_id)
|
|
if err != nil {
|
|
return fmt.Errorf("find public report: %w", err)
|
|
}
|
|
event.Updated(event.TypeRMOReport, report.OrganizationID, report.PublicID)
|
|
} else {
|
|
log.Debug().Msg("no report info on text")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Send a text message and save the appropriate database records.
|
|
// Send immediately using the current goroutine
|
|
func sendTextDirect(ctx context.Context, txn bob.Executor, origin enums.CommsTextorigin, destination, content string, is_visible_to_llm, is_welcome bool) (*models.CommsTextLog, error) {
|
|
text_log, err := models.CommsTextLogs.Insert(&models.CommsTextLogSetter{
|
|
//ID:
|
|
Content: omit.From(content),
|
|
Created: omit.From(time.Now()),
|
|
Destination: omit.From(destination),
|
|
IsVisibleToLLM: omit.From(is_visible_to_llm),
|
|
IsWelcome: omit.From(is_welcome),
|
|
Origin: omit.From(origin),
|
|
Source: omit.From(config.PhoneNumberReportStr),
|
|
TwilioSid: omitnull.FromPtr[string](nil),
|
|
TwilioStatus: omit.From(""),
|
|
}).One(ctx, txn)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("insert text log: %w", err)
|
|
}
|
|
pid, err := text.SendText(ctx, config.VoipMSNumber, destination, content)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("send text: %w", err)
|
|
}
|
|
err = text_log.Update(ctx, txn, &models.CommsTextLogSetter{
|
|
TwilioSid: omitnull.From(pid),
|
|
TwilioStatus: omit.From("created"),
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("update %w", err)
|
|
}
|
|
|
|
return text_log, nil
|
|
}
|