From 3059e96041d3b515d991e6e2dc8ac79af7752ae2 Mon Sep 17 00:00:00 2001 From: cachho Date: Tue, 5 Sep 2023 10:18:52 +0200 Subject: [PATCH] feat: Slack bot (#469) Co-authored-by: Taranjeet Singh --- docs/examples/slack_bot.mdx | 26 ++++---- embedchain/bots/slack.py | 98 +++++++++++++++++++++++++++++ examples/slack_bot/.gitignore | 7 --- examples/slack_bot/README.md | 3 - examples/slack_bot/requirements.txt | 5 -- examples/slack_bot/slack_bot.py | 58 ----------------- examples/slack_bot/variables.env | 3 - pyproject.toml | 2 + 8 files changed, 112 insertions(+), 90 deletions(-) create mode 100644 embedchain/bots/slack.py delete mode 100644 examples/slack_bot/.gitignore delete mode 100644 examples/slack_bot/README.md delete mode 100644 examples/slack_bot/requirements.txt delete mode 100644 examples/slack_bot/slack_bot.py delete mode 100644 examples/slack_bot/variables.env diff --git a/docs/examples/slack_bot.mdx b/docs/examples/slack_bot.mdx index e4fa8f89..3bae4293 100644 --- a/docs/examples/slack_bot.mdx +++ b/docs/examples/slack_bot.mdx @@ -2,27 +2,25 @@ title: '💼 Slack Bot' --- -### 🖼️ Template Setup +### 🖼️ Setup -- Fork [this](https://replit.com/@taranjeetio/EC-Slack-Bot-Template?v=1#README.md) replit template. -- Set your `OPENAI_API_KEY` in Secrets. -- Create a workspace on Slack if you don't have one already by clicking [here](https://slack.com/intl/en-in/). -- Create a new App on your Slack account by going [here](https://api.slack.com/apps). -- Select `From Scratch`, then enter the Bot Name and select your workspace. -- On the `Basic Information` page copy the `Signing Secret` and set it in your secrets as `SLACK_SIGNING_SECRET`. -- On the left Sidebar, go to `OAuth and Permissions` and add the following scopes under `Bot Token Scopes`: +1. Create a workspace on Slack if you don't have one already by clicking [here](https://slack.com/intl/en-in/). +2. Create a new App on your Slack account by going [here](https://api.slack.com/apps). +3. Select `From Scratch`, then enter the Bot Name and select your workspace. +4. On the left Sidebar, go to `OAuth and Permissions` and add the following scopes under `Bot Token Scopes`: ```text app_mentions:read channels:history channels:read chat:write ``` -- Now select the option `Install to Workspace` and after it's done, copy the `Bot User OAuth Token` and set it in your secrets as `SLACK_BOT_TOKEN`. -- Start your replit container now by clicking on `Run`. -- On the Slack API website go to `Event Subscriptions` on the left Sidebar and turn on `Enable Events`. -- Copy the generated server URL in replit, append `/chat` at its end and paste it in `Request URL` box. -- After it gets verified, click on `Subscribe to bot events`, add `message.channels` Bot User Event and click on `Save Changes`. -- Now go to your workspace, click on the bot name in the Sidebar and then add the bot to any channel you want. +5. Now select the option `Install to Workspace` and after it's done, copy the `Bot User OAuth Token` and set it in your secrets as `SLACK_BOT_TOKEN`. +6. Run your bot now with `python3 -m embedchain.bots.slack` +7. Expose your bot to the internet. Default port is `5000`, which can be changed by adding `port --8080` to the startup command. You can use your machine's public IP or DNS. Otherwise, employ a proxy server like [ngrok](https://ngrok.com/) to make your local bot accessible. +8. On the Slack API website go to `Event Subscriptions` on the left Sidebar and turn on `Enable Events`. +9. In `Request URL`, enter your server or ngrok address. +10. After it gets verified, click on `Subscribe to bot events`, add `message.channels` Bot User Event and click on `Save Changes`. +11. Now go to your workspace, right click on the bot name in the sidebar, click `view app details`, then `add this app to a channel`. ### 🚀 Usage Instructions diff --git a/embedchain/bots/slack.py b/embedchain/bots/slack.py new file mode 100644 index 00000000..db2e9bb6 --- /dev/null +++ b/embedchain/bots/slack.py @@ -0,0 +1,98 @@ +import argparse +import logging +import os +import signal +import sys + +from embedchain import App +from embedchain.helper_classes.json_serializable import register_deserializable + +from .base import BaseBot + +try: + from flask import Flask, request + from slack_sdk import WebClient +except ModuleNotFoundError: + raise ModuleNotFoundError( + "The required dependencies for Slack are not installed." 'Please install with `pip install "embedchain[slack]"`' + ) from None + + +SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN") + + +@register_deserializable +class SlackBot(BaseBot): + def __init__(self): + self.client = WebClient(token=SLACK_BOT_TOKEN) + self.chat_bot = App() + self.recent_message = {"ts": 0, "channel": ""} + super().__init__() + + def handle_message(self, event_data): + message = event_data.get("event") + if message and "text" in message and message.get("subtype") != "bot_message": + text: str = message["text"] + if float(message.get("ts")) > float(self.recent_message["ts"]): + self.recent_message["ts"] = message["ts"] + self.recent_message["channel"] = message["channel"] + if text.startswith("query"): + _, question = text.split(" ", 1) + try: + response = self.chat_bot.chat(question) + self.send_slack_message(message["channel"], response) + logging.info("Query answered successfully!") + except Exception as e: + self.send_slack_message(message["channel"], "An error occurred. Please try again!") + logging.error("Error occurred during 'query' command:", e) + elif text.startswith("add"): + _, data_type, url_or_text = text.split(" ", 2) + if url_or_text.startswith("<") and url_or_text.endswith(">"): + url_or_text = url_or_text[1:-1] + try: + self.chat_bot.add(url_or_text, data_type) + self.send_slack_message(message["channel"], f"Added {data_type} : {url_or_text}") + except ValueError as e: + self.send_slack_message(message["channel"], f"Error: {str(e)}") + logging.error("Error occurred during 'add' command:", e) + except Exception as e: + self.send_slack_message(message["channel"], f"Failed to add {data_type} : {url_or_text}") + logging.error("Error occurred during 'add' command:", e) + + def send_slack_message(self, channel, message): + response = self.client.chat_postMessage(channel=channel, text=message) + return response + + def start(self, host="0.0.0.0", port=5000, debug=True): + app = Flask(__name__) + + def signal_handler(sig, frame): + logging.info("\nGracefully shutting down the SlackBot...") + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + + @app.route("/", methods=["POST"]) + def chat(): + # Check if the request is a verification request + if request.json.get("challenge"): + return str(request.json.get("challenge")) + + response = self.handle_message(request.json) + return str(response) + + app.run(host=host, port=port, debug=debug) + + +def start_command(): + parser = argparse.ArgumentParser(description="EmbedChain SlackBot command line interface") + parser.add_argument("--host", default="0.0.0.0", help="Host IP to bind") + parser.add_argument("--port", default=5000, type=int, help="Port to bind") + args = parser.parse_args() + + slack_bot = SlackBot() + slack_bot.start(host=args.host, port=args.port) + + +if __name__ == "__main__": + start_command() diff --git a/examples/slack_bot/.gitignore b/examples/slack_bot/.gitignore deleted file mode 100644 index ba288ed3..00000000 --- a/examples/slack_bot/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -__pycache__ -db -database -pyenv -venv -.env -trash_files/ diff --git a/examples/slack_bot/README.md b/examples/slack_bot/README.md deleted file mode 100644 index beb86341..00000000 --- a/examples/slack_bot/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Slack Bot - -This is a replit template to create your own Slack bot using the embedchain package. To know more about the bot and how to use it, go [here](https://docs.embedchain.ai/examples/slack_bot). \ No newline at end of file diff --git a/examples/slack_bot/requirements.txt b/examples/slack_bot/requirements.txt deleted file mode 100644 index d604507f..00000000 --- a/examples/slack_bot/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -flask==2.3.2 -slackeventsapi==3.0.1 -slacksdk==3.21.3 -python-dotenv==1.0.0 -embedchain \ No newline at end of file diff --git a/examples/slack_bot/slack_bot.py b/examples/slack_bot/slack_bot.py deleted file mode 100644 index 302a2167..00000000 --- a/examples/slack_bot/slack_bot.py +++ /dev/null @@ -1,58 +0,0 @@ -import os - -from dotenv import load_dotenv -from flask import Flask -from slack_sdk import WebClient -from slackeventsapi import SlackEventAdapter - -from embedchain import App - -load_dotenv() -app = Flask(__name__) - -slack_signing_secret = os.environ.get("SLACK_SIGNING_SECRET") -slack_events_adapter = SlackEventAdapter(slack_signing_secret, "/chat", app) - -slack_bot_token = os.environ.get("SLACK_BOT_TOKEN") -client = WebClient(token=slack_bot_token) - -chat_bot = App() -recent_message = {"ts": 0, "channel": ""} - - -@slack_events_adapter.on("message") -def handle_message(event_data): - message = event_data["event"] - if "text" in message and message.get("subtype") != "bot_message": - text = message["text"] - if float(message.get("ts")) > float(recent_message["ts"]): - recent_message["ts"] = message["ts"] - recent_message["channel"] = message["channel"] - if text.startswith("query"): - _, question = text.split(" ", 1) - try: - response = chat_bot.chat(question) - send_slack_message(message["channel"], response) - print("Query answered successfully!") - except Exception as e: - send_slack_message(message["channel"], "An error occurred. Please try again!") - print("Error occurred during 'query' command:", e) - elif text.startswith("add"): - _, data_type, url_or_text = text.split(" ", 2) - if url_or_text.startswith("<") and url_or_text.endswith(">"): - url_or_text = url_or_text[1:-1] - try: - chat_bot.add(data_type, url_or_text) - send_slack_message(message["channel"], f"Added {data_type} : {url_or_text}") - except Exception as e: - send_slack_message(message["channel"], f"Failed to add {data_type} : {url_or_text}") - print("Error occurred during 'add' command:", e) - - -def send_slack_message(channel, message): - response = client.chat_postMessage(channel=channel, text=message) - return response - - -if __name__ == "__main__": - app.run(host="0.0.0.0", port=5000, debug=False) diff --git a/examples/slack_bot/variables.env b/examples/slack_bot/variables.env deleted file mode 100644 index 1b00ead5..00000000 --- a/examples/slack_bot/variables.env +++ /dev/null @@ -1,3 +0,0 @@ -SLACK_SIGNING_SECRET="" -SLACK_BOT_TOKEN="" -OPENAI_API_KEY="" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 7011e769..3dc67a6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,6 +101,7 @@ flask = "^2.3.3" twilio = "^8.5.0" fastapi-poe = { version = "0.0.16", optional = true } discord = { version = "^2.3.2", optional = true } +slack-sdk = { version = "3.21.3", optional = true } @@ -121,6 +122,7 @@ opensource = ["sentence-transformers", "torch", "gpt4all"] elasticsearch = ["elasticsearch"] poe = ["fastapi-poe"] discord = ["discord"] +slack = ["slack-sdk", "flask"] [tool.poetry.group.docs.dependencies]