Added caller id lookup to cti feature.

This commit is contained in:
Martin Edenhofer 2016-04-28 15:15:50 +02:00
parent c7e510f416
commit 4366d60270
8 changed files with 606 additions and 15 deletions

View file

@ -85,6 +85,7 @@ gem 'net-ldap'
gem 'writeexcel'
gem 'icalendar'
gem 'browser'
gem 'phony'
# integrations
gem 'slack-notifier'

View file

@ -135,7 +135,7 @@ GEM
addressable
faraday
multi_json
libv8 (3.16.14.13)
libv8 (3.16.14.15)
listen (3.1.1)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9.7)
@ -150,7 +150,7 @@ GEM
mime-types (2.99.1)
mini_portile2 (2.0.0)
minitest (5.8.4)
multi_json (1.11.2)
multi_json (1.11.3)
multi_xml (0.5.5)
multipart-post (2.0.0)
mysql2 (0.3.20)
@ -191,8 +191,9 @@ GEM
omniauth-twitter (1.2.1)
json (~> 1.3)
omniauth-oauth (~> 1.1)
parser (2.3.0.7)
parser (2.3.1.0)
ast (~> 2.2)
phony (2.15.21)
pluginator (1.3.0)
power_assert (0.2.7)
powerpack (0.1.1)
@ -246,7 +247,7 @@ GEM
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.7.5)
ruby-progressbar (1.8.0)
rubyzip (1.2.0)
sass (3.4.22)
sass-rails (5.0.4)
@ -354,6 +355,7 @@ DEPENDENCIES
omniauth-google-oauth2
omniauth-linkedin
omniauth-twitter
phony
pre-commit
puma
rack-livereload

View file

@ -75,7 +75,6 @@ class App.CTI extends App.Controller
url: '#cti'
title: title
)
App.OnlineNotification.play()
featureActive: =>
return true

View file

@ -8,7 +8,7 @@ class Integration::SipgateController < ApplicationController
def index
return if !authentication_check
return if deny_if_not_role('CTI')
list = Cti::Log.order('created_at DESC').limit(60)
list = Cti::Log.order('created_at DESC, id DESC').limit(60)
render json: list
end
@ -130,8 +130,10 @@ class Integration::SipgateController < ApplicationController
to_comment = nil
if params['direction'] == 'in'
to_comment = user
from_comment = update_log_item('from')
else
from_comment = user
to_comment = update_log_item('to')
end
comment = nil
if params['cause']
@ -178,6 +180,37 @@ class Integration::SipgateController < ApplicationController
end
def update_log_item(direction)
from_comment_known = ''
from_comment_maybe = ''
caller_ids = Cti::CallerId.lookup(params[direction])
caller_ids.each {|record|
comment = ''
if record.user_id
user = User.lookup(id: record.user_id)
if user
comment += user.fullname
end
elsif !record.comment.empty?
comment += record.comment
end
if record.level == 'known'
if !from_comment_known.empty?
from_comment_known += ','
end
from_comment_known += comment
else
if !from_comment_maybe.empty?
from_comment_maybe += ','
end
from_comment_maybe += comment
end
}
return from_comment_known if !from_comment_known.empty?
return "maybe #{from_comment_maybe}" if !from_comment_maybe.empty?
nil
end
def xml_error(error)
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!

217
app/models/cti/caller_id.rb Normal file
View file

@ -0,0 +1,217 @@
module Cti
class CallerId < ApplicationModel
self.table_name = 'cti_caller_ids'
=begin
Cti::CallerId.maybe_add(
caller_id: '49123456789',
comment: 'Hairdresser Bob Smith, San Francisco', #optional
level: 'maybe', # known|maybe
user_id: 1, # optional
object: 'Ticket',
o_id: 123,
)
=end
def self.maybe_add(data)
records = Cti::CallerId.where(
caller_id: data[:caller_id],
level: data[:level],
object: data[:object],
o_id: data[:o_id],
user_id: data[:user_id],
)
return if records[0]
Cti::CallerId.create(
caller_id: data[:caller_id],
comment: data[:comment],
level: data[:level],
object: data[:object],
o_id: data[:o_id],
user_id: data[:user_id],
)
end
=begin
caller_id_records = Cti::CallerId.lookup('49123456789')
returns
[record1, record2, ...]
=end
def self.lookup(caller_id)
Cti::CallerId.where(
caller_id: caller_id,
).order('id DESC')
end
=begin
Cti::CallerId.build(ticket)
=end
def self.build(record)
map = config
level = nil
model = nil
map.each {|item|
next if item[:model] != record.class
level = item[:level]
model = item[:model]
}
return if !level || !model
build_item(record, model, level)
end
=begin
Cti::CallerId.build_item(record, model, level)
=end
def self.build_item(record, model, level)
# use first customer article
if model == Ticket
article = record.articles.first
return if !article
return if article.sender.name != 'Customer'
record = article
end
# set user id
user_id = record[:created_by_id]
if model == User
user_id = record.id
end
return if !user_id
# get caller ids
caller_ids = []
attributes = record.attributes
attributes.each {|_attribute, value|
next if value.class != String
next if value.empty?
local_caller_ids = Cti::CallerId.parse_text(value)
next if local_caller_ids.empty?
caller_ids = caller_ids.concat(local_caller_ids)
}
# store caller ids
Cti::CallerId.where(object: model.to_s, o_id: record.id).destroy_all
caller_ids.each {|caller_id|
Cti::CallerId.maybe_add(
caller_id: caller_id,
level: level,
object: model.to_s,
o_id: record.id,
user_id: user_id,
)
}
true
end
=begin
Cti::CallerId.rebuild
=end
def self.rebuild
map = config
map.each {|item|
level = item[:level]
model = item[:model]
item[:model].find_each(batch_size: 500) do |record|
build_item(record, model, level)
end
}
end
=begin
Cti::CallerId.config
returns
[
{
model: User,
level: 'known',
},
{
model: Ticket,
level: 'maybe',
},
]
=end
def self.config
[
{
model: User,
level: 'known',
},
{
model: Ticket,
level: 'maybe',
},
]
end
=begin
caller_ids = Cti::CallerId.parse_text('...')
returns
['49123456789', '49987654321']
=end
def self.parse_text(text)
caller_ids = []
# 022 1234567
# 021 123 2345
# 0271233211
# 021-233-9123
# 09 123 32112
# 021 2331231 or 021 321123123
# 622 32281
# 5754321
# 092213212
# (09)1234321
# +41 30 53 00 00 000
# +42 160 0000000
# +43 (0) 30 60 00 00 00-0
# 0043 (0) 30 60 00 00 00-0
default_country_id = '49'
text.gsub!(/([\d|\s|\-|\(|\)]{6,26})/) {
number = $1.strip
number.sub!(/^00/, '')
number.sub!(/\(0\)/, '')
number.gsub!(/(\s|\-|\(|\))/, '')
if !Phony.plausible?(number)
if number =~ /^0/
number.gsub!(/^0/, default_country_id)
else
number = "#{default_country_id}#{number}"
end
next if !Phony.plausible?(number)
end
caller_ids.push number
}
caller_ids
end
end
end

View file

@ -0,0 +1,19 @@
class CreateCtiCallerId < ActiveRecord::Migration
def up
create_table :cti_caller_ids do |t|
t.string :caller_id, limit: 100, null: false
t.string :comment, limit: 500, null: true
t.string :level, limit: 100, null: false
t.string :object, limit: 100, null: false
t.integer :o_id, null: false
t.integer :user_id, null: true
t.timestamps null: false
end
add_index :cti_caller_ids, [:caller_id]
add_index :cti_caller_ids, [:caller_id, :level]
add_index :cti_caller_ids, [:caller_id, :user_id]
add_index :cti_caller_ids, [:object, :o_id]
end
end

View file

@ -82,6 +82,44 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
created_by_id: 1,
)
customer1 = User.create_or_update(
login: 'ticket-caller_id_sipgate-customer1@example.com',
firstname: 'CallerId',
lastname: 'Customer1',
email: 'ticket-caller_id_sipgate-customer1@example.com',
password: 'customerpw',
active: true,
phone: '+49 99999 222222',
fax: '+49 99999 222223',
mobile: '+4912347114711',
note: 'Phone at home: +49 99999 222224',
updated_by_id: 1,
created_by_id: 1,
)
customer2 = User.create_or_update(
login: 'ticket-caller_id_sipgate-customer2@example.com',
firstname: 'CallerId',
lastname: 'Customer2',
email: 'ticket-caller_id_sipgate-customer2@example.com',
password: 'customerpw',
active: true,
phone: '+49 99999 222222 2',
updated_by_id: 1,
created_by_id: 1,
)
customer3 = User.create_or_update(
login: 'ticket-caller_id_sipgate-customer3@example.com',
firstname: 'CallerId',
lastname: 'Customer3',
email: 'ticket-caller_id_sipgate-customer3@example.com',
password: 'customerpw',
active: true,
phone: '+49 99999 222222 2',
updated_by_id: 1,
created_by_id: 1,
)
Cti::CallerId.rebuild
end
test 'basic call' do
@ -223,6 +261,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.to)
assert_equal('out', log.direction)
assert_equal('user 1', log.from_comment)
assert_equal('CallerId Customer1', log.to_comment)
assert_equal(nil, log.comment)
assert_equal('newCall', log.state)
assert_equal(true, log.done)
@ -237,6 +276,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.to)
assert_equal('out', log.direction)
assert_equal('user 1', log.from_comment)
assert_equal('CallerId Customer1', log.to_comment)
assert_equal('cancel', log.comment)
assert_equal('hangup', log.state)
assert_equal(true, log.done)
@ -251,6 +291,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.to)
assert_equal('out', log.direction)
assert_equal('user 1', log.from_comment)
assert_equal('CallerId Customer1', log.to_comment)
assert_equal(nil, log.comment)
assert_equal('newCall', log.state)
assert_equal(true, log.done)
@ -265,6 +306,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.to)
assert_equal('out', log.direction)
assert_equal('user 1', log.from_comment)
assert_equal('CallerId Customer1', log.to_comment)
assert_equal(nil, log.comment)
assert_equal('answer', log.state)
assert_equal(true, log.done)
@ -279,6 +321,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.to)
assert_equal('out', log.direction)
assert_equal('user 1', log.from_comment)
assert_equal('CallerId Customer1', log.to_comment)
assert_equal('normalClearing', log.comment)
assert_equal('hangup', log.state)
assert_equal(true, log.done)
@ -293,6 +336,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('user 1', log.to_comment)
assert_equal('CallerId Customer1', log.from_comment)
assert_equal(nil, log.comment)
assert_equal('newCall', log.state)
assert_equal(true, log.done)
@ -307,6 +351,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('user 1', log.to_comment)
assert_equal('CallerId Customer1', log.from_comment)
assert_equal(nil, log.comment)
assert_equal('answer', log.state)
assert_equal(true, log.done)
@ -321,6 +366,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('user 1', log.to_comment)
assert_equal('CallerId Customer1', log.from_comment)
assert_equal('normalClearing', log.comment)
assert_equal('hangup', log.state)
assert_equal(true, log.done)
@ -335,6 +381,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('user 1,user 2', log.to_comment)
assert_equal('CallerId Customer1', log.from_comment)
assert_equal(nil, log.comment)
assert_equal('newCall', log.state)
assert_equal(true, log.done)
@ -349,6 +396,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('voicemail', log.to_comment)
assert_equal('CallerId Customer1', log.from_comment)
assert_equal(nil, log.comment)
assert_equal('answer', log.state)
assert_equal(true, log.done)
@ -363,6 +411,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('voicemail', log.to_comment)
assert_equal('CallerId Customer1', log.from_comment)
assert_equal('normalClearing', log.comment)
assert_equal('hangup', log.state)
assert_equal(false, log.done)
@ -377,6 +426,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('user 1,user 2', log.to_comment)
assert_equal('CallerId Customer1', log.from_comment)
assert_equal(nil, log.comment)
assert_equal('newCall', log.state)
assert_equal(true, log.done)
@ -391,10 +441,26 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('user 1,user 2', log.to_comment)
assert_equal('CallerId Customer1', log.from_comment)
assert_equal('normalClearing', log.comment)
assert_equal('hangup', log.state)
assert_equal(false, log.done)
# inbound - IV - new call
params = 'event=newCall&direction=in&to=4930600000000&from=49999992222222&callId=1234567890-6&user%5B%5D=user+1,user+2'
post '/api/v1/sipgate/in', params
assert_response(200)
log = Cti::Log.find_by(call_id: '1234567890-6')
assert(log)
assert_equal('4930600000000', log.to)
assert_equal('49999992222222', log.from)
assert_equal('in', log.direction)
assert_equal('user 1,user 2', log.to_comment)
assert_equal('CallerId Customer3,CallerId Customer2', log.from_comment)
assert_equal(nil, log.comment)
assert_equal('newCall', log.state)
assert_equal(true, log.done)
# get caller list
get '/api/v1/cti/log'
assert_response(401)
@ -405,15 +471,20 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_response(200)
result = JSON.parse(@response.body)
assert_equal(result.class, Array)
assert_equal(5, result.count)
assert_equal('hangup', result[1]['state'])
assert_equal('4930777000000', result[1]['from'])
assert_equal('user 1', result[1]['from_comment'])
assert_equal('4912347114711', result[1]['to'])
assert_equal(nil, result[1]['to_comment'])
assert_equal('1234567890-2', result[1]['call_id'])
assert_equal('normalClearing', result[1]['comment'])
assert_equal('hangup', result[1]['state'])
assert_equal(6, result.count)
assert_equal('1234567890-6', result[0]['call_id'])
assert_equal('1234567890-5', result[1]['call_id'])
assert_equal('1234567890-4', result[2]['call_id'])
assert_equal('1234567890-3', result[3]['call_id'])
assert_equal('1234567890-2', result[4]['call_id'])
assert_equal('hangup', result[4]['state'])
assert_equal('4930777000000', result[4]['from'])
assert_equal('user 1', result[4]['from_comment'])
assert_equal('4912347114711', result[4]['to'])
assert_equal('CallerId Customer1', result[4]['to_comment'])
assert_equal('normalClearing', result[4]['comment'])
assert_equal('hangup', result[4]['state'])
assert_equal('1234567890-1', result[5]['call_id'])
end

View file

@ -0,0 +1,249 @@
# encoding: utf-8
require 'test_helper'
class CtiCallerIdTest < ActiveSupport::TestCase
test '1 parse possible phone numbers in text' do
text = "some text\ntest 123"
result = []
assert_equal(result, Cti::CallerId.parse_text(text))
text = '0049 1234 123456789'
result = ['491234123456789']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '022 1234567'
result = ['49221234567']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '0271233211'
result = ['49271233211']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '021-233-9123'
result = ['49212339123']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '09 123 32112'
result = ['49912332112']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '021 2331231'
result = ['49212331231']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '021 321123123'
result = ['4921321123123']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '622 32281'
result = ['4962232281']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '5754321'
result = ['495754321']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '092213212'
result = ['4992213212']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '(09)1234321'
result = ['4991234321']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '+49 30 53 00 00 000'
result = ['4930530000000']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '+49 160 0000000'
result = ['491600000000']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '+49 (0) 30 60 00 00 00-0'
result = ['4930600000000']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '0043 (0) 30 60 00 00 00-0'
result = ['4330600000000']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '0043 30 60 00 00 00-0'
result = ['4330600000000']
assert_equal(result, Cti::CallerId.parse_text(text))
text = '1-888-407-4747'
result = ['18884074747']
assert_equal(result, Cti::CallerId.parse_text(text))
text = 'Lorem ipsum dolor sit amet, consectetuer +49 (0) 30 60 00 00 00-0 adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel.'
result = ['4930600000000']
assert_equal(result, Cti::CallerId.parse_text(text))
text = "GS Oberalteich\nTelefon 09422 1000 Telefax 09422 805000\nE-Mail: "
result = %w(4994221000 499422805000)
assert_equal(result, Cti::CallerId.parse_text(text))
text = 'Tel +41 81 288 63 93 / +41 76 346 72 14 ...'
result = %w(41812886393 41763467214)
assert_equal(result, Cti::CallerId.parse_text(text))
text = "P: +1 (949) 431 0000\nF: +1 (949) 431 0001\nW: http://znuny"
result = %w(19494310000 19494310001)
assert_equal(result, Cti::CallerId.parse_text(text))
end
test '2 lookups' do
Ticket.destroy_all
Cti::CallerId.destroy_all
agent1 = User.create_or_update(
login: 'ticket-caller_id-agent1@example.com',
firstname: 'CallerId',
lastname: 'Agent1',
email: 'ticket-caller_id-agent1@example.com',
password: 'agentpw',
active: true,
phone: '+49 1111 222222',
fax: '+49 1111 222223',
mobile: '+49 1111 222223',
note: 'Phone at home: +49 1111 222224',
updated_by_id: 1,
created_by_id: 1,
)
agent2 = User.create_or_update(
login: 'ticket-caller_id-agent2@example.com',
firstname: 'CallerId',
lastname: 'Agent2',
email: 'ticket-caller_id-agent2@example.com',
password: 'agentpw',
phone: '+49 2222 222222',
note: 'Phone at home: <b>+49 2222 222224</b>',
active: true,
updated_by_id: 1,
created_by_id: 1,
)
agent3 = User.create_or_update(
login: 'ticket-caller_id-agent3@example.com',
firstname: 'CallerId',
lastname: 'Agent3',
email: 'ticket-caller_id-agent3@example.com',
password: 'agentpw',
phone: '+49 2222 222222',
active: true,
updated_by_id: 1,
created_by_id: 1,
)
customer1 = User.create_or_update(
login: 'ticket-caller_id-customer1@example.com',
firstname: 'CallerId',
lastname: 'Customer1',
email: 'ticket-caller_id-customer1@example.com',
password: 'customerpw',
active: true,
updated_by_id: 1,
created_by_id: 1,
)
Cti::CallerId.rebuild
caller_ids = Cti::CallerId.lookup('491111222277')
assert_equal(0, caller_ids.count)
caller_ids = Cti::CallerId.lookup('491111222223')
assert_equal(1, caller_ids.count)
assert_equal(agent1.id, caller_ids[0].user_id)
assert_equal('known', caller_ids[0].level)
caller_ids = Cti::CallerId.lookup('492222222222')
assert_equal(2, caller_ids.count)
assert_equal(agent3.id, caller_ids[0].user_id)
assert_equal('known', caller_ids[0].level)
assert_equal(agent2.id, caller_ids[1].user_id)
assert_equal('known', caller_ids[1].level)
# create ticket in group
ticket1 = Ticket.create(
title: 'some caller id test 1',
group: Group.lookup(name: 'Users'),
customer: customer1,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: agent1.id,
created_by_id: agent1.id,
)
article1 = Ticket::Article.create(
ticket_id: ticket1.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: "some message\nFon (GEL): +49 111 366-1111 Mi-Fr
Fon (LIN): +49 222 6112222 Mo-Di
Mob: +49 333 8362222",
internal: false,
sender: Ticket::Article::Sender.where(name: 'Customer').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: customer1.id,
created_by_id: customer1.id,
)
assert(ticket1)
# create ticket in group
ticket2 = Ticket.create(
title: 'some caller id test 2',
group: Group.lookup(name: 'Users'),
customer: customer1,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: agent1.id,
created_by_id: agent1.id,
)
article2 = Ticket::Article.create(
ticket_id: ticket2.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: "some message\nFon (GEL): +49 111 111-1111 Mi-Fr
Fon (LIN): +49 222 1112222 Mo-Di
Mob: +49 333 1112222",
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: agent1.id,
created_by_id: agent1.id,
)
assert(ticket2)
Cti::CallerId.rebuild
caller_ids = Cti::CallerId.lookup('491111222277')
assert_equal(0, caller_ids.count)
caller_ids = Cti::CallerId.lookup('491111222223')
assert_equal(1, caller_ids.count)
assert_equal(agent1.id, caller_ids[0].user_id)
assert_equal('known', caller_ids[0].level)
caller_ids = Cti::CallerId.lookup('492222222222')
assert_equal(2, caller_ids.count)
assert_equal(agent3.id, caller_ids[0].user_id)
assert_equal('known', caller_ids[0].level)
assert_equal(agent2.id, caller_ids[1].user_id)
assert_equal('known', caller_ids[1].level)
caller_ids = Cti::CallerId.lookup('492226112222')
assert_equal(1, caller_ids.count)
assert_equal(customer1.id, caller_ids[0].user_id)
assert_equal('maybe', caller_ids[0].level)
caller_ids = Cti::CallerId.lookup('492221112222')
assert_equal(0, caller_ids.count)
end
end