interactive bots: Add FourSquare bot and documentation.
ADD
contrib_bots/lib/FourSquareBot/doc.md
+# FourSquare Bot
+
+* This is a bot that returns a list of restaurants from a user input of location,
+proximity and restaurant type in that exact order. The number of returned
+restaurants are capped at 3 per request.
+
+* The list of restaurants are brought to Zulip using an API. The bot sends a GET
+request to https://api.foursquare.com/v2/. If the user does not correctly input
+a location, proximity and a restaurant type, the bot will return an error message.
+
+* For example, if the user says "@foursquare 'Chicago, IL' 80000 seafood", the bot
+will return:
+
+Food nearby 'Chicago, IL' coming right up:
+
+ Dee's Seafood Co.
+ 2723 S Poplar Ave, Chicago, IL 60608, United States
+ Fish Markets
+
+ Seafood Harbor
+ 2131 S Archer Ave (at Wentworth Ave), Chicago, IL 60616, United States
+ Seafood Restaurants
+
+ Joe's Seafood, Prime Steak & Stone Crab
+ 60 E Grand Ave (at N Rush St), Chicago, IL 60611, United States
+ Seafood Restaurants
+
+* If the user enters a wrong word, like "@foursquare 80000 donuts" or "@foursquare",
+then an error message saying invalid input will be displayed.
+
+* To get the required API Key, visit: https://developer.foursquare.com/overview/auth
+for more information.

ADD
contrib_bots/lib/FourSquareBot/settings.ini
+[Foursquare]
+api_key = abcdefghijksm

ADD
contrib_bots/lib/foursquare.py
+from __future__ import print_function
+from __future__ import absolute_import
+
+import datetime as dt
+import re
+import requests
+from os.path import expanduser
+from six.moves import configparser as cp
+from six.moves import range
+
+home = expanduser('~')
+CONFIG_PATH = home + '/zulip/contrib_bots/lib/FourSquareBot/settings.ini'
+
+def get_api_key():
+ # settings.ini must have been moved from
+ # ~/zulip/contrib_bots/lib/FourSquareBot/settings.ini into
+ # ~/settings.ini for program to work
+ # see doc.md for more information
+ with open(CONFIG_PATH) as settings:
+ config = cp.ConfigParser()
+ config.readfp(settings)
+ return config.get('Foursquare', 'api_key')
+
+class FoursquareHandler(object):
+ def __init__(self):
+ self.api_key = get_api_key()
+
+ def usage(self):
+ return '''
+ This plugin allows users to search for restaurants nearby an inputted
+ location to a limit of 3 venues for every location. The name, address
+ and description of the restaurant will be outputted.
+ It looks for messages starting with '@foursquare'.
+ If you need help, simply type:
+ @foursquare /help into the Compose Message box
+
+ Sample input:
+ @foursquare Chicago, IL
+ @foursquare help
+ '''
+
+ help_info = '''
+The Foursquare bot can receive keyword limiters that specify the location, distance (meters) and
+cusine of a restaurant in that exact order.
+Please note the required use of quotes in the search location.
+
+Example Inputs:
+@foursquare 'Millenium Park' 8000 donuts
+@foursquare 'Melbourne, Australia' 40000 seafood
+ '''
+
+ def triage_message(self, message, client):
+ callers = ['@FourSquare', '@Foursquare', '@foursquare', '@4square', '@4sq']
+ for call in callers:
+ if call in message['content']:
+ return True
+ break
+ return False
+
+ def format_json(self, venues):
+ def format_venue(venue):
+ name = venue['name']
+ address = ', '.join(venue['location']['formattedAddress'])
+ keyword = venue['categories'][0]['pluralName']
+ blurb = '\n'.join([name, address, keyword])
+ return blurb
+
+ return '\n'.join(format_venue(venue) for venue in venues)
+
+ def send_info(self, message, letter, client):
+ if message['type'] == 'private':
+ client.send_message(dict(
+ type='private',
+ to=message['sender_email'],
+ content=letter,
+ ))
+ else:
+ client.send_message(dict(
+ type='stream',
+ subject=message['subject'],
+ to=message['display_recipient'],
+ content=letter,
+ ))
+
+ def handle_message(self, message, client, state_handler):
+ words = message['content'].split()
+ if "/help" in words:
+ self.send_info(message, self.help_info, client)
+ return
+
+ # These are required inputs for the HTTP request.
+ try:
+ params = {'limit': '3'}
+ params['near'] = re.search('\'[A-Za-z]\w+[,]?[\s\w+]+?\'', message['content']).group(0)
+ params['v'] = 20170108
+ params['oauth_token'] = self.api_key
+ except AttributeError:
+ pass
+
+ # Optional params for HTTP request.
+ if len(words) >= 2:
+ try:
+ params['radius'] = re.search('([0-9]){3,}', message['content']).group(0)
+ except AttributeError:
+ pass
+ try:
+ params['query'] = re.search('\s([A-Za-z]+)$', message['content']).group(0)[1:]
+ except AttributeError:
+ params['query'] = 'food'
+
+ response = requests.get('https://api.foursquare.com/v2/venues/search?',
+ params=params)
+ print(response.url)
+ if response.status_code == 200:
+ received_json = response.json()
+ else:
+ self.send_info(message,
+ "Invalid Request\nIf stuck, try '@foursquare help'.",
+ client)
+ return
+
+ if received_json['meta']['code'] == 200:
+ response_msg = ('Food nearby ' + params['near']
+ + ' coming right up:\n'
+ + self.format_json(received_json['response']['venues']))
+ self.send_info(message, response_msg, client)
+ return
+
+ self.send_info(message,
+ "Invalid Request\nIf stuck, try '@foursquare help'.",
+ client)
+ return
+
+handler_class = FoursquareHandler
+
+def test_get_api_key():
+ # must change to your own api key for test to work
+ result = get_api_key()
+ assert result == 'abcdefghijksm'
+
+if __name__ == '__main__':
+ test_get_api_key()
+ print('Success')

interactive bots: Add FourSquare bot and documentation.