contacts: calls on the Contrack timeline, summaries opt-in (C5)
Spec 11 §4.4: the contrackLog job puts each connected person's calls with their Contrack contacts on that contact's timeline once (contrack_call_log, migration 0011), from when they connected. With summaries on (My settings → Contacts, needs call summaries), the escaped summary and action items go in, and the first action item becomes a follow-up due the next day. Waits up to 30 min for a summary; Contrack down is retried, refusals noted once. Tested against a fake and a real Contrack 2.0.2.
This commit is contained in:
1 parent
b05361e840
commit
e6a1373f36
19 files changed
+5096
-13
No files matched your search
@@ -3,10 +3,11 @@
|
||||
import { existsSync } from 'node:fs';
|
||||
import { mkdir, rm } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { and, desc, eq, isNull } from 'drizzle-orm';
|
||||
import { and, desc, eq, isNull, sql } from 'drizzle-orm';
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
ContrackOptionsSchema,
|
||||
GREETINGS,
|
||||
MeUpdateSchema,
|
||||
type ListResponse,
|
||||
@@ -133,10 +134,18 @@ export async function meRoutes(app: FastifyInstance) {
|
||||
if (!c) return null;
|
||||
const token = await tokenFor(db, c, userId);
|
||||
const book = token ? await contrackBook(db, c, userId) : null;
|
||||
const [u] = await db
|
||||
.select({ logCalls: users.contrackLogCalls, logSummaries: users.contrackLogSummaries })
|
||||
.from(users)
|
||||
.where(eq(users.id, userId));
|
||||
return {
|
||||
connected: !!book && !book.error,
|
||||
tokenPrefix: token ? `${token.slice(0, 12)}…` : null,
|
||||
publicUrl: c.publicUrl,
|
||||
logCalls: u?.logCalls ?? true,
|
||||
logSummaries: u?.logSummaries ?? false,
|
||||
summariesAvailable:
|
||||
(await getSetting(db, 'ai.callTranscripts')) && (await getSetting(db, 'ai.callSummaries')),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -148,9 +157,13 @@ export async function meRoutes(app: FastifyInstance) {
|
||||
await contrackCheck(c.url, token).catch((err: Error) => {
|
||||
throw badRequest(err.message);
|
||||
});
|
||||
// Calls are logged from now on, not the ones before (11 §4.4).
|
||||
await db
|
||||
.update(users)
|
||||
.set({ contrackTokenEnc: encrypt(token) })
|
||||
.set({
|
||||
contrackTokenEnc: encrypt(token),
|
||||
contrackLogSince: sql`coalesce(${users.contrackLogSince}, now())`,
|
||||
})
|
||||
.where(eq(users.id, req.user!.id));
|
||||
forgetBook(req.user!.id);
|
||||
await audit(db, {
|
||||
@@ -164,6 +177,25 @@ export async function meRoutes(app: FastifyInstance) {
|
||||
return myState(req.user!.id);
|
||||
});
|
||||
|
||||
/** Log my calls in Contrack, with or without their summaries (11 §4.4). */
|
||||
app.patch('/api/me/contrack', async (req) => {
|
||||
const o = ContrackOptionsSchema.parse(req.body);
|
||||
const [u] = await db
|
||||
.select({ logCalls: users.contrackLogCalls })
|
||||
.from(users)
|
||||
.where(eq(users.id, req.user!.id));
|
||||
await db
|
||||
.update(users)
|
||||
.set({
|
||||
...(o.logCalls !== undefined ? { contrackLogCalls: o.logCalls } : {}),
|
||||
...(o.logSummaries !== undefined ? { contrackLogSummaries: o.logSummaries } : {}),
|
||||
// Turned back on: from now, not the calls while it was off.
|
||||
...(o.logCalls && !u?.logCalls ? { contrackLogSince: new Date() } : {}),
|
||||
})
|
||||
.where(eq(users.id, req.user!.id));
|
||||
return myState(req.user!.id);
|
||||
});
|
||||
|
||||
app.delete('/api/me/contrack', async (req) => {
|
||||
await db.update(users).set({ contrackTokenEnc: null }).where(eq(users.id, req.user!.id));
|
||||
forgetBook(req.user!.id);
|
||||
|
||||
Reference in new issue
Block a user