b578ea5951
# 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
60 lines
1.7 KiB
TypeScript
60 lines
1.7 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 SERVER_URL = process.env.SERVER_URL || 'http://localhost:3000';
|
|
const API_KEY = process.env.TWENTY_API_KEY;
|
|
const meetingId = process.argv[2];
|
|
|
|
const main = async (): Promise<void> => {
|
|
if (!API_KEY) {
|
|
console.error('❌ TWENTY_API_KEY is required');
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!meetingId) {
|
|
console.error('Usage: yarn delete:meeting <meetingId>');
|
|
process.exit(1);
|
|
}
|
|
|
|
const response = await fetch(`${SERVER_URL}/graphql`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Bearer ${API_KEY}`,
|
|
},
|
|
body: JSON.stringify({
|
|
query: `mutation DeleteMeeting($id: UUID!) { deleteMeeting(id: $id) { id } }`,
|
|
variables: { id: meetingId },
|
|
}),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
console.error(`❌ Delete failed (status ${response.status})`);
|
|
console.error(errorText);
|
|
process.exit(1);
|
|
}
|
|
|
|
const result = await response.json();
|
|
const deletedId = result.data?.deleteMeeting?.id;
|
|
if (result.errors || !deletedId) {
|
|
const message = result.errors?.[0]?.message || 'deleteMeeting returned null';
|
|
console.error('❌ Error:', message);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log('✅ Deleted meeting:', deletedId);
|
|
};
|
|
|
|
main().catch((error) => {
|
|
console.error('❌ Failed to delete meeting');
|
|
console.error(error instanceof Error ? error.message : String(error));
|
|
process.exit(1);
|
|
});
|