Skip to content

Commit 9aaa27b

Browse files
ezynda3opencode
andcommitted
Fix DM threading behavior - no threads in direct messages
Corrected the bot behavior for better UX: - DMs: Respond directly in conversation without creating threads - Channels: Continue to respond only to @mentions in threads - Added GetChannelHistory method to fetch DM conversation history - Updated examples to clearly show channel vs DM behavior This provides the most natural experience: - Clean channels with threaded discussions - Natural flowing conversations in DMs 🤖 Generated with [opencode](https://opencode.ai) Co-Authored-By: opencode <noreply@opencode.ai>
1 parent 08d8f34 commit 9aaa27b

File tree

3 files changed

+66
-37
lines changed

3 files changed

+66
-37
lines changed

cookbook/slack-bot/README.md

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ A sophisticated Slack bot built with Flyt workflow framework that integrates Ope
1313
- **Conversation Memory**: Maintains context across messages with thread history
1414
- **Smart Threading**:
1515
- In channels: Only responds to @mentions and always replies in threads
16-
- In DMs: Maintains conversation flow naturally
16+
- In DMs: Responds directly in conversation (no threading)
1717
- **Context Awareness**: Includes thread/DM history in LLM context for better responses
1818

1919
## Architecture
@@ -191,28 +191,37 @@ The bot will connect to Slack and start listening for messages.
191191

192192
### Example Interactions
193193

194-
**Calculator:**
194+
**In a Channel (must @mention):**
195195
```
196-
User: Can you calculate 25 * 4 + sqrt(16)?
197-
Bot: I'll help you calculate that expression.
196+
User: Can you calculate 25 * 4?
197+
Bot: [No response - not mentioned]
198+
199+
User: @Bot Can you calculate 25 * 4 + sqrt(16)?
200+
Bot: [In thread] I'll help you calculate that expression.
198201
Result: 104
202+
203+
User: [In same thread] Now divide by 2
204+
Bot: [In thread] Dividing 104 by 2 gives us 52.
199205
```
200206
201-
**Chuck Norris Facts:**
207+
**In Direct Message:**
202208
```
203209
User: Tell me a Chuck Norris fact about programming
204210
Bot: Here's a Chuck Norris fact for you:
205211
Chuck Norris doesn't use web standards. The web conforms to Chuck Norris.
212+
213+
User: Give me another one
214+
Bot: [Knows context] Here's another Chuck Norris fact:
215+
Chuck Norris can unit test an entire application with a single assert.
206216
```
207217
208-
**Combined:**
218+
**Combined Tools in DM:**
209219
```
210-
User: What's 2^10 and also give me a Chuck Norris fact
211-
Bot: I'll calculate 2^10 and get you a Chuck Norris fact.
212-
213-
The calculation 2^10 equals 1024.
214-
215-
And here's your Chuck Norris fact:
220+
User: What's 2^10?
221+
Bot: The calculation 2^10 equals 1024.
222+
223+
User: Cool! Now give me a Chuck Norris fact
224+
Bot: Here's your Chuck Norris fact:
216225
Chuck Norris can divide by zero.
217226
```
218227
@@ -229,7 +238,7 @@ Bot: I'll calculate 2^10 and get you a Chuck Norris fact.
229238
7. GPT-4.1 formulates a final response using the tool results
230239
8. The response is sent back:
231240
- In channels: Always as a thread reply
232-
- In DMs: In the conversation flow
241+
- In DMs: Directly in the conversation (no threads)
233242
9. Conversation history is maintained per thread for context continuity
234243
235244
### Available Tools

cookbook/slack-bot/main.go

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/joho/godotenv"
1313
"github.com/mark3labs/flyt"
14+
"github.com/slack-go/slack"
1415
"github.com/slack-go/slack/slackevents"
1516
"github.com/slack-go/slack/socketmode"
1617
)
@@ -161,16 +162,10 @@ func (b *SlackBot) handleMessage(ctx context.Context, event *slackevents.Message
161162

162163
log.Printf("DM from %s: %s", event.User, event.Text)
163164

164-
// For DMs, use the message timestamp as thread (maintains conversation flow)
165-
threadTS := event.ThreadTimeStamp
166-
if threadTS == "" {
167-
threadTS = event.TimeStamp
168-
}
169-
170-
// Process message through Flyt workflow
171-
b.processWithFlyt(ctx, event.Text, event.Channel, threadTS, event.User)
165+
// For DMs, don't use threads - respond directly in the conversation
166+
// Pass empty string for threadTS to indicate no threading
167+
b.processWithFlyt(ctx, event.Text, event.Channel, "", event.User)
172168
}
173-
174169
func (b *SlackBot) handleMention(ctx context.Context, event *slackevents.AppMentionEvent) {
175170
log.Printf("Mention from %s in channel %s: %s", event.User, event.Channel, event.Text)
176171

@@ -247,29 +242,38 @@ func (b *SlackBot) processWithFlyt(ctx context.Context, message, channel, thread
247242

248243
func (b *SlackBot) fetchConversationHistory(channel, threadTS string) []map[string]string {
249244
var history []map[string]string
245+
var messages []slack.Message
246+
var err error
250247

251248
if threadTS != "" {
252-
// Fetch thread messages
253-
messages, err := b.slack.GetThreadMessages(channel, threadTS)
249+
// For threads in channels, fetch thread messages
250+
messages, err = b.slack.GetThreadMessages(channel, threadTS)
254251
if err != nil {
255252
log.Printf("Failed to fetch thread history: %v", err)
256253
return history
257254
}
255+
} else if b.isDirectMessage(channel) {
256+
// For DMs, fetch recent channel history
257+
messages, err = b.slack.GetChannelHistory(channel, 20)
258+
if err != nil {
259+
log.Printf("Failed to fetch DM history: %v", err)
260+
return history
261+
}
262+
}
258263

259-
// Convert to simplified format, excluding bot's own messages
260-
botID := b.slack.GetBotUserID()
261-
for _, msg := range messages {
262-
// Skip bot's own messages and empty messages
263-
if msg.User == botID || msg.BotID != "" || msg.Text == "" {
264-
continue
265-
}
266-
267-
history = append(history, map[string]string{
268-
"user": msg.User,
269-
"text": msg.Text,
270-
"timestamp": msg.Timestamp,
271-
})
264+
// Convert to simplified format, excluding bot's own messages
265+
botID := b.slack.GetBotUserID()
266+
for _, msg := range messages {
267+
// Skip bot's own messages and empty messages
268+
if msg.User == botID || msg.BotID != "" || msg.Text == "" {
269+
continue
272270
}
271+
272+
history = append(history, map[string]string{
273+
"user": msg.User,
274+
"text": msg.Text,
275+
"timestamp": msg.Timestamp,
276+
})
273277
}
274278

275279
// Limit history to last 10 messages for context
@@ -279,6 +283,7 @@ func (b *SlackBot) fetchConversationHistory(channel, threadTS string) []map[stri
279283

280284
return history
281285
}
286+
282287
func (b *SlackBot) createWorkflow(llmService *LLMService) *flyt.Flow {
283288
// Create nodes with injected dependencies
284289
parseNode := &ParseMessageNode{

cookbook/slack-bot/slack.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,21 @@ func (s *SlackService) GetThreadMessages(channel, threadTS string) ([]slack.Mess
188188
return messages, nil
189189
}
190190

191+
// GetChannelHistory retrieves recent messages from a channel or DM
192+
func (s *SlackService) GetChannelHistory(channel string, limit int) ([]slack.Message, error) {
193+
params := &slack.GetConversationHistoryParameters{
194+
ChannelID: channel,
195+
Limit: limit,
196+
}
197+
198+
resp, err := s.client.GetConversationHistory(params)
199+
if err != nil {
200+
return nil, fmt.Errorf("failed to get channel history: %w", err)
201+
}
202+
203+
return resp.Messages, nil
204+
}
205+
191206
// EnrichMessageContext adds user and channel information to the message context
192207
func (s *SlackService) EnrichMessageContext(userID, channelID string) map[string]string {
193208
context := make(map[string]string)

0 commit comments

Comments
 (0)