Skip to content

Commit 0f94305

Browse files
committed
Add decorator for event deduplication and apply it to event handlers
1 parent 57f6795 commit 0f94305

File tree

1 file changed

+29
-33
lines changed
  • packages/slackBotFunction

1 file changed

+29
-33
lines changed

packages/slackBotFunction/app.py

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import json
44
import boto3
55
import time
6+
from functools import wraps
7+
from typing import Callable
68
from slack_bolt import App
79
from slack_bolt.adapter.aws_lambda import SlackRequestHandler
810
from slack_sdk import WebClient
@@ -75,6 +77,29 @@ def mark_event_processed(event_id):
7577
logger.error(f"Failed to mark event as processed: {e}")
7678

7779

80+
def deduplicate_event(func: Callable) -> Callable:
81+
"""
82+
Decorator to deduplicate Slack events based on event_id from the request body.
83+
Prevents re-processing of already handled events.
84+
"""
85+
86+
@wraps(func)
87+
def wrapper(event, ack, body, *args, **kwargs):
88+
event_id = body.get("event_id")
89+
if not event_id:
90+
logger.warning("Missing event_id in Slack event body.")
91+
return func(event, ack, body, *args, **kwargs)
92+
93+
if is_duplicate_event(event_id):
94+
logger.info(f"Duplicate event detected, skipping: {event_id}")
95+
return
96+
97+
mark_event_processed(event_id)
98+
return func(event, ack, body, *args, **kwargs)
99+
100+
return wrapper
101+
102+
78103
def trigger_async_processing(event_data):
79104
"""
80105
Trigger async processing of the Slack event to avoid timeout issues.
@@ -164,47 +189,26 @@ def process_async_slack_event(slack_event_data):
164189

165190
# Handle @mentions in channels and DMs
166191
@app.event("app_mention")
192+
@deduplicate_event
167193
def handle_app_mention(event, ack, body):
168194
"""Handle when the bot is @mentioned"""
169195
event_id = body.get("event_id")
170-
171-
# Check for duplicate
172-
if is_duplicate_event(event_id):
173-
logger.info(f"Skipping duplicate event {event_id}")
174-
return
175-
176-
# Mark as processed
177-
mark_event_processed(event_id)
178-
179-
# Log the event
180196
user_id = event.get("user", "unknown")
181197
logger.info(f"Processing @mention from user {user_id}", extra={"event_id": event_id})
182198

183-
# Trigger async processing
184199
trigger_async_processing({"event": event, "event_id": event_id, "bot_token": bot_token})
185200

186201

187202
# Handle direct messages
188203
@app.event("message")
204+
@deduplicate_event
189205
def handle_direct_message(event, ack, body):
190206
"""Handle direct messages to the bot"""
191-
# Only respond to direct messages (not channel messages)
192207
if event.get("channel_type") == "im":
193208
event_id = body.get("event_id")
194-
195-
# Check for duplicate
196-
if is_duplicate_event(event_id):
197-
logger.info(f"Skipping duplicate DM event {event_id}")
198-
return
199-
200-
# Mark as processed
201-
mark_event_processed(event_id)
202-
203-
# Log the event
204209
user_id = event.get("user", "unknown")
205210
logger.info(f"Processing DM from user {user_id}", extra={"event_id": event_id})
206211

207-
# Trigger async processing
208212
trigger_async_processing({"event": event, "event_id": event_id, "bot_token": bot_token})
209213

210214

@@ -216,13 +220,5 @@ def handler(event: dict, context: LambdaContext) -> dict:
216220
process_async_slack_event(event["slack_event"])
217221
return {"statusCode": 200}
218222

219-
# Handle Slack events with no-retry header
220-
try:
221-
slack_handler = SlackRequestHandler(app=app)
222-
slack_handler.handle(event, context)
223-
224-
# Return 202 with x-slack-no-retry header to prevent retries
225-
return {"statusCode": 202, "headers": {"x-slack-no-retry": "1"}}
226-
except Exception as e:
227-
logger.error(f"Error handling Slack event: {e}")
228-
return {"statusCode": 202, "headers": {"x-slack-no-retry": "1"}}
223+
slack_handler = SlackRequestHandler(app=app)
224+
return slack_handler.handle(event, context)

0 commit comments

Comments
 (0)