stack_overflow : Create StackOverflow bot.

(Use stackoverflow APIs to answer queries.)
This commit is contained in:
Dhruv Thakker 2018-03-10 18:24:11 +05:30 committed by showell
parent fb228f13ff
commit 9c5eaa2f1b
10 changed files with 285 additions and 0 deletions

View file

@ -0,0 +1,41 @@
# StackOverflow Bot
The StackOverflow bot is a Zulip bot that will search Stackoverflow
for a provided set of keywords or a question, and fetch a link to the associated
query. The link is returned to the same stream
it was @mentioned in
The Stackoverflow bot uses the
[StackExchange API](http://api.stackexchange.com/docs)
to obtain the search results it returns
Using the StackOverflow bot is as simple as mentioning @\<stackoverflow-bot-name\>,
followed by the query:
```
@<stackoverflow-bot-name> <query>
```
## Setup
Beyond the typical obtaining of the zuliprc file, no extra setup is required to use the StackOverflow Bot
## Usage
1. ```@<stackoverflow-bot-name> <query>``` -
fetches the link to the appropriate StackOverflow questions.
* For example, `@<stackoverflow-bot-name> rest api`
will return the links having questions related to rest api.
<br>
2. If there are no questions related to the query,
the bot will respond with an error message:
`I am sorry. The search query you provided is does not have any related results.`
<br>
3. If no query is provided, the bot will return the help text:
```Please enter your message after @mention-bot```

View file

@ -0,0 +1,17 @@
{
"request": {
"api_url":"http://api.stackexchange.com/2.2/search/advanced?order=desc&sort=relevance&site=stackoverflow&title=narendra"
},
"response": {
"data": {
"status_code":200
},
"items": [
]
},
"response-headers":{
"status":200,
"ok":true,
"content-type":"application/json; charset=utf-8"
}
}

View file

@ -0,0 +1,21 @@
{
"request": {
"api_url":"http://api.stackexchange.com/2.2/search/advanced?order=desc&sort=relevance&site=stackoverflow&title=what%20is%20flutter"
},
"response": {
"data": {
"status_code":200
},
"items": [
{
"title":"What is flutter/dart and what are its benefits over other tools?",
"link":"https://stackoverflow.com/questions/49023008/what-is-flutter-dart-and-what-are-its-benefits-over-other-tools"
}
]
},
"response-headers":{
"status":200,
"ok":true,
"content-type":"application/json; charset=utf-8"
}
}

View file

@ -0,0 +1,29 @@
{
"request": {
"api_url":"http://api.stackexchange.com/2.2/search/advanced?order=desc&sort=relevance&site=stackoverflow&title=113"
},
"response": {
"data": {
"status_code":200
},
"items": [
{
"title":"INSTALL_FAILED_NO_MATCHING_ABIS res-113",
"link":"https://stackoverflow.com/questions/47117788/install-failed-no-matching-abis-res-113"
},
{
"title":"com.sun.tools.xjc.reader.Ring.get(Ring.java:113)",
"link":"https://stackoverflow.com/questions/12848282/com-sun-tools-xjc-reader-ring-getring-java113"
},
{
"title":"no route to host error 113",
"link":"https://stackoverflow.com/questions/10516222/no-route-to-host-error-113"
}
]
},
"response-headers":{
"status":200,
"ok":true,
"content-type":"application/json; charset=utf-8"
}
}

View file

@ -0,0 +1,29 @@
{
"request": {
"api_url":"http://api.stackexchange.com/2.2/search/advanced?order=desc&sort=relevance&site=stackoverflow&title=restful"
},
"response": {
"data": {
"status_code":200
},
"items": [
{
"title":"What exactly is RESTful programming?",
"link":"https://stackoverflow.com/questions/671118/what-exactly-is-restful-programming"
},
{
"title":"RESTful Authentication",
"link":"https://stackoverflow.com/questions/319530/restful-authentication"
},
{
"title":"RESTful URL design for search",
"link":"https://stackoverflow.com/questions/319530/restful-authentication"
}
]
},
"response-headers":{
"status":200,
"ok":true,
"content-type":"application/json; charset=utf-8"
}
}

View file

@ -0,0 +1,16 @@
{
"request": {
"api_url":"http://api.stackexchange.com/2.2/search/advanced?order=desc&sort=relevance&site=stackoverflow&title=Zulip"
},
"response": {
"data": {
"status_code":404
},
"items": [
]
},
"response-headers":{
"status":404,
"content-type":"text/html"
}
}

View file

@ -0,0 +1 @@
requests

View file

@ -0,0 +1,77 @@
import requests
import logging
import re
import urllib
from zulip_bots.lib import Any
from typing import Optional, Any, Dict
# See readme.md for instructions on running this code.
class StackOverflowHandler(object):
'''
This plugin facilitates searching Stack Overflow for a
specific query and returns the top 3 questions from the
search. It looks for messages starting with '@mention-bot'
In this example, we write all Stack Overflow searches into
the same stream that it was called from.
'''
META = {
'name': 'StackOverflow',
'description': 'Searches Stack Overflow for a query and returns the top 3 articles.',
}
def usage(self) -> str:
return '''
This plugin will allow users to directly search
Stack Overflow for a specific query and get the top 3
articles that are returned from the search. Users
should preface query with "@mention-bot".
@mention-bot <search query>'''
def handle_message(self, message: Dict[str, str], bot_handler: Any) -> None:
bot_response = self.get_bot_stackoverflow_response(message, bot_handler)
bot_handler.send_reply(message, bot_response)
def get_bot_stackoverflow_response(self, message: Dict[str, str], bot_handler: Any) -> Optional[str]:
'''This function returns the URLs of the requested topic.'''
help_text = 'Please enter your query after @mention-bot to search StackOverflow'
# Checking if the link exists.
query = message['content']
if query == '' or query == 'help':
return help_text
query_stack_link = ('http://api.stackexchange.com/2.2/search/advanced?'
'order=desc&sort=relevance&site=stackoverflow&title=%s'
% (urllib.parse.quote(query),))
try:
data = requests.get(query_stack_link)
except requests.exceptions.RequestException:
logging.error('broken link')
return 'Uh-Oh ! Sorry ,couldn\'t process the request right now.:slightly_frowning_face:\n' \
'Please try again later.'
# Checking if the bot accessed the link.
if data.status_code != 200:
logging.error('Page not found.')
return 'Uh-Oh ! Sorry ,couldn\'t process the request right now.:slightly_frowning_face:\n' \
'Please try again later.'
new_content = 'For search term:' + query + '\n'
# Checking if there is content for the searched term
if len(data.json()['items']) == 0:
new_content = 'I am sorry. The search term you provided is not found :slightly_frowning_face:'
else:
for i in range(min(3, len(data.json()['items']))):
search_string = data.json()['items'][i]['title']
link = data.json()['items'][i]['link']
new_content += str(i+1) + ' : ' + '[' + search_string + ']' + '(' + link + ')\n'
return new_content
handler_class = StackOverflowHandler

View file

@ -0,0 +1,54 @@
from zulip_bots.test_lib import BotTestCase
from zulip_bots.request_test_lib import mock_request_exception
class TestStackoverflowBot(BotTestCase):
bot_name = "stack_overflow"
def test_bot(self) -> None:
# Single-word query
bot_request = 'restful'
bot_response = ('''For search term:restful
1 : [What exactly is RESTful programming?](https://stackoverflow.com/questions/671118/what-exactly-is-restful-programming)
2 : [RESTful Authentication](https://stackoverflow.com/questions/319530/restful-authentication)
3 : [RESTful URL design for search](https://stackoverflow.com/questions/319530/restful-authentication)
''')
with self.mock_http_conversation('test_single_word'):
self.verify_reply(bot_request, bot_response)
# Multi-word query
bot_request = 'what is flutter'
bot_response = ('''For search term:what is flutter
1 : [What is flutter/dart and what are its benefits over other tools?](https://stackoverflow.com/questions/49023008/what-is-flutter-dart-and-what-are-its-benefits-over-other-tools)
''')
with self.mock_http_conversation('test_multi_word'):
self.verify_reply(bot_request, bot_response)
# Number query
bot_request = '113'
bot_response = ('''For search term:113
1 : [INSTALL_FAILED_NO_MATCHING_ABIS res-113](https://stackoverflow.com/questions/47117788/install-failed-no-matching-abis-res-113)
2 : [com.sun.tools.xjc.reader.Ring.get(Ring.java:113)](https://stackoverflow.com/questions/12848282/com-sun-tools-xjc-reader-ring-getring-java113)
3 : [no route to host error 113](https://stackoverflow.com/questions/10516222/no-route-to-host-error-113)
''')
with self.mock_http_conversation('test_number_query'):
self.verify_reply(bot_request, bot_response)
# Incorrect word
bot_request = 'narendra'
bot_response = "I am sorry. The search term you provided is not found :slightly_frowning_face:"
with self.mock_http_conversation('test_incorrect_query'):
self.verify_reply(bot_request, bot_response)
# 404 status code
bot_request = 'Zulip'
bot_response = 'Uh-Oh ! Sorry ,couldn\'t process the request right now.:slightly_frowning_face:\n' \
'Please try again later.'
with self.mock_http_conversation('test_status_code'):
self.verify_reply(bot_request, bot_response)
# Request Exception
bot_request = 'Z'
with mock_request_exception():
self.verify_reply(bot_request, bot_response)