Skip to content

Commit

Permalink
create amurex chrome extension
Browse files Browse the repository at this point in the history
  • Loading branch information
sansyrox committed Dec 1, 2024
0 parents commit f8de369
Show file tree
Hide file tree
Showing 33 changed files with 5,245 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
661 changes: 661 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

95 changes: 95 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<div align="center">
<img src="./assets/ogimage.jpg" alt="Amurex Logo" width="800" />
<h2>Your AI Meeting Copilot</h2>

<p>
<a href="https://github.com/thepersonalaicompany/amurex/blob/main/LICENSE">
<img src="https://img.shields.io/badge/license-AGPL--3.0-blue.svg" alt="License" />
</a>
<a href="https://chrome.google.com/webstore/detail/amurex/dckidmhhpnfhachdpobgfbjnhfnmddmc">
<img src="https://img.shields.io/chrome-web-store/v/dckidmhhpnfhachdpobgfbjnhfnmddmc.svg" alt="Chrome Web Store" />
</a>
<a href="https://twitter.com/thepersonalaico">
<img src="https://img.shields.io/twitter/follow/thepersonalaico?style=social" alt="Twitter Follow" />
</a>
</p>
</div>

## About Amurex

Amurex is your simple yet powerful AI meeting assistant that seamlessly integrates into your workflow. Built with cutting-edge AI, Amurex ensures you never miss a detail, always stay on top of action items, and make every meeting more productive.

With features like real-time suggestions, smart summaries, and follow-up emails, Amurex acts as your personal copilot for all your meetings—saving time and boosting efficiency.

As an open-source tool, Amurex is designed to be transparent, secure, and privacy-focused, giving you confidence in how your data is handled while delivering a seamless AI-driven experience.

Take control of your meetings with Amurex, and let it handle the busywork while you focus on what truly matters.

## Features

- **Real-time Suggestions During Meetings**
<p>
Get intelligent suggestions and prompts while your meeting is happening.
</p>

<img src="https://github.com/user-attachments/assets/7e610998-71f9-454d-bff6-e566f58e447a" alt="Real-time meeting suggestions" width="600" />
<br></br>

- **Smart Summaries & Key Takeaways**
<p>
Automatically generate comprehensive meeting summaries and action items.
</p>

<img src="https://github.com/user-attachments/assets/43349cc5-cb35-43c8-a6ca-3a259bd1afbc" alt="Meeting summaries and takeaways" width="600" />
<br></br>


- **Late Join Recap**
<p>
Quickly catch up on what you missed when joining late.
</p>

<img src="https://github.com/user-attachments/assets/3f793f6d-5e83-4667-90a5-41bb52e8008b" alt="Late join meeting recap" width="600" />

<br></br>

- **Full Meeting Transcripts**
<p>
Get accurate, real-time transcriptions of your entire meeting.
</p>

<img src="https://github.com/user-attachments/assets/8300dfa3-12cb-418a-9242-526b19e05134" alt="Meeting transcriptions" width="600" />

<br></br>

- **Built in Follow up Emails**
<p>
Generate and send professional follow-up emails with one click.
</p>

<img src="https://github.com/user-attachments/assets/72c92702-f249-4525-9d3b-dbf536935a14" alt="Follow up emails" width="600" />


## Quick Start

0. Star this repository :star:
1. Install Amurex from the [Chrome Web Store](https://chromewebstore.google.com/detail/amurex/dckidmhhpnfhachdpobgfbjnhfnmddmc)
2. Complete the 30 second onboarding process
3. Become a 10x human with your personal copilot

## Self Hosting
1. Clone the repository
2. Load the unpacked extension
3. Navigate to the `server` directory
4. Use one of the options from `server/README.md` to run the server

## Using the development version

1. Download the latest zip
2. Navigate to `chrome://extensions`
3. Enable Developer Mode
4. Load the unpacked extension

<div align="center">
Made with ❤️ for better <del>meetings</del> life
</div>
Binary file added assets/amurex-readme.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/ogimage.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added chrome_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
244 changes: 244 additions & 0 deletions extension/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// Allows users to open the side panel by clicking on the action toolbar icon
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick: true })
.catch((error) => console.error(error));

// Check whether new version is installed
chrome.runtime.onInstalled.addListener(function (details) {
if (details.reason == "install") {
// Open welcome page in new tab
chrome.tabs.create({
url: "https://app.amurex.ai/signin?welcome=true",
});
} else if (details.reason == "update") {
let thisVersion = chrome.runtime.getManifest().version;
console.log(
"Updated from " + details.previousVersion + " to " + thisVersion + "!"
);
}
});

async function getUserId() {
let session = await chrome.cookies.get({
url: "http://localhost:3000",
name: "amurex_session",
});
if (session && session.value) {
const decodedSession = JSON.parse(decodeURIComponent(session.value));
const userId = decodedSession.user.id;
return userId;
}

session = await chrome.cookies.get({
url: "https://app.amurex.ai",
name: "amurex_session",
});
if (session && session.value) {
const decodedSession = JSON.parse(decodeURIComponent(session.value));
const userId = decodedSession.user.id;
return userId;
}
return null;
}

chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
if (message.type == "new_meeting_started") {
// Saving current tab id, to download transcript when this tab is closed
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
const tabId = tabs[0].id;
chrome.storage.local.set({ meetingTabId: tabId }, function () {
console.log("Meeting tab id saved");
});
});
} else if (message.type == "download") {
// Invalidate tab id since transcript is downloaded, prevents double downloading of transcript from tab closed event listener
downloadTranscript();
} else if (message.action === "getUserId") {
(async () => {
const userId = await getUserId();
sendResponse({ userId });
})();
return true;
} else if (message.action === "checkAuthentication") {
// Check cookies sequentially
// const checkCookies = async () => {
// Make the message handler async
(async () => {
for (const cookieInfo of message.cookies) {
const cookie = await chrome.cookies.get({
url: cookieInfo.url,
name: cookieInfo.name,
});

if (cookie && cookie.value) {
sendResponse({ is_authenticated: true });
return;
}
}
sendResponse({ is_authenticated: false });
})();

return true;
// };
} else if (
message.type === "open_side_panel" ||
message.type === "open_late_meeting_side_panel" ||
message.type === "open_file_upload_panel"
) {
// Map message types to their corresponding paths
const pathMap = {
open_side_panel: "sidepanels/sidepanel.html",
open_late_meeting_side_panel: `sidepanels/lateMeetingSidePanel.html${
message.meetingId ? `?meetingId=${message.meetingId}` : ""
}`,
open_file_upload_panel: `sidepanels/file_upload_panel.html${
message.meetingId ? `?meetingId=${message.meetingId}` : ""
}`,
};

const panelPath = pathMap[message.type];

chrome.sidePanel
.open({ tabId: sender.tab.id }, async () => {
await chrome.storage.local.set({ redirect: message.type });
if (message.meetingId) {
await chrome.storage.local.set({ meetingId: message.meetingId });
}
})
.then(() => {
return chrome.sidePanel.setOptions({
tabId: sender.tab.id,
path: panelPath,
enabled: true,
});
})
.then(() => {
// Set the current navigation item in storage
let navItem;
if (message.type === "open_late_meeting_side_panel") {
navItem = "lateMeetingSidePanel";
} else if (message.type === "open_file_upload_panel") {
navItem = "file_upload_panel";
} else if (message.type === "open_side_panel") {
navItem = "sidepanel";
} else {
console.error("Invalid side panel type");
}

return chrome.storage.local.set({ navItem });
})
.catch((error) => console.error("Error handling side panel:", error));
}
});

// Download transcript if meeting tab is closed
chrome.tabs.onRemoved.addListener(function (tabid) {
chrome.storage.local.get(["meetingTabId"], function (data) {
if (tabid == data.meetingTabId) {
console.log("Successfully intercepted tab close");
// downloadTranscript()
// Clearing meetingTabId to prevent misfires of onRemoved until next meeting actually starts
chrome.storage.local.set({ meetingTabId: null }, function () {
console.log("Meeting tab id cleared for next meeting");
});
}
});
});

function downloadTranscript() {
chrome.storage.local.get(
[
"userName",
"transcript",
"chatMessages",
"meetingTitle",
"meetingStartTimeStamp",
],
function (result) {
if (result.userName && result.transcript && result.chatMessages) {
// Create file name if values or provided, use default otherwise
const fileName =
result.meetingTitle && result.meetingStartTimeStamp
? `Amurex/Transcript-${result.meetingTitle} at ${result.meetingStartTimeStamp}.txt`
: `Amurex/Transcript.txt`;

// Create an array to store lines of the text file
const lines = [];

// Iterate through the transcript array and format each entry
result.transcript.forEach((entry) => {
lines.push(`${entry.personName} (${entry.timeStamp})`);
lines.push(entry.personTranscript);
// Add an empty line between entries
lines.push("");
});
lines.push("");
lines.push("");

if (result.chatMessages.length > 0) {
// Iterate through the chat messages array and format each entry
lines.push("---------------");
lines.push("CHAT MESSAGES");
lines.push("---------------");
result.chatMessages.forEach((entry) => {
lines.push(`${entry.personName} (${entry.timeStamp})`);
lines.push(entry.chatMessageText);
// Add an empty line between entries
lines.push("");
});
lines.push("");
lines.push("");
}

// Join the lines into a single string, replace "You" with userName from storage
const textContent = lines
.join("\n")
.replace(/You \(/g, result.userName + " (");

// Create a blob containing the text content
const blob = new Blob([textContent], { type: "text/plain" });

// Read the blob as a data URL
const reader = new FileReader();

// Download once blob is read
reader.onload = function (event) {
const dataUrl = event.target.result;

// Create a download with Chrome Download API
chrome.downloads
.download({
url: dataUrl,
filename: fileName,
conflictAction: "uniquify",
})
.then(() => {
console.log("Transcript downloaded to Amurex directory");
})
.catch((error) => {
console.log(error);
chrome.downloads.download({
url: dataUrl,
filename: "Amurex/Transcript.txt",
conflictAction: "uniquify",
});
console.log(
"Invalid file name. Transcript downloaded to Amurex directory with simple file name."
);
});
};

// Read the blob and download as text file
reader.readAsDataURL(blob);
} else console.log("No transcript found");
}
);
}

chrome.action.onClicked.addListener(async (tab) => {
await chrome.sidePanel.setOptions({
enabled: true,
path: "sidepanels/sidepanel.html",
});
chrome.sidePanel.open({ tabId: tab.id });
});
Loading

0 comments on commit f8de369

Please sign in to comment.