diff --git a/packages/cdk/nagSuppressions.ts b/packages/cdk/nagSuppressions.ts index 9f3c61b1..d0f9220f 100644 --- a/packages/cdk/nagSuppressions.ts +++ b/packages/cdk/nagSuppressions.ts @@ -45,6 +45,22 @@ export const nagSuppressions = (stack: Stack) => { safeAddNagSuppression( stack, "/EpsAssistMeStack/Apis/EpsAssistApiGateway/ApiGateway/Default/slack/events/POST/Resource", + [ + { + id: "AwsSolutions-APIG4", + reason: "Slack event endpoint is intentionally unauthenticated." + }, + { + id: "AwsSolutions-COG4", + reason: "Cognito not required for this public endpoint." + } + ] + ) + + // Suppress unauthenticated API route warnings + safeAddNagSuppression( + stack, + "/EpsAssistMeStack/Apis/EpsAssistApiGateway/ApiGateway/Default/slack/commands/POST/Resource", [ { id: "AwsSolutions-APIG4", diff --git a/packages/cdk/resources/Apis.ts b/packages/cdk/resources/Apis.ts index 39702ddb..1ea5475d 100644 --- a/packages/cdk/resources/Apis.ts +++ b/packages/cdk/resources/Apis.ts @@ -37,6 +37,17 @@ export class Apis extends Construct { lambdaFunction: props.functions.slackBot }) + // Create the '/slack/commands' POST endpoint for Slack Events API + // This endpoint will handle slash commands, such as /test + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const slackCommandsEndpoint = new LambdaEndpoint(this, "SlackCommandsEndpoint", { + parentResource: slackResource, + resourceName: "commands", + method: HttpMethod.POST, + restApiGatewayRole: apiGateway.role, + lambdaFunction: props.functions.slackBot + }) + this.apis = { api: apiGateway } diff --git a/packages/slackBotFunction/app/slack/slack_handlers.py b/packages/slackBotFunction/app/slack/slack_handlers.py index 2100be2d..e535ab75 100644 --- a/packages/slackBotFunction/app/slack/slack_handlers.py +++ b/packages/slackBotFunction/app/slack/slack_handlers.py @@ -41,6 +41,7 @@ def setup_handlers(app: App) -> None: app.event("message")(ack=respond_to_events, lazy=[unified_message_handler]) app.action("feedback_yes")(ack=respond_to_action, lazy=[feedback_handler]) app.action("feedback_no")(ack=respond_to_action, lazy=[feedback_handler]) + app.command("test")(ack=respond_to_command, lazy=[prompt_test_handler]) for i in range(1, 10): app.action(f"cite_{i}")(ack=respond_to_action, lazy=[feedback_handler]) @@ -64,6 +65,11 @@ def respond_to_action(ack: Ack): ack() +def respond_to_command(ack: Ack): + logger.debug("Sending ack response") + ack() + + def feedback_handler(body: Dict[str, Any], client: WebClient) -> None: """Handle feedback button clicks (both positive and negative).""" try: @@ -127,3 +133,14 @@ def unified_message_handler(client: WebClient, event: Dict[str, Any], body: Dict process_async_slack_event(event=event, event_id=event_id, client=client) except Exception: logger.error("Error triggering async processing", extra={"error": traceback.format_exc()}) + + +def prompt_test_handler(body: Dict[str, Any], event: Dict[str, Any], client: WebClient) -> None: + """Handle /test command to prompt the bot to respond.""" + try: + event_id = gate_common(event=event, body=body) + logger.debug("logging result of gate_common", extra={"event_id": event_id, "body": body}) + if not event_id: + return + except Exception as e: + logger.error(f"Error handling /test command: {e}", extra={"error": traceback.format_exc()})