mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-12-01 09:33:14 +00:00
Merge pull request #1346 from mousumi2002/feat/slack_bot
Add slack integration
This commit is contained in:
3
extensions/slack-bot/.gitignore
vendored
Normal file
3
extensions/slack-bot/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.env
|
||||
.venv/
|
||||
get-pip.py
|
||||
84
extensions/slack-bot/Readme.md
Normal file
84
extensions/slack-bot/Readme.md
Normal file
@@ -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.
|
||||
112
extensions/slack-bot/app.py
Normal file
112
extensions/slack-bot/app.py
Normal file
@@ -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())
|
||||
10
extensions/slack-bot/requirements.txt
Normal file
10
extensions/slack-bot/requirements.txt
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user