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