├── Procfile ├── requirements.txt ├── config.py.example ├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md └── app.py /Procfile: -------------------------------------------------------------------------------- 1 | web: python app.py 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask==0.10.1 2 | beautifulsoup4==4.3.2 3 | bottlenose==0.6.2 4 | -------------------------------------------------------------------------------- /config.py.example: -------------------------------------------------------------------------------- 1 | aws = dict( 2 | AWS_ACCESS_KEY_ID = '', 3 | AWS_SECRET_ACCESS_KEY = '', 4 | AWS_ASSOCIATE_TAG = '', 5 | ) 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | 9 | # Matches multiple files with brace expansion notation 10 | # Set default charset 11 | [*.{js,py}] 12 | charset = utf-8 13 | 14 | # 4 space indentation 15 | [*.py] 16 | indent_style = space 17 | indent_size = 4 18 | 19 | # Tab indentation (no size specified) 20 | [Makefile] 21 | indent_style = tab 22 | 23 | # Indentation override for all JS under lib directory 24 | [lib/**.js] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | # Matches the exact files either package.json or .travis.yml 29 | [{package.json,.travis.yml}] 30 | indent_style = space 31 | indent_size = 2 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #### joe made this: https://goel.io/joe 2 | 3 | #####=== Python ===##### 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | 47 | # Translations 48 | *.mo 49 | *.pot 50 | 51 | # Django stuff: 52 | *.log 53 | 54 | # Sphinx documentation 55 | docs/_build/ 56 | 57 | # PyBuilder 58 | target/ 59 | 60 | config.py 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Karan Goel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # slackzon 2 | 3 | Simple Amazon Product Search in Slack. All searches are private and only visible to you. 4 | 5 | ![](http://i.imgur.com/XO71JrY.gif) 6 | 7 | ## Usage 8 | 9 | From any Slack channel, just type `/amazon [search terms]`. The products will be shown on the same channel visible just to you. 10 | 11 | ## Integrate with your team 12 | 13 | 1. Go to your channel 14 | 2. Click on **Configure Integrations**. 15 | 3. Scroll all the way down to **DIY Integrations & Customizations section**. 16 | 4. Click on **Add** next to **Slash Commands**. 17 | - Command: `/amazon` 18 | - URL: `http://amazon.goel.io/search` 19 | - Method: `POST` 20 | - For the **Autocomplete help text**, check to show the command in autocomplete list. 21 | - Description: `Simple Amazon Product Search in Slack.` 22 | - Usage hint: `[search terms]` 23 | - Descriptive Label: `Search Amazon` 24 | 25 | ## Developing 26 | 27 | Add a `config.py` file based on `config.py.example` file. Grab your AWS credentials. 28 | 29 | ```python 30 | # Install python dependencies 31 | $ pip install -r requirements.txt 32 | 33 | # Start the server 34 | $ python app.py 35 | ``` 36 | 37 | ## Contributing 38 | 39 | - Please use the [issue tracker](https://github.com/karan/slackzon/issues) to report any bugs or file feature requests. 40 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from bs4 import BeautifulSoup 6 | from flask import Flask, request, Response, redirect 7 | import bottlenose 8 | 9 | try: 10 | import config 11 | AWS_ACCESS_KEY_ID = config.aws['AWS_ACCESS_KEY_ID'] 12 | AWS_SECRET_ACCESS_KEY = config.aws['AWS_SECRET_ACCESS_KEY'] 13 | AWS_ASSOCIATE_TAG = config.aws['AWS_ASSOCIATE_TAG'] 14 | except: 15 | AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') 16 | AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') 17 | AWS_ASSOCIATE_TAG = os.environ.get('AWS_ASSOCIATE_TAG') 18 | 19 | 20 | if not all([AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_ASSOCIATE_TAG]): 21 | import sys 22 | print 'No config.py file found. Exiting...' 23 | sys.exit(0) 24 | 25 | 26 | MAX_PRODUCTS = 5 27 | 28 | 29 | app = Flask(__name__) 30 | amazon_client = bottlenose.Amazon(AWS_ACCESS_KEY_ID, 31 | AWS_SECRET_ACCESS_KEY, 32 | AWS_ASSOCIATE_TAG, 33 | Parser=BeautifulSoup) 34 | 35 | 36 | def get_response_string(item_xml): 37 | try: 38 | item = amazon_client.ItemLookup(ItemId=item_xml.asin.string, 39 | ResponseGroup='Offers', 40 | MerchantId='All') 41 | price = item.find('formattedprice').string 42 | url = item_xml.detailpageurl.string 43 | title = item_xml.itemattributes.title.string 44 | manufacturer = item_xml.itemattributes.manufacturer.string 45 | return "*%s* <%s|%s> (by %s)" % (price, url, title, manufacturer) 46 | except: 47 | return '' 48 | 49 | 50 | @app.route('/search', methods=['post']) 51 | def search(): 52 | ''' 53 | Example: 54 | /amazon kindle 3g 55 | ''' 56 | text = request.values.get('text') 57 | 58 | try: 59 | xml = amazon_client.ItemSearch(Keywords=text, SearchIndex='All') 60 | except UnicodeEncodeError: 61 | return Response(('Only English language is supported. ' 62 | '%s is not valid input.' % text), 63 | content_type='text/plain; charset=utf-8') 64 | 65 | 66 | resp = [('Amazon Top Products for ' 67 | '"<%s|%s>"\n') % (xml.items.moresearchresultsurl.string, text)] 68 | products = xml.find_all('item')[:MAX_PRODUCTS] 69 | resp.extend(map(get_response_string, products)) 70 | 71 | if len(resp) is 1: 72 | resp.append(('No products found. Please try a broader search or ' 73 | 'search directly on ' 74 | '.')) 75 | 76 | return Response('\n'.join(resp), content_type='text/plain; charset=utf-8') 77 | 78 | 79 | @app.route('/amazon') 80 | def amazon(): 81 | return redirect('http://amzn.to/1Gjm2pk', code=302) 82 | 83 | 84 | @app.route('/') 85 | def hello(): 86 | return redirect('https://github.com/karan/slackzon') 87 | 88 | 89 | if __name__ == '__main__': 90 | port = int(os.environ.get('PORT', 5000)) 91 | app.run(host='0.0.0.0', port=port) 92 | --------------------------------------------------------------------------------