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.

167 lines
6.8 KiB

  1. #! /usr/bin/python
  2. # -*- encoding: utf-8 -*-
  3. """
  4. CallerID name lookup in OpenERP for Asterisk IPBX
  5. When executed from the dialplan on an incoming phone call, it will lookup in
  6. OpenERP's partner addresses, and, if it finds the phone number, it will get the
  7. corresponding name of the person and use this name as CallerID name for the incoming call.
  8. Requires the "asterisk_click2dial" module (available in the extra-addons)
  9. on OpenERP version >= 5
  10. This script is designed to be used as an AGI on an Asterisk IPBX...
  11. BUT I advise you to use a wrapper around this script to control the
  12. execution time. Why ? Because if the script takes too much time to
  13. execute or get stucks (in the XML-RPC request for example), then the
  14. incoming phone call will also get stucks and you will miss a call !
  15. The most simple solution I found is to use the "timeout" shell command to
  16. call this script, for example :
  17. # timeout 1s get_cid_name.py <OPTIONS>
  18. See my sample wrapper "get_cid_name_timeout.sh"
  19. Asterisk dialplan example :
  20. [from-extern]
  21. exten => _0141981242,1,AGI(/usr/local/bin/get_cid_name_timeout.sh)
  22. exten => _0141981242,n,Dial(SIP/10, 30)
  23. exten => _0141981242,n,Answer()
  24. exten => _0141981242,n,Voicemail(10@default,u)
  25. exten => _0141981242,n,Hangup()
  26. """
  27. __author__ = "Alexis de Lattre <alexis.delattre@akretion.com>"
  28. __date__ = "December 2010"
  29. __version__ = "0.1"
  30. # Copyright (C) 2010 Alexis de Lattre <alexis.delattre@akretion.com>
  31. #
  32. # This program is free software: you can redistribute it and/or modify
  33. # it under the terms of the GNU Affero General Public License as
  34. # published by the Free Software Foundation, either version 3 of the
  35. # License, or (at your option) any later version.
  36. #
  37. # This program is distributed in the hope that it will be useful,
  38. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  39. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  40. # GNU Affero General Public License for more details.
  41. #
  42. # You should have received a copy of the GNU Affero General Public License
  43. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  44. import xmlrpclib
  45. import sys
  46. from optparse import OptionParser
  47. # CID Name that will be displayed if there is no match in res.partner.address
  48. default_cid_name = "Not in OpenERP"
  49. # Define command line options
  50. option_server = {'names': ('-s', '--server'), 'dest': 'server', 'type': 'string', 'help': 'DNS or IP address of the OpenERP server', 'action': 'store', 'default':'localhost'}
  51. option_port = {'names': ('-p', '--port'), 'dest': 'port', 'type': 'int', 'help': "Port of OpenERP's XML-RPC interface", 'action': 'store', 'default': 8069}
  52. option_database = {'names': ('-d', '--database'), 'dest': 'database', 'type': 'string', 'help': "OpenERP database name", 'action': 'store', 'default': 'openerp'}
  53. option_user = {'names': ('-u', '--user-id'), 'dest': 'user', 'type': 'int', 'help': "OpenERP user ID to use when connecting to OpenERP", 'action': 'store', 'default': 1}
  54. option_password = {'names': ('-w', '--password'), 'dest': 'password', 'type': 'string', 'help': "Password of the OpenERP user", 'action': 'store', 'default': 'admin'}
  55. options = [option_server, option_port, option_database, option_user, option_password]
  56. def reformat_phone_number_before_query_openerp(number):
  57. '''We match only on the end of the phone number'''
  58. if len(number) >= 9:
  59. return number[-9:len(number)] # Take 9 last numbers
  60. else:
  61. return number
  62. def convert_to_ascii(my_unicode):
  63. '''Convert to ascii, with clever management of accents (é -> e, è -> e)'''
  64. import unicodedata
  65. if isinstance(my_unicode, unicode):
  66. my_unicode_with_ascii_chars_only = ''.join((char for char in unicodedata.normalize('NFD', my_unicode) if unicodedata.category(char) != 'Mn'))
  67. return str(my_unicode_with_ascii_chars_only)
  68. # If the argument is already of string type, we return it with the same value
  69. elif isinstance(my_unicode, str):
  70. return my_unicode
  71. else:
  72. return False
  73. def main(options, arguments):
  74. #print 'options = %s' % options
  75. #print 'arguments = %s' % arguments
  76. # AGI passes parameters to the script on standard input
  77. stdinput = {}
  78. while 1:
  79. input_line = sys.stdin.readline().strip()
  80. if input_line == '':
  81. break
  82. variable, value = input_line.split(':') # TODO à protéger !
  83. if variable[:4] != 'agi_': # All AGI parameters start with 'agi_'
  84. sys.stderr.write("Bad stdin variable : %s\n" % variable)
  85. continue
  86. variable = variable.strip()
  87. value = value.strip()
  88. if variable != '':
  89. stdinput[variable] = value
  90. sys.stderr.write("Full AGI environnement :\n")
  91. for variable in stdinput.keys():
  92. sys.stderr.write("%s = %s\n" % (variable, stdinput[variable]))
  93. input_cid_number = stdinput.get('agi_callerid', False)
  94. if not isinstance(input_cid_number, str):
  95. exit(0)
  96. # Match for particular cases and anonymous phone calls
  97. # To test anonymous call in France, dial 3651 + number
  98. if not input_cid_number.isdigit():
  99. sys.stdout.write('VERBOSE "CallerID number (%s) is not a digit"\n' % input_cid_number)
  100. sys.stdout.flush()
  101. exit(0)
  102. sys.stdout.write('VERBOSE "CallerID number = %s"\n' % input_cid_number)
  103. query_number = reformat_phone_number_before_query_openerp(input_cid_number)
  104. sys.stderr.write("phone number sent to OpenERP = %s\n" % query_number)
  105. sys.stdout.write('VERBOSE "Starting XML-RPC request on OpenERP %s:%s"\n' % (options.server, str(options.port)))
  106. sys.stdout.flush()
  107. sock = xmlrpclib.ServerProxy('http://%s:%s/xmlrpc/object' % (options.server, str(options.port)))
  108. res = sock.execute(options.database, options.user, options.password, 'res.partner.address', 'get_name_from_phone_number', query_number)
  109. # To simulate a long execution of the XML-RPC request
  110. #import time
  111. #time.sleep(5)
  112. sys.stdout.write('VERBOSE "End of XML-RPC request on OpenERP"\n')
  113. sys.stdout.flush()
  114. # Function to limit the size of the CID name to 40 chars
  115. if res:
  116. if len(res) > 40:
  117. res = res[0:40]
  118. else:
  119. # if the number is not found in OpenERP, we put 'default_cid_name' as CID Name
  120. res = default_cid_name
  121. # I am not sure how SIP and IP phones manage non-ASCII caracters, so I prefer
  122. # to replace all non-ASCII caracters in the name
  123. res_ascii = convert_to_ascii(res)
  124. sys.stdout.write('VERBOSE "CallerID Name = %s"\n' % res_ascii)
  125. sys.stdout.flush()
  126. sys.stdout.write('SET CALLERID "%s" <%s>\n' % (res_ascii, input_cid_number))
  127. sys.stdout.flush()
  128. if __name__ == '__main__':
  129. parser = OptionParser()
  130. for option in options:
  131. param = option['names']
  132. del option['names']
  133. parser.add_option(*param, **option)
  134. options, arguments = parser.parse_args()
  135. sys.argv[:] = arguments
  136. main(options, arguments)