Skip to content

Commit

Permalink
fix slack bot actions in personal channels
Browse files Browse the repository at this point in the history
remove un-necesary args passed to slack bot
handle users with no real_name
  • Loading branch information
devxpy committed Sep 19, 2023
1 parent a1da6a9 commit c0a44aa
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 120 deletions.
41 changes: 41 additions & 0 deletions bots/migrations/0042_alter_message_platform_msg_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Generated by Django 4.2.3 on 2023-09-19 20:51

from django.db import migrations, models


def forwards_func(apps, schema_editor):
Message = apps.get_model("bots", "Message")
db_alias = schema_editor.connection.alias
objects = Message.objects.using(db_alias)
objects.filter(platform_msg_id="").update(platform_msg_id=None)


class Migration(migrations.Migration):
dependencies = [
("bots", "0041_alter_botintegration_slack_create_personal_channels"),
]

operations = [
migrations.AlterField(
model_name="message",
name="platform_msg_id",
field=models.TextField(
blank=True,
default=None,
help_text="The platform's delivered message id",
null=True,
),
),
migrations.RunPython(forwards_func),
migrations.AlterField(
model_name="message",
name="platform_msg_id",
field=models.TextField(
blank=True,
default=None,
help_text="The platform's delivered message id",
null=True,
unique=True,
),
),
]
4 changes: 3 additions & 1 deletion bots/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,9 @@ class Message(models.Model):

platform_msg_id = models.TextField(
blank=True,
default="",
null=True,
default=None,
unique=True,
help_text="The platform's delivered message id",
)

Expand Down
6 changes: 5 additions & 1 deletion daras_ai_v2/bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ def nice_filename(self, mime_type: str) -> str:
ext = mimetypes.guess_extension(mime_type) or ""
return f"{self.platform}_{self.input_type}_from_{self.user_id}_to_{self.bot_id}{ext}"

def _unpack_bot_integration(self, bi: BotIntegration):
def _unpack_bot_integration(self):
bi = self.convo.bot_integration
if bi.saved_run:
self.page_cls = Workflow(bi.saved_run.workflow).page_cls
self.query_params = self.page_cls.clean_query_params(
Expand Down Expand Up @@ -305,6 +306,9 @@ def _handle_interactive_msg(bot: BotInterface):
try:
context_msg = Message.objects.get(platform_msg_id=context_msg_id)
except Message.DoesNotExist as e:
traceback.print_exc()
capture_exception(e)
# send error msg as repsonse
bot.send_msg(text=ERROR_MSG.format(e))
return
if button_id == ButtonIds.feedback_thumbs_up:
Expand Down
4 changes: 2 additions & 2 deletions daras_ai_v2/facebook_bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __init__(self, message: dict, metadata: dict):
bot_integration=bi,
wa_phone_number="+" + self.user_id,
)[0]
self._unpack_bot_integration(bi)
self._unpack_bot_integration()

def get_input_text(self) -> str | None:
try:
Expand Down Expand Up @@ -325,7 +325,7 @@ def __init__(self, object_name: str, messaging: dict):
fb_page_id=self.user_id,
ig_account_id=self.user_id,
)[0]
self._unpack_bot_integration(bi)
self._unpack_bot_integration()
self.bot_id = bi.fb_page_id

self._access_token = bi.fb_page_access_token
Expand Down
154 changes: 75 additions & 79 deletions daras_ai_v2/slack_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,77 +29,73 @@
SLACK_MAX_SIZE = 3000


class SlackMessage(TypedDict):
application_id: str
channel: str
thread_ts: str
text: str
user_id: str
files: list[dict]
actions: list[dict]
msg_id: str
team_id: str


class SlackBot(BotInterface):
_read_msg_id: str | None = None
_file: dict | None = None
platform = Platform.SLACK

_read_rcpt_ts: str | None = None

def __init__(
self,
message: SlackMessage,
*,
message_ts: str,
team_id: str,
channel_id: str,
user_id: str,
text: str = "",
files: list[dict] = None,
actions: list[dict] = None,
):
self.input_message = message # type: ignore
self.platform = Platform.SLACK

self.bot_id = message["channel"]
self.user_id = message["user_id"]
self._msg_ts = message_ts
self._team_id = team_id
self.bot_id = channel_id
self.user_id = user_id
self._text = text

# Try to find an existing conversation, this could either be a personal channel or the main channel the integration was added to
try:
self.convo = Conversation.objects.get(
slack_channel_id=self.bot_id,
slack_user_id=self.user_id,
slack_team_id=message["team_id"],
slack_team_id=self._team_id,
)
except Conversation.DoesNotExist:
# No existing conversation found, this could be a personal channel or the main channel the integration was added to
# find the bot integration for this main channel and use it to create a new conversation
self.convo = Conversation.objects.get_or_create(
slack_channel_id=self.bot_id,
slack_user_id=self.user_id,
slack_team_id=message["team_id"],
slack_team_id=self._team_id,
defaults=dict(
bot_integration=BotIntegration.objects.get(
slack_channel_id=self.bot_id,
slack_team_id=message["team_id"],
slack_team_id=self._team_id,
),
),
)[0]

fetch_missing_convo_metadata(self.convo)
self._access_token = self.convo.bot_integration.slack_access_token

bi = self.convo.bot_integration
self.name = bi.name
self.slack_access_token = bi.slack_access_token
self.read_msg = bi.slack_read_receipt_msg.strip()
self._unpack_bot_integration(bi)

self._thread_ts = message["thread_ts"]

self.input_type = "text"
files = message.get("files", [])
if files:
self._file = files[0] # we only process the first file for now
# Additional check required to access file info - https://api.slack.com/apis/channels-between-orgs#check_file_info
if self._file.get("file_access") == "check_file_info":
self._file = fetch_file_info(self._file["id"], bi.slack_access_token)
self._file = fetch_file_info(self._file["id"], self._access_token)
self.input_type = self._file.get("mimetype", "").split("/")[0] or "unknown"
if message.get("actions"):
else:
self._file = None
self.input_type = "text"

if actions:
self._actions = actions
self.input_type = "interactive"
else:
self._actions = None

self._unpack_bot_integration()

def get_input_text(self) -> str | None:
return self.input_message.get("text")
return self._text

def get_input_audio(self) -> str | None:
if not self._file:
Expand All @@ -112,9 +108,7 @@ def get_input_audio(self) -> str | None:
"audio/" in mime_type or "video/" in mime_type
), f"Unsupported mime type {mime_type} for {url}"
# download file from slack
r = requests.get(
url, headers={"Authorization": f"Bearer {self.slack_access_token}"}
)
r = requests.get(url, headers={"Authorization": f"Bearer {self._access_token}"})
r.raise_for_status()
# convert to wav
data, _ = audio_bytes_to_wav(r.content)
Expand All @@ -128,10 +122,8 @@ def get_input_audio(self) -> str | None:
return audio_url

def get_interactive_msg_info(self) -> tuple[str, str]:
return (
self.input_message["actions"][0]["value"],
self.input_message["msg_id"],
)
button_id = self._actions[0]["value"]
return button_id, self._msg_ts

def send_msg(
self,
Expand All @@ -147,50 +139,50 @@ def send_msg(
if should_translate and self.language and self.language != "en":
text = run_google_translate([text], self.language)[0]

if self._read_msg_id and self._read_msg_id != self._thread_ts:
if self._read_rcpt_ts and self._read_rcpt_ts != self._msg_ts:
delete_msg(
channel=self.bot_id,
thread_ts=self._read_msg_id,
token=self.slack_access_token,
thread_ts=self._read_rcpt_ts,
token=self._access_token,
)
self._read_msg_id = None
self._read_rcpt_ts = None

splits = text_splitter(text, chunk_size=SLACK_MAX_SIZE, length_function=len)
for doc in splits[:-1]:
self._thread_ts = chat_post_message(
self._msg_ts = chat_post_message(
text=doc.text,
channel=self.bot_id,
channel_is_personal=self.convo.slack_channel_is_personal,
thread_ts=self._thread_ts,
username=self.name,
token=self.slack_access_token,
thread_ts=self._msg_ts,
username=self.convo.bot_integration.name,
token=self._access_token,
)
self._thread_ts = chat_post_message(
self._msg_ts = chat_post_message(
text=splits[-1].text,
audio=audio,
video=video,
channel=self.bot_id,
channel_is_personal=self.convo.slack_channel_is_personal,
thread_ts=self._thread_ts,
username=self.name,
token=self.slack_access_token,
thread_ts=self._msg_ts,
username=self.convo.bot_integration.name,
token=self._access_token,
buttons=buttons or [],
)
return self._thread_ts
return self._msg_ts

def mark_read(self):
if not self.read_msg:
text = self.convo.bot_integration.slack_read_receipt_msg.strip()
if not text:
return
text = self.read_msg
if self.language and self.language != "en":
text = run_google_translate([text], self.language)[0]
self._read_msg_id = chat_post_message(
self._read_rcpt_ts = chat_post_message(
text=text,
channel=self.bot_id,
channel_is_personal=self.convo.slack_channel_is_personal,
thread_ts=self._thread_ts,
token=self.slack_access_token,
username=self.name,
thread_ts=self._msg_ts,
token=self._access_token,
username=self.convo.bot_integration.name,
)


Expand Down Expand Up @@ -239,7 +231,7 @@ def fetch_missing_convo_metadata(convo: Conversation):
if not convo.slack_user_name:
user_data = fetch_user_info(convo.slack_user_id, token)
Conversation.objects.filter(id=convo.id).update(
slack_user_name=user_data["real_name"]
slack_user_name=get_slack_user_name(user_data)
)
if not convo.slack_channel_name:
channel_data = fetch_channel_info(convo.slack_channel_id, token)
Expand All @@ -254,6 +246,15 @@ def fetch_missing_convo_metadata(convo: Conversation):
raise


def get_slack_user_name(user: dict) -> str | None:
return (
user.get("real_name")
or user.get("profile", {}).get("display_name")
or user.get("name")
or ""
)


def fetch_channel_info(channel: str, token: str) -> dict[str, typing.Any]:
res = requests.get(
"https://slack.com/api/conversations.info",
Expand Down Expand Up @@ -306,7 +307,7 @@ def create_personal_channel(
if user["is_bot"]:
return None
user_id = user["id"]
user_name = user["real_name"]
user_name = get_slack_user_name(user)
team_id = user["team_id"]
# lookup the personal convo with this bot and user
convo_lookup = dict(
Expand Down Expand Up @@ -550,26 +551,21 @@ def create_file_block(
]


def create_button_block(
buttons: list[
dict[{"type": str, "chat_post_message": dict[{"id": str, "title": str}]}]
]
) -> list[dict]:
def create_button_block(buttons: list[dict]) -> list[dict]:
if not buttons:
return []
elements = []
for button in buttons:
element = {}
element["type"] = "button"
element["text"] = {"type": "plain_text", "text": button["reply"]["title"]}
element["value"] = button["reply"]["id"]
element["action_id"] = "button_" + button["reply"]["id"]
elements.append(element)

return [
{
"type": "actions",
"elements": elements,
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": button["reply"]["title"]},
"value": button["reply"]["id"],
"action_id": "button_" + button["reply"]["id"],
}
for button in buttons
],
}
]

Expand Down
Loading

0 comments on commit c0a44aa

Please sign in to comment.