def test_events(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) async def event_handler(): pass app.event("app_mention")(event_handler) input = { "token": "verification_token", "team_id": "T111", "enterprise_id": "E111", "api_app_id": "A111", "event": { "client_msg_id": "9cbd4c5b-7ddf-4ede-b479-ad21fca66d63", "type": "app_mention", "text": "<@W111> Hi there!", "user": "******", "ts": "1595926230.009600", "team": "T111", "channel": "C111", "event_ts": "1595926230.009600", }, "type": "event_callback", "event_id": "Ev111", "event_time": 1595926230, "authed_users": ["W111"], } timestamp, body = str(int(time())), json.dumps(input) api = FastAPI() app_handler = AsyncSlackRequestHandler(app) @api.post("/slack/events") async def endpoint(req: Request): return await app_handler.handle(req) client = TestClient(api) response = client.post( "/slack/events", data=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 assert_auth_test_count(self, 1)
async def test_self_events(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, authorize=authorize, ) app.event("reaction_added")(whats_up) self_event = { "token": "verification_token", "team_id": "T_SOURCE", "enterprise_id": "E_SOURCE", "api_app_id": "A111", "event": { "type": "reaction_added", "user": "******", # bot_user_id "item": { "type": "message", "channel": "C111", "ts": "1599529504.000400", }, "reaction": "heart_eyes", "item_user": "******", "event_ts": "1599616881.000800", }, "type": "event_callback", "event_id": "Ev111", "event_time": 1599616881, "authorizations": [ { "enterprise_id": "E_INSTALLED", "team_id": "T_INSTALLED", "user_id": "W111", "is_bot": True, "is_enterprise_install": False, } ], } timestamp, body = str(int(time())), json.dumps(self_event) request = AsyncBoltRequest( body=body, headers=self.build_headers(timestamp, body) ) response = await app.async_dispatch(request) assert response.status == 200 await assert_auth_test_count_async(self, 1) await asyncio.sleep(1) # wait a bit after auto ack() # The listener should not be executed assert self.mock_received_requests.get("/chat.postMessage") is None
def test_shortcuts(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) async def shortcut_handler(ack): await ack() app.shortcut("test-shortcut")(shortcut_handler) input = { "type": "shortcut", "token": "verification_token", "action_ts": "111.111", "team": { "id": "T111", "domain": "workspace-domain", "enterprise_id": "E111", "enterprise_name": "Org Name", }, "user": { "id": "W111", "username": "******", "team_id": "T111" }, "callback_id": "test-shortcut", "trigger_id": "111.111.xxxxxx", } timestamp, body = str(int( time())), f"payload={quote(json.dumps(input))}" api = FastAPI() app_handler = AsyncSlackRequestHandler(app) @api.post("/slack/events") async def endpoint(req: Request): return await app_handler.handle(req) client = TestClient(api) response = client.post( "/slack/events", data=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 assert_auth_test_count(self, 1)
def test_oauth(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, oauth_settings=AsyncOAuthSettings( client_id="111.111", client_secret="xxx", scopes=["chat:write", "commands"], ), ) app_handler = AsyncSlackRequestHandler(app) async def endpoint(req: Request): return await app_handler.handle(req) api = Starlette( debug=True, routes=[ Route("/slack/install", endpoint=endpoint, methods=["GET"]) ], ) client = TestClient(api) response = client.get("/slack/install", allow_redirects=False) assert response.status_code == 200 assert response.headers.get( "content-type") == "text/html; charset=utf-8" assert response.headers.get("content-length") == "565" assert "https://slack.com/oauth/v2/authorize?state=" in response.text
async def test_message_subtypes_3(self): app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret) app._client = AsyncWebClient(token="uninstalled-revoked", base_url=self.mock_api_server_base_url) @app.event("message") async def handler1(event): assert event["subtype"] == "file_share" timestamp, body = str(int(time())), json.dumps( self.message_file_share_body) request: AsyncBoltRequest = AsyncBoltRequest( body=body, headers=self.build_headers(timestamp, body)) response = await app.async_dispatch(request) assert response.status == 200
async def test_string_keyword(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) result = {"call_count": 0} @app.message("Hi there!") async def handle_messages(event, logger): logger.info(event) result["call_count"] = result["call_count"] + 1 request = self.build_request(user_message_event_payload) response = await app.async_dispatch(request) assert response.status == 200 request = self.build_request(bot_message_event_payload) response = await app.async_dispatch(request) assert response.status == 200 request = self.build_request(classic_bot_message_event_payload) response = await app.async_dispatch(request) assert response.status == 200 assert self.mock_received_requests["/auth.test"] == 1 await asyncio.sleep(1) # wait a bit after auto ack() assert result["call_count"] == 3
async def test_success_global_2(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) app.global_shortcut("test-shortcut")(simple_listener) request = self.build_valid_request(global_shortcut_raw_body) response = await app.async_dispatch(request) assert response.status == 200 assert self.mock_received_requests["/auth.test"] == 1 request = self.build_valid_request(message_shortcut_raw_body) response = await app.async_dispatch(request) assert response.status == 404 assert self.mock_received_requests["/auth.test"] == 2
async def test_default(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) app.event("app_mention")(whats_up) timestamp, body = str(int(time())), json.dumps(app_mention_body) request = AsyncBoltRequest( body=body, headers=self.build_headers(timestamp, body) ) response = await app.async_dispatch(request) assert response.status == 200 await assert_auth_test_count_async(self, 1) await asyncio.sleep(1) # wait a bit after auto ack() assert self.mock_received_requests["/chat.postMessage"] == 1
async def test_success_global_2(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) app.global_shortcut("test-shortcut")(simple_listener) request = self.build_valid_request(global_shortcut_raw_body) response = await app.async_dispatch(request) assert response.status == 200 await assert_auth_test_count_async(self, 1) request = self.build_valid_request(message_shortcut_raw_body) response = await app.async_dispatch(request) assert response.status == 404 await assert_auth_test_count_async(self, 1)
async def test_handle_callback(self): oauth_flow = AsyncOAuthFlow( client=AsyncWebClient(base_url=self.mock_api_server_base_url), settings=AsyncOAuthSettings( client_id="111.222", client_secret="xxx", scopes=["chat:write", "commands"], installation_store=FileInstallationStore(), state_store=FileOAuthStateStore(expiration_seconds=120), success_url="https://www.example.com/completion", failure_url="https://www.example.com/failure", ), ) state = await oauth_flow.issue_new_state(None) req = AsyncBoltRequest( body="", query=f"code=foo&state={state}", headers={ "cookie": [f"{oauth_flow.settings.state_cookie_name}={state}"] }, ) resp = await oauth_flow.handle_callback(req) assert resp.status == 200 assert "https://www.example.com/completion" in resp.body app = AsyncApp(signing_secret="signing_secret", oauth_flow=oauth_flow) global_shortcut_body = { "type": "shortcut", "token": "verification_token", "action_ts": "111.111", "team": { "id": "T111", "domain": "workspace-domain", "enterprise_id": "E111", "enterprise_name": "Org Name", }, "user": { "id": "W111", "username": "******", "team_id": "T111" }, "callback_id": "test-shortcut", "trigger_id": "111.111.xxxxxx", } body = f"payload={quote(json.dumps(global_shortcut_body))}" timestamp = str(int(time())) signature_verifier = SignatureVerifier("signing_secret") headers = { "content-type": ["application/x-www-form-urlencoded"], "x-slack-signature": [ signature_verifier.generate_signature(body=body, timestamp=timestamp) ], "x-slack-request-timestamp": [timestamp], } request = AsyncBoltRequest(body=body, headers=headers) response = await app.async_dispatch(request) assert response.status == 200 await assert_auth_test_count_async(self, 1)
def test_commands(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) async def command_handler(ack): await ack() app.command("/hello-world")(command_handler) input = ( "token=verification_token" "&team_id=T111" "&team_domain=test-domain" "&channel_id=C111" "&channel_name=random" "&user_id=W111" "&user_name=primary-owner" "&command=%2Fhello-world" "&text=Hi" "&enterprise_id=E111" "&enterprise_name=Org+Name" "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx" "&trigger_id=111.111.xxx") timestamp, body = str(int(time())), input async def endpoint(req: Request): return await app_handler.handle(req) api = Starlette( debug=True, routes=[ Route("/slack/events", endpoint=endpoint, methods=["POST"]) ], ) app_handler = AsyncSlackRequestHandler(app) client = TestClient(api) response = client.post( "/slack/events", data=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 assert self.mock_received_requests["/auth.test"] == 1
async def test_failure(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) request = self.build_valid_request() response = await app.async_dispatch(request) assert response.status == 404 assert self.mock_received_requests["/auth.test"] == 1 app.action({ "callback_id": "unknown", "type": "interactive_message", })(simple_listener) response = await app.async_dispatch(request) assert response.status == 404 assert self.mock_received_requests["/auth.test"] == 1
async def test_failure(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) request = self.build_valid_request() response = await app.async_dispatch(request) assert response.status == 404 assert self.mock_received_requests["/auth.test"] == 1 app.view({ "type": "view_closed", "callback_id": "view-idddd" })(simple_listener) response = await app.async_dispatch(request) assert response.status == 404 assert self.mock_received_requests["/auth.test"] == 2
async def test_cancellation_failure_2(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) request = self.build_valid_request(suggestion_raw_body) response = await app.async_dispatch(request) assert response.status == 404 await assert_auth_test_count_async(self, 1) app.action({ "type": "dialog_cancellation", "callback_id": "dialog-callback-iddddd" })(handle_cancellation) response = await app.async_dispatch(request) assert response.status == 404 await assert_auth_test_count_async(self, 1)
async def test_submission_failure_2(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) request = self.build_valid_request(suggestion_raw_body) response = await app.async_dispatch(request) assert response.status == 404 assert self.mock_received_requests["/auth.test"] == 1 app.action({ "type": "dialog_submission", "callback_id": "dialog-callback-iddddd" })(handle_submission) response = await app.async_dispatch(request) assert response.status == 404 assert self.mock_received_requests["/auth.test"] == 2
async def test_simultaneous_requests(self): app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret,) app.event("app_mention")(random_sleeper) request = self.build_valid_app_mention_request() times = 10 tasks = [] for i in range(times): tasks.append(asyncio.ensure_future(app.async_dispatch(request))) await asyncio.sleep(5) # Verifies all the tasks have been completed with 200 OK assert sum([t.result().status for t in tasks if t.done()]) == 200 * times assert self.mock_received_requests["/auth.test"] == times assert self.mock_received_requests["/chat.postMessage"] == times
async def test_save(self): timestamp, body = str(int(time())), f"payload={quote(json.dumps(save_payload))}" headers = { "content-type": ["application/x-www-form-urlencoded"], "x-slack-signature": [self.generate_signature(body, timestamp)], "x-slack-request-timestamp": [timestamp], } request = AsyncBoltRequest(body=body, headers=headers) response = await self.app.async_dispatch(request) assert response.status == 200 assert self.mock_received_requests["/auth.test"] == 1 self.app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret) self.app.step( callback_id="copy_review___", edit=edit, save=save, execute=execute ) response = await self.app.async_dispatch(request) assert response.status == 404
def __init__(self, data: dict, slack: 'Slack'): self.slack = slack self.bot = self.slack.bot self.team_id = data['team_id'] self.token = data['token'] self.bot_id = data['bot_id'] self.name = self.team_id self.app = AsyncApp(token=self.token) self.app.view("socket_modal_submission")(self.submission) self.app.event("message")(self.slack_message) self.app.event("member_joined_channel")(self.slack_member_joined) self.app.event("channel_left")(self.slack_channel_left) self.handler = AsyncSocketModeHandler(self.app, config.SLACK_APP_TOKEN) self.bot.loop.create_task(self.handler.start_async()) self.bot.add_listener(self.on_message, 'on_message') self.bot.add_listener(self.on_raw_message_edit, 'on_raw_message_edit') self.bot.add_listener(self.on_raw_message_delete, 'on_raw_message_delete') self.channels: list[SlackChannel] = [] self.members: list[SlackMember] = [] self.slack.bot.loop.create_task(self.get_team_info()) self.discord_messages: TTLCache[int, DiscordMessage] = TTLCache(ttl=600.0, maxsize=500) self.slack_messages: TTLCache[str, SlackMessage] = TTLCache(ttl=600.0, maxsize=500) self.message_links = TTLCache(ttl=86400.0, maxsize=1000) self.initialize_data() self.messages_cached = asyncio.Event() self.members_cached = asyncio.Event() self.channels_cached = asyncio.Event() self.slack.bot.loop.create_task(self.cache_members()) self.slack.bot.loop.create_task(self.cache_channels()) self.slack.bot.loop.create_task(self.cache_messages())
async def test_app_home_opened(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, installation_store=OrgAppInstallationStore(), ) event_payload = { "token": "verification-token", "team_id": "T111", "enterprise_id": "E111", "api_app_id": "A111", "event": { "type": "app_home_opened", "user": "******", "channel": "D111", "tab": "messages", "event_ts": "1606810927.510671", }, "type": "event_callback", "event_id": "Ev111", "event_time": 1606810927, "authorizations": [{ "enterprise_id": "E111", "team_id": None, "user_id": "W111", "is_bot": True, "is_enterprise_install": True, }], "is_ext_shared_channel": False, } result = Result() @app.event("app_home_opened") async def handle_app_mention(body): assert body == event_payload result.called = True timestamp, body = str(int(time())), json.dumps(event_payload) request: AsyncBoltRequest = AsyncBoltRequest( body=body, headers=self.build_headers(timestamp, body)) response = await app.async_dispatch(request) assert response.status == 200 # auth.test API call must be skipped assert self.mock_received_requests["/auth.test"] == 1 await asyncio.sleep(1) # wait a bit after auto ack() assert result.called is True
def test_instance(self): server = AsyncSlackAppServer( port=3001, path="/slack/events", app=AsyncApp( signing_secret="valid", token="xoxb-valid", ), ) assert server is not None
async def test_no_installation_store(self): self.web_client.token = valid_token app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) with pytest.raises(BoltError): app.default_tokens_revoked_event_listener() with pytest.raises(BoltError): app.default_app_uninstalled_event_listener() with pytest.raises(BoltError): app.enable_token_revocation_listeners()
async def test_instantiation_non_async_settings_to_app(self): with pytest.raises(BoltError): AsyncApp( signing_secret="xxx", oauth_settings=OAuthSettings( client_id="111.222", client_secret="xxx", scopes="chat:write,commands", ), )
async def test_disabled(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, request_verification_enabled=False, ) app.event("app_mention")(whats_up) # request including invalid headers expired = int(time()) - 3600 timestamp, body = str(expired), json.dumps(app_mention_body) request = AsyncBoltRequest( body=body, headers=self.build_headers(timestamp, body) ) response = await app.async_dispatch(request) assert response.status == 200 await assert_auth_test_count_async(self, 1) await asyncio.sleep(1) # wait a bit after auto ack() assert self.mock_received_requests["/chat.postMessage"] == 1
async def test_commands(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) async def command_handler(ack): await ack() app.command("/hello-world")(command_handler) input = ( "token=verification_token" "&team_id=T111" "&team_domain=test-domain" "&channel_id=C111" "&channel_name=random" "&user_id=W111" "&user_name=primary-owner" "&command=%2Fhello-world" "&text=Hi" "&enterprise_id=E111" "&enterprise_name=Org+Name" "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx" "&trigger_id=111.111.xxx" ) timestamp, body = str(int(time())), input api = Sanic(name=self.unique_sanic_app_name()) app_handler = AsyncSlackRequestHandler(app) @api.post("/slack/events") async def endpoint(req: Request): return await app_handler.handle(req) _, response = await api.asgi_client.post( url="/slack/events", data=body, headers=self.build_headers(timestamp, body), ) assert response.status_code == 200 assert_auth_test_count(self, 1)
async def test_disabled(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, url_verification_enabled=False, ) request = self.build_valid_request() response = await app.async_dispatch(request) assert response.status == 404 assert response.body == """{"error": "unhandled request"}""" await assert_auth_test_count_async(self, 0)
async def test_execute(self): timestamp, body = str(int(time())), json.dumps(execute_payload) headers = { "content-type": ["application/json"], "x-slack-signature": [self.generate_signature(body, timestamp)], "x-slack-request-timestamp": [timestamp], } request = AsyncBoltRequest(body=body, headers=headers) response = await self.app.async_dispatch(request) assert response.status == 200 assert self.mock_received_requests["/auth.test"] == 1 await asyncio.sleep(0.5) assert self.mock_received_requests["/workflows.stepCompleted"] == 1 self.app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret) self.app.step( callback_id="copy_review___", edit=edit, save=save, execute=execute ) response = await self.app.async_dispatch(request) assert response.status == 404
async def test_instance_methods_uncommon_name(self): app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret) awesome = AwesomeClass("Slackbot") app.use(awesome.instance_middleware) app.shortcut("test-shortcut")(awesome.instance_method2) await self.run_app_and_verify(app)
async def test_success_without_type(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) app.options("dialog-callback-id")(handle_suggestion) app.action("dialog-callback-id")(handle_submission_or_cancellation) request = self.build_valid_request(suggestion_raw_body) response = await app.async_dispatch(request) assert response.status == 200 assert response.body != "" assert response.headers["content-type"][ 0] == "application/json;charset=utf-8" await assert_auth_test_count_async(self, 1) request = self.build_valid_request(submission_raw_body) response = await app.async_dispatch(request) assert response.status == 200 assert response.body == "" await assert_auth_test_count_async(self, 1) request = self.build_valid_request(cancellation_raw_body) response = await app.async_dispatch(request) assert response.status == 200 assert response.body == "" await assert_auth_test_count_async(self, 1)
async def test_default(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, ) request = self.build_valid_request() response = await app.async_dispatch(request) assert response.status == 200 assert ( response.body == """{"challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P"}""" ) await assert_auth_test_count_async(self, 0)
async def test_tokens_revoked(self): app = AsyncApp( client=self.web_client, signing_secret=self.signing_secret, installation_store=MyInstallationStore(), ) event_payload = { "token": "verification-token", "enterprise_id": "E111", "api_app_id": "A111", "event": { "type": "tokens_revoked", "tokens": { "oauth": ["W111"], "bot": ["W222"] }, }, "type": "event_callback", "event_id": "Ev111", "event_time": 1606805974, } timestamp, body = str(int(time())), json.dumps(event_payload) request: AsyncBoltRequest = AsyncBoltRequest( body=body, headers=self.build_headers(timestamp, body)) response = await app.async_dispatch(request) assert response.status == 404 # Enable the built-in event listeners app.enable_token_revocation_listeners() response = await app.async_dispatch(request) assert response.status == 200 # auth.test API call must be skipped await assert_auth_test_count_async(self, 0) await asyncio.sleep(1) # wait a bit after auto ack() assert app.installation_store.delete_bot_called is True assert app.installation_store.delete_installation_called is True assert app.installation_store.delete_all_called is False