Skip to content
This repository was archived by the owner on May 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ gem 'sqlite3'
gem 'uglifier', '>= 1.3.0'
gem 'jquery-rails'
gem 'turbolinks'
gem 'twilio-ruby'
gem 'twilio-ruby', '>= 5.0.0'

group :test do
gem 'rspec-rails'
gem 'pry'
gem 'vcr'
gem 'webmock'
gem 'byebug'
end

group :development do
Expand Down
21 changes: 13 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ GEM
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
builder (3.2.2)
byebug (9.0.6)
coderay (1.1.1)
coffee-rails (4.1.1)
coffee-script (>= 2.2.0)
Expand All @@ -56,6 +57,8 @@ GEM
diff-lcs (1.2.5)
erubis (2.7.0)
execjs (2.7.0)
faraday (0.12.1)
multipart-post (>= 1.2, < 3)
globalid (0.3.6)
activesupport (>= 4.1.0)
hashdiff (0.2.3)
Expand All @@ -65,7 +68,8 @@ GEM
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (1.8.3)
jwt (1.5.4)
jwt (1.5.6)
libxml-ruby (3.0.0)
loofah (2.0.3)
nokogiri (>= 1.5.9)
mail (2.6.4)
Expand All @@ -76,7 +80,7 @@ GEM
mime-types-data (3.2016.0521)
mini_portile2 (2.1.0)
minitest (5.9.0)
multi_json (1.12.1)
multipart-post (2.0.0)
nokogiri (1.6.8)
mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
Expand Down Expand Up @@ -144,10 +148,10 @@ GEM
thread_safe (0.3.5)
turbolinks (2.5.3)
coffee-rails
twilio-ruby (4.11.1)
builder (>= 2.1.2)
jwt (~> 1.0)
multi_json (>= 1.3.0)
twilio-ruby (5.0.0.rc26)
faraday (~> 0.9)
jwt (~> 1.5)
libxml-ruby (= 3.0.0)
tzinfo (1.2.2)
thread_safe (~> 0.1)
uglifier (3.0.0)
Expand All @@ -167,17 +171,18 @@ PLATFORMS
ruby

DEPENDENCIES
byebug
jquery-rails
pry
rails (= 4.2.6)
rspec-rails
sqlite3
turbolinks
twilio-ruby
twilio-ruby (~> 5.0.0.rc26)
uglifier (>= 1.3.0)
vcr
web-console (~> 2.0)
webmock

BUNDLED WITH
1.11.2
1.15.1
5 changes: 2 additions & 3 deletions app/controllers/callback_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,13 @@ def redirect_to_voicemail(call_sid)
redirect_url =
"http://twimlets.com/voicemail?Email=#{email}&#{url_message}"

call = client.account.calls.get(call_sid)
call.redirect_to(redirect_url)
client.calls(call_sid).update(url: redirect_url)
end

def notify_offline_status(phone_number)
message = 'Your status has changed to Offline. Reply with '\
'"On" to get back Online'
client.account.messages.create(
client.messages.create(
to: phone_number,
from: ENV['TWILIO_NUMBER'],
body: message
Expand Down
13 changes: 7 additions & 6 deletions app/controllers/message_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ def incoming

worker_sid = WorkspaceInfo.instance.workers[from_number][:sid]
client
.workspace
.workers
.get(worker_sid)
.workspaces(WorkspaceInfo.instance.workspace_sid)
.workers(worker_sid)
.fetch
.update(activity_sid: activity_sid)

render xml: TwimlGenerator.generate_confirm_message(status)
Expand All @@ -26,10 +26,11 @@ def incoming
private

def client
Twilio::REST::TaskRouterClient.new(
client_instance = Twilio::REST::Client.new(
ENV['TWILIO_ACCOUNT_SID'],
ENV['TWILIO_AUTH_TOKEN'],
WorkspaceInfo.instance.workspace_sid
ENV['TWILIO_AUTH_TOKEN']
)

client_instance.taskrouter.v1
end
end
34 changes: 19 additions & 15 deletions lib/twiml_generator.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
module TwimlGenerator
def self.generate_gather_product(callback_url)
Twilio::TwiML::Response.new do |r|
r.Gather numDigits: 1, action: callback_url, method: 'POST' do |g|
g.Say 'Welcome to the Twilio support line!'
g.Say 'To get specialized help with programmable voice press 1, '\
'or press 2 for programmable SMS'
end
end.to_xml
response = Twilio::TwiML::VoiceResponse.new
gather = Twilio::TwiML::Gather.new(num_digits: 1,
action: callback_url,
method: 'POST')
gather.say 'Welcome to the Twilio support line!'
gather.say 'To get specialized help with programmable voice press 1, '\
'or press 2 for programmable SMS'

response.append(gather)
response.to_s
end

def self.generate_task_enqueue(selected_product)
Twilio::TwiML::Response.new do |r|
r.Enqueue workflowSid: WorkspaceInfo.instance.workflow_sid do |e|
e.Task "{\"selected_product\": \"#{selected_product}\"}"
end
end.to_xml
enqueue = Twilio::TwiML::Enqueue.new(nil, workflow_sid: WorkspaceInfo.instance.workflow_sid)
enqueue.task "{\"selected_product\": \"#{selected_product}\"}"

response = Twilio::TwiML::VoiceResponse.new
response.append(enqueue)
response.to_s
end

def self.generate_confirm_message(status)
Twilio::TwiML::Response.new do |r|
r.Message "Your status has changed to #{status}"
end.to_xml
response = Twilio::TwiML::MessagingResponse.new
response.message(body: "Your status has changed to #{status}")
response.to_s
end
end
77 changes: 50 additions & 27 deletions lib/workspace_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ def setup
@workspace_sid = create_workspace
@client = taskrouter_client
WorkspaceInfo.instance.workers = create_workers
queues = create_task_queues
workflow_sid = create_workflow(queues).sid
workflow_sid = create_workflow.sid
WorkspaceInfo.instance.workflow_sid = workflow_sid
idle_activity_sid = activity_by_name('Idle').sid
WorkspaceInfo.instance.post_work_activity_sid = idle_activity_sid
Expand All @@ -39,11 +38,12 @@ def setup
attr_reader :client, :account_sid, :auth_token

def taskrouter_client
Twilio::REST::TaskRouterClient.new(
client_instance = Twilio::REST::Client.new(
account_sid,
auth_token,
workspace_sid
auth_token
)

client_instance.taskrouter.v1
end

def create_workspace
Expand Down Expand Up @@ -72,15 +72,15 @@ def create_workers
end

def create_worker(name, attributes)
client.workspace.workers.create(
client.workspaces(@workspace_sid).workers.create(
friendly_name: name,
attributes: attributes,
activity_sid: activity_by_name('Idle').sid
)
end

def activity_by_name(name)
client.workspace.activities.list(friendly_name: name).first
client.workspaces(@workspace_sid).activities.list(friendly_name: name).first
end

def create_task_queues
Expand All @@ -102,26 +102,19 @@ def create_task_queues
end

def create_task_queue(name, reservation_sid, assignment_sid, target_workers)
client.workspace.task_queues.create(
client.workspaces(@workspace_sid).task_queues.create(
friendly_name: name,
reservation_activity_sid: reservation_sid,
assignment_activity_sid: assignment_sid,
target_workers: target_workers
)
end

def create_workflow(queues)
default_rule_target = create_rule_target(queues[:all].sid, 1, QUEUE_TIMEOUT, '1==1')
voice_rule_target = create_rule_target(queues[:voice].sid, 5, QUEUE_TIMEOUT, nil)
sms_rule_target = create_rule_target(queues[:sms].sid, 5, QUEUE_TIMEOUT, nil)
voice_rule = create_rule('selected_product=="ProgrammableVoice"',
[voice_rule_target, default_rule_target])
sms_rule = create_rule('selected_product=="ProgrammableSMS"',
[sms_rule_target, default_rule_target])

rules = [voice_rule, sms_rule]
config = Twilio::TaskRouter::WorkflowConfiguration.new(rules, default_rule_target)
client.workspace.workflows.create(
def create_workflow
queues = create_task_queues
config = workflow_config(queues)

client.workspaces(@workspace_sid).workflows.create(
configuration: config.to_json,
friendly_name: WORKFLOW_NAME,
assignment_callback_url: ASSIGNMENT_CALLBACK_URL,
Expand All @@ -130,16 +123,46 @@ def create_workflow(queues)
)
end

def create_rule_target(queue_sid, priority, timeout, expression)
Twilio::TaskRouter::WorkflowRuleTarget.new(queue_sid, priority, timeout,
expression)
def workspace_sid
@workspace_sid || 'no_workspace_yet'
end

def create_rule(expression, targets)
Twilio::TaskRouter::WorkflowRule.new(expression, targets)
def workflow_config(queues)
default_target = default_rule_target(queues[:all].sid)

{
task_routing: {
filters: [
{
expression: 'selected_product=="ProgrammableVoice"',
targets: [
rule_target(queues[:voice].sid),
default_target
]
},
{
expression: 'selected_product=="ProgrammableSMS"',
targets: [
rule_target(queues[:sms].sid),
default_target
]
}
],
default_filter: default_target
}
}
end

def workspace_sid
@workspace_sid || 'no_workspace_yet'
def rule_target(sid)
{ queue: sid, priority: 5, timeout: QUEUE_TIMEOUT }
end

def default_rule_target(sid)
{
queue: sid,
priority: 1,
timeout: QUEUE_TIMEOUT,
expression: '1==1'
}
end
end
11 changes: 5 additions & 6 deletions spec/controllers/callback_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
message_body = 'Your status has changed to Offline. Reply with '\
'"On" to get back Online'

expect(client_double).to receive_message_chain(:account, :messages).and_return(messages_double)
expect(client_double).to receive(:messages).and_return(messages_double)
expect(messages_double)
.to receive(:create)
.with(from: ENV['TWILIO_NUMBER'], to: worker_number, body: message_body)
Expand Down Expand Up @@ -74,13 +74,12 @@
url_message = { Message: message }.to_query
redirect_url =
"http://twimlets.com/voicemail?Email=#{email}&#{url_message}"
calls_double = double(:calls)
call_double = double(:call)

allow(client_double).to receive_message_chain(:account, :calls).and_return(calls_double)
expect(calls_double).to receive(:get).with(call_sid).and_return(call_double)
expect(call_double).to receive(:redirect_to)
.with(redirect_url)
allow(client_double).to receive(:calls).with(call_sid)
.and_return(call_double)
expect(call_double).to receive(:update)
.with(url: redirect_url)

post :events,
EventType: 'workflow.timeout',
Expand Down
7 changes: 4 additions & 3 deletions spec/controllers/message_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
workers_double = double(:workers)
worker_double = double(:worker)

allow(Twilio::REST::TaskRouterClient).to receive(:new).and_return(client_double)
allow(client_double).to receive_message_chain(:workspace, :workers).and_return(workers_double)
expect(workers_double).to receive(:get).with(worker_sid).and_return(worker_double)
allow(Twilio::REST::Client).to receive_message_chain(:new, :taskrouter, :v1)
.and_return(client_double)
allow(client_double).to receive_message_chain(:workspaces, :workers).and_return(workers_double)
expect(workers_double).to receive(:fetch).and_return(worker_double)
expect(worker_double).to receive(:update).with(activity_sid: idle_activity_sid)

expected_response = '<Response></Response>'
Expand Down
Loading