Files
odoo_source/addons/mail_bot/models/mail_bot.py
T
Martin Trigaux d6a0e9db81 [FIX] mail_bot: do not crash on user with no email
When creating a new user, the linked partner may not yet have an email address
(not mandatory field).
When logging in for the first time, after some time an error comes up
   "Unable to post message, please configure the sender's email address."

While the user should set up an email, this popup coming from no specific action
is confusing.

With this patch, the message will be posted as the system (which has an email)
The user will be warned of having no email when trying to reply.

Closes #26601
2018-09-03 11:06:59 +02:00

223 lines
11 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}).sudo().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
odoobot_state = self.env.user.odoobot_state
if self._is_bot_in_private_channel(record):
# main flow
if odoobot_state == 'onboarding_emoji' and self._body_contains_emoji(body):
self.env.user.odoobot_state = "onboarding_attachement"
return _("Great! 👍<br/>Now, try to <b>send an attachment</b>, like a picture of your cute dog...")
elif odoobot_state == 'onboarding_attachement' and values.get("attachment_ids"):
self.env.user.odoobot_state = "onboarding_command"
return _("Not a cute dog, but you get it 😊<br/>To access special features, <b>start your sentence with '/'</b>. Try to get help.")
elif odoobot_state == 'onboarding_command' and command == 'help':
self.env.user.odoobot_state = "onboarding_ping"
return _("Wow you are a natural!<br/>Ping someone to grab its attention with @nameoftheuser. <b>Try to ping me using @OdooBot</b> in a sentence.")
elif odoobot_state == 'onboarding_ping' and self._is_bot_pinged(values):
self.env.user.odoobot_state = "idle"
return _("Yep, I am here! 🎉 <br/>You finished the tour, you can <b>close this chat window</b>. Enjoy discovering Odoo.")
elif odoobot_state == "idle" and (_('start the tour') in body.lower()):
self.env.user.odoobot_state = "onboarding_emoji"
return _("To start, try to send me an emoji :)")
# easter eggs
elif odoobot_state == "idle" and 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 odoobot_state == "idle" and (('help' in body) or _('help') in body):
return _("I'm just a bot... :( You can 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 nice! I'm a bot but I have feelings... 💔")
else:
#repeat question
if odoobot_state == 'onboarding_emoji':
return _("Not exactly. To continue the tour, send an emoji, <b>type \":)\"</b> and press enter.")
elif odoobot_state == 'onboarding_attachement':
return _("To <b>send an attachment</b>, click the 📎 icon on the right, and select a file.")
elif odoobot_state == 'onboarding_command':
return _("Not sure wat you are doing. Please press / and wait for the propositions. Select \"help\" and press enter")
elif odoobot_state == 'onboarding_ping':
return _("Sorry, I am not listening. To get someone's attention, <b>ping him</b>. Write \"@odoobot\" and select me.")
return random.choice([
_("I'm not smart enough to answer your question.<br/>To follow my guide, ask") + ": <b>"+_('start the tour') + "</b>",
_("Hmmm..."),
_("I'm afraid I don't understand. Sorry!"),
_("Sorry I'm sleepy. Or not! Maybe I'm just trying to hide my unawareness of human language...<br/>I can show you features if you write")+ ": '<b>"+_('start the tour')+"</b>'.",
])
elif self._is_bot_pinged(values):
return random.choice([_("Yep, OdooBot is in the place!"), _("Pong.")])
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