interactive bots: Create Howdoi bot.
This bot allows users to search technical questions from Stack Overflow.
This commit is contained in:
parent
a654ba51e1
commit
5ef742638e
BIN
contrib_bots/lib/HowdoiBot/answer_howdoi_all.png
Normal file
BIN
contrib_bots/lib/HowdoiBot/answer_howdoi_all.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
BIN
contrib_bots/lib/HowdoiBot/answer_howdowe.png
Normal file
BIN
contrib_bots/lib/HowdoiBot/answer_howdowe.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
56
contrib_bots/lib/HowdoiBot/docs.md
Normal file
56
contrib_bots/lib/HowdoiBot/docs.md
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# Howdoi bot
|
||||||
|
|
||||||
|
This bot will allow users to get technical answers from
|
||||||
|
[StackOverflow](https://stackoverflow.com). It is build on top of the
|
||||||
|
python command line tool [howdoi](https://github.com/gleitz/howdoi) by
|
||||||
|
Benjamin Gleitzman.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Simply prepend your questions with one of the following commands. The
|
||||||
|
answer will be formatted differently depending the chosen command.
|
||||||
|
|
||||||
|
| Command | Respond |
|
||||||
|
| ----------- | ------------------------------------------------------ |
|
||||||
|
| `@howdowe` | Concise answer to the same stream. |
|
||||||
|
| `@howdowe!` | Same as `@howdowe` but with full answer and URL of the solutions. |
|
||||||
|
| `@howdoi` | Concise answer replied to sender via private message. |
|
||||||
|
| `@howdoi!` | Same as `@howdoi` but with full answer and URL of the solutions. |
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
#### Example 1
|
||||||
|
|
||||||
|
Question -> `@howdowe use supervisor in elixir`
|
||||||
|
|
||||||
|
![howdowe question](question_howdowe.png)
|
||||||
|
|
||||||
|
Answer -> Howdoi would try to **only** respond with the coding section
|
||||||
|
of the answer.
|
||||||
|
|
||||||
|
![howdowe answer](answer_howdowe.png)
|
||||||
|
|
||||||
|
#### Example 2
|
||||||
|
|
||||||
|
Question -> `@howdoi! stack vs heap`
|
||||||
|
|
||||||
|
![howdoi! question](question_howdoi_all.png)
|
||||||
|
|
||||||
|
Answer -> Howdoi would return the **full** stackoverflow answer via
|
||||||
|
**private message** to the original sender. The URL of the answer can be
|
||||||
|
seen at the bottom of the message.
|
||||||
|
|
||||||
|
![howdoi! answer](answer_howdoi_all.png)
|
||||||
|
|
||||||
|
**Note:**
|
||||||
|
|
||||||
|
* Line wrapped is enabled with a maximum line length of 85 characters.
|
||||||
|
This could be adjusted in the source code (`HowdoiHandler.MAX_LINE_LENGTH`).
|
||||||
|
|
||||||
|
* *Howdoi* generally perform better if you ask a question using keywords
|
||||||
|
instead of a complete sentences (eg: "How do i make a decorator in Python"
|
||||||
|
-> "python decorator").
|
||||||
|
|
||||||
|
* __[*Limitation*]__ If a answer contains multiple code blocks, the `@howdoi`
|
||||||
|
and `@howdowe` commands would only return the first coding section, use
|
||||||
|
`@howdo[we|i]!` in that case.
|
BIN
contrib_bots/lib/HowdoiBot/question_howdoi_all.png
Normal file
BIN
contrib_bots/lib/HowdoiBot/question_howdoi_all.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9 KiB |
BIN
contrib_bots/lib/HowdoiBot/question_howdowe.png
Normal file
BIN
contrib_bots/lib/HowdoiBot/question_howdowe.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
130
contrib_bots/lib/howdoi_bot.py
Normal file
130
contrib_bots/lib/howdoi_bot.py
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
"""
|
||||||
|
This bot uses the python library `howdoi` which is not a
|
||||||
|
dependency of Zulip. To use this module, you will have to
|
||||||
|
install it in your local machine. In your terminal, enter
|
||||||
|
the following command:
|
||||||
|
|
||||||
|
$ sudo pip install howdoi --upgrade
|
||||||
|
|
||||||
|
Note:
|
||||||
|
* You might have to use `pip3` if you are using python 3.
|
||||||
|
* The install command would also download any dependency
|
||||||
|
required by `howdoi`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from textwrap import fill
|
||||||
|
try:
|
||||||
|
from howdoi.howdoi import howdoi
|
||||||
|
except ImportError:
|
||||||
|
logging.error("Dependency missing!!\n%s" % (__doc__))
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
class HowdoiHandler(object):
|
||||||
|
'''
|
||||||
|
This plugin facilitates searching Stack Overflow for
|
||||||
|
techanical answers based on the Python library `howdoi`.
|
||||||
|
To get the best possible answer, only include keywords
|
||||||
|
in your questions.
|
||||||
|
|
||||||
|
There are two possible commands:
|
||||||
|
* @howdowe > This would return the answer to the same
|
||||||
|
stream that it was called from.
|
||||||
|
|
||||||
|
* @howdoi > The bot would send a private message to the
|
||||||
|
user containing the answer.
|
||||||
|
|
||||||
|
By default, howdoi only returns the coding section of the
|
||||||
|
first search result if possible, to see the full answer
|
||||||
|
from Stack Overflow, append a '!' to the commands.
|
||||||
|
(ie '@howdoi!', '@howdowe!')
|
||||||
|
'''
|
||||||
|
|
||||||
|
MAX_LINE_LENGTH = 85
|
||||||
|
|
||||||
|
def usage(self):
|
||||||
|
return '''
|
||||||
|
This plugin will allow users to get techanical
|
||||||
|
answers from Stackoverflow. Users should preface
|
||||||
|
their questions with one of the following:
|
||||||
|
|
||||||
|
* @howdowe > Answer to the same stream
|
||||||
|
* @howdoi > Answer via private message
|
||||||
|
|
||||||
|
* @howdowe! OR @howdoi! > Full answer from SO
|
||||||
|
'''
|
||||||
|
|
||||||
|
def triage_message(self, message):
|
||||||
|
cmd_list = ['@howdowe', '@howdoi', '@howdowe!', '@howdoi!']
|
||||||
|
question = message['content']
|
||||||
|
|
||||||
|
# This next line of code is defensive, as we never want
|
||||||
|
# to get into an infinite loop of searching answers
|
||||||
|
# from Stackoverflow!
|
||||||
|
if message['sender_email'].startswith('howdoi'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
is_howdoi = any([question.startswith(cmd) for cmd in cmd_list])
|
||||||
|
|
||||||
|
return is_howdoi
|
||||||
|
|
||||||
|
def line_wrap(self, string, length):
|
||||||
|
lines = string.split("\n")
|
||||||
|
|
||||||
|
wrapped = [(fill(line) if len(line) > length else line)
|
||||||
|
for line in lines]
|
||||||
|
|
||||||
|
return "\n".join(wrapped).strip()
|
||||||
|
|
||||||
|
def get_answer(self, command, query):
|
||||||
|
question = query[len(command):].strip()
|
||||||
|
result = howdoi(dict(
|
||||||
|
query=question,
|
||||||
|
num_answers=1,
|
||||||
|
pos=1,
|
||||||
|
all=command[-1] == '!',
|
||||||
|
color=False
|
||||||
|
))
|
||||||
|
_answer = self.line_wrap(result, HowdoiHandler.MAX_LINE_LENGTH)
|
||||||
|
|
||||||
|
answer = "Answer to '%s':\n```\n%s\n```" % (question, _answer)
|
||||||
|
|
||||||
|
return answer
|
||||||
|
|
||||||
|
def handle_message(self, message, client, state_handler):
|
||||||
|
question = message['content']
|
||||||
|
|
||||||
|
if question.startswith('@howdowe!'):
|
||||||
|
client.send_message(dict(
|
||||||
|
type='stream',
|
||||||
|
to=message['display_recipient'],
|
||||||
|
subject=message['subject'],
|
||||||
|
content=self.get_answer('@howdowe!', question)
|
||||||
|
))
|
||||||
|
|
||||||
|
elif question.startswith('@howdoi!'):
|
||||||
|
client.send_message(dict(
|
||||||
|
type='private',
|
||||||
|
to=message['sender_email'],
|
||||||
|
content=self.get_answer('@howdoi!', question)
|
||||||
|
))
|
||||||
|
|
||||||
|
elif question.startswith('@howdowe'):
|
||||||
|
client.send_message(dict(
|
||||||
|
type='stream',
|
||||||
|
to=message['display_recipient'],
|
||||||
|
subject=message['subject'],
|
||||||
|
content=self.get_answer('@howdowe', question)
|
||||||
|
))
|
||||||
|
|
||||||
|
elif question.startswith('@howdoi'):
|
||||||
|
client.send_message(dict(
|
||||||
|
type='private',
|
||||||
|
to=message['sender_email'],
|
||||||
|
content=self.get_answer('@howdoi', question)
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
handler_class = HowdoiHandler
|
Loading…
Reference in a new issue