Restructure omni services and add Chatwoot research snapshot
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
class Voice::Provider::Twilio::Adapter
|
||||
def initialize(channel)
|
||||
@channel = channel
|
||||
end
|
||||
|
||||
def initiate_call(to:, conference_sid: nil, agent_id: nil)
|
||||
call = twilio_client.calls.create(**call_params(to))
|
||||
|
||||
{
|
||||
provider: 'twilio',
|
||||
call_sid: call.sid,
|
||||
status: call.status,
|
||||
call_direction: 'outbound',
|
||||
requires_agent_join: true,
|
||||
agent_id: agent_id,
|
||||
conference_sid: conference_sid
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def call_params(to)
|
||||
phone_digits = @channel.phone_number.delete_prefix('+')
|
||||
|
||||
{
|
||||
from: @channel.phone_number,
|
||||
to: to,
|
||||
url: twilio_call_twiml_url(phone_digits),
|
||||
status_callback: twilio_call_status_url(phone_digits),
|
||||
status_callback_event: %w[
|
||||
initiated ringing answered completed failed busy no-answer canceled
|
||||
],
|
||||
status_callback_method: 'POST'
|
||||
}
|
||||
end
|
||||
|
||||
def twilio_call_twiml_url(phone_digits)
|
||||
Rails.application.routes.url_helpers.twilio_voice_call_url(phone: phone_digits)
|
||||
end
|
||||
|
||||
def twilio_call_status_url(phone_digits)
|
||||
Rails.application.routes.url_helpers.twilio_voice_status_url(phone: phone_digits)
|
||||
end
|
||||
|
||||
def twilio_client
|
||||
Twilio::REST::Client.new(config['account_sid'], config['auth_token'])
|
||||
end
|
||||
|
||||
def config
|
||||
@config ||= @channel.provider_config_hash
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
class Voice::Provider::Twilio::ConferenceService
|
||||
pattr_initialize [:conversation!, { twilio_client: nil }]
|
||||
|
||||
def ensure_conference_sid
|
||||
existing = conversation.additional_attributes&.dig('conference_sid')
|
||||
return existing if existing.present?
|
||||
|
||||
sid = Voice::Conference::Name.for(conversation)
|
||||
merge_attributes('conference_sid' => sid)
|
||||
sid
|
||||
end
|
||||
|
||||
def mark_agent_joined(user:)
|
||||
merge_attributes(
|
||||
'agent_joined' => true,
|
||||
'joined_at' => Time.current.to_i,
|
||||
'joined_by' => { id: user.id, name: user.name }
|
||||
)
|
||||
end
|
||||
|
||||
def end_conference
|
||||
twilio_client
|
||||
.conferences
|
||||
.list(friendly_name: Voice::Conference::Name.for(conversation), status: 'in-progress')
|
||||
.each { |conf| twilio_client.conferences(conf.sid).update(status: 'completed') }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def merge_attributes(attrs)
|
||||
current = conversation.additional_attributes || {}
|
||||
conversation.update!(additional_attributes: current.merge(attrs))
|
||||
end
|
||||
|
||||
def twilio_client
|
||||
@twilio_client ||= ::Twilio::REST::Client.new(account_sid, auth_token)
|
||||
end
|
||||
|
||||
def account_sid
|
||||
@account_sid ||= conversation.inbox.channel.provider_config_hash['account_sid']
|
||||
end
|
||||
|
||||
def auth_token
|
||||
@auth_token ||= conversation.inbox.channel.provider_config_hash['auth_token']
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,62 @@
|
||||
class Voice::Provider::Twilio::TokenService
|
||||
pattr_initialize [:inbox!, :user!, :account!]
|
||||
|
||||
def generate
|
||||
{
|
||||
token: access_token.to_jwt,
|
||||
identity: identity,
|
||||
voice_enabled: true,
|
||||
account_sid: config['account_sid'],
|
||||
agent_id: user.id,
|
||||
account_id: account.id,
|
||||
inbox_id: inbox.id,
|
||||
phone_number: inbox.channel.phone_number,
|
||||
twiml_endpoint: twiml_url,
|
||||
has_twiml_app: config['twiml_app_sid'].present?
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def config
|
||||
@config ||= inbox.channel.provider_config_hash || {}
|
||||
end
|
||||
|
||||
def identity
|
||||
@identity ||= "agent-#{user.id}-account-#{account.id}"
|
||||
end
|
||||
|
||||
def access_token
|
||||
Twilio::JWT::AccessToken.new(
|
||||
config['account_sid'],
|
||||
config['api_key_sid'],
|
||||
config['api_key_secret'],
|
||||
identity: identity,
|
||||
ttl: 1.hour.to_i
|
||||
).tap { |token| token.add_grant(voice_grant) }
|
||||
end
|
||||
|
||||
def voice_grant
|
||||
Twilio::JWT::AccessToken::VoiceGrant.new.tap do |grant|
|
||||
grant.incoming_allow = true
|
||||
grant.outgoing_application_sid = config['twiml_app_sid']
|
||||
grant.outgoing_application_params = outgoing_params
|
||||
end
|
||||
end
|
||||
|
||||
def outgoing_params
|
||||
{
|
||||
account_id: account.id,
|
||||
agent_id: user.id,
|
||||
identity: identity,
|
||||
client_name: identity,
|
||||
accountSid: config['account_sid'],
|
||||
is_agent: 'true'
|
||||
}
|
||||
end
|
||||
|
||||
def twiml_url
|
||||
digits = inbox.channel.phone_number.delete_prefix('+')
|
||||
Rails.application.routes.url_helpers.twilio_voice_call_url(phone: digits)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user