- Enables outbound voice calls in voice channel . We are only caring about wiring the logic to trigger outgoing calls to the call button introduced in previous PRs. We will connect it to call component in subsequent PRs ref: #11602 ## Screens <img width="2304" height="1202" alt="image" src="https://github.com/user-attachments/assets/b91543a8-8d4e-4229-bd80-9727b42c7b0f" /> <img width="2304" height="1200" alt="image" src="https://github.com/user-attachments/assets/1a1dad2a-8cb2-4aa2-9702-c062416556a7" /> --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Vishnu Narayanan <vishnu@chatwoot.com>
61 lines
1.4 KiB
Ruby
61 lines
1.4 KiB
Ruby
class Voice::StatusUpdateService
|
|
pattr_initialize [:account!, :call_sid!, :call_status, { payload: {} }]
|
|
|
|
TWILIO_STATUS_MAP = {
|
|
'queued' => 'ringing',
|
|
'initiated' => 'ringing',
|
|
'ringing' => 'ringing',
|
|
'in-progress' => 'in-progress',
|
|
'inprogress' => 'in-progress',
|
|
'answered' => 'in-progress',
|
|
'completed' => 'completed',
|
|
'busy' => 'no-answer',
|
|
'no-answer' => 'no-answer',
|
|
'failed' => 'failed',
|
|
'canceled' => 'failed'
|
|
}.freeze
|
|
|
|
def perform
|
|
normalized_status = normalize_status(call_status)
|
|
return if normalized_status.blank?
|
|
|
|
conversation = account.conversations.find_by(identifier: call_sid)
|
|
return unless conversation
|
|
|
|
Voice::CallStatus::Manager.new(
|
|
conversation: conversation,
|
|
call_sid: call_sid
|
|
).process_status_update(
|
|
normalized_status,
|
|
duration: payload_duration,
|
|
timestamp: payload_timestamp
|
|
)
|
|
end
|
|
|
|
private
|
|
|
|
def normalize_status(status)
|
|
return if status.to_s.strip.empty?
|
|
|
|
TWILIO_STATUS_MAP[status.to_s.downcase]
|
|
end
|
|
|
|
def payload_duration
|
|
return unless payload.is_a?(Hash)
|
|
|
|
duration = payload['CallDuration'] || payload['call_duration']
|
|
duration&.to_i
|
|
end
|
|
|
|
def payload_timestamp
|
|
return unless payload.is_a?(Hash)
|
|
|
|
ts = payload['Timestamp'] || payload['timestamp']
|
|
return unless ts
|
|
|
|
Time.zone.parse(ts).to_i
|
|
rescue ArgumentError
|
|
nil
|
|
end
|
|
end
|