stack_overflow : Create StackOverflow bot.
(Use stackoverflow APIs to answer queries.)
This commit is contained in:
		
							parent
							
								
									fb228f13ff
								
							
						
					
					
						commit
						9c5eaa2f1b
					
				
					 10 changed files with 285 additions and 0 deletions
				
			
		
							
								
								
									
										0
									
								
								zulip_bots/zulip_bots/bots/stack_overflow/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								zulip_bots/zulip_bots/bots/stack_overflow/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										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…
	
	Add table
		Add a link
		
	
		Reference in a new issue