├── .editorconfig ├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── app.json ├── app.py ├── config.py.example └── requirements.txt /.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 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | config.py 59 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: python app.py 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # slack-overflow 2 | 3 | A programmer's best friend, now in Slack. Search StackOverflow right from Slack without coming off as dumb. 4 | 5 | ![](http://i.imgur.com/c9HuKw8.gif) 6 | 7 | ## Usage 8 | 9 | From any Slack channel, just type `/overflow [search terms]`. The questions 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: `/overflow` 18 | - URL: `http://so.goel.io/overflow` 19 | - Method: `POST` 20 | - For the **Autocomplete help text**, check to show the command in autocomplete list. 21 | - Description: `A programmer's best friend, now in Slack.` 22 | - Usage hint: `[search terms]` 23 | - Descriptive Label: `Search StackOverflow` 24 | 25 | ## Developing 26 | 27 | Add a `config.py` file based on `config.py.example` file. Grab your StackExchange key from http://stackapps.com/ 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 | ## Deploy to Heroku 38 | 39 | [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) 40 | 41 | You will need to set the `SE_KEY` environment variable in your heroku app in order for this to work. You can read more about it [clicking here](https://devcenter.heroku.com/articles/config-vars#setting-up-config-vars-for-a-deployed-application) 42 | 43 | 44 | ## Contributing 45 | 46 | - Please use the [issue tracker](https://github.com/karan/slack-overflow/issues) to report any bugs or file feature requests. 47 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Slack Overflow", 3 | "description": "A programmer's best friend, now in Slack.", 4 | "keywords": [ 5 | "slack", 6 | "flask", 7 | "python", 8 | "web", 9 | "stack overflow" 10 | ], 11 | "repository": "https://github.com/karan/slack-overflow" 12 | } 13 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from flask import Flask, request, Response, redirect 6 | from stackexchange import Site, StackOverflow, Sort, DESC 7 | 8 | try: 9 | import config 10 | se_key = config.stackexchange['api_key'] 11 | except: 12 | se_key = os.environ.get('SE_KEY') 13 | 14 | 15 | if not se_key: 16 | import sys 17 | print 'No config.py file found. Exiting...' 18 | sys.exit(0) 19 | 20 | 21 | MAX_QUESTIONS = 5 22 | 23 | 24 | app = Flask(__name__) 25 | so = Site(StackOverflow, se_key) 26 | 27 | 28 | def get_response_string(q): 29 | q_data = q.json 30 | check = ' :white_check_mark:' if q.json['is_answered'] else '' 31 | return "|%d|%s <%s|%s> (%d answers)" % (q_data['score'], check, q.url, 32 | q.title, q_data['answer_count']) 33 | 34 | 35 | @app.route('/overflow', methods=['post']) 36 | def overflow(): 37 | ''' 38 | Example: 39 | /overflow python list comprehension 40 | ''' 41 | text = request.values.get('text') 42 | 43 | try: 44 | qs = so.search(intitle=text, sort=Sort.Votes, order=DESC) 45 | except UnicodeEncodeError: 46 | return Response(('Only English language is supported. ' 47 | '%s is not valid input.' % text), 48 | content_type='text/plain; charset=utf-8') 49 | 50 | 51 | resp_qs = ['Stack Overflow Top Questions for "%s"\n' % text] 52 | resp_qs.extend(map(get_response_string, qs[:MAX_QUESTIONS])) 53 | 54 | if len(resp_qs) is 1: 55 | resp_qs.append(('No questions found. Please try a broader search or ' 56 | 'search directly on ' 57 | '.')) 58 | 59 | return Response('\n'.join(resp_qs), 60 | content_type='text/plain; charset=utf-8') 61 | 62 | 63 | @app.route('/') 64 | def hello(): 65 | return redirect('https://github.com/karan/slack-overflow') 66 | 67 | 68 | if __name__ == '__main__': 69 | port = int(os.environ.get('PORT', 5000)) 70 | app.run(host='0.0.0.0', port=port) 71 | -------------------------------------------------------------------------------- /config.py.example: -------------------------------------------------------------------------------- 1 | stackexchange = dict( 2 | api_key = '' 3 | ) 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask==0.10.1 2 | py-stackexchange==2.2.004 3 | --------------------------------------------------------------------------------