From 2df29bcfbdf8e754903b6ef3a0a7dddc00f61b08 Mon Sep 17 00:00:00 2001 From: XavierDo Date: Mon, 13 Aug 2018 18:49:19 +0200 Subject: [PATCH] [IMP] mail_bot: make odoobot statefull The current implementation of odoobot is stateless, making him a little dummy. Adding a state on the user allow to be sure that the user don't skip a step, or loop back to a previous step. States also allows odoobot to repeat the question when the user don't give the right answer. A quick modification asked by FP before freeze, in order to change the field type in time. --- .../im_livechat_mail_bot/models/__init__.py | 1 + .../im_livechat_mail_bot/models/mail_bot.py | 10 ++++-- .../im_livechat_mail_bot/models/res_users.py | 12 +++++++ addons/mail_bot/models/ir_http.py | 2 +- addons/mail_bot/models/mail_bot.py | 32 +++++++++++++------ addons/mail_bot/models/mail_channel.py | 4 +-- addons/mail_bot/models/res_users.py | 12 +++++-- addons/test_mail/tests/test_odoobot.py | 10 +----- 8 files changed, 57 insertions(+), 26 deletions(-) create mode 100644 addons/im_livechat_mail_bot/models/res_users.py diff --git a/addons/im_livechat_mail_bot/models/__init__.py b/addons/im_livechat_mail_bot/models/__init__.py index ce6a8455179..dc2d4b2fef6 100644 --- a/addons/im_livechat_mail_bot/models/__init__.py +++ b/addons/im_livechat_mail_bot/models/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import mail_bot +from . import res_users diff --git a/addons/im_livechat_mail_bot/models/mail_bot.py b/addons/im_livechat_mail_bot/models/mail_bot.py index 57eacc5db5d..0c042d56fab 100644 --- a/addons/im_livechat_mail_bot/models/mail_bot.py +++ b/addons/im_livechat_mail_bot/models/mail_bot.py @@ -8,10 +8,13 @@ class MailBot(models.AbstractModel): _inherit = 'mail.bot' def _get_answer(self, record, body, values, command): + odoobot_state = self.env.user.odoobot_state if self._is_bot_in_private_channel(record): - if self._is_bot_pinged(values): + if odoobot_state == "onboarding_ping" and self._is_bot_pinged(values): + self.env.user.odoobot_state = "onboarding_canned" return _("That's me! 🎉
Try to type \":\" to use canned responses.") - elif values.get("canned_response_ids"): + elif odoobot_state == "onboarding_canned" and values.get("canned_response_ids"): + self.env.user.odoobot_state = "idle" 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"' @@ -21,5 +24,8 @@ class MailBot(models.AbstractModel): -via the Discuss application:


\ -or via the chatter:


\ Aaaaand that's it! Enjoy discovering Odoo!") % (discuss_href, discuss_src, chatter_src) + #repeat question if needed + elif odoobot_state == 'onboarding_canned': + return _("Not sure wat you are doing. Please press : and wait for the propositions. Select one of them and press enter.") return super(MailBot, self)._get_answer(record, body, values, command) diff --git a/addons/im_livechat_mail_bot/models/res_users.py b/addons/im_livechat_mail_bot/models/res_users.py new file mode 100644 index 00000000000..7e4a526b897 --- /dev/null +++ b/addons/im_livechat_mail_bot/models/res_users.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, fields + +class Users(models.Model): + _inherit = 'res.users' + odoobot_state = fields.Selection( + selection_add=[ + ('onboarding_canned', 'Onboarding canned'), + ]) + diff --git a/addons/mail_bot/models/ir_http.py b/addons/mail_bot/models/ir_http.py index ae01cb3e76f..09d997dbca0 100644 --- a/addons/mail_bot/models/ir_http.py +++ b/addons/mail_bot/models/ir_http.py @@ -9,5 +9,5 @@ class Http(models.AbstractModel): def session_info(self): res = super(Http, self).session_info() - res['odoobot_initialized'] = self.env.user.odoobot_initialized + res['odoobot_initialized'] = self.env.user.odoobot_state == 'not_initialized' return res diff --git a/addons/mail_bot/models/mail_bot.py b/addons/mail_bot/models/mail_bot.py index c3a6e74b3c5..50d43185454 100644 --- a/addons/mail_bot/models/mail_bot.py +++ b/addons/mail_bot/models/mail_bot.py @@ -33,17 +33,20 @@ class MailBot(models.AbstractModel): 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 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): + if odoobot_state == 'onboarding_emoji' and self._body_contains_emoji(body): + self.env.user.odoobot_state = "onboarding_attachement" 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"): + 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 :) To access special features, start your sentence with '/' (e.g. /help).") - elif command == 'help': + elif odoobot_state == 'onboarding_command' and command == 'help': + self.env.user.odoobot_state = "onboarding_ping" return _("Wow you are a natural! Ping someone to grab its attention with @nameoftheuser. Try to ping me with @OdooBot.") - elif self._is_bot_pinged(values): + elif odoobot_state == 'onboarding_ping' and self._is_bot_pinged(values): + self.env.user.odoobot_state = "idle" 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"' @@ -54,15 +57,24 @@ class MailBot(models.AbstractModel): -or via the chatter:


\ Aaaaand that's it! Enjoy discovering Odoo!") % (discuss_href, discuss_src, chatter_src) # easter eggs - elif body in ['❤️', _('i love you'), _('love')]: + 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 body in [_('help'), _('help me'), _('i need help')]: + elif odoobot_state == "idle" and 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 our documentation) for more information!") - elif _('fuck') in body or "fuck" in body: + elif odoobot_state == "idle" and _('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: + #repeat question + if odoobot_state == 'onboarding_emoji': + return _("Not exactly. To send an emoji, type \":)\" and press enter.") + elif odoobot_state == 'onboarding_attachement': + return _("I want you to send me an attachement, you should press the button next to the chat bar to 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 _("Please, ping me. Type and \"@\" and begin to type \"odoobot\". Select the proposition and press enter.") return random.choice([ - _("Mmmmh I'm not sure what you mean.. Can you try again?"), + _("I'm not smart enough to answer your question, sorry."), _("I'm afraid I don't understand. Sorry!") ]) elif self._is_bot_pinged(values): diff --git a/addons/mail_bot/models/mail_channel.py b/addons/mail_bot/models/mail_channel.py index 23978eff1e6..60a8f7dd07d 100644 --- a/addons/mail_bot/models/mail_channel.py +++ b/addons/mail_bot/models/mail_channel.py @@ -25,7 +25,7 @@ class Channel(models.Model): @api.model def init_odoobot(self): - if not self.env.user.odoobot_initialized: + if self.env.user.odoobot_state == 'not_initialized': partner = self.env.user.partner_id odoobot_id = self.env['ir.model.data'].xmlid_to_res_id("mail_bot.partner_odoobot") channel = self.with_context({"mail_create_nosubscribe": True}).create({ @@ -37,5 +37,5 @@ class Channel(models.Model): }) message = _("Hello, I'm here to help you discover chat features. Try answering me with an emoji 😊") channel.message_post(body=message, author_id=odoobot_id, message_type="comment", subtype="mail.mt_comment") - self.env.user.odoobot_initialized = True + self.env.user.odoobot_state = 'onboarding_emoji' return channel diff --git a/addons/mail_bot/models/res_users.py b/addons/mail_bot/models/res_users.py index 8c880b11a6e..2905e3d8f5e 100644 --- a/addons/mail_bot/models/res_users.py +++ b/addons/mail_bot/models/res_users.py @@ -5,5 +5,13 @@ from odoo import models, fields class Users(models.Model): _inherit = 'res.users' - - odoobot_initialized = fields.Boolean(readonly=True, default=False) + odoobot_state = fields.Selection( + [ + ('not_initialized', 'Not initialized'), + ('onboarding_emoji', 'Onboarding emoji'), + ('onboarding_attachement', 'Onboarding attachement'), + ('onboarding_command', 'Onboarding command'), + ('onboarding_ping', 'Onboarding ping'), + ('idle', 'Idle'), + ('disabled', 'Disabled'), + ], string="Odoobot Status", readonly=True, required=True, default="not_initialized") # keep track of the state: correspond to the code of the last message sent diff --git a/addons/test_mail/tests/test_odoobot.py b/addons/test_mail/tests/test_odoobot.py index b171020244a..bf0f235069f 100644 --- a/addons/test_mail/tests/test_odoobot.py +++ b/addons/test_mail/tests/test_odoobot.py @@ -68,7 +68,7 @@ class TestOdoobot(BaseFunctionalTest, MockEmails, TestRecipients): answer="Wow you are a natural! Ping someone to grab its attention with @nameoftheuser. Try to ping me with @OdooBot." ) # we dont test the end of the flow since it will depends of the installed apps (livechat) - + self.user_employee.odoobot_state = "idle" kwargs['partner_ids'] = [] kwargs['body'] = "I love you" self.assertNextMessage( @@ -82,14 +82,6 @@ class TestOdoobot(BaseFunctionalTest, MockEmails, TestRecipients): sender=self.odoobot, answer="That's not a really nice thing to say, you know? I'm a bot but I have feelings, ok?! 💔" ) - # we should have a default answer - with patch('random.choice', lambda x: x[0]): - kwargs['body'] = "I'm batman" - self.assertNextMessage( - channel.message_post(**kwargs), - sender=self.odoobot, - answer="Mmmmh I'm not sure what you mean.. Can you try again?" - ) @mute_logger('odoo.addons.mail.models.mail_mail') def test_odoobot_no_default_answer(self):