Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GOOGLE_USERNAME="john@gmail.com"
GOOGLE_PASSWORD="password"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules/
built/
docs/
.env
24 changes: 0 additions & 24 deletions core/audio.js

This file was deleted.

19 changes: 19 additions & 0 deletions core/audio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class MeetAudio {
page: unknown;
constructor(page: unknown) {
// https://stackoverflow.com/questions/52464583/possible-to-get-puppeteer-audio-feed-and-or-input-audio-directly-to-puppeteer
// Could not get this to work yet

this.page = page;
}

async stream(_source: unknown) {
// Stream audio
}

async stop() {
// Stop audio stream
}
}

export default MeetAudio;
50 changes: 0 additions & 50 deletions core/authenticate.js

This file was deleted.

79 changes: 79 additions & 0 deletions core/authenticate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Main initializing function

// Only logs in, however we can skip this by just waiting for the chat button or the leave meeting button. Then signing in can be done manually with headless mode disabled, and the package just automates the other stuff
export default async function authenticate({ meetingLink, email, pw }) {
if (!meetingLink.startsWith("https://meet.google.com/")) {
throw "Meeting Link isn't valid. Make sure it looks like 'https://meet.google.com/xyz-wxyz-xyz'!";
}
if (!email.endsWith("@gmail.com")) {
throw "Email isn't a Google Account";
}

this.meetingLink = meetingLink;
this.email = email;
this.browser = await this.puppeteer.launch({ headless: false });
this.page = await this.browser.newPage();
this.ctx = await this.browser.defaultBrowserContext();
await this.ctx.overridePermissions("https://meet.google.com", [
"microphone",
"camera",
"notifications",
]);
await this.page.goto(meetingLink);
// Authenticating with credentials
console.log("Logging in...");
try {
var signInButton = await this.page.waitForSelector(".NPEfkd", {
visible: true,
timeout: 10000,
});
await signInButton.focus();
await signInButton.click();
} catch (e) {
console.log(e);
// Sign In button is not visible, so we assume the page has already redirected, and is not accepting anonymous meeting members - Support for anonymous joining may be implemented in the future
}
var input = await this.page.waitForSelector("input[type=email]", {
visible: true,
timeout: 0,
});
await input.focus();
await this.page.keyboard.type(email);
await this.page.keyboard.press("Enter");
var input = await this.page.waitForSelector("input[type=password]", {
visible: true,
timeout: 0,
});
await input.focus();
await this.page.keyboard.type(pw);
await this.page.keyboard.press("Enter");
console.log("Authenticated successfully!");
await this.screenshot("logged-in.png"); // Double check that the meet is about to be joined to. Quickest way to make sure that there aren't any prompts (Like Google's "confirm recovery email" prompt), that can leave the browser hanging.
// Although you can edit the package's code to fit your scenario, the easiest way to fix anything that leaves this program hanging, is to just run the package without headless mode. That way you can continue on any prompts or see issues fast.
// Join Google Meet
await this.page.waitForSelector("div[role=button]");
var join = await this.page.waitForSelector(".VfPpkd-vQzf8d", {
visible: true,
timeout: 0,
});
for (var i = 3; i > 0; i--) {
await this.toggleMic(this.page);
await this.toggleVideo(this.page);
} // toggle mic and video 3 times because Google Meet glitches and leaves mic on if it's toggled as soon as page loads
await join.click();

// Beyond, is code separate from logging in. You could log in manually and just wait for the chat button to show up to start the bot, for example.
await this.page.waitForXPath(
"/html/body/div[1]/c-wiz/div[1]/div/div[9]/div[3]/div[10]/div[3]/div[3]/div/div/div[3]/span/button",
{ visible: true, timeout: 0 },
); // wait for chat button

await this.toggleMemberList();
await this.toggleChat();
this.message.messageListener(this);
this.member.memberListener(this); // Start listeners
this.isChatEnabled = this.chatEnabled;
this.Audio = new this.audio(this.page);
console.log("Meeting joined, and listeners are listening!");
this.emit("ready");
}
54 changes: 0 additions & 54 deletions core/meeting.js

This file was deleted.

63 changes: 63 additions & 0 deletions core/meeting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Core meeting methods

export async function toggleMic() {
await this.page.keyboard.down("ControlLeft");
await this.page.keyboard.press("KeyD");
await this.page.keyboard.up("ControlLeft");
this.isMicEnabled = !this.isMicEnabled;
}

export async function toggleVideo() {
await this.page.keyboard.down("ControlLeft");
await this.page.keyboard.press("KeyE");
await this.page.keyboard.up("ControlLeft");
this.isVideoEnabled = !this.isVideoEnabled;
}

export async function toggleChat() {
var chatBtn = await this.page.waitForXPath(
"/html/body/div[1]/c-wiz/div[1]/div/div[9]/div[3]/div[10]/div[3]/div[3]/div/div/div[3]/span/button",
);
await chatBtn.click();
}

export async function toggleMemberList() {
var memberListBtn = await this.page.waitForXPath(
"/html/body/div[1]/c-wiz/div[1]/div/div[9]/div[3]/div[10]/div[3]/div[3]/div/div/div[2]/span/button",
);
await memberListBtn.click();
}

export async function chatEnabled() {
await this.page.waitForSelector("#bfTqV");
var disabled = await this.page.evaluate(() => {
disabled = document.querySelector("#bfTqV");
if (disabled.disabled === false) {
return true;
} else if (disabled.disabled === true) {
return false;
}
});
return disabled;
}

export async function sendMessage(message) {
if (await this.chatEnabled()) {
var chat = await this.page.waitForSelector("#bfTqV");
await chat.focus();
await this.page.$eval(
"#bfTqV",
(input, message) => {
input.value = message;
console.log(input);
console.log(message);
},
message,
); // replaced `await page.keyboard.type(message)`, because this is a little more instant
await this.page.keyboard.press("Enter");
}
}

export async function screenshot(path: string) {
await this.page.screenshot({ path, fullPage: true });
}
67 changes: 0 additions & 67 deletions core/member.js

This file was deleted.

Loading