stack_overflow : Create StackOverflow bot.
(Use stackoverflow APIs to answer queries.)
This commit is contained in:
parent
fb228f13ff
commit
9c5eaa2f1b
41
zulip_bots/zulip_bots/bots/stack_overflow/doc.md
Normal file
41
zulip_bots/zulip_bots/bots/stack_overflow/doc.md
Normal 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```
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
requests
|
77
zulip_bots/zulip_bots/bots/stack_overflow/stack_overflow.py
Normal file
77
zulip_bots/zulip_bots/bots/stack_overflow/stack_overflow.py
Normal 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
|
54
zulip_bots/zulip_bots/bots/stack_overflow/test_stack_overflow.py
Executable file
54
zulip_bots/zulip_bots/bots/stack_overflow/test_stack_overflow.py
Executable 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)
|
Loading…
Reference in a new issue