Get Voip.ms working again in the text system
Because we need it for the conference.
This commit is contained in:
parent
a900c23090
commit
d2d5f003d8
8 changed files with 221 additions and 57 deletions
|
|
@ -39,7 +39,7 @@ func apiAudioPost(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|||
return
|
||||
}
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
debugSaveRequest(body, err, "Audio note POST JSON decode error")
|
||||
//debugSaveRequest(body, err, "Audio note POST JSON decode error")
|
||||
http.Error(w, "Failed to decode the payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
|
@ -136,7 +136,7 @@ func apiImagePost(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|||
return
|
||||
}
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
debugSaveRequest(body, err, "Image note POST JSON decode error")
|
||||
//debugSaveRequest(body, err, "Image note POST JSON decode error")
|
||||
http.Error(w, "Failed to decode the payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
21
api/debug.go
21
api/debug.go
|
|
@ -1,22 +1,25 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func debugSaveRequest(body []byte, err error, message string) {
|
||||
// TODO(eliribble): avoid using a single static filename and instead securely generate
|
||||
// this value
|
||||
func debugSaveRequest(r *http.Request) {
|
||||
tmpFile, err := os.CreateTemp("/tmp", "request-*.data")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg(message)
|
||||
log.Error().Err(err).Msg("failed to create temp file for debugSaveRequest")
|
||||
return
|
||||
}
|
||||
output, err := os.OpenFile("/tmp/request.body", os.O_RDWR|os.O_CREATE, 0666)
|
||||
defer tmpFile.Close()
|
||||
|
||||
_, err = io.Copy(tmpFile, r.Body)
|
||||
if err != nil {
|
||||
log.Info().Msg("Failed to open temp request.bady")
|
||||
log.Error().Err(err).Msg("failed to copy request body in debugSaveRequest")
|
||||
return
|
||||
}
|
||||
defer output.Close()
|
||||
output.Write(body)
|
||||
log.Info().Msg("Wrote request to /tmp/request.body")
|
||||
log.Info().Str("filename", tmpFile.Name()).Msg("Saved request body")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ func AddRoutes(r chi.Router) {
|
|||
r.Post("/twilio/message", twilioMessagePost)
|
||||
r.Post("/twilio/text", twilioTextPost)
|
||||
r.Post("/twilio/text/status", twilioTextStatusPost)
|
||||
r.Get("/voipms/text", voipmsTextGet)
|
||||
r.Post("/voipms/text", voipmsTextPost)
|
||||
r.Get("/webhook/fieldseeker", webhookFieldseeker)
|
||||
r.Post("/webhook/fieldseeker", webhookFieldseeker)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,51 +2,17 @@ package text
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/config"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/twilio/twilio-go"
|
||||
twilioApi "github.com/twilio/twilio-go/rest/api/v2010"
|
||||
)
|
||||
|
||||
func SendText(ctx context.Context, source string, destination string, message string) (string, error) {
|
||||
client := twilio.NewRestClient()
|
||||
|
||||
params := &twilioApi.CreateMessageParams{}
|
||||
params.SetMessagingServiceSid(config.TwilioMessagingServiceSID)
|
||||
|
||||
params.SetBody(message)
|
||||
params.SetTo(destination)
|
||||
resp, err := client.Api.CreateMessage(params)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to create message to %s: %w", destination, err)
|
||||
switch config.TextProvider {
|
||||
case "voipms":
|
||||
return sendTextVoipms(ctx, destination, message)
|
||||
case "twilio":
|
||||
return sendTextTwilio(ctx, source, destination, message)
|
||||
}
|
||||
if resp.Sid == nil {
|
||||
log.Warn().Str("src", source).Str("dst", destination).Msg("Text message sid is nil")
|
||||
return "", nil
|
||||
}
|
||||
log.Info().Str("src", source).Str("dst", destination).Str("message", message).Str("sid", *resp.Sid).Msg("Created text message")
|
||||
return *resp.Sid, nil
|
||||
}
|
||||
|
||||
func sendSMS(destination, source, message string) error {
|
||||
client := twilio.NewRestClientWithParams(twilio.ClientParams{
|
||||
Username: config.TwilioAccountSID,
|
||||
Password: config.TwilioAuthToken,
|
||||
})
|
||||
params := &twilioApi.CreateMessageParams{}
|
||||
params.SetTo("+15558675309")
|
||||
params.SetFrom("+15017250604")
|
||||
params.SetBody("Hello from Go!")
|
||||
|
||||
resp, err := client.Api.CreateMessage(params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error sending SMS message: %w", err)
|
||||
}
|
||||
response, _ := json.Marshal(*resp)
|
||||
log.Debug().Str("response", string(response)).Msg("Send SMS")
|
||||
return nil
|
||||
return "", fmt.Errorf("Unsupported provider '%s'", config.TextProvider)
|
||||
}
|
||||
|
|
|
|||
52
comms/text/twilio.go
Normal file
52
comms/text/twilio.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package text
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/config"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/twilio/twilio-go"
|
||||
twilioApi "github.com/twilio/twilio-go/rest/api/v2010"
|
||||
)
|
||||
|
||||
func sendTextTwilio(ctx context.Context, source string, destination string, message string) (string, error) {
|
||||
client := twilio.NewRestClient()
|
||||
|
||||
params := &twilioApi.CreateMessageParams{}
|
||||
params.SetMessagingServiceSid(config.TwilioMessagingServiceSID)
|
||||
|
||||
params.SetBody(message)
|
||||
params.SetTo(destination)
|
||||
resp, err := client.Api.CreateMessage(params)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to create message to %s: %w", destination, err)
|
||||
}
|
||||
if resp.Sid == nil {
|
||||
log.Warn().Str("src", source).Str("dst", destination).Msg("Text message sid is nil")
|
||||
return "", nil
|
||||
}
|
||||
log.Info().Str("src", source).Str("dst", destination).Str("message", message).Str("sid", *resp.Sid).Msg("Created text message")
|
||||
return *resp.Sid, nil
|
||||
}
|
||||
|
||||
func sendSMSTwilio(destination, source, message string) error {
|
||||
client := twilio.NewRestClientWithParams(twilio.ClientParams{
|
||||
Username: config.TwilioAccountSID,
|
||||
Password: config.TwilioAuthToken,
|
||||
})
|
||||
params := &twilioApi.CreateMessageParams{}
|
||||
params.SetTo("+15558675309")
|
||||
params.SetFrom("+15017250604")
|
||||
params.SetBody("Hello from Go!")
|
||||
|
||||
resp, err := client.Api.CreateMessage(params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error sending SMS message: %w", err)
|
||||
}
|
||||
response, _ := json.Marshal(*resp)
|
||||
log.Debug().Str("response", string(response)).Msg("Send SMS")
|
||||
return nil
|
||||
}
|
||||
103
comms/text/voipms.go
Normal file
103
comms/text/voipms.go
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package text
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/config"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var VOIP_MS_API = "https://voip.ms/api/v1/rest.php"
|
||||
|
||||
type VoipMSResponse struct {
|
||||
Message string `json:"message"`
|
||||
Status string `json:"status"`
|
||||
SMS int `json:"sms"`
|
||||
}
|
||||
|
||||
func sendTextVoipms(ctx context.Context, to string, content string, media ...string) (string, error) {
|
||||
if len(content) > 2048 {
|
||||
return "", errors.New("Message content is more than 160 characters")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Add("api_password", config.VoipMSPassword)
|
||||
params.Add("api_username", config.VoipMSUsername)
|
||||
params.Add("method", "sendMMS")
|
||||
params.Add("did", config.VoipMSNumber)
|
||||
params.Add("dst", to)
|
||||
params.Add("message", content)
|
||||
/*
|
||||
for i, med := range media {
|
||||
// These should be one of:
|
||||
// 1. A full URL that the service cat GET
|
||||
// 2. A base64-encoded image starting with "data:image/png;base64,iVBORw0KGgoAAAANSUh..."
|
||||
params.Add(fmt.Sprintf("media%d", i+1), med)
|
||||
}
|
||||
params.Add(fmt.Sprintf("media%d", len(media)+1), "")
|
||||
*/
|
||||
|
||||
response, err := makeVoipMSRequest(params)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to send MMS: %w", err)
|
||||
}
|
||||
log.Info().Str("status", response.Status).Int("sms", response.SMS).Msg("Sent MMS message")
|
||||
return strconv.Itoa(response.SMS), nil
|
||||
}
|
||||
|
||||
func sendSMSVoipms(to string, content string) (string, error) {
|
||||
if len(content) > 160 {
|
||||
return "", errors.New("Message content is more than 160 characters")
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Add("api_password", config.VoipMSPassword)
|
||||
params.Add("api_username", config.VoipMSUsername)
|
||||
params.Add("method", "sendSMS")
|
||||
params.Add("did", config.VoipMSNumber)
|
||||
params.Add("dst", to)
|
||||
params.Add("message", content)
|
||||
|
||||
response, err := makeVoipMSRequest(params)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to send SMS: %w", err)
|
||||
}
|
||||
log.Info().Str("status", response.Status).Int("sms", response.SMS).Msg("Sent MMS message")
|
||||
return strconv.Itoa(response.SMS), nil
|
||||
}
|
||||
|
||||
func makeVoipMSRequest(params url.Values) (VoipMSResponse, error) {
|
||||
result := VoipMSResponse{}
|
||||
// Construct the URL with query parameters
|
||||
full_url := VOIP_MS_API + "?" + params.Encode()
|
||||
|
||||
// Make the HTTP request
|
||||
log.Debug().Str("full_url", full_url).Msg("Sending command to VoIP.ms")
|
||||
resp, err := http.Get(full_url)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("url", full_url).Msg("Failed to make request to Voip.MS")
|
||||
return result, fmt.Errorf("Error making request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Read the response body
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("url", full_url).Msg("Failed to read Voip.MS response body")
|
||||
return result, fmt.Errorf("Failed to read response: %w", err)
|
||||
}
|
||||
log.Info().Str("response", string(body)).Msg("Response from Voip.MS")
|
||||
|
||||
// Parse the JSON response
|
||||
var response VoipMSResponse
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("Failed to unmarshal JSON response: %w", err)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
|
@ -31,10 +31,14 @@ var (
|
|||
PhoneNumberSupport phonenumbers.PhoneNumber
|
||||
PhoneNumberSupportStr string
|
||||
SentryDSN string
|
||||
TextProvider string
|
||||
TwilioAuthToken string
|
||||
TwilioAccountSID string
|
||||
TwilioMessagingServiceSID string
|
||||
TwilioRCSSenderRMO string
|
||||
VoipMSNumber string
|
||||
VoipMSPassword string
|
||||
VoipMSUsername string
|
||||
)
|
||||
|
||||
func IsProductionEnvironment() bool {
|
||||
|
|
@ -156,6 +160,16 @@ func Parse() (err error) {
|
|||
if SentryDSN == "" {
|
||||
return fmt.Errorf("You must specify a non-empty SENTRY_DSN")
|
||||
}
|
||||
TextProvider = os.Getenv("TEXT_PROVIDER")
|
||||
switch TextProvider {
|
||||
case "":
|
||||
return fmt.Errorf("You must specify a non-empty TEXT_PROVIDER")
|
||||
case "twilio":
|
||||
case "voipms":
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("Unrecognized text provider '%s'", TextProvider)
|
||||
}
|
||||
TwilioAccountSID = os.Getenv("TWILIO_ACCOUNT_SID")
|
||||
if TwilioAccountSID == "" {
|
||||
return fmt.Errorf("You must specify a non-empty TWILIO_ACCOUNT_SID")
|
||||
|
|
@ -172,6 +186,18 @@ func Parse() (err error) {
|
|||
if TwilioRCSSenderRMO == "" {
|
||||
return fmt.Errorf("You must specify a non-empty TWILIO_RCS_SENDER_RMO")
|
||||
}
|
||||
VoipMSNumber = os.Getenv("VOIPMS_NUMBER")
|
||||
if VoipMSNumber == "" {
|
||||
return fmt.Errorf("You must specify a non-empty VOIPMS_NUMBER")
|
||||
}
|
||||
VoipMSPassword = os.Getenv("VOIPMS_PASSWORD")
|
||||
if VoipMSPassword == "" {
|
||||
return fmt.Errorf("You must specify a non-empty VOIPMS_PASSWORD")
|
||||
}
|
||||
VoipMSUsername = os.Getenv("VOIPMS_USERNAME")
|
||||
if VoipMSPassword == "" {
|
||||
return fmt.Errorf("You must specify a non-empty VOIPMS_USERNAME")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,6 @@ func HandleTextMessage(from string, to string, body string) {
|
|||
// We don't know if they're subscribed or not.
|
||||
if subscribed == nil {
|
||||
switch body_l {
|
||||
case "stop":
|
||||
setSubscribed(ctx, src, false)
|
||||
case "yes":
|
||||
setSubscribed(ctx, src, true)
|
||||
handleWaitingTextJobs(ctx, src)
|
||||
|
|
@ -60,10 +58,19 @@ func HandleTextMessage(from string, to string, body string) {
|
|||
}
|
||||
return
|
||||
}
|
||||
// If we get the super-special "reset conversation" then wipe the LLM's memory
|
||||
if body_l == "reset conversation" {
|
||||
switch body_l {
|
||||
case "stop":
|
||||
content := "You have successfully been unsubscribed. You will not receive any more messages from this number. Reply START to resubscribe."
|
||||
err = sendText(ctx, dst, src, content, enums.CommsTextoriginCommandResponse, false, false)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to send unsubscribe acknowledgement.")
|
||||
}
|
||||
setSubscribed(ctx, src, false)
|
||||
return
|
||||
case "reset conversation":
|
||||
handleResetConversation(ctx, src, dst)
|
||||
return
|
||||
default:
|
||||
}
|
||||
previous_messages, err := loadPreviousMessagesForLLM(ctx, dst, src)
|
||||
if err != nil {
|
||||
|
|
@ -90,8 +97,13 @@ func ParsePhoneNumber(input string) (*E164, error) {
|
|||
|
||||
func StoreSources() error {
|
||||
ctx := context.TODO()
|
||||
src := phonenumbers.Format(&config.PhoneNumberReport, phonenumbers.E164)
|
||||
return ensureInDB(ctx, src)
|
||||
for _, n := range []string{config.PhoneNumberReportStr, config.PhoneNumberSupportStr, config.VoipMSNumber} {
|
||||
err := ensureInDB(ctx, n)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to add number '%s' to DB: %w", n, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateMessageStatus(twilio_sid string, status string) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue