generated from slack-samples/bolt-python-starter-template
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
376 additions
and
305 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 |
---|---|---|
@@ -1,3 +1,3 @@ | ||
[flake8] | ||
max-line-length = 125 | ||
max-line-length = 200 | ||
exclude = .gitignore,venv |
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 |
---|---|---|
|
@@ -35,4 +35,5 @@ tmp.txt | |
.DS_Store | ||
logs/ | ||
*.db | ||
.pytype/ | ||
.pytype/ | ||
.idea/ |
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
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
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
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 |
---|---|---|
@@ -1,15 +1,5 @@ | ||
from listeners import actions | ||
from listeners import commands | ||
from listeners import events | ||
from listeners import messages | ||
from listeners import shortcuts | ||
from listeners import views | ||
|
||
|
||
def register_listeners(app): | ||
actions.register(app) | ||
commands.register(app) | ||
events.register(app) | ||
messages.register(app) | ||
shortcuts.register(app) | ||
views.register(app) |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
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 |
---|---|---|
@@ -1,6 +1,29 @@ | ||
from typing import Dict, Any | ||
|
||
from slack_bolt import App | ||
from .app_home_opened import app_home_opened_callback | ||
from slack_bolt.request.payload_utils import is_event | ||
|
||
from .assistant_thread_started import start_thread_with_suggested_prompts | ||
from .asssistant_thread_context_changed import save_new_thread_context | ||
from .user_message import respond_to_user_message | ||
|
||
|
||
def register(app: App): | ||
app.event("app_home_opened")(app_home_opened_callback) | ||
app.event("assistant_thread_started")(start_thread_with_suggested_prompts) | ||
app.event("assistant_thread_context_changed")(save_new_thread_context) | ||
app.event("message", matchers=[is_user_message_event_in_assistant_thread])(respond_to_user_message) | ||
app.event("message", matchers=[is_message_event_in_assistant_thread])(just_ack) | ||
|
||
|
||
def is_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool: | ||
if is_event(body): | ||
return body["event"]["type"] == "message" and body["event"].get("channel_type") == "im" | ||
return False | ||
|
||
|
||
def is_user_message_event_in_assistant_thread(body: Dict[str, Any]) -> bool: | ||
return is_message_event_in_assistant_thread(body) and body["event"].get("subtype") in (None, "file_share") | ||
|
||
|
||
def just_ack(): | ||
pass |
This file was deleted.
Oops, something went wrong.
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,64 @@ | ||
from typing import List, Dict | ||
from logging import Logger | ||
|
||
from slack_sdk import WebClient | ||
|
||
|
||
def start_thread_with_suggested_prompts( | ||
payload: dict, | ||
client: WebClient, | ||
logger: Logger, | ||
): | ||
thread = payload["assistant_thread"] | ||
channel_id, thread_ts = thread["channel_id"], thread["thread_ts"] | ||
try: | ||
thread_context = thread.get("context") | ||
message_metadata = ( | ||
{ | ||
"event_type": "assistant_thread_context", | ||
"event_payload": thread_context, | ||
} | ||
if bool(thread_context) is True # the dict is not empty | ||
else None | ||
) | ||
client.chat_postMessage( | ||
text="How can I help you?", | ||
channel=channel_id, | ||
thread_ts=thread_ts, | ||
metadata=message_metadata, | ||
) | ||
|
||
prompts: List[Dict[str, str]] = [ | ||
{ | ||
"title": "What does Slack stand for?", | ||
"message": "Slack, a business communication service, was named after an acronym. Can you guess what it stands for?", | ||
}, | ||
{ | ||
"title": "Write a draft announcement", | ||
"message": "Can you write a draft announcement about a new feature my team just released? It must include how impactful it is.", | ||
}, | ||
{ | ||
"title": "Suggest names for my Slack app", | ||
"message": "Can you suggest a few names for my Slack app? The app helps my teammates better organize information and plan priorities and action items.", | ||
}, | ||
] | ||
if message_metadata is not None: | ||
prompts.append( | ||
{ | ||
"title": "Summarize the referred channel", | ||
"message": "Can you generate a brief summary of the referred channel?", | ||
} | ||
) | ||
|
||
client.assistant_threads_setSuggestedPrompts( | ||
channel_id=channel_id, | ||
thread_ts=thread_ts, | ||
prompts=prompts, | ||
) | ||
except Exception as e: | ||
logger.exception(f"Failed to handle an assistant_thread_started event: {e}", e) | ||
client.chat_postMessage( | ||
channel=channel_id, | ||
thread_ts=thread_ts, | ||
text=f":warning: Something went wrong! ({e})", | ||
) |
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,19 @@ | ||
from slack_sdk import WebClient | ||
from slack_bolt import BoltContext | ||
|
||
from .thread_context_store import save_thread_context | ||
|
||
|
||
def save_new_thread_context( | ||
payload: dict, | ||
client: WebClient, | ||
context: BoltContext, | ||
): | ||
thread = payload["assistant_thread"] | ||
save_thread_context( | ||
context=context, | ||
client=client, | ||
channel_id=thread["channel_id"], | ||
thread_ts=thread["thread_ts"], | ||
new_context=thread.get("context"), | ||
) |
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,56 @@ | ||
import os | ||
import re | ||
from typing import List, Dict | ||
|
||
import openai | ||
|
||
DEFAULT_SYSTEM_CONTENT = """ | ||
You're an assistant in a Slack workspace. | ||
Users in the workspace will ask you to help them write something or to think better about a specific topic. | ||
You'll respond to those questions in a professional way. | ||
When you include markdown text, convert them to Slack compatible ones. | ||
When a prompt has Slack's special syntax like <@USER_ID> or <#CHANNEL_ID>, you must keep them as-is in your response. | ||
""" | ||
|
||
|
||
def call_llm(messages_in_thread: List[Dict[str, str]], system_content: str = DEFAULT_SYSTEM_CONTENT) -> str: | ||
openai_client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY")) | ||
messages = [{"role": "system", "content": system_content}] | ||
messages.extend(messages_in_thread) | ||
response = openai_client.chat.completions.create( | ||
model="gpt-4o-mini", | ||
n=1, | ||
messages=messages, | ||
max_tokens=16384, | ||
) | ||
return markdown_to_slack(response.choices[0].message.content) | ||
|
||
|
||
# Conversion from OpenAI markdown to Slack mrkdwn | ||
# See also: https://api.slack.com/reference/surfaces/formatting#basics | ||
def markdown_to_slack(content: str) -> str: | ||
# Split the input string into parts based on code blocks and inline code | ||
parts = re.split(r"(?s)(```.+?```|`[^`\n]+?`)", content) | ||
|
||
# Apply the bold, italic, and strikethrough formatting to text not within code | ||
result = "" | ||
for part in parts: | ||
if part.startswith("```") or part.startswith("`"): | ||
result += part | ||
else: | ||
for o, n in [ | ||
( | ||
r"\*\*\*(?!\s)([^\*\n]+?)(?<!\s)\*\*\*", | ||
r"_*\1*_", | ||
), # ***bold italic*** to *_bold italic_* | ||
( | ||
r"(?<![\*_])\*(?!\s)([^\*\n]+?)(?<!\s)\*(?![\*_])", | ||
r"_\1_", | ||
), # *italic* to _italic_ | ||
(r"\*\*(?!\s)([^\*\n]+?)(?<!\s)\*\*", r"*\1*"), # **bold** to *bold* | ||
(r"__(?!\s)([^_\n]+?)(?<!\s)__", r"*\1*"), # __bold__ to *bold* | ||
(r"~~(?!\s)([^~\n]+?)(?<!\s)~~", r"~\1~"), # ~~strike~~ to ~strike~ | ||
]: | ||
part = re.sub(o, n, part) | ||
result += part | ||
return result |
Oops, something went wrong.