From 545e256b09dacd067a47b346136a47aa3ba8ffb5 Mon Sep 17 00:00:00 2001 From: jose-donato <43375532+jose-donato@users.noreply.github.com> Date: Thu, 10 Mar 2022 14:31:05 +0000 Subject: [PATCH] Slack Bot (#1465) * initial slack files * improving messages * updated slack and telegram dependencies * added website content * added images in docs * fixed functions docs Co-authored-by: didierlopes.eth --- bots/helpers.py | 30 ++- bots/requirements.txt | 69 ++++--- bots/slack/run_slack.py | 267 +++++++++++++++++++++++++ bots/telegram/run_telegram.py | 20 +- poetry.lock | 60 +++++- pyproject.toml | 4 +- website/content/bot/_index.md | 9 + website/content/bot/groupme/_index.md | 20 ++ website/content/bot/slack/_index.md | 20 ++ website/content/bot/telegram/_index.md | 20 ++ website/data/menu/main.yml | 6 + 11 files changed, 484 insertions(+), 41 deletions(-) create mode 100644 bots/slack/run_slack.py create mode 100644 website/content/bot/groupme/_index.md create mode 100644 website/content/bot/slack/_index.md create mode 100644 website/content/bot/telegram/_index.md diff --git a/bots/helpers.py b/bots/helpers.py index b76827304de..d47c655007e 100644 --- a/bots/helpers.py +++ b/bots/helpers.py @@ -292,6 +292,35 @@ class ShowView: message = f"{title}\n{clean_desc}" send_message(message, group_id) + def slack(self, func, channel_id, user_id, client, *args, **kwargs): + data = func(*args, **kwargs) + if "imagefile" in data: + client.files_upload( + file=data["imagefile"], + initial_comment=data.get("title", ""), + channels=channel_id, + user_id=user_id, + ) + os.remove(data["imagefile"]) + elif "embeds_img" in data: + client.files_upload( + file=data["embeds_img"][0], + initial_comment=data.get("title", ""), + channels=channel_id, + user_id=user_id, + ) + os.remove(data["embeds_img"][0]) + elif "description" in data: + title = data.get("title", "") + description = data.get("description") + if isinstance(description, List): + clean_desc = description[0].replace("Page ", "") + else: + clean_desc = description.replace("Page ", "") + message = f"{title}\n{clean_desc}" + payload = {"channel": channel_id, "username": user_id, "text": message} + client.chat_postMessage(**payload) + def telegram(self, func, message, bot, cmd, *args, **kwargs): data = func(*args, **kwargs) if "imagefile" in data: @@ -306,7 +335,6 @@ class ShowView: os.remove(data["embeds_img"][0]) elif "description" in data: title = data.get("title", "") - # TODO: Allow navigation through pages description = data.get("description") if isinstance(description, List): clean_desc = description[0].replace("Page ", "") diff --git a/bots/requirements.txt b/bots/requirements.txt index 9d27830ae5a..6ceed8d8311 100644 --- a/bots/requirements.txt +++ b/bots/requirements.txt @@ -1,37 +1,38 @@ aiohttp==3.8.1; python_version >= "3.6" and python_full_version >= "3.8.0" -aiosignal==1.2.0; python_version >= "3.6" +aiosignal==1.2.0; python_version >= "3.6" and python_full_version >= "3.8.0" alabaster==0.7.12; python_version >= "3.7" alpha-vantage==2.3.1 ansiwrap==0.8.4; python_version >= "3.6" anyio==3.5.0; python_full_version >= "3.6.2" and python_version >= "3.7" appdirs==1.4.4; python_full_version >= "3.6.2" and python_version >= "3.8" appnope==0.1.2; platform_system == "Darwin" and python_version >= "3.8" and sys_platform == "darwin" -argon2-cffi-bindings==21.2.0; python_version >= "3.6" +argon2-cffi-bindings==21.2.0; python_version >= "3.7" argon2-cffi==21.3.0; python_version >= "3.7" ascii-magic==1.6; python_version >= "3.5" asgiref==3.5.0; python_version >= "3.7" +astor==0.8.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" astroid==2.9.3; python_full_version >= "3.6.2" asttokens==2.0.5; python_version >= "3.8" -async-timeout==4.0.2; python_version >= "3.6" +async-timeout==4.0.2; python_version >= "3.6" and python_full_version >= "3.8.0" atomicwrites==1.4.0; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.4.0" -attrs==21.4.0; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_full_version >= "3.5.0" and python_version >= "3.7" and python_version < "4.0" +attrs==21.4.0; python_full_version >= "3.8.0" and python_version >= "3.7" and (python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7") babel==2.9.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" backcall==0.2.0; python_version >= "3.8" backports.zoneinfo==0.2.1; python_version < "3.9" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") bandit==1.7.2; python_version >= "3.7" beartype==0.7.1; python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0" -beautifulsoup4==4.10.0; python_full_version > "3.0.0" and python_version >= "3.5" +beautifulsoup4==4.10.0; python_full_version > "3.0.0" and python_version >= "3.7" black==21.7b0; python_full_version >= "3.6.2" bleach==4.1.0; python_version >= "3.7" bs4==0.0.1 bt==0.2.9; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0") -certifi==2021.10.8; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" -cffi==1.15.0; implementation_name == "pypy" and python_version >= "3.6" +certifi==2021.10.8; python_full_version >= "3.7.1" and python_version >= "3.8" and python_version < "4.0" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7") +cffi==1.15.0; implementation_name == "pypy" and python_version >= "3.7" and python_full_version >= "3.7.0" cfgv==3.3.1; python_full_version >= "3.6.1" -charset-normalizer==2.0.12; python_full_version >= "3.6.0" and python_version >= "3.6" +charset-normalizer==2.0.12; python_full_version >= "3.8.0" and python_version >= "3.8" and python_version < "4.0" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7") click==8.0.4; python_version >= "3.8" and python_full_version >= "3.6.2" codespell==2.1.0; python_version >= "3.5" -colorama==0.4.4; python_full_version >= "3.6.2" and python_full_version < "4.0.0" and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5") and (python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.5.0") and sys_platform == "win32" and (python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.5.0") and (python_version >= "3.7" and python_full_version < "3.0.0" and platform_system == "Windows" or platform_system == "Windows" and python_version >= "3.7" and python_full_version >= "3.5.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and platform_system == "Windows" or python_full_version >= "3.5.0" and platform_system == "Windows") and (python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or platform_system == "Windows" and python_version >= "3.6" and python_full_version >= "3.5.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.8" and python_full_version >= "3.5.0") +colorama==0.4.4; python_full_version >= "3.6.2" and python_full_version < "4.0.0" and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5") and (python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.5.0") and sys_platform == "win32" and (python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.5.0") and (python_version >= "3.7" and python_full_version < "3.0.0" and platform_system == "Windows" or platform_system == "Windows" and python_version >= "3.7" and python_full_version >= "3.5.0") and (python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or python_full_version >= "3.5.0" and platform_system == "Windows" and python_version >= "3.6") and platform_system == "Windows" and python_version >= "3.7" and (python_version >= "3.8" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.8" and python_full_version >= "3.5.0") commonmark==0.9.1; python_full_version >= "3.6.2" and python_full_version < "4.0.0" convertdate==2.4.0; python_version >= "3.7" and python_version < "4" coverage==6.3.2; python_version >= "3.7" @@ -54,7 +55,7 @@ distlib==0.3.4; python_full_version >= "3.6.1" dnspython==2.2.0; python_version >= "3.6" and python_version < "4.0" docutils==0.16; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" ecos==2.0.10; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" -entrypoints==0.4; python_full_version >= "3.6.1" and python_version >= "3.7" +entrypoints==0.4; python_full_version >= "3.7.0" and python_version >= "3.7" et-xmlfile==1.1.0; python_version >= "3.6" exchange-calendars==3.6; python_version >= "3.7" and python_full_version >= "3.7.0" executing==0.8.2; python_version >= "3.8" @@ -68,10 +69,11 @@ finviz==1.4.3 finvizfinance==0.9.10; python_version >= "3.5" flake8==3.9.2; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") fonttools==4.29.1; python_version >= "3.7" +formulaic==0.2.4; python_version >= "3.7" fred==3.1 fredapi==0.4.3 frozendict==2.3.0; python_version >= "3.6" -frozenlist==1.3.0; python_version >= "3.7" +frozenlist==1.3.0; python_version >= "3.7" and python_full_version >= "3.8.0" fundamentalanalysis==0.2.12 future==0.18.2; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" gitdb==4.0.9; python_version >= "3.7" @@ -81,26 +83,27 @@ h11==0.13.0; python_version >= "3.7" hijri-converter==2.2.3; python_version >= "3.6" holidays==0.11.3.1; python_version >= "3.6" identify==2.4.11; python_version >= "3.7" and python_full_version >= "3.6.1" -idna==3.3; python_full_version >= "3.6.2" and python_version >= "3.7" +idna==3.3; python_full_version >= "3.8.0" and python_version >= "3.8" and python_version < "4.0" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7") imagesize==1.3.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" inflection==0.5.1; python_version >= "3.6" iniconfig==1.1.1; python_version >= "3.7" +interface-meta==1.2.4; python_version >= "3.7" investpy==1.0.8; python_version >= "3.7" ipykernel==6.9.1; python_version >= "3.7" ipympl==0.8.8 -ipython-genutils==0.2.0; python_version >= "3.7" +ipython-genutils==0.2.0; python_full_version >= "3.7.0" and python_version >= "3.7" ipython==8.0.1; python_version >= "3.8" ipywidgets==7.6.5 iso8601==0.1.16 isort==5.10.1; python_full_version >= "3.6.2" and python_version < "4.0" jedi==0.18.1; python_version >= "3.8" jinja2==3.0.3; python_version >= "3.7" -joblib==1.1.0; python_version >= "3.7" -json5==0.9.6; python_version >= "3.6" +joblib==1.1.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" +json5==0.9.6; python_version >= "3.7" jsonschema==3.2.0 jupyter-client==7.1.2; python_full_version >= "3.7.0" and python_version >= "3.7" jupyter-console==6.4.0; python_version >= "3.6" -jupyter-core==4.9.2; python_full_version >= "3.6.1" and python_version >= "3.7" +jupyter-core==4.9.2; python_full_version >= "3.7.0" and python_version >= "3.7" jupyter-server==1.13.5; python_version >= "3.7" jupyter==1.0.0 jupyterlab-pygments==0.1.2; python_version >= "3.7" @@ -111,6 +114,7 @@ kaleido==0.2.1; python_version >= "3.8" and python_version < "4.0" kiwisolver==1.3.2; python_version >= "3.7" korean-lunar-calendar==0.2.1; python_version >= "3.7" and python_full_version >= "3.7.0" lazy-object-proxy==1.7.1; python_version >= "3.6" and python_full_version >= "3.6.2" +linearmodels==4.25; python_version >= "3.7" lxml==4.8.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" m2r2==0.2.8; python_version >= "3.7" markdown-it-py==1.1.0; python_version >= "3.6" and python_version < "4.0" @@ -123,7 +127,7 @@ mistune==0.8.4; python_version >= "3.7" mock==4.0.3; python_version >= "3.6" more-itertools==8.12.0; python_version >= "3.6" mplfinance==0.12.8b9 -multidict==6.0.2; python_version >= "3.7" +multidict==6.0.2; python_version >= "3.7" and python_full_version >= "3.8.0" multitasking==0.0.10 mypy-extensions==0.4.3; python_version >= "3.8" and python_full_version >= "3.6.2" mypy==0.930; python_version >= "3.6" @@ -164,11 +168,12 @@ prawcore==2.3.0; python_version >= "3.6" and python_version < "4.0" pre-commit==2.17.0; python_full_version >= "3.6.1" prometheus-client==0.13.1; python_version >= "3.7" prompt-toolkit==3.0.28; python_full_version >= "3.6.2" +property-cached==1.6.4; python_version >= "3.7" protobuf==3.19.4; python_full_version >= "3.7.1" and python_full_version < "4.0.0" and python_version >= "3.5" psaw==0.0.12; python_version >= "3" ptyprocess==0.7.0; os_name != "nt" and python_version >= "3.8" and sys_platform != "win32" pure-eval==0.2.2; python_version >= "3.8" -py==1.11.0; python_version >= "3.7" and python_full_version < "3.0.0" and implementation_name == "pypy" or python_full_version >= "3.5.0" and python_version >= "3.7" and implementation_name == "pypy" +py==1.11.0; python_full_version >= "3.7.0" and python_version >= "3.7" and implementation_name == "pypy" pyally==1.1.2 pycodestyle==2.7.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pycoingecko==2.2.0 @@ -177,6 +182,7 @@ pydantic==1.9.0; python_full_version >= "3.6.1" pyex==0.5.0 pyflakes==2.3.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pygments==2.11.2; python_version >= "3.5" +pyhdfe==0.1.0; python_version >= "3.7" pyimgur==0.6.0 pylint==2.12.2; python_full_version >= "3.6.2" pyluach==1.4.0; python_version >= "3.7" and python_full_version >= "3.7.0" @@ -190,22 +196,23 @@ pyparsing==3.0.7; python_version >= "3.7" pyportfolioopt==1.5.1; python_full_version >= "3.6.1" and python_full_version < "4.0.0" pyprind==2.11.3; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" pyrsistent==0.18.1; python_version >= "3.7" +pytelegrambotapi==4.4.0 pytest-cov==3.0.0; python_version >= "3.6" pytest-mock==3.7.0; python_version >= "3.7" pytest-recording==0.12.0; python_version >= "3.5" pytest==6.2.5; python_version >= "3.6" python-binance==1.0.15 python-coinmarketcap==0.2 -python-dateutil==2.8.2; python_full_version >= "3.7.1" and python_version >= "3.7" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5") +python-dateutil==2.8.2; python_full_version >= "3.7.1" and python_version >= "3.8" and python_full_version < "4.0.0" and python_version < "4.0" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5") python-dotenv==0.19.2; python_version >= "3.5" pytrends==4.8.0 pytz-deprecation-shim==0.1.0.post0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" -pytz==2021.3; python_full_version >= "3.7.1" and python_version >= "3.7" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7") +pytz==2021.3; python_full_version >= "3.7.1" and python_version >= "3.8" and python_full_version < "4.0.0" and python_version < "4.0" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7") pyupgrade==2.31.0; python_full_version >= "3.6.1" -pywin32==303; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_version >= "3.6" +pywin32==303; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_version >= "3.7" and python_full_version >= "3.7.0" pywinpty==1.1.6; os_name == "nt" and python_version >= "3.7" pyyaml==6.0; python_version >= "3.7" and python_full_version >= "3.6.1" -pyzmq==22.3.0; python_full_version >= "3.6.1" and python_version >= "3.7" +pyzmq==22.3.0; python_full_version >= "3.7.0" and python_version >= "3.7" qdldl==0.1.5.post0; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" qtconsole==5.2.2; python_version >= "3.6" qtpy==2.0.1; python_version >= "3.6" @@ -218,7 +225,7 @@ requests==2.27.1; (python_version >= "2.7" and python_full_version < "3.0.0") or rich==10.16.2; python_full_version >= "3.6.2" and python_full_version < "4.0.0" robin-stocks==2.1.0; python_version >= "3" scikit-learn==1.0.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" -scipy==1.8.0; python_version >= "3.8" and python_version < "3.11" and python_full_version >= "3.6.1" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") +scipy==1.8.0; python_version >= "3.8" and python_version < "3.11" and python_full_version >= "3.6.1" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7") screeninfo==0.6.7 scs==3.2.0; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" seaborn==0.11.2; python_version >= "3.6" @@ -226,7 +233,9 @@ selenium==3.141.0 send2trash==1.8.0; python_version >= "3.7" sentiment-investor==2.1.0; python_version >= "3.8" and python_version < "4.0" setuptools-scm==6.4.2; python_version >= "3.7" -six==1.16.0; python_full_version >= "3.7.1" and python_version >= "3.7" and python_full_version < "4.0.0" and (python_version >= "3.8" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.3.0") and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5") and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") and (python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.8") +six==1.16.0; python_full_version >= "3.7.1" and python_version >= "3.8" and python_full_version < "4.0.0" and python_version < "4.0" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.3.0") and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5") and (python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.8") +slack-bolt==1.11.6; python_version >= "3.6" +slack-sdk==3.15.2; python_full_version >= "3.6.0" and python_version >= "3.6" smmap==5.0.0; python_version >= "3.7" sniffio==1.2.0; python_full_version >= "3.6.2" and python_version >= "3.7" snowballstemmer==2.2.0; python_version >= "3.7" @@ -253,12 +262,12 @@ terminado==0.13.1; python_version >= "3.7" testpath==0.5.0; python_version >= "3.7" textwrap3==0.9.2; python_version >= "3.6" thepassiveinvestor==1.0.10 -threadpoolctl==3.1.0; python_version >= "3.7" +threadpoolctl==3.1.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" tokenize-rt==4.2.1; python_full_version >= "3.6.1" toml==0.10.2; python_full_version >= "3.6.2" and python_version >= "3.6" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") tomli==1.2.3; python_version >= "3.8" and python_full_version >= "3.6.2" toolz==0.11.2; python_version >= "3.7" and python_full_version >= "3.7.0" -tornado==6.1; python_full_version >= "3.6.1" and python_version >= "3.7" +tornado==6.1; python_full_version >= "3.7.0" and python_version >= "3.7" tqdm==4.62.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" tradingview-ta==3.2.10; python_version >= "3.6" traitlets==5.1.1; python_full_version >= "3.7.0" and python_version >= "3.8" @@ -274,7 +283,7 @@ tzlocal==4.1; python_version >= "3.6" ujson==5.1.0; python_version >= "3.7" unidecode==1.3.3; python_version >= "3.7" update-checker==0.18.0; python_version >= "3.6" and python_version < "4.0" -urllib3==1.26.8; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.7" +urllib3==1.26.8; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.8" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7") user-agent==0.1.10 uvicorn==0.17.5; python_version >= "3.7" vadersentiment==3.3.2 @@ -282,12 +291,12 @@ valinvest==0.0.2; python_version >= "3.6" vcrpy==4.1.1; python_version >= "3.5" virtualenv==20.13.1; python_full_version >= "3.6.1" voila==0.3.2; python_version >= "3.7" -wcwidth==0.2.5; python_full_version >= "3.6.2" and python_version >= "3.6" +wcwidth==0.2.5; python_full_version >= "3.6.2" and python_version >= "3.8" webencodings==0.5.1; python_version >= "3.7" websocket-client==1.2.3; python_version >= "3.8" and python_version < "4.0" websockets==9.1; python_full_version >= "3.6.1" and python_version >= "3.7" widgetsnbextension==3.5.2 -wrapt==1.13.3; python_full_version >= "3.7.1" and python_full_version < "4.0.0" and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5") -yarl==1.7.2; python_version >= "3.6" +wrapt==1.13.3; python_full_version >= "3.7.1" and python_full_version < "4.0.0" and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7") +yarl==1.7.2; python_version >= "3.6" and python_full_version >= "3.8.0" yfinance==0.1.70 zope.interface==5.4.0; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" diff --git a/bots/slack/run_slack.py b/bots/slack/run_slack.py new file mode 100644 index 00000000000..4816ef5a14d --- /dev/null +++ b/bots/slack/run_slack.py @@ -0,0 +1,267 @@ +import os +import logging +from typing import List, Pattern, Dict, Any, Set, Union +from dotenv import load_dotenv +from slack_bolt import App +from slack_bolt.adapter.socket_mode import SocketModeHandler +from bots.common.commands_dict import commands +from bots.helpers import ShowView + + +load_dotenv() + +app = App(token=os.environ["GT_SLACK_APP_TOKEN"]) + +available_commands = list(commands.keys()) + + +def get_syntax(selected: Dict[str, Any], cmd: str) -> str: + """Returns the syntax for a given command + + Parameters + ---------- + selected : Dict[str, Any] + The command object + cmd : str + The command that was attempted + + Returns + --------- + syntax : str + The syntax for the given command + """ + + syntax = f"{cmd}/" + syntax += "/".join(selected.get("required", [])) + return syntax + + +def get_arguments( + selected: Dict[str, Any], req_name: str, channel_id: str, user_id: str, client: Any +) -> None: + """Returns the arguments for a given command + + Parameters + ---------- + selected : Dict[str, Any] + The command object + req_name : str + The name of the requirement + channel_id : str + Slack channel id + user_id : str + Slack user id + client : Any + Client that sends messages to slack + """ + if req_name == "ticker": + message = "Please give a listed ticker" + elif req_name == "past_transactions_days": + message = "Please give the number of days as an integer" + elif req_name == "raw": + message = "Please type true or false" + + else: + select = [str(x) for x in selected["required"].get(req_name, [])] + selections = ", ".join(select) + if len(selections) < 990: + selections = f"{selections[:990]}" + message = f"Options: {selections}" + + payload = {"channel": channel_id, "username": user_id, "text": message} + client.chat_postMessage(**payload) + + +def send_options( + name: str, + items: Union[List[Any], Set[Any]], + channel_id: str, + user_id: str, + client: Any, +) -> None: + """Sends the options for a user + + Parameters + ---------- + name : str + The name of the section + items : List[str] + The items the user can select from + channel_id : str + Slack channel id + user_id : str + Slack user id + client : Any + Client that sends messages to slack + """ + message = name + clean = list(items) + clean.sort() + message += ", ".join(list(clean)) + payload = {"channel": channel_id, "username": user_id, "text": message} + client.chat_postMessage(**payload) + + +@app.event("message") +def processMessage(event, client): + """Process users' commands""" + channel_id = event.get("channel") + user_id = event.get("user") + text = event.get("text") + if text[0] == "!": + cmd = text[1:] + full_cmd = cmd.split("/") + if full_cmd[0] == "help": + help_text = ( + "\n!disc-fidelity\n" + "!ins-last/ticker \n" + "\n!opt-unu\n" + "!opt-iv/ticker\n" + "!opt-vsurf/ticker \n" + "!opt-hist/ticker \n" + "!opt-oi/ticker \n" + "!opt-vol/ticker \n" + "!opt-overview/ticker \n" + "!opt-chain/ticker \n" + "\n!ta-summary/ticker\n" + "!ta-view/ticker\n" + "!ta-recom/ticker\n" + "!ta-obv/ticker \n" + "!ta-fib/ticker \n" + "!ta-ad/ticker \n" + "!ta-cg/ticker \n" + "!ta-fisher/ticker \n" + "!ta-cci/ticker \n" + "!ta-ema/ticker \n" + "!ta-sma/ticker \n" + "!ta-wma/ticker \n" + "!ta-hma/ticker \n" + "!ta-zlma/ticker \n" + "!ta-aroon/ticker \n" + "!ta-adosc/ticker \n" + "!ta-macd/ticker \n" + "!ta-kc/ticker \n" + "!ta-adx/ticker \n" + "!ta-rsi/ticker \n" + "!ta-stoch/ticker \n" + "!ta-bbands/ticker \n" + "!ta-donchian/ticker \n" + "\n!dd-est/ticker\n" + "!dd-sec/ticker\n" + "!dd-analyst/ticker\n" + "!dd-supplier/ticker\n" + "!dd-customer/ticker\n" + "!dd-arktrades/ticker\n" + "!dd-pt/ticker \n" + "\n!dps-hsi \n" + "!dps-shorted/(NUM)\n" + "!dps-psi/ticker\n" + "!dps-spos/ticker\n" + "!dps-dpotc/ticker\n" + "!dps-pos \n" + "!dps-sidtc \n" + "!dps-ftd/ticker \n" + "\n!scr-presets-default\n" + "!scr-presets-custom\n" + "!scr-historical/(SIGNAL) \n" + "!scr-overview/(PRESET) \n" + "!scr-technical/(PRESET) \n" + "!scr-valuation/(PRESET) \n" + "!scr-financial/(PRESET) \n" + "!scr-ownership/(PRESET) \n" + "!scr-performance/(PRESET) \n" + "\n!gov-histcont/ticker\n" + "!gov-lobbying/ticker \n" + "!gov-toplobbying \n" + "!gov-lastcontracts \n" + "!gov-contracts/ticker \n" + "!gov-qtrcontracts \n" + "!gov-lasttrades \n" + "!gov-gtrades/ticker \n" + "!gov-topbuys \n" + "!gov-topsells \n" + "\n` = Past Transaction Days`\n" + "` = Past Transaction Months`" + "\n!econ-softs\n" + "!econ-meats\n" + "!econ-energy\n" + "!econ-metals\n" + "!econ-grains\n" + "!econ-futures\n" + "!econ-usbonds\n" + "!econ-glbonds\n" + "!econ-indices\n" + "!econ-overview\n" + "!econ-feargreed\n" + "!econ-currencies\n" + "!econ-valuation \n" + "!econ-performance \n" + ) + payload = { + "channel": channel_id, + "username": user_id, + "text": help_text, + } + client.chat_postMessage(**payload) + return True + group = full_cmd[0].split("-")[0] + parents = {x.split("-")[0] for x in commands} + if group in parents: + if full_cmd[0] in commands: + selected = commands[full_cmd[0]] + if len(full_cmd) != len(selected.get("required", [])) + 1: + syntax = get_syntax(selected, full_cmd[0]) + payload = { + "channel": channel_id, + "username": user_id, + "text": f"Required syntax: {syntax}", + } + client.chat_postMessage(**payload) + return False + other_args = {} + for i, val in enumerate(full_cmd[1:]): + req_name = list(selected.get("required", {}).keys())[i] + required = selected.get("required", [])[req_name] + if isinstance(required, List) and required != [True, False]: + required = [str(x) for x in required] + if isinstance(val, str) and req_name in ["ticker"]: + val = val.upper() + elif isinstance(val, str) and req_name == "raw": + val = bool(val) + if (isinstance(required, List) and val not in required) or ( + isinstance(required, Pattern) and not required.match(val) + ): + syntax = get_syntax(selected, full_cmd[0]) + payload = { + "channel": channel_id, + "username": user_id, + "text": f"{syntax}\nInvalid argument for: {req_name}", + } + client.chat_postMessage(**payload) + get_arguments(selected, req_name, channel_id, user_id, client) + return False + other_args[req_name] = val + func = selected["function"] + ShowView().slack(func, channel_id, user_id, client, **other_args) + return True + show_cmds = [] + for command in commands: + if group == command[: len(group)]: + show_cmds.append(command) + send_options("Valid commands: ", show_cmds, channel_id, user_id, client) + return False + send_options("Valid categories: ", parents, channel_id, user_id, client) + return False + return False + + +if __name__ == "__main__": + # TODO: replace with GST logging + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler()) + handler = SocketModeHandler( + app, + os.environ["GT_SLACK_BOT_TOKEN"], + ) + handler.start() diff --git a/bots/telegram/run_telegram.py b/bots/telegram/run_telegram.py index 0fd1182220c..d6b525ddda5 100644 --- a/bots/telegram/run_telegram.py +++ b/bots/telegram/run_telegram.py @@ -1,4 +1,5 @@ import os +import logging from typing import List, Pattern, Dict, Any, Union, Set import telebot from dotenv import load_dotenv @@ -55,8 +56,8 @@ def get_arguments(selected: Dict[str, Any], req_name: str, message: Any) -> None The command object req_name : str The name of the requirement - group_id : str - The groupme chat id + message: Any + Object that contains telegram request info """ if req_name == "ticker": @@ -82,8 +83,8 @@ def send_options(name: str, items: Union[List[Any], Set[Any]], message: Any) -> The name of the section items : List[str] The items the user can select from - group_id : str - The groupme chat id + message: Any + Object that contains telegram request info """ help_message = name clean = list(items) @@ -120,12 +121,10 @@ Check the available commands with /cmds bot.send_message( chat_id=message.chat.id, text=text, reply_markup=markdown, parse_mode="MARKDOWN" ) - # bot.reply_to(markdown, text, parse_mode="MARKDOWN") - # bot.reply_to(message, text, parse_mode="MARKDOWN") @bot.message_handler(commands=["cmds", "commands"]) -def send_help(message): +def send_cmds(message): helptext = ( "\n/disc_fidelity\n" "/ins_last/ticker \n" @@ -259,4 +258,9 @@ def send_command(message): return False -bot.infinity_polling() +if __name__ == "__main__": + # TODO: replace with GST logging + logger = logging.getLogger() + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler()) + bot.infinity_polling() diff --git a/poetry.lock b/poetry.lock index 2c324d2ac45..0329823aff7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2729,6 +2729,22 @@ category = "main" optional = false python-versions = ">=3.7" +[[package]] +name = "pytelegrambotapi" +version = "4.4.0" +description = "Python Telegram bot api." +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +requests = "*" + +[package.extras] +PIL = ["pillow"] +json = ["ujson"] +redis = ["redis (>=3.4.1)"] + [[package]] name = "pytest" version = "6.2.5" @@ -3220,6 +3236,36 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "slack-bolt" +version = "1.11.6" +description = "The Bolt Framework for Python" +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +slack-sdk = ">=3.9.0,<4" + +[package.extras] +adapter = ["boto3 (<=2)", "bottle (>=0.12,<1)", "chalice (>=1.26.5,<2)", "click (>=7,<8)", "CherryPy (>=18,<19)", "Django (>=3,<5)", "falcon (>=2,<4)", "fastapi (>=0.70.0,<1)", "Flask (>=1,<3)", "Werkzeug (>=2,<3)", "pyramid (>=1,<3)", "sanic (>=21,<22)", "starlette (>=0.14,<1)", "tornado (>=6,<7)", "uvicorn (<1)", "gunicorn (>=20,<21)", "websocket-client (>=1.2.3,<2)"] +adapter_testing = ["moto (>=3,<4)", "docker (>=5,<6)", "boddle (>=0.2,<0.3)", "Flask (>=1,<2)", "Werkzeug (>=1,<2)", "sanic-testing (>=0.7)", "requests (>=2,<3)"] +async = ["aiohttp (>=3,<4)", "websockets (>=8,<10)"] +testing = ["pytest (>=6.2.5,<7)", "pytest-cov (>=3,<4)", "Flask-Sockets (>=0.2,<1)", "Werkzeug (>=1,<2)", "itsdangerous (==2.0.1)", "black (==22.1.0)", "pytest-asyncio (<1)", "aiohttp (>=3,<4)"] +testing_without_asyncio = ["pytest (>=6.2.5,<7)", "pytest-cov (>=3,<4)", "Flask-Sockets (>=0.2,<1)", "Werkzeug (>=1,<2)", "itsdangerous (==2.0.1)", "black (==22.1.0)"] + +[[package]] +name = "slack-sdk" +version = "3.15.2" +description = "The Slack API Platform SDK for Python" +category = "main" +optional = true +python-versions = ">=3.6.0" + +[package.extras] +optional = ["aiodns (>1.0)", "aiohttp (>=3.7.3,<4)", "boto3 (<=2)", "SQLAlchemy (>=1,<2)", "websockets (>=10,<11)", "websocket-client (>=1,<2)"] +testing = ["pytest (>=6.2.5,<7)", "pytest-asyncio (<1)", "Flask-Sockets (>=0.2,<1)", "Flask (>=1,<2)", "Werkzeug (<2)", "itsdangerous (==2.0.1)", "pytest-cov (>=2,<3)", "codecov (>=2,<3)", "flake8 (>=4,<5)", "black (==22.1.0)", "psutil (>=5,<6)", "databases (>=0.5)", "boto3 (<=2)", "moto (>=3,<4)"] + [[package]] name = "smmap" version = "5.0.0" @@ -4076,12 +4122,13 @@ test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [extras] -discord = ["pyimgur", "df2img", "fastapi", "uvicorn", "disnake", "PyNaCl"] +discord = ["pyimgur", "df2img", "fastapi", "uvicorn", "disnake", "PyNaCl", "pyTelegramBotAPI", "slack-bolt"] prediction = ["tensorflow"] [metadata] lock-version = "1.1" python-versions = "^3.8,<3.10" +content-hash = "1ef9648b6c7b7720bd8847def2696ab06cca0028839841f4e319500c6e1165b0" [metadata.files] absl-py = [ @@ -5946,6 +5993,9 @@ pyrsistent = [ {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, ] +pytelegrambotapi = [ + {file = "pyTelegramBotAPI-4.4.0.tar.gz", hash = "sha256:e6f22356abebffe0a893dcf72fe09a0c8bdedad6f498c55a30c3dd32ce488e6a"}, +] pytest = [ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, @@ -6423,6 +6473,14 @@ six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +slack-bolt = [ + {file = "slack_bolt-1.11.6-py2.py3-none-any.whl", hash = "sha256:43aaadde470a8ed0d6737ed97b1f4f38a6ec53ab842eca076bad497cd8eb1d70"}, + {file = "slack_bolt-1.11.6.tar.gz", hash = "sha256:7aad413b3d59884c74defe73f9d697ac13056c9cfcd2f5482c5eb8c9f9aa9322"}, +] +slack-sdk = [ + {file = "slack_sdk-3.15.2-py2.py3-none-any.whl", hash = "sha256:e1fa26786169176e707676decc287fd9d3d547bbc43c0a1a4f99eb373b07da94"}, + {file = "slack_sdk-3.15.2.tar.gz", hash = "sha256:128f3bb0b5b91454a3d5f140a61f3db370a0e1b50ffe0a8d9e9ebe0e894faed7"}, +] smmap = [ {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, diff --git a/pyproject.toml b/pyproject.toml index 0b8a985663d..14f706dcb19 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,6 +84,8 @@ fastapi = {version = "^0.73.0", optional = true} uvicorn = {version = "^0.17.1", optional = true} disnake = {version = "^2.3.0", optional = true} PyNaCl = {version = "^1.5.0", optional = true} +pyTelegramBotAPI = {version = "^4.4.0", optional = true} +slack-bolt = {version = "^1.11.6", optional = true} ascii-magic = "^1.6" squarify = "^0.4.3" finnhub-python = "^2.4.10" @@ -117,7 +119,7 @@ pytest-cov = "^3.0.0" [tool.poetry.extras] prediction = ["tensorflow"] -discord = ["pyimgur", "df2img", "fastapi", "uvicorn", "disnake", "PyNaCl"] +discord = ["pyimgur", "df2img", "fastapi", "uvicorn", "disnake", "PyNaCl", "pyTelegramBotAPI", "slack_bolt"] [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/website/content/bot/_index.md b/website/content/bot/_index.md index d5f2fccdcca..15dd1361146 100644 --- a/website/content/bot/_index.md +++ b/website/content/bot/_index.md @@ -19,3 +19,12 @@ For local or new remote server installations, follow the instructions here: http Server administrators for Discord can invite the GameStonks Bot to their servers by clicking the invite link: https://discord.com/oauth2/authorize?client_id=927232119346962513&permissions=510500797553&scope=bot%20applications.commands + +WIP + +Bot currently available for the following platforms: + +- Discord +- Telegram +- Groupme +- Slack diff --git a/website/content/bot/groupme/_index.md b/website/content/bot/groupme/_index.md new file mode 100644 index 00000000000..0c71e915683 --- /dev/null +++ b/website/content/bot/groupme/_index.md @@ -0,0 +1,20 @@ +--- +geekdocCollapseSection: true +--- + +{{< toc-tree >}} + +WIP + +[GroupmeDemo.mov.zip](https://github.com/GamestonkTerminal/GamestonkTerminal/files/8178584/Screen.Recording.2022-03-03.at.9.23.58.AM.mov.zip) + +No installation or signup is required, nor is there a need to be in a server setting to use the bot. Any user call it through direct messaging / commands to: [t.me/GamestonkTerminalBot](https://t.me/GamestonkTerminalBot) +You can also invite the bot + +Alternatively, the bot can be installed locally, or on a privately-hosted server, using the installation instructions found here: + +https://github.com/GamestonkTerminal/GamestonkTerminal/blob/main/bots/README.md + +Learn how to use the GameStonksBot in less than five minutes, by reading the Introduction page. + +Questions and feedback are welcome in the [official support server](https://discord.com/invite/Up2QGbMKHY) diff --git a/website/content/bot/slack/_index.md b/website/content/bot/slack/_index.md new file mode 100644 index 00000000000..e991a85b776 --- /dev/null +++ b/website/content/bot/slack/_index.md @@ -0,0 +1,20 @@ +--- +geekdocCollapseSection: true +--- + +{{< toc-tree >}} + +WIP + +![Slack Bot](https://user-images.githubusercontent.com/43375532/157667922-f0d9abdb-a39b-4593-b8ca-7f4f169f922f.png) + +No installation or signup is required, nor is there a need to be in a server setting to use the bot. Any user call it through direct messaging / commands to: [t.me/GamestonkTerminalBot](https://t.me/GamestonkTerminalBot) +You can also invite the bot + +Alternatively, the bot can be installed locally, or on a privately-hosted server, using the installation instructions found here: + +https://github.com/GamestonkTerminal/GamestonkTerminal/blob/main/bots/README.md + +Learn how to use the GameStonksBot in less than five minutes, by reading the Introduction page. + +Questions and feedback are welcome in the [official support server](https://discord.com/invite/Up2QGbMKHY) diff --git a/website/content/bot/telegram/_index.md b/website/content/bot/telegram/_index.md new file mode 100644 index 00000000000..7127586f41c --- /dev/null +++ b/website/content/bot/telegram/_index.md @@ -0,0 +1,20 @@ +--- +geekdocCollapseSection: true +--- + +{{< toc-tree >}} + +WIP + +![Telegram Bot](https://user-images.githubusercontent.com/43375532/157494575-72d7202f-7f1c-4093-a815-a2c3aac21835.png) + +No installation or signup is required, nor is there a need to be in a server setting to use the bot. Any user call it through direct messaging / commands to: [t.me/GamestonkTerminalBot](https://t.me/GamestonkTerminalBot) +You can also invite the bot + +Alternatively, the bot can be installed locally, or on a privately-hosted server, using the installation instructions found here: + +https://github.com/GamestonkTerminal/GamestonkTerminal/blob/main/bots/README.md + +Learn how to use the GameStonksBot in less than five minutes, by reading the Introduction page. + +Questions and feedback are welcome in the [official support server](https://discord.com/invite/Up2QGbMKHY) diff --git a/website/data/menu/main.yml b/website/data/menu/main.yml index 3f872097345..3f51ed76592 100755 --- a/website/data/menu/main.yml +++ b/website/data/menu/main.yml @@ -1233,6 +1233,12 @@ main: - name: "bot" ref: "/bot" sub: + - name: "GroupMe" + ref: "/bot/groupme" + - name: "Slack" + ref: "/bot/slack" + - name: "Telegram" + ref: "/bot/telegram" - name: "Discord" ref: "/bot/discord" sub: