Пример #1
0
def oauth_callback():
    # Retrieve the auth code and state from the request params
    if "code" in request.args:
        # Verify the state parameter
        if state_store.consume(request.args["state"]):
            client = WebClient()  # no prepared token needed for this
            # Complete the installation by calling oauth.v2.access API method
            oauth_response = client.oauth_v2_access(
                client_id=settings.slack_client_id,
                client_secret=settings.slack_client_secret,
                redirect_uri=settings.auth_redir_url,
                code=request.args["code"])

            installer = oauth_response.get("authed_user", {})
            user_token = installer.get("access_token")

            if user_token is None:
                return make_response(
                    f"Failure getting user token :( {str(oauth_response)}",
                    500)

            settings.slack_token = user_token

            shutdown_server()
            return "Thanks for installing this app!"
        else:
            return make_response(
                f"Try the installation again (the state value is already expired)",
                400)

    error = request.args["error"] if "error" in request.args else ""
    return make_response(
        f"Something is wrong with the installation (error: {error})", 400)
Пример #2
0
class SlackHandler(StreamHandler):
    """Slack log handler Class

    Inherits:
        logging.StreamHandler: Base log StreamHandler class.
    """
    def __init__(self,
                 channel: str,
                 slack_bot_token: str = None,
                 username: str = "Gytrash"):
        """Initialize the stream handler with some specifics for slack.

        Args:
            channel (str): Slack channel to publish logs
            slack_bot_token (str, optional): Slack bot token to use the slack published app. Defaults to None.
            username (str, optional): Username of the Slack bot. Defaults to "Gytrash".
        """
        StreamHandler.__init__(self)
        # Initialize a Web API client
        if slack_bot_token:
            self.slack_web_client = WebClient(token=slack_bot_token)
        else:
            self.slack_web_client = WebClient(
                token=os.environ["SLACK_BOT_TOKEN"])
        self.channel = channel
        self.username = username

    def _send_log(self, message: dict):
        """Posts the formatted message to slack via the web client.

        Args:
            message (dict): Slack message dictionary. Follows the blocks API.
        """
        self.slack_web_client.chat_postMessage(**message)

    def emit(self, message: "logging.LogRecord"):
        """Emits a message from the handler.

        Args:
            message (logging.LogRecord): Log record from the stream.
        """
        assert isinstance(message, logging.LogRecord)

        slack_message = self.format(message)

        # List of LogRecord attributes expected when reading the
        # documentation of the logging module:
        expected_attributes = (
            "args,asctime,created,exc_info,filename,funcName,levelname,"
            "levelno,lineno,module,msecs,message,msg,name,pathname,"
            "process,processName,relativeCreated,stack_info,thread,threadName")
        for ea in expected_attributes.split(","):
            if not hasattr(message, ea):
                print("UNEXPECTED: LogRecord does not have the '{}' field!".
                      format(ea))

        slack_message["channel"] = self.channel
        slack_message["username"] = self.username

        self._send_log(slack_message)
Пример #3
0
 def __init__(self, ack: Ack, action: dict, client: WebClient, context,
              logger: logging.Logger, payload: dict, request: BoltRequest):
     ack()
     container = request.body.get('container')
     view: dict = request.body.get(container.get('type'))
     plugin_help_content: list = []
     plugins = payload['plugins']
     if plugins is not None:
         for p in plugins:
             plugin: Plugin = p
             if plugin.callback.__doc__ is not None:
                 label = plugin.keyword if type(
                     plugin.keyword) == type("") else plugin.file_name
                 plugin_help_content.append({
                     "type": "section",
                     "text": {
                         "type": "mrkdwn",
                         "text": f"*{label}:* {plugin.callback.__doc__}"
                     }
                 })
     title: dict = view.get('title')
     title.update(text="Plugin Info")
     close_button = view.get('close')
     close_button.update(text="Go Back")
     client.views_push(trigger_id=request.body.get('trigger_id'),
                       view={
                           "type": view.get('type'),
                           "title": title,
                           "close": close_button,
                           "blocks": plugin_help_content
                       })
 def test_blocks_without_text_arg(self):
     client = WebClient(
         base_url="http://localhost:8888", token="xoxb-api_test", team_id="T111"
     )
     with self.assertWarns(UserWarning):
         resp = client.chat_postMessage(channel="C111", blocks=[])
     self.assertTrue(resp["ok"])
Пример #5
0
    def test_issue_605(self):
        self.text = "This message was sent to verify issue #605"
        self.called = False

        @RTMClient.run_on(event="message")
        def process_messages(**payload):
            self.logger.info(payload)
            self.called = True

        def connect():
            self.logger.debug("Starting RTM Client...")
            self.rtm_client.start()

        t = threading.Thread(target=connect)
        t.daemon = True
        try:
            t.start()
            self.assertFalse(self.called)

            time.sleep(3)

            self.web_client = WebClient(
                token=self.bot_token,
                run_async=False,
            )
            new_message = self.web_client.chat_postMessage(
                channel=self.channel_id, text=self.text)
            self.assertFalse("error" in new_message)

            time.sleep(5)
            self.assertTrue(self.called)
        finally:
            t.join(0.3)
Пример #6
0
 def test_remote_disconnected(self):
     client = WebClient(
         base_url="http://localhost:8889",
         token="xoxb-remote_disconnected",
         team_id="T111",
     )
     client.auth_test()
 def test_attachments_without_fallback(self):
     client = WebClient(
         base_url="http://localhost:8888", token="xoxb-api_test", team_id="T111"
     )
     with self.assertWarns(UserWarning):
         resp = client.chat_postMessage(channel="C111", attachments=[{}])
     self.assertTrue(resp["ok"])
Пример #8
0
    def setUp(self):
        self.logger = logging.getLogger(__name__)
        self.org_admin_token = os.environ[
            SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN]
        self.sync_client: WebClient = WebClient(token=self.org_admin_token)
        self.async_client: AsyncWebClient = AsyncWebClient(
            token=self.org_admin_token)

        self.team_id = os.environ[SLACK_SDK_TEST_GRID_TEAM_ID]
        self.idp_group_id = os.environ[SLACK_SDK_TEST_GRID_IDP_USERGROUP_ID]

        if not hasattr(self, "channel_id"):
            team_admin_token = os.environ[
                SLACK_SDK_TEST_GRID_WORKSPACE_ADMIN_USER_TOKEN]
            client = WebClient(token=team_admin_token)
            # Only fetching private channels since admin.conversations.restrictAccess methods do not work for public channels
            convs = client.conversations_list(exclude_archived=True,
                                              limit=100,
                                              types="private_channel")
            self.channel_id = next(
                (c["id"] for c in convs["channels"] if c["name"] != "general"),
                None)
            if self.channel_id is None:
                millis = int(round(time.time() * 1000))
                channel_name = f"private-test-channel-{millis}"
                self.channel_id = client.conversations_create(
                    name=channel_name,
                    is_private=True,
                )["channel"]["id"]
Пример #9
0
def oauth_callback():
    # Retrieve the auth code and state from the request params
    if "code" in request.args:
        state = request.args["state"]
        if state_store.consume(state):
            code = request.args["code"]
            client = WebClient()  # no prepared token needed for this app
            oauth_response = client.oauth_v2_access(
                client_id=client_id, client_secret=client_secret, code=code)
            logger.info(f"oauth.v2.access response: {oauth_response}")

            installed_enterprise = oauth_response.get("enterprise", {})
            is_enterprise_install = oauth_response.get("is_enterprise_install")
            installed_team = oauth_response.get("team", {})
            installer = oauth_response.get("authed_user", {})
            incoming_webhook = oauth_response.get("incoming_webhook", {})

            bot_token = oauth_response.get("access_token")
            # NOTE: oauth.v2.access doesn't include bot_id in response
            bot_id = None
            enterprise_url = None
            if bot_token is not None:
                auth_test = client.auth_test(token=bot_token)
                bot_id = auth_test["bot_id"]
                if is_enterprise_install is True:
                    enterprise_url = auth_test.get("url")

            installation = Installation(
                app_id=oauth_response.get("app_id"),
                enterprise_id=installed_enterprise.get("id"),
                enterprise_name=installed_enterprise.get("name"),
                enterprise_url=enterprise_url,
                team_id=installed_team.get("id"),
                team_name=installed_team.get("name"),
                bot_token=bot_token,
                bot_id=bot_id,
                bot_user_id=oauth_response.get("bot_user_id"),
                bot_scopes=oauth_response.get(
                    "scope"),  # comma-separated string
                user_id=installer.get("id"),
                user_token=installer.get("access_token"),
                user_scopes=installer.get("scope"),  # comma-separated string
                incoming_webhook_url=incoming_webhook.get("url"),
                incoming_webhook_channel=incoming_webhook.get("channel"),
                incoming_webhook_channel_id=incoming_webhook.get("channel_id"),
                incoming_webhook_configuration_url=incoming_webhook.get(
                    "configuration_url"),
                is_enterprise_install=is_enterprise_install,
                token_type=oauth_response.get("token_type"),
            )
            installation_store.save(installation)
            return "Thanks for installing this app!"
        else:
            return make_response(
                f"Try the installation again (the state value is already expired)",
                400)

    error = request.args["error"] if "error" in request.args else ""
    return make_response(
        f"Something is wrong with the installation (error: {error})", 400)
 def test_blocks_as_deserialzed_json_without_text_arg(self):
     client = WebClient(
         base_url="http://localhost:8888", token="xoxb-api_test", team_id="T111"
     )
     # this generates a warning because "text" is missing
     with self.assertWarns(UserWarning):
         resp = client.chat_postMessage(channel="C111", attachments=json.dumps([]))
     self.assertTrue(resp["ok"])
Пример #11
0
    def _send_to_listeners(self, message):
        channels = SlackBotChannel.objects.select_related('bot').filter(
            bot__is_active=True)

        for channel in channels:
            client = WebClient(token=channel.bot.bot_token)
            client.chat_postMessage(channel=channel.channel_id,
                                    attachments=message)
 def test_rate_limited(self):
     client = WebClient(
         base_url="http://localhost:8888",
         token="xoxb-rate_limited_only_once",
         team_id="T111",
     )
     client.retry_handlers.append(rate_limit_error_retry_handler)
     client.auth_test()
Пример #13
0
    def test_receiving_all_messages(self):
        self.rtm_client = RTMClient(token=self.bot_token,
                                    loop=asyncio.new_event_loop())
        self.web_client = WebClient(token=self.bot_token)

        self.call_count = 0

        @RTMClient.run_on(event="message")
        def send_reply(**payload):
            self.logger.debug(payload)
            web_client, data = payload["web_client"], payload["data"]
            web_client.reactions_add(channel=data["channel"],
                                     timestamp=data["ts"],
                                     name="eyes")
            self.call_count += 1

        def connect():
            self.logger.debug("Starting RTM Client...")
            self.rtm_client.start()

        rtm = threading.Thread(target=connect)
        rtm.daemon = True

        rtm.start()
        time.sleep(3)

        total_num = 10

        sender_completion = []

        def sent_bulk_message():
            for i in range(total_num):
                text = f"Sent by <https://slack.dev/python-slackclient/|python-slackclient>! ({i})"
                self.web_client.chat_postMessage(channel="#random", text=text)
                time.sleep(0.1)
            sender_completion.append(True)

        num_of_senders = 3
        senders = []
        for sender_num in range(num_of_senders):
            sender = threading.Thread(target=sent_bulk_message)
            sender.daemon = True
            sender.start()
            senders.append(sender)

        while len(sender_completion) < num_of_senders:
            time.sleep(1)

        expected_call_count = total_num * num_of_senders
        wait_seconds = 0
        max_wait = 20
        while self.call_count < expected_call_count and wait_seconds < max_wait:
            time.sleep(1)
            wait_seconds += 1

        self.assertEqual(total_num * num_of_senders, self.call_count,
                         "The RTM handler failed")
Пример #14
0
 def test_if_it_uses_custom_logger(self):
     logger = CustomLogger("test-logger")
     client = WebClient(
         base_url="http://localhost:8888",
         token="xoxb-api_test",
         logger=logger,
     )
     client.chat_postMessage(channel="C111", text="hello")
     self.assertTrue(logger.called)
Пример #15
0
def per_request():
    try:
        client = WebClient(token=os.environ["SLACK_BOT_TOKEN"], run_async=False)
        response = client.chat_postMessage(
            channel="#random", text="You used a new WebClient for posting this message!"
        )
        return str(response)
    except SlackApiError as e:
        return make_response(str(e), 400)
 def test_blocks_as_deserialized_json_with_text_arg(self):
     client = WebClient(
         base_url="http://localhost:8888", token="xoxb-api_test", team_id="T111"
     )
     # this DOESN'T warn because the "text" arg is present
     resp = client.chat_postMessage(
         channel="C111", text="test", blocks=json.dumps([])
     )
     self.assertTrue(resp["ok"])
 def test_html_response_body_issue_829(self):
     client = WebClient(base_url="http://localhost:8888")
     try:
         client.users_list(token="xoxb-error_html_response")
         self.fail("SlackApiError expected here")
     except err.SlackApiError as e:
         self.assertTrue(
             str(e).startswith("Received a response in a non-JSON format: "), e
         )
 def test_attachments_without_fallback_with_text_arg(self):
     client = WebClient(
         base_url="http://localhost:8888", token="xoxb-api_test", team_id="T111"
     )
     # this warns because each attachment should have its own fallback, even with "text"
     with self.assertWarns(UserWarning):
         resp = client.chat_postMessage(
             channel="C111", text="test", attachments=[{}]
         )
     self.assertTrue(resp["ok"])
Пример #19
0
def oauth_callback():
    # Retrieve the auth code and state from the request params
    if "code" in request.args:
        state = request.args["state"]
        if state_store.consume(state):
            code = request.args["code"]
            try:
                token_response = WebClient().openid_connect_token(
                    client_id=client_id,
                    client_secret=client_secret,
                    code=code)
                logger.info(f"openid.connect.token response: {token_response}")
                id_token = token_response.get("id_token")
                claims = jwt.decode(id_token,
                                    options={"verify_signature": False},
                                    algorithms=["RS256"])
                logger.info(f"claims (decoded id_token): {claims}")

                user_token = token_response.get("access_token")
                user_info_response = WebClient(
                    token=user_token).openid_connect_userInfo()
                logger.info(
                    f"openid.connect.userInfo response: {user_info_response}")
                return f"""
            <html>
            <head>
            <style>
            body h2 {{
              padding: 10px 15px;
              font-family: verdana;
              text-align: center;
            }}
            </style>
            </head>
            <body>
            <h2>OpenID Connect Claims</h2>
            <pre>{json.dumps(claims, indent=2)}</pre>
            <h2>openid.connect.userInfo response</h2>
            <pre>{json.dumps(user_info_response.data, indent=2)}</pre>
            </body>
            </html>
            """

            except Exception:
                logger.exception(
                    "Failed to perform openid.connect.token API call")
                return redirect_page_renderer.render_failure_page(
                    "Failed to perform openid.connect.token API call")
        else:
            return redirect_page_renderer.render_failure_page(
                "The state value is already expired")

    error = request.args["error"] if "error" in request.args else ""
    return make_response(
        f"Something is wrong with the installation (error: {error})", 400)
Пример #20
0
    def test_ignores_unmonitored_channels(self):
        client = WebClient()
        client.chat_postMessage = MagicMock()

        subject = LoggingBot(client, [], ["MONITORED"], [], [], [])
        handled = subject.handle_message("NOT_MONITORED",
                                         "user",
                                         "text",
                                         bot_profile="bot_profile")
        self.assertFalse(handled)
        client.chat_postMessage.assert_not_called()
Пример #21
0
 def test_triggers_replies_in_a_thread(self):
     client = WebClient()
     client.chat_postMessage = MagicMock()
     user = "******"
     channel = "channel"
     trigger_word = "help me"
     subject = LoggingBot(client, [trigger_word], [channel], [], [], [])
     handled = subject.handle_message(channel, user, trigger_word, ts="ts")
     self.assertTrue(handled)
     self.assertNotEqual(
         "", client.chat_postMessage.call_args.kwargs.get("thread_ts", ""))
 def test_attachments_as_deserialzed_json_without_text_arg(self):
     client = WebClient(
         base_url="http://localhost:8888", token="xoxb-api_test", team_id="T111"
     )
     # this still generates a warning because "text" is missing. The attachment has already
     # been deserialized, which isn't explicitly prohibited in the docs (but isn't recommended)
     with self.assertWarns(UserWarning):
         resp = client.chat_postMessage(
             channel="C111", attachments=json.dumps([{"fallback": "test"}])
         )
     self.assertTrue(resp["ok"])
Пример #23
0
 def test_html_response_body_issue_829(self):
     client = WebClient(base_url="http://localhost:8888")
     try:
         client.users_list(token="xoxb-error_html_response")
         self.fail("SlackApiError expected here")
     except err.SlackApiError as e:
         self.assertTrue(
             str(e).startswith(
                 "Failed to parse the response body: Expecting value: "),
             e,
         )
 def test_ratelimited(self):
     client = WebClient(
         base_url="http://localhost:8888",
         token="xoxp-ratelimited",
         team_id="T111",
     )
     client.retry_handlers.append(RateLimitErrorRetryHandler())
     try:
         client.auth_test()
         self.fail("An exception is expected")
     except SlackApiError as e:
         # Just running retries; no assertions for call count so far
         self.assertEqual(429, e.response.status_code)
Пример #25
0
 def test_triggers_does_not_trigger_in_threads(self):
     client = WebClient()
     client.chat_postMessage = MagicMock()
     user = "******"
     channel = "channel"
     trigger_word = "help me"
     subject = LoggingBot(client, [trigger_word], [channel], [], [], [])
     handled = subject.handle_message(channel,
                                      user,
                                      trigger_word,
                                      thread_ts="ts")
     self.assertFalse(handled)
     client.chat_postMessage.assert_not_called()
Пример #26
0
    def test_ignores_bot_messages(self):
        client = WebClient()
        client.chat_postMessage = MagicMock()
        logging.debug = MagicMock()

        subject = LoggingBot(client, [], [], [], [], [])
        handled = subject.handle_message("channel",
                                         "user",
                                         "text",
                                         bot_profile="bot_profile")
        self.assertFalse(handled)
        client.chat_postMessage.assert_not_called()
        logging.debug.assert_called_with(AnyStringWith("bot"))
Пример #27
0
    def test_webhook(self):
        url = os.environ[SLACK_SDK_TEST_INCOMING_WEBHOOK_URL]
        webhook = WebhookClient(url)
        response = webhook.send(text="Hello!")
        self.assertEqual(200, response.status_code)
        self.assertEqual("ok", response.body)

        token = os.environ[SLACK_SDK_TEST_BOT_TOKEN]
        client = WebClient(token=token)
        history = client.conversations_history(channel=self.channel_id,
                                               limit=1)
        self.assertIsNotNone(history)
        actual_text = history["messages"][0]["text"]
        self.assertEqual("Hello!", actual_text)
Пример #28
0
    def test_ignores_admin_messages(self):
        client = WebClient()
        client.chat_postMessage = MagicMock()
        logging.debug = MagicMock()

        user = "******"
        trigger_word = "triggered!"
        channel = "channel_8"

        subject = LoggingBot(client, [trigger_word], [channel], [], [user], [])
        handled = subject.handle_message(channel, user, "text")
        self.assertFalse(handled)
        client.chat_postMessage.assert_not_called()
        logging.debug.assert_called_with(AnyStringWith("admin"))
Пример #29
0
 def setUp(self):
     if not hasattr(self, "channel_id"):
         token = os.environ[SLACK_SDK_TEST_BOT_TOKEN]
         channel_name = os.environ[
             SLACK_SDK_TEST_INCOMING_WEBHOOK_CHANNEL_NAME].replace("#", "")
         client = WebClient(token=token)
         self.channel_id = None
         for resp in client.conversations_list(limit=1000):
             for c in resp["channels"]:
                 if c["name"] == channel_name:
                     self.channel_id = c["id"]
                     break
             if self.channel_id is not None:
                 break
Пример #30
0
    def test_html_response_body_issue_829(self):
        retry_handlers = [ServerErrorRetryHandler(max_retry_count=2)]
        client = WebClient(
            base_url="http://localhost:8888",
            retry_handlers=retry_handlers,
        )
        try:
            client.users_list(token="xoxb-error_html_response")
            self.fail("SlackApiError expected here")
        except err.SlackApiError as e:
            self.assertTrue(
                str(e).startswith(
                    "Received a response in a non-JSON format: "), e)

        self.assertEqual(2, retry_handlers[0].call_count)