192 lines
7 KiB
Go
192 lines
7 KiB
Go
package text
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
"source.gleipnir.technology/Gleipnir/nidus-sync/comms/text"
|
|
"source.gleipnir.technology/Gleipnir/nidus-sync/config"
|
|
"source.gleipnir.technology/Gleipnir/nidus-sync/db"
|
|
"source.gleipnir.technology/Gleipnir/nidus-sync/db/enums"
|
|
modelcomms "source.gleipnir.technology/Gleipnir/nidus-sync/db/gen/nidus-sync/comms/model"
|
|
modelpublic "source.gleipnir.technology/Gleipnir/nidus-sync/db/gen/nidus-sync/public/model"
|
|
modelpublicreport "source.gleipnir.technology/Gleipnir/nidus-sync/db/gen/nidus-sync/publicreport/model"
|
|
querycomms "source.gleipnir.technology/Gleipnir/nidus-sync/db/query/comms"
|
|
querypublic "source.gleipnir.technology/Gleipnir/nidus-sync/db/query/public"
|
|
querypublicreport "source.gleipnir.technology/Gleipnir/nidus-sync/db/query/publicreport"
|
|
"source.gleipnir.technology/Gleipnir/nidus-sync/lint"
|
|
"source.gleipnir.technology/Gleipnir/nidus-sync/platform/background"
|
|
"source.gleipnir.technology/Gleipnir/nidus-sync/platform/event"
|
|
"source.gleipnir.technology/Gleipnir/nidus-sync/platform/types"
|
|
)
|
|
|
|
func ensureInitialText(ctx context.Context, txn db.Ex, dst types.E164) error {
|
|
logs, err := querycomms.TextLogWelcomeFromDestination(ctx, txn, dst.PhoneString())
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to query text logs: %w", err)
|
|
}
|
|
if len(logs) > 0 {
|
|
return nil
|
|
}
|
|
return sendInitialText(ctx, txn, dst)
|
|
}
|
|
func resendInitialText(ctx context.Context, txn db.Ex, dst types.E164) error {
|
|
phone, err := querycomms.ContactPhoneFromE164(ctx, txn, dst.PhoneString())
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to find phone %s: %w", dst, err)
|
|
}
|
|
err = querycomms.ContactPhoneUpdateStopMessageID(ctx, txn, phone.E164, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to clear subscription on phone %s: %w", dst, err)
|
|
}
|
|
return nil
|
|
}
|
|
func sendInitialText(ctx context.Context, txn db.Ex, 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, modelcomms.Textorigin_WebsiteAction, 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 db.Ex, user_id *int32, report_id *int32, destination types.E164, content string, type_ modelcomms.Textjobtype) (*int32, error) {
|
|
job, err := querycomms.TextJobInsert(ctx, txn, modelcomms.TextJob{
|
|
Content: content,
|
|
CreatorID: user_id,
|
|
Created: time.Now(),
|
|
Destination: destination.PhoneString(),
|
|
//ID:
|
|
ReportID: report_id,
|
|
Source: modelcomms.Textjobsource_Rmo,
|
|
Type: type_,
|
|
})
|
|
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 db.Ex, dst types.E164, content string) error {
|
|
_, err := sendTextDirect(ctx, txn, modelcomms.Textorigin_CommandResponse, dst.PhoneString(), content, false, false)
|
|
return err
|
|
}
|
|
func sendTextComplete(ctx context.Context, job modelcomms.TextJob) error {
|
|
txn, err := db.BeginTxn(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("begin tx: %w", err)
|
|
}
|
|
defer lint.LogOnErrRollback(txn.Rollback, ctx, "rollback")
|
|
dst, err := ParsePhoneNumber(job.Destination)
|
|
if err != nil {
|
|
return fmt.Errorf("parse phone: %w", err)
|
|
}
|
|
var origin modelcomms.Textorigin
|
|
switch job.Type {
|
|
case modelcomms.Textjobtype_ReportConfirmation:
|
|
origin = modelcomms.Textorigin_WebsiteAction
|
|
case modelcomms.Textjobtype_ReportMessage:
|
|
origin = modelcomms.Textorigin_District
|
|
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:
|
|
lint.LogOnErrCtx(func(ctx context.Context) error {
|
|
return resendInitialText(ctx, txn, *dst)
|
|
}, ctx, "resend initial text")
|
|
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 = querycomms.TextJobComplete(ctx, txn, int64(job.ID))
|
|
if err != nil {
|
|
return fmt.Errorf("update job: %w", err)
|
|
}
|
|
if job.ReportID != nil {
|
|
creator_id := *job.CreatorID
|
|
report_id := *job.ReportID
|
|
log.Debug().Int32("creator", creator_id).Int32("report_id", report_id).Msg("Creating report entries for text message")
|
|
querypublic.ReportTextInsert(ctx, txn, modelpublic.ReportText{
|
|
CreatorID: creator_id,
|
|
ReportID: report_id,
|
|
TextLogID: text_log.ID,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("insert report_text: %w", err)
|
|
}
|
|
_, err = querypublicreport.ReportLogInsert(ctx, txn, modelpublicreport.ReportLog{
|
|
Created: time.Now(),
|
|
EmailLogID: nil,
|
|
// ID
|
|
ReportID: report_id,
|
|
TextLogID: &text_log.ID,
|
|
Type: modelpublicreport.Reportlogtype_MessageText,
|
|
UserID: &creator_id,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("insert report log: %w", err)
|
|
}
|
|
report, err := querypublicreport.ReportFromID(ctx, txn, int64(report_id))
|
|
if err != nil {
|
|
return fmt.Errorf("find public report: %w", err)
|
|
}
|
|
event.Updated(event.TypeRMOPublicReport, report.OrganizationID, report.PublicID)
|
|
} else {
|
|
log.Debug().Msg("no report info on text")
|
|
}
|
|
if err := txn.Commit(ctx); err != nil {
|
|
return fmt.Errorf("commit: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Send a text message and save the appropriate database records.
|
|
// Send immediately using the current goroutine
|
|
func sendTextDirect(ctx context.Context, txn db.Ex, origin modelcomms.Textorigin, destination, content string, is_visible_to_llm, is_welcome bool) (modelcomms.TextLog, error) {
|
|
text_log, err := querycomms.TextLogInsert(ctx, txn, modelcomms.TextLog{
|
|
Content: content,
|
|
Created: time.Now(),
|
|
Destination: destination,
|
|
IsVisibleToLlm: is_visible_to_llm,
|
|
IsWelcome: is_welcome,
|
|
Origin: origin,
|
|
Source: config.PhoneNumberReportStr,
|
|
TwilioSid: nil,
|
|
TwilioStatus: "",
|
|
})
|
|
if err != nil {
|
|
return modelcomms.TextLog{}, fmt.Errorf("insert text log: %w", err)
|
|
}
|
|
pid, err := text.SendText(ctx, config.VoipMSNumber, destination, content)
|
|
if err != nil {
|
|
return modelcomms.TextLog{}, fmt.Errorf("send text: %w", err)
|
|
}
|
|
err = querycomms.TextLogUpdate(ctx, txn, int64(text_log.ID), pid, "created")
|
|
if err != nil {
|
|
return modelcomms.TextLog{}, fmt.Errorf("update %w", err)
|
|
}
|
|
return text_log, nil
|
|
}
|