From e9ff73b87371c8dbfbfba457d8a84be366891e94 Mon Sep 17 00:00:00 2001 From: Harjyot Bagga Date: Thu, 8 Oct 2020 12:44:51 +0530 Subject: [PATCH 1/2] Implemented slash-search --- poetry.lock | 431 +++++++++++--------- pybot/endpoints/slack/commands.py | 27 ++ pybot/endpoints/slack/utils/slash_search.py | 23 ++ pyproject.toml | 1 + 4 files changed, 285 insertions(+), 197 deletions(-) create mode 100644 pybot/endpoints/slack/utils/slash_search.py diff --git a/poetry.lock b/poetry.lock index f706c1c..c457d4e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,26 +1,26 @@ [[package]] -category = "main" -description = "Asyncio support for PEP-567 contextvars backport." name = "aiocontextvars" +version = "0.2.2" +description = "Asyncio support for PEP-567 contextvars backport." +category = "main" optional = false python-versions = ">=3.5" -version = "0.2.2" [[package]] -category = "main" -description = "File support for asyncio." name = "aiofiles" +version = "0.4.0" +description = "File support for asyncio." +category = "main" optional = false python-versions = "*" -version = "0.4.0" [[package]] -category = "main" -description = "Async http client/server framework (asyncio)" name = "aiohttp" +version = "3.6.2" +description = "Async http client/server framework (asyncio)" +category = "main" optional = false python-versions = ">=3.5.3" -version = "3.6.2" [package.dependencies] async-timeout = ">=3.0,<4.0" @@ -33,24 +33,23 @@ yarl = ">=1.0,<2.0" speedups = ["aiodns", "brotlipy", "cchardet"] [[package]] -category = "dev" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" optional = false python-versions = "*" -version = "1.4.4" [[package]] -category = "main" -description = "In-process task scheduler with Cron-like capabilities" name = "apscheduler" +version = "3.6.3" +description = "In-process task scheduler with Cron-like capabilities" +category = "main" optional = false python-versions = "*" -version = "3.6.3" [package.dependencies] pytz = "*" -setuptools = ">=0.7" six = ">=1.4.0" tzlocal = ">=1.2" @@ -68,28 +67,28 @@ twisted = ["twisted"] zookeeper = ["kazoo"] [[package]] -category = "main" -description = "Timeout context manager for asyncio programs" name = "async-timeout" +version = "3.0.1" +description = "Timeout context manager for asyncio programs" +category = "main" optional = false python-versions = ">=3.5.3" -version = "3.0.1" [[package]] -category = "main" -description = "Decorator that turns async generator functions into async context managers." name = "asyncio-contextmanager" +version = "1.0.1" +description = "Decorator that turns async generator functions into async context managers." +category = "main" optional = false python-versions = "*" -version = "1.0.1" [[package]] -category = "main" -description = "An asyncio PosgtreSQL driver" name = "asyncpg" +version = "0.18.3" +description = "An asyncio PosgtreSQL driver" +category = "main" optional = false python-versions = ">=3.5.0" -version = "0.18.3" [package.extras] dev = ["Cython (0.29)", "pytest (>=3.6.0)", "Sphinx (>=1.7.3,<1.8.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)", "flake8 (>=3.5.0,<3.6.0)", "uvloop (>=0.8.0)"] @@ -97,43 +96,57 @@ docs = ["Sphinx (>=1.7.3,<1.8.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "sp test = ["flake8 (>=3.5.0,<3.6.0)", "uvloop (>=0.8.0)"] [[package]] -category = "dev" -description = "Enhance the standard unittest package with features for testing asyncio libraries" name = "asynctest" +version = "0.13.0" +description = "Enhance the standard unittest package with features for testing asyncio libraries" +category = "dev" optional = false python-versions = ">=3.5" -version = "0.13.0" [[package]] -category = "dev" -description = "Atomic file writes." -marker = "sys_platform == \"win32\"" name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.4.0" [[package]] -category = "main" -description = "Classes Without Boilerplate" name = "attrs" +version = "20.2.0" +description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.2.0" [package.extras] -dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] + +[[package]] +name = "beautifulsoup4" +version = "4.9.3" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +soupsieve = {version = ">1.2", markers = "python_version >= \"3.0\""} + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] [[package]] -category = "dev" -description = "The uncompromising code formatter." name = "black" +version = "20.8b1" +description = "The uncompromising code formatter." +category = "dev" optional = false python-versions = ">=3.6" -version = "20.8b1" [package.dependencies] appdirs = "*" @@ -150,78 +163,85 @@ colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] +name = "bs4" +version = "0.0.1" +description = "Dummy package for Beautiful Soup" category = "main" -description = "cChardet is high speed universal character encoding detector." -name = "cchardet" optional = false python-versions = "*" -version = "2.1.6" + +[package.dependencies] +beautifulsoup4 = "*" [[package]] +name = "cchardet" +version = "2.1.6" +description = "cChardet is high speed universal character encoding detector." category = "main" -description = "Python package for providing Mozilla's CA Bundle." -name = "certifi" optional = false python-versions = "*" -version = "2020.6.20" [[package]] +name = "certifi" +version = "2020.6.20" +description = "Python package for providing Mozilla's CA Bundle." category = "main" -description = "Universal encoding detector for Python 2 and 3" -name = "chardet" optional = false python-versions = "*" + +[[package]] +name = "chardet" version = "3.0.4" +description = "Universal encoding detector for Python 2 and 3" +category = "main" +optional = false +python-versions = "*" [[package]] -category = "dev" -description = "Composable command line interface toolkit" name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "7.1.2" [[package]] -category = "dev" -description = "Cross-platform colored terminal text." -marker = "sys_platform == \"win32\"" name = "colorama" +version = "0.4.3" +description = "Cross-platform colored terminal text." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.4.3" [[package]] -category = "main" -description = "The Cython compiler for writing C extensions for the Python language." name = "cython" +version = "0.29.21" +description = "The Cython compiler for writing C extensions for the Python language." +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "0.29.21" [[package]] -category = "dev" -description = "the modular source code checker: pep8 pyflakes and co" name = "flake8" +version = "3.8.3" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "3.8.3" [package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} mccabe = ">=0.6.0,<0.7.0" pycodestyle = ">=2.6.0a1,<2.7.0" pyflakes = ">=2.2.0,<2.3.0" -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" - [[package]] -category = "main" -description = "An async GitHub API library" name = "gidgethub" +version = "3.3.0" +description = "An async GitHub API library" +category = "main" optional = false python-versions = ">=3.6" -version = "3.3.0" [package.dependencies] uritemplate = ">=3.0.0" @@ -236,21 +256,20 @@ tornado = ["tornado"] treq = ["treq", "twisted"] [[package]] -category = "main" -description = "Internationalized Domain Names in Applications (IDNA)" name = "idna" +version = "2.10" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.10" [[package]] -category = "dev" -description = "Read metadata from Python packages" -marker = "python_version < \"3.8\"" name = "importlib-metadata" +version = "2.0.0" +description = "Read metadata from Python packages" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "2.0.0" [package.dependencies] zipp = ">=0.5" @@ -260,20 +279,20 @@ docs = ["sphinx", "rst.linker"] testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] [[package]] -category = "dev" -description = "iniconfig: brain-dead simple config-ini parsing" name = "iniconfig" +version = "1.0.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" optional = false python-versions = "*" -version = "1.0.1" [[package]] -category = "dev" -description = "A Python utility / library to sort Python imports." name = "isort" +version = "4.3.21" +description = "A Python utility / library to sort Python imports." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "4.3.21" [package.extras] pipfile = ["pipreqs", "requirementslib"] @@ -282,142 +301,137 @@ requirements = ["pipreqs", "pip-api"] xdg_home = ["appdirs (>=1.4.0)"] [[package]] -category = "dev" -description = "McCabe checker, plugin for flake8" name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" optional = false python-versions = "*" -version = "0.6.1" [[package]] -category = "main" -description = "multidict implementation" name = "multidict" +version = "4.7.6" +description = "multidict implementation" +category = "main" optional = false python-versions = ">=3.5" -version = "4.7.6" [[package]] -category = "dev" -description = "Experimental type system extensions for programs checked with the mypy typechecker." name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" optional = false python-versions = "*" -version = "0.4.3" [[package]] -category = "dev" -description = "Core utilities for Python packages" name = "packaging" +version = "20.4" +description = "Core utilities for Python packages" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.4" [package.dependencies] pyparsing = ">=2.0.2" six = "*" [[package]] -category = "dev" -description = "Utility library for gitignore style pattern matching of file paths." name = "pathspec" +version = "0.8.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.8.0" [[package]] -category = "dev" -description = "plugin and hook calling mechanisms for python" name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.13.1" [package.dependencies] -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] [[package]] -category = "dev" -description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" +version = "1.9.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.9.0" [[package]] -category = "dev" -description = "Python style guide checker" name = "pycodestyle" +version = "2.6.0" +description = "Python style guide checker" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.6.0" [[package]] -category = "dev" -description = "passive checker of Python programs" name = "pyflakes" +version = "2.2.0" +description = "passive checker of Python programs" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.2.0" [[package]] -category = "dev" -description = "Python parsing module" name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.4.7" [[package]] -category = "dev" -description = "pytest: simple powerful testing with Python" name = "pytest" +version = "6.1.0" +description = "pytest: simple powerful testing with Python" +category = "dev" optional = false python-versions = ">=3.5" -version = "6.1.0" [package.dependencies] -atomicwrites = ">=1.0" +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=17.4.0" -colorama = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<1.0" py = ">=1.8.2" toml = "*" -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" - [package.extras] checkqa_mypy = ["mypy (0.780)"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] -category = "dev" -description = "pytest plugin for aiohttp support" name = "pytest-aiohttp" +version = "0.3.0" +description = "pytest plugin for aiohttp support" +category = "dev" optional = false python-versions = "*" -version = "0.3.0" [package.dependencies] aiohttp = ">=2.3.5" pytest = "*" [[package]] -category = "dev" -description = "Pytest support for asyncio." name = "pytest-asyncio" +version = "0.14.0" +description = "Pytest support for asyncio." +category = "dev" optional = false python-versions = ">= 3.5" -version = "0.14.0" [package.dependencies] pytest = ">=5.4.0" @@ -426,12 +440,12 @@ pytest = ">=5.4.0" testing = ["async-generator (>=1.3)", "coverage", "hypothesis (>=5.7.1)"] [[package]] -category = "dev" -description = "Thin-wrapper around the mock package for easier use with pytest" name = "pytest-mock" +version = "3.3.1" +description = "Thin-wrapper around the mock package for easier use with pytest" +category = "dev" optional = false python-versions = ">=3.5" -version = "3.3.1" [package.dependencies] pytest = ">=5.0" @@ -440,47 +454,47 @@ pytest = ">=5.0" dev = ["pre-commit", "tox", "pytest-asyncio"] [[package]] -category = "main" -description = "Add .env support to your django/flask apps in development and deployments" name = "python-dotenv" +version = "0.14.0" +description = "Add .env support to your django/flask apps in development and deployments" +category = "main" optional = false python-versions = "*" -version = "0.14.0" [package.extras] cli = ["click (>=5.0)"] [[package]] -category = "main" -description = "World timezone definitions, modern and historical" name = "pytz" +version = "2020.1" +description = "World timezone definitions, modern and historical" +category = "main" optional = false python-versions = "*" -version = "2020.1" [[package]] -category = "main" -description = "YAML parser and emitter for Python" name = "pyyaml" +version = "5.3.1" +description = "YAML parser and emitter for Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.3.1" [[package]] -category = "dev" -description = "Alternative regular expression module, to replace re." name = "regex" +version = "2020.9.27" +description = "Alternative regular expression module, to replace re." +category = "dev" optional = false python-versions = "*" -version = "2020.9.27" [[package]] -category = "dev" -description = "Python HTTP for Humans." name = "requests" +version = "2.24.0" +description = "Python HTTP for Humans." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.24.0" [package.dependencies] certifi = ">=2017.4.17" @@ -493,12 +507,12 @@ security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] [[package]] -category = "main" -description = "Python client for Sentry (https://sentry.io)" name = "sentry-sdk" +version = "0.17.8" +description = "Python client for Sentry (https://sentry.io)" +category = "main" optional = false python-versions = "*" -version = "0.17.8" [package.dependencies] certifi = "*" @@ -521,12 +535,12 @@ sqlalchemy = ["sqlalchemy (>=1.2)"] tornado = ["tornado (>=5)"] [[package]] -category = "main" -description = "The good Sir Bot-a-lot. An asynchronous python bot framework." name = "sirbot" +version = "0.1.1" +description = "The good Sir Bot-a-lot. An asynchronous python bot framework." +category = "main" optional = false python-versions = ">=3.6,<4.0" -version = "0.1.1" [package.dependencies] aiofiles = ">=0.4.0,<0.5.0" @@ -539,85 +553,93 @@ slack-sansio = ">=1.0.0,<2.0.0" ujson = ">=1.35,<2.0" [[package]] -category = "main" -description = "Python 2 and 3 compatibility utilities" name = "six" +version = "1.15.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.15.0" [[package]] -category = "main" -description = "Python (a)sync Slack API library" name = "slack-sansio" +version = "1.1.0" +description = "Python (a)sync Slack API library" +category = "main" optional = false python-versions = ">=3.6,<4.0" -version = "1.1.0" [package.extras] +requests = ["requests (>=2.20,<3.0)", "websocket-client (>=0.54.0,<0.55.0)"] aiohttp = ["aiohttp (>=3.4,<4.0)"] curio = ["curio (>=0.9.0,<0.10.0)", "asks (>=2.2,<3.0)"] -requests = ["requests (>=2.20,<3.0)", "websocket-client (>=0.54.0,<0.55.0)"] trio = ["asks (>=2.2,<3.0)", "trio (>=0.11.0,<0.12.0)"] [[package]] -category = "dev" -description = "Python Library for Tom's Obvious, Minimal Language" +name = "soupsieve" +version = "2.0.1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] name = "toml" +version = "0.10.1" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" optional = false python-versions = "*" -version = "0.10.1" [[package]] -category = "dev" -description = "a fork of Python 2 and 3 ast modules with type comment support" name = "typed-ast" +version = "1.4.1" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" optional = false python-versions = "*" -version = "1.4.1" [[package]] -category = "main" -description = "Backported and Experimental Type Hints for Python 3.5+" name = "typing-extensions" +version = "3.7.4.3" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "main" optional = false python-versions = "*" -version = "3.7.4.3" [[package]] -category = "main" -description = "tzinfo object for the local timezone" name = "tzlocal" +version = "2.1" +description = "tzinfo object for the local timezone" +category = "main" optional = false python-versions = "*" -version = "2.1" [package.dependencies] pytz = "*" [[package]] -category = "main" -description = "Ultra fast JSON encoder and decoder for Python" name = "ujson" +version = "1.35" +description = "Ultra fast JSON encoder and decoder for Python" +category = "main" optional = false python-versions = "*" -version = "1.35" [[package]] -category = "main" -description = "URI templates" name = "uritemplate" +version = "3.0.1" +description = "URI templates" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.0.1" [[package]] -category = "main" -description = "HTTP library with thread-safe connection pooling, file post, and more." name = "urllib3" +version = "1.25.10" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "1.25.10" [package.extras] brotli = ["brotlipy (>=0.6.0)"] @@ -625,45 +647,42 @@ secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0 socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] [[package]] -category = "main" -description = "Yet another URL library" name = "yarl" +version = "1.6.0" +description = "Yet another URL library" +category = "main" optional = false python-versions = ">=3.5" -version = "1.6.0" [package.dependencies] idna = ">=2.0" multidict = ">=4.0" - -[package.dependencies.typing-extensions] -python = "<3.8" -version = ">=3.7.4" +typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [[package]] -category = "main" -description = "No-SQLite U.S. zipcode validation Python package, ready for use in AWS Lambda" name = "zipcodes" +version = "1.1.2" +description = "No-SQLite U.S. zipcode validation Python package, ready for use in AWS Lambda" +category = "main" optional = false python-versions = "*" -version = "1.1.2" [[package]] -category = "dev" -description = "Backport of pathlib-compatible object wrapper for zip files" -marker = "python_version < \"3.8\"" name = "zipp" +version = "3.2.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" optional = false python-versions = ">=3.6" -version = "3.2.0" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] -content-hash = "2d587eba0ef9a049615cc107242e107195490bfdc7f3182b64ac397dd6a110e4" +lock-version = "1.1" python-versions = "^3.7" +content-hash = "570abe735fecebce1cb68ec7795ab0b1f220f2b7b5418cf280c7143d8575518e" [metadata.files] aiocontextvars = [ @@ -733,9 +752,17 @@ attrs = [ {file = "attrs-20.2.0-py2.py3-none-any.whl", hash = "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"}, {file = "attrs-20.2.0.tar.gz", hash = "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594"}, ] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"}, + {file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"}, + {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"}, +] black = [ {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] +bs4 = [ + {file = "bs4-0.0.1.tar.gz", hash = "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"}, +] cchardet = [ {file = "cchardet-2.1.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:2aa1b008965c703ad6597361b0f6d427c8971fe94a2c99ec3724c228ae50d6a6"}, {file = "cchardet-2.1.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:fd16f57ce42a72397cd9fe38977fc809eb02172731cb354572f28a6d8e4cf322"}, @@ -955,6 +982,12 @@ regex = [ {file = "regex-2020.9.27-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:8d69cef61fa50c8133382e61fd97439de1ae623fe943578e477e76a9d9471637"}, {file = "regex-2020.9.27-cp38-cp38-win32.whl", hash = "sha256:f2388013e68e750eaa16ccbea62d4130180c26abb1d8e5d584b9baf69672b30f"}, {file = "regex-2020.9.27-cp38-cp38-win_amd64.whl", hash = "sha256:4318d56bccfe7d43e5addb272406ade7a2274da4b70eb15922a071c58ab0108c"}, + {file = "regex-2020.9.27-cp39-cp39-manylinux1_i686.whl", hash = "sha256:84cada8effefe9a9f53f9b0d2ba9b7b6f5edf8d2155f9fdbe34616e06ececf81"}, + {file = "regex-2020.9.27-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:816064fc915796ea1f26966163f6845de5af78923dfcecf6551e095f00983650"}, + {file = "regex-2020.9.27-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:5d892a4f1c999834eaa3c32bc9e8b976c5825116cde553928c4c8e7e48ebda67"}, + {file = "regex-2020.9.27-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c9443124c67b1515e4fe0bb0aa18df640965e1030f468a2a5dc2589b26d130ad"}, + {file = "regex-2020.9.27-cp39-cp39-win32.whl", hash = "sha256:49f23ebd5ac073765ecbcf046edc10d63dcab2f4ae2bce160982cb30df0c0302"}, + {file = "regex-2020.9.27-cp39-cp39-win_amd64.whl", hash = "sha256:3d20024a70b97b4f9546696cbf2fd30bae5f42229fbddf8661261b1eaff0deb7"}, {file = "regex-2020.9.27.tar.gz", hash = "sha256:a6f32aea4260dfe0e55dc9733ea162ea38f0ea86aa7d0f77b15beac5bf7b369d"}, ] requests = [ @@ -977,6 +1010,10 @@ slack-sansio = [ {file = "slack-sansio-1.1.0.tar.gz", hash = "sha256:4dec16e6f9ced6003de201c5e3bd1dbfc053ab8c8772ab29529772805b8a18a1"}, {file = "slack_sansio-1.1.0-py3-none-any.whl", hash = "sha256:0403c02ba6c3e57f6de7e9522aef7973bd71e33453f5b2ea760ec513b619750a"}, ] +soupsieve = [ + {file = "soupsieve-2.0.1-py3-none-any.whl", hash = "sha256:1634eea42ab371d3d346309b93df7870a88610f0725d47528be902a0d95ecc55"}, + {file = "soupsieve-2.0.1.tar.gz", hash = "sha256:a59dc181727e95d25f781f0eb4fd1825ff45590ec8ff49eadfd7f1a537cc0232"}, +] toml = [ {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, diff --git a/pybot/endpoints/slack/commands.py b/pybot/endpoints/slack/commands.py index af1e46a..a514446 100644 --- a/pybot/endpoints/slack/commands.py +++ b/pybot/endpoints/slack/commands.py @@ -16,6 +16,7 @@ from pybot.endpoints.slack.utils.command_utils import get_slash_repeat_messages from pybot.endpoints.slack.utils.general_utils import catch_command_slack_error from pybot.endpoints.slack.utils.slash_lunch import LunchCommand +from pybot.endpoints.slack.utils.slash_search import get_search_links logger = logging.getLogger(__name__) @@ -27,6 +28,7 @@ def create_endpoints(plugin: SlackPlugin): plugin.on_command("/roll", slash_roll, wait=False) plugin.on_command("/mentor", slash_mentor, wait=False) plugin.on_command("/mentor-volunteer", slash_mentor_volunteer, wait=False) + plugin.on_command("/search", slash_search, wait=False) @catch_command_slack_error @@ -154,3 +156,28 @@ async def slash_roll(command: Command, app: SirBot): message = f"<@{slack_id}> Rolled {numdice} D{typedice}: {dice}" response = dict(channel=channel_id, text=message) await slack.query(methods.CHAT_POST_MESSAGE, response) + + +@catch_command_slack_error +async def slash_search(command: Command, app: SirBot): + """ + Invoked by /search doubt_query. + + Used to find the top 5 search result links of your doubt from stack-overflow. + """ + slack = app["plugins"]["slack"].api + slack_id = command["user_id"] + channel_id = command["channel_id"] + text = command["text"] + + links = get_search_links(text) + if (len(links)==0): + # No results found + response_message = "Sorry, couldn't find relevant links.\nCan you reformat the question?" + else: + response_message = "Here are some StackOverflow links to help cater your doubts:\n\n" + for link in links: + response_message = response_message + link["text"] + "\n" + response_message = response_message + link["link"] + "\n\n" + response = dict(channel=channel_id, text=response_message) + await slack.query(methods.CHAT_POST_MESSAGE, response) \ No newline at end of file diff --git a/pybot/endpoints/slack/utils/slash_search.py b/pybot/endpoints/slack/utils/slash_search.py new file mode 100644 index 0000000..c1b2e14 --- /dev/null +++ b/pybot/endpoints/slack/utils/slash_search.py @@ -0,0 +1,23 @@ +import requests +from bs4 import BeautifulSoup +import urllib + +def get_search_links(query): + """ + Takes in the doubt query and returns a dictionary of at max 5 query items. + returning a list of dictionary of format {"text": , "link": } + """ + params = urllib.parse.urlencode({'q': query, 'sort': 'relevance'}) + url = "https://stackoverflow.com/search?" + params + r = requests.get(url) + soup = BeautifulSoup(r.content, 'lxml') + links = [] + result_count = 0 + for result in soup.findAll('div', attrs = {'class': 'result-link'}): + query_link = ("https://stackoverflow.com/" + result.h3.a['href']) + query_data = (result.h3.a.text) + links.append({"text": str.strip(query_data), "link": query_link}) + result_count+=1 + if (result_count>=5): + break + return links \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 0f3de3f..a0882fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ pyyaml = "^5.3.1" sentry-sdk = "^0.17.8" sirbot = "^0.1.1" zipcodes = "^1.1.2" +bs4 = "^0.0.1" [tool.poetry.dev-dependencies] asynctest = "^0.13.0" From 2a9cc5fcf44fa5e47ba2cd2ca5a2bc20a73e8363 Mon Sep 17 00:00:00 2001 From: Harjyot Bagga Date: Sat, 10 Oct 2020 03:12:53 +0530 Subject: [PATCH 2/2] changed search from slash-command to event --- pybot/endpoints/slack/commands.py | 27 --------- .../message_templates/search_stackoverflow.py | 59 +++++++++++++++++++ pybot/endpoints/slack/messages.py | 20 +++++++ pybot/endpoints/slack/utils/slash_search.py | 23 -------- 4 files changed, 79 insertions(+), 50 deletions(-) create mode 100644 pybot/endpoints/slack/message_templates/search_stackoverflow.py delete mode 100644 pybot/endpoints/slack/utils/slash_search.py diff --git a/pybot/endpoints/slack/commands.py b/pybot/endpoints/slack/commands.py index a514446..af1e46a 100644 --- a/pybot/endpoints/slack/commands.py +++ b/pybot/endpoints/slack/commands.py @@ -16,7 +16,6 @@ from pybot.endpoints.slack.utils.command_utils import get_slash_repeat_messages from pybot.endpoints.slack.utils.general_utils import catch_command_slack_error from pybot.endpoints.slack.utils.slash_lunch import LunchCommand -from pybot.endpoints.slack.utils.slash_search import get_search_links logger = logging.getLogger(__name__) @@ -28,7 +27,6 @@ def create_endpoints(plugin: SlackPlugin): plugin.on_command("/roll", slash_roll, wait=False) plugin.on_command("/mentor", slash_mentor, wait=False) plugin.on_command("/mentor-volunteer", slash_mentor_volunteer, wait=False) - plugin.on_command("/search", slash_search, wait=False) @catch_command_slack_error @@ -156,28 +154,3 @@ async def slash_roll(command: Command, app: SirBot): message = f"<@{slack_id}> Rolled {numdice} D{typedice}: {dice}" response = dict(channel=channel_id, text=message) await slack.query(methods.CHAT_POST_MESSAGE, response) - - -@catch_command_slack_error -async def slash_search(command: Command, app: SirBot): - """ - Invoked by /search doubt_query. - - Used to find the top 5 search result links of your doubt from stack-overflow. - """ - slack = app["plugins"]["slack"].api - slack_id = command["user_id"] - channel_id = command["channel_id"] - text = command["text"] - - links = get_search_links(text) - if (len(links)==0): - # No results found - response_message = "Sorry, couldn't find relevant links.\nCan you reformat the question?" - else: - response_message = "Here are some StackOverflow links to help cater your doubts:\n\n" - for link in links: - response_message = response_message + link["text"] + "\n" - response_message = response_message + link["link"] + "\n\n" - response = dict(channel=channel_id, text=response_message) - await slack.query(methods.CHAT_POST_MESSAGE, response) \ No newline at end of file diff --git a/pybot/endpoints/slack/message_templates/search_stackoverflow.py b/pybot/endpoints/slack/message_templates/search_stackoverflow.py new file mode 100644 index 0000000..e9f2538 --- /dev/null +++ b/pybot/endpoints/slack/message_templates/search_stackoverflow.py @@ -0,0 +1,59 @@ +import requests +from bs4 import BeautifulSoup +import urllib + +class SearchStockOverflow: + + def __init__(self, channel: str, user: str, input_text: str, app): + self.channel_id = channel + self.user_id = user + self.input_text = self.remove_search(input_text) + self.app = app + + def remove_search(self, initial_input): + return initial_input.split("!search", 1)[1] + + + async def return_links(self) -> dict: + if not self.input_text: + return {"message": {"text": self._default_error_message(), "channel": self.channel_id}} + else: + if self.input_text: + links = _get_search_links(self.input_text) + if (len(links)==0): + # No results found + response_message = "Sorry, couldn't find relevant links.\nCan you reformat the question?" + else: + response_message = "Here are some StackOverflow links to help cater your doubts:\n\n" + for link in links: + response_message = response_message + link["text"] + "\n" + response_message = response_message + link["link"] + "\n\n" + return {"message": response_message} + + return {"message": "Sorry, couldn't find relevant links.\nCan you reformat the question?"} + + def _default_error_message(self): + return ( + "Please enter an appropriate search query.\n"\ + + "For example: !search how to print a line in Java." + ) + + def _get_search_links(self, query): + """ + Takes in the doubt query and returns a dictionary of at max 5 query items. + returning a list of dictionary of format {"text": , "link": } + """ + params = urllib.parse.urlencode({'q': query, 'sort': 'relevance'}) + url = "https://stackoverflow.com/search?" + params + r = requests.get(url) + soup = BeautifulSoup(r.content, 'lxml') + links = [] + result_count = 0 + for result in soup.findAll('div', attrs = {'class': 'result-link'}): + query_link = ("https://stackoverflow.com/" + result.h3.a['href']) + query_data = (result.h3.a.text) + links.append({"text": str.strip(query_data), "link": query_link}) + result_count+=1 + if (result_count>=5): + break + return links \ No newline at end of file diff --git a/pybot/endpoints/slack/messages.py b/pybot/endpoints/slack/messages.py index 04a3a1e..66bd633 100644 --- a/pybot/endpoints/slack/messages.py +++ b/pybot/endpoints/slack/messages.py @@ -5,6 +5,7 @@ from slack.events import Message from .message_templates.tech import TechTerms +from .message_templates.search_stackoverflow import SearchStockOverflow from .utils import BOT_URL logger = logging.getLogger(__name__) @@ -14,6 +15,7 @@ def create_endpoints(plugin): plugin.on_message(r".*", message_changed, subtype="message_changed") plugin.on_message(r".*", message_deleted, subtype="message_deleted") plugin.on_message(r".*\!tech", tech_tips) + plugin.on_message(r".*\!search", search_stackoverflow) plugin.on_message(r".*\<\!here\>", here_bad) plugin.on_message(r".*\<\!channel\>", here_bad) plugin.on_message(r".*\!pybot", advertise_pybot) @@ -68,6 +70,24 @@ async def tech_tips(event: Message, app: SirBot): logger.debug(f"Exception thrown while logging message_changed {event}") +async def search_stackoverflow(event: Message, app: SirBot): + """ + Command to search help links from stackoverflow. + """ + if not_bot_message(event): + logger.info(f"tech tips logging: {event}") + try: + response = await SearchStockOverflow( + event["channel"], event["user"], event.get("text"), app + ) + await app.plugins["slack"].api.query( + methods.CHAT_POST_MESSAGE, response["message"] + ) + + except Exception: + logger.debug(f"Exception thrown while logging message_changed {event}") + + async def message_changed(event: Message, app: SirBot): """ Logs all message edits not made by a bot. diff --git a/pybot/endpoints/slack/utils/slash_search.py b/pybot/endpoints/slack/utils/slash_search.py deleted file mode 100644 index c1b2e14..0000000 --- a/pybot/endpoints/slack/utils/slash_search.py +++ /dev/null @@ -1,23 +0,0 @@ -import requests -from bs4 import BeautifulSoup -import urllib - -def get_search_links(query): - """ - Takes in the doubt query and returns a dictionary of at max 5 query items. - returning a list of dictionary of format {"text": , "link": } - """ - params = urllib.parse.urlencode({'q': query, 'sort': 'relevance'}) - url = "https://stackoverflow.com/search?" + params - r = requests.get(url) - soup = BeautifulSoup(r.content, 'lxml') - links = [] - result_count = 0 - for result in soup.findAll('div', attrs = {'class': 'result-link'}): - query_link = ("https://stackoverflow.com/" + result.h3.a['href']) - query_data = (result.h3.a.text) - links.append({"text": str.strip(query_data), "link": query_link}) - result_count+=1 - if (result_count>=5): - break - return links \ No newline at end of file