mirror of
https://github.com/lingble/chatwoot.git
synced 2025-12-14 17:57:09 +00:00
chore: clean up
This commit is contained in:
@@ -1,19 +1,19 @@
|
||||
class Api::V1::Accounts::Channels::Voice::WebhooksController < Api::V1::Accounts::BaseController
|
||||
skip_before_action :authenticate_user!, :set_current_user, only: [:incoming, :conference_status]
|
||||
protect_from_forgery with: :null_session, only: [:incoming, :conference_status]
|
||||
before_action :validate_twilio_signature, only: [:incoming]
|
||||
before_action :handle_options_request, only: [:incoming, :conference_status]
|
||||
|
||||
skip_before_action :authenticate_user!, :set_current_user, only: [:incoming, :conference_status, :call_status]
|
||||
protect_from_forgery with: :null_session, only: [:incoming, :conference_status, :call_status]
|
||||
before_action :validate_twilio_signature, only: [:incoming, :call_status]
|
||||
before_action :handle_options_request, only: [:incoming, :conference_status, :call_status]
|
||||
|
||||
# Handle CORS preflight OPTIONS requests
|
||||
def handle_options_request
|
||||
if request.method == "OPTIONS"
|
||||
if request.method == 'OPTIONS'
|
||||
set_cors_headers
|
||||
head :ok
|
||||
return true
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
|
||||
def set_cors_headers
|
||||
headers['Access-Control-Allow-Origin'] = '*'
|
||||
headers['Access-Control-Allow-Methods'] = 'POST, OPTIONS'
|
||||
@@ -25,78 +25,121 @@ class Api::V1::Accounts::Channels::Voice::WebhooksController < Api::V1::Accounts
|
||||
def incoming
|
||||
# Set CORS headers first to ensure they're included
|
||||
set_cors_headers
|
||||
|
||||
# Log basic request info
|
||||
Rails.logger.info("🔔 INCOMING CALL WEBHOOK: CallSid=#{params['CallSid']} From=#{params['From']} To=#{params['To']}")
|
||||
|
||||
|
||||
# Process incoming call using service
|
||||
begin
|
||||
# Ensure account is set properly
|
||||
if !Current.account && params[:account_id].present?
|
||||
Current.account = Account.find(params[:account_id])
|
||||
Rails.logger.info("👑 Set Current.account to #{Current.account.id}")
|
||||
end
|
||||
|
||||
Current.account = Account.find(params[:account_id]) if !Current.account && params[:account_id].present?
|
||||
|
||||
# Validate required parameters
|
||||
validate_incoming_params
|
||||
|
||||
|
||||
# Process the call
|
||||
service = Voice::IncomingCallService.new(
|
||||
account: Current.account,
|
||||
account: Current.account,
|
||||
params: params.to_unsafe_h.merge(host_with_port: request.host_with_port)
|
||||
)
|
||||
twiml_response = service.process
|
||||
|
||||
|
||||
# Return TwiML response
|
||||
Rails.logger.info("✅ INCOMING CALL: Successfully processed")
|
||||
render xml: twiml_response
|
||||
rescue StandardError => e
|
||||
# Log the error with detailed information
|
||||
Rails.logger.error("❌ INCOMING CALL ERROR: #{e.message}")
|
||||
Rails.logger.error("❌ BACKTRACE: #{e.backtrace[0..5].join("\n")}")
|
||||
|
||||
Rails.logger.error("Incoming call error: #{e.message}")
|
||||
|
||||
# Return friendly error message to caller
|
||||
render_error("We're sorry, but we're experiencing technical difficulties. Please try your call again later.")
|
||||
end
|
||||
end
|
||||
|
||||
# Handle individual call status updates
|
||||
def call_status
|
||||
# Set CORS headers first to ensure they're always included
|
||||
set_cors_headers
|
||||
|
||||
# Return immediately for OPTIONS requests
|
||||
return head :ok if request.method == 'OPTIONS'
|
||||
|
||||
# Process call status updates
|
||||
begin
|
||||
# Set account for local development if needed
|
||||
Current.account = Account.find(params[:account_id]) if !Current.account && params[:account_id].present?
|
||||
|
||||
# Find conversation by CallSid
|
||||
call_sid = params['CallSid']
|
||||
# For dial action callbacks, use DialCallStatus; fallback to CallStatus for other types
|
||||
call_status = params['DialCallStatus'] || params['CallStatus']
|
||||
|
||||
if call_sid.present? && call_status.present?
|
||||
conversation = Current.account.conversations.where("additional_attributes->>'call_sid' = ?", call_sid).first
|
||||
|
||||
if conversation
|
||||
# Use CallStatusManager to handle the status update
|
||||
status_manager = Voice::CallStatus::Manager.new(
|
||||
conversation: conversation,
|
||||
call_sid: call_sid,
|
||||
provider: :twilio
|
||||
)
|
||||
|
||||
# Map Twilio call/dial statuses to our statuses and update
|
||||
case call_status.downcase
|
||||
when 'completed', 'busy', 'failed', 'no-answer', 'canceled'
|
||||
# Standard call status values
|
||||
if conversation.additional_attributes['call_status'] == 'ringing'
|
||||
status_manager.process_status_update('no_answer')
|
||||
else
|
||||
status_manager.process_status_update('ended')
|
||||
end
|
||||
when 'answered'
|
||||
# DialCallStatus: conference calls return 'answered' when successful
|
||||
# No action needed - call continues in conference
|
||||
else
|
||||
# Handle any other dial statuses (busy, no-answer, failed from dial action)
|
||||
if conversation.additional_attributes['call_status'] == 'ringing'
|
||||
status_manager.process_status_update('no_answer')
|
||||
else
|
||||
status_manager.process_status_update('ended')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Call status processed successfully
|
||||
rescue StandardError => e
|
||||
# Log errors but don't affect the response
|
||||
Rails.logger.error("Call status error: #{e.message}")
|
||||
end
|
||||
|
||||
# Always return a successful response for Twilio
|
||||
head :ok
|
||||
end
|
||||
|
||||
# Handle conference status updates
|
||||
def conference_status
|
||||
# Set CORS headers first to ensure they're always included
|
||||
set_cors_headers
|
||||
|
||||
|
||||
# Return immediately for OPTIONS requests
|
||||
if request.method == "OPTIONS"
|
||||
return head :ok
|
||||
end
|
||||
|
||||
# Log basic request info
|
||||
Rails.logger.info("🎧 CONFERENCE STATUS WEBHOOK: ConferenceSid=#{params['ConferenceSid']} Event=#{params['StatusCallbackEvent']}")
|
||||
|
||||
return head :ok if request.method == 'OPTIONS'
|
||||
|
||||
# Process conference status updates using service
|
||||
begin
|
||||
# Set account for local development if needed
|
||||
if !Current.account && params[:account_id].present?
|
||||
Current.account = Account.find(params[:account_id])
|
||||
Rails.logger.info("👑 Set Current.account to #{Current.account.id}")
|
||||
end
|
||||
|
||||
# Validate required parameters
|
||||
if params['ConferenceSid'].blank? && params['CallSid'].blank?
|
||||
Rails.logger.error("❌ MISSING REQUIRED PARAMS: Need either ConferenceSid or CallSid")
|
||||
end
|
||||
|
||||
Current.account = Account.find(params[:account_id]) if !Current.account && params[:account_id].present?
|
||||
|
||||
# Validate required parameters - need either ConferenceSid or CallSid
|
||||
return head :ok if params['ConferenceSid'].blank? && params['CallSid'].blank?
|
||||
|
||||
# Use service to process conference status
|
||||
service = Voice::ConferenceStatusService.new(account: Current.account, params: params)
|
||||
service.process
|
||||
|
||||
Rails.logger.info("✅ CONFERENCE STATUS: Successfully processed")
|
||||
|
||||
# Conference status processed successfully
|
||||
rescue StandardError => e
|
||||
# Log errors but don't affect the response
|
||||
Rails.logger.error("❌ CONFERENCE STATUS ERROR: #{e.message}")
|
||||
Rails.logger.error("❌ BACKTRACE: #{e.backtrace[0..5].join("\n")}")
|
||||
Rails.logger.error("Conference status error: #{e.message}")
|
||||
end
|
||||
|
||||
|
||||
# Always return a successful response for Twilio
|
||||
head :ok
|
||||
end
|
||||
@@ -104,43 +147,34 @@ class Api::V1::Accounts::Channels::Voice::WebhooksController < Api::V1::Accounts
|
||||
private
|
||||
|
||||
def validate_incoming_params
|
||||
if params['CallSid'].blank?
|
||||
raise "Missing required parameter: CallSid"
|
||||
end
|
||||
|
||||
if params['From'].blank?
|
||||
raise "Missing required parameter: From"
|
||||
end
|
||||
|
||||
if params['To'].blank?
|
||||
raise "Missing required parameter: To"
|
||||
end
|
||||
|
||||
if Current.account.nil?
|
||||
raise "Current account not set"
|
||||
end
|
||||
raise 'Missing required parameter: CallSid' if params['CallSid'].blank?
|
||||
|
||||
raise 'Missing required parameter: From' if params['From'].blank?
|
||||
|
||||
raise 'Missing required parameter: To' if params['To'].blank?
|
||||
|
||||
return unless Current.account.nil?
|
||||
|
||||
raise 'Current account not set'
|
||||
end
|
||||
|
||||
def validate_twilio_signature
|
||||
begin
|
||||
validator = Voice::TwilioValidatorService.new(
|
||||
account: Current.account,
|
||||
params: params,
|
||||
request: request
|
||||
)
|
||||
|
||||
if !validator.valid?
|
||||
Rails.logger.error("❌ INVALID TWILIO SIGNATURE")
|
||||
render_error('Invalid Twilio signature')
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("❌ TWILIO VALIDATION ERROR: #{e.message}")
|
||||
render_error('Error validating Twilio request')
|
||||
validator = Voice::TwilioValidatorService.new(
|
||||
account: Current.account,
|
||||
params: params,
|
||||
request: request
|
||||
)
|
||||
|
||||
unless validator.valid?
|
||||
render_error('Invalid Twilio signature')
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("Twilio validation error: #{e.message}")
|
||||
render_error('Error validating Twilio request')
|
||||
return false
|
||||
end
|
||||
|
||||
def render_error(message)
|
||||
@@ -149,4 +183,4 @@ class Api::V1::Accounts::Channels::Voice::WebhooksController < Api::V1::Accounts
|
||||
response.hangup
|
||||
render xml: response.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,7 +21,6 @@ class Api::V1::Accounts::VoiceController < Api::V1::Accounts::BaseController
|
||||
provider: :twilio)
|
||||
.process_status_update('completed', nil, false, "Call ended by #{current_user.name}")
|
||||
|
||||
broadcast_status(call_sid, 'completed')
|
||||
render_success('Call successfully ended')
|
||||
rescue StandardError => e
|
||||
render_error("Failed to end call: #{e.message}")
|
||||
@@ -35,7 +34,6 @@ class Api::V1::Accounts::VoiceController < Api::V1::Accounts::BaseController
|
||||
|
||||
conference_sid = convo_attr('conference_sid') || create_conference_sid!
|
||||
update_join_metadata!(call_sid)
|
||||
broadcast_status(call_sid, 'in-progress')
|
||||
|
||||
render json: {
|
||||
status: 'success',
|
||||
@@ -158,18 +156,6 @@ class Api::V1::Accounts::VoiceController < Api::V1::Accounts::BaseController
|
||||
.process_status_update('in_progress', nil, false, "#{current_user.name} joined the call")
|
||||
end
|
||||
|
||||
def broadcast_status(call_sid, status)
|
||||
ActionCable.server.broadcast "account_#{@conversation.account_id}", {
|
||||
event_name: 'call_status_changed',
|
||||
data: {
|
||||
call_sid: call_sid,
|
||||
status: status,
|
||||
conversation_id: @conversation.display_id,
|
||||
inbox_id: @conversation.inbox_id,
|
||||
timestamp: Time.current.to_i
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
# ---- TwiML -----------------------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user