-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit f8de369
Showing
33 changed files
with
5,245 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.DS_Store |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }); | ||
}); |
Oops, something went wrong.