chore: fix conversation list status
This commit is contained in:
@@ -51,29 +51,17 @@ class Api::V1::Accounts::VoiceController < Api::V1::Accounts::BaseController
|
||||
|
||||
# Update call status using the unified CallStatusManager
|
||||
# The CallStatusManager will determine if the call is outbound internally
|
||||
# and will create an appropriate activity message
|
||||
custom_message = "Call ended by #{current_user.name}"
|
||||
|
||||
status_manager = Voice::CallStatus::Manager.new(
|
||||
conversation: @conversation,
|
||||
call_sid: call_sid,
|
||||
provider: :twilio
|
||||
)
|
||||
status_manager.process_status_update('completed')
|
||||
|
||||
# CallStatusManager handles all voice call message updates
|
||||
|
||||
# Create an activity message noting the call has ended
|
||||
Messages::MessageBuilder.new(
|
||||
nil,
|
||||
@conversation,
|
||||
{
|
||||
content: 'Call ended by agent',
|
||||
message_type: :activity,
|
||||
additional_attributes: {
|
||||
call_sid: call_sid,
|
||||
call_status: 'completed',
|
||||
ended_by: current_user.name
|
||||
}
|
||||
}
|
||||
).perform
|
||||
status_manager.process_status_update('completed', nil, false, custom_message)
|
||||
|
||||
# No need to create additional activity messages - the manager handles it
|
||||
|
||||
# Broadcast call status update on the account channel
|
||||
ActionCable.server.broadcast(
|
||||
@@ -143,33 +131,19 @@ class Api::V1::Accounts::VoiceController < Api::V1::Accounts::BaseController
|
||||
name: current_user.name
|
||||
}
|
||||
|
||||
# Update call status using the unified CallStatusManager
|
||||
# Update call status using the unified CallStatusManager with custom message
|
||||
# The CallStatusManager will determine if the call is outbound internally
|
||||
custom_message = "#{current_user.name} joined the call"
|
||||
|
||||
status_manager = Voice::CallStatus::Manager.new(
|
||||
conversation: @conversation,
|
||||
call_sid: call_sid,
|
||||
provider: :twilio
|
||||
)
|
||||
status_manager.process_status_update('in-progress')
|
||||
status_manager.process_status_update('in-progress', nil, false, custom_message)
|
||||
|
||||
# Save the conversation with agent join details
|
||||
@conversation.save!
|
||||
|
||||
# Create an activity message
|
||||
Messages::MessageBuilder.new(
|
||||
nil,
|
||||
@conversation,
|
||||
{
|
||||
content: "#{current_user.name} joined the call",
|
||||
message_type: :activity,
|
||||
additional_attributes: {
|
||||
call_sid: call_sid,
|
||||
conference_sid: conference_sid,
|
||||
joined_by: current_user.name,
|
||||
joined_at: Time.now.to_i
|
||||
}
|
||||
}
|
||||
).perform
|
||||
|
||||
# Broadcast call status update on the account channel
|
||||
ActionCable.server.broadcast(
|
||||
@@ -220,20 +194,19 @@ class Api::V1::Accounts::VoiceController < Api::V1::Accounts::BaseController
|
||||
}
|
||||
@conversation.save!
|
||||
|
||||
# Create an activity message noting the agent rejected the call
|
||||
Messages::MessageBuilder.new(
|
||||
nil,
|
||||
@conversation,
|
||||
{
|
||||
content: "#{current_user.name} declined to answer",
|
||||
message_type: :activity,
|
||||
additional_attributes: {
|
||||
call_sid: call_sid,
|
||||
rejected_by: current_user.name,
|
||||
rejected_at: Time.now.to_i
|
||||
}
|
||||
}
|
||||
).perform
|
||||
# Update call status and create activity message through the unified manager
|
||||
custom_message = "#{current_user.name} declined to answer"
|
||||
|
||||
status_manager = Voice::CallStatus::Manager.new(
|
||||
conversation: @conversation,
|
||||
call_sid: call_sid,
|
||||
provider: :twilio
|
||||
)
|
||||
status_manager.create_activity_message(custom_message, {
|
||||
call_sid: call_sid,
|
||||
rejected_by: current_user.name,
|
||||
rejected_at: Time.now.to_i
|
||||
})
|
||||
|
||||
render json: {
|
||||
status: 'success',
|
||||
|
||||
@@ -1546,8 +1546,65 @@ export default {
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// This function was removed as it's no longer needed
|
||||
|
||||
// Add watcher for call status changes
|
||||
watch(
|
||||
() => store.state.calls.activeCall?.status,
|
||||
(newStatus) => {
|
||||
if (newStatus === 'ended' || newStatus === 'completed' || newStatus === 'missed') {
|
||||
console.log('Call status changed to:', newStatus);
|
||||
stopDurationTimer();
|
||||
stopRingtone();
|
||||
isCallActive.value = false;
|
||||
|
||||
// Update app state
|
||||
if (window.app && window.app.$data) {
|
||||
window.app.$data.showCallWidget = false;
|
||||
}
|
||||
|
||||
// Emit event
|
||||
emit('callEnded');
|
||||
|
||||
// Clear store state
|
||||
store.dispatch('calls/clearActiveCall');
|
||||
store.dispatch('calls/clearIncomingCall');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Add specific watcher for outbound call status
|
||||
watch(
|
||||
() => store.state.calls.activeCall,
|
||||
(newCall) => {
|
||||
// Check if this is an outbound call
|
||||
const isOutboundCall = newCall && newCall.isOutbound === true;
|
||||
|
||||
if (isOutboundCall) {
|
||||
console.log('Outbound call status:', newCall?.status);
|
||||
|
||||
// Handle outbound call status changes
|
||||
if (newCall?.status === 'ended' || newCall?.status === 'completed' || newCall?.status === 'missed') {
|
||||
console.log('Outbound call ended with status:', newCall.status);
|
||||
stopDurationTimer();
|
||||
stopRingtone();
|
||||
isCallActive.value = false;
|
||||
|
||||
// Update app state
|
||||
if (window.app && window.app.$data) {
|
||||
window.app.$data.showCallWidget = false;
|
||||
}
|
||||
|
||||
// Emit event
|
||||
emit('callEnded');
|
||||
|
||||
// Clear store state
|
||||
store.dispatch('calls/clearActiveCall');
|
||||
store.dispatch('calls/clearIncomingCall');
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true } // Watch for nested changes in the call object
|
||||
);
|
||||
|
||||
return {
|
||||
isCallActive,
|
||||
callDuration,
|
||||
|
||||
@@ -232,13 +232,40 @@ class ActionCableConnector extends BaseActionCableConnector {
|
||||
status: data.status,
|
||||
conversationId: data.conversation_id,
|
||||
inboxId: data.inbox_id,
|
||||
timestamp: data.timestamp || Date.now()
|
||||
};
|
||||
|
||||
// Update store with call status change
|
||||
this.app.$store.dispatch('calls/handleCallStatusChanged', normalizedPayload);
|
||||
|
||||
// For non-terminal statuses, update the active call
|
||||
if (!['ended', 'missed', 'completed'].includes(data.status)) {
|
||||
// For terminal statuses, clear the active call to close the widget
|
||||
if (['ended', 'missed', 'completed', 'failed', 'busy', 'no_answer'].includes(data.status)) {
|
||||
console.log(`ActionCable: Call status changed to terminal status: ${data.status}`);
|
||||
|
||||
// Clear active call for terminal statuses
|
||||
this.app.$store.dispatch('calls/clearActiveCall');
|
||||
|
||||
// Ensure window.app.$data exists before modifying it
|
||||
if (window.app && window.app.$data) {
|
||||
console.log('ActionCable: Hiding call widget');
|
||||
window.app.$data.showCallWidget = false;
|
||||
}
|
||||
|
||||
// Update conversation list to show current status
|
||||
if (data.conversation_id) {
|
||||
console.log(`ActionCable: Updating conversation last activity for conversation ${data.conversation_id}`);
|
||||
this.app.$store.dispatch('updateConversationLastActivity', {
|
||||
conversationId: data.conversation_id,
|
||||
lastActivityAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// Also ensure that the conversation gets refreshed
|
||||
this.app.$store.dispatch('fetchConversation', {
|
||||
id: data.conversation_id
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Update active call for non-terminal statuses
|
||||
this.app.$store.dispatch('calls/setActiveCall', normalizedPayload);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -29,7 +29,8 @@ export default {
|
||||
const transcription = ref('');
|
||||
const durationTimer = ref(null);
|
||||
const isCallActive = computed(
|
||||
() => callStatus.value && callStatus.value !== 'completed'
|
||||
() => callStatus.value &&
|
||||
!['completed', 'ended', 'missed', 'failed', 'busy', 'no-answer'].includes(callStatus.value)
|
||||
);
|
||||
|
||||
const callStatusText = computed(() => {
|
||||
@@ -77,16 +78,24 @@ export default {
|
||||
const updateCallStatus = status => {
|
||||
callStatus.value = status;
|
||||
|
||||
if (status === 'in-progress') {
|
||||
// Log the status update to help with debugging
|
||||
console.log(`CallManager: Updating call status to ${status}`);
|
||||
|
||||
if (status === 'in-progress' || status === 'in_progress') {
|
||||
startDurationTimer();
|
||||
} else if (
|
||||
status === 'completed' ||
|
||||
status === 'failed' ||
|
||||
status === 'busy' ||
|
||||
status === 'no-answer'
|
||||
['completed', 'ended', 'missed', 'failed', 'busy', 'no-answer', 'no_answer'].includes(status)
|
||||
) {
|
||||
console.log(`CallManager: Call ended with status ${status}`);
|
||||
stopDurationTimer();
|
||||
emit('callEnded');
|
||||
|
||||
// Forcefully hide the call widget after a short delay
|
||||
setTimeout(() => {
|
||||
if (window.app && window.app.$data) {
|
||||
window.app.$data.showCallWidget = false;
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -271,8 +280,8 @@ export default {
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-show="isCallActive && callStatus"
|
||||
v-if="isCallActive && callStatus"
|
||||
v-show="callStatus"
|
||||
v-if="callStatus"
|
||||
class="relative p-4 mb-4 border border-solid rounded-md bg-n-slate-1 border-n-slate-4 flex flex-col gap-2"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
|
||||
@@ -13,10 +13,36 @@ const getters = {
|
||||
const actions = {
|
||||
// This action will handle both message updates and direct call status changes
|
||||
handleCallStatusChanged({ state, dispatch }, { callSid, status }) {
|
||||
// Check if this is the active call
|
||||
const isActiveCall = callSid === state.activeCall?.callSid;
|
||||
const isOutboundCall = state.activeCall?.isOutbound === true;
|
||||
|
||||
// If this is the active call and it has ended or was missed, close the widget
|
||||
if (callSid === state.activeCall?.callSid &&
|
||||
if (isActiveCall &&
|
||||
(status === 'ended' || status === 'missed' || status === 'completed')) {
|
||||
console.log('Call status changed to:', status, 'isOutbound:', isOutboundCall);
|
||||
|
||||
// Clear the active call
|
||||
dispatch('clearActiveCall');
|
||||
|
||||
// Force update app state to hide widget
|
||||
if (window.app && window.app.$data) {
|
||||
window.app.$data.showCallWidget = false;
|
||||
}
|
||||
|
||||
// Emit event to notify components
|
||||
if (window.app) {
|
||||
window.app.$emit('callEnded');
|
||||
}
|
||||
|
||||
// For outbound calls, also clear any pending state
|
||||
if (isOutboundCall) {
|
||||
// Additional cleanup for outbound calls
|
||||
if (window.globalCallStatus) {
|
||||
window.globalCallStatus.active = false;
|
||||
window.globalCallStatus.incoming = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -232,7 +232,8 @@ class Conversation < ApplicationRecord
|
||||
def allowed_keys?
|
||||
(
|
||||
previous_changes.keys.intersect?(list_of_keys) ||
|
||||
(previous_changes['additional_attributes'].present? && previous_changes['additional_attributes'][1].keys.intersect?(%w[conversation_language]))
|
||||
(previous_changes['additional_attributes'].present? &&
|
||||
(previous_changes['additional_attributes'][1].keys.intersect?(%w[conversation_language call_status call_duration])))
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -64,18 +64,12 @@ module Voice
|
||||
|
||||
# Provider-specific message templates for different call statuses
|
||||
# These messages must EXACTLY match what the UI expects to show
|
||||
# "In-progress" messages have been removed as they don't provide value
|
||||
PROVIDER_MESSAGES = {
|
||||
twilio: {
|
||||
# Outgoing calls
|
||||
'initiated' => { outbound: 'Call started…', inbound: 'Incoming call…' },
|
||||
'ringing' => { outbound: 'Call started…', inbound: 'Incoming call…' },
|
||||
'in-progress' => {
|
||||
outbound: { first: 'Call in progress…', next: 'Call in progress…' },
|
||||
inbound: { first: 'Call in progress…', next: 'Call in progress…' }
|
||||
},
|
||||
'active' => { outbound: 'Call in progress…', inbound: 'Call in progress…' },
|
||||
# Status messages (only for terminal statuses)
|
||||
'completed' => { outbound: 'Call ended', inbound: 'Call ended' },
|
||||
'busy' => { outbound: 'Line busy', inbound: 'Missed call' }, # Show as missed for inbound
|
||||
'busy' => { outbound: 'Line busy', inbound: 'Missed call' },
|
||||
'failed' => { outbound: 'Call failed', inbound: 'Missed call' },
|
||||
'no-answer' => { outbound: 'No answer', inbound: 'Missed call' },
|
||||
'canceled' => { outbound: 'Call canceled', inbound: 'Missed call' },
|
||||
@@ -86,6 +80,11 @@ module Voice
|
||||
# Create a custom activity message
|
||||
# This provides a clean migration path from MessageUpdateService
|
||||
def create_activity_message(content, additional_attributes = {})
|
||||
return nil if content.blank?
|
||||
|
||||
Rails.logger.info("📝 [CallStatusManager] Creating activity message: '#{content}'")
|
||||
|
||||
# Create message
|
||||
Messages::MessageBuilder.new(
|
||||
nil,
|
||||
conversation,
|
||||
@@ -105,8 +104,9 @@ module Voice
|
||||
# @param status [String] The status from the provider (e.g., 'completed', 'ringing')
|
||||
# @param duration [Integer, nil] The call duration in seconds (if available)
|
||||
# @param is_first_response [Boolean] Whether this is the first status update for this status
|
||||
# @param custom_message [String, nil] Optional custom activity message to create
|
||||
# @return [Boolean] Whether the update was processed successfully
|
||||
def process_status_update(status, duration = nil, is_first_response = false)
|
||||
def process_status_update(status, duration = nil, is_first_response = false, custom_message = nil)
|
||||
# Normalize status using the STATUS_MAPPING if present
|
||||
normalized_status = STATUS_MAPPING[status] || status
|
||||
|
||||
@@ -145,8 +145,23 @@ module Voice
|
||||
# Update conversation and message status in a database transaction
|
||||
result = update_status(normalized_status, duration)
|
||||
|
||||
# Create an activity message with provider-specific text if status was updated
|
||||
create_provider_activity_message(normalized_status, is_first_response) if result
|
||||
# Create activity message based on the provided custom message or use default provider message
|
||||
if result
|
||||
if custom_message.present?
|
||||
# Use the custom message provided
|
||||
Rails.logger.info("📝 [CallStatusManager] Creating custom activity message: '#{custom_message}'")
|
||||
create_activity_message(custom_message, { call_sid: call_sid, call_status: normalized_status })
|
||||
elsif should_create_activity_message?(normalized_status)
|
||||
# For outbound calls in initiated/ringing stage, only add message if explicitly requested
|
||||
if is_outbound? && ['initiated', 'ringing'].include?(normalized_status) && !is_first_response
|
||||
Rails.logger.info("📝 [CallStatusManager] Skipping default activity message for outbound call status: '#{normalized_status}'")
|
||||
else
|
||||
# Use default provider message
|
||||
Rails.logger.info("📝 [CallStatusManager] Creating default activity message for status: '#{normalized_status}'")
|
||||
create_provider_activity_message(normalized_status, is_first_response)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
@@ -208,6 +223,9 @@ module Voice
|
||||
|
||||
# Generate provider-specific activity messages (e.g., for Twilio)
|
||||
def create_provider_activity_message(status, is_first_response = false)
|
||||
# Skip activity messages for non-terminal states
|
||||
return nil unless call_ended?(status)
|
||||
|
||||
provider_key = provider&.to_sym
|
||||
call_direction = is_outbound? ? :outbound : :inbound
|
||||
|
||||
@@ -217,11 +235,7 @@ module Voice
|
||||
if provider_key && PROVIDER_MESSAGES.key?(provider_key)
|
||||
messages = PROVIDER_MESSAGES[provider_key]
|
||||
|
||||
if status == 'in-progress' && messages.dig(status, call_direction).is_a?(Hash)
|
||||
message_type = is_first_response ? :first : :next
|
||||
provider_message = messages.dig(status, call_direction, message_type)
|
||||
message = provider_message if provider_message
|
||||
elsif messages.dig(status, call_direction)
|
||||
if messages.dig(status, call_direction)
|
||||
message = messages.dig(status, call_direction)
|
||||
end
|
||||
end
|
||||
@@ -251,11 +265,19 @@ module Voice
|
||||
current_status = conversation.additional_attributes['call_status']
|
||||
|
||||
# Only update if status is changing or we're forcing an update
|
||||
# Exception: Don't create duplicate "completed" status updates for the same conversation
|
||||
if current_status == internal_status
|
||||
Rails.logger.info("🔄 [CallStatusManager] Status unchanged: '#{internal_status}'")
|
||||
return true
|
||||
end
|
||||
|
||||
# Don't process multiple call ending events - once a call is in a terminal state, keep it there
|
||||
# This prevents duplicate "Call ended" messages
|
||||
if current_status.present? && call_ended?(current_status) && call_ended?(internal_status)
|
||||
Rails.logger.info("🔄 [CallStatusManager] Call already in terminal state '#{current_status}', not changing to '#{internal_status}'")
|
||||
return true
|
||||
end
|
||||
|
||||
# Log the status transition
|
||||
Rails.logger.info("🔄 [CallStatusManager] Status transition: '#{current_status}' -> '#{internal_status}'")
|
||||
|
||||
@@ -266,11 +288,36 @@ module Voice
|
||||
# Update message content_attributes
|
||||
update_message_status(internal_status, duration)
|
||||
|
||||
# Create activity message for status change if it's a significant change
|
||||
create_status_activity_message(internal_status) if should_create_activity_message?(internal_status)
|
||||
# Only create activity messages for status changes that warrant them
|
||||
# For terminal states, we'll create exactly one appropriate activity message
|
||||
# For outbound calls in initiated/ringing states, we'll suppress standard messages
|
||||
|
||||
# Track status transitions in conversation metadata to prevent duplicate activity messages
|
||||
status_from = conversation.additional_attributes['call_status'] || 'none'
|
||||
status_to = internal_status
|
||||
transition = "#{status_from}→#{status_to}"
|
||||
|
||||
# Store which transitions we've already handled with activity messages
|
||||
conversation.additional_attributes['status_transitions'] ||= {}
|
||||
transition_handled = conversation.additional_attributes['status_transitions'][transition]
|
||||
|
||||
# Mark this transition as handled
|
||||
conversation.additional_attributes['status_transitions'][transition] = true
|
||||
|
||||
# Only create an activity message for the first occurrence of any status transition
|
||||
# and only for terminal states or custom messages
|
||||
create_should_activity = should_create_activity_message?(internal_status) &&
|
||||
!(is_outbound? && ['initiated', 'ringing'].include?(internal_status)) &&
|
||||
!transition_handled
|
||||
|
||||
# Create activity message for status change if needed
|
||||
create_status_activity_message(internal_status) if create_should_activity
|
||||
|
||||
# Broadcast status change notification
|
||||
broadcast_status_change(internal_status)
|
||||
|
||||
# No need for additional broadcast - status change already broadcasts
|
||||
# conversation.updated events for terminal call states
|
||||
end
|
||||
|
||||
true
|
||||
@@ -319,8 +366,8 @@ module Voice
|
||||
conversation.additional_attributes['meta']["#{status}_at"] = Time.now.to_i
|
||||
end
|
||||
|
||||
# Save the conversation
|
||||
conversation.save!
|
||||
# Save the conversation - force timestamp update to trigger UI refresh
|
||||
conversation.update!(last_activity_at: Time.current)
|
||||
end
|
||||
|
||||
def update_message_status(status, duration)
|
||||
@@ -386,9 +433,9 @@ module Voice
|
||||
end
|
||||
|
||||
def should_create_activity_message?(status)
|
||||
# Only create activity messages for significant state changes
|
||||
# Avoid creating too many messages for intermediate states
|
||||
call_ended?(status) || status == 'in-progress'
|
||||
# Only create activity messages for terminal call states
|
||||
# "in-progress" messages are removed as they don't provide much value
|
||||
call_ended?(status)
|
||||
end
|
||||
|
||||
def create_status_activity_message(status)
|
||||
@@ -421,14 +468,9 @@ module Voice
|
||||
'Call ended'
|
||||
end
|
||||
end
|
||||
elsif %w[in-progress active].include?(status)
|
||||
'Call in progress…'
|
||||
elsif status == 'ringing'
|
||||
is_outbound? ? 'Call started…' : 'Incoming call…'
|
||||
elsif status == 'initiated'
|
||||
is_outbound? ? 'Call started…' : 'Incoming call…'
|
||||
else
|
||||
"Call status: #{status}"
|
||||
# No intermediate status messages for in-progress or ringing
|
||||
nil
|
||||
end
|
||||
|
||||
# Use the public create_activity_message method
|
||||
@@ -462,7 +504,23 @@ module Voice
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
# Also broadcast a conversation.updated event for terminal statuses
|
||||
# This ensures the conversation list gets refreshed when a call ends
|
||||
if call_ended?(status)
|
||||
Rails.logger.info("📢 [CallStatusManager] Broadcasting conversation.updated for completed call")
|
||||
ActionCable.server.broadcast(
|
||||
"account_#{conversation.account_id}",
|
||||
{
|
||||
event_name: 'conversation.updated',
|
||||
data: conversation.push_event_data
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# NOTE: This method is no longer used - conversation updates are handled
|
||||
# directly in the broadcast_status_change method for terminal call states.
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,9 +32,6 @@ module Voice
|
||||
Rails.logger.warn("🎧 UNKNOWN CONFERENCE EVENT: #{event}")
|
||||
end
|
||||
|
||||
# Create activity message for the event
|
||||
create_activity_message
|
||||
|
||||
# Save conversation changes
|
||||
conversation.save!
|
||||
end
|
||||
@@ -124,11 +121,11 @@ module Voice
|
||||
elsif current_status == 'ringing'
|
||||
# Call never connected
|
||||
Rails.logger.info('📞 MARKING RINGING CALL AS MISSED')
|
||||
call_status_manager.process_status_update('missed')
|
||||
call_status_manager.process_status_update('missed', nil, false, 'Missed call')
|
||||
else
|
||||
# Default to completed status
|
||||
Rails.logger.info('📞 MARKING CALL AS COMPLETED (DEFAULT)')
|
||||
call_status_manager.process_status_update('completed')
|
||||
call_status_manager.process_status_update('completed', nil, false, 'Call ended')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -141,7 +138,7 @@ module Voice
|
||||
end
|
||||
|
||||
Rails.logger.info('📞 MARKING ACTIVE CALL AS COMPLETED')
|
||||
call_status_manager.process_status_update('completed', duration)
|
||||
call_status_manager.process_status_update('completed', duration, false, 'Call ended')
|
||||
end
|
||||
|
||||
# Helper methods for participant handling
|
||||
@@ -183,7 +180,8 @@ module Voice
|
||||
|
||||
Rails.logger.info('📞 UPDATING RINGING CALL TO CONNECTED (agent joined)')
|
||||
# Always use in-progress to be consistent with status mapping
|
||||
call_status_manager.process_status_update('in-progress', nil, true)
|
||||
# Pass event context to create appropriate activity message
|
||||
call_status_manager.process_status_update('in-progress', nil, true, 'Agent joined the call')
|
||||
end
|
||||
|
||||
def handle_caller_join
|
||||
@@ -195,7 +193,9 @@ module Voice
|
||||
|
||||
Rails.logger.info('📞 UPDATING RINGING OUTBOUND CALL TO CONNECTED (caller joined)')
|
||||
# Always use in-progress to be consistent with status mapping
|
||||
call_status_manager.process_status_update('in-progress', nil, true)
|
||||
# Only create activity message for inbound calls where caller joining is significant
|
||||
custom_message = call_status_manager.is_outbound? ? nil : 'Caller joined the call'
|
||||
call_status_manager.process_status_update('in-progress', nil, true, custom_message)
|
||||
end
|
||||
|
||||
def handle_generic_participant_join
|
||||
@@ -241,22 +241,34 @@ module Voice
|
||||
return unless caller_participant? && ringing_call? && !participant_has_joined?(AGENT)
|
||||
|
||||
Rails.logger.info('📞 MARKING AS MISSED (caller left during ringing, no agent joined)')
|
||||
call_status_manager.process_status_update('missed')
|
||||
call_status_manager.process_status_update('missed', nil, false, 'Missed call')
|
||||
end
|
||||
|
||||
# Everyone left check
|
||||
def check_if_everyone_left
|
||||
call_active = conversation.additional_attributes['call_status'] == 'active'
|
||||
call_in_progress = %w[active in-progress].include?(conversation.additional_attributes['call_status'])
|
||||
conference_active = conversation.additional_attributes['conference_status'] != 'ended'
|
||||
|
||||
return unless all_participants_left? && conference_active && call_active
|
||||
# If the caller has left but the call is still active, mark it as completed
|
||||
if caller_participant? && call_in_progress
|
||||
# Calculate duration if possible
|
||||
duration = nil
|
||||
duration = Time.now.to_i - conversation.additional_attributes['call_started_at'].to_i if conversation.additional_attributes['call_started_at']
|
||||
|
||||
Rails.logger.info('📞 MARKING CALL AS COMPLETED (caller left during active call)')
|
||||
call_status_manager.process_status_update('completed', duration, false, 'Caller left the call')
|
||||
return
|
||||
end
|
||||
|
||||
# Old code path for when everyone has left
|
||||
return unless all_participants_left? && conference_active && call_in_progress
|
||||
|
||||
# Calculate duration if possible
|
||||
duration = nil
|
||||
duration = Time.now.to_i - conversation.additional_attributes['call_started_at'].to_i if conversation.additional_attributes['call_started_at']
|
||||
|
||||
Rails.logger.info('📞 MARKING CALL AS COMPLETED (all participants left)')
|
||||
call_status_manager.process_status_update('completed', duration)
|
||||
call_status_manager.process_status_update('completed', duration, false, 'Call ended')
|
||||
end
|
||||
|
||||
# Participant tracking methods
|
||||
@@ -297,42 +309,7 @@ module Voice
|
||||
!participants.values.any? { |p| p['status'] == 'joined' }
|
||||
end
|
||||
|
||||
# Activity message creation
|
||||
def create_activity_message
|
||||
content = activity_message_content_for_event
|
||||
# Only create message if we have content to show
|
||||
call_status_manager.create_activity_message(content) if content.present?
|
||||
end
|
||||
|
||||
def activity_message_content_for_event
|
||||
case event
|
||||
when CONFERENCE_START
|
||||
# Don't show this message to avoid redundancy with call status messages
|
||||
nil
|
||||
when CONFERENCE_END
|
||||
# Don't show this message to avoid redundancy with call status messages
|
||||
nil
|
||||
when PARTICIPANT_JOIN
|
||||
if agent_participant?
|
||||
"Agent joined the call"
|
||||
elsif caller_participant?
|
||||
# Only for inbound calls - creates "Caller joined the call" message
|
||||
# For outbound calls, we don't need this as we show "Call connected" instead
|
||||
call_status_manager.is_outbound? ? nil : "Caller joined the call"
|
||||
else
|
||||
nil # Don't show for generic participants
|
||||
end
|
||||
when PARTICIPANT_LEAVE
|
||||
if agent_participant?
|
||||
"Agent left the call"
|
||||
elsif caller_participant?
|
||||
"Caller left the call"
|
||||
else
|
||||
nil # Don't show for generic participants
|
||||
end
|
||||
else
|
||||
nil # Don't show unknown events
|
||||
end
|
||||
end
|
||||
# Activity messages are now handled by the call_status_manager through the
|
||||
# process_status_update method, which takes a custom_message parameter.
|
||||
end
|
||||
end
|
||||
|
||||
@@ -115,11 +115,10 @@ module Voice
|
||||
provider: :twilio # Specify the provider for accurate messaging
|
||||
)
|
||||
|
||||
# Process first status update and create activity message
|
||||
status_manager.process_status_update('initiated', nil, true)
|
||||
|
||||
# Additional custom message for outgoing calls
|
||||
status_manager.create_activity_message("Outgoing call to #{contact.name || contact.phone_number}")
|
||||
# Process first status update with a custom message instead of default
|
||||
# This creates only one activity message
|
||||
custom_message = "Outgoing call to #{contact.name || contact.phone_number}"
|
||||
status_manager.process_status_update('initiated', nil, true, custom_message)
|
||||
end
|
||||
|
||||
def broadcast_to_agent
|
||||
|
||||
Reference in New Issue
Block a user