-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1346 from mousumi2002/feat/slack_bot
Add slack integration
- Loading branch information
Showing
4 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.env | ||
.venv/ | ||
get-pip.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
|
||
# Slack Bot Configuration Guide | ||
|
||
> **Note:** The following guidelines must be followed on the [Slack API website](https://api.slack.com/) for setting up your Slack app and generating the necessary tokens. | ||
## Step-by-Step Instructions | ||
|
||
### 1. Navigate to Your Apps | ||
- Go to the Slack API page for apps and select **Create an App** from the “From Scratch” option. | ||
|
||
### 2. App Creation | ||
- Name your app and choose the workspace where you wish to add the assistant. | ||
|
||
### 3. Enabling Socket Mode | ||
- Navigate to **Settings > Socket Mode** and enable **Socket Mode**. | ||
- This action will generate an App-level token. Select the `connections:write` scope and copy the App-level token for future use. | ||
|
||
### 4. Socket Naming | ||
- Assign a name to your socket as per your preference. | ||
|
||
### 5. Basic Information Setup | ||
- Go to **Basic Information** (under **Settings**) and configure the following: | ||
- Assistant name | ||
- App icon | ||
- Background color | ||
|
||
### 6. Bot Token and Permissions | ||
- In the **OAuth & Permissions** option found under the **Features** section, retrieve the Bot Token. Save it for future usage. | ||
- You will also need to add specific bot token scopes: | ||
- `app_mentions:read` | ||
- `assistant:write` | ||
- `chat:write` | ||
- `chat:write.public` | ||
- `im:history` | ||
|
||
### 7. Enable Events | ||
- From **Event Subscriptions**, enable events and add the following Bot User events: | ||
- `app_mention` | ||
- `assistant_thread_context_changed` | ||
- `assistant_thread_started` | ||
- `message.im` | ||
|
||
### 8. Agent/Assistant Toggle | ||
- In the **Features > Agent & Assistants** section, toggle on the Agent or Assistant option. | ||
- In the **Suggested Prompts** setting, leave it as `dynamic` (this is the default setting). | ||
|
||
--- | ||
|
||
## Code-Side Configuration Guide | ||
|
||
This section focuses on generating and setting up the necessary tokens in the `.env` file, using the `.env-example` as a template. | ||
|
||
### Step 1: Generating Required Keys | ||
|
||
1. **SLACK_APP_TOKEN** | ||
- Navigate to **Settings > Socket Mode** in the Slack API and enable **Socket Mode**. | ||
- Copy the App-level token generated (usually starts with `xapp-`). | ||
|
||
2. **SLACK_BOT_TOKEN** | ||
- Go to **OAuth & Permissions** (under the **Features** section in Slack API). | ||
- Retrieve the **Bot Token** (starts with `xoxb-`). | ||
|
||
3. **DOCSGPT_API_KEY** | ||
- Go to the **DocsGPT website**. | ||
- Navigate to **Settings > Chatbots > Create New** to generate a DocsGPT API Key. | ||
- Copy the generated key for use. | ||
|
||
### Step 2: Creating and Updating the `.env` File | ||
|
||
1. Create a new `.env` file in the root of your project (if it doesn’t already exist). | ||
2. Use the `.env-example` as a reference and update the file with the following keys and values: | ||
|
||
```bash | ||
# .env file | ||
SLACK_APP_TOKEN=xapp-your-generated-app-token | ||
SLACK_BOT_TOKEN=xoxb-your-generated-bot-token | ||
DOCSGPT_API_KEY=your-docsgpt-generated-api-key | ||
``` | ||
|
||
Replace the placeholder values with the actual tokens generated from the Slack API and DocsGPT as per the steps outlined above. | ||
|
||
--- | ||
|
||
This concludes the guide for both setting up the Slack API and configuring the `.env` file on the code side. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import os | ||
import hashlib | ||
import httpx | ||
import re | ||
from slack_bolt.async_app import AsyncApp | ||
from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler | ||
from dotenv import load_dotenv | ||
|
||
load_dotenv() | ||
API_BASE = os.getenv("API_BASE", "https://gptcloud.arc53.com") | ||
API_URL = API_BASE + "/api/answer" | ||
|
||
# Slack bot token and signing secret | ||
SLACK_BOT_TOKEN = os.getenv("SLACK_BOT_TOKEN") | ||
SLACK_APP_TOKEN = os.getenv("SLACK_APP_TOKEN") | ||
|
||
# OpenAI API key for DocsGPT (replace this with your actual API key) | ||
DOCSGPT_API_KEY = os.getenv("DOCSGPT_API_KEY") | ||
|
||
# Initialize Slack app | ||
app = AsyncApp(token=SLACK_BOT_TOKEN) | ||
|
||
def encode_conversation_id(conversation_id: str) -> str: | ||
""" | ||
Encodes 11 length Slack conversation_id to 12 length string | ||
Args: | ||
conversation_id (str): The 11 digit slack conversation_id. | ||
Returns: | ||
str: Hashed id. | ||
""" | ||
# Create a SHA-256 hash of the string | ||
hashed_id = hashlib.sha256(conversation_id.encode()).hexdigest() | ||
|
||
# Take the first 24 characters of the hash | ||
hashed_24_char_id = hashed_id[:24] | ||
return hashed_24_char_id | ||
|
||
async def generate_answer(question: str, messages: list, conversation_id: str | None) -> dict: | ||
"""Generates an answer using the external API.""" | ||
payload = { | ||
"question": question, | ||
"api_key": DOCSGPT_API_KEY, | ||
"history": messages, | ||
"conversation_id": conversation_id, | ||
} | ||
headers = { | ||
"Content-Type": "application/json; charset=utf-8" | ||
} | ||
timeout = 60.0 | ||
async with httpx.AsyncClient() as client: | ||
response = await client.post(API_URL, json=payload, headers=headers, timeout=timeout) | ||
|
||
if response.status_code == 200: | ||
data = response.json() | ||
conversation_id = data.get("conversation_id") | ||
answer = data.get("answer", "Sorry, I couldn't find an answer.") | ||
return {"answer": answer, "conversation_id": conversation_id} | ||
else: | ||
print(response.json()) | ||
return {"answer": "Sorry, I couldn't find an answer.", "conversation_id": None} | ||
|
||
@app.message(".*") | ||
async def message_docs(message, say): | ||
client = app.client | ||
channel = message['channel'] | ||
thread_ts = message['thread_ts'] | ||
user_query = message['text'] | ||
await client.assistant_threads_setStatus( | ||
channel_id = channel, | ||
thread_ts = thread_ts, | ||
status = "is generating your answer...", | ||
) | ||
|
||
docs_gpt_channel_id = encode_conversation_id(thread_ts) | ||
|
||
# Get response from DocsGPT | ||
response = await generate_answer(user_query,[], docs_gpt_channel_id) | ||
answer = convert_to_slack_markdown(response['answer']) | ||
|
||
# Respond in Slack | ||
await client.chat_postMessage(text = answer, mrkdwn= True, channel= message['channel'], | ||
thread_ts = message['thread_ts'],) | ||
|
||
def convert_to_slack_markdown(markdown_text: str): | ||
# Convert bold **text** to *text* for Slack | ||
slack_text = re.sub(r'\*\*(.*?)\*\*', r'*\1*', markdown_text) # **text** to *text* | ||
|
||
# Convert italics _text_ to _text_ for Slack | ||
slack_text = re.sub(r'_(.*?)_', r'_\1_', slack_text) # _text_ to _text_ | ||
|
||
# Convert inline code `code` to `code` (Slack supports backticks for inline code) | ||
slack_text = re.sub(r'`(.*?)`', r'`\1`', slack_text) | ||
|
||
# Convert bullet points with single or no spaces to filled bullets (•) | ||
slack_text = re.sub(r'^\s{0,1}[-*]\s+', ' • ', slack_text, flags=re.MULTILINE) | ||
|
||
# Convert bullet points with multiple spaces to hollow bullets (◦) | ||
slack_text = re.sub(r'^\s{2,}[-*]\s+', '\t◦ ', slack_text, flags=re.MULTILINE) | ||
|
||
# Convert headers (##) to bold in Slack | ||
slack_text = re.sub(r'^\s*#{1,6}\s*(.*?)$', r'*\1*', slack_text, flags=re.MULTILINE) | ||
|
||
return slack_text | ||
|
||
async def main(): | ||
handler = AsyncSocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]) | ||
await handler.start_async() | ||
|
||
# Start the app | ||
if __name__ == "__main__": | ||
import asyncio | ||
asyncio.run(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
aiohttp>=3,<4 | ||
certifi==2024.7.4 | ||
h11==0.14.0 | ||
httpcore==1.0.5 | ||
httpx==0.27.0 | ||
idna==3.7 | ||
python-dotenv==1.0.1 | ||
sniffio==1.3.1 | ||
slack-bolt==1.21.0 | ||
bson==0.5.10 |