Files
tower/apps/worker/src/main.ts
T

92 lines
2.8 KiB
TypeScript

import { PrismaClient } from '@prisma/client';
import { createLogger } from '@tower/logger';
import { validateEnv } from '@tower/config';
import { IngestJobData } from '@tower/types';
import { createWhatsAppSession } from './whatsapp/session';
import { normalizeMessage } from './whatsapp/normalizer';
import { detectTags, isFlagged } from './whatsapp/tag-detector';
import { syncGroups } from './whatsapp/group-sync';
import { createIngestQueue } from './queues/ingest.queue';
import { createIngestWorker } from './queues/ingest.processor';
const logger = createLogger('tower-worker');
async function bootstrap() {
const env = validateEnv();
const prisma = new PrismaClient();
await prisma.$connect();
const adminJids = env.TOWER_ADMIN_JIDS
? env.TOWER_ADMIN_JIDS.split(',').map((j) => j.trim()).filter(Boolean)
: [];
const ingestQueue = createIngestQueue(env.REDIS_URL);
const ingestWorker = createIngestWorker(env.REDIS_URL, prisma);
ingestWorker.on('completed', (job) => {
logger.info({ jobId: job.id }, 'Ingest job completed');
});
ingestWorker.on('failed', (job, err) => {
logger.error({ jobId: job?.id, err }, 'Ingest job failed');
});
let groupMap = new Map<string, string>();
await createWhatsAppSession(
env.WHATSAPP_SESSION_PATH,
async (msg) => {
const normalized = normalizeMessage(msg);
if (!normalized) return;
const tags = detectTags(normalized.content, normalized.senderJid, adminJids);
if (!isFlagged(tags)) return;
const sourceGroupId = groupMap.get(normalized.sourceGroupJid);
if (!sourceGroupId) {
logger.warn({ jid: normalized.sourceGroupJid }, 'Unknown group — skipping message');
return;
}
const jobData: IngestJobData = {
platformMsgId: normalized.platformMsgId,
platform: 'whatsapp',
sourceGroupId,
senderJid: normalized.senderJid,
senderName: normalized.senderName,
content: normalized.content,
tags,
};
await ingestQueue.add('ingest', jobData, {
attempts: 3,
backoff: { type: 'exponential', delay: 1000 },
});
logger.info({ platformMsgId: normalized.platformMsgId, tags }, 'Message enqueued');
},
async (groups) => {
logger.info({ count: Object.keys(groups).length }, 'Syncing groups');
groupMap = await syncGroups(groups, prisma);
logger.info({ count: groupMap.size }, 'Groups synced');
},
);
logger.info('Tower worker ready');
const shutdown = async () => {
logger.info('Shutting down...');
await ingestWorker.close();
await ingestQueue.close();
await prisma.$disconnect();
process.exit(0);
};
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
}
bootstrap().catch((err) => {
console.error('Worker failed to start', err);
process.exit(1);
});