feat(slack): add direct message (DM) support#411
feat(slack): add direct message (DM) support#411duboff wants to merge 6 commits intoColeMurray:mainfrom
Conversation
Users can now interact with the bot via direct messages without needing to @mention it. Handles message.im events with channel_type "im", skips bot messages to prevent self-responses, and supports the same session management, repo selection, and multi-turn conversation as channel mentions. DMs don't have channel metadata (name/description), so those are omitted from context when building prompts.
Greptile SummaryThis PR adds direct message (DM) support to the Slack bot by introducing a Three areas could use polish:
Prompt To Fix All With AIThis is a comment left during a code review.
Path: packages/slack-bot/src/index.ts
Line: 1156-1159
Comment:
**Silent empty-message handling differs from `handleAppMention`**
When `handleAppMention` receives a message with no text it replies with `"Hi! Please include a message with your request."`, giving the user actionable feedback. The DM handler silently returns with only an internal log, so the user sees nothing and has no idea why the bot didn't respond.
```suggestion
if (!messageText) {
log.info("slack.dm.empty_message", { trace_id: traceId });
await postMessage(
env.SLACK_BOT_TOKEN,
channel,
"Hi! Please include a message with your request.",
{ thread_ts: ts }
);
return;
}
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: packages/slack-bot/src/index.ts
Line: 1152-1154
Comment:
**`@mention` syntax not stripped from DM text**
The comment says "the text doesn't have a bot mention to strip," but users *can* still `@mention` the bot inside a DM (e.g., `@Bot please fix the bug`). When they do, the raw Slack mention token (`<@U12345>`) is forwarded verbatim to the session, which may confuse the agent.
Applying the same strip that `handleAppMention` uses prevents this:
```suggestion
// Strip any bot mention tokens the user may have typed in the DM (uncommon but possible)
const rawText = event.text || "";
const messageText = rawText.replace(/<@[A-Z0-9]+>/g, "").trim();
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: packages/slack-bot/src/index.ts
Line: 813-836
Comment:
**No explicit `subtype` guard for DM message events**
Slack delivers every message-related event in an IM channel under `message.im`, including `message_changed` (user edits) and `message_deleted`. Those events omit the top-level `user` field, so the current `event.user` guard incidentally prevents them from being processed — but this is implicit.
Adding an explicit `!event.subtype` check (and surfacing `subtype` in the type definition) makes the intent clear, guards against future Slack API changes, and removes the dependence on the coincidental absence of `user`:
```suggestion
// Handle direct messages (DMs) to the bot
// message.im events have channel_type "im" - these are 1:1 DMs with the bot
if (
event.type === "message" &&
!event.subtype &&
event.channel_type === "im" &&
event.channel &&
event.ts &&
event.user
) {
```
You would also need to add `subtype?: string;` to the event payload type at the top of `handleSlackEvent`.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "feat(slack): add direct message (DM) sup..." | Re-trigger Greptile |
ColeMurray
left a comment
There was a problem hiding this comment.
[Automated Review]
The feature is correct and the event handling is well-guarded, but the implementation duplicates nearly all of handleAppMention (~280 lines). See inline comments for details.
No tests included. The slack-bot package has no test files at all (pre-existing), but the duplication makes this riskier — a bug fix applied to only one of the two copies would silently regress the other path.
- Strip <@mention> tokens from DM text before processing - Guard DM dispatch with !event.subtype to skip bot/edited messages - Add subtype field to event payload type - Add observability log at start of handleDirectMessage
Extract mention stripping and DM dispatch guard into dm-utils.ts so they can be unit tested independently of the Cloudflare Worker entry point. Add 13 tests covering stripMentions and isDmDispatchable.
TypeScript can't narrow the outer event's optional fields through the isDmDispatchable type guard, so use non-null assertions for text/user/channel/ts which isDmDispatchable already validated.
Extract shared logic into handleIncomingMessage. Both handlers now clean the text, optionally fetch channel info, and delegate to the shared function for thread context, session lookup, repo classification, and session creation. Also fixes typecheck error (text variable not in scope) and makes handleDirectMessage params non-optional since isDmDispatchable guards them.
Summary
message.imevents withchannel_type === "im"Test plan