initial commit 2
This commit is contained in:
363
server/router/twilioroute/controller.go
Normal file
363
server/router/twilioroute/controller.go
Normal file
@@ -0,0 +1,363 @@
|
||||
package twilioroute
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
|
||||
"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
|
||||
"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
|
||||
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
|
||||
"bitbucket.org/nemt/nemt-portal-api/infra/config"
|
||||
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/ttacon/libphonenumber"
|
||||
)
|
||||
|
||||
var (
|
||||
instance *controller
|
||||
once sync.Once
|
||||
upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type controller struct {
|
||||
cfg *config.Config
|
||||
svc *applicationservice.Service
|
||||
tnc *tncservice.Service
|
||||
notification *notificationservice.Service
|
||||
broadcast chan viewmodel.SocketMessage
|
||||
clients map[*websocket.Conn]viewmodel.SocketMessage
|
||||
}
|
||||
|
||||
func controllerInstance(cfg *config.Config, svc *applicationservice.Service, tnc *tncservice.Service, notification *notificationservice.Service) *controller {
|
||||
once.Do(func() {
|
||||
instance = &controller{
|
||||
cfg: cfg,
|
||||
svc: svc,
|
||||
tnc: tnc,
|
||||
notification: notification,
|
||||
broadcast: make(chan viewmodel.SocketMessage),
|
||||
clients: make(map[*websocket.Conn]viewmodel.SocketMessage),
|
||||
}
|
||||
go instance.handleMessages()
|
||||
})
|
||||
return instance
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Message *Message `xml:"Message,omitempty"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
To string `xml:"to,attr"`
|
||||
Body string `xml:"Body,omitempty"`
|
||||
}
|
||||
|
||||
func (c *controller) getDriverNotification(ride viewmodel.Ride, notificationType string, subject string, message string, to string, from string) viewmodel.Notification {
|
||||
retVal := viewmodel.Notification{
|
||||
Type: notificationType,
|
||||
Message: message,
|
||||
Subject: subject,
|
||||
Ride: ride,
|
||||
User: ride.User,
|
||||
CreatedUser: ride.CreatedUser,
|
||||
To: to,
|
||||
From: from,
|
||||
}
|
||||
|
||||
return retVal
|
||||
}
|
||||
|
||||
func (c *controller) handleTwilio(ctx echo.Context) error {
|
||||
var twilioResponse viewmodel.TwilioWebhook
|
||||
err := ctx.Bind(&twilioResponse)
|
||||
if err != nil {
|
||||
fmt.Println("Error binding response: ", err.Error())
|
||||
return routeutils.HandleAPIError(ctx, err)
|
||||
}
|
||||
|
||||
// bResp, _ := json.MarshalIndent(twilioResponse, "", "\t")
|
||||
// fmt.Println("Twilio Request")
|
||||
// fmt.Println(string(bResp))
|
||||
|
||||
var resp Response
|
||||
requestMessage := strings.ToUpper(strings.TrimSpace(twilioResponse.Body))
|
||||
if requestMessage != "CANCEL RIDE" && requestMessage != "YES" && requestMessage != "NO" && !strings.HasPrefix(requestMessage, "DRIVER") && !strings.HasPrefix(requestMessage, "MEMBER") && requestMessage != "I AM READY" {
|
||||
lastRide, err := c.svc.Rides.GetLastRideByPhoneNumber(twilioResponse.From, "", "")
|
||||
if err != nil {
|
||||
fmt.Println("Error to get the ride: ", err.Error())
|
||||
}
|
||||
|
||||
notifications := make([]viewmodel.Notification, 0)
|
||||
notifications = append(notifications, c.svc.Notification.GetNotification(lastRide, "sms", "Message to Dispatcher", twilioResponse.Body, true, "message"))
|
||||
notifications = append(notifications, c.svc.Notification.GetNotification(lastRide, "app", "Message to Dispatcher", twilioResponse.Body, true, "message"))
|
||||
notifications, err = c.svc.Notification.SendNotifications(notifications)
|
||||
if err != nil {
|
||||
fmt.Println("Error to notify: ", err.Error())
|
||||
}
|
||||
} else {
|
||||
toNumber := twilioResponse.From
|
||||
|
||||
var message string
|
||||
var lastRide viewmodel.Ride
|
||||
isDriver := false
|
||||
|
||||
num, _ := libphonenumber.Parse(twilioResponse.From, "US")
|
||||
if strings.HasPrefix(requestMessage, "MEMBER") {
|
||||
isDriver = true
|
||||
lastRide, err = c.svc.Rides.GetLastRideByDriversNumber(twilioResponse.From)
|
||||
if err != nil {
|
||||
fmt.Println("Error to get last driver ride: ", err.Error())
|
||||
message = fmt.Sprintf("We received a request to cancel a ride from you at %s, but cannot find a ride for this mobile number.", libphonenumber.Format(num, libphonenumber.NATIONAL))
|
||||
}
|
||||
} else {
|
||||
lastRide, err = c.svc.Rides.GetLastRideByPhoneNumber(twilioResponse.From, "", "")
|
||||
if err != nil {
|
||||
fmt.Println("Error to get last ride: ", err.Error())
|
||||
message = fmt.Sprintf("We received a request to cancel a ride from you at %s, but cannot find a ride for this mobile number.", libphonenumber.Format(num, libphonenumber.NATIONAL))
|
||||
}
|
||||
}
|
||||
if !isDriver {
|
||||
if requestMessage == "I AM READY" && lastRide.UUID != "" {
|
||||
if (lastRide.Visit.TripType.Key == "roundtrip_call" && (lastRide.TripType.Key == "to_visit" || lastRide.TripType.Key == "from_visit_call")) || lastRide.Visit.TripType.Key == "from_visit_call" {
|
||||
var readyRide viewmodel.Ride
|
||||
authUser, err := c.svc.Users.GetByUUID(lastRide.CreatedUser.ID, "")
|
||||
if err != nil {
|
||||
fmt.Println("Error to get created user: ", err.Error())
|
||||
}
|
||||
|
||||
if lastRide.TripType.Key == "to_visit" {
|
||||
readyRide, err = c.svc.Rides.GetByVisitUUIDAndTripType(lastRide.Visit.UUID, "from_visit_call", authUser)
|
||||
if err != nil || readyRide.UUID == "" {
|
||||
message = fmt.Sprintf("We received a request to send the return ride from you at %s, but you do not have the a return ride prepared.", libphonenumber.Format(num, libphonenumber.NATIONAL))
|
||||
}
|
||||
} else {
|
||||
readyRide = lastRide
|
||||
}
|
||||
|
||||
switch readyRide.Status.Key {
|
||||
case "pending", "accepted", "scheduled":
|
||||
message = "We already request your return ride, you should receive updates soon."
|
||||
case "willCall":
|
||||
var lyftRide viewmodel.RideRequest
|
||||
lyftRide.RideID = ""
|
||||
lyftRide.Destination = readyRide.Route.Destination
|
||||
lyftRide.Origin = readyRide.Route.Origin
|
||||
name := readyRide.User.Name
|
||||
names := strings.Split(name, " ")
|
||||
lyftRide.Passenger.FirstName = names[0]
|
||||
lyftRide.Passenger.LastName = " "
|
||||
lyftRide.Passenger.PhoneNumber = *readyRide.User.PhoneNumber
|
||||
lyftRide.RideType = "lyft"
|
||||
|
||||
if c.cfg.LyftProd.UserUUID != readyRide.CreatedUser.ID {
|
||||
lyftRide, err = c.tnc.Lyft.RequestRide(lyftRide)
|
||||
} else {
|
||||
fmt.Println("In Production")
|
||||
lyftRide, err = c.tnc.LyftProd.RequestRide(lyftRide)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println("Error to create a lyft ride on twilio: ", err.Error())
|
||||
message = "There was a problem to call your ride"
|
||||
}
|
||||
|
||||
readyRide, err = c.svc.Rides.UpdateNewRide(readyRide, lyftRide, authUser)
|
||||
if err != nil {
|
||||
fmt.Println("Error to update ride: ", err.Error())
|
||||
message = "There was a problem to call your ride"
|
||||
}
|
||||
|
||||
go func() {
|
||||
err = c.svc.Notification.SendNotification(readyRide.Status.Key, readyRide, lyftRide)
|
||||
if err != nil {
|
||||
fmt.Println("Error to notify user: ", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
message = fmt.Sprintf("Ride requested at %s, for pickup at %s.", lyftRide.RequestAt.Format("03:04 PM"), readyRide.Route.Destination.Name)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if requestMessage == "CANCEL RIDE" && lastRide.UUID != "" {
|
||||
var visitDate time.Time
|
||||
if lastRide.VisitTime != nil {
|
||||
visitDate = *lastRide.VisitTime
|
||||
} else {
|
||||
visitDate = *lastRide.VisitDate
|
||||
}
|
||||
|
||||
var pickupDate time.Time
|
||||
if lastRide.PickupTime != nil {
|
||||
pickupDate = *lastRide.PickupTime
|
||||
}
|
||||
|
||||
message = fmt.Sprintf("Are you sure you want to cancel a ride to %s visit to %s scheduled for %s?\nReply YES to confirm this cancellation", visitDate.Format("01/02/2006 03:04 PM"), lastRide.Route.Destination.Name, pickupDate.Format("03:04 PM"))
|
||||
}
|
||||
|
||||
if requestMessage == "YES" && lastRide.UUID != "" {
|
||||
var lyftRide viewmodel.RideRequest
|
||||
if lastRide.CreatedUser.ID != c.cfg.LyftProd.UserUUID {
|
||||
lyftRide, err = c.tnc.Lyft.GetRideDetails(viewmodel.RideRequest{RideID: lastRide.InternalID})
|
||||
if err != nil {
|
||||
fmt.Println("Error: ", err.Error())
|
||||
message = "There was a problem to find your ride"
|
||||
}
|
||||
|
||||
if err = c.tnc.Lyft.CancelRide(lyftRide); err != nil {
|
||||
fmt.Println("Error to cancel with Lyft: ", err.Error())
|
||||
message = "There was a problem to cancel your ride with our transportation provider"
|
||||
}
|
||||
} else {
|
||||
fmt.Println("In Production")
|
||||
lyftRide, err = c.tnc.LyftProd.GetRideDetails(viewmodel.RideRequest{RideID: lastRide.InternalID})
|
||||
if err != nil {
|
||||
fmt.Println("Error: ", err.Error())
|
||||
message = "There was a problem to find your ride"
|
||||
}
|
||||
|
||||
if err = c.tnc.LyftProd.CancelRide(lyftRide); err != nil {
|
||||
fmt.Println("Error to cancel with Lyft: ", err.Error())
|
||||
message = "There was a problem to cancel your ride with our transportation provider"
|
||||
}
|
||||
}
|
||||
|
||||
if err = c.svc.Rides.UpdateStatus(lastRide.UUID, "canceled"); err != nil {
|
||||
fmt.Println("Error to cancel the ride: ", err.Error())
|
||||
message = "There was a problem to cancel your ride on our systems"
|
||||
}
|
||||
|
||||
var dateToFormat time.Time
|
||||
if lastRide.VisitTime != nil {
|
||||
dateToFormat = *lastRide.VisitTime
|
||||
} else {
|
||||
dateToFormat = *lastRide.VisitDate
|
||||
}
|
||||
|
||||
var pickupDate time.Time
|
||||
if lastRide.PickupTime != nil {
|
||||
pickupDate = *lastRide.PickupTime
|
||||
}
|
||||
lastRide.Status.Key = "cancelled"
|
||||
message = fmt.Sprintf("A ride to a %s visit to %s scheduled for %s has been cancelled.", dateToFormat.Format("01/02/2006 03:04 PM"), lastRide.Route.Destination.Name, pickupDate.Format("03:04 PM"))
|
||||
}
|
||||
|
||||
// if requestMessage == "NO" && lastRide.UUID != "" {
|
||||
// var dateToFormat time.Time
|
||||
// if lastRide.VisitTime != nil {
|
||||
// dateToFormat = *lastRide.VisitTime
|
||||
// } else {
|
||||
// dateToFormat = *lastRide.VisitDate
|
||||
// }
|
||||
// message = fmt.Sprintf("You ride is still scheduled on %s at %s", lastRide.Route.Destination.Name, dateToFormat.Format("01/02/2006 03:04 PM"))
|
||||
// }
|
||||
|
||||
if strings.HasPrefix(requestMessage, "DRIVER") && lastRide.UUID != "" {
|
||||
driverMessage := strings.Replace(requestMessage, "DRIVER", "", -1)
|
||||
driverMessage = strings.TrimSpace(driverMessage) + "\nReply MEMBER and your message to send a message to the member."
|
||||
|
||||
notifications := make([]viewmodel.Notification, 0)
|
||||
notifications = append(notifications, c.getDriverNotification(lastRide, "sms", "Message to the driver", driverMessage, lastRide.Driver.PhoneNumber, twilioResponse.To))
|
||||
notifications = append(notifications, c.getDriverNotification(lastRide, "sms", "Message to the driver", requestMessage, *lastRide.CreatedUser.PhoneNumber, *lastRide.User.PhoneNumber))
|
||||
notifications = append(notifications, c.getDriverNotification(lastRide, "email", "Message to the driver", driverMessage, *lastRide.CreatedUser.Email, *lastRide.User.Email))
|
||||
notifications, err := c.svc.Notification.SendNotifications(notifications)
|
||||
if err != nil {
|
||||
message = "There was a problem to send a message to the driver."
|
||||
fmt.Println("Error to send drivers notification: ", err.Error())
|
||||
}
|
||||
|
||||
message = "Your message was sent to the driver via SMS."
|
||||
}
|
||||
} else {
|
||||
driverMessage := strings.Replace(requestMessage, "MEMBER", "", -1)
|
||||
driverMessage = strings.TrimSpace(driverMessage) + "\nReply DRIVER and your message to send a message to the driver."
|
||||
|
||||
userPhoneNumber := ""
|
||||
createUserPhoneNumber := ""
|
||||
userEmail := ""
|
||||
createdUserEmail := ""
|
||||
if lastRide.User.PhoneNumber != nil {
|
||||
userPhoneNumber = *lastRide.User.PhoneNumber
|
||||
}
|
||||
|
||||
if lastRide.CreatedUser.PhoneNumber != nil {
|
||||
createUserPhoneNumber = *lastRide.CreatedUser.PhoneNumber
|
||||
}
|
||||
|
||||
if lastRide.User.Email != nil {
|
||||
userEmail = *lastRide.User.Email
|
||||
}
|
||||
|
||||
if lastRide.CreatedUser.Email != nil {
|
||||
createdUserEmail = *lastRide.CreatedUser.Email
|
||||
}
|
||||
|
||||
notifications := make([]viewmodel.Notification, 0)
|
||||
notifications = append(notifications, c.getDriverNotification(lastRide, "sms", "Message to the member from the driver", driverMessage, userPhoneNumber, twilioResponse.To))
|
||||
notifications = append(notifications, c.getDriverNotification(lastRide, "sms", "Message to the member from the driver", requestMessage, createUserPhoneNumber, userPhoneNumber))
|
||||
notifications = append(notifications, c.getDriverNotification(lastRide, "email", "Message to the member from the driver", driverMessage, createdUserEmail, userEmail))
|
||||
notifications, err := c.svc.Notification.SendNotifications(notifications)
|
||||
if err != nil {
|
||||
message = "There was a problem to send a message to the member."
|
||||
fmt.Println("Error to send member notification: ", err.Error())
|
||||
}
|
||||
|
||||
message = "Your message was sent to the member via SMS."
|
||||
}
|
||||
resp.Message = &Message{
|
||||
To: toNumber,
|
||||
Body: message,
|
||||
}
|
||||
}
|
||||
return ctx.XML(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (c *controller) handleMessages() {
|
||||
for {
|
||||
msg := <-c.broadcast
|
||||
for client := range c.clients {
|
||||
if c.clients[client].From == msg.To {
|
||||
err := client.WriteJSON(msg)
|
||||
if err != nil {
|
||||
log.Printf("error: %v", err)
|
||||
client.Close()
|
||||
delete(c.clients, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) handleSocket(ctx echo.Context) error {
|
||||
ws, err := upgrader.Upgrade(ctx.Response(), ctx.Request(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ws.Close()
|
||||
|
||||
c.clients[ws] = viewmodel.SocketMessage{Filled: false}
|
||||
for {
|
||||
var msg viewmodel.SocketMessage
|
||||
err := ws.ReadJSON(&msg)
|
||||
if err != nil {
|
||||
log.Printf("error: %v", err)
|
||||
delete(c.clients, ws)
|
||||
break
|
||||
}
|
||||
msg.Filled = true
|
||||
c.clients[ws] = msg
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user