diff --git a/connector_voicent/README.rst b/connector_voicent/README.rst new file mode 100644 index 0000000..2bcf14d --- /dev/null +++ b/connector_voicent/README.rst @@ -0,0 +1,13 @@ +**Connector Voicent** + +*This module allows you to connect Odoo with Voicent (https://www.voicent.com).* + +* **DESCRIPTION.rst** +* **USAGE.rst** +* **CONTRIBUTORS.rst** +* INSTALL.rst +* CONFIGURE.rst +* DEVELOP.rst +* ROADMAP.rst +* HISTORY.rst +* CREDITS.rst diff --git a/connector_voicent/__init__.py b/connector_voicent/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/connector_voicent/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/connector_voicent/__manifest__.py b/connector_voicent/__manifest__.py new file mode 100644 index 0000000..716fc65 --- /dev/null +++ b/connector_voicent/__manifest__.py @@ -0,0 +1,30 @@ +# @TODO: To use the right License. +# @TODO: Description, Website, Contributros..etc. +{ + 'name': 'Voicent Connector', + 'version': '12.0.1.0.0', + 'category': 'Phone', + 'license': 'AGPL-3', + 'summary': 'Automate phone calls based on stage changes', + 'development_status': 'Stable', + 'maintainers': [ + 'younessmaafi', + 'max3903', + ], + 'author': 'Maxime Chambreuil, Youness MAAFI,' + ' Odoo Community Association (OCA)', + 'website': 'http://opensourceintegrators.com', + 'depends': [ + 'base', + ], + 'data': [ + # Security + 'security/ir.model.access.csv', + # Views + 'view/res_partner.xml', + 'view/backend_voicent.xml', + ], + 'demo': [], + 'installable': True, + 'auto_install': False, +} diff --git a/connector_voicent/examples/prototype.py b/connector_voicent/examples/prototype.py new file mode 100644 index 0000000..1f6a9c2 --- /dev/null +++ b/connector_voicent/examples/prototype.py @@ -0,0 +1,32 @@ +# Copyright (C) 2019 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +# Simple message +import voicent + + +v = voicent.Voicent() +phoneno = "6024275632" +reqid = v.callText(phoneno, "Hello, This is a test of the autodialer.", "1") +status = v.callStatus(reqid) + +# Using Campaign in 2 steps +v = voicent.Voicent() +filepath = "/home/mchambreuil/odoo/pvm/voicent.csv" +listname = "Test" +leadsrc_id = v.importCampaign(listname, filepath) +res = v.runCampaign(listname) +v.checkStatus(res['leadsrc_id']) + +# Using Campaign in 1 step with TTS +v = voicent.Voicent() +filepath = "/home/mchambreuil/odoo/pvm/voicent.csv" +res = v.importAndRunCampaign(filepath, "tts", "Hello, This is a test. Bye") +status = v.checkStatus(res['camp_id']) + +# Using Campaign in 1 step with Template +v = voicent.Voicent() +filepath = "/home/mchambreuil/odoo/pvm/voicent.csv" +res = v.importAndRunCampaign(filepath, "template", "Test") +status = v.checkStatus(res['camp_id']) diff --git a/connector_voicent/examples/voicent.csv b/connector_voicent/examples/voicent.csv new file mode 100644 index 0000000..3353237 --- /dev/null +++ b/connector_voicent/examples/voicent.csv @@ -0,0 +1,2 @@ +Name,Phone +Maxime Chambreuil,6024275632 diff --git a/connector_voicent/examples/voicent.py b/connector_voicent/examples/voicent.py new file mode 100644 index 0000000..d0c8015 --- /dev/null +++ b/connector_voicent/examples/voicent.py @@ -0,0 +1,187 @@ +# Copyright (C) 2018 Voicent +# Copyright (C) 2019 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# +# Documentation available at https://voicent.com/developer/docs/camp-api/ + +import ntpath +import requests +import ast + + +class Voicent(): + + def __init__(self, host="localhost", port="8155"): + self.host_ = host + self.port_ = port + + def postToGateway(self, urlstr, params, files=None): + url = "http://" + self.host_ + ":" + self.port_ + urlstr + res = requests.post(url, params, files=files) + return res.text + + def getReqId(self, rcstr): + index1 = rcstr.find("[ReqId=") + if (index1 == -1): + return "" + index1 += 7 + index2 = rcstr.find("]", index1) + if (index2 == -1): + return "" + return rcstr[index1:index2] + + def callText(self, phoneno, text, selfdelete): + urlstr = "/ocall/callreqHandler.jsp" + params = { + 'info': 'simple text call', + 'phoneno': phoneno, + 'firstocc': 10, + 'txt': text, + 'selfdelete': selfdelete + } + res = self.postToGateway(urlstr, params) + return self.getReqId(res) + + def callAudio(self, phoneno, filename, selfdelete): + urlstr = "/ocall/callreqHandler.jsp" + params = { + 'info': 'simple audio call', + 'phoneno': phoneno, + 'firstocc': 10, + 'audiofile': filename, + 'selfdelete': selfdelete + } + res = self.postToGateway(urlstr, params) + return self.getReqId(res) + + def callIvr(self, phoneno, appname, selfdelete): + urlstr = "/ocall/callreqHandler.jsp" + params = { + 'info': 'simple text call', + 'phoneno': phoneno, + 'firstocc': 10, + 'startapp': appname, + 'selfdelete': selfdelete + } + res = self.postToGateway(urlstr, params) + return self.getReqId(res) + + def callStatus(self, reqid): + urlstr = "/ocall/callstatusHandler.jsp" + params = {'reqid': reqid} + res = self.postToGateway(urlstr, params) + return self.getReqId(res) + + def callRemove(self, reqId): + urlstr = "/ocall/callremoveHandler.jsp" + params = {'reqid': reqId} + res = self.postToGateway(urlstr, params) + return self.getReqId(res) + + def callTillConfirm(self, vcastexe, vocfile, wavfile, ccode): + urlstr = "/ocall/callreqHandler.jsp" + cmdline = "\"" + cmdline += vocfile + cmdline += "\"" + cmdline += " -startnow" + cmdline += " -confirmcode " + cmdline += ccode + cmdline += " -wavfile " + cmdline += "\"" + cmdline += wavfile + cmdline += "\"" + + params = { + 'info': 'Simple Call till Confirm', + 'phoneno': '1111111', + 'firstocc': 10, + 'selfdelete': 0, + 'startexec': vcastexe, + 'cmdline': cmdline + } + res = self.postToGateway(urlstr, params) + return self.getReqId(res) + + ################ + # CAMPAIGN API # + ################ + + def importCampaign(self, listname, filepath): + urlstr = "/ocall/campapi" + params = { + 'action': 'import', + 'importfile': ntpath.basename(filepath), + 'importfilepath': filepath, + 'profile': 'Test', + 'mod': 'cus', + 'fieldsep': ',', + 'row1': 1, + 'mergeopt': 'empty', + 'leadsrcname': listname + } + files = { + 'file': (ntpath.basename(filepath), open(filepath, 'rb')) + } + res = self.postToGateway(urlstr, params, files) + return ast.literal_eval(res) + + def runCampaign(self, listname): + urlstr = "/ocall/campapi" + params = { + 'action': 'bbp', + 'CAMP_NAME': 'Test', + 'listname': listname, + 'phonecols': 'Phone', + 'lines': '4', + 'calldisps': '', + 'callerid': '+18884728568', + } + res = self.postToGateway(urlstr, params) + return ast.literal_eval(res) + + def importAndRunCampaign(self, filepath, msgtype, msginfo): + urlstr = "/ocall/campapi" + params = { + 'action': 'bbp', + # Parameters for importing the campaign + 'importfile': ntpath.basename(filepath), + 'importfilepath': filepath, + # 'profile': 'Test', + 'mod': 'cus', + 'row1': 1, + 'leadsrcname': 'Test', + # Parameters for running the campaign + 'CAMP_NAME': 'Test', + 'phonecols': 'Phone', + 'lines': '4', + 'calldisps': '', + 'callerid': '+18884728568', + # Parameters for Autodialer + 'msgtype': msgtype, + 'msginfo': msginfo, + } + files = { + 'file': (ntpath.basename(filepath), open(filepath, 'rb')) + } + res = self.postToGateway(urlstr, params, files) + return ast.literal_eval(res) + + def checkStatus(self, leadsrc_id): + urlstr = "/ocall/campapi" + params = { + 'action': 'campstats', + 'leadsrc_id': leadsrc_id + } + res = self.postToGateway(urlstr, params) + return ast.literal_eval(res) + + def exportResult(self, camp_id, filename, extracols): + urlstr = "/ocall/campapi" + params = { + 'action': 'exportcamp', + 'camp_id_id': camp_id, + 'f': filename, + 'extracols': extracols + } + res = self.postToGateway(urlstr, params) + return ast.literal_eval(res) diff --git a/connector_voicent/models/__init__.py b/connector_voicent/models/__init__.py new file mode 100644 index 0000000..c0c25d8 --- /dev/null +++ b/connector_voicent/models/__init__.py @@ -0,0 +1,4 @@ +from . import res_partner +from . import backend_voicent +from . import backend_voicent_call_line +from . import backend_voicent_time_line diff --git a/connector_voicent/models/backend_voicent.py b/connector_voicent/models/backend_voicent.py new file mode 100644 index 0000000..5952cee --- /dev/null +++ b/connector_voicent/models/backend_voicent.py @@ -0,0 +1,33 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class BackendVoicent(models.Model): + _name = 'backend.voicent' + _description = 'Voicent Backend' + _rec_name = 'host' + + host = fields.Char( + string=u'Host', + required=True, + ) + port = fields.Integer( + string=u'Port', + required=True, + ) + next_call = fields.Datetime( + string=u'Next Call', + copy=False, + default=lambda self: fields.Datetime.now(), + ) + call_line_ids = fields.One2many( + string=u'Call Lines', + comodel_name='backend.voicent.call.line', + inverse_name='backend_id', + ) + time_line_ids = fields.One2many( + string=u'Call Times', + comodel_name='backend.voicent.time.line', + inverse_name='backend_id', + ) diff --git a/connector_voicent/models/backend_voicent_call_line.py b/connector_voicent/models/backend_voicent_call_line.py new file mode 100644 index 0000000..9006b93 --- /dev/null +++ b/connector_voicent/models/backend_voicent_call_line.py @@ -0,0 +1,25 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class BackendVoicentCallLine(models.Model): + _name = 'backend.voicent.call.line' + _description = 'Voicent Backend Call Line' + + name = fields.Char( + string=u'Name', + required=True, + ) + applies_on = fields.Selection( + string=u'Applies on', + selection=[], + ) + voicent_app = fields.Char( + string=u'Voicent App', + ) + backend_id = fields.Many2one( + string=u'Backend', + comodel_name='backend.voicent', + ondelete='set null', + ) diff --git a/connector_voicent/models/backend_voicent_time_line.py b/connector_voicent/models/backend_voicent_time_line.py new file mode 100644 index 0000000..3335849 --- /dev/null +++ b/connector_voicent/models/backend_voicent_time_line.py @@ -0,0 +1,23 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class BackendVoicentTimeLine(models.Model): + _name = 'backend.voicent.time.line' + _description = 'Voicent Backend Time Line' + + name = fields.Char( + string=u'Name', + required=True, + ) + time = fields.Datetime( + string=u'Time', + copy=False, + default=lambda self: fields.Datetime.now(), + ) + backend_id = fields.Many2one( + string=u'Backend', + comodel_name='backend.voicent', + ondelete='set null', + ) diff --git a/connector_voicent/models/res_partner.py b/connector_voicent/models/res_partner.py new file mode 100644 index 0000000..0315fe1 --- /dev/null +++ b/connector_voicent/models/res_partner.py @@ -0,0 +1,11 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + can_call = fields.Boolean( + string=u'Accepts Calls', + ) diff --git a/connector_voicent/readme/CONFIGURE.rst b/connector_voicent/readme/CONFIGURE.rst new file mode 100644 index 0000000..7583d6e --- /dev/null +++ b/connector_voicent/readme/CONFIGURE.rst @@ -0,0 +1 @@ +There is no specific configuration procedure for this module. diff --git a/connector_voicent/readme/CONTRIBUTORS.rst b/connector_voicent/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..0f79ba1 --- /dev/null +++ b/connector_voicent/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Maxime Chambreuil +* Youness MAAFI diff --git a/connector_voicent/readme/CREDITS.rst b/connector_voicent/readme/CREDITS.rst new file mode 100644 index 0000000..e69de29 diff --git a/connector_voicent/readme/DESCRIPTION.rst b/connector_voicent/readme/DESCRIPTION.rst new file mode 100644 index 0000000..a14dec3 --- /dev/null +++ b/connector_voicent/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module allows you to connect Odoo with Voicent (https://www.voicent.com), +and is meant to be extended to integrate Odoo records and processes with phone calls made by Voicent. diff --git a/connector_voicent/readme/HISTORY.rst b/connector_voicent/readme/HISTORY.rst new file mode 100644 index 0000000..e69de29 diff --git a/connector_voicent/readme/INSTALL.rst b/connector_voicent/readme/INSTALL.rst new file mode 100644 index 0000000..9d94322 --- /dev/null +++ b/connector_voicent/readme/INSTALL.rst @@ -0,0 +1 @@ +There is no specific installation procedure for this module. diff --git a/connector_voicent/readme/ROADMAP.rst b/connector_voicent/readme/ROADMAP.rst new file mode 100644 index 0000000..e69de29 diff --git a/connector_voicent/readme/USAGE.rst b/connector_voicent/readme/USAGE.rst new file mode 100644 index 0000000..5272d0a --- /dev/null +++ b/connector_voicent/readme/USAGE.rst @@ -0,0 +1,4 @@ +#. Go to Connectors > Backends > Voicent Backends +#. Create a new Voicent Backend with the host and port +#. Create Call Lines to determine when (which stage in the process) calls are added to the queue +#. Create Time Line to determine when (what time) calls are made diff --git a/connector_voicent/security/ir.model.access.csv b/connector_voicent/security/ir.model.access.csv new file mode 100644 index 0000000..a1c6cb8 --- /dev/null +++ b/connector_voicent/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_backend_voicent,access_backend_voicent,model_backend_voicent,base.group_user,1,1,1,0 +access_backend_voicent_call_line,access_backend_voicent_call_line,model_backend_voicent_call_line,base.group_user,1,1,1,0 +access_backend_voicent_time_line,access_backend_voicent_time_line,model_backend_voicent_time_line,base.group_user,1,0,1,0 diff --git a/connector_voicent/static/description/icon.png b/connector_voicent/static/description/icon.png new file mode 100644 index 0000000..3a0328b Binary files /dev/null and b/connector_voicent/static/description/icon.png differ diff --git a/connector_voicent/view/backend_voicent.xml b/connector_voicent/view/backend_voicent.xml new file mode 100644 index 0000000..759e200 --- /dev/null +++ b/connector_voicent/view/backend_voicent.xml @@ -0,0 +1,94 @@ + + + + + view.backend.voicent.tree + backend.voicent + primary + + + + + + + + + + + view.backend.voicent.form + backend.voicent + primary + +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + view.backend.voicent.search + backend.voicent + primary + + + + + + + + + + ir.actions.act_window + Voicent Backend + backend.voicent + tree,form + form + + +

+ Click to add new Voicent Backend +

+
+
+ + + Connectors + + + + + Backends + + + + + + Voicent Backends + + + + + +
diff --git a/connector_voicent/view/res_partner.xml b/connector_voicent/view/res_partner.xml new file mode 100644 index 0000000..f359f21 --- /dev/null +++ b/connector_voicent/view/res_partner.xml @@ -0,0 +1,15 @@ + + + + view.res.partner.form + res.partner + + + + + + + + + +