Example #1
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_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()
 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)
    def test_remote_disconnected(self):
        retry_handler = MyRetryHandler(max_retry_count=2)
        client = WebClient(
            base_url="http://localhost:8888",
            token="xoxp-remote_disconnected",
            team_id="T111",
            retry_handlers=[retry_handler],
        )
        try:
            client.auth_test()
            self.fail("An exception is expected")
        except Exception as _:
            pass

        self.assertEqual(2, retry_handler.call_count)
Example #5
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)
Example #6
0
def authorize(enterprise_id, team_id, client: WebClient):
    assert enterprise_id == "E_INSTALLED"
    assert team_id == "T_INSTALLED"
    auth_test = client.auth_test(token=valid_token)
    return AuthorizeResult.from_auth_test_response(
        auth_test_response=auth_test, bot_token=valid_token,
    )
Example #7
0
import asyncio
import logging

logging.basicConfig(level=logging.DEBUG)

logger = logging.getLogger(__name__)

import os
from slack_sdk.web import WebClient

# export HTTPS_PROXY=http://localhost:9000
client = WebClient(token=os.environ["SLACK_API_TOKEN"])
response = client.auth_test()
logger.info(f"HTTPS_PROXY response: {response}")

client = WebClient(token=os.environ["SLACK_API_TOKEN"],
                   proxy="http://localhost:9000")
response = client.auth_test()
logger.info(f"sync response: {response}")

client = WebClient(token=os.environ["SLACK_API_TOKEN"], proxy="localhost:9000")
response = client.auth_test()
logger.info(f"sync response: {response}")


async def async_call():
    client = WebClient(
        token=os.environ["SLACK_API_TOKEN"],
        proxy="http://localhost:9000",
        run_async=True,
    )
Example #8
0
class SlackBot():
    def __init__(self, setting):
        xoxb_token = setting['slack']['xoxb_token']
        xapp_token = setting['slack']['xapp_token']
        self._web_client = WebClient(token=xoxb_token)
        self._sm_client = SocketModeClient(app_token=xapp_token,
                                           web_client=self._web_client)
        self.plugins_setting = setting['plugins']
        self.plugins_path = setting['bot']['plugins_dir']
        self.plugin_modules = []
        self.plugin_classes = []
        self.plugin_instances = []
        self._self = None
        self._team = None
        self._users_list = {}  # [ 'id' ] => SlackUser
        self._channels_list = {}
        self._data = None

    def _get_rtm_client(self):
        return self._rtm_client

    def _set_rtm_client(self, rc):
        self._rtm_client = rc

    rtm_client = property(_get_rtm_client, _set_rtm_client)

    def _get_web_client(self):
        return self._web_client

    def _set_web_client(self, rc):
        self._web_client = rc

    web_client = property(_get_web_client, _set_web_client)

    # plugin loader

    def load_plugins(self):
        for ps in self.plugins_setting:
            mod = importlib.import_module(ps['module'])
            klass_name = ps['name']
            klass = getattr(mod, klass_name)
            self.plugin_classes.append(klass)
            self.plugin_instances.append(klass(self, ps))

    def load_plugins_filename_based(self):
        plugins_dir = os.listdir(self.plugins_path)
        #        current_dir = os.path.dirname( os.path.abspath( __file__ ) )
        for filename in plugins_dir:
            if filename.endswith('.py'):
                if filename == "__init__.py":
                    continue
                klass_name = os.path.splitext(filename)[0]
                klass_name = klass_name[0].upper() + klass_name[1:]
                modulePath = self.plugins_path + '/' + filename
                cpath = os.path.splitext(modulePath)[0].replace(
                    os.path.sep, '.')
                try:
                    mod = importlib.import_module(cpath)
                    self.plugin_modules.append(mod)
                    klass = getattr(mod, klass_name)
                    self.plugin_classes.append(klass)
                    self.plugin_instances.append(klass(self, klass_name))
                except ModuleNotFoundError:
                    print('Module not found')
                except AttributeError:
                    print('Method not found')

    def unload_plugins(self):
        for ins in self.plugin_instances:
            del (ins)
        self.plugin_instances = []

        for cls in self.plugin_classes:
            del (cls)
        self.plugin_classes = []

        for mod in self.plugin_modules:
            del (mod)
        self.plugin_modules = []

    def reload_plugins(self):
        self.unload_plugins()
        self.load_plugins()

    # bot information

    def self_user(self):
        return self._self

    def self_id(self):
        u = self.self_user()
        return u.id

    def self_name(self):
        return self.self_user().name

    def team_info(self):
        return self._team

    def team_id(self):
        return self.team_info()['id']

    def team_name(self):
        return self.team_info()['name']

    def update_self_user(self, user):
        self._self = user

    def update_team_info(self, info):
        self._team = info

    def update_users_list(self, users):
        for user in users:
            self._users_list[user['id']] = SlackUser(user)

    def update_groups_list(self, groups):
        for group in groups:
            self._channels_list[group['id']] = SlackGroup(group)

    def update_ims_list(self, ims):
        for im in ims:
            self._channels_list[im['id']] = SlackIM(im)

    def update_channels_list(self, channels):
        for channel in channels:
            self._channels_list[channel['id']] = SlackChannel(channel)

    def resolve_channel_id_from_name(self, name):
        pass

    # plugin commands

    def send_message(self, channel, message, attachments_json=None):
        self._web_client.chat_postMessage(channel=channel.id,
                                          text=message,
                                          attachments=attachments_json)

    def send_mention_message(self,
                             channel,
                             user,
                             message,
                             attachments_json=None):
        mention_message = "<@" + user.id + "> " + message
        self._web_client.chat_postMessage(channel=channel.id,
                                          text=mention_message,
                                          attachments=attachments_json)

    def send_kick(self, channel, user):
        self._web_client.channels_kick(channel=channel.id, user=user.id)

    # plugin events

    def on_server_connect(self):
        for plugin in self.plugin_instances:
            plugin.on_server_connect()

    def process_message(self, data):
        channel = self._channels_list[data['channel']]
        user = self._users_list[data['user']]
        text = data['text']
        #        if user.id != self.self_id(): # ignore own message
        for plugin in self.plugin_instances:
            plugin.on_message(channel, user, text)

    def process_message_changed(self, data):
        channel = self._channels_list[data['channel']]
        user = self._users_list[data['message']['user']]
        text = data['message']['text']
        prev_user = data['previous_message']['user']
        prev_text = data['previous_message']['text']
        #        if user.id != self.self_id(): # ignore own message
        for plugin in self.plugin_instances:
            plugin.on_message_changed(channel, user, text, prev_user,
                                      prev_text)

    def on_message(self, payload):
        data = payload
        if 'bot_id' in data:
            return
        if 'subtype' in data:
            if data['subtype'] == 'message_changed':
                self.process_message_changed(data)
        else:
            self.process_message(data)

    def on_channel_joined(self, **payload):
        data = payload
        channel = data['channel']
        self._channels_list[channel['id']] = SlackChannel(channel)

    def on_channel_left(self, **payload):
        data = payload
        del self._channels_list[data['channel']]
        # TODO: It should be not delete the channel and It must be update the status such as a 'is_member'.
        # self._channels_list[ data[ 'channel' ] ].is_member = False

    def on_member_joined_channel(self, **payload):
        data = payload
        channel = self._channels_list[data['channel']]
        user = self._users_list[data['user']]
        for plugin in self.plugin_instances:
            plugin.on_joined(channel, user)

    def on_member_left_channel(self, **payload):
        data = payload
        channel = self._channels_list[data['channel']]
        user = self._users_list[data['user']]
        for plugin in self.plugin_instances:
            plugin.on_left(channel, user)

    # process slack rtm

    def on_socket_mode_request(self, client: SocketModeClient,
                               req: SocketModeRequest):
        if req.type == "events_api":
            # Acknowledge the request anyway
            response = SocketModeResponse(envelope_id=req.envelope_id)
            client.send_socket_mode_response(response)

            if req.payload['event']['type'] == 'open':
                self.on_open(req.payload['event'])
            elif req.payload['event']['type'] == 'message':
                self.on_message(req.payload['event'])
            elif req.payload['event']['type'] == 'channel_joined':
                self.on_channel_joined(req.payload['event'])
            elif req.payload['event']['type'] == 'channel_left':
                self.on_channel_left(req.payload['event'])
            elif req.payload['event']['type'] == 'member_joined_channel':
                self.on_member_joined_channel(req.payload['event'])
            elif req.payload['event']['type'] == 'member_left_channel':
                self.on_member_left_channel(req.payload['event'])

    def start(self):
        self._sm_client.socket_mode_request_listeners.append(
            self.on_socket_mode_request)
        self._sm_client.connect()

        response = self._web_client.users_list()
        self.update_users_list(response['members'])

        response = self._web_client.conversations_list()
        self.update_channels_list(response['channels'])

        response = self._web_client.team_info()
        self.update_team_info(response['team'])

        response = self._web_client.auth_test()
        self_id = response['user_id']
        self.update_self_user(self._users_list[self_id])

        self.load_plugins()
        self.on_server_connect()

        from threading import Event
        Event().wait()
Example #9
0
def _oauth(request: flask.Request, session: dict, user: Entity, dest: str,
           redirect_uri: str):
    # Retrieve the auth code and state from the request params
    if 'code' not in request.args:
        error = request.args["error"] if "error" in request.args else ""
        return flask.make_response(
            f"Something is wrong with the installation (error: {error})", 400)
    code = request.args['code']

    # Verify the state parameter
    state_store = DatastoreOAuthStateStore(ds_util.client,
                                           STATE_EXPIRATION_SECONDS)
    if not state_store.consume(request.args["state"]):
        return flask.make_response(
            "Try the installation again (the state value is already expired)",
            400)

    # Verify the state parameter
    # Complete the installation by calling oauth.v2.access API method
    client = WebClient()
    oauth_response = client.oauth_v2_access(
        client_id=config.slack_creds['client_id'],
        client_secret=config.slack_creds['client_secret'],
        redirect_uri=redirect_uri,
        code=code,
    )

    # These seem to sometimes return None rather than being unset, so for maps, return {}
    installed_enterprise = oauth_response.get("enterprise", {}) or {}
    is_enterprise_install = oauth_response.get("is_enterprise_install")
    installed_team = oauth_response.get("team", {}) or {}
    installer = oauth_response.get("authed_user", {}) or {}
    incoming_webhook = oauth_response.get("incoming_webhook", {}) or {}

    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"),
    )

    # Store the installation
    service = Service.get(SERVICE_NAME, parent=user.key)
    store = DatastoreInstallationStore(ds_util.client, parent=service.key)
    store.save(installation)

    task_util.sync_service(Service.get(SERVICE_NAME, parent=user.key))
    return flask.redirect(config.devserver_url + dest)