From 706554a5d35a78c1d7702c699371e047884f80f2 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Thu, 4 Dec 2025 12:24:29 +0100 Subject: [PATCH] fix(mom): private channel messages not being logged - Add message.groups to required bot events in README - Add groups:history and groups:read to required scopes in README - app_mention handler now logs messages directly instead of relying on message event - Add deduplication in ChannelStore.logMessage() to prevent double-logging - Remove redundant current message append in agent.ts (already in log) --- packages/mom/CHANGELOG.md | 8 ++++++++ packages/mom/README.md | 3 +++ packages/mom/dev.sh | 30 ++++++++++++++++++++++++++++++ packages/mom/src/agent.ts | 10 +--------- packages/mom/src/slack.ts | 10 +++++++++- packages/mom/src/store.ts | 17 ++++++++++++++++- 6 files changed, 67 insertions(+), 11 deletions(-) create mode 100755 packages/mom/dev.sh diff --git a/packages/mom/CHANGELOG.md b/packages/mom/CHANGELOG.md index b61b97b1..c2a79f0b 100644 --- a/packages/mom/CHANGELOG.md +++ b/packages/mom/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +### Fixed + +- Private channel messages not being logged + - Added `message.groups` to required bot events in README + - Added `groups:history` and `groups:read` to required scopes in README + - `app_mention` handler now logs messages directly instead of relying on `message` event + - Added deduplication in `ChannelStore.logMessage()` to prevent double-logging + ### Added - Message backfill on startup (#103) diff --git a/packages/mom/README.md b/packages/mom/README.md index f6f7d922..b893affb 100644 --- a/packages/mom/README.md +++ b/packages/mom/README.md @@ -31,6 +31,8 @@ npm install @mariozechner/pi-mom - `chat:write` - `files:read` - `files:write` + - `groups:history` + - `groups:read` - `im:history` - `im:read` - `im:write` @@ -38,6 +40,7 @@ npm install @mariozechner/pi-mom 5. **Subscribe to Bot Events** (Event Subscriptions): - `app_mention` - `message.channels` + - `message.groups` - `message.im` 6. Install the app to your workspace. Get the **Bot User OAuth Token**. This is `MOM_SLACK_BOT_TOKEN` 7. Add mom to any channels where you want her to operate (she'll only see messages in channels she's added to) diff --git a/packages/mom/dev.sh b/packages/mom/dev.sh new file mode 100755 index 00000000..689ca171 --- /dev/null +++ b/packages/mom/dev.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e + +CONTAINER_NAME="mom-sandbox" +DATA_DIR="$(pwd)/data" + +# Create data directory if it doesn't exist +mkdir -p "$DATA_DIR" + +# Check if container exists +if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + # Check if it's running + if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then + echo "Starting existing container: $CONTAINER_NAME" + docker start "$CONTAINER_NAME" + else + echo "Container $CONTAINER_NAME already running" + fi +else + echo "Creating container: $CONTAINER_NAME" + docker run -d \ + --name "$CONTAINER_NAME" \ + -v "$DATA_DIR:/workspace" \ + alpine:latest \ + tail -f /dev/null +fi + +# Run mom with tsx watch mode +echo "Starting mom in dev mode..." +npx tsx --watch-path src --watch src/main.ts --sandbox=docker:$CONTAINER_NAME ./data diff --git a/packages/mom/src/agent.ts b/packages/mom/src/agent.ts index ba3fd762..fa04f383 100644 --- a/packages/mom/src/agent.ts +++ b/packages/mom/src/agent.ts @@ -364,15 +364,7 @@ export function createAgentRunner(sandboxConfig: SandboxConfig): AgentRunner { const channelId = ctx.message.channel; const workspacePath = executor.getWorkspacePath(channelDir.replace(`/${channelId}`, "")); - const recentMessagesFromLog = getRecentMessages(channelDir, 50); - - // Append the current message (may not be in log yet due to race condition - // between app_mention and message events) - const currentMsgDate = new Date(parseFloat(ctx.message.ts) * 1000).toISOString().substring(0, 19); - const currentMsgUser = ctx.message.userName || ctx.message.user; - const currentMsgAttachments = ctx.message.attachments.map((a) => a.local).join(","); - const currentMsgLine = `${currentMsgDate}\t${currentMsgUser}\t${ctx.message.rawText}\t${currentMsgAttachments}`; - const recentMessages = recentMessagesFromLog + "\n" + currentMsgLine; + const recentMessages = getRecentMessages(channelDir, 50); const memory = getMemory(channelDir); const systemPrompt = buildSystemPrompt( diff --git a/packages/mom/src/slack.ts b/packages/mom/src/slack.ts index 1065ac59..f0cf0007 100644 --- a/packages/mom/src/slack.ts +++ b/packages/mom/src/slack.ts @@ -208,7 +208,6 @@ export class MomBot { private setupEventHandlers(): void { // Handle @mentions in channels - // Note: We don't log here - the message event handler logs all messages this.socketClient.on("app_mention", async ({ event, ack }) => { await ack(); @@ -220,6 +219,15 @@ export class MomBot { files?: Array<{ name: string; url_private_download?: string; url_private?: string }>; }; + // Log the mention message (message event may not fire for all channel types) + await this.logMessage({ + text: slackEvent.text, + channel: slackEvent.channel, + user: slackEvent.user, + ts: slackEvent.ts, + files: slackEvent.files, + }); + const ctx = await this.createContext(slackEvent); await this.handler.onChannelMention(ctx); }); diff --git a/packages/mom/src/store.ts b/packages/mom/src/store.ts index d319cee4..59a5a519 100644 --- a/packages/mom/src/store.ts +++ b/packages/mom/src/store.ts @@ -35,6 +35,9 @@ export class ChannelStore { private botToken: string; private pendingDownloads: PendingDownload[] = []; private isDownloading = false; + // Track recently logged message timestamps to prevent duplicates + // Key: "channelId:ts", automatically cleaned up after 60 seconds + private recentlyLogged = new Map(); constructor(config: ChannelStoreConfig) { this.workingDir = config.workingDir; @@ -107,8 +110,19 @@ export class ChannelStore { /** * Log a message to the channel's log.jsonl + * Returns false if message was already logged (duplicate) */ - async logMessage(channelId: string, message: LoggedMessage): Promise { + async logMessage(channelId: string, message: LoggedMessage): Promise { + // Check for duplicate (same channel + timestamp) + const dedupeKey = `${channelId}:${message.ts}`; + if (this.recentlyLogged.has(dedupeKey)) { + return false; // Already logged + } + + // Mark as logged and schedule cleanup after 60 seconds + this.recentlyLogged.set(dedupeKey, Date.now()); + setTimeout(() => this.recentlyLogged.delete(dedupeKey), 60000); + const logPath = join(this.getChannelDir(channelId), "log.jsonl"); // Ensure message has a date field @@ -127,6 +141,7 @@ export class ChannelStore { const line = JSON.stringify(message) + "\n"; await appendFile(logPath, line, "utf-8"); + return true; } /**