diff --git a/pkg/channels/discord.go b/pkg/channels/discord.go index 342ddb478..dc92b6256 100644 --- a/pkg/channels/discord.go +++ b/pkg/channels/discord.go @@ -3,7 +3,6 @@ package channels import ( "context" "fmt" - "os" "strings" "sync" "time" @@ -210,19 +209,10 @@ func (c *DiscordChannel) handleMessage(s *discordgo.Session, m *discordgo.Messag content := m.Content content = c.stripBotMention(content) mediaPaths := make([]string, 0, len(m.Attachments)) - localFiles := make([]string, 0, len(m.Attachments)) - - // Ensure temp files are cleaned up when function returns - defer func() { - for _, file := range localFiles { - if err := os.Remove(file); err != nil { - logger.DebugCF("discord", "Failed to cleanup temp file", map[string]any{ - "file": file, - "error": err.Error(), - }) - } - } - }() + // Note: media files in os.TempDir()/picoclaw_media/ are not cleaned up here + // because HandleMessage publishes to an async message bus. The consumer + // goroutine may still need these files after this function returns. + // Temp files are managed by OS temp directory lifecycle. for _, attachment := range m.Attachments { isAudio := utils.IsAudioFile(attachment.Filename, attachment.ContentType) @@ -230,8 +220,7 @@ func (c *DiscordChannel) handleMessage(s *discordgo.Session, m *discordgo.Messag if isAudio { localPath := c.downloadAttachment(attachment.URL, attachment.Filename) if localPath != "" { - localFiles = append(localFiles, localPath) - + mediaPaths = append(mediaPaths, localPath) transcribedText := "" if c.transcriber != nil && c.transcriber.IsAvailable() { ctx, cancel := context.WithTimeout(c.getContext(), transcriptionTimeout) diff --git a/pkg/channels/line.go b/pkg/channels/line.go index 9f7d2bde0..ec7ac9e72 100644 --- a/pkg/channels/line.go +++ b/pkg/channels/line.go @@ -10,7 +10,6 @@ import ( "fmt" "io" "net/http" - "os" "strings" "sync" "time" @@ -307,18 +306,10 @@ func (c *LINEChannel) processEvent(event lineEvent) { var content string var mediaPaths []string - localFiles := []string{} - - defer func() { - for _, file := range localFiles { - if err := os.Remove(file); err != nil { - logger.DebugCF("line", "Failed to cleanup temp file", map[string]interface{}{ - "file": file, - "error": err.Error(), - }) - } - } - }() + // Note: media files in os.TempDir()/picoclaw_media/ are not cleaned up here + // because HandleMessage publishes to an async message bus. The consumer + // goroutine may still need these files after this function returns. + // Temp files are managed by OS temp directory lifecycle. switch msg.Type { case "text": @@ -330,21 +321,18 @@ func (c *LINEChannel) processEvent(event lineEvent) { case "image": localPath := c.downloadContent(msg.ID, "image.jpg") if localPath != "" { - localFiles = append(localFiles, localPath) mediaPaths = append(mediaPaths, localPath) content = "[image]" } case "audio": localPath := c.downloadContent(msg.ID, "audio.m4a") if localPath != "" { - localFiles = append(localFiles, localPath) mediaPaths = append(mediaPaths, localPath) content = "[audio]" } case "video": localPath := c.downloadContent(msg.ID, "video.mp4") if localPath != "" { - localFiles = append(localFiles, localPath) mediaPaths = append(mediaPaths, localPath) content = "[video]" } diff --git a/pkg/channels/onebot.go b/pkg/channels/onebot.go index 06186f783..feeb930af 100644 --- a/pkg/channels/onebot.go +++ b/pkg/channels/onebot.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "os" "strconv" "strings" "sync" @@ -571,7 +570,6 @@ type parseMessageResult struct { Text string IsBotMentioned bool Media []string - LocalFiles []string ReplyTo string } @@ -603,7 +601,6 @@ func (c *OneBotChannel) parseMessageSegments(raw json.RawMessage, selfID int64) mentioned := false selfIDStr := strconv.FormatInt(selfID, 10) var media []string - var localFiles []string var replyTo string for _, seg := range segments { @@ -642,7 +639,6 @@ func (c *OneBotChannel) parseMessageSegments(raw json.RawMessage, selfID int64) }) if localPath != "" { media = append(media, localPath) - localFiles = append(localFiles, localPath) textParts = append(textParts, fmt.Sprintf("[%s]", segType)) } } @@ -656,7 +652,6 @@ func (c *OneBotChannel) parseMessageSegments(raw json.RawMessage, selfID int64) LoggerPrefix: "onebot", }) if localPath != "" { - localFiles = append(localFiles, localPath) if c.transcriber != nil && c.transcriber.IsAvailable() { tctx, tcancel := context.WithTimeout(c.ctx, 30*time.Second) result, err := c.transcriber.Transcribe(tctx, localPath) @@ -703,7 +698,6 @@ func (c *OneBotChannel) parseMessageSegments(raw json.RawMessage, selfID int64) Text: strings.TrimSpace(strings.Join(textParts, "")), IsBotMentioned: mentioned, Media: media, - LocalFiles: localFiles, ReplyTo: replyTo, } } @@ -824,19 +818,10 @@ func (c *OneBotChannel) handleMessage(raw *oneBotRawEvent) { } } - // Clean up temp files when done - if len(parsed.LocalFiles) > 0 { - defer func() { - for _, f := range parsed.LocalFiles { - if err := os.Remove(f); err != nil { - logger.DebugCF("onebot", "Failed to remove temp file", map[string]interface{}{ - "path": f, - "error": err.Error(), - }) - } - } - }() - } + // Note: media files in os.TempDir()/picoclaw_media/ are not cleaned up here + // because HandleMessage publishes to an async message bus. The consumer + // goroutine may still need these files after this function returns. + // Temp files are managed by OS temp directory lifecycle. if c.isDuplicate(messageID) { logger.DebugCF("onebot", "Duplicate message, skipping", map[string]interface{}{ diff --git a/pkg/channels/slack.go b/pkg/channels/slack.go index 0060972ed..f7a337195 100644 --- a/pkg/channels/slack.go +++ b/pkg/channels/slack.go @@ -3,7 +3,6 @@ package channels import ( "context" "fmt" - "os" "strings" "sync" "time" @@ -232,19 +231,10 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) { content = c.stripBotMention(content) var mediaPaths []string - localFiles := []string{} // 跟踪需要清理的本地文件 - - // 确保临时文件在函数返回时被清理 - defer func() { - for _, file := range localFiles { - if err := os.Remove(file); err != nil { - logger.DebugCF("slack", "Failed to cleanup temp file", map[string]interface{}{ - "file": file, - "error": err.Error(), - }) - } - } - }() + // Note: media files in os.TempDir()/picoclaw_media/ are not cleaned up here + // because HandleMessage publishes to an async message bus. The consumer + // goroutine may still need these files after this function returns. + // Temp files are managed by OS temp directory lifecycle. if ev.Message != nil && len(ev.Message.Files) > 0 { for _, file := range ev.Message.Files { @@ -252,7 +242,6 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) { if localPath == "" { continue } - localFiles = append(localFiles, localPath) mediaPaths = append(mediaPaths, localPath) if utils.IsAudioFile(file.Name, file.Mimetype) && c.transcriber != nil && c.transcriber.IsAvailable() { diff --git a/pkg/channels/telegram.go b/pkg/channels/telegram.go index 20bbf6830..c64fb7126 100644 --- a/pkg/channels/telegram.go +++ b/pkg/channels/telegram.go @@ -221,19 +221,10 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes content := "" mediaPaths := []string{} - localFiles := []string{} // 跟踪需要清理的本地文件 - - // 确保临时文件在函数返回时被清理 - defer func() { - for _, file := range localFiles { - if err := os.Remove(file); err != nil { - logger.DebugCF("telegram", "Failed to cleanup temp file", map[string]interface{}{ - "file": file, - "error": err.Error(), - }) - } - } - }() + // Note: media files in os.TempDir()/picoclaw_media/ are not cleaned up here + // because HandleMessage publishes to an async message bus. The consumer + // goroutine may still need these files after this function returns. + // Temp files are managed by OS temp directory lifecycle. if message.Text != "" { content += message.Text @@ -250,7 +241,6 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes photo := message.Photo[len(message.Photo)-1] photoPath := c.downloadPhoto(ctx, photo.FileID) if photoPath != "" { - localFiles = append(localFiles, photoPath) mediaPaths = append(mediaPaths, photoPath) if content != "" { content += "\n" @@ -262,7 +252,6 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes if message.Voice != nil { voicePath := c.downloadFile(ctx, message.Voice.FileID, ".ogg") if voicePath != "" { - localFiles = append(localFiles, voicePath) mediaPaths = append(mediaPaths, voicePath) transcribedText := "" @@ -297,7 +286,6 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes if message.Audio != nil { audioPath := c.downloadFile(ctx, message.Audio.FileID, ".mp3") if audioPath != "" { - localFiles = append(localFiles, audioPath) mediaPaths = append(mediaPaths, audioPath) if content != "" { content += "\n" @@ -309,7 +297,6 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes if message.Document != nil { docPath := c.downloadFile(ctx, message.Document.FileID, "") if docPath != "" { - localFiles = append(localFiles, docPath) mediaPaths = append(mediaPaths, docPath) if content != "" { content += "\n"