/integrations/slack/events/ endpoint. Slack Bolt dispatches the payload to the matching handler.
Event handler architecture
All event handlers inherit fromEventBase (backend/apps/slack/events/event.py). The base class provides:
- Template loading — each handler has a
direct_message_templateand an optionalephemeral_message_template, both resolved frombackend/apps/slack/templates/events/. - Context building —
get_context()populates standard variables (NEST_BOT_NAME,OWASP_NEST_URL,USER_ID, etc.) that every template can reference. - Block rendering —
render_blocks()renders the Jinja2 template and converts the output into Slack Block Kit JSON. - Message dispatch —
handle_event()opens a DM conversation if needed and sends direct and/or ephemeral messages. - Error handling —
SlackApiErrorwithcannot_dm_botis caught and logged rather than raised, so bot-to-bot DM attempts fail gracefully.
EventBase.configure_events() iterates over all subclasses and calls register() on each, which calls SlackConfig.app.event(self.event_type, matchers=self.matchers)(self.handler).
Handled events
app_home_opened
File: backend/apps/slack/events/app_home_opened.py
Fired when a user opens the NestBot home tab in Slack. The handler calls client.views_publish() to render a personalised home view using the app_home_opened.jinja template combined with a shared header block.
app_mention
File: backend/apps/slack/events/app_mention.py
Fired when a user mentions @NestBot in a channel. The handler:
- Checks whether the NestBot AI assistant is enabled for that channel (
Conversation.is_nest_bot_assistant_enabled). - Extracts the plain-text query from the Block Kit
rich_text_sectionelement. - Posts a
⏳ Thinking…placeholder message in the same thread. - Calls the AI handler (
apps.slack.common.handlers.ai.get_blocks) to generate a response. - Updates the placeholder message with the AI response.
message (message posted)
File: backend/apps/slack/events/message_posted.py
Fired when a new message is posted in a channel the bot has joined. The handler:
- Ignores bot messages, messages with subtypes, and thread replies.
- Looks up the
Conversationobject for the channel and checksis_nest_bot_assistant_enabled. - Runs the message text through
QuestionDetector.is_owasp_question()to determine whether the message is an OWASP-related question worth answering. - Saves the message to the
Messagemodel. - Enqueues an AI reply job via Django RQ after a configurable delay (
QUEUE_RESPONSE_TIME_MINUTES), giving human respondents time to answer first.
member_joined_channel
Directory: backend/apps/slack/events/member_joined_channel/
Fired when any user joins a channel the bot monitors. NestBot uses matchers to route the event to different handlers based on which channel was joined:
| File | Channel | Behaviour |
|---|---|---|
gsoc.py | #gsoc | Sends a DM with GSoC onboarding info and an ephemeral welcome message in the channel. |
contribute.py | #contribute | Sends a DM with contribution guidance. |
project_nest.py | #project-nest | Sends a DM welcoming the user to the OWASP Nest project. |
catch_all.py | All other channels | Acknowledges the event without sending any message. |
Gsoc handler demonstrates the pattern: it sets matchers to [lambda event: event["channel"] == OWASP_GSOC_CHANNEL_ID.lstrip("#")] so Bolt only routes matching events to it.
team_join
File: backend/apps/slack/events/team_join.py
Fired when a user joins the OWASP Slack workspace for the first time. The handler sends a welcome DM that includes links to key channels: #appsec, #ask-owasp, #community, #contribute, #gsoc, #jobs, #leaders, #mentors, and several project channels. The list of channel IDs is injected into the Jinja2 template context.
Unlike most other event handlers,
TeamJoin.get_user_id() extracts the user ID from event["user"]["id"] rather than event["user"], because the team_join payload includes a full user object.url_verification
File: backend/apps/slack/events/url_verification.py
Handles the one-time URL verification challenge Slack sends when you first configure or update the events endpoint. The handler returns event["challenge"] directly.
Subscribed events (MANIFEST.yaml)
The following events are declared inbackend/apps/slack/MANIFEST.yaml:
Adding a new event handler
Define the handler class
Inherit from
EventBase and set event_type to the Slack event name. Override get_context() to provide template variables, and optionally override handle_event() for custom dispatch logic.Create a Jinja2 template
Add
backend/apps/slack/templates/events/my_event.jinja with the Block Kit message content.