def custom_slack_on_pipeline_failure( context: PipelineFailureSensorContext): base_url = "http://localhost:3000" slack_client = WebClient( token=os.environ["SLACK_DAGSTER_ETL_BOT_TOKEN"]) run_page_url = f"{base_url}/instance/runs/{context.pipeline_run.run_id}" channel = "#yuhan-test" message = "\n".join([ f'Pipeline "{context.pipeline_run.pipeline_name}" failed.', f"error: {context.failure_event.message}", f"mode: {context.pipeline_run.mode}", f"run_page_url: {run_page_url}", ]) slack_client.chat_postMessage( channel=channel, blocks=[{ "type": "section", "text": { "type": "mrkdwn", "text": message } }], )
def __init__(self): _settings, _ = import_settings() slack_api_token = _settings.get('SLACK_API_TOKEN', None) http_proxy = _settings.get('HTTP_PROXY', None) self.rtm_client = RTMClient(token=slack_api_token, proxy=http_proxy) self.web_client = WebClient(token=slack_api_token, proxy=http_proxy) self._bot_info = {} self._users = {} self._channels = {}
def __init__(self, token: str = None, default_channel: str = None): # Rely on system token if one wasn't passed if not token: token = Slack_Settings.APP_SLACK_TOKEN # Create internal client to serve as an adapter for self._client = WebClient(token=token) # Set the default channel self.default_channel = Slack_Settings.APP_SLACK_LOGGING_CHANNEL if not default_channel else default_channel
def upload_images(token: str, channel: str, filepath: str, description: str): client = WebClient(token=token) with _open(filepath, mode="rb") as f: if isinstance(f, io.BytesIO): image_type = imghdr.what(f) else: raise ValueError(f"'{filepath}' is not supported format") if image_type not in SUPPORTED_IMAGES: raise ValueError(f"'{filepath}' is not supported format") client.files_upload(file=f, channels=channel, initial_comment=description)
def post_message_to_slack(self, text: str, blocks: Any = None, use_conversation_threads: bool = True) -> Any: """ Posts a message to Slack :param text: message to post. Can be markdown. :param blocks: (Optional) blocks to post :param use_conversation_threads: whether to send messages as reply to first message :return: response from Slack """ if not text: return None if (self.wait_till_datetime is not None and self.wait_till_datetime > datetime.utcnow()): return None try: web = WebClient(token=self.slack_token) response: SlackResponse = web.chat_postMessage( text=text, channel=self.slack_channel, thread_ts=self.slack_thread, icon_url=self.slack_icon_url, username=self.slack_user_name, blocks=json.dumps(blocks) if blocks else None, ) if not self.slack_thread and use_conversation_threads: if (response.status_code == 200 and response.data and "ts" in response.data): self.slack_thread = response.data["ts"] # type: ignore self.wait_till_datetime = None # clear this on success return response except SlackApiError as e: logger.warning(f"Slack API Error: {e.response['error']}") if e.response.status_code == 429: # The `Retry-After` header will tell you how long to wait before retrying delay_in_seconds: int = int(e.response.headers["Retry-After"]) logger.warning( f"Rate limited. Retrying in {delay_in_seconds} seconds") self.wait_till_datetime = datetime.utcnow() + timedelta( seconds=delay_in_seconds) return None except Exception as e: logger.warning(f"Unknown error calling Slack API: {e}") return None
class KolgaSlackPlugin(PluginBase): name = "slack" verbose_name = "Kolga Slack Plugin" version = 0.1 # Environment variables SLACK_TOKEN: str SLACK_CHANNEL: str def __init__(self, env: Env) -> None: self.required_variables = [("SLACK_TOKEN", env.str), ("SLACK_CHANNEL", env.str)] self.configure(env) self.client = WebClient(self.SLACK_TOKEN) @hookimpl def project_deployment_complete( self, exception: Optional[Exception], namespace: str, project: "Project", track: str, ) -> Optional[bool]: if not self.configured or exception is not None: return None deployment_message = new_environment_message(track, project) try: self.client.chat_postMessage( channel=self.SLACK_CHANNEL, blocks=deployment_message, username="******", icon_emoji=":rocket:", ) except SlackApiError as e: logger.error( message=f"Could not send slack message -> {e.response['error']}" ) return True
def file_shared(body, client: WebClient, context, logger): context.ack() file_id = body["event"]["file_id"] file_info = client.files_info(file=file_id) file_type = file_info["file"]["filetype"] if file_type not in ["jpg", "jpeg", "png"]: return url = file_info["file"]["url_private"] image = im.open_url(url, cfg.token) sides = ["left", "right"] uploaded_files = {} for side in sides: mirrored = im.mirror(image, side=side) mirrored.save(f"/tmp/{file_id}-{side}.{file_type}") with open(f"/tmp/{file_id}-{side}.{file_type}", "rb") as file_content: result = client.files_upload(file=file_content) uploaded_files[side] = result["file"]["permalink"] msg = f"<{uploaded_files['right']}| ><{uploaded_files['left']}| >" channel_name = channel_name_from_id(body["event"]["channel_id"]) channel = "sapsik" if channel_name == "bot_testing": channel = channel_name sent = client.chat_postMessage(text=msg, channel=channel) for side in sides: try: client.reactions_add(channel=sent["channel"], timestamp=sent["ts"], name=f"point_{side}") except SlackApiError: pass
class Slack: ''' Client for sending Slack messages ''' def __init__(self, token: str = None, default_channel: str = None): # Rely on system token if one wasn't passed if not token: token = Slack_Settings.APP_SLACK_TOKEN # Create internal client to serve as an adapter for self._client = WebClient(token=token) # Set the default channel self.default_channel = Slack_Settings.APP_SLACK_LOGGING_CHANNEL if not default_channel else default_channel def send_message(self, message: str, channel: str = None, thread_id: str = None) -> str: ''' Sends a message to the passed Slack channel and returns the thread ID. Posts as a reply if `thread_id` is passed''' # Fall back on the default channel if a channel name isn't immediately passed if not channel: channel = self.default_channel # Ensure the channel name starts with '#' if channel[0] != '#': channel = f'#{channel}' try: # Send the message to the channel response = self._client.api_call( api_method='chat.postMessage', json={ 'channel': channel, 'text': message, **({} if not thread_id else { 'thread_ts': thread_id }) }, ) # Log errors if not response or 'error' in response: capture_exception( Exception(f"Slack logging error: {response['error']}")) return None # Return the thread ID return response.get('ts') # Catch and log any exceptions thrown when logging except Exception as e: capture_exception(e) def log_api_exception(self, error: API_Error, endpoint: str, payload: dict, channel: str = None, thread_title: str = None): ''' Log an API level exception to Slack with supplementary information ''' # Generate top level message for the error thread if one was not passed if not thread_title: emoji = ':broken_wifi:' if str( error.code)[0] != '4' else ':large_yellow_circle:' thread_title = f'{emoji} API Error: `{error.code}`\n:api: Endpoint: `{endpoint}`\n:speech_balloon: Error: `{error.message}`' # Post the message and get the ID of the thread thread_id = self.send_message(thread_title, channel) # Only post replies if the last post succeeded if thread_id: # Prettify paylod data for Slack payload_reply = f':package: Payload:\n```{pformat(payload)}```' # Post the payload as a reply replied = self.send_message(payload_reply, channel, thread_id) # Only post replies if the last post succeeded if replied: # Prettify traceback data for Slack traceback_reply = f':mag: Traceback:\n```{"".join(format_tb(error.__traceback__))}```' # Post the traceback as a reply self.send_message(traceback_reply, channel, thread_id) def log_exception(self, error: Exception, channel: str = None, thread_title: str = None, endpoint: str = None, payload: dict = None): ''' Log an exception to Slack with supplementary information ''' # Generate top level message for the error thread if one was not passed if not thread_title: thread_title = f':x: Exception: `{error.args[0]}`' if endpoint: thread_title += f'\n:api: Endpoint: `{endpoint}`' # Post the message and get the ID of the thread thread_id = self.send_message(thread_title, channel) # Only post replies if the last post succeeded if thread_id and payload: # Prettify paylod data for Slack payload_reply = f':package: Payload:\n```{pformat(payload)}```' # Post the payload as a reply self.send_message(payload_reply, channel, thread_id) if thread_id: # Prettify traceback data for Slack traceback_reply = f':mag: Traceback:\n```{"".join(format_tb(error.__traceback__))}```' # Post the traceback as a reply self.send_message(traceback_reply, channel, thread_id)
def __init__(self, env: Env) -> None: self.required_variables = [("SLACK_TOKEN", env.str), ("SLACK_CHANNEL", env.str)] self.configure(env) self.client = WebClient(self.SLACK_TOKEN)
class LowLevelSlackClient(metaclass=Singleton): def __init__(self): _settings, _ = import_settings() slack_api_token = _settings.get('SLACK_API_TOKEN', None) http_proxy = _settings.get('HTTP_PROXY', None) self.rtm_client = RTMClient(token=slack_api_token, proxy=http_proxy) self.web_client = WebClient(token=slack_api_token, proxy=http_proxy) self._bot_info = {} self._users = {} self._channels = {} @staticmethod def get_instance() -> 'LowLevelSlackClient': return LowLevelSlackClient() def _register_user(self, user_response): user = User.from_api_response(user_response) self._users[user.id] = user return user def _register_channel(self, channel_response): channel = Channel.from_api_response(channel_response) self._channels[channel.id] = channel return channel def ping(self): # Ugly hack because some parts of slackclient > 2.0 are async-only (like the ping function) # and Slack Machine isn't async yet loop = asyncio.new_event_loop() result = self.rtm_client.ping() loop.run_until_complete(result) def _on_open(self, **payload): # Set bot info self._bot_info = payload['data']['self'] # Build user cache all_users = call_paginated_endpoint(self.web_client.users_list, 'members') for u in all_users: self._register_user(u) logger.debug("Number of users found: %s" % len(self._users)) logger.debug("Users: %s" % ", ".join([ f"{u.profile.display_name}|{u.profile.real_name}" for u in self._users.values() ])) # Build channel cache all_channels = call_paginated_endpoint( self.web_client.conversations_list, 'channels', types='public_channel,private_channel,mpim,im') for c in all_channels: self._register_channel(c) logger.debug("Number of channels found: %s" % len(self._channels)) logger.debug("Channels: %s" % ", ".join([c.identifier for c in self._channels.values()])) puts("Final initialization of plugins...") for instance, class_name in self._plugins: instance.init_final() show_valid(class_name) def _on_team_join(self, **payload): user = self._register_user(payload['data']['user']) logger.debug("User joined team: %s" % user) def _on_user_change(self, **payload): user = self._register_user(payload['data']['user']) logger.debug("User changed: %s" % user) def _on_channel_created(self, **payload): channel_resp = self.web_client.conversations_info( channel=payload['data']['channel']['id']) channel = self._register_channel(channel_resp['channel']) logger.debug("Channel created: %s" % channel) def _on_channel_updated(self, **payload): data = payload['data'] if isinstance(data['channel'], dict): channel_id = data['channel']['id'] else: channel_id = data['channel'] channel_resp = self.web_client.conversations_info(channel=channel_id) channel = self._register_channel(channel_resp['channel']) logger.debug("Channel updated: %s" % channel) def _on_channel_deleted(self, **payload): channel = self._channels[payload['data']['channel']] del self._channels[payload['data']['channel']] logger.debug("Channel %s deleted" % channel.name) @property def bot_info(self) -> Dict[str, str]: return self._bot_info def start(self, plugins): self._plugins = plugins RTMClient.on(event='open', callback=self._on_open) RTMClient.on(event='team_join', callback=self._on_team_join) RTMClient.on(event='channel_created', callback=self._on_channel_created) RTMClient.on(event='group_joined', callback=self._on_channel_created) RTMClient.on(event='mpim_joined', callback=self._on_channel_created) RTMClient.on(event='im_created', callback=self._on_channel_created) RTMClient.on(event='channel_deleted', callback=self._on_channel_deleted) RTMClient.on(event='im_close', callback=self._on_channel_deleted) RTMClient.on(event='channel_rename', callback=self._on_channel_updated) RTMClient.on(event='channel_archive', callback=self._on_channel_updated) RTMClient.on(event='channel_unarchive', callback=self._on_channel_updated) RTMClient.on(event='user_change', callback=self._on_user_change) self.rtm_client.start() @property def users(self) -> Dict[str, User]: return self._users @property def channels(self) -> Dict[str, Channel]: return self._channels
load_dotenv(find_dotenv()) # Setup the Algolia client algolia = SearchClient.create( app_id=os.getenv('ALGOLIA_APP_ID'), api_key=os.getenv('ALGOLIA_API_KEY') ) employees_index = algolia.init_index(os.getenv('ALGOLIA_INDEX_NAME')) # Setup the Google Cloud client # https://cloud.google.com/vision/docs/libraries gc_vision = vision.ImageAnnotatorClient() # Setup the Slack client slack = WebClient(token=os.getenv('SLACK_TOKEN')) # Setup Flask STATIC_DIR = str( os.path.abspath(os.path.join( __file__, '..', os.getenv('STATIC_DIR') )) ) app = Flask( __name__, static_folder=STATIC_DIR, static_url_path="", template_folder=STATIC_DIR )
import os import sys import json from datetime import datetime from slack_sdk.web.client import WebClient CHANNEL = "#alerts-universe" SLACK_TOKEN = os.environ.get("SLACK_BOT_TOKEN", "ENV VAR not available!") DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" client = WebClient(SLACK_TOKEN) github_context = json.loads(sys.argv[1]) event = github_context['event'] pr_title = event['pull_request']["title"] pr_link = event['pull_request']["patch_url"].replace(".patch", "") pr_author_url = event['sender']["html_url"] pr_author_name = pr_author_url.rsplit('/')[-1] pr_created_at_dt = datetime.strptime(event['pull_request']["created_at"], DATETIME_FORMAT) pr_created_at = pr_created_at_dt.strftime("%c") pr_updated_at_dt = datetime.strptime(event['pull_request']["updated_at"], DATETIME_FORMAT) pr_updated_at = pr_updated_at_dt.strftime("%c") blocks = [{ "type": "section", "text": { "type": "mrkdwn", "text": "📣 New spaCy Universe Project Alert ✨"