fireflies-api 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/cli/index.cjs +1435 -217
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +1436 -222
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +1372 -392
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +462 -3
- package/dist/index.d.ts +462 -3
- package/dist/index.js +1357 -393
- package/dist/index.js.map +1 -1
- package/dist/middleware/express.cjs +459 -16
- package/dist/middleware/express.cjs.map +1 -1
- package/dist/middleware/express.d.cts +2 -2
- package/dist/middleware/express.d.ts +2 -2
- package/dist/middleware/express.js +459 -16
- package/dist/middleware/express.js.map +1 -1
- package/dist/middleware/fastify.cjs +459 -16
- package/dist/middleware/fastify.cjs.map +1 -1
- package/dist/middleware/fastify.d.cts +2 -2
- package/dist/middleware/fastify.d.ts +2 -2
- package/dist/middleware/fastify.js +459 -16
- package/dist/middleware/fastify.js.map +1 -1
- package/dist/middleware/hono.cjs +459 -16
- package/dist/middleware/hono.cjs.map +1 -1
- package/dist/middleware/hono.d.cts +2 -2
- package/dist/middleware/hono.d.ts +2 -2
- package/dist/middleware/hono.js +459 -16
- package/dist/middleware/hono.js.map +1 -1
- package/dist/templates/digest/compact.md +8 -0
- package/dist/templates/digest/default.md +44 -0
- package/dist/templates/digest/executive.md +22 -0
- package/dist/{types-CaHcwnKw.d.ts → types-BMzVSd6w.d.ts} +1 -1
- package/dist/{types-BX-3JcRI.d.cts → types-BeXRmVD7.d.cts} +1 -1
- package/dist/{types-DIPZmUl3.d.ts → types-D2XsCR5R.d.ts} +120 -1
- package/dist/{types-C_XxdRd1.d.cts → types-zVGqyFzP.d.cts} +120 -1
- package/package.json +8 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Handler } from 'hono';
|
|
2
|
-
import { W as WebhookMiddlewareOptions } from '../types-
|
|
3
|
-
import '../types-
|
|
2
|
+
import { W as WebhookMiddlewareOptions } from '../types-BeXRmVD7.cjs';
|
|
3
|
+
import '../types-zVGqyFzP.cjs';
|
|
4
4
|
import '../action-items-CC9yUxHY.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Handler } from 'hono';
|
|
2
|
-
import { W as WebhookMiddlewareOptions } from '../types-
|
|
3
|
-
import '../types-
|
|
2
|
+
import { W as WebhookMiddlewareOptions } from '../types-BMzVSd6w.js';
|
|
3
|
+
import '../types-D2XsCR5R.js';
|
|
4
4
|
import '../action-items-CC9yUxHY.js';
|
|
5
5
|
|
|
6
6
|
/**
|
package/dist/middleware/hono.js
CHANGED
|
@@ -289,8 +289,8 @@ async function retry(fn, options) {
|
|
|
289
289
|
if (attempt >= maxRetries || !shouldRetry(error, attempt)) {
|
|
290
290
|
throw error;
|
|
291
291
|
}
|
|
292
|
-
const
|
|
293
|
-
await sleep(
|
|
292
|
+
const delay2 = calculateDelay(error, attempt, baseDelay, maxDelay);
|
|
293
|
+
await sleep(delay2);
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
throw lastError;
|
|
@@ -439,9 +439,9 @@ var GraphQLClient = class {
|
|
|
439
439
|
if (!this.rateLimitTracker || !this.rateLimitConfig?.throttle) {
|
|
440
440
|
return;
|
|
441
441
|
}
|
|
442
|
-
const
|
|
443
|
-
if (
|
|
444
|
-
await sleep2(
|
|
442
|
+
const delay2 = this.rateLimitTracker.getThrottleDelay(this.rateLimitConfig.throttle);
|
|
443
|
+
if (delay2 > 0) {
|
|
444
|
+
await sleep2(delay2);
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
/**
|
|
@@ -1095,6 +1095,46 @@ function buildAggregatedResult(items, transcriptsProcessed, transcriptsWithItems
|
|
|
1095
1095
|
};
|
|
1096
1096
|
}
|
|
1097
1097
|
|
|
1098
|
+
// src/helpers/batch.ts
|
|
1099
|
+
async function* batch(items, processor, options = {}) {
|
|
1100
|
+
const { delayMs = 100, handleRateLimit = true, maxRateLimitRetries = 3 } = options;
|
|
1101
|
+
let isFirst = true;
|
|
1102
|
+
for await (const item of items) {
|
|
1103
|
+
if (!isFirst && delayMs > 0) {
|
|
1104
|
+
await delay(delayMs);
|
|
1105
|
+
}
|
|
1106
|
+
isFirst = false;
|
|
1107
|
+
yield await processWithRetry(item, processor, {
|
|
1108
|
+
handleRateLimit,
|
|
1109
|
+
maxRateLimitRetries
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
async function processWithRetry(item, processor, options) {
|
|
1114
|
+
const { handleRateLimit, maxRateLimitRetries } = options;
|
|
1115
|
+
let retries = 0;
|
|
1116
|
+
while (true) {
|
|
1117
|
+
try {
|
|
1118
|
+
const result = await processor(item);
|
|
1119
|
+
return { item, result };
|
|
1120
|
+
} catch (err) {
|
|
1121
|
+
if (handleRateLimit && err instanceof RateLimitError && retries < maxRateLimitRetries) {
|
|
1122
|
+
const waitTime = err.retryAfter ?? 1e3;
|
|
1123
|
+
await delay(waitTime);
|
|
1124
|
+
retries++;
|
|
1125
|
+
continue;
|
|
1126
|
+
}
|
|
1127
|
+
return {
|
|
1128
|
+
item,
|
|
1129
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
function delay(ms) {
|
|
1135
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1098
1138
|
// src/helpers/domain-utils.ts
|
|
1099
1139
|
function extractDomain(email) {
|
|
1100
1140
|
const atIndex = email.indexOf("@");
|
|
@@ -1110,6 +1150,332 @@ function hasExternalParticipants(participants, internalDomain) {
|
|
|
1110
1150
|
});
|
|
1111
1151
|
}
|
|
1112
1152
|
|
|
1153
|
+
// src/helpers/markdown.ts
|
|
1154
|
+
var DEFAULT_OPTIONS2 = {
|
|
1155
|
+
includeMetadata: true,
|
|
1156
|
+
includeSummary: true,
|
|
1157
|
+
includeActionItems: true,
|
|
1158
|
+
actionItemFormat: "checkbox",
|
|
1159
|
+
includeTimestamps: false,
|
|
1160
|
+
speakerFormat: "bold",
|
|
1161
|
+
groupBySpeaker: true
|
|
1162
|
+
};
|
|
1163
|
+
async function transcriptToMarkdown(transcript, options = {}) {
|
|
1164
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
1165
|
+
const sections = [];
|
|
1166
|
+
if (opts.includeMetadata) {
|
|
1167
|
+
sections.push(formatMetadata(transcript));
|
|
1168
|
+
}
|
|
1169
|
+
if (opts.includeSummary && transcript.summary) {
|
|
1170
|
+
sections.push(formatSummary(transcript.summary, opts));
|
|
1171
|
+
}
|
|
1172
|
+
if (transcript.sentences && transcript.sentences.length > 0) {
|
|
1173
|
+
sections.push(formatTranscript(transcript.sentences, opts));
|
|
1174
|
+
}
|
|
1175
|
+
const content = sections.join("\n\n---\n\n");
|
|
1176
|
+
await writeIfOutputPath(content, options.outputPath);
|
|
1177
|
+
return content;
|
|
1178
|
+
}
|
|
1179
|
+
function formatMetadata(transcript) {
|
|
1180
|
+
const lines = [`# ${transcript.title || "Untitled Meeting"}`];
|
|
1181
|
+
if (transcript.dateString) {
|
|
1182
|
+
lines.push(`
|
|
1183
|
+
**Date:** ${formatDate(transcript.dateString)}`);
|
|
1184
|
+
}
|
|
1185
|
+
const duration = calculateDuration(transcript);
|
|
1186
|
+
if (duration > 0) {
|
|
1187
|
+
lines.push(`**Duration:** ${formatDuration(duration)}`);
|
|
1188
|
+
}
|
|
1189
|
+
const participants = getParticipantNames(transcript);
|
|
1190
|
+
if (participants.length > 0) {
|
|
1191
|
+
lines.push(`**Participants:** ${participants.join(", ")}`);
|
|
1192
|
+
}
|
|
1193
|
+
return lines.join("\n");
|
|
1194
|
+
}
|
|
1195
|
+
function calculateDuration(transcript) {
|
|
1196
|
+
if (transcript.sentences && transcript.sentences.length > 0) {
|
|
1197
|
+
const lastSentence = transcript.sentences[transcript.sentences.length - 1];
|
|
1198
|
+
if (lastSentence) {
|
|
1199
|
+
return parseFloat(lastSentence.end_time);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
return transcript.duration || 0;
|
|
1203
|
+
}
|
|
1204
|
+
function formatSummary(summary, opts) {
|
|
1205
|
+
const sections = ["## Summary"];
|
|
1206
|
+
if (summary.gist) {
|
|
1207
|
+
sections.push("", summary.gist);
|
|
1208
|
+
}
|
|
1209
|
+
if (summary.bullet_gist) {
|
|
1210
|
+
const bullets = parseMultilineField(summary.bullet_gist);
|
|
1211
|
+
if (bullets.length > 0) {
|
|
1212
|
+
sections.push("", "### Key Points");
|
|
1213
|
+
sections.push(bullets.map((p) => `- ${p}`).join("\n"));
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
if (opts.includeActionItems && summary.action_items) {
|
|
1217
|
+
const items = parseMultilineField(summary.action_items);
|
|
1218
|
+
if (items.length > 0) {
|
|
1219
|
+
sections.push("", "### Action Items");
|
|
1220
|
+
const prefix = opts.actionItemFormat === "checkbox" ? "- [ ] " : "- ";
|
|
1221
|
+
sections.push(items.map((a) => `${prefix}${a}`).join("\n"));
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
return sections.join("\n");
|
|
1225
|
+
}
|
|
1226
|
+
function formatTranscript(sentences, opts) {
|
|
1227
|
+
const lines = ["## Transcript"];
|
|
1228
|
+
if (opts.groupBySpeaker) {
|
|
1229
|
+
const groups = groupSentencesBySpeaker(sentences);
|
|
1230
|
+
for (const group of groups) {
|
|
1231
|
+
lines.push("", formatSpeakerGroup(group, opts));
|
|
1232
|
+
}
|
|
1233
|
+
} else {
|
|
1234
|
+
for (const sentence of sentences) {
|
|
1235
|
+
lines.push("", formatSentence(sentence, opts));
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
return lines.join("\n");
|
|
1239
|
+
}
|
|
1240
|
+
function groupSentencesBySpeaker(sentences) {
|
|
1241
|
+
const groups = [];
|
|
1242
|
+
let current = null;
|
|
1243
|
+
for (const sentence of sentences) {
|
|
1244
|
+
if (!current || current.speakerName !== sentence.speaker_name) {
|
|
1245
|
+
current = { speakerName: sentence.speaker_name, sentences: [] };
|
|
1246
|
+
groups.push(current);
|
|
1247
|
+
}
|
|
1248
|
+
current.sentences.push(sentence);
|
|
1249
|
+
}
|
|
1250
|
+
return groups;
|
|
1251
|
+
}
|
|
1252
|
+
function formatSpeakerGroup(group, opts) {
|
|
1253
|
+
const speaker = formatSpeakerName(group.speakerName, opts.speakerFormat);
|
|
1254
|
+
const text = group.sentences.map((s) => s.text).join(" ");
|
|
1255
|
+
const firstSentence = group.sentences[0];
|
|
1256
|
+
if (opts.includeTimestamps && firstSentence) {
|
|
1257
|
+
const timestamp = formatTimestamp(firstSentence.start_time);
|
|
1258
|
+
return `${timestamp} ${speaker} ${text}`;
|
|
1259
|
+
}
|
|
1260
|
+
return `${speaker} ${text}`;
|
|
1261
|
+
}
|
|
1262
|
+
function formatSentence(sentence, opts) {
|
|
1263
|
+
const speaker = formatSpeakerName(sentence.speaker_name, opts.speakerFormat);
|
|
1264
|
+
if (opts.includeTimestamps) {
|
|
1265
|
+
const timestamp = formatTimestamp(sentence.start_time);
|
|
1266
|
+
return `${timestamp} ${speaker} ${sentence.text}`;
|
|
1267
|
+
}
|
|
1268
|
+
return `${speaker} ${sentence.text}`;
|
|
1269
|
+
}
|
|
1270
|
+
function formatSpeakerName(name, format) {
|
|
1271
|
+
switch (format) {
|
|
1272
|
+
case "bold":
|
|
1273
|
+
return `**${name}:**`;
|
|
1274
|
+
case "plain":
|
|
1275
|
+
return `${name}:`;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
function formatTimestamp(startTime) {
|
|
1279
|
+
const seconds = parseFloat(startTime);
|
|
1280
|
+
const mins = Math.floor(seconds / 60);
|
|
1281
|
+
const secs = Math.floor(seconds % 60);
|
|
1282
|
+
return `[${mins}:${secs.toString().padStart(2, "0")}]`;
|
|
1283
|
+
}
|
|
1284
|
+
function formatDuration(seconds) {
|
|
1285
|
+
const hours = Math.floor(seconds / 3600);
|
|
1286
|
+
const mins = Math.floor(seconds % 3600 / 60);
|
|
1287
|
+
if (hours > 0) {
|
|
1288
|
+
return `${hours}h ${mins}m`;
|
|
1289
|
+
}
|
|
1290
|
+
return `${mins} minutes`;
|
|
1291
|
+
}
|
|
1292
|
+
function formatDate(isoString) {
|
|
1293
|
+
return new Date(isoString).toLocaleDateString("en-US", {
|
|
1294
|
+
weekday: "long",
|
|
1295
|
+
year: "numeric",
|
|
1296
|
+
month: "long",
|
|
1297
|
+
day: "numeric"
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
function getParticipantNames(transcript) {
|
|
1301
|
+
if (transcript.meeting_attendees?.length) {
|
|
1302
|
+
return transcript.meeting_attendees.map((a) => a.displayName || a.name || a.email).filter(Boolean);
|
|
1303
|
+
}
|
|
1304
|
+
return transcript.speakers?.map((s) => s.name) || [];
|
|
1305
|
+
}
|
|
1306
|
+
function parseMultilineField(value) {
|
|
1307
|
+
return value.split(/\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
1308
|
+
}
|
|
1309
|
+
async function writeIfOutputPath(content, outputPath) {
|
|
1310
|
+
if (outputPath) {
|
|
1311
|
+
const { writeFile } = await import('fs/promises');
|
|
1312
|
+
await writeFile(outputPath, content, "utf-8");
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// src/helpers/export-formats.ts
|
|
1317
|
+
var DEFAULT_TEXT_OPTIONS = {
|
|
1318
|
+
includeTimestamps: false,
|
|
1319
|
+
includeMetadata: true
|
|
1320
|
+
};
|
|
1321
|
+
var DEFAULT_CSV_OPTIONS = {
|
|
1322
|
+
includeHeader: true,
|
|
1323
|
+
delimiter: ","
|
|
1324
|
+
};
|
|
1325
|
+
function transcriptToText(transcript, options = {}) {
|
|
1326
|
+
const opts = { ...DEFAULT_TEXT_OPTIONS, ...options };
|
|
1327
|
+
const sections = [];
|
|
1328
|
+
if (opts.includeMetadata) {
|
|
1329
|
+
sections.push(formatTextMetadata(transcript));
|
|
1330
|
+
}
|
|
1331
|
+
if (transcript.sentences && transcript.sentences.length > 0) {
|
|
1332
|
+
sections.push(formatTextTranscript(transcript.sentences, opts));
|
|
1333
|
+
}
|
|
1334
|
+
return sections.join("\n\n");
|
|
1335
|
+
}
|
|
1336
|
+
function transcriptToCsv(transcript, options = {}) {
|
|
1337
|
+
const opts = { ...DEFAULT_CSV_OPTIONS, ...options };
|
|
1338
|
+
const d = opts.delimiter;
|
|
1339
|
+
const lines = [];
|
|
1340
|
+
if (opts.includeHeader) {
|
|
1341
|
+
lines.push(`timestamp${d}speaker${d}text${d}is_question${d}is_task`);
|
|
1342
|
+
}
|
|
1343
|
+
for (const sentence of transcript.sentences) {
|
|
1344
|
+
const isQuestion = Boolean(sentence.ai_filters?.question);
|
|
1345
|
+
const isTask = Boolean(sentence.ai_filters?.task);
|
|
1346
|
+
const row = [
|
|
1347
|
+
sentence.start_time,
|
|
1348
|
+
escapeCsvField(sentence.speaker_name, d),
|
|
1349
|
+
escapeCsvField(sentence.text, d),
|
|
1350
|
+
String(isQuestion),
|
|
1351
|
+
String(isTask)
|
|
1352
|
+
];
|
|
1353
|
+
lines.push(row.join(d));
|
|
1354
|
+
}
|
|
1355
|
+
return lines.join("\n");
|
|
1356
|
+
}
|
|
1357
|
+
function sanitizeFilename(title) {
|
|
1358
|
+
if (!title.trim()) {
|
|
1359
|
+
return "untitled";
|
|
1360
|
+
}
|
|
1361
|
+
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 100);
|
|
1362
|
+
}
|
|
1363
|
+
function generateExportFilename(transcript, extension) {
|
|
1364
|
+
const sanitizedTitle = sanitizeFilename(transcript.title);
|
|
1365
|
+
let datePrefix = "";
|
|
1366
|
+
if (transcript.dateString) {
|
|
1367
|
+
try {
|
|
1368
|
+
const date = new Date(transcript.dateString);
|
|
1369
|
+
if (!Number.isNaN(date.getTime())) {
|
|
1370
|
+
datePrefix = `${date.toISOString().slice(0, 10)}-`;
|
|
1371
|
+
}
|
|
1372
|
+
} catch {
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return `${datePrefix}${sanitizedTitle}.${extension}`;
|
|
1376
|
+
}
|
|
1377
|
+
async function exportTranscript(transcript, format) {
|
|
1378
|
+
switch (format) {
|
|
1379
|
+
case "markdown":
|
|
1380
|
+
return transcriptToMarkdown(transcript);
|
|
1381
|
+
case "json":
|
|
1382
|
+
return JSON.stringify(transcript, null, 2);
|
|
1383
|
+
case "txt":
|
|
1384
|
+
return transcriptToText(transcript);
|
|
1385
|
+
case "csv":
|
|
1386
|
+
return transcriptToCsv(transcript);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
async function createZipArchive(files) {
|
|
1390
|
+
const archiver = await import('archiver');
|
|
1391
|
+
const { Writable } = await import('stream');
|
|
1392
|
+
return new Promise((resolve, reject) => {
|
|
1393
|
+
const chunks = [];
|
|
1394
|
+
const archive = archiver.default("zip", { zlib: { level: 9 } });
|
|
1395
|
+
const writable = new Writable({
|
|
1396
|
+
write(chunk, _encoding, callback) {
|
|
1397
|
+
chunks.push(chunk);
|
|
1398
|
+
callback();
|
|
1399
|
+
}
|
|
1400
|
+
});
|
|
1401
|
+
writable.on("finish", () => {
|
|
1402
|
+
resolve(Buffer.concat(chunks));
|
|
1403
|
+
});
|
|
1404
|
+
archive.on("error", reject);
|
|
1405
|
+
archive.pipe(writable);
|
|
1406
|
+
for (const file of files) {
|
|
1407
|
+
archive.append(file.content, { name: file.filename });
|
|
1408
|
+
}
|
|
1409
|
+
archive.finalize();
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
function formatTextMetadata(transcript) {
|
|
1413
|
+
const lines = [];
|
|
1414
|
+
lines.push(transcript.title || "Untitled Meeting");
|
|
1415
|
+
if (transcript.dateString) {
|
|
1416
|
+
lines.push(`Date: ${formatDate2(transcript.dateString)}`);
|
|
1417
|
+
}
|
|
1418
|
+
const participants = getParticipantNames2(transcript);
|
|
1419
|
+
if (participants.length > 0) {
|
|
1420
|
+
lines.push(`Participants: ${participants.join(", ")}`);
|
|
1421
|
+
}
|
|
1422
|
+
return lines.join("\n");
|
|
1423
|
+
}
|
|
1424
|
+
function formatTextTranscript(sentences, opts) {
|
|
1425
|
+
const groups = groupSentencesBySpeaker2(sentences);
|
|
1426
|
+
const lines = [];
|
|
1427
|
+
for (const group of groups) {
|
|
1428
|
+
const text = group.sentences.map((s) => s.text).join(" ");
|
|
1429
|
+
const speaker = group.speakerName || "Unknown";
|
|
1430
|
+
const firstSentence = group.sentences[0];
|
|
1431
|
+
if (opts.includeTimestamps && firstSentence) {
|
|
1432
|
+
const timestamp = formatTimestamp2(firstSentence.start_time);
|
|
1433
|
+
lines.push(`${timestamp} ${speaker}: ${text}`);
|
|
1434
|
+
} else {
|
|
1435
|
+
lines.push(`${speaker}: ${text}`);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
return lines.join("\n");
|
|
1439
|
+
}
|
|
1440
|
+
function groupSentencesBySpeaker2(sentences) {
|
|
1441
|
+
const groups = [];
|
|
1442
|
+
let current = null;
|
|
1443
|
+
for (const sentence of sentences) {
|
|
1444
|
+
if (!current || current.speakerName !== sentence.speaker_name) {
|
|
1445
|
+
current = { speakerName: sentence.speaker_name, sentences: [] };
|
|
1446
|
+
groups.push(current);
|
|
1447
|
+
}
|
|
1448
|
+
current.sentences.push(sentence);
|
|
1449
|
+
}
|
|
1450
|
+
return groups;
|
|
1451
|
+
}
|
|
1452
|
+
function formatTimestamp2(startTime) {
|
|
1453
|
+
const seconds = parseFloat(startTime);
|
|
1454
|
+
const mins = Math.floor(seconds / 60);
|
|
1455
|
+
const secs = Math.floor(seconds % 60);
|
|
1456
|
+
return `[${mins}:${secs.toString().padStart(2, "0")}]`;
|
|
1457
|
+
}
|
|
1458
|
+
function formatDate2(isoString) {
|
|
1459
|
+
return new Date(isoString).toLocaleDateString("en-US", {
|
|
1460
|
+
weekday: "long",
|
|
1461
|
+
year: "numeric",
|
|
1462
|
+
month: "long",
|
|
1463
|
+
day: "numeric"
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
function getParticipantNames2(transcript) {
|
|
1467
|
+
if (transcript.meeting_attendees?.length) {
|
|
1468
|
+
return transcript.meeting_attendees.map((a) => a.displayName || a.name || a.email).filter(Boolean);
|
|
1469
|
+
}
|
|
1470
|
+
return transcript.speakers?.map((s) => s.name) || [];
|
|
1471
|
+
}
|
|
1472
|
+
function escapeCsvField(field, delimiter) {
|
|
1473
|
+
if (field.includes('"') || field.includes(delimiter) || field.includes("\n")) {
|
|
1474
|
+
return `"${field.replace(/"/g, '""')}"`;
|
|
1475
|
+
}
|
|
1476
|
+
return field;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1113
1479
|
// src/helpers/meeting-insights.ts
|
|
1114
1480
|
function analyzeMeetings(transcripts, options = {}) {
|
|
1115
1481
|
const { speakers, groupBy, topSpeakersCount = 10, topParticipantsCount = 10 } = options;
|
|
@@ -1583,6 +1949,21 @@ var TRANSCRIPT_LIST_FIELDS = `
|
|
|
1583
1949
|
summary_status
|
|
1584
1950
|
}
|
|
1585
1951
|
`;
|
|
1952
|
+
function buildListFields(params) {
|
|
1953
|
+
const includeSentences = params?.includeSentences === true;
|
|
1954
|
+
const includeSummary = params?.includeSummary === true;
|
|
1955
|
+
if (!includeSentences && !includeSummary) {
|
|
1956
|
+
return TRANSCRIPT_LIST_FIELDS;
|
|
1957
|
+
}
|
|
1958
|
+
let fields = TRANSCRIPT_BASE_FIELDS;
|
|
1959
|
+
if (includeSentences) {
|
|
1960
|
+
fields += SENTENCES_FIELDS;
|
|
1961
|
+
}
|
|
1962
|
+
if (includeSummary) {
|
|
1963
|
+
fields += SUMMARY_FIELDS;
|
|
1964
|
+
}
|
|
1965
|
+
return fields;
|
|
1966
|
+
}
|
|
1586
1967
|
function createTranscriptsAPI(client) {
|
|
1587
1968
|
return {
|
|
1588
1969
|
async get(id, params) {
|
|
@@ -1598,6 +1979,7 @@ function createTranscriptsAPI(client) {
|
|
|
1598
1979
|
return normalizeTranscript(data.transcript);
|
|
1599
1980
|
},
|
|
1600
1981
|
async list(params) {
|
|
1982
|
+
const fields = buildListFields(params);
|
|
1601
1983
|
const query = `
|
|
1602
1984
|
query ListTranscripts(
|
|
1603
1985
|
$keyword: String
|
|
@@ -1635,13 +2017,23 @@ function createTranscriptsAPI(client) {
|
|
|
1635
2017
|
participant_email: $participant_email
|
|
1636
2018
|
date: $date
|
|
1637
2019
|
) {
|
|
1638
|
-
${
|
|
2020
|
+
${fields}
|
|
1639
2021
|
}
|
|
1640
2022
|
}
|
|
1641
2023
|
`;
|
|
2024
|
+
let internalDomain;
|
|
2025
|
+
if (params?.external) {
|
|
2026
|
+
const userQuery = "query { user { email } }";
|
|
2027
|
+
const userData = await client.execute(userQuery);
|
|
2028
|
+
internalDomain = extractDomain(userData.user.email);
|
|
2029
|
+
}
|
|
1642
2030
|
const variables = buildListVariables(params);
|
|
1643
2031
|
const data = await client.execute(query, variables);
|
|
1644
|
-
|
|
2032
|
+
let results = data.transcripts.map(normalizeTranscript);
|
|
2033
|
+
if (internalDomain) {
|
|
2034
|
+
results = results.filter((t) => hasExternalParticipants(t.participants, internalDomain));
|
|
2035
|
+
}
|
|
2036
|
+
return results;
|
|
1645
2037
|
},
|
|
1646
2038
|
async getSummary(id) {
|
|
1647
2039
|
const query = `
|
|
@@ -1688,9 +2080,10 @@ function createTranscriptsAPI(client) {
|
|
|
1688
2080
|
} = params;
|
|
1689
2081
|
const transcripts = [];
|
|
1690
2082
|
for await (const t of this.listAll({
|
|
2083
|
+
...listParams,
|
|
1691
2084
|
keyword: query,
|
|
1692
2085
|
scope,
|
|
1693
|
-
|
|
2086
|
+
includeSentences: true
|
|
1694
2087
|
})) {
|
|
1695
2088
|
transcripts.push(t);
|
|
1696
2089
|
if (limit && transcripts.length >= limit) break;
|
|
@@ -1698,8 +2091,7 @@ function createTranscriptsAPI(client) {
|
|
|
1698
2091
|
const allMatches = [];
|
|
1699
2092
|
let transcriptsWithMatches = 0;
|
|
1700
2093
|
for (const t of transcripts) {
|
|
1701
|
-
const
|
|
1702
|
-
const matches = searchTranscript(full, {
|
|
2094
|
+
const matches = searchTranscript(t, {
|
|
1703
2095
|
query,
|
|
1704
2096
|
caseSensitive,
|
|
1705
2097
|
speakers,
|
|
@@ -1751,13 +2143,13 @@ function createTranscriptsAPI(client) {
|
|
|
1751
2143
|
organizers,
|
|
1752
2144
|
participants,
|
|
1753
2145
|
user_id,
|
|
1754
|
-
channel_id
|
|
2146
|
+
channel_id,
|
|
2147
|
+
includeSentences: true
|
|
1755
2148
|
})) {
|
|
1756
2149
|
if (internalDomain && !hasExternalParticipants(t.participants, internalDomain)) {
|
|
1757
2150
|
continue;
|
|
1758
2151
|
}
|
|
1759
|
-
|
|
1760
|
-
transcripts.push(full);
|
|
2152
|
+
transcripts.push(t);
|
|
1761
2153
|
if (limit && transcripts.length >= limit) break;
|
|
1762
2154
|
}
|
|
1763
2155
|
return analyzeMeetings(transcripts, {
|
|
@@ -1775,13 +2167,20 @@ function createTranscriptsAPI(client) {
|
|
|
1775
2167
|
toDate,
|
|
1776
2168
|
mine,
|
|
1777
2169
|
organizers,
|
|
1778
|
-
participants
|
|
2170
|
+
participants,
|
|
2171
|
+
includeSummary: true
|
|
1779
2172
|
})) {
|
|
1780
|
-
|
|
1781
|
-
transcripts.push(full);
|
|
2173
|
+
transcripts.push(t);
|
|
1782
2174
|
if (limit && transcripts.length >= limit) break;
|
|
1783
2175
|
}
|
|
1784
2176
|
return aggregateActionItems(transcripts, {}, filterOptions);
|
|
2177
|
+
},
|
|
2178
|
+
async bulkExport(params = {}) {
|
|
2179
|
+
const { format = "markdown", asZip = false, onProgress } = params;
|
|
2180
|
+
const transcriptIds = await collectTranscriptIds(client, this, params);
|
|
2181
|
+
const files = await convertTranscripts(this, transcriptIds, format, onProgress);
|
|
2182
|
+
const zip = asZip ? await createZipArchive(files) : void 0;
|
|
2183
|
+
return { files, zip, format, totalExported: files.length };
|
|
1785
2184
|
}
|
|
1786
2185
|
};
|
|
1787
2186
|
}
|
|
@@ -1860,6 +2259,50 @@ function buildListVariables(params) {
|
|
|
1860
2259
|
date: params.date
|
|
1861
2260
|
};
|
|
1862
2261
|
}
|
|
2262
|
+
async function collectTranscriptIds(client, api, params) {
|
|
2263
|
+
const { ids, fromDate, toDate, mine, organizers, participants, external, limit } = params;
|
|
2264
|
+
if (ids?.length) {
|
|
2265
|
+
return ids;
|
|
2266
|
+
}
|
|
2267
|
+
let internalDomain;
|
|
2268
|
+
if (external) {
|
|
2269
|
+
const userQuery = "query { user { email } }";
|
|
2270
|
+
const userData = await client.execute(userQuery);
|
|
2271
|
+
internalDomain = extractDomain(userData.user.email);
|
|
2272
|
+
}
|
|
2273
|
+
const result = [];
|
|
2274
|
+
for await (const t of api.listAll({ fromDate, toDate, mine, organizers, participants })) {
|
|
2275
|
+
if (internalDomain && !hasExternalParticipants(t.participants, internalDomain)) {
|
|
2276
|
+
continue;
|
|
2277
|
+
}
|
|
2278
|
+
result.push(t.id);
|
|
2279
|
+
if (limit && result.length >= limit) break;
|
|
2280
|
+
}
|
|
2281
|
+
return result;
|
|
2282
|
+
}
|
|
2283
|
+
async function convertTranscripts(api, transcriptIds, format, onProgress) {
|
|
2284
|
+
const files = [];
|
|
2285
|
+
const extension = format === "markdown" ? "md" : format;
|
|
2286
|
+
let completed = 0;
|
|
2287
|
+
const total = transcriptIds.length;
|
|
2288
|
+
for await (const result of batch(
|
|
2289
|
+
transcriptIds,
|
|
2290
|
+
async (id) => {
|
|
2291
|
+
const transcript = await api.get(id);
|
|
2292
|
+
const content = await exportTranscript(transcript, format);
|
|
2293
|
+
const filename = generateExportFilename(transcript, extension);
|
|
2294
|
+
return { id, title: transcript.title, filename, content };
|
|
2295
|
+
},
|
|
2296
|
+
{ delayMs: 100 }
|
|
2297
|
+
)) {
|
|
2298
|
+
if (!result.error) {
|
|
2299
|
+
files.push(result.result);
|
|
2300
|
+
}
|
|
2301
|
+
completed++;
|
|
2302
|
+
onProgress?.(completed, total);
|
|
2303
|
+
}
|
|
2304
|
+
return files;
|
|
2305
|
+
}
|
|
1863
2306
|
|
|
1864
2307
|
// src/graphql/queries/users.ts
|
|
1865
2308
|
var USER_FIELDS = `
|