214 lines
10 KiB
Python
214 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import itertools
|
|
import random
|
|
|
|
from odoo import models, _
|
|
|
|
|
|
class MailBot(models.AbstractModel):
|
|
_name = 'mail.bot'
|
|
|
|
def _apply_logic(self, record, values, command=None):
|
|
""" Apply bot logic to generate an answer (or not) for the user
|
|
The logic will only be applied if odoobot is in a chat with a user or
|
|
if someone pinged odoobot.
|
|
|
|
:param record: the mail_thread (or mail_channel) where the user
|
|
message was posted/odoobot will answer.
|
|
:param values: msg_values of the message_post or other values needed by logic
|
|
:param command: the name of the called command if the logic is not triggered by a message_post
|
|
"""
|
|
odoobot_id = self.env['ir.model.data'].xmlid_to_res_id("mail_bot.partner_odoobot")
|
|
if len(record) != 1 or values.get("author_id") == odoobot_id:
|
|
return
|
|
if self._is_bot_pinged(values) or self._is_bot_in_private_channel(record):
|
|
body = values.get("body", "").replace(u'\xa0', u' ').strip().lower().strip(".?!")
|
|
answer = self._get_answer(record, body, values, command)
|
|
if answer:
|
|
message_type = values.get('message_type', 'comment')
|
|
subtype_id = values.get('subtype_id', self.env['ir.model.data'].xmlid_to_res_id('mail.mt_comment'))
|
|
record.with_context({"mail_create_nosubscribe": True}).message_post(body=answer, author_id=odoobot_id, message_type=message_type, subtype_id=subtype_id)
|
|
|
|
def _get_answer(self, record, body, values, command=False):
|
|
# onboarding
|
|
if self._is_bot_in_private_channel(record):
|
|
# main flow
|
|
if body in ('hi', 'hello'):
|
|
return _("Hi, I will guide you through chat features. First, try to send me an emoji 😊")
|
|
elif self._body_contains_emoji(body):
|
|
return _("Great! :) Did you notice that you can also send attachments, like a picture of your cute dog? Try it!")
|
|
elif values.get("attachment_ids"):
|
|
return _("Not a cute dog, but you get it :) To access special features, start your sentence with '/' (e.g. /help).")
|
|
elif command == 'help':
|
|
return _("Wow you are a natural! Ping someone to grab its attention with @nameoftheuser. Try to ping me with <b>@OdooBot</b>.")
|
|
elif self._is_bot_pinged(values):
|
|
discuss_href = 'href="/web#action=mail.mail_channel_action_client_chat&active_id=%s"' % record.id
|
|
discuss_src = 'src="/mail_bot/static/img/odoobot_discuss.png"'
|
|
chatter_src = 'src="/mail_bot/static/img/odoobot_chatter.png"'
|
|
return _("That's me! 🎉<br/>") + \
|
|
_("There are 3 different ways in Odoo to interact with your colleagues: <br/>\
|
|
-via this chat window<br/>\
|
|
-via the <a href=%s>Discuss</a> application:<br/><img %s/><br/><br/>\
|
|
-or via the chatter:<br/><img %s/><br/><br/>\
|
|
Aaaaand that's it! Enjoy discovering Odoo!") % (discuss_href, discuss_src, chatter_src)
|
|
# easter eggs
|
|
elif body in ['❤️', _('i love you'), _('love')]:
|
|
return _("Aaaaaw that's really cute but, you know, bots don't work that way. You're too human for me! Let's keep it professional ❤️")
|
|
elif body in [_('help'), _('help me'), _('i need help')]:
|
|
return _("I'm trying to help you, but I'm just a bot... :( You can also check <a href=\"https://www.odoo.com/page/docs\">our documentation</a>) for more information!")
|
|
elif _('fuck') in body or "fuck" in body:
|
|
return _("That's not a really nice thing to say, you know? I'm a bot but I have feelings, ok?! 💔")
|
|
else:
|
|
return random.choice([
|
|
_("Mmmmh I'm not sure what you mean.. Can you try again?"),
|
|
_("I'm afraid I don't understand. Sorry!")
|
|
])
|
|
elif self._is_bot_pinged(values):
|
|
return random.choice([_("Yaaaay that's me!"), _("Odeyhoooo")])
|
|
return False
|
|
|
|
def _body_contains_emoji(self, body):
|
|
# coming from https://unicode.org/emoji/charts/full-emoji-list.html
|
|
emoji_list = itertools.chain(
|
|
range(0x231A, 0x231c),
|
|
range(0x23E9, 0x23f4),
|
|
range(0x23F8, 0x23fb),
|
|
range(0x25AA, 0x25ac),
|
|
range(0x25FB, 0x25ff),
|
|
range(0x2600, 0x2605),
|
|
range(0x2614, 0x2616),
|
|
range(0x2622, 0x2624),
|
|
range(0x262E, 0x2630),
|
|
range(0x2638, 0x263b),
|
|
range(0x2648, 0x2654),
|
|
range(0x265F, 0x2661),
|
|
range(0x2665, 0x2667),
|
|
range(0x267E, 0x2680),
|
|
range(0x2692, 0x2698),
|
|
range(0x269B, 0x269d),
|
|
range(0x26A0, 0x26a2),
|
|
range(0x26AA, 0x26ac),
|
|
range(0x26B0, 0x26b2),
|
|
range(0x26BD, 0x26bf),
|
|
range(0x26C4, 0x26c6),
|
|
range(0x26D3, 0x26d5),
|
|
range(0x26E9, 0x26eb),
|
|
range(0x26F0, 0x26f6),
|
|
range(0x26F7, 0x26fb),
|
|
range(0x2708, 0x270a),
|
|
range(0x270A, 0x270c),
|
|
range(0x270C, 0x270e),
|
|
range(0x2733, 0x2735),
|
|
range(0x2753, 0x2756),
|
|
range(0x2763, 0x2765),
|
|
range(0x2795, 0x2798),
|
|
range(0x2934, 0x2936),
|
|
range(0x2B05, 0x2b08),
|
|
range(0x2B1B, 0x2b1d),
|
|
range(0x1F170, 0x1f172),
|
|
range(0x1F191, 0x1f19b),
|
|
range(0x1F1E6, 0x1f200),
|
|
range(0x1F201, 0x1f203),
|
|
range(0x1F232, 0x1f23b),
|
|
range(0x1F250, 0x1f252),
|
|
range(0x1F300, 0x1f321),
|
|
range(0x1F324, 0x1f32d),
|
|
range(0x1F32D, 0x1f330),
|
|
range(0x1F330, 0x1f336),
|
|
range(0x1F337, 0x1f37d),
|
|
range(0x1F37E, 0x1f380),
|
|
range(0x1F380, 0x1f394),
|
|
range(0x1F396, 0x1f398),
|
|
range(0x1F399, 0x1f39c),
|
|
range(0x1F39E, 0x1f3a0),
|
|
range(0x1F3A0, 0x1f3c5),
|
|
range(0x1F3C6, 0x1f3cb),
|
|
range(0x1F3CB, 0x1f3cf),
|
|
range(0x1F3CF, 0x1f3d4),
|
|
range(0x1F3D4, 0x1f3e0),
|
|
range(0x1F3E0, 0x1f3f1),
|
|
range(0x1F3F3, 0x1f3f6),
|
|
range(0x1F3F8, 0x1f400),
|
|
range(0x1F400, 0x1f43f),
|
|
range(0x1F442, 0x1f4f8),
|
|
range(0x1F4F9, 0x1f4fd),
|
|
range(0x1F500, 0x1f53e),
|
|
range(0x1F549, 0x1f54b),
|
|
range(0x1F54B, 0x1f54f),
|
|
range(0x1F550, 0x1f568),
|
|
range(0x1F56F, 0x1f571),
|
|
range(0x1F573, 0x1f57a),
|
|
range(0x1F58A, 0x1f58e),
|
|
range(0x1F595, 0x1f597),
|
|
range(0x1F5B1, 0x1f5b3),
|
|
range(0x1F5C2, 0x1f5c5),
|
|
range(0x1F5D1, 0x1f5d4),
|
|
range(0x1F5DC, 0x1f5df),
|
|
range(0x1F5FB, 0x1f600),
|
|
range(0x1F601, 0x1f611),
|
|
range(0x1F612, 0x1f615),
|
|
range(0x1F61C, 0x1f61f),
|
|
range(0x1F620, 0x1f626),
|
|
range(0x1F626, 0x1f628),
|
|
range(0x1F628, 0x1f62c),
|
|
range(0x1F62E, 0x1f630),
|
|
range(0x1F630, 0x1f634),
|
|
range(0x1F635, 0x1f641),
|
|
range(0x1F641, 0x1f643),
|
|
range(0x1F643, 0x1f645),
|
|
range(0x1F645, 0x1f650),
|
|
range(0x1F680, 0x1f6c6),
|
|
range(0x1F6CB, 0x1f6d0),
|
|
range(0x1F6D1, 0x1f6d3),
|
|
range(0x1F6E0, 0x1f6e6),
|
|
range(0x1F6EB, 0x1f6ed),
|
|
range(0x1F6F4, 0x1f6f7),
|
|
range(0x1F6F7, 0x1f6f9),
|
|
range(0x1F910, 0x1f919),
|
|
range(0x1F919, 0x1f91f),
|
|
range(0x1F920, 0x1f928),
|
|
range(0x1F928, 0x1f930),
|
|
range(0x1F931, 0x1f933),
|
|
range(0x1F933, 0x1f93b),
|
|
range(0x1F93C, 0x1f93f),
|
|
range(0x1F940, 0x1f946),
|
|
range(0x1F947, 0x1f94c),
|
|
range(0x1F94D, 0x1f950),
|
|
range(0x1F950, 0x1f95f),
|
|
range(0x1F95F, 0x1f96c),
|
|
range(0x1F96C, 0x1f971),
|
|
range(0x1F973, 0x1f977),
|
|
range(0x1F97C, 0x1f980),
|
|
range(0x1F980, 0x1f985),
|
|
range(0x1F985, 0x1f992),
|
|
range(0x1F992, 0x1f998),
|
|
range(0x1F998, 0x1f9a3),
|
|
range(0x1F9B0, 0x1f9ba),
|
|
range(0x1F9C1, 0x1f9c3),
|
|
range(0x1F9D0, 0x1f9e7),
|
|
range(0x1F9E7, 0x1fa00),
|
|
[0x2328, 0x23cf, 0x24c2, 0x25b6, 0x25c0, 0x260e, 0x2611, 0x2618, 0x261d, 0x2620, 0x2626,
|
|
0x262a, 0x2640, 0x2642, 0x2663, 0x2668, 0x267b, 0x2699, 0x26c8, 0x26ce, 0x26cf,
|
|
0x26d1, 0x26fd, 0x2702, 0x2705, 0x270f, 0x2712, 0x2714, 0x2716, 0x271d, 0x2721, 0x2728, 0x2744, 0x2747, 0x274c,
|
|
0x274e, 0x2757, 0x27a1, 0x27b0, 0x27bf, 0x2b50, 0x2b55, 0x3030, 0x303d, 0x3297, 0x3299, 0x1f004, 0x1f0cf, 0x1f17e,
|
|
0x1f17f, 0x1f18e, 0x1f21a, 0x1f22f, 0x1f321, 0x1f336, 0x1f37d, 0x1f3c5, 0x1f3f7, 0x1f43f, 0x1f440, 0x1f441, 0x1f4f8,
|
|
0x1f4fd, 0x1f4ff, 0x1f57a, 0x1f587, 0x1f590, 0x1f5a4, 0x1f5a5, 0x1f5a8, 0x1f5bc, 0x1f5e1, 0x1f5e3, 0x1f5e8, 0x1f5ef,
|
|
0x1f5f3, 0x1f5fa, 0x1f600, 0x1f611, 0x1f615, 0x1f616, 0x1f617, 0x1f618, 0x1f619, 0x1f61a, 0x1f61b, 0x1f61f, 0x1f62c,
|
|
0x1f62d, 0x1f634, 0x1f6d0, 0x1f6e9, 0x1f6f0, 0x1f6f3, 0x1f6f9, 0x1f91f, 0x1f930, 0x1f94c, 0x1f97a, 0x1f9c0]
|
|
)
|
|
if any(chr(emoji) in body for emoji in emoji_list):
|
|
return True
|
|
return False
|
|
|
|
def _is_bot_pinged(self, values):
|
|
odoobot_id = self.env['ir.model.data'].xmlid_to_res_id("mail_bot.partner_odoobot")
|
|
return (4, odoobot_id) in values.get('partner_ids', [])
|
|
|
|
def _is_bot_in_private_channel(self, record):
|
|
odoobot_id = self.env['ir.model.data'].xmlid_to_res_id("mail_bot.partner_odoobot")
|
|
if record._name == 'mail.channel' and record.channel_type == 'chat':
|
|
return odoobot_id in record.with_context(active_test=False).channel_partner_ids.ids
|
|
return False
|