odoo-addon-mail-gateway 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.
Potentially problematic release.
This version of odoo-addon-mail-gateway might be problematic. Click here for more details.
- odoo/addons/mail_gateway/README.rst +100 -0
- odoo/addons/mail_gateway/__init__.py +6 -0
- odoo/addons/mail_gateway/__manifest__.py +38 -0
- odoo/addons/mail_gateway/controllers/__init__.py +2 -0
- odoo/addons/mail_gateway/controllers/discuss.py +11 -0
- odoo/addons/mail_gateway/controllers/gateway.py +81 -0
- odoo/addons/mail_gateway/hooks.py +12 -0
- odoo/addons/mail_gateway/i18n/mail_gateway.pot +533 -0
- odoo/addons/mail_gateway/models/__init__.py +10 -0
- odoo/addons/mail_gateway/models/ir_websocket.py +21 -0
- odoo/addons/mail_gateway/models/mail_channel.py +79 -0
- odoo/addons/mail_gateway/models/mail_gateway.py +149 -0
- odoo/addons/mail_gateway/models/mail_gateway_abstract.py +82 -0
- odoo/addons/mail_gateway/models/mail_guest.py +18 -0
- odoo/addons/mail_gateway/models/mail_message.py +123 -0
- odoo/addons/mail_gateway/models/mail_notification.py +43 -0
- odoo/addons/mail_gateway/models/mail_thread.py +85 -0
- odoo/addons/mail_gateway/models/res_partner.py +89 -0
- odoo/addons/mail_gateway/models/res_users.py +16 -0
- odoo/addons/mail_gateway/readme/CONFIGURATION.rst +4 -0
- odoo/addons/mail_gateway/readme/CONTRIBUTORS.rst +2 -0
- odoo/addons/mail_gateway/readme/CREDITS.rst +1 -0
- odoo/addons/mail_gateway/readme/DESCRIPTION.rst +5 -0
- odoo/addons/mail_gateway/readme/USAGE.rst +9 -0
- odoo/addons/mail_gateway/security/ir.model.access.csv +9 -0
- odoo/addons/mail_gateway/security/security.xml +61 -0
- odoo/addons/mail_gateway/static/description/icon.png +0 -0
- odoo/addons/mail_gateway/static/description/index.html +441 -0
- odoo/addons/mail_gateway/static/src/components/chatter/chatter.xml +40 -0
- odoo/addons/mail_gateway/static/src/components/composer/composer.xml +24 -0
- odoo/addons/mail_gateway/static/src/components/discuss_sidebar/discuss_sidebar.xml +17 -0
- odoo/addons/mail_gateway/static/src/components/gateway_follower/gateway_follower.esm.js +31 -0
- odoo/addons/mail_gateway/static/src/components/gateway_follower/gateway_follower.scss +12 -0
- odoo/addons/mail_gateway/static/src/components/gateway_follower/gateway_follower.xml +30 -0
- odoo/addons/mail_gateway/static/src/components/message/message.xml +22 -0
- odoo/addons/mail_gateway/static/src/components/message_notification_popover_content/message_notification_popover_content.xml +15 -0
- odoo/addons/mail_gateway/static/src/models/channel.esm.js +33 -0
- odoo/addons/mail_gateway/static/src/models/channel_member_view.esm.js +39 -0
- odoo/addons/mail_gateway/static/src/models/chatter.esm.js +41 -0
- odoo/addons/mail_gateway/static/src/models/composer.esm.js +32 -0
- odoo/addons/mail_gateway/static/src/models/composer_gateway_follower.esm.js +32 -0
- odoo/addons/mail_gateway/static/src/models/composer_view.esm.js +56 -0
- odoo/addons/mail_gateway/static/src/models/discuss.esm.js +51 -0
- odoo/addons/mail_gateway/static/src/models/discuss_sidebar_category.esm.js +128 -0
- odoo/addons/mail_gateway/static/src/models/discuss_sidebar_category_item.esm.js +51 -0
- odoo/addons/mail_gateway/static/src/models/gateway.esm.js +16 -0
- odoo/addons/mail_gateway/static/src/models/gateway_channel.esm.js +14 -0
- odoo/addons/mail_gateway/static/src/models/gateway_channel_view.esm.js +15 -0
- odoo/addons/mail_gateway/static/src/models/guest.esm.js +10 -0
- odoo/addons/mail_gateway/static/src/models/message.esm.js +76 -0
- odoo/addons/mail_gateway/static/src/models/message_action.esm.js +45 -0
- odoo/addons/mail_gateway/static/src/models/message_action_list.esm.js +37 -0
- odoo/addons/mail_gateway/static/src/models/message_action_view.esm.js +91 -0
- odoo/addons/mail_gateway/static/src/models/message_view.esm.js +13 -0
- odoo/addons/mail_gateway/static/src/models/messaging_initializer.esm.js +24 -0
- odoo/addons/mail_gateway/static/src/models/notification.esm.js +20 -0
- odoo/addons/mail_gateway/static/src/models/partner.esm.js +11 -0
- odoo/addons/mail_gateway/static/src/models/thread.esm.js +77 -0
- odoo/addons/mail_gateway/tests/__init__.py +0 -0
- odoo/addons/mail_gateway/tests/common.py +21 -0
- odoo/addons/mail_gateway/views/mail_gateway.xml +93 -0
- odoo/addons/mail_gateway/views/res_partner_gateway_channel.xml +59 -0
- odoo/addons/mail_gateway/wizards/__init__.py +3 -0
- odoo/addons/mail_gateway/wizards/mail_guest_manage.py +58 -0
- odoo/addons/mail_gateway/wizards/mail_guest_manage.xml +37 -0
- odoo/addons/mail_gateway/wizards/mail_message_gateway_link.py +42 -0
- odoo/addons/mail_gateway/wizards/mail_message_gateway_link.xml +28 -0
- odoo/addons/mail_gateway/wizards/mail_message_gateway_send.py +20 -0
- odoo/addons/mail_gateway/wizards/mail_message_gateway_send.xml +39 -0
- odoo_addon_mail_gateway-16.0.1.0.0.2.dist-info/METADATA +118 -0
- odoo_addon_mail_gateway-16.0.1.0.0.2.dist-info/RECORD +73 -0
- odoo_addon_mail_gateway-16.0.1.0.0.2.dist-info/WHEEL +5 -0
- odoo_addon_mail_gateway-16.0.1.0.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Copyright 2024 Dixmit
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
from odoo import Command, api, fields, models, tools
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MailGateway(models.Model):
|
|
7
|
+
_name = "mail.gateway"
|
|
8
|
+
_description = "Mail Gateway"
|
|
9
|
+
|
|
10
|
+
name = fields.Char(required=True)
|
|
11
|
+
token = fields.Char(required=True, help="Key used for integration purposes")
|
|
12
|
+
gateway_type = fields.Selection([], required=True)
|
|
13
|
+
webhook_key = fields.Char(help="Key used on the connection URL")
|
|
14
|
+
webhook_secret = fields.Char(
|
|
15
|
+
help="""Key used to ensure that the connection is secure and
|
|
16
|
+
comes from the desired source"""
|
|
17
|
+
)
|
|
18
|
+
integrated_webhook_state = fields.Selection(
|
|
19
|
+
[("pending", "Pending"), ("integrated", "Integrated")], readonly=True
|
|
20
|
+
)
|
|
21
|
+
can_set_webhook = fields.Boolean(compute="_compute_webhook_checks")
|
|
22
|
+
webhook_url = fields.Char(compute="_compute_webhook_url")
|
|
23
|
+
has_new_channel_security = fields.Boolean(
|
|
24
|
+
help="When checked, channels are not created automatically. Usable on Telegram"
|
|
25
|
+
)
|
|
26
|
+
webhook_user_id = fields.Many2one(
|
|
27
|
+
"res.users",
|
|
28
|
+
default=lambda self: self.env.user.id,
|
|
29
|
+
help="User that will create the messages",
|
|
30
|
+
)
|
|
31
|
+
member_ids = fields.Many2many(
|
|
32
|
+
"res.users", default=lambda self: [Command.link(self.env.user.id)]
|
|
33
|
+
)
|
|
34
|
+
company_id = fields.Many2one(
|
|
35
|
+
"res.company", default=lambda self: self.env.company.id
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
_sql_constraints = [
|
|
39
|
+
("mail_gateway_token", "unique(token)", "Token must be unique"),
|
|
40
|
+
(
|
|
41
|
+
"mail_gateway_webhook_key",
|
|
42
|
+
"unique(webhook_key)",
|
|
43
|
+
"Webhook Key must be unique",
|
|
44
|
+
),
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
@api.depends("webhook_key")
|
|
48
|
+
def _compute_webhook_url(self):
|
|
49
|
+
for record in self:
|
|
50
|
+
record.webhook_url = record._get_webhook_url()
|
|
51
|
+
|
|
52
|
+
def _get_channel_id(self, chat_token):
|
|
53
|
+
return (
|
|
54
|
+
self.env["mail.channel"]
|
|
55
|
+
.search(
|
|
56
|
+
[
|
|
57
|
+
("gateway_channel_token", "=", str(chat_token)),
|
|
58
|
+
("gateway_id", "=", self.id),
|
|
59
|
+
],
|
|
60
|
+
limit=1,
|
|
61
|
+
)
|
|
62
|
+
.id
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def _get_webhook_url(self):
|
|
66
|
+
return "%s/gateway/%s/%s/update" % (
|
|
67
|
+
self.webhook_url
|
|
68
|
+
or self.env["ir.config_parameter"].get_param("web.base.url"),
|
|
69
|
+
self.gateway_type,
|
|
70
|
+
self.webhook_key,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def _can_set_webhook(self):
|
|
74
|
+
return self.webhook_key and self.webhook_user_id
|
|
75
|
+
|
|
76
|
+
@api.depends("gateway_type")
|
|
77
|
+
def _compute_webhook_checks(self):
|
|
78
|
+
for record in self:
|
|
79
|
+
record.can_set_webhook = record._can_set_webhook()
|
|
80
|
+
|
|
81
|
+
def set_webhook(self):
|
|
82
|
+
self.ensure_one()
|
|
83
|
+
if self.can_set_webhook:
|
|
84
|
+
self.env["mail.gateway.%s" % self.gateway_type]._set_webhook(self)
|
|
85
|
+
|
|
86
|
+
def remove_webhook(self):
|
|
87
|
+
self.ensure_one()
|
|
88
|
+
self.env["mail.gateway.%s" % self.gateway_type]._remove_webhook(self)
|
|
89
|
+
|
|
90
|
+
def update_webhook(self):
|
|
91
|
+
self.ensure_one()
|
|
92
|
+
self.remove_webhook()
|
|
93
|
+
self.set_webhook()
|
|
94
|
+
|
|
95
|
+
def write(self, vals):
|
|
96
|
+
res = super(MailGateway, self).write(vals)
|
|
97
|
+
if (
|
|
98
|
+
"webhook_key" in vals
|
|
99
|
+
or "integrated_webhook_state" in vals
|
|
100
|
+
or "webhook_secret" in vals
|
|
101
|
+
or "webhook_user_id" in vals
|
|
102
|
+
):
|
|
103
|
+
self.clear_caches()
|
|
104
|
+
return res
|
|
105
|
+
|
|
106
|
+
@api.model_create_multi
|
|
107
|
+
def create(self, mvals):
|
|
108
|
+
res = super(MailGateway, self).create(mvals)
|
|
109
|
+
self.clear_caches()
|
|
110
|
+
return res
|
|
111
|
+
|
|
112
|
+
@api.model
|
|
113
|
+
@tools.ormcache()
|
|
114
|
+
def _get_gateway_map(self, state="integrated", gateway_type=False):
|
|
115
|
+
result = {}
|
|
116
|
+
for record in self.search(
|
|
117
|
+
[
|
|
118
|
+
("integrated_webhook_state", "=", state),
|
|
119
|
+
("gateway_type", "=", gateway_type),
|
|
120
|
+
]
|
|
121
|
+
):
|
|
122
|
+
result[record.webhook_key] = record._get_gateway_data()
|
|
123
|
+
return result
|
|
124
|
+
|
|
125
|
+
def _get_gateway_data(self):
|
|
126
|
+
return {
|
|
127
|
+
"id": self.id,
|
|
128
|
+
"webhook_secret": self.webhook_secret,
|
|
129
|
+
"webhook_user_id": self.webhook_user_id.id,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@api.model
|
|
133
|
+
def _get_gateway(self, key, state="integrated", gateway_type=False):
|
|
134
|
+
# We are using cache in order to avoid an exploit
|
|
135
|
+
if not key:
|
|
136
|
+
return False
|
|
137
|
+
return self._get_gateway_map(state=state, gateway_type=gateway_type).get(
|
|
138
|
+
key, False
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def gateway_info(self):
|
|
142
|
+
return [record._gateway_info() for record in self]
|
|
143
|
+
|
|
144
|
+
def _gateway_info(self):
|
|
145
|
+
return {
|
|
146
|
+
"id": self.id,
|
|
147
|
+
"name": self.name,
|
|
148
|
+
"type": self.gateway_type,
|
|
149
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Copyright 2024 Dixmit
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
from odoo import Command, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MailGatewayAbstract(models.AbstractModel):
|
|
7
|
+
_name = "mail.gateway.abstract"
|
|
8
|
+
_description = "Gateway abstract for functions"
|
|
9
|
+
|
|
10
|
+
def _verify_update(self, bot_data, kwargs):
|
|
11
|
+
return True
|
|
12
|
+
|
|
13
|
+
def _receive_update(self, gateway, kwargs):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
def _post_process_message(self, message, channel):
|
|
17
|
+
self.env["mail.notification"].search(
|
|
18
|
+
[("gateway_channel_id", "=", channel.id), ("is_read", "=", False)]
|
|
19
|
+
)._set_read_gateway()
|
|
20
|
+
|
|
21
|
+
def _post_process_reply(self, related_message):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
def _update_content_after_hook(self, channel, message):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
def _set_webhook(self, gateway):
|
|
28
|
+
gateway.integrated_webhook_state = "integrated"
|
|
29
|
+
|
|
30
|
+
def _remove_webhook(self, gateway):
|
|
31
|
+
gateway.integrated_webhook_state = False
|
|
32
|
+
|
|
33
|
+
def _get_channel(self, gateway, token, update, force_create=False):
|
|
34
|
+
chat_id = gateway._get_channel_id(token)
|
|
35
|
+
if chat_id:
|
|
36
|
+
return gateway.env["mail.channel"].browse(chat_id)
|
|
37
|
+
if not force_create and gateway.has_new_channel_security:
|
|
38
|
+
return False
|
|
39
|
+
channel = gateway.env["mail.channel"].create(
|
|
40
|
+
self._get_channel_vals(gateway, token, update)
|
|
41
|
+
)
|
|
42
|
+
channel._broadcast(channel.channel_member_ids.mapped("partner_id").ids)
|
|
43
|
+
return channel
|
|
44
|
+
|
|
45
|
+
def _get_author(self, gateway, update):
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
def _get_channel_vals(self, gateway, token, update):
|
|
49
|
+
author = self._get_author(gateway, update)
|
|
50
|
+
members = [
|
|
51
|
+
Command.create({"partner_id": partner.id, "is_pinned": True})
|
|
52
|
+
for partner in gateway.member_ids.partner_id
|
|
53
|
+
]
|
|
54
|
+
if author:
|
|
55
|
+
members.append(
|
|
56
|
+
Command.create(
|
|
57
|
+
{
|
|
58
|
+
"partner_id": author._name == "res.partner" and author.id,
|
|
59
|
+
"guest_id": author._name == "mail.guest" and author.id,
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
return {
|
|
64
|
+
"gateway_channel_token": token,
|
|
65
|
+
"gateway_id": gateway.id,
|
|
66
|
+
"channel_type": "gateway",
|
|
67
|
+
"channel_member_ids": members,
|
|
68
|
+
"company_id": gateway.company_id.id,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
def _send(
|
|
72
|
+
self,
|
|
73
|
+
gateway,
|
|
74
|
+
record,
|
|
75
|
+
auto_commit=False,
|
|
76
|
+
raise_exception=False,
|
|
77
|
+
parse_mode=False,
|
|
78
|
+
):
|
|
79
|
+
raise NotImplementedError()
|
|
80
|
+
|
|
81
|
+
def _get_message_body(self, record):
|
|
82
|
+
return record.mail_message_id.body
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Copyright 2024 Dixmit
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
from odoo import fields, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MailGuest(models.Model):
|
|
8
|
+
_inherit = "mail.guest"
|
|
9
|
+
|
|
10
|
+
gateway_id = fields.Many2one("mail.gateway")
|
|
11
|
+
gateway_token = fields.Char()
|
|
12
|
+
|
|
13
|
+
def _guest_format(self, fields=None):
|
|
14
|
+
result = super()._guest_format(fields=fields)
|
|
15
|
+
if not fields or "gateway_id" in fields:
|
|
16
|
+
for guest in result:
|
|
17
|
+
result[guest]["gateway"] = {"id": guest.gateway_id.id}
|
|
18
|
+
return result
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Copyright 2024 Dixmit
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from odoo import api, fields, models
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MailMessage(models.Model):
|
|
9
|
+
|
|
10
|
+
_inherit = "mail.message"
|
|
11
|
+
|
|
12
|
+
gateway_type = fields.Selection(
|
|
13
|
+
selection=lambda r: r.env["mail.gateway"]._fields["gateway_type"].selection
|
|
14
|
+
)
|
|
15
|
+
gateway_notification_ids = fields.One2many(
|
|
16
|
+
"mail.notification",
|
|
17
|
+
inverse_name="mail_message_id",
|
|
18
|
+
domain=[("notification_type", "=", "gateway")],
|
|
19
|
+
)
|
|
20
|
+
gateway_channel_ids = fields.Many2many(
|
|
21
|
+
"res.partner.gateway.channel", compute="_compute_gateway_channel_ids"
|
|
22
|
+
)
|
|
23
|
+
gateway_channel_data = fields.Json(compute="_compute_gateway_channel_ids")
|
|
24
|
+
gateway_message_ids = fields.One2many(
|
|
25
|
+
"mail.message",
|
|
26
|
+
inverse_name="gateway_message_id",
|
|
27
|
+
string="Child gateway messages",
|
|
28
|
+
)
|
|
29
|
+
gateway_message_id = fields.Many2one(
|
|
30
|
+
"mail.message", string="Original gateway message"
|
|
31
|
+
)
|
|
32
|
+
gateway_thread_data = fields.Json(compute="_compute_gateway_thread_data")
|
|
33
|
+
|
|
34
|
+
@api.depends("gateway_message_id")
|
|
35
|
+
def _compute_gateway_thread_data(self):
|
|
36
|
+
for record in self:
|
|
37
|
+
gateway_thread_data = {}
|
|
38
|
+
if record.gateway_message_id:
|
|
39
|
+
gateway_thread_data.update(
|
|
40
|
+
{
|
|
41
|
+
"name": record.gateway_message_id.record_name,
|
|
42
|
+
"id": record.gateway_message_id.res_id,
|
|
43
|
+
"model": record.gateway_message_id.model,
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
record.gateway_thread_data = gateway_thread_data
|
|
47
|
+
|
|
48
|
+
@api.depends("notification_ids", "gateway_message_ids")
|
|
49
|
+
def _compute_gateway_channel_ids(self):
|
|
50
|
+
for record in self:
|
|
51
|
+
if self.env.user.has_group("mail_gateway.gateway_user"):
|
|
52
|
+
channels = record.notification_ids.res_partner_id.gateway_channel_ids.filtered(
|
|
53
|
+
lambda r: (r.gateway_token, r.gateway_id.id)
|
|
54
|
+
not in [
|
|
55
|
+
(
|
|
56
|
+
notification.gateway_channel_id.gateway_channel_token,
|
|
57
|
+
notification.gateway_channel_id.gateway_id.id,
|
|
58
|
+
)
|
|
59
|
+
for notification in record.gateway_message_ids.gateway_notification_ids
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
else:
|
|
63
|
+
channels = self.env["res.partner.gateway.channel"]
|
|
64
|
+
record.gateway_channel_ids = channels
|
|
65
|
+
record.gateway_channel_data = {
|
|
66
|
+
"channels": channels.ids,
|
|
67
|
+
"partners": channels.partner_id.ids,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@api.depends("gateway_notification_ids")
|
|
71
|
+
def _compute_gateway_channel_id(self):
|
|
72
|
+
for rec in self:
|
|
73
|
+
if rec.gateway_notification_ids:
|
|
74
|
+
rec.gateway_channel_id = rec.gateway_notification_ids[
|
|
75
|
+
0
|
|
76
|
+
].gateway_channel_id
|
|
77
|
+
|
|
78
|
+
def _get_message_format_fields(self):
|
|
79
|
+
result = super()._get_message_format_fields()
|
|
80
|
+
result.append("gateway_type")
|
|
81
|
+
result.append("gateway_channel_data")
|
|
82
|
+
result.append("gateway_thread_data")
|
|
83
|
+
return result
|
|
84
|
+
|
|
85
|
+
def _send_to_gateway_thread(self, gateway_channel_id):
|
|
86
|
+
chat_id = gateway_channel_id.gateway_id._get_channel_id(
|
|
87
|
+
gateway_channel_id.gateway_token
|
|
88
|
+
)
|
|
89
|
+
channel = self.env["mail.channel"].browse(chat_id)
|
|
90
|
+
channel.message_post(**self._get_gateway_thread_message_vals())
|
|
91
|
+
if not self.gateway_type:
|
|
92
|
+
self.gateway_type = gateway_channel_id.gateway_id.gateway_type
|
|
93
|
+
self.env["mail.notification"].create(
|
|
94
|
+
{
|
|
95
|
+
"notification_status": "sent",
|
|
96
|
+
"mail_message_id": self.id,
|
|
97
|
+
"gateway_channel_id": channel.id,
|
|
98
|
+
"notification_type": "gateway",
|
|
99
|
+
"gateway_type": gateway_channel_id.gateway_id.gateway_type,
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
self.env["bus.bus"]._sendone(
|
|
103
|
+
self.env.user.partner_id,
|
|
104
|
+
"mail.message/insert",
|
|
105
|
+
{
|
|
106
|
+
"id": self.id,
|
|
107
|
+
"gateway_type": self.gateway_type,
|
|
108
|
+
"notifications": self.sudo()
|
|
109
|
+
.notification_ids._filtered_for_web_client()
|
|
110
|
+
._notification_format(),
|
|
111
|
+
},
|
|
112
|
+
)
|
|
113
|
+
return {}
|
|
114
|
+
|
|
115
|
+
def _get_gateway_thread_message_vals(self):
|
|
116
|
+
return {
|
|
117
|
+
"body": self.body,
|
|
118
|
+
"attachment_ids": self.attachment_ids.ids,
|
|
119
|
+
"subtype_id": self.subtype_id.id,
|
|
120
|
+
"author_id": self.env.user.partner_id.id,
|
|
121
|
+
"gateway_message_id": self.id,
|
|
122
|
+
"message_type": "comment",
|
|
123
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Copyright 2024 Dixmit
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
from odoo import fields, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MailNotification(models.Model):
|
|
8
|
+
_inherit = "mail.notification"
|
|
9
|
+
|
|
10
|
+
gateway_channel_id = fields.Many2one("mail.channel")
|
|
11
|
+
notification_type = fields.Selection(
|
|
12
|
+
selection_add=[("gateway", "Gateway")], ondelete={"gateway": "cascade"}
|
|
13
|
+
)
|
|
14
|
+
gateway_message_id = fields.Char(readonly=True)
|
|
15
|
+
gateway_failure_reason = fields.Text(
|
|
16
|
+
readonly=1,
|
|
17
|
+
help="Failure reason. This is usually the exception thrown by the"
|
|
18
|
+
" email server, stored to ease the debugging of mailing issues.",
|
|
19
|
+
)
|
|
20
|
+
gateway_type = fields.Selection(
|
|
21
|
+
selection=lambda r: r.env["mail.gateway"]._fields["gateway_type"].selection
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def _set_read_gateway(self):
|
|
25
|
+
self.sudo().write({"is_read": True, "read_date": fields.Datetime.now()})
|
|
26
|
+
|
|
27
|
+
def _notification_format(self):
|
|
28
|
+
result = super()._notification_format()
|
|
29
|
+
for record, formatted_value in zip(self, result):
|
|
30
|
+
formatted_value["gateway_type"] = record.gateway_type
|
|
31
|
+
formatted_value["channel_name"] = record.gateway_channel_id.name
|
|
32
|
+
return result
|
|
33
|
+
|
|
34
|
+
def send_gateway(self, auto_commit=False, raise_exception=False, parse_mode="HTML"):
|
|
35
|
+
for record in self:
|
|
36
|
+
gateway = record.gateway_channel_id.gateway_id
|
|
37
|
+
self.env["mail.gateway.%s" % gateway.gateway_type]._send(
|
|
38
|
+
gateway,
|
|
39
|
+
record,
|
|
40
|
+
auto_commit=auto_commit,
|
|
41
|
+
raise_exception=raise_exception,
|
|
42
|
+
parse_mode=parse_mode,
|
|
43
|
+
)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Copyright 2024 Dixmit
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
from odoo import models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MailThread(models.AbstractModel):
|
|
7
|
+
_inherit = "mail.thread"
|
|
8
|
+
|
|
9
|
+
def _notify_thread_by_email(self, message, recipients_data, **kwargs):
|
|
10
|
+
partners_data = [r for r in recipients_data if r["notif"] == "gateway"]
|
|
11
|
+
if partners_data:
|
|
12
|
+
self._notify_thread_by_gateway(message, partners_data, **kwargs)
|
|
13
|
+
return super()._notify_thread_by_email(message, recipients_data, **kwargs)
|
|
14
|
+
|
|
15
|
+
def _notify_thread_by_gateway(self, message, partners_data, **kwargs):
|
|
16
|
+
for partner_data in partners_data:
|
|
17
|
+
if partner_data["notif"] != "gateway" or not partner_data.get(
|
|
18
|
+
"gateway_channel_id"
|
|
19
|
+
):
|
|
20
|
+
continue
|
|
21
|
+
message._send_to_gateway_thread(
|
|
22
|
+
self.env["res.partner.gateway.channel"].browse(
|
|
23
|
+
partner_data.get("gateway_channel_id")
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def _notify_get_recipients(self, message, msg_vals, **kwargs):
|
|
28
|
+
if "gateway_notifications" in kwargs:
|
|
29
|
+
result = []
|
|
30
|
+
for notification in kwargs["gateway_notifications"]:
|
|
31
|
+
if not notification.get("channel_type"):
|
|
32
|
+
continue
|
|
33
|
+
partner = self.env["res.partner"].browse(notification["partner_id"])
|
|
34
|
+
user = partner.user_ids
|
|
35
|
+
follower_data = {
|
|
36
|
+
"active": partner.active,
|
|
37
|
+
"id": partner.id,
|
|
38
|
+
"is_follower": True,
|
|
39
|
+
"lang": partner.lang,
|
|
40
|
+
"groups": set(user.groups_id.ids),
|
|
41
|
+
"notif": notification.get("channel_type"),
|
|
42
|
+
"share": partner.partner_share,
|
|
43
|
+
"uid": user[:1].id,
|
|
44
|
+
"ushare": user and any(user.mapped("share")),
|
|
45
|
+
"gateway_channel_id": notification.get("gateway_channel_id"),
|
|
46
|
+
}
|
|
47
|
+
if follower_data["ushare"]: # any type of share user
|
|
48
|
+
follower_data["type"] = "portal"
|
|
49
|
+
elif follower_data[
|
|
50
|
+
"share"
|
|
51
|
+
]: # no user, is share -> customer (partner only)
|
|
52
|
+
follower_data["type"] = "customer"
|
|
53
|
+
else: # has a user not share -> internal user
|
|
54
|
+
follower_data["type"] = "user"
|
|
55
|
+
result.append(follower_data)
|
|
56
|
+
return result
|
|
57
|
+
return super()._notify_get_recipients(message, msg_vals, **kwargs)
|
|
58
|
+
|
|
59
|
+
def _check_can_update_message_content(self, messages):
|
|
60
|
+
# We can delete the messages comming from a gateway on not channels
|
|
61
|
+
if self._name != "mail.channel":
|
|
62
|
+
new_messages = messages.filtered(lambda r: not r.gateway_message_ids)
|
|
63
|
+
else:
|
|
64
|
+
new_messages = messages
|
|
65
|
+
return super()._check_can_update_message_content(new_messages)
|
|
66
|
+
|
|
67
|
+
def _message_update_content(
|
|
68
|
+
self, message, body, attachment_ids=None, strict=True, **kwargs
|
|
69
|
+
):
|
|
70
|
+
result = super()._message_update_content(
|
|
71
|
+
message, body, attachment_ids=attachment_ids, strict=strict, **kwargs
|
|
72
|
+
)
|
|
73
|
+
if body == "":
|
|
74
|
+
# Unlink the message
|
|
75
|
+
for gateway_message in message.gateway_message_ids:
|
|
76
|
+
gateway_message.gateway_message_id = False
|
|
77
|
+
self.env["bus.bus"]._sendone(
|
|
78
|
+
self.env.user.partner_id,
|
|
79
|
+
"mail.message/insert",
|
|
80
|
+
{
|
|
81
|
+
"id": gateway_message.id,
|
|
82
|
+
"gateway_thread_data": gateway_message.sudo().gateway_thread_data,
|
|
83
|
+
},
|
|
84
|
+
)
|
|
85
|
+
return result
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Copyright 2024 Dixmit
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
from odoo import fields, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ResPartner(models.Model):
|
|
8
|
+
"""Update of res.partner class to take into account the gateway."""
|
|
9
|
+
|
|
10
|
+
_inherit = "res.partner"
|
|
11
|
+
|
|
12
|
+
gateway_channel_ids = fields.One2many(
|
|
13
|
+
"res.partner.gateway.channel", inverse_name="partner_id"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
def mail_partner_format(self, fields=None):
|
|
17
|
+
"""Override to add gateway info."""
|
|
18
|
+
partners_format = super().mail_partner_format(fields=fields)
|
|
19
|
+
if not fields:
|
|
20
|
+
fields = {"gateway_channel_ids": True}
|
|
21
|
+
for partner in self:
|
|
22
|
+
if "gateway_channel_ids" in fields:
|
|
23
|
+
partners_format.get(partner).update(
|
|
24
|
+
{
|
|
25
|
+
"gateway_channels": partner.gateway_channel_ids.mail_format(),
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
return partners_format
|
|
29
|
+
|
|
30
|
+
def _get_channels_as_member(self):
|
|
31
|
+
channels = super()._get_channels_as_member()
|
|
32
|
+
if self.env.user.has_group("mail_gateway.gateway_user"):
|
|
33
|
+
channels |= self.env["mail.channel"].search(
|
|
34
|
+
[
|
|
35
|
+
("channel_type", "=", "gateway"),
|
|
36
|
+
(
|
|
37
|
+
"channel_member_ids",
|
|
38
|
+
"in",
|
|
39
|
+
self.env["mail.channel.member"]
|
|
40
|
+
.sudo()
|
|
41
|
+
._search(
|
|
42
|
+
[
|
|
43
|
+
("partner_id", "=", self.id),
|
|
44
|
+
("is_pinned", "=", True),
|
|
45
|
+
]
|
|
46
|
+
),
|
|
47
|
+
),
|
|
48
|
+
]
|
|
49
|
+
)
|
|
50
|
+
return channels
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ResPartnerGatewayChannel(models.Model):
|
|
54
|
+
_name = "res.partner.gateway.channel"
|
|
55
|
+
_description = "Technical data used to get the gateway author"
|
|
56
|
+
|
|
57
|
+
name = fields.Char(related="gateway_id.name")
|
|
58
|
+
partner_id = fields.Many2one(
|
|
59
|
+
"res.partner", required=True, readonly=True, ondelete="cascade"
|
|
60
|
+
)
|
|
61
|
+
gateway_id = fields.Many2one(
|
|
62
|
+
"mail.gateway", required=True, readonly=True, ondelete="cascade"
|
|
63
|
+
)
|
|
64
|
+
gateway_token = fields.Char(readonly=True)
|
|
65
|
+
company_id = fields.Many2one(
|
|
66
|
+
"res.company", related="gateway_id.company_id", store=True
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
_sql_constraints = [
|
|
70
|
+
(
|
|
71
|
+
"unique_partner_gateway",
|
|
72
|
+
"UNIQUE(partner_id, gateway_id)",
|
|
73
|
+
"Partner can only have one configuration for each gateway.",
|
|
74
|
+
),
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
def mail_format(self):
|
|
78
|
+
return [r._mail_format() for r in self]
|
|
79
|
+
|
|
80
|
+
def _mail_format(self):
|
|
81
|
+
return {
|
|
82
|
+
"id": self.id,
|
|
83
|
+
"name": self.name,
|
|
84
|
+
"gateway": {
|
|
85
|
+
"id": self.gateway_id.id,
|
|
86
|
+
"name": self.gateway_id.name,
|
|
87
|
+
"type": self.gateway_id.gateway_type,
|
|
88
|
+
},
|
|
89
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Copyright 2024 Dixmit
|
|
2
|
+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
from odoo import fields, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ResUsers(models.Model):
|
|
8
|
+
|
|
9
|
+
_inherit = "res.users"
|
|
10
|
+
|
|
11
|
+
gateway_ids = fields.Many2many("mail.gateway")
|
|
12
|
+
|
|
13
|
+
def _init_messaging(self):
|
|
14
|
+
result = super()._init_messaging()
|
|
15
|
+
result["gateways"] = self.gateway_ids.gateway_info()
|
|
16
|
+
return result
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This work has been funded by AEOdoo (Asociación Española de Odoo - https://www.aeodoo.org)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
This module will allow you to integrate an external chat system in your Odoo system.
|
|
2
|
+
It requires extra modules with the specific configuration of each chat system, like `mail_gateway_telegram` or `mail_gateway_whatsapp`.
|
|
3
|
+
|
|
4
|
+
This way, a group of users can respond customers or any other set
|
|
5
|
+
of partners within Odoo, but the messages will be sent through the external chat system.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
When external messages are received, they will be directly sent to the discuss menu.
|
|
2
|
+
Answering to these messages will send the answer to the external contact.
|
|
3
|
+
We can assign this messages to any record using the message actions.
|
|
4
|
+
Also, we can assign the sender to a partner using the followers menu and selecting the partner.
|
|
5
|
+
|
|
6
|
+
On a standard record associated to a partner with external chat, we can send messages to the external contact directly selecting the methods of the partner.
|
|
7
|
+
To use this, we just need to use the
|
|
8
|
+
|
|
9
|
+
It is recomended to enable chatter notification to all users that will receive messages from gateways.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|
2
|
+
access_res_partner_gateway_channel_portal,res.partner.gateway.channel.portal,model_res_partner_gateway_channel,base.group_portal,1,0,0,0
|
|
3
|
+
access_res_partner_gateway_channel_user,res.partner.gateway.channel,model_res_partner_gateway_channel,base.group_user,1,0,0,0
|
|
4
|
+
manage_res_partner_gateway_channel_user,res.partner.gateway.channel,model_res_partner_gateway_channel,gateway_user,1,1,1,1
|
|
5
|
+
access_mail_message_gateway_send_user,mail.message.gateway.send,model_mail_message_gateway_send,base.group_user,1,1,1,0
|
|
6
|
+
access_mail_gateway_all,mail.telegram.bot.all,model_mail_gateway,,1,0,0,0
|
|
7
|
+
access_mail_guest_manage,mail.telegram.bot.all,model_mail_guest_manage,base.group_user,1,1,1,1
|
|
8
|
+
access_mail_message_gateway_link,mail.message.link.all,model_mail_message_gateway_link,base.group_user,1,1,1,1
|
|
9
|
+
access_mail_gateway_system,mail_gateway,model_mail_gateway,base.group_system,1,1,1,1
|