odoo-addon-mail-gateway-whatsapp 16.0.1.0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odoo/addons/mail_gateway_whatsapp/README.rst +127 -0
- odoo/addons/mail_gateway_whatsapp/__init__.py +4 -0
- odoo/addons/mail_gateway_whatsapp/__manifest__.py +28 -0
- odoo/addons/mail_gateway_whatsapp/i18n/mail_gateway_whatsapp.pot +537 -0
- odoo/addons/mail_gateway_whatsapp/models/__init__.py +5 -0
- odoo/addons/mail_gateway_whatsapp/models/mail_channel.py +25 -0
- odoo/addons/mail_gateway_whatsapp/models/mail_gateway.py +15 -0
- odoo/addons/mail_gateway_whatsapp/models/mail_gateway_whatsapp.py +383 -0
- odoo/addons/mail_gateway_whatsapp/models/mail_thread.py +65 -0
- odoo/addons/mail_gateway_whatsapp/models/res_partner.py +20 -0
- odoo/addons/mail_gateway_whatsapp/readme/CONFIGURE.rst +30 -0
- odoo/addons/mail_gateway_whatsapp/readme/CONTRIBUTORS.rst +2 -0
- odoo/addons/mail_gateway_whatsapp/readme/CREDITS.rst +1 -0
- odoo/addons/mail_gateway_whatsapp/readme/DESCRIPTION.rst +4 -0
- odoo/addons/mail_gateway_whatsapp/readme/USAGE.rst +3 -0
- odoo/addons/mail_gateway_whatsapp/security/ir.model.access.csv +2 -0
- odoo/addons/mail_gateway_whatsapp/static/description/icon.png +0 -0
- odoo/addons/mail_gateway_whatsapp/static/description/icon.svg +48 -0
- odoo/addons/mail_gateway_whatsapp/static/description/index.html +478 -0
- odoo/addons/mail_gateway_whatsapp/static/src/components/message/message.xml +11 -0
- odoo/addons/mail_gateway_whatsapp/static/src/components/phone_field/phone_field.esm.js +25 -0
- odoo/addons/mail_gateway_whatsapp/static/src/components/phone_field/phone_field.xml +20 -0
- odoo/addons/mail_gateway_whatsapp/static/src/components/send_whatsapp_button/send_whatsapp_button.esm.js +44 -0
- odoo/addons/mail_gateway_whatsapp/static/src/components/send_whatsapp_button/send_whatsapp_button.xml +13 -0
- odoo/addons/mail_gateway_whatsapp/static/src/models/message.esm.js +21 -0
- odoo/addons/mail_gateway_whatsapp/static/src/models/message_view.esm.js +25 -0
- odoo/addons/mail_gateway_whatsapp/static/src/models/notification.esm.js +25 -0
- odoo/addons/mail_gateway_whatsapp/tests/__init__.py +1 -0
- odoo/addons/mail_gateway_whatsapp/tests/test_mail_gateway_whatsapp.py +308 -0
- odoo/addons/mail_gateway_whatsapp/views/mail_gateway.xml +103 -0
- odoo/addons/mail_gateway_whatsapp/wizards/__init__.py +1 -0
- odoo/addons/mail_gateway_whatsapp/wizards/whatsapp_composer.py +59 -0
- odoo/addons/mail_gateway_whatsapp/wizards/whatsapp_composer.xml +50 -0
- odoo_addon_mail_gateway_whatsapp-16.0.1.0.0.2.dist-info/METADATA +147 -0
- odoo_addon_mail_gateway_whatsapp-16.0.1.0.0.2.dist-info/RECORD +37 -0
- odoo_addon_mail_gateway_whatsapp-16.0.1.0.0.2.dist-info/WHEEL +5 -0
- odoo_addon_mail_gateway_whatsapp-16.0.1.0.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# Copyright 2022 CreuBlanca
|
|
2
|
+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
import hashlib
|
|
5
|
+
import hmac
|
|
6
|
+
import json
|
|
7
|
+
from unittest.mock import MagicMock, patch
|
|
8
|
+
|
|
9
|
+
from odoo.exceptions import UserError
|
|
10
|
+
from odoo.tests.common import tagged
|
|
11
|
+
from odoo.tools import mute_logger
|
|
12
|
+
|
|
13
|
+
from odoo.addons.mail_gateway.tests.common import MailGatewayTestCase
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@tagged("-at_install", "post_install")
|
|
17
|
+
class TestMailGatewayTelegram(MailGatewayTestCase):
|
|
18
|
+
@classmethod
|
|
19
|
+
def setUpClass(cls):
|
|
20
|
+
super().setUpClass()
|
|
21
|
+
cls.webhook = "demo_hook"
|
|
22
|
+
cls.gateway = cls.env["mail.gateway"].create(
|
|
23
|
+
{
|
|
24
|
+
"name": "gateway",
|
|
25
|
+
"gateway_type": "whatsapp",
|
|
26
|
+
"token": "token",
|
|
27
|
+
"whatsapp_security_key": "key",
|
|
28
|
+
"webhook_secret": "MY-SECRET",
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
cls.partner = cls.env["res.partner"].create(
|
|
32
|
+
{"name": "Partner", "mobile": "+34 600 000 000"}
|
|
33
|
+
)
|
|
34
|
+
cls.password = "my_new_password"
|
|
35
|
+
cls.message_01 = {
|
|
36
|
+
"object": "whatsapp_business_account",
|
|
37
|
+
"entry": [
|
|
38
|
+
{
|
|
39
|
+
"id": "WHATSAPP_BUSINESS_ACCOUNT_ID",
|
|
40
|
+
"changes": [
|
|
41
|
+
{
|
|
42
|
+
"value": {
|
|
43
|
+
"messaging_product": "whatsapp",
|
|
44
|
+
"metadata": {
|
|
45
|
+
"display_phone_number": "1234",
|
|
46
|
+
"phone_number_id": "34699999999",
|
|
47
|
+
},
|
|
48
|
+
"contacts": [
|
|
49
|
+
{
|
|
50
|
+
"profile": {"name": "NAME"},
|
|
51
|
+
"wa_id": "34699999999",
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"messages": [
|
|
55
|
+
{
|
|
56
|
+
"from": "34699999999",
|
|
57
|
+
"id": "wamid.ID",
|
|
58
|
+
"timestamp": "1234",
|
|
59
|
+
"text": {"body": "MESSAGE_BODY"},
|
|
60
|
+
"type": "text",
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
"field": "messages",
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
}
|
|
70
|
+
cls.message_02 = {
|
|
71
|
+
"object": "whatsapp_business_account",
|
|
72
|
+
"entry": [
|
|
73
|
+
{
|
|
74
|
+
"id": "WHATSAPP_BUSINESS_ACCOUNT_ID",
|
|
75
|
+
"changes": [
|
|
76
|
+
{
|
|
77
|
+
"value": {
|
|
78
|
+
"messaging_product": "whatsapp",
|
|
79
|
+
"metadata": {
|
|
80
|
+
"display_phone_number": "1234",
|
|
81
|
+
"phone_number_id": "1234",
|
|
82
|
+
},
|
|
83
|
+
"contacts": [
|
|
84
|
+
{"profile": {"name": "NAME"}, "wa_id": "1234"}
|
|
85
|
+
],
|
|
86
|
+
"messages": [
|
|
87
|
+
{
|
|
88
|
+
"from": "1234",
|
|
89
|
+
"id": "wamid.ID",
|
|
90
|
+
"timestamp": "1234",
|
|
91
|
+
"type": "image",
|
|
92
|
+
"image": {
|
|
93
|
+
"caption": "CAPTION",
|
|
94
|
+
"mime_type": "image/jpeg",
|
|
95
|
+
"sha256": "IMAGE_HASH",
|
|
96
|
+
"id": "12356",
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
"field": "messages",
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
def test_webhook_management(self):
|
|
109
|
+
self.gateway.webhook_key = self.webhook
|
|
110
|
+
self.assertTrue(self.gateway.can_set_webhook)
|
|
111
|
+
self.gateway.set_webhook()
|
|
112
|
+
self.assertEqual(self.gateway.integrated_webhook_state, "pending")
|
|
113
|
+
self.gateway.remove_webhook()
|
|
114
|
+
self.assertFalse(self.gateway.integrated_webhook_state)
|
|
115
|
+
self.gateway.set_webhook()
|
|
116
|
+
self.assertEqual(self.gateway.integrated_webhook_state, "pending")
|
|
117
|
+
self.url_open(
|
|
118
|
+
"/gateway/{}/{}/update?hub.verify_token={}&hub.challenge={}".format(
|
|
119
|
+
self.gateway.gateway_type,
|
|
120
|
+
self.webhook,
|
|
121
|
+
self.gateway.whatsapp_security_key + "12",
|
|
122
|
+
"22",
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
self.assertEqual(self.gateway.integrated_webhook_state, "pending")
|
|
126
|
+
self.integrate_webhook()
|
|
127
|
+
self.assertEqual(self.gateway.integrated_webhook_state, "integrated")
|
|
128
|
+
self.gateway.remove_webhook()
|
|
129
|
+
self.assertFalse(self.gateway.integrated_webhook_state)
|
|
130
|
+
|
|
131
|
+
def integrate_webhook(self):
|
|
132
|
+
self.url_open(
|
|
133
|
+
"/gateway/{}/{}/update?hub.verify_token={}&hub.challenge={}".format(
|
|
134
|
+
self.gateway.gateway_type,
|
|
135
|
+
self.webhook,
|
|
136
|
+
self.gateway.whatsapp_security_key,
|
|
137
|
+
"22",
|
|
138
|
+
),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def set_message(self, message, webhook, headers=True):
|
|
142
|
+
data = json.dumps(message)
|
|
143
|
+
headers_dict = {"Content-Type": "application/json"}
|
|
144
|
+
if headers:
|
|
145
|
+
headers_dict["x-hub-signature-256"] = (
|
|
146
|
+
"sha256=%s"
|
|
147
|
+
% hmac.new(
|
|
148
|
+
self.gateway.webhook_secret.encode(),
|
|
149
|
+
data.encode(),
|
|
150
|
+
hashlib.sha256,
|
|
151
|
+
).hexdigest()
|
|
152
|
+
)
|
|
153
|
+
self.url_open(
|
|
154
|
+
"/gateway/{}/{}/update".format(self.gateway.gateway_type, webhook),
|
|
155
|
+
data=data,
|
|
156
|
+
headers=headers_dict,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
def receive_message(self, message):
|
|
160
|
+
self.gateway.webhook_key = self.webhook
|
|
161
|
+
self.gateway.set_webhook()
|
|
162
|
+
self.integrate_webhook()
|
|
163
|
+
self.set_message(message, self.webhook)
|
|
164
|
+
chat = self.env["mail.channel"].search([("gateway_id", "=", self.gateway.id)])
|
|
165
|
+
self.assertTrue(chat)
|
|
166
|
+
self.assertTrue(chat.message_ids)
|
|
167
|
+
return chat.message_ids
|
|
168
|
+
|
|
169
|
+
def test_receive_message_01(self):
|
|
170
|
+
message = self.receive_message(self.message_01)
|
|
171
|
+
self.assertFalse(message.author_id)
|
|
172
|
+
|
|
173
|
+
def test_receive_message_02(self):
|
|
174
|
+
# Check that the partner is assigned automatically
|
|
175
|
+
partner = self.env["res.partner"].create(
|
|
176
|
+
{"name": "DEMO", "phone": "+34699999999"}
|
|
177
|
+
)
|
|
178
|
+
message = self.receive_message(self.message_01)
|
|
179
|
+
self.assertEqual(message.author_id, partner)
|
|
180
|
+
|
|
181
|
+
def test_receive_message_03(self):
|
|
182
|
+
class GetImageResponse:
|
|
183
|
+
def raise_for_status(self):
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
def json(self):
|
|
187
|
+
return {"url": "http://demo.url", "mime_type": "image/png"}
|
|
188
|
+
|
|
189
|
+
content = b"binary_data"
|
|
190
|
+
|
|
191
|
+
with patch("requests.get") as get_mock:
|
|
192
|
+
get_mock.return_value = GetImageResponse()
|
|
193
|
+
self.receive_message(self.message_02)
|
|
194
|
+
|
|
195
|
+
def test_post_no_signature_no_message(self):
|
|
196
|
+
self.gateway.webhook_key = self.webhook
|
|
197
|
+
self.gateway.set_webhook()
|
|
198
|
+
self.integrate_webhook()
|
|
199
|
+
self.set_message(self.message_01, self.webhook, False)
|
|
200
|
+
self.assertFalse(
|
|
201
|
+
self.env["mail.channel"].search([("gateway_id", "=", self.gateway.id)])
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def test_post_wrong_signature_no_message(self):
|
|
205
|
+
self.gateway.webhook_key = self.webhook
|
|
206
|
+
self.gateway.set_webhook()
|
|
207
|
+
self.integrate_webhook()
|
|
208
|
+
data = json.dumps(self.message_01)
|
|
209
|
+
headers = {
|
|
210
|
+
"Content-Type": "application/json",
|
|
211
|
+
"x-hub-signature-256": (
|
|
212
|
+
"sha256=1234%s"
|
|
213
|
+
% hmac.new(
|
|
214
|
+
self.gateway.webhook_secret.encode(),
|
|
215
|
+
data.encode(),
|
|
216
|
+
hashlib.sha256,
|
|
217
|
+
).hexdigest()
|
|
218
|
+
),
|
|
219
|
+
}
|
|
220
|
+
self.url_open(
|
|
221
|
+
"/gateway/{}/{}/update".format(self.gateway.gateway_type, self.webhook),
|
|
222
|
+
data=data,
|
|
223
|
+
headers=headers,
|
|
224
|
+
)
|
|
225
|
+
self.assertFalse(
|
|
226
|
+
self.env["mail.channel"].search([("gateway_id", "=", self.gateway.id)])
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
def test_send_image(self):
|
|
230
|
+
self.gateway.webhook_key = self.webhook
|
|
231
|
+
self.gateway.set_webhook()
|
|
232
|
+
self.integrate_webhook()
|
|
233
|
+
composer = self.env["whatsapp.composer"].create(
|
|
234
|
+
{
|
|
235
|
+
"res_model": self.partner._name,
|
|
236
|
+
"res_id": self.partner.id,
|
|
237
|
+
"number_field_name": "mobile",
|
|
238
|
+
"gateway_id": self.gateway.id,
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
composer.action_view_whatsapp()
|
|
242
|
+
channel = self.env["mail.channel"].search(
|
|
243
|
+
[("gateway_id", "=", self.gateway.id)]
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
with patch("requests.post") as post_mock:
|
|
247
|
+
post_mock.return_value = MagicMock()
|
|
248
|
+
channel.message_post(
|
|
249
|
+
attachments=[("demo.png", b"IMAGE")],
|
|
250
|
+
subtype_xmlid="mail.mt_comment",
|
|
251
|
+
message_type="comment",
|
|
252
|
+
)
|
|
253
|
+
post_mock.assert_called()
|
|
254
|
+
self.assertEqual(post_mock.call_count, 2)
|
|
255
|
+
|
|
256
|
+
def test_send_document_error(self):
|
|
257
|
+
self.gateway.webhook_key = self.webhook
|
|
258
|
+
self.gateway.set_webhook()
|
|
259
|
+
self.integrate_webhook()
|
|
260
|
+
composer = self.env["whatsapp.composer"].create(
|
|
261
|
+
{
|
|
262
|
+
"res_model": self.partner._name,
|
|
263
|
+
"res_id": self.partner.id,
|
|
264
|
+
"number_field_name": "mobile",
|
|
265
|
+
"gateway_id": self.gateway.id,
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
composer.action_view_whatsapp()
|
|
269
|
+
channel = self.env["mail.channel"].search(
|
|
270
|
+
[("gateway_id", "=", self.gateway.id)]
|
|
271
|
+
)
|
|
272
|
+
with mute_logger(
|
|
273
|
+
"odoo.addons.mail_gateway_whatsapp.models.mail_gateway_whatsapp"
|
|
274
|
+
):
|
|
275
|
+
message = channel.message_post(
|
|
276
|
+
attachments=[("demo.xml", b"IMAGE")],
|
|
277
|
+
subtype_xmlid="mail.mt_comment",
|
|
278
|
+
message_type="comment",
|
|
279
|
+
)
|
|
280
|
+
self.assertEqual(message.notification_ids.notification_status, "exception")
|
|
281
|
+
|
|
282
|
+
def test_compose(self):
|
|
283
|
+
self.gateway.webhook_key = self.webhook
|
|
284
|
+
self.gateway.set_webhook()
|
|
285
|
+
self.integrate_webhook()
|
|
286
|
+
composer = self.env["whatsapp.composer"].create(
|
|
287
|
+
{
|
|
288
|
+
"res_model": self.partner._name,
|
|
289
|
+
"res_id": self.partner.id,
|
|
290
|
+
"number_field_name": "mobile",
|
|
291
|
+
"gateway_id": self.gateway.id,
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
composer.action_view_whatsapp()
|
|
295
|
+
channel = self.env["mail.channel"].search(
|
|
296
|
+
[("gateway_id", "=", self.gateway.id)]
|
|
297
|
+
)
|
|
298
|
+
self.assertTrue(channel)
|
|
299
|
+
self.assertFalse(channel.message_ids)
|
|
300
|
+
with self.assertRaises(UserError):
|
|
301
|
+
composer.action_send_whatsapp()
|
|
302
|
+
composer.body = "DEMO"
|
|
303
|
+
with patch("requests.post") as post_mock:
|
|
304
|
+
post_mock.return_value = MagicMock()
|
|
305
|
+
composer.action_send_whatsapp()
|
|
306
|
+
post_mock.assert_called()
|
|
307
|
+
channel.invalidate_recordset()
|
|
308
|
+
self.assertTrue(channel.message_ids)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<!-- Copyright 2022 Creu Blanca
|
|
3
|
+
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
|
4
|
+
<odoo>
|
|
5
|
+
<record model="ir.ui.view" id="mail_gateway_form_view">
|
|
6
|
+
<field name="name">mail.gateway.form (in mail_gateway_telegram)</field>
|
|
7
|
+
<field name="model">mail.gateway</field>
|
|
8
|
+
<field name="inherit_id" ref="mail_gateway.mail_gateway_form_view" />
|
|
9
|
+
<field name="arch" type="xml">
|
|
10
|
+
<field name="webhook_user_id" position="after">
|
|
11
|
+
<field
|
|
12
|
+
name="whatsapp_security_key"
|
|
13
|
+
attrs="{'invisible': [('gateway_type', '!=', 'whatsapp')]}"
|
|
14
|
+
/>
|
|
15
|
+
<field
|
|
16
|
+
name="whatsapp_from_phone"
|
|
17
|
+
attrs="{'invisible': [('gateway_type', '!=', 'whatsapp')]}"
|
|
18
|
+
/>
|
|
19
|
+
<field
|
|
20
|
+
name="whatsapp_version"
|
|
21
|
+
attrs="{'invisible': [('gateway_type', '!=', 'whatsapp')]}"
|
|
22
|
+
/>
|
|
23
|
+
</field>
|
|
24
|
+
<notebook position="inside">
|
|
25
|
+
<page
|
|
26
|
+
name="whatsapp"
|
|
27
|
+
string="Whatsapp configuration info"
|
|
28
|
+
attrs="{'invisible': [('gateway_type', '!=', 'whatsapp')]}"
|
|
29
|
+
>
|
|
30
|
+
<div>
|
|
31
|
+
<h2>First steps</h2>
|
|
32
|
+
<span>Define the values of the fields <b>Webhook Key</b> and <b
|
|
33
|
+
>Whatsapp Security Key</b>.
|
|
34
|
+
You should set some random value of your choice for this fields.
|
|
35
|
+
Ensure that facebook will be able to comunicate with your server.</span>
|
|
36
|
+
<h2>Creating the Application from meta developer</h2>
|
|
37
|
+
<ol>
|
|
38
|
+
<li>Access
|
|
39
|
+
<a
|
|
40
|
+
href="https://developers.facebook.com/apps/?show_reminder=true"
|
|
41
|
+
target="_blank"
|
|
42
|
+
rel="noopener noreferrer"
|
|
43
|
+
>
|
|
44
|
+
Meta developer platform</a>.</li>
|
|
45
|
+
<li>Create a new application.</li>
|
|
46
|
+
<li>Select the <b>Other</b> option, then <b
|
|
47
|
+
>Business</b> in order to create Whatsapp manager</li>
|
|
48
|
+
<li>Select a name and create it</li>
|
|
49
|
+
<li
|
|
50
|
+
>Select the Whatsapp API on the products of the application</li>
|
|
51
|
+
<li>Select the business that you will use</li>
|
|
52
|
+
<li>Go to <b>Whatsapp / API Configuration</b> menú.</li>
|
|
53
|
+
<li>Add a new number.</li>
|
|
54
|
+
<li>Copy the phone identification number to the field <b
|
|
55
|
+
>Whastapp From phone</b> field.</li>
|
|
56
|
+
<li>Access the menu <b
|
|
57
|
+
>Configuration / Basic information</b>.</li>
|
|
58
|
+
<li
|
|
59
|
+
>Show the secret key of the application and copy it to the <b
|
|
60
|
+
>Webhook secret</b> field.</li>
|
|
61
|
+
<li>Access the menu <b
|
|
62
|
+
>Configuration / Advanced Options</b>.</li>
|
|
63
|
+
<li>Copy the API Version on the <b
|
|
64
|
+
>Whastapp Version</b> field.</li>
|
|
65
|
+
</ol>
|
|
66
|
+
<h2>Creating a permanent token</h2>
|
|
67
|
+
<ol>
|
|
68
|
+
<li>Access your <a
|
|
69
|
+
href="https://business.facebook.com/"
|
|
70
|
+
target="_blank"
|
|
71
|
+
rel="noopener noreferrer"
|
|
72
|
+
>meta business site.</a></li>
|
|
73
|
+
<li>Access your business settings menu</li>
|
|
74
|
+
<li>Go to <b
|
|
75
|
+
>Users / System Users</b> and create a new one with administrator</li>
|
|
76
|
+
<li
|
|
77
|
+
>Once it is created, Generate a new token related to the created app with no expiry and give access to all whatsapp permissions.</li>
|
|
78
|
+
<li>Copy the token value to token field in this page.</li>
|
|
79
|
+
</ol>
|
|
80
|
+
<h2>Setting webhook and version</h2>
|
|
81
|
+
<ol>
|
|
82
|
+
<li>Save this record. All the values have been filled</li>
|
|
83
|
+
<li>Press the button Integrate webhook.</li>
|
|
84
|
+
<li
|
|
85
|
+
>On Facebook Develpment, return to the app menu and go to <b
|
|
86
|
+
>Whatsapp / Configuration</b>.</li>
|
|
87
|
+
<li>Click on <b>Edit Button</b> on the webhook area.</li>
|
|
88
|
+
<li>On the wizard, fill the URL field with <b
|
|
89
|
+
>Webhook URL</b> field of the gateway. For the verification identifier, use the <b
|
|
90
|
+
>Whatsapp Security Key</b> field.</li>
|
|
91
|
+
<li>Verify and save the wizard.</li>
|
|
92
|
+
<li
|
|
93
|
+
>If no error is raised, refresh this gateway data, and you should see that it is integrated.
|
|
94
|
+
You should be able to receive and send messages.
|
|
95
|
+
If an error is raised, check that the fields are filled properly and that facebook server is able to access your server.</li>
|
|
96
|
+
</ol>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
</page>
|
|
100
|
+
</notebook>
|
|
101
|
+
</field>
|
|
102
|
+
</record>
|
|
103
|
+
</odoo>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import whatsapp_composer
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Copyright 2022 CreuBlanca
|
|
2
|
+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
from odoo import _, api, fields, models
|
|
5
|
+
from odoo.exceptions import UserError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WhatsappComposer(models.TransientModel):
|
|
9
|
+
|
|
10
|
+
_name = "whatsapp.composer"
|
|
11
|
+
_description = "Compose a whatsapp message"
|
|
12
|
+
|
|
13
|
+
res_model = fields.Char("Document Model Name")
|
|
14
|
+
res_id = fields.Integer("Document ID")
|
|
15
|
+
number_field_name = fields.Char()
|
|
16
|
+
find_gateway = fields.Boolean()
|
|
17
|
+
gateway_id = fields.Many2one(
|
|
18
|
+
"mail.gateway", domain=[("gateway_type", "=", "whatsapp")], required=True
|
|
19
|
+
)
|
|
20
|
+
body = fields.Text("Message")
|
|
21
|
+
|
|
22
|
+
@api.model
|
|
23
|
+
def default_get(self, fields):
|
|
24
|
+
result = super().default_get(fields)
|
|
25
|
+
gateways = self.env["mail.gateway"].search([("gateway_type", "=", "whatsapp")])
|
|
26
|
+
result["find_gateway"] = len(gateways) != 1
|
|
27
|
+
if not result["find_gateway"]:
|
|
28
|
+
result["gateway_id"] = gateways.id
|
|
29
|
+
return result
|
|
30
|
+
|
|
31
|
+
def _action_send_whatsapp(self):
|
|
32
|
+
record = self.env[self.res_model].browse(self.res_id)
|
|
33
|
+
if not record:
|
|
34
|
+
return
|
|
35
|
+
channel = record._whatsapp_get_channel(self.number_field_name, self.gateway_id)
|
|
36
|
+
channel.message_post(
|
|
37
|
+
body=self.body, subtype_xmlid="mail.mt_comment", message_type="comment"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def action_view_whatsapp(self):
|
|
41
|
+
self.ensure_one()
|
|
42
|
+
record = self.env[self.res_model].browse(self.res_id)
|
|
43
|
+
if not record:
|
|
44
|
+
return
|
|
45
|
+
channel = record._whatsapp_get_channel(self.number_field_name, self.gateway_id)
|
|
46
|
+
if channel:
|
|
47
|
+
return {
|
|
48
|
+
"type": "ir.actions.client",
|
|
49
|
+
"tag": "mail.action_discuss",
|
|
50
|
+
"params": {"active_id": "{}_{}".format(channel._name, channel.id)},
|
|
51
|
+
}
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
def action_send_whatsapp(self):
|
|
55
|
+
self.ensure_one()
|
|
56
|
+
if not self.body:
|
|
57
|
+
raise UserError(_("Body is required"))
|
|
58
|
+
self._action_send_whatsapp()
|
|
59
|
+
return False
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
|
2
|
+
<!-- Copyright 2022 CreuBlanca
|
|
3
|
+
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
|
|
4
|
+
<odoo>
|
|
5
|
+
|
|
6
|
+
<record model="ir.ui.view" id="whatsapp_composer_form_view">
|
|
7
|
+
<field name="name">whatsapp.composer.form (in mail_gateway_whatsapp)</field>
|
|
8
|
+
<field name="model">whatsapp.composer</field>
|
|
9
|
+
<field name="arch" type="xml">
|
|
10
|
+
<form string="Whatsapp Composer">
|
|
11
|
+
<group>
|
|
12
|
+
<field
|
|
13
|
+
name="gateway_id"
|
|
14
|
+
attrs="{'invisible': [('find_gateway', '=', False)]}"
|
|
15
|
+
/>
|
|
16
|
+
<field name="find_gateway" invisible="1" />
|
|
17
|
+
<field name="res_model" invisible="1" />
|
|
18
|
+
<field name="res_id" invisible="1" />
|
|
19
|
+
<field name="number_field_name" invisible="1" />
|
|
20
|
+
<field name="body" />
|
|
21
|
+
</group>
|
|
22
|
+
<footer>
|
|
23
|
+
<button
|
|
24
|
+
name="action_send_whatsapp"
|
|
25
|
+
string="Send"
|
|
26
|
+
class="btn-primary"
|
|
27
|
+
icon="fa-whatsapp"
|
|
28
|
+
type="object"
|
|
29
|
+
/>
|
|
30
|
+
<button
|
|
31
|
+
name="action_view_whatsapp"
|
|
32
|
+
string="Show Chat"
|
|
33
|
+
type="object"
|
|
34
|
+
/>
|
|
35
|
+
<button string="Cancel" class="btn-default" special="cancel" />
|
|
36
|
+
</footer>
|
|
37
|
+
</form>
|
|
38
|
+
</field>
|
|
39
|
+
</record>
|
|
40
|
+
|
|
41
|
+
<record model="ir.actions.act_window" id="whatsapp_composer_act_window">
|
|
42
|
+
<field name="name">Send Whatsapp Message</field>
|
|
43
|
+
<field name="res_model">whatsapp.composer</field>
|
|
44
|
+
<field name="view_mode">form</field>
|
|
45
|
+
<field name="context">{}</field>
|
|
46
|
+
<field name="target">new</field>
|
|
47
|
+
</record>
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
</odoo>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: odoo-addon-mail-gateway-whatsapp
|
|
3
|
+
Version: 16.0.1.0.0.2
|
|
4
|
+
Summary: Set a gateway for whatsapp
|
|
5
|
+
Home-page: https://github.com/OCA/social
|
|
6
|
+
Author: Creu Blanca, Dixmit, Odoo Community Association (OCA)
|
|
7
|
+
Author-email: support@odoo-community.org
|
|
8
|
+
License: AGPL-3
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Framework :: Odoo
|
|
12
|
+
Classifier: Framework :: Odoo :: 16.0
|
|
13
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Requires-Dist: odoo-addon-mail-gateway <16.1dev,>=16.0dev
|
|
16
|
+
Requires-Dist: odoo <16.1dev,>=16.0a
|
|
17
|
+
Requires-Dist: requests-toolbelt
|
|
18
|
+
|
|
19
|
+
=====================
|
|
20
|
+
Mail Whatsapp Gateway
|
|
21
|
+
=====================
|
|
22
|
+
|
|
23
|
+
..
|
|
24
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
25
|
+
!! This file is generated by oca-gen-addon-readme !!
|
|
26
|
+
!! changes will be overwritten. !!
|
|
27
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
28
|
+
!! source digest: sha256:7e8914aa79754fb57cdfe774336f8ffe4c6784994839b64e09cf393ed7c2aa4a
|
|
29
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
30
|
+
|
|
31
|
+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
|
32
|
+
:target: https://odoo-community.org/page/development-status
|
|
33
|
+
:alt: Beta
|
|
34
|
+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
|
35
|
+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
|
36
|
+
:alt: License: AGPL-3
|
|
37
|
+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github
|
|
38
|
+
:target: https://github.com/OCA/social/tree/16.0/mail_gateway_whatsapp
|
|
39
|
+
:alt: OCA/social
|
|
40
|
+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
|
41
|
+
:target: https://translation.odoo-community.org/projects/social-16-0/social-16-0-mail_gateway_whatsapp
|
|
42
|
+
:alt: Translate me on Weblate
|
|
43
|
+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
|
44
|
+
:target: https://runboat.odoo-community.org/builds?repo=OCA/social&target_branch=16.0
|
|
45
|
+
:alt: Try me on Runboat
|
|
46
|
+
|
|
47
|
+
|badge1| |badge2| |badge3| |badge4| |badge5|
|
|
48
|
+
|
|
49
|
+
This module allows to respond whatsapp chats.
|
|
50
|
+
|
|
51
|
+
This way, a group of users can respond customers or any other set
|
|
52
|
+
of partners in an integrated way.
|
|
53
|
+
|
|
54
|
+
**Table of contents**
|
|
55
|
+
|
|
56
|
+
.. contents::
|
|
57
|
+
:local:
|
|
58
|
+
|
|
59
|
+
Configuration
|
|
60
|
+
=============
|
|
61
|
+
|
|
62
|
+
First steps
|
|
63
|
+
~~~~~~~~~~~
|
|
64
|
+
|
|
65
|
+
You need to create a WhatsApp Business Account (WABA), a Meta App and define a phone number.
|
|
66
|
+
You can follow this `steps <https://developers.facebook.com/micro_site/url/?click_from_context_menu=true&country=ES&destination=https%3A%2F%2Fwww.facebook.com%2Fbusiness%2Fhelp%2F2087193751603668&event_type=click&last_nav_impression_id=0m3TRxrxOlly1eRmB&max_percent_page_viewed=22&max_viewport_height_px=1326&max_viewport_width_px=2560&orig_http_referrer=https%3A%2F%2Fdevelopers.facebook.com%2Fdocs%2Fwhatsapp%2Fcloud-api%2Fget-started-for-bsps%3Flocale%3Den_US&orig_request_uri=https%3A%2F%2Fdevelopers.facebook.com%2Fajax%2Fpagelet%2Fgeneric.php%2FDeveloperNotificationsPayloadPagelet%3Ffb_dtsg_ag%3D--sanitized--%26data%3D%257B%2522businessUserID%2522%253Anull%252C%2522cursor%2522%253Anull%252C%2522length%2522%253A15%252C%2522clientRequestID%2522%253A%2522js_k6%2522%257D%26__usid%3D6-Trd7hi4itpm%253APrd7ifiub2tvy%253A0-Ard7g9twdm0p1-RV%253D6%253AF%253D%26locale%3Den_US%26jazoest%3D24920®ion=emea&scrolled=false&session_id=1jLoVJNU6iVMaw3ml&site=developers>`_.
|
|
67
|
+
|
|
68
|
+
If you create a test Business Account, passwords will change every 24 hours.
|
|
69
|
+
|
|
70
|
+
In order to make the webhook accessible, the system must be public.
|
|
71
|
+
|
|
72
|
+
Configure the gateway
|
|
73
|
+
~~~~~~~~~~~~~~~~~~~~~
|
|
74
|
+
|
|
75
|
+
Once you have created the Meta App, you need to add the gateway and webhook.
|
|
76
|
+
In order to make it you must follow this steps:
|
|
77
|
+
|
|
78
|
+
* Access `Settings > Emails > Mail Gateway`
|
|
79
|
+
* Create a Gateway of type `WhatsApp`
|
|
80
|
+
|
|
81
|
+
* Use the Meta App authentication key as `Token` field
|
|
82
|
+
* Use the Meta App Phone Number ID as `Whatsapp from Phone` field
|
|
83
|
+
* Write your own `Webhook key`
|
|
84
|
+
* Use the Application Secret Key on `Whatsapp Security Key`. It will be used in order to validate the data
|
|
85
|
+
* Press the `Integrate Webhook Key`. In this case, it will not integrate it, we need to make it manually
|
|
86
|
+
* Copy the webhook URL
|
|
87
|
+
|
|
88
|
+
* Access `Facebook Apps website <https://developers.facebook.com/apps/>`_
|
|
89
|
+
* Access your App then `Whatsapp > Configuration`
|
|
90
|
+
* Create your webhook using your URL and put the Whatsapp Security Key as validation Key
|
|
91
|
+
* Administer the Webhook and activate the messages webhook
|
|
92
|
+
|
|
93
|
+
Usage
|
|
94
|
+
=====
|
|
95
|
+
|
|
96
|
+
1. Access `Gateway`
|
|
97
|
+
2. Wait until someone starts a conversation.
|
|
98
|
+
3. Now you will be able to respond and receive messages to this person.
|
|
99
|
+
|
|
100
|
+
Bug Tracker
|
|
101
|
+
===========
|
|
102
|
+
|
|
103
|
+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/social/issues>`_.
|
|
104
|
+
In case of trouble, please check there if your issue has already been reported.
|
|
105
|
+
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
|
106
|
+
`feedback <https://github.com/OCA/social/issues/new?body=module:%20mail_gateway_whatsapp%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
|
107
|
+
|
|
108
|
+
Do not contact contributors directly about support or help with technical issues.
|
|
109
|
+
|
|
110
|
+
Credits
|
|
111
|
+
=======
|
|
112
|
+
|
|
113
|
+
Authors
|
|
114
|
+
~~~~~~~
|
|
115
|
+
|
|
116
|
+
* Creu Blanca
|
|
117
|
+
* Dixmit
|
|
118
|
+
|
|
119
|
+
Contributors
|
|
120
|
+
~~~~~~~~~~~~
|
|
121
|
+
|
|
122
|
+
* Olga Marco <olga.marco@creublanca.es>
|
|
123
|
+
* Enric Tobella <etobella@creublanca.es>
|
|
124
|
+
|
|
125
|
+
Other credits
|
|
126
|
+
~~~~~~~~~~~~~
|
|
127
|
+
|
|
128
|
+
This work has been funded by AEOdoo (Asociación Española de Odoo - https://www.aeodoo.org)
|
|
129
|
+
|
|
130
|
+
Maintainers
|
|
131
|
+
~~~~~~~~~~~
|
|
132
|
+
|
|
133
|
+
This module is maintained by the OCA.
|
|
134
|
+
|
|
135
|
+
.. image:: https://odoo-community.org/logo.png
|
|
136
|
+
:alt: Odoo Community Association
|
|
137
|
+
:target: https://odoo-community.org
|
|
138
|
+
|
|
139
|
+
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
|
140
|
+
mission is to support the collaborative development of Odoo features and
|
|
141
|
+
promote its widespread use.
|
|
142
|
+
|
|
143
|
+
This module is part of the `OCA/social <https://github.com/OCA/social/tree/16.0/mail_gateway_whatsapp>`_ project on GitHub.
|
|
144
|
+
|
|
145
|
+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
|
146
|
+
|
|
147
|
+
|