Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3be55ef
feat: added fireflies icon
chime3 Nov 11, 2024
fe24693
feat: added fireFlies UI
chime3 Nov 11, 2024
f5fa519
feat: added flireFlies config
chime3 Nov 11, 2024
fe69fca
feat: added fireflies controller and route
chime3 Nov 11, 2024
ab672ba
feat: added fireflies route
chime3 Nov 13, 2024
e633ec1
feat: added fireflies functions
chime3 Nov 13, 2024
001cdff
fix: updated fireflies user type
chime3 Nov 14, 2024
2567448
fix: added fireflies callback function
chime3 Nov 14, 2024
4979a23
feat: defined meeting transcript schema
chime3 Nov 15, 2024
127339f
feat: added meeting transcript handler
chime3 Nov 15, 2024
b36dd5a
Merge branch 'develop' into feature/70-fireflies-connector
chime3 Nov 21, 2024
0f42109
feat: improved meeting transcript handler
chime3 Nov 22, 2024
57deabb
fix: modified batch size
chime3 Nov 22, 2024
c2b0670
Merge branch 'develop' into feature/70-fireflies-connector
chime3 Dec 23, 2024
a6320bf
feat: added fireflies status as active
chime3 Dec 23, 2024
b81cc0a
feat: added meeting transcript option in UI
chime3 Dec 23, 2024
834246a
fix: meeting transcript type bug
chime3 Dec 23, 2024
a5a1873
fix: meetingattendee data type bug
chime3 Dec 23, 2024
787b1e3
feat: fireflies meeting transcript unit test
chime3 Jan 10, 2025
60875c8
feat: added fireflies readme
chime3 Jan 10, 2025
79a787a
fix: update meeting transcript date time format
chime3 Jan 15, 2025
faccf45
Merge branch 'develop' into feature/70-fireflies-connector
chime3 Jan 22, 2025
8fd5ef1
feat: refactored fireflies unit test
chime3 Jan 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/fireflies/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions src/api/rest/v1/fireflies/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Request, Response, NextFunction } from "express";

export default class Controller {
public static async apiKeySubmit(req: Request, res: Response, next: NextFunction) {
try {
const apiKey = req.body.apiKey;

// @todo Validate API key if any (pending from FireFlies)

// Redirect to the callback endpoint with the apiKey
res.status(200).send({
redirect: `/callback/fireflies?apiKey=${encodeURIComponent(apiKey)}`
});

} catch (error) {
res.status(400).send({
error: error.message
});
}
}
}
8 changes: 8 additions & 0 deletions src/api/rest/v1/fireflies/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import express from 'express'
import Controller from './controller'

const router = express.Router()

router.post('/apiKeySubmit', Controller.apiKeySubmit)

export default router
2 changes: 2 additions & 0 deletions src/api/rest/v1/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import AdminRoutes from './admin/routes'
import InfoRoutes from './info/routes'
import LLMRoutes from './llm/routes'
import TelegramRoutes from './telegram/routes'
import FireFliesRoutes from './fireflies/routes'
import SearchRoutes from "./search/routes"
import AccountRoutes from './account/routes'
import AuthRoutes from "./auth/routes"
Expand All @@ -30,5 +31,6 @@ router.use('/account', AccountRoutes)
router.use('/app', AppRoutes)

router.use('/telegram', TelegramRoutes)
router.use('/fireflies', FireFliesRoutes)

export default router
10 changes: 10 additions & 0 deletions src/providers/fireflies/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## Authentication

Fireflies uses API key authentication and GraphQL for all API interactions.

#### Getting an API Key

1. Log in to your Fireflies.ai account
2. Navigate to Settings > Developer Settings
3. Use the existing one or reset another
4. Copy and securely store your API key
90 changes: 90 additions & 0 deletions src/providers/fireflies/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import axios, { AxiosResponse } from 'axios';

export interface FireFliesConfig {
apiKey: string;
baseUrl?: string;
}

export interface User {
user_id: string;
name: string;
email: string;
is_admin?: string;
num_transcripts?: string;
integrations?: string[];

}

export interface GraphQLResponse<T> {
data: T;
errors?: { message: string }[];
}

export class FireFliesClient {
private apiKey: string;
private baseUrl: string;

constructor(config: FireFliesConfig) {
this.apiKey = config.apiKey;
this.baseUrl = config.baseUrl || 'https://api.fireflies.ai/graphql';
}

private get headers() {
return {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.apiKey}`
};
}

public async executeQuery<T>(query: string, variables?: Record<string, any>): Promise<GraphQLResponse<T>> {
const payload = { query, variables };

try {
const response: AxiosResponse<GraphQLResponse<T>> = await axios.post(this.baseUrl, payload, { headers: this.headers });
if (response.data.errors) {
throw new Error(`GraphQL error: ${response.data.errors.map(e => e.message).join(', ')}`);
}
return response.data;
} catch (error) {
console.error('Error executing GraphQL query:', error);
throw error;
}
}

/**
*
* @param userId Optional
*
* @returns Owner by default
*/
public async getUser(userId?: string): Promise<User> {
const query = userId
? `
query User($userId: String!) {
user(id: $userId) {
user_id
name
email
}
}
`
: `
query {
user {
user_id
name
email
}
}
`;

// Pass variables only if userId is defined
const response = userId
? await this.executeQuery<{ user: User }>(query, { userId })
: await this.executeQuery<{ user: User }>(query);

return response.data.user;
}

// You can add more methods to handle other queries or mutations here
}
85 changes: 85 additions & 0 deletions src/providers/fireflies/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Request, Response } from 'express'
import Base from "../BaseProvider"

import { BaseProviderConfig, ConnectionCallbackResponse, PassportProfile } from '../../interfaces'
import { FireFliesClient, FireFliesConfig } from './api'
import MeetingTranscriptHandler from './meeting-transcript'

export default class FireFliesProvider extends Base {

protected config: BaseProviderConfig


public getProviderName() {
return 'fireflies'
}

public getProviderLabel() {
return 'FireFlies'
}

public getProviderApplicationUrl() {
return 'https://fireflies.ai/'
}

public setConfig(config: BaseProviderConfig) {
this.config = config
}

public syncHandlers(): any[] {
return [
MeetingTranscriptHandler
]
}

public async connect(req: Request, res: Response, next: any): Promise<any> {
return res.redirect('/provider/fireflies')
}

public async callback(req: Request, res: Response, next: any): Promise<ConnectionCallbackResponse> {
const apiKey = req.query.apiKey!.toString();

// Initialize Fireflies client configuration
const config: FireFliesConfig = {
apiKey: apiKey
};

const client = new FireFliesClient(config);

// Fetch user profile from Fireflies
const ffProfile = await client.getUser();

// Set up display name
const displayName = ffProfile.name.trim();

// Construct the profile structure similar to the Telegram format
const profile: PassportProfile = {
id: ffProfile.user_id.toString(),
provider: this.getProviderId(), // Assuming getProviderId() returns 'fireflies' or similar identifier
displayName: displayName,
name: {
familyName: ffProfile.name.split(" ").slice(-1)[0], // Last word as family name
givenName: ffProfile.name.split(" ").slice(0, -1).join(" ") // First part as given name
},
connectionProfile: {
username: ffProfile.email.split('@')[0], // Username from email prefix
readableId: ffProfile.user_id,
email: ffProfile.email,
verified: true // Assuming profile is verified
}
};

return {
id: profile.id,
accessToken: apiKey,
refreshToken: apiKey,
profile
};
}

public async getApi(
accessToken?: string,
refreshToken?: string
): Promise<any> { }
}

Loading
Loading