Source code for operators.slack_to_s3

from airflow.models import BaseOperator
from airflow.utils.decorators import apply_defaults
from airflow.providers.slack.operators.slack import SlackHook
from config import Config
import json

DEFAULT_S3_BUCKET = Config.DATA_LAKE_BUCKET


[docs] class SlackToS3Operator(BaseOperator): """ pull targetted slack history to s3 for channels that ReadyAirflow is added to """ ui_color = "#eb4034" @apply_defaults def __init__( self, slack_channel_id: str, slack_users: list[str], s3_key: str, slack_conn: str = Config.SLACK_DEFAULT_API, s3_bucket_name: str = DEFAULT_S3_BUCKET, *args, **kwargs, ): super().__init__(*args, **kwargs) self.slack_channel_id = slack_channel_id self.slack_users = slack_users self.s3_bucket_name = s3_bucket_name self.s3_key = s3_key self.slack_conn = slack_conn
[docs] def execute(self, context): """ Gets all threads where specified users are mentioned, saves each thread as a separate JSON file to S3. """ import boto3 slack_hook = SlackHook(slack_conn_id=Config.SLACK_DEFAULT_API) threads = {} cursor = None try: while True: params = {"channel": self.slack_channel_id, "limit": 1000} if cursor: params["cursor"] = cursor response = slack_hook.call("conversations.history", json=params) if not response.get("ok"): raise Exception(f"Slack API error: {response.get('error')}") # Look for messages that mention our users for msg in response.get("messages", []): mentions = [ user for user in self.slack_users if f"<@{user}>" in msg.get("text", "") ] if mentions: thread_ts = msg.get("thread_ts") or msg.get("ts") if thread_ts and thread_ts not in threads: # Print debug info print( f"Fetching thread {thread_ts} for channel {self.slack_channel_id}" ) thread_params = { "channel": self.slack_channel_id, "ts": thread_ts, } thread_response = slack_hook.call( "conversations.replies", params=thread_params ) if thread_response.get("ok"): thread_messages = thread_response.get("messages", []) threads[thread_ts] = { "parent_message": ( thread_messages[0] if thread_messages else msg ), "replies": ( thread_messages[1:] if thread_messages else [] ), "mentioned_users": mentions, } else: print( f"Error fetching thread: {thread_response.get('error')}" ) cursor = response.get("response_metadata", {}).get("next_cursor") if not cursor: break if not threads: print( f"No threads found mentioning users {self.slack_users} in channel {self.slack_channel_id}" ) return # Initialize S3 client s3_client = boto3.client("s3") # Format all threads into a list thread_list = [ { "thread_ts": thread_ts, "parent": { "text": thread_data["parent_message"].get("text", ""), "sender": thread_data["parent_message"].get("user", ""), "ts": thread_data["parent_message"].get("ts", ""), }, "replies": [ { "text": reply.get("text", ""), "sender": reply.get("user", ""), "ts": reply.get("ts", ""), } for reply in thread_data["replies"] ], } for thread_ts, thread_data in threads.items() ] # Upload single JSON file with all threads s3_client.put_object( Bucket=self.s3_bucket_name, Key=f"{self.s3_key.rstrip('/')}/threads.json", Body=json.dumps(thread_list, indent=2), ContentType="application/json", ) print(f"Successfully uploaded {len(thread_list)} threads to S3") except Exception as e: raise Exception(f"Error transferring threads to S3: {str(e)}")