Skip to content

Commit

Permalink
add files
Browse files Browse the repository at this point in the history
  • Loading branch information
SilvestriStefano committed May 16, 2023
1 parent eca892f commit ca5e723
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .env_sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SLACK_SIGNING_SECRET=""
SLACK_BOT_TOKEN=""
OPENAI_API_KEY=""
OPENAI_ORGANIZATION=""
59 changes: 59 additions & 0 deletions INSTALL_ON_SLACK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# How to install the AI Slack App

In this guide, it is assumed the web app has a valid url address.

## Steps
1. Go to [https://api.slack.com/apps](https://api.slack.com/apps). Log in. then click on the big green button "Create an App".
2. It will ask you if you want to create one from scratch or use a template. We will create one from scratch
3. Enter a name, say *AssistantAI*
4. Choose the workspace. Note that you can only select those in which you have access.
5. Click on the green button "Create".
6. Set permissions
7. Install on workspace
8. Add the bot to the channels of your interest.
9. Connect the Web App to the bot

### Setting permissions
On the left menu select "OAuth & Permissions" under **Features**. Scroll down to the **Scopes** section.
Add bot scopes. In our case we will give the following:
- `chat:write`
- `app_mentions:read`
- `channels:read`
- `groups:read`

![Bot token scopes](screenshots/Slack_BotTokenScopes.png)

### Install on workspace
On the left menu select "Basic Information" under **Settings**. Click on "Install to Workspace".

![Add app to channel](screenshots/Slack_BasicInformation-Install.png)

You will see a confirmation page in which it is summarized the app permissions and capabilities. Click on the green button "Allow".

![Add app to channel](screenshots/Slack_allow.png)

Now the app is now installed on your workspace. You can check it on the left menu in your Slack application under "Apps"

![Add app to channel](screenshots/Slack_Apps_menu.png)

### Add the bot to a channel
Right click on the channel of your interest and select "View channels details". Select the "Integrations" tab and select "Add an App". Select your app from the list.

![Add app to channel](screenshots/Slack_addAppToChannel.png)

### Connect the Web App to the bot
In [https://api.slack.com/apps](https://api.slack.com/apps) select your bot from the dropdown menu on the top left.

Under **Features** select "OAuth & Permissions". Copy the OAuth Token for the Workspace and save it in the environment variable `SLACK_BOT_TOKEN`.

Under **Settings** select "Basic Information". Copy the "Signing Secret" in the **App Credentials** section and save it in the environment variable `SLACK_SIGNING_SECRET`.

Under **Features** select "Event Subscriptions". Toggle "Enable Events" (if it is not on already) and paste the url of your web app in the *Request Url* field and add the endpoint `/slack/event` to it. You should see a green check mark and *Verified*

![Add app to channel](screenshots/Slack_eventVerified.png)

Scroll down to the **Subscribe to bot events** section and click "Add Bot User Event". Select `app_mention` and `message.channels` and click the green button on the bottom that says "Save changes". The last event that we added will require to reinstall your app because it requires more permission that we have given it, so do it.

![Subscribe Bot Events](screenshots/Slack_subscribeBotEvents.png)

That's it! The app is now installed. Make sure you also retrieve the OpenAI API key and OpenAI Organization ID and save them in the `.env` file.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
# ai-slack-app
Create an AI assistant as a Slack App
An AI assistant as a Slack App

## Disclaimer
The code in `main.py` works fine, however as of 05/16/2023 it seems like the bot continues to answer to the same prompt without being asked to. I have not figured out the full reason for such a bug, however the issue seems to be with how I use the openAI API.

## How it works
This is a Flask web app so it must have a valid url. If you want to test it out locally you can use [ngrok](https://ngrok.com/).
I have tested it using [replit](replit.com) but without deploying it.

Once it is installed on your Slack channel simply mention the bot (in my case I called it *AssistantAI*, very clever I know) and it will respond to you in a thread.

![sample response in thread](screenshots/Slack_airesponse.png)

## References
To integrate the App on Slack I have mostly followed [DavidAtReplit guide](https://youtu.be/Rw84iRwFbJQ) but I modified it a little using the `slack-sdk` and `slackeventsapi` documentation.
167 changes: 167 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import os

from typing import Union
from pathlib import Path

from flask import Flask
from slack_sdk.web import WebClient
from slackeventsapi import SlackEventAdapter
from dotenv import load_dotenv
import re
import logging
import openai

# create a console logger
logger = logging.getLogger('chatLog')
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)-8s : %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)

env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)

# constants
SLACK_SIGNING_SECRET = os.getenv("SLACK_SIGNING_SECRET")
SLACK_BOT_TOKEN = os.getenv("SLACK_BOT_TOKEN")

# Authenticate OpenAI
openai.api_key = os.getenv("OPENAI_API_KEY")
openai.organization = os.getenv("OPENAI_ORGANIZATION")

# Helping classes for the AI bot
class Message:
"""
A basic class to create a message as a dict for chat
"""
def __init__(self, role:str, content:str, name:str = None)->None:
self.role = role
self.content = content
self.name = name

def message_for_ai(self)->dict:
if self.name is not None:
return {"role": self.role, "name": self.name, "content": self.content}
else:
return {"role": self.role, "content": self.content}

class Chat:
"""
The Chat class is used to preserve the conversation between the user and the bot and to send new messages to it.
"""
def __init__(self)->None:
self.conversation_history = []

def _get_assistant_response(self, prompt:list)->Union[Message,str]:
try:
completion = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=prompt
)
except openai.error.APIError as e:
# Handle API error here, e.g. retry or log
err_msg = f"OpenAI API returned an API Error: {e}"
logger.error(err_msg)
return err_msg
except openai.error.AuthenticationError as e:
# Handle Authentication error here, e.g. invalid API key
err_msg = f"OpenAI API returned an Authentication Error: {e}"
logger.error(err_msg)
return err_msg
except openai.error.APIConnectionError as e:
# Handle connection error here
err_msg = f"Failed to connect to OpenAI API: {e}"
logger.error(err_msg)
return err_msg
except openai.error.InvalidRequestError as e:
# Handle connection error here
err_msg = f"Invalid Request Error: {e}"
logger.error(err_msg)
return err_msg
except openai.error.RateLimitError as e:
# Handle rate limit error
err_msg = f"OpenAI API request exceeded rate limit: {e}"
logger.error(err_msg)
return err_msg
except openai.error.ServiceUnavailableError as e:
# Handle Service Unavailable error
err_msg = f"Service Unavailable: {e}"
logger.error(err_msg)
except openai.error.Timeout as e:
# Handle request timeout
err_msg = f"Request timed out: {e}"
logger.error(err_msg)
except Exception as e:
# Handle all other exceptions
err_msg = f"An exception has occured: {e}"
logger.error(err_msg)
return err_msg
else:
response_message = Message(
completion['choices'][0]['message']['role'],
completion['choices'][0]['message']['content']
)
return response_message

def ask_assistant(self, next_user_prompt:Union[list,dict])->Union[Message,str]:
if type(next_user_prompt)==dict:
self.conversation_history.append(next_user_prompt)
else:
[self.conversation_history.append(x) for x in next_user_prompt]
assistant_response = self._get_assistant_response(self.conversation_history)
if type(assistant_response)==Message:
self.conversation_history.append(assistant_response.message_for_ai())
return assistant_response

def _del_conversation(self)->None:
self.conversation_history = []


SYSTEM_PROMPTS = [
Message('system',"You are AssistantAI, a seasoned full-stack developer with expertise in Django, React, Flutter, Java Spring framework, and SAS software.You have a strong background in software engineering principles and have extensive experience in managing complex software projects in many programming languages like Python and Javascript. You are well-versed in Agile methodologies and have worked on projects in different domains, including e-commerce, education, healthcare, and finance. As an SEO advisor, you have helped many businesses improve their online visibility and conversion rates through effective search engine optimization strategies. You have a deep understanding of keyword research, on-page optimization, backlink analysis, and content marketing. Your proficiency in database management and design allows you to design efficient databases that optimize performance and scalability. You are highly proficient in SQL and have experience in working with different databases like MySQL, PostgreSQL, Oracle, and MongoDB."),
Message('system',"For what does SQL stand?", "example_user"),
Message('system',"SQL stands for Structured Query Language.", "example_assistant")
]
SYSTEM_MSGS = [prompt.message_for_ai() for prompt in SYSTEM_PROMPTS]

history = {} # conversation histories of the users history[userId] = Chat

# Initialize app
app = Flask(__name__)

# Set the callback to validate the bot to be an authorized user
# Bind the Events API route to the existing Flask app by passing the server instance as the last param, or with \`server=app\`.
slack_event_adapter = SlackEventAdapter(SLACK_SIGNING_SECRET, endpoint='/slack/events', server=app)

client = WebClient(token=SLACK_BOT_TOKEN)

# Get the bot id
BOT_ID = client.api_call("auth.test")["user_id"]

@slack_event_adapter.on("app_mention") # subscribe to only the message events that mention your app or bot
def on_slack_message(event_data):
event = event_data["event"] # get the event
channel = event["channel"] # get the channel id
user = event["user"] # get the user id
text = event["text"] # get the body of the message
ts = event["ts"] # get the individual id of the message so we can respond to it in a thread

if BOT_ID in text: #is the bot being addressed?
try:
user_assistant = history[user]
except KeyError: # the user has never messaged the bot
history[user] = Chat() # initialize the chat
user_assistant = history[user]
user_assistant.ask_assistant(SYSTEM_MSGS) # provide the bot with the system prompts
finally:
prompt = "".join(re.split(r"(?:<@[A-Z0-9]*>)", text)) # remove the bot mention (<@BOT_ID>) from the message
user_input = Message("user", prompt)
response = user_assistant.ask_assistant(user_input.message_for_ai()) # prompt the bot
answer = response if type(response)==str else response.content
client.chat_postMessage(channel=channel, thread_ts=ts, text=answer)



app.run(host="0.0.0.0", port=5000)
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
flask==2.3.2
slack-sdk==3.21.3
slackeventsapi==3.0.1
openai==0.27.6
Binary file added screenshots/Slack_Apps_menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/Slack_BasicInformation-Install.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/Slack_BotTokenScopes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/Slack_addAppToChannel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/Slack_airesponse.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/Slack_allow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/Slack_eventVerified.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/Slack_subscribeBotEvents.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ca5e723

Please sign in to comment.