/// <summary>
        /// Check call cannot be reached
        /// </summary>
        /// <param name="status"></param>
        /// <param name="isSpecialist"></param>
        /// <returns>TRUE: call fails - FAILD: call completed</returns>
        private bool CannotBeReached(string callSid, CallDto call, string status, bool notifyForCustomer)
        {
            var result = false;
            bool isTalkNow = true;
            if (call.Caller.Id.Equals(call.Booking.Specialist.Id))
            {
                isTalkNow = false;
            }

            // Case1: Call status == Canceled, Busy, Failed, NoAnswer > Call status of Twilio
            // Case2: CallerSid == null && Booking.Deferral == 0 && isTalkNow > TALKNOW > Specialist pick up but don't talk now or defer/re-schedule
            // Case3: call.CallerSid == null && !isTalkNow > CALL FROM BOOKING > Customer pick up but don't press key to talk with specialist
            if (status == CallStatus.Canceled || status == CallStatus.Busy
                || status == CallStatus.Failed || status == CallStatus.NoAnswer
                || (call.CallerSid == null && call.Booking.Deferral == 0 && isTalkNow)
                || (call.CallerSid == null && !isTalkNow)) // || call.ReceiverSid == null) // !Important
            {
                if (notifyForCustomer) // Notify for customer
                {
                    // Show popup notify unreached
                    CallHelper.ShowPopupNotifyInConference(call.Booking.Customer, ConferencePopupConst.ConsultantUnReachedTitle,
                        string.Format(ConferencePopupConst.UnReachedContent, call.Booking.Specialist.Name));

                    if (!isTalkNow)
                    {
                        // Terminate call of customer
                        Services.Call.Terminate(call.ReceiverSid);
                    }

                    // Update booking status
                    Services.Booking.UpdateBookingStatus(call.Booking.Id, BookingStatus.Finish);
                }
                else // Notify for specialist
                {
                    // Check call is talk now or call from book
                    // Make call from book
                    if (call.Receiver.Id.Equals(call.Booking.Customer.Id) && call.ReceiverSid == callSid)
                    {
                        // Update call fails
                        Services.Call.UpdateCallFails(call.Booking.Id);

                        if (call.Booking.CallFails < 2)
                        {
                            // Notify to customer
                            AddBookingEvent(call, true);

                            // Update booking status
                            Services.Booking.UpdateBookingStatus(call.Booking.Id, BookingStatus.Confirmed);
                        }

                        if (call.Booking.CallFails == 2)
                        {
                            // Show popup notify for specialist
                            CallHelper.ShowPopupNotifyInConference(call.Booking.Specialist,
                                ConferencePopupConst.CallFailsTitle,
                                string.Format(ConferencePopupConst.CallFailsContent, call.Booking.CallFails));

                            // Add to booking event
                            Services.Booking.CreateBookingEvent(new BookingEventDto
                            {
                                Booking = call.Booking,
                                SourceUser = call.Booking.Specialist,
                                TargetUser = call.Booking.Customer,
                                ShortDescription = AlertShortDescription.FailedAttemptsCancellation,
                                Description = AlertDescription.FailedAttemptsCancellation,
                                CreatedDate = DateTime.UtcNow,
                                ModifiedDate = DateTime.UtcNow,
                                IsRead = false,
                                EmailAlertSubject = EmailAlertSubject.FailedAttemptsCancellation,
                                EmailAlertDetail = EmailAlertDetail.FailedAttemptsCancellation,
                                SMSAlertDetail = SMSAlertDetail.FailedAttemptsCancellation
                            });

                            // Create minimum charge invoice when customer no answer three times
                            try
                            {
                                PaymentHelper.CreateTransaction(call.Booking, call.Booking.CustomerMinCharge, true, Services);

                                Services.Invoices.CreateConsultationInvoice(call.Booking.SpecialistMinCharge, call.Booking.Id, true, false, null);
                            }
                            catch (Exception e)
                            {
                                Log.Error("Create minimum charge invoice", e);
                                throw;
                            }

                            // Update booking status
                            Services.Booking.UpdateBookingStatus(call.Booking.Id, BookingStatus.Finish);
                        }
                    }
                    else // Talk now > Specialist is receiver
                    {
                        // Terminate call of specialist
                        Services.Call.Terminate(call.ReceiverSid);

                        // Update booking status
                        Services.Booking.UpdateBookingStatus(call.Booking.Id, BookingStatus.Finish);
                    }

                    // Show popup notify unreached
                    CallHelper.ShowPopupNotifyInConference(call.Booking.Specialist,
                        ConferencePopupConst.CustomerUnReachedTitle,
                        string.Format(ConferencePopupConst.UnReachedContent,
                        call.Booking.Customer.Name));
                }

                // Update call status
                Services.Call.UpdateCallStatus(callSid, status);

                result = true;
            }

            return result;
        }
예제 #2
0
        public void Update_CallDto()
        {
            // Setup dependence
            var settingMock = new Mock<ISettings>();
            var componentMock = new Mock<IComponents>();
            var repositoryMock = new Mock<IRepository>();
            var uowMock = new Mock<IUnitOfWork>();
            var serviceLocatorMock = new Mock<IServiceLocator>();

            serviceLocatorMock.Setup(r => r.GetInstance<IRepository>())
                .Returns(repositoryMock.Object);
            serviceLocatorMock.Setup(r => r.GetInstance<IUnitOfWork>())
                .Returns(uowMock.Object);
            ServiceLocator.SetLocatorProvider(() => serviceLocatorMock.Object);
            repositoryMock.Setup(r => r.CreateUnitOfWork()).Returns(uowMock.Object);
            // Arrange data
            Guid id = Guid.NewGuid();
            CallDto callDto = new CallDto { Id = id };
            // Act
            CallService callService = new CallService(uowMock.Object, repositoryMock.Object, settingMock.Object, componentMock.Object);
            var currentCall = callService.Update(callDto);

            // Assert
            repositoryMock.Verify(r => r.Update<Call>(
                It.Is<Call>(b => b.Id == id)));
            uowMock.Verify(u => u.Save());
        }
        /// <summary>
        /// Add booking event for customer
        /// </summary>
        /// <param name="call"></param>
        /// <returns></returns>
        private void AddBookingEvent(CallDto call, bool notifyForCustomer)
        {
            var sourceUser = call.Booking.Specialist;
            var targetUser = call.Booking.Customer;

            if (!notifyForCustomer)
            {
                sourceUser = call.Booking.Customer;
                targetUser = call.Booking.Specialist;
            }

            string shortDescription = call.Booking.CallFails == 0
                ? AlertShortDescription.FailedConferenceAttemptAtFirst
                : AlertShortDescription.FailedConferenceAttemptAtSecond;

            string description = call.Booking.CallFails == 0
                ? AlertDescription.FailedConferenceAttemptAtFirst
                : AlertDescription.FailedConferenceAttemptAtSecond;

            string emailAlertSubject = call.Booking.CallFails == 0
                ? EmailAlertSubject.FailedConferenceAttemptAtFirst
                : EmailAlertSubject.FailedConferenceAttemptAtSecond;

            string emailAlertDetail = call.Booking.CallFails == 0
                ? EmailAlertDetail.FailedConferenceAttemptAtFirst
                : EmailAlertDetail.FailedConferenceAttemptAtSecond;

            string smsAlertDetail = call.Booking.CallFails == 0
                ? SMSAlertDetail.FailedConferenceAttemptAtFirst
                : SMSAlertDetail.FailedConferenceAttemptAtSecond;

            // Add to booking event for customer
            Services.Booking.CreateBookingEvent(new BookingEventDto
            {
                Booking = call.Booking,
                SourceUser = sourceUser,
                TargetUser = targetUser,
                ShortDescription = shortDescription,
                Description = description,
                CreatedDate = DateTime.UtcNow,
                ModifiedDate = DateTime.UtcNow,
                IsRead = false,
                EmailAlertSubject = emailAlertSubject,
                EmailAlertDetail = emailAlertDetail,
                SMSAlertDetail = smsAlertDetail
            });

            // Reload alert and booking list
            NotifyHelper.ReloadAlert(call.Booking.Specialist.UserName);
            NotifyHelper.ReloadAlert(call.Booking.Customer.UserName);
        }
예제 #4
0
        /// <summary>
        /// Update price and twilio cost
        /// </summary>
        /// <param name="callId"></param>
        public void UpdateCallCost(CallDto call)
        {
            if (call.CallerSid != null && call.ReceiverSid != null)
            {
                // Twilio cost
                decimal callerCost = GetCallBySid(call.CallerSid).Price.HasValue ? GetCallBySid(call.CallerSid).Price.Value : 0;
                decimal receiverCost = GetCallBySid(call.ReceiverSid).Price.HasValue ? GetCallBySid(call.ReceiverSid).Price.Value : 0;
                call.TwilioCost = callerCost + receiverCost;

                Repository.Update<CallDomain>(call.ExposedAs<CallDomain>(Repository));
                UnitOfWork.Save();
            }
        }
        public TwiMLResult ConsultantAfterCallResponse(CallContextModel model, string digits)
        {
            var response = new TwilioResponse();
            var message = ConferenceConst.AcctionCompleted;
            var specialist = new UserDto();
            var call = new CallDto();

            bool canWaiveFee = true;
            int transcriptSuccess = 2;

            try
            {
                if (digits.Equals("2") || digits.Equals("3") || digits.Equals("4") || digits.Equals("5"))
                {
                    call = Services.Call.GetByCallSid(Request["CallSid"]);

                    if (string.IsNullOrWhiteSpace(model.RecordUrl))
                    {
                        model.RecordSid = call.RecordSid;
                        model.RecordDuration = call.RecordDuration;
                        model.RecordUrl = call.RecordUrl;
                    }

                    // Get specialist
                    if (model.IsCustomer) // Caller is customer
                    {
                        specialist = Services.Users.GetUserById(model.ReceiverId);
                    }
                    else
                    {
                        specialist = Services.Users.GetUserById(model.CallerId);
                    }

                    int startingTime = Convert.ToInt32(Services.SystemConfig.GetByKey(ParamatricBusinessRules.STARTING_TIME.ToString()).Value);
                    if (call.Duration <= startingTime)
                    {
                        canWaiveFee = false;
                    }
                }

                switch (digits)
                {
                    case "1": // Dictate your follow up action
                        var statusCallBack = Utilities.FormatLink(Url.Action("VoiceMail", "Conference", model));
                        Services.Call.RedirectToVoiceMail(Request["CallSid"], statusCallBack);

                        break;

                    case "2": // Transcript consultation
                        // Send transcription request
                        transcriptSuccess = SendTranscriptionRequest(specialist.Id.ToString(), specialist.UserName,
                                                                        specialist.Email, call.RecordUrl,
                                                                        call.RecordDuration, call.Booking);

                        if (transcriptSuccess == 0)
                        {
                            message = ConferenceConst.BalanceNotEnoughForTranscript;
                        }
                        else if (transcriptSuccess == 1)
                        {
                            message = ConferenceConst.TranscriptError;
                        }

                        break;

                    case "3": // Play consultation record
                        response.Pause(5);
                        response.Play(model.RecordUrl);

                        break;

                    case "4": //  Play consultation record and transcription
                        // Play consultation record
                        response.Pause(5);
                        response.Play(model.RecordUrl);

                        // Send transcription request
                        transcriptSuccess = SendTranscriptionRequest(specialist.Id.ToString(), specialist.UserName,
                                                                        specialist.Email, call.RecordUrl,
                                                                        call.RecordDuration, call.Booking);

                        if (transcriptSuccess == 0)
                        {
                            message = ConferenceConst.BalanceNotEnoughForTranscript;
                        }
                        else if (transcriptSuccess == 1)
                        {
                            message = ConferenceConst.TranscriptError;
                        }

                        break;

                    case "5": // Waive consultation fee
                        if (canWaiveFee)
                        {
                            response.Redirect(Url.Action("WaiveFeeAction", model));
                        }
                        else
                        {
                            response.Redirect(Url.Action("ConsultantAfterCall", model));
                        }

                        break;

                    default:
                        response.Redirect(Url.Action("ConsultantAfterCall", model));
                        return new TwiMLResult(response);
                }
            }
            catch (Exception e)
            {
                // Log
                Log.Error("Consultant after call response. Error: ", e);

                // Error
                response.Say(ConferenceConst.ErrorMessage, new { voice = VoiceInConference, language = LanguageInConference });
                response.Hangup();
            }

            response.Say(message, new { voice = VoiceInConference, language = LanguageInConference });
            response.Redirect(Url.Action("ConsultantAfterCall", model));
            return new TwiMLResult(response);
        }
예제 #6
0
 // ====================== .CREATE CALL ======================
 //// ====================== UPDATE CALL ======================
 /// <summary>
 /// Update a call
 /// </summary>
 /// <param name="call"></param>
 /// <returns></returns>
 public CallDto Update(CallDto call)
 {
     Repository.Update<CallDomain>(call.ExposedAs<CallDomain>(Repository));
     UnitOfWork.Save();
     return call;
 }
예제 #7
0
        // ============================ INTERACTING WITH TWILIO ============================
        /// <summary>
        /// Make call for consultantion
        /// </summary>
        /// <param name="booking"></param>
        /// <param name="twiMLUrl"></param>
        /// <param name="isTalkNow"></param>
        /// <returns></returns>
        public Call MakeCallForConsultantion(BookingDto booking, string twiMLUrl, bool isTalkNow)
        {
            try
            {
                string fullPhoneNo = booking.Specialist.MobileCountryCode + booking.Specialist.MobilePhone;

                if (!isTalkNow) // If call from confirm booking
                {
                    fullPhoneNo = booking.Customer.MobileCountryCode + booking.Customer.MobilePhone;
                }

                var call = Dial(twiMLUrl, fullPhoneNo);

                // Update into database
                // Default make call for talk now
                var callDto = new CallDto
                {
                    ReceiverSid = call.Sid,
                    Booking = booking,
                    ReceiverStatus = call.Status,
                    Caller = booking.Customer,
                    Receiver = booking.Specialist,
                    StartTime = DateTime.UtcNow,
                    EndTime = DateTime.UtcNow,
                    CreatedDate = DateTime.UtcNow,
                    ModifiedDate = DateTime.UtcNow
                };

                // If make call for booking
                if (!isTalkNow)
                {
                    callDto.Caller = booking.Specialist;
                    callDto.Receiver = booking.Customer;
                }

                // Insert to database
                Create(callDto);

                return call;
            }
            catch (Exception e)
            {
                // Log
                Log.Error("CallService_MakeCallForTalkNow_Error making call",
                   e);

                throw new Exception("Error making call. Error: " + e.Message);
            }
        }
예제 #8
0
        // ====================== .GET CALL ======================
        //// ====================== CREATE CALL ======================
        /// <summary>
        /// Create call
        /// </summary>
        /// <param name="call"></param>
        /// <returns></returns>
        public CallDto Create(CallDto call)
        {
            Repository.Insert<CallDomain>(call.ExposedAs<CallDomain>(Repository));
            UnitOfWork.Save();

            return call;
        }
예제 #9
0
        /// <summary>
        /// show popup follow upaction for customer
        /// </summary>
        /// <param name="callLog"></param>
        /// <param name="totalCost"></param>
        public static void ShowPopupFollowUpActionAfterCall(CallDto callDto, decimal totalCost, decimal minimumCost, string userName, bool IsFavourite, bool isSendForCustomer)
        {
            string urlDownload = string.Empty;
            string recordUrl = string.Empty;
            if (!string.IsNullOrWhiteSpace(callDto.RecordSid))
            {
                string urlTwilio = string.Format(Settings.UrlCallRecord, Settings.AccountSid);
                urlDownload = string.Format("{0}{1}", urlTwilio, callDto.RecordSid);

                var twilioServer = ConfigurationManager.AppSettings["Twilio_ApiServer"];
                // Check record url
                recordUrl = !string.IsNullOrWhiteSpace(callDto.RecordUrl) && callDto.RecordUrl.Contains(twilioServer)
                    ? Utilities.ConvertLinkRecord(callDto.RecordSid)
                    : S3ReaderHelper.CombineFileS3Root(callDto.RecordUrl);
            }
            TimeSpan duration = new TimeSpan(0, 0, callDto.Duration);

            var context = GlobalHost.ConnectionManager.GetHubContext<FollowUpActionHub>();

            UserDto sendUser = isSendForCustomer ? callDto.Booking.Specialist : callDto.Booking.Customer;
            foreach (var connectionId in FollowUpActionHub.connections.GetConnections(userName))
            {
                context.Clients.Client(connectionId).showPopupFollowUpAction(new
                {
                    BookingId = callDto.Booking.Id,
                    CallId = callDto.Id,
                    TitleGender = !string.IsNullOrWhiteSpace(sendUser.Title) ? sendUser.Title : string.Empty,
                    FirstName = sendUser.FirstName,
                    LastName = sendUser.LastName,
                    Name = sendUser.Name,
                    Avatar = S3ReaderHelper.CombineS3Root(sendUser.Avatar),
                    Url = urlDownload,
                    Duration = duration.ToString(Constants.TimeSpanFormat),
                    SpecialistId = callDto.Booking.Specialist.Id,
                    CustomerId = callDto.Booking.Customer.Id,
                    Cost = totalCost,
                    RecordUrl = recordUrl,
                    IsShowFeature = isSendForCustomer,
                    PostNominal = isSendForCustomer && !string.IsNullOrWhiteSpace(sendUser.PostNominal) ? sendUser.PostNominal : string.Empty,
                    IsFavourite = IsFavourite
                });
            }
        }
예제 #10
0
 /// <summary>
 /// Push start/stop for calltimer to client
 /// </summary>
 /// <param name="UserName"></param>
 /// <param name="callLog"></param>
 /// <param name="costPerMinute"></param>
 /// <param name="isBegin"></param>
 public static void CallDurationForUser(string UserName, UserDto userForLoadCallTimer, CallDto callDto, decimal costPerMinute, bool isBegin, int startingTime)
 {
     var context = GlobalHost.ConnectionManager.GetHubContext<CallTimerHub>();
     foreach (var connectionId in CallTimerHub.connections.GetConnections(UserName))
     {
         if (isBegin && callDto.StartTime.HasValue)
         {
             decimal minimumCost = Role.Specialist.Equals(userForLoadCallTimer.Role) ? callDto.Booking.CustomerMinCharge : callDto.Booking.SpecialistMinCharge;
             //load user information for calltimer
             context.Clients.Client(connectionId).loadCallTimer(userForLoadCallTimer.Id);
             //start call timer
             context.Clients.Client(connectionId).callDurationWeb(
                 callDto.StartTime.Value.ToString(Code.Constants.DateTimeFormatTimeZone),
                 DateTime.UtcNow.ToString(Code.Constants.DateTimeFormatTimeZone),
                 costPerMinute, minimumCost, startingTime, callDto.Booking.IsApplyNoMinimumCharge);
             //start call timer for mobile
             context.Clients.Client(connectionId).callDuration(new
             {
                 BookingId = callDto.Booking.Id,
                 ReferenceNo = callDto.Booking.ReferenceNo,
                 StartTime = callDto.StartTime.Value.ToString(Code.Constants.DateTimeFormatTimeZone),
                 currentTimeServerSend = DateTime.UtcNow.ToString(Code.Constants.DateTimeFormatTimeZone),
                 CostPerMinute = costPerMinute,
                 UserTitleGender = !string.IsNullOrWhiteSpace(userForLoadCallTimer.Title) ? userForLoadCallTimer.Title : string.Empty,
                 UserFirstName = userForLoadCallTimer.FirstName,
                 UserLastName = userForLoadCallTimer.LastName,
                 UserFullName = userForLoadCallTimer.Name,
                 AvatarPath = S3ReaderHelper.CombineS3Root(userForLoadCallTimer.Avatar),
                 MinimumCost = minimumCost,
                 FreeTime = startingTime,
                 UserPostNominal = Role.Specialist.Equals(userForLoadCallTimer.Role) && !string.IsNullOrWhiteSpace(userForLoadCallTimer.PostNominal) ? userForLoadCallTimer.PostNominal : string.Empty,
                 IsApplyNoMinimumCharge = callDto.Booking.IsApplyNoMinimumCharge
             });
         }
         else if (!isBegin && callDto.EndTime.HasValue)
         {
             //stop call timer
             context.Clients.Client(connectionId).stopCall(
                 callDto.EndTime.Value.ToString(Code.Constants.DateTimeFormatTimeZone));
         }
     }
 }