summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordmonzonis <mod@odoo.com>2020-05-07 07:09:43 +0000
committerThibault DelavallΓ©e <tde@odoo.com>2020-05-27 12:47:16 +0000
commit936d206559035627b07a25e7a0f807ad43d39f54 (patch)
tree23b7d64a07f15a26b630383e70bc472fef1bbb70
parent5b20117495b908bde3877c02db3ada7249eb7993 (diff)
[IMP] mail_bot: improve OdooBot workflow
This commit attempts to improve the workflow of the OdooBot onboarding tutorial, as well as the answer given by the bot while idle. These changes come from watching how users interact with OdooBot. To improve the user's experience, the following changes have been made: 1. Move the attachment to the end of the tour, as it is where most people just close the bot. 2. Remove the quotation marks around commands that OdooBot tells the user to type, like "/" or ":)", as some users try to actually type the quotation marks too. Instead, use a light grey background around the command that the user should type. 3. Some users ask OdooBot questions, but the responses they get are useless. To solve this, use a message linking to the documentation or the videos when: - The user gives wrong answers twice for the same stage of the tour - The user talks again when the tour is completed - There's a question mark in the answer To achieve this a "failed" state field is added on user model, allowing to distinguish state in the bot workflow from state of answers (failed / not failed). Task ID: 2233014 PR #49661
-rw-r--r--addons/im_livechat_mail_bot/models/mail_bot.py13
-rw-r--r--addons/mail/models/mail_channel.py6
-rw-r--r--addons/mail_bot/__manifest__.py2
-rw-r--r--addons/mail_bot/models/mail_bot.py51
-rw-r--r--addons/mail_bot/models/mail_channel.py2
-rw-r--r--addons/mail_bot/models/res_users.py2
-rw-r--r--addons/mail_bot/static/src/scss/odoobot_style.scss8
-rw-r--r--addons/mail_bot/views/assets.xml1
-rw-r--r--addons/test_mail_full/tests/test_odoobot.py37
9 files changed, 82 insertions, 40 deletions
diff --git a/addons/im_livechat_mail_bot/models/mail_bot.py b/addons/im_livechat_mail_bot/models/mail_bot.py
index 593e3d69f63..a17e1cac292 100644
--- a/addons/im_livechat_mail_bot/models/mail_bot.py
+++ b/addons/im_livechat_mail_bot/models/mail_bot.py
@@ -10,13 +10,16 @@ class MailBot(models.AbstractModel):
def _get_answer(self, record, body, values, command):
odoobot_state = self.env.user.odoobot_state
if self._is_bot_in_private_channel(record):
- if odoobot_state == "onboarding_ping" and self._is_bot_pinged(values):
+ if odoobot_state == "onboarding_attachement" and values.get("attachment_ids"):
+ self.env.user.odoobot_failed = False
self.env.user.odoobot_state = "onboarding_canned"
- return _("That's me! πŸŽ‰<br/>Try to type \":\" to use canned responses.")
+ return _("That's me! πŸŽ‰<br/>Try typing <span class=\"o_odoobot_command\">:</span> to use canned responses.")
elif odoobot_state == "onboarding_canned" and values.get("canned_response_ids"):
+ self.env.user.odoobot_failed = False
self.env.user.odoobot_state = "idle"
return _("Good, you can customize canned responses in the live chat application.<br/><br/><b>It's the end of this overview</b>, enjoy discovering Odoo!")
- #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.")
+ # repeat question if needed
+ elif odoobot_state == 'onboarding_canned' and not self._is_help_requested(body):
+ self.env.user.odoobot_failed = True
+ return _("Not sure what you are doing. Please, type <span class=\"o_odoobot_command\">:</span> 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/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py
index b6e11e4fdc3..62489a88e5b 100644
--- a/addons/mail/models/mail_channel.py
+++ b/addons/mail/models/mail_channel.py
@@ -973,7 +973,7 @@ class Channel(models.Model):
})
def _define_command_help(self):
- return {'help': _("Show an helper message")}
+ return {'help': _("Show a helper message")}
def _execute_command_help(self, **kwargs):
partner = self.env.user.partner_id
@@ -987,9 +987,9 @@ class Channel(models.Model):
msg = _("You are in a private conversation with <b>@%s</b>.") % (channel_partners[0].partner_id.name if channel_partners else _('Anonymous'))
msg += _("""<br><br>
Type <b>@username</b> to mention someone, and grab his attention.<br>
- Type <b>#channel</b>.to mention a channel.<br>
+ Type <b>#channel</b> to mention a channel.<br>
Type <b>/command</b> to execute a command.<br>
- Type <b>:shortcut</b> to insert canned responses in your message.<br>""")
+ Type <b>:shortcut</b> to insert a canned response in your message.<br>""")
self._send_transient_message(partner, msg)
diff --git a/addons/mail_bot/__manifest__.py b/addons/mail_bot/__manifest__.py
index c1010924ea6..348754d3f62 100644
--- a/addons/mail_bot/__manifest__.py
+++ b/addons/mail_bot/__manifest__.py
@@ -3,7 +3,7 @@
{
'name': 'OdooBot',
- 'version': '1.0',
+ 'version': '1.1',
'category': 'Productivity/Discuss',
'summary': 'Add OdooBot in discussions',
'description': "",
diff --git a/addons/mail_bot/models/mail_bot.py b/addons/mail_bot/models/mail_bot.py
index 7a4aebee197..6c17d60bbd4 100644
--- a/addons/mail_bot/models/mail_bot.py
+++ b/addons/mail_bot/models/mail_bot.py
@@ -25,7 +25,7 @@ class MailBot(models.AbstractModel):
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(".?!")
+ 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')
@@ -38,42 +38,53 @@ class MailBot(models.AbstractModel):
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.")
+ self.env.user.odoobot_failed = False
+ return _("Great! πŸ‘<br/>To access special commands, <b>start your sentence with</b> <span class=\"o_odoobot_command\">/</span>. Try getting 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.")
+ self.env.user.odoobot_failed = False
+ return _("Wow you are a natural!<br/>Ping someone to grab its attention with @username. <b>Try to ping me using</b> <span class=\"o_odoobot_command\">@OdooBot</span> in a sentence.")
elif odoobot_state == 'onboarding_ping' and self._is_bot_pinged(values):
+ self.env.user.odoobot_state = "onboarding_attachement"
+ self.env.user.odoobot_failed = False
+ return _("Yep, I am here! πŸŽ‰ <br/>Now, try <b>sending 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 = "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_failed = False
+ return _("I am a simple bot, but if that's a dog, he is the cutest 😊 <br/>Congratulations, you finished this tour. You can now <b>close this chat window</b>. Enjoy discovering Odoo.")
+ elif odoobot_state in ("idle", "not_initialized") 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... πŸ’”")
+ # help message
+ elif self._is_help_requested(body) or odoobot_state == 'idle':
+ return _("Unfortunately, I'm just a bot 😞 I don't understand! If you need help discovering our product, please check "
+ "<a href=\"https://www.odoo.com/page/docs\" target=\"_blank\">our documentation</a> or "
+ "<a href=\"https://www.odoo.com/slides\" target=\"_blank\">our videos</a>.")
else:
- #repeat question
+ # repeat question
if odoobot_state == 'onboarding_emoji':
- return _("Not exactly. To continue the tour, send an emoji, <b>type \":)\"</b> and press enter.")
+ self.env.user.odoobot_failed = True
+ return _("Not exactly. To continue the tour, send an emoji: <b>type</b> <span class=\"o_odoobot_command\">:)</span> 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.")
+ self.env.user.odoobot_failed = True
+ return _("To <b>send an attachment</b>, click on the <i class=\"fa fa-paperclip\" aria-hidden=\"true\"></i> icon 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")
+ self.env.user.odoobot_failed = True
+ return _("Not sure what you are doing. Please, type <span class=\"o_odoobot_command\">/</span> and wait for the propositions. Select <span class=\"o_odoobot_command\">help</span> 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.")
+ self.env.user.odoobot_failed = True
+ return _("Sorry, I am not listening. To get someone's attention, <b>ping him</b>. Write <span class=\"o_odoobot_command\">@OdooBot</span> 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>",
+ _("I'm not smart enough to answer your question.<br/>To follow my guide, ask: <span class=\"o_odoobot_command\">start the tour</span>."),
_("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>'.",
+ _("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: <span class=\"o_odoobot_command\">start the tour</span>.")
])
return False
@@ -219,3 +230,9 @@ class MailBot(models.AbstractModel):
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
+
+ def _is_help_requested(self, body):
+ """Returns whether a message linking to the documentation and videos
+ should be sent back to the user.
+ """
+ return any(token in body for token in ['help', _('help'), '?']) or self.env.user.odoobot_failed
diff --git a/addons/mail_bot/models/mail_channel.py b/addons/mail_bot/models/mail_channel.py
index 8b792780971..909696e5618 100644
--- a/addons/mail_bot/models/mail_channel.py
+++ b/addons/mail_bot/models/mail_channel.py
@@ -23,7 +23,7 @@ class Channel(models.Model):
'email_send': False,
'name': 'OdooBot'
})
- message = _("Hello,<br/>Odoo's chat helps employees collaborate efficiently. I'm here to help you discover its features.<br/><b>Try to send me an emoji :)</b>")
+ message = _("Hello,<br/>Odoo's chat helps employees collaborate efficiently. I'm here to help you discover its features.<br/><b>Try to send me an emoji</b> <span class=\"o_odoobot_command\">:)</span>")
channel.sudo().message_post(body=message, author_id=odoobot_id, message_type="comment", subtype_xmlid="mail.mt_comment")
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 f1975df23e4..8468bc16d0f 100644
--- a/addons/mail_bot/models/res_users.py
+++ b/addons/mail_bot/models/res_users.py
@@ -5,6 +5,7 @@ from odoo import models, fields
class Users(models.Model):
_inherit = 'res.users'
+
odoobot_state = fields.Selection(
[
('not_initialized', 'Not initialized'),
@@ -15,6 +16,7 @@ class Users(models.Model):
('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
+ odoobot_failed = fields.Boolean(readonly=True)
def __init__(self, pool, cr):
""" Override of __init__ to add access rights.
diff --git a/addons/mail_bot/static/src/scss/odoobot_style.scss b/addons/mail_bot/static/src/scss/odoobot_style.scss
new file mode 100644
index 00000000000..aa5e187bb00
--- /dev/null
+++ b/addons/mail_bot/static/src/scss/odoobot_style.scss
@@ -0,0 +1,8 @@
+.o_odoobot_command {
+ background-color: lightgray;
+ border-radius: 4px;
+ padding: 2px;
+ padding-left: 10px;
+ padding-right: 10px;
+ font-weight: bold;
+}
diff --git a/addons/mail_bot/views/assets.xml b/addons/mail_bot/views/assets.xml
index 243fa5228a2..e3d5ee81feb 100644
--- a/addons/mail_bot/views/assets.xml
+++ b/addons/mail_bot/views/assets.xml
@@ -3,6 +3,7 @@
<data>
<template id="assets_backend" name="mailbot assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
+ <link rel="stylesheet" type="text/scss" href="/mail_bot/static/src/scss/odoobot_style.scss"/>
<script type="text/javascript" src="/mail_bot/static/src/js/mailbot_service.js"></script>
<script type="text/javascript" src="/mail_bot/static/src/js/notification_alert.js"></script>
<script type="text/javascript" src="/mail_bot/static/src/js/systray_messaging_menu.js"></script>
diff --git a/addons/test_mail_full/tests/test_odoobot.py b/addons/test_mail_full/tests/test_odoobot.py
index 2e4dc55490a..ddd6015b7e1 100644
--- a/addons/test_mail_full/tests/test_odoobot.py
+++ b/addons/test_mail_full/tests/test_odoobot.py
@@ -58,6 +58,19 @@ class TestOdoobot(TestMailCommon, TestRecipients):
channel = self.env['mail.channel'].with_user(self.user_employee).init_odoobot()
kwargs['body'] = 'tagada 😊'
+ last_message = self.assertNextMessage(
+ channel.message_post(**kwargs),
+ sender=self.odoobot,
+ answer=("help",)
+ )
+ channel.execute_command(command="help")
+ self.assertNextMessage(
+ last_message, # no message will be post with command help, use last odoobot message instead
+ sender=self.odoobot,
+ answer=("@OdooBot",)
+ )
+ kwargs['body'] = ''
+ kwargs['partner_ids'] = [self.env['ir.model.data'].xmlid_to_res_id("base.partner_root")]
self.assertNextMessage(
channel.message_post(**kwargs),
sender=self.odoobot,
@@ -70,20 +83,12 @@ class TestOdoobot(TestMailCommon, TestRecipients):
'res_model': 'mail.compose.message',
})
kwargs['attachment_ids'] = [attachment.id]
- last_message = self.assertNextMessage(
- channel.message_post(**kwargs),
- sender=self.odoobot,
- answer=("help",)
- )
- kwargs['attachment_ids'] = []
+ # For the end of the flow, we only test that the state changed, but not to which
+ # one since it depends on the intalled apps, which can add more steps (like livechat)
+ channel.message_post(**kwargs)
+ self.assertNotEqual(self.user_employee.odoobot_state, 'onboarding_attachement')
- channel.execute_command(command="help")
- self.assertNextMessage(
- last_message, # no message will be post with command help, use last odoobot message instead
- sender=self.odoobot,
- answer=("@OdooBot",)
- )
- # we dont test the end of the flow since it will depends of the installed apps (livechat)
+ # Test miscellaneous messages
self.user_employee.odoobot_state = "idle"
kwargs['partner_ids'] = []
kwargs['body'] = "I love you"
@@ -98,6 +103,12 @@ class TestOdoobot(TestMailCommon, TestRecipients):
sender=self.odoobot,
answer=("I have feelings",)
)
+ kwargs['body'] = "help me"
+ self.assertNextMessage(
+ channel.message_post(**kwargs),
+ sender=self.odoobot,
+ answer=("If you need help",)
+ )
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_odoobot_no_default_answer(self):