scrapinghub / slackbot

A chat bot for Slack (https://slack.com).
MIT License
1.26k stars 394 forks source link

Bot conversation with user #158

Open forgettableEgg opened 7 years ago

forgettableEgg commented 7 years ago

I am looking to see if it is possible to have a bot have a conversation with the user. it would start with a respond_to and then the bot would ask a question the user would respond and so forth until all questions are answered. I don't want to have separate functions for responding to "yes" or "no" since they can be said normally and it would allow users to skip certain parts of the conversation. I would prefer it to be part of the initial "help me? " prompt. Is this possible? An example below...

user: "Help Me?" bot: "What do you need help with 1,2,3,4, or 5? user: "3" bot" "Are you sure you need help with 3?" user: yes .....

jtatum commented 7 years ago

I wrote a library to do this but it needs some polish and a little help from slackbot itself if I'm going to release it. Good idea though, it's a pretty useful use case.

lins05 commented 7 years ago

+1 for this type of "chat session" concept. A very basic implementation can be a list of questions the bot can ask the user and the user need to answer.

The hard part I see is the bot need to maintain a tuple of (user, channel/group/DM, current_step) in memory, and use that to decide what the bot should do next when receiving user input.

For more advanced session support, we can support conditional execution, e.g. one of question Q2/Q3 is asked/skipped based on the answer from question Q1.

upbeta01 commented 7 years ago

It would be ideal if it's implemented via plugin. And add ACL to it, so the ones that can add predefine question are only the bot admins.

Commands like:

Adding new question

User: !kb add-q admin-test-credentials
Bot: Question admin-test-credentials, added

User: !kb list-q
Bot: Here are the listed questions I know the answers.
+-----------------------------+
+ ID    |      Question   +
+-----------------------------+
+ 1     | admin-test...   +
+-----------------------------+

User: !kb add-a admin-test-credentials "admin-user:passwordhere"
Bot: Answer for admin-test-credntials, added

Unauth-User:!kb add-q sample-note-for-all
Bot: I only serve my master! Be a bot_admin so I can serve you...

Asking question to the bot.

User: Help me?
Bot: What can I help you with? 1, 2, 3, 4 or 5?
User: 1
Bot: admin-test-credential is admin-user:passwordhere

NOTE: It's ideal to add questions and answers via PM. And show what's been added either via PM or Channel. This might be possible via SQLite.

jtatum commented 7 years ago

Hi @upbeta01, what you're suggesting could be written in slackbot today without any kind of conversation module. Python has sqlite built in. Storing and retrieving data within a plugin should be pretty easy.

As for permissions, you can examine the sender in the message object passed in. I use a decorator that checks to see if the user is an authorized user by comparing the user ID to a list.

The idea behind this bug is to have a conversation where each successive message maintains some state of the conversation. In other words, if you do something like:

user: @bot serious command
bot: are you sure?
user: yes
bot: executing serious command

With a conversation module, the bot should maintain some state about the first thing the user said. The user's next reply would come into a function with context set by the previous message. The bot would need to understand that user saying yes is different from user2 saying yes since user was the one that initiated the conversation.

twcurrie commented 7 years ago

+1 on the direct message - what's the status?

jtatum commented 7 years ago

I started some code to support this. It's in a branch at the moment because I wanted to get some feedback on the design. First, you can see how a plugin author would implement this in a plugin at https://github.com/jtatum/slackbot/blob/eb9cd906d6016799cc6621186421dd7c74546c41/slackbot/plugins/state.py

Here's a conversation:

user: create widget
bot: How many widget(s) would you like to create?
user: 3
bot: Making 3 widget(s), are you sure? (yes/no)
user: yes
bot: Creating 3 widget(s)!

Each time new_state() is called or the message matches any state action, any previous state is discarded:

user: create widget
bot: How many widget(s) would you like to create?
user: 2
bot: Making 2 widget(s), are you sure? (yes/no)
user: create widget
bot: How many widget(s) would you like to create?
user: 3
bot: Making 3 widget(s), are you sure? (yes/no)
user: 4
bot: <displays unknown_response message>
user: yes
bot: Creating 3 widget(s)!
user: yes
bot: <displays unknown_response message>
user: no
bot: <displays unknown_response message>

State has a timeout, which defaults to 5 minutes. If the user tries to perform a state action after this amount of time, they'll get an error. (Should it have a timeout? Is that timeout too long/short? Should you be able to disable the timeout?)

State is tied to a specific user in a specific channel. You can't start a conversation in a channel/DM and continue it in a second channel/DM. Additionally, since each call to new_state() discards previous state, a user can only have one conversation in a channel. Invoking another command that calls new_state will abort the previous conversation. You can, however, start two distinct conversations in two distinct channels. (Are these limitations sound? Is any of this behavior undesirable?)

How does the API look? Any comments/suggestions welcome.

twcurrie commented 7 years ago

Timeout should be configurable. If the new conversation overrides the old, is the user informed of the override? Or is it just logged somewhere? For what you're doing, looks good.

jeffbildz commented 6 years ago

I've been working on this functionality as well. At the present time, I've added a helpfile.txt file that is read in and then presented to the channel to assist people with the commands it can run.

For example:

user: help bot: sends a snip-it with the help file for users to see examples and know how to interact with bot commands

I'm really interested in a means of being able to lock down who can speak with the bot. I'm using saltstack as an orchestration piece and have written plugins to interface with remediating events. I'm really looking at a means to parse additional slack-based alerts to have it fully remediate and validate things are working.

nafets33 commented 6 years ago

@jeffbildz or @jtatum

Has anyone got a working sample besides the widget code?

Also can someone explain the subtle difference between listen_to vs. repsond_to - I don't don't see how listen_to is even necessary when you can always use respond_to ? I'm trying to create conversations based on finding key words/phrases in the message.body["text"] but listen_to nor respond_to are able to catch key words within a multiline message, it always has to be the exact command.

Appreciate the clarification / update if any.

vigneshp826 commented 6 years ago

is it possible to use message threading concept to achieve ?

jeffbildz commented 6 years ago

@nafets33 in the testing I've done, I've only been able to get it to respond when speaking directly to the bot, or by using @botname to ask questions in a channel. Given slack's reliability, I've moved away from having the bot auto remediate, unless told to do so :) . Happy to answer any additional questions.