feat: add is_bot_message column and support dedicated phone numbers (#235)
* feat: add is_bot_message column and support dedicated phone numbers Replace fragile content-prefix bot detection with an explicit is_bot_message database column. The old prefix check (content NOT LIKE 'Andy:%') is kept as a backstop for pre-migration messages. - Add is_bot_message column with automatic backfill migration - Add ASSISTANT_HAS_OWN_NUMBER env var to skip name prefix when the assistant has its own WhatsApp number - Move prefix logic into WhatsApp channel (no longer a router concern) - Remove prefixAssistantName from Channel interface - Load .env via dotenv so launchd-managed processes pick up config - WhatsApp bot detection: fromMe for own number, prefix match for shared Based on #160 and #173. Co-Authored-By: Stefan Gasser <stefan@stefangasser.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract shared .env parser and remove dotenv dependency Extract .env parsing into src/env.ts, used by both config.ts and container-runner.ts. Reads only requested keys without loading secrets into process.env, avoiding leaking API keys to child processes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Stefan Gasser <stefan@stefangasser.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
35
src/db.ts
35
src/db.ts
@@ -2,7 +2,7 @@ import Database from 'better-sqlite3';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { DATA_DIR, STORE_DIR } from './config.js';
|
||||
import { ASSISTANT_NAME, DATA_DIR, STORE_DIR } from './config.js';
|
||||
import { NewMessage, RegisteredGroup, ScheduledTask, TaskRunLog } from './types.js';
|
||||
|
||||
let db: Database.Database;
|
||||
@@ -22,6 +22,7 @@ function createSchema(database: Database.Database): void {
|
||||
content TEXT,
|
||||
timestamp TEXT,
|
||||
is_from_me INTEGER,
|
||||
is_bot_message INTEGER DEFAULT 0,
|
||||
PRIMARY KEY (id, chat_jid),
|
||||
FOREIGN KEY (chat_jid) REFERENCES chats(jid)
|
||||
);
|
||||
@@ -82,6 +83,19 @@ function createSchema(database: Database.Database): void {
|
||||
} catch {
|
||||
/* column already exists */
|
||||
}
|
||||
|
||||
// Add is_bot_message column if it doesn't exist (migration for existing DBs)
|
||||
try {
|
||||
database.exec(
|
||||
`ALTER TABLE messages ADD COLUMN is_bot_message INTEGER DEFAULT 0`,
|
||||
);
|
||||
// Backfill: mark existing bot messages that used the content prefix pattern
|
||||
database.prepare(
|
||||
`UPDATE messages SET is_bot_message = 1 WHERE content LIKE ?`,
|
||||
).run(`${ASSISTANT_NAME}:%`);
|
||||
} catch {
|
||||
/* column already exists */
|
||||
}
|
||||
}
|
||||
|
||||
export function initDatabase(): void {
|
||||
@@ -194,7 +208,7 @@ export function setLastGroupSync(): void {
|
||||
*/
|
||||
export function storeMessage(msg: NewMessage): void {
|
||||
db.prepare(
|
||||
`INSERT OR REPLACE INTO messages (id, chat_jid, sender, sender_name, content, timestamp, is_from_me) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||
`INSERT OR REPLACE INTO messages (id, chat_jid, sender, sender_name, content, timestamp, is_from_me, is_bot_message) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
).run(
|
||||
msg.id,
|
||||
msg.chat_jid,
|
||||
@@ -203,6 +217,7 @@ export function storeMessage(msg: NewMessage): void {
|
||||
msg.content,
|
||||
msg.timestamp,
|
||||
msg.is_from_me ? 1 : 0,
|
||||
msg.is_bot_message ? 1 : 0,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -217,9 +232,10 @@ export function storeMessageDirect(msg: {
|
||||
content: string;
|
||||
timestamp: string;
|
||||
is_from_me: boolean;
|
||||
is_bot_message?: boolean;
|
||||
}): void {
|
||||
db.prepare(
|
||||
`INSERT OR REPLACE INTO messages (id, chat_jid, sender, sender_name, content, timestamp, is_from_me) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||
`INSERT OR REPLACE INTO messages (id, chat_jid, sender, sender_name, content, timestamp, is_from_me, is_bot_message) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
).run(
|
||||
msg.id,
|
||||
msg.chat_jid,
|
||||
@@ -228,6 +244,7 @@ export function storeMessageDirect(msg: {
|
||||
msg.content,
|
||||
msg.timestamp,
|
||||
msg.is_from_me ? 1 : 0,
|
||||
msg.is_bot_message ? 1 : 0,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -239,11 +256,13 @@ export function getNewMessages(
|
||||
if (jids.length === 0) return { messages: [], newTimestamp: lastTimestamp };
|
||||
|
||||
const placeholders = jids.map(() => '?').join(',');
|
||||
// Filter out bot's own messages by checking content prefix (not is_from_me, since user shares the account)
|
||||
// Filter bot messages using both the is_bot_message flag AND the content
|
||||
// prefix as a backstop for messages written before the migration ran.
|
||||
const sql = `
|
||||
SELECT id, chat_jid, sender, sender_name, content, timestamp
|
||||
FROM messages
|
||||
WHERE timestamp > ? AND chat_jid IN (${placeholders}) AND content NOT LIKE ?
|
||||
WHERE timestamp > ? AND chat_jid IN (${placeholders})
|
||||
AND is_bot_message = 0 AND content NOT LIKE ?
|
||||
ORDER BY timestamp
|
||||
`;
|
||||
|
||||
@@ -264,11 +283,13 @@ export function getMessagesSince(
|
||||
sinceTimestamp: string,
|
||||
botPrefix: string,
|
||||
): NewMessage[] {
|
||||
// Filter out bot's own messages by checking content prefix
|
||||
// Filter bot messages using both the is_bot_message flag AND the content
|
||||
// prefix as a backstop for messages written before the migration ran.
|
||||
const sql = `
|
||||
SELECT id, chat_jid, sender, sender_name, content, timestamp
|
||||
FROM messages
|
||||
WHERE chat_jid = ? AND timestamp > ? AND content NOT LIKE ?
|
||||
WHERE chat_jid = ? AND timestamp > ?
|
||||
AND is_bot_message = 0 AND content NOT LIKE ?
|
||||
ORDER BY timestamp
|
||||
`;
|
||||
return db
|
||||
|
||||
Reference in New Issue
Block a user