# Fireflies Automatically (with cli util : webhook not working and no cron added) captures meeting notes with AI-generated summaries and insights from Fireflies.ai into your Twenty CRM. ## Current Status - Doesn't work with Fireflies webhook yet due to missing headers forwarding in twenty serverless func - Meeting ingestion utility script are available for individual meeting insertion and historical meetings with filters with yarn meeting:all - You have to push the secrets as application.config.ts values (despite env variables in .env in docker compose or container I couldn't push the secrets with the cli) ## Current Platform Limitation (Headers) - Twenty serverless route triggers currently do **not forward HTTP headers** to functions. Fireflies signatures sent in headers are stripped, so header-based verification does not work in production. - Workaround: the provided test script also includes the signature inside the payload; the handler falls back to that payload signature. Use this only for testing until header forwarding is supported. ## Utilities for meeting insertion (workarounds) - Ingest a specific Fireflies meeting into Twenty: `yarn meeting:ingest <meetingId>` or `MEETING_ID=... yarn meeting:ingest` - Fetch all/historical Fireflies meetings into Twenty: `yarn meeting:all [--from 2024-01-01] [--to 2024-02-01] [--organizer a@x.com] [--participant b@x.com] [--channel <channelId>] [--mine] [--dry-run]` - Filters (combine as needed): - `--from` / `--to`: ISO or date string range filter - `--organizer` / `--participant`: comma-separated emails - `--channel`: Fireflies channel id - `--mine`: only meetings for the current Fireflies user - Controls: - `--dry-run`: list and transform without writing to Twenty - `--page-size`: pagination size (default 50) - `--max-records`: stop after N transcripts (default 500) I am closing previous #16378 as this one includes it all
82 lines
2.6 KiB
TypeScript
82 lines
2.6 KiB
TypeScript
/* eslint-disable no-console */
|
|
import * as dotenv from 'dotenv';
|
|
import * as path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
dotenv.config({ path: path.join(__dirname, '../.env') });
|
|
|
|
const FIREFLIES_API_KEY = process.env.FIREFLIES_API_KEY;
|
|
const meetingId = process.argv[2] || '01KBMR1ZYQ34YP8D2KB4B16QPH';
|
|
|
|
const main = async (): Promise<void> => {
|
|
if (!FIREFLIES_API_KEY) {
|
|
console.error('❌ FIREFLIES_API_KEY is required');
|
|
process.exit(1);
|
|
}
|
|
|
|
const query = `
|
|
query GetTranscript($transcriptId: String!) {
|
|
transcript(id: $transcriptId) {
|
|
id
|
|
title
|
|
summary {
|
|
overview
|
|
notes
|
|
gist
|
|
bullet_gist
|
|
short_summary
|
|
short_overview
|
|
outline
|
|
shorthand_bullet
|
|
action_items
|
|
keywords
|
|
topics_discussed
|
|
meeting_type
|
|
transcript_chapters
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
const response = await fetch('https://api.fireflies.ai/graphql', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${FIREFLIES_API_KEY}`,
|
|
},
|
|
body: JSON.stringify({ query, variables: { transcriptId: meetingId } }),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error(`❌ API request failed with status ${response.status}`);
|
|
console.error(errorText);
|
|
process.exit(1);
|
|
}
|
|
|
|
const json = await response.json();
|
|
console.log('=== Fireflies API Response ===\n');
|
|
console.log(JSON.stringify(json, null, 2));
|
|
|
|
if (json.data?.transcript?.summary) {
|
|
const s = json.data.transcript.summary;
|
|
console.log('\n=== Summary Fields Status ===');
|
|
console.log('overview:', s.overview ? `✓ (${s.overview.length} chars)` : '✗ empty');
|
|
console.log('notes:', s.notes ? `✓ (${s.notes.length} chars)` : '✗ empty');
|
|
console.log('gist:', s.gist ? `✓ (${s.gist.length} chars)` : '✗ empty');
|
|
console.log('bullet_gist:', s.bullet_gist ? `✓ (${s.bullet_gist.length} chars)` : '✗ empty');
|
|
console.log('outline:', s.outline ? `✓ (${s.outline.length} chars)` : '✗ empty');
|
|
console.log('action_items:', s.action_items?.length || 0, 'items');
|
|
console.log('topics_discussed:', s.topics_discussed?.length || 0, 'topics');
|
|
console.log('keywords:', s.keywords?.length || 0, 'keywords');
|
|
}
|
|
};
|
|
|
|
main().catch((error) => {
|
|
console.error('❌ Failed to fetch meeting');
|
|
console.error(error instanceof Error ? error.message : String(error));
|
|
process.exit(1);
|
|
});
|