You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

271 lines
9.3 KiB

#!/usr/bin/python
# -*- encoding: utf-8 -*-
# (c) 2010-2014 Alexis de Lattre <alexis.delattre@akretion.com>
# (c) 2014-2016 Trever L. Adams <trever.adams@gmail.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
"""
Name lookup in OpenERP for incoming and outgoing calls with an
FreeSWITCH system
This script is designed to be used as an WSGI script on the same server
as OpenERP/Odoo.
Relevant documentation"
https://wiki.freeswitch.org/wiki/Mod_cidlookup
https://freeswitch.org/confluence/display/FREESWITCH/mod_cidlookup
Apache Configuration:
Listen 8080
<VirtualHost *:8080>
WSGIScriptAlias /wscgi-bin/ /var/www/wscgi-bin/
ServerAdmin webmaster@localhost
# ...
</VirtualHost>
FreeSWITCH mod_cidlookup configuration:
<configuration name="cidlookup.conf" description="cidlookup Configuration">
<settings>
<param name="url" value="https://openerp.localdomain/wscgi-bin/get_caller_name.py?name=${caller_id_name}&number=${caller_id_number}&notify=1004,1007"/>
<param name="cache" value="false"/>
</settings>
</configuration>
If you want geoloc, add &geoloc=true to the end (it is going to be slow).
Notify should be the internal number of the called parties. This should be
comma (,) delimited, not :_: delimited. It is up to you to format the
extensions list appropriately. The persons who are at extensions in the
notify list will receive a poppup if so configured and if they are logged in.
The notify list actually shouldn't be in the cidlookup.conf, but should be
used when doing notify (in an on answer hook for example).
From the dialplan, do something like this <action application="set"
data="effective_caller_id_name=${cidlookup(${caller_id_number})}"/>.
If you are not using FreeTDM modules for the incoming line, doing
<action application="pre_answer"/> before the cidlookup is a VERY good idea.
If you are wishing to set the callee name, <action application="export"
data="callee_id_name=${cidlookup($1)}" />
Of course, you should adapt this example to the FreeSWITCH server you are
using. This is especially true of the options variable in the application
function. The user (by number id, not name) that is used to connect to
OpenERP/Odoo must have "Phone CallerID" access rights. That may also require
"Technical Features" rights.
"""
__author__ = "Trever Adams <trever.adams@gmail.com>"
__date__ = "August 2016"
__version__ = "0.5"
import sys
sys.path.append('.')
import xmlrpclib
from cgi import parse_qs, escape
import unicodedata
# Name that will be displayed if there is no match
# and no geolocalisation
not_found_name = "Not in OpenERP"
# Name used if name and number are both empty
unknown_name = "unknown"
# Set to 1 for debugging output
verbose = 0
def stdout_write(string):
'''Wrapper on sys.stdout.write'''
if verbose == 1:
sys.stdout.write(string)
sys.stdout.flush()
return True
def stderr_write(string):
'''Wrapper on sys.stderr.write'''
if verbose == 1:
sys.stderr.write(string)
sys.stderr.flush()
return True
def geolocate_phone_number(number, my_country_code, lang):
import phonenumbers
from phonenumbers import geocoder
res = ''
phonenum = phonenumbers.parse(number, my_country_code.upper())
city = phonenumbers.geocoder.description_for_number(phonenum, lang.lower())
country_code = phonenumbers.region_code_for_number(phonenum)
# We don't display the country name when it's my own country
if country_code == my_country_code.upper():
if city:
res = city
else:
# Convert country code to country name
country = phonenumbers.geocoder._region_display_name(
country_code, lang.lower())
if country and city:
res = country + ' ' + city
elif country and not city:
res = country
return res
def convert_to_ascii(my_unicode):
'''Convert to ascii, with clever management of accents (é -> e, è -> e)'''
if isinstance(my_unicode, unicode):
my_unicode_with_ascii_chars_only = ''.join((
char for char in unicodedata.normalize('NFD', my_unicode)
if unicodedata.category(char) != 'Mn'))
return str(my_unicode_with_ascii_chars_only)
# If the argument is already of string type, return it with the same value
elif isinstance(my_unicode, str):
return my_unicode
else:
return False
def main(name, phone_number, options):
# If we already have a "True" caller ID name
# i.e. not just digits, but a real name, then we don't try to
# connect to OpenERP or geoloc, we just keep it
if (
name and
not name.isdigit() and
name.lower()
not in ['freeswitch', 'unknown', 'anonymous', 'unavailable']):
stdout_write('Incoming CallerID name is %s\n' % name)
stdout_write('As it is a real name, we do not change it\n')
return name
if not isinstance(phone_number, str):
stdout_write('Phone number is empty\n')
exit(0)
# Match for particular cases and anonymous phone calls
# To test anonymous call in France, dial 3651 + number
if not phone_number.isdigit():
stdout_write(
'Phone number (%s) is not a digit\n' % phone_number)
exit(0)
stdout_write('Phone number = %s\n' % phone_number)
res = name
# Yes, this script can be used without "-s openerp_server" !
if options["server"]:
if options["ssl"]:
stdout_write(
'Starting XML-RPC secure request on OpenERP %s:%s\n'
% (options["server"], str(options["port"])))
protocol = 'https'
else:
stdout_write(
'Starting clear XML-RPC request on OpenERP %s:%s\n'
% (options["server"], str(options["port"])))
protocol = 'http'
sock = xmlrpclib.ServerProxy(
'%s://%s:%s/xmlrpc/object'
% (protocol, options["server"], str(options["port"])))
try:
if options["notify"]:
res = sock.execute(
options["database"], options["user"], options["password"],
'phone.common', 'incall_notify_by_extension',
phone_number, options["notify"])
stdout_write('Calling incall_notify_by_extension\n')
else:
res = sock.execute(
options["database"], options["user"], options["password"],
'phone.common', 'get_name_from_phone_number',
phone_number)
stdout_write('Calling get_name_from_phone_number\n')
stdout_write('End of XML-RPC request on OpenERP\n')
if not res:
stdout_write('Phone number not found in OpenERP\n')
except:
stdout_write('Could not connect to OpenERP %s\n'
% options["database"])
res = False
# To simulate a long execution of the XML-RPC request
# import time
# time.sleep(5)
# Function to limit the size of the name
if res:
if len(res) > options["max_size"]:
res = res[0:options["max_size"]]
elif options["geoloc"]:
# if the number is not found in OpenERP, we try to geolocate
stdout_write(
'Trying to geolocate with country %s and lang %s\n'
% (options["country"], options["lang"]))
res = geolocate_phone_number(
phone_number, options["country"], options["lang"])
else:
# if the number is not found in OpenERP and geoloc is off,
# we put 'not_found_name' as Name
res = not_found_name
# All SIP phones should support UTF-8...
# but in case you have analog phones over TDM
# or buggy phones, you should set options["ascii"] to True below
if options["ascii"]:
res = convert_to_ascii(res)
stdout_write('Name = %s\n' % res)
return res
def application(environ, start_response):
output = ""
name = False
options = {}
options["server"] = "127.0.0.1"
options["port"] = 8069
options["database"] = "test"
options["user"] = 1
options["password"] = "admin"
options["geoloc"] = True
options["country"] = "US"
options["lang"] = "en"
options["ssl"] = False
options["ascii"] = False
options["max_size"] = 40
parameters = parse_qs(environ.get('QUERY_STRING', ''))
if 'number' in parameters:
number = escape(parameters['number'][0])
if 'name' in parameters:
name = escape(parameters['name'][0])
else:
name = unknown_name
if 'notify' in parameters:
options["notify"] = []
for item in parameters['notify'][0].split(','):
options["notify"].append(escape(item))
stdout_write(
'Trying to notify %s\n' % options["notify"])
else:
options["notify"] = False
if 'geoloc' in parameters:
options["geoloc"] = True
else:
options["geoloc"] = False
try:
output = main(name if name else False, number, options)
except:
output = name
output = output.encode('utf-8')
status = '200 OK'
response_headers = [('Content-type', 'text/plain; charset=utf-8'),
('Content-Length', str(len(output)))]
start_response(status, response_headers)
return [output]