From 67fc470b30a377f7c8aad147fa00c80b694f8676 Mon Sep 17 00:00:00 2001 From: Ahmad Reza Date: Mon, 4 Mar 2024 14:26:08 +0700 Subject: [PATCH 1/3] change to ts --- .gitignore | 1 + built/core/audio.js | 61 ++++++++++++ built/core/authenticate.js | 187 +++++++++++++++++++++++++++++++++++++ built/core/meeting.js | 183 ++++++++++++++++++++++++++++++++++++ built/core/member.js | 175 ++++++++++++++++++++++++++++++++++ built/core/message.js | 96 +++++++++++++++++++ built/examples/start.js | 107 +++++++++++++++++++++ built/meet.js | 90 ++++++++++++++++++ core/audio.js | 24 ----- core/audio.ts | 19 ++++ core/authenticate.js | 50 ---------- core/authenticate.ts | 79 ++++++++++++++++ core/meeting.js | 54 ----------- core/meeting.ts | 63 +++++++++++++ core/member.js | 67 ------------- core/member.ts | 73 +++++++++++++++ core/message.js | 36 ------- core/message.ts | 36 +++++++ examples/start.js | 39 -------- examples/start.ts | 44 +++++++++ meet.js | 62 ------------ meet.ts | 76 +++++++++++++++ package-lock.json | 22 +++++ package.json | 3 + tsconfig.json | 9 ++ 25 files changed, 1324 insertions(+), 332 deletions(-) create mode 100644 built/core/audio.js create mode 100644 built/core/authenticate.js create mode 100644 built/core/meeting.js create mode 100644 built/core/member.js create mode 100644 built/core/message.js create mode 100644 built/examples/start.js create mode 100644 built/meet.js delete mode 100644 core/audio.js create mode 100644 core/audio.ts delete mode 100644 core/authenticate.js create mode 100644 core/authenticate.ts delete mode 100644 core/meeting.js create mode 100644 core/meeting.ts delete mode 100644 core/member.js create mode 100644 core/member.ts delete mode 100644 core/message.js create mode 100644 core/message.ts delete mode 100644 examples/start.js create mode 100644 examples/start.ts delete mode 100644 meet.js create mode 100644 meet.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 3465ead..008001b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ +built/ docs/ diff --git a/built/core/audio.js b/built/core/audio.js new file mode 100644 index 0000000..2b8324d --- /dev/null +++ b/built/core/audio.js @@ -0,0 +1,61 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var MeetAudio = /** @class */ (function () { + function MeetAudio(page) { + // 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; + } + MeetAudio.prototype.stream = function (_source) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/]; + }); + }); + }; + MeetAudio.prototype.stop = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/]; + }); + }); + }; + return MeetAudio; +}()); +exports.default = MeetAudio; diff --git a/built/core/authenticate.js b/built/core/authenticate.js new file mode 100644 index 0000000..542166f --- /dev/null +++ b/built/core/authenticate.js @@ -0,0 +1,187 @@ +"use strict"; +// Main initializing function +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +// 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 +function authenticate(_a) { + var meetingLink = _a.meetingLink, email = _a.email, pw = _a.pw; + return __awaiter(this, void 0, void 0, function () { + var _b, _c, _d, signInButton, e_1, input, input, join, i; + return __generator(this, function (_e) { + switch (_e.label) { + case 0: + 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; + _b = this; + return [4 /*yield*/, this.puppeteer.launch({ headless: false })]; + case 1: + _b.browser = _e.sent(); + _c = this; + return [4 /*yield*/, this.browser.newPage()]; + case 2: + _c.page = _e.sent(); + _d = this; + return [4 /*yield*/, this.browser.defaultBrowserContext()]; + case 3: + _d.ctx = _e.sent(); + return [4 /*yield*/, this.ctx.overridePermissions("https://meet.google.com", [ + "microphone", + "camera", + "notifications", + ])]; + case 4: + _e.sent(); + return [4 /*yield*/, this.page.goto(meetingLink)]; + case 5: + _e.sent(); + // Authenticating with credentials + console.log("Logging in..."); + _e.label = 6; + case 6: + _e.trys.push([6, 10, , 11]); + return [4 /*yield*/, this.page.waitForSelector(".NPEfkd", { + visible: true, + timeout: 10000, + })]; + case 7: + signInButton = _e.sent(); + return [4 /*yield*/, signInButton.focus()]; + case 8: + _e.sent(); + return [4 /*yield*/, signInButton.click()]; + case 9: + _e.sent(); + return [3 /*break*/, 11]; + case 10: + e_1 = _e.sent(); + console.log(e_1); + return [3 /*break*/, 11]; + case 11: return [4 /*yield*/, this.page.waitForSelector("input[type=email]", { + visible: true, + timeout: 0, + })]; + case 12: + input = _e.sent(); + return [4 /*yield*/, input.focus()]; + case 13: + _e.sent(); + return [4 /*yield*/, this.page.keyboard.type(email)]; + case 14: + _e.sent(); + return [4 /*yield*/, this.page.keyboard.press("Enter")]; + case 15: + _e.sent(); + return [4 /*yield*/, this.page.waitForSelector("input[type=password]", { + visible: true, + timeout: 0, + })]; + case 16: + input = _e.sent(); + return [4 /*yield*/, input.focus()]; + case 17: + _e.sent(); + return [4 /*yield*/, this.page.keyboard.type(pw)]; + case 18: + _e.sent(); + return [4 /*yield*/, this.page.keyboard.press("Enter")]; + case 19: + _e.sent(); + console.log("Authenticated successfully!"); + return [4 /*yield*/, this.screenshot("logged-in.png")]; + case 20: + _e.sent(); // 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 + return [4 /*yield*/, this.page.waitForSelector("div[role=button]")]; + case 21: + // 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 + _e.sent(); + return [4 /*yield*/, this.page.waitForSelector(".VfPpkd-vQzf8d", { + visible: true, + timeout: 0, + })]; + case 22: + join = _e.sent(); + i = 3; + _e.label = 23; + case 23: + if (!(i > 0)) return [3 /*break*/, 27]; + return [4 /*yield*/, this.toggleMic(this.page)]; + case 24: + _e.sent(); + return [4 /*yield*/, this.toggleVideo(this.page)]; + case 25: + _e.sent(); + _e.label = 26; + case 26: + i--; + return [3 /*break*/, 23]; + case 27: // toggle mic and video 3 times because Google Meet glitches and leaves mic on if it's toggled as soon as page loads + return [4 /*yield*/, join.click()]; + case 28: + _e.sent(); + // 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. + return [4 /*yield*/, 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 })]; + case 29: + // 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. + _e.sent(); // wait for chat button + return [4 /*yield*/, this.toggleMemberList()]; + case 30: + _e.sent(); + return [4 /*yield*/, this.toggleChat()]; + case 31: + _e.sent(); + 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"); + return [2 /*return*/]; + } + }); + }); +} +exports.default = authenticate; diff --git a/built/core/meeting.js b/built/core/meeting.js new file mode 100644 index 0000000..6110884 --- /dev/null +++ b/built/core/meeting.js @@ -0,0 +1,183 @@ +"use strict"; +// Core meeting methods +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.screenshot = exports.sendMessage = exports.chatEnabled = exports.toggleMemberList = exports.toggleChat = exports.toggleVideo = exports.toggleMic = void 0; +function toggleMic() { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.page.keyboard.down("ControlLeft")]; + case 1: + _a.sent(); + return [4 /*yield*/, this.page.keyboard.press("KeyD")]; + case 2: + _a.sent(); + return [4 /*yield*/, this.page.keyboard.up("ControlLeft")]; + case 3: + _a.sent(); + this.isMicEnabled = !this.isMicEnabled; + return [2 /*return*/]; + } + }); + }); +} +exports.toggleMic = toggleMic; +function toggleVideo() { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.page.keyboard.down("ControlLeft")]; + case 1: + _a.sent(); + return [4 /*yield*/, this.page.keyboard.press("KeyE")]; + case 2: + _a.sent(); + return [4 /*yield*/, this.page.keyboard.up("ControlLeft")]; + case 3: + _a.sent(); + this.isVideoEnabled = !this.isVideoEnabled; + return [2 /*return*/]; + } + }); + }); +} +exports.toggleVideo = toggleVideo; +function toggleChat() { + return __awaiter(this, void 0, void 0, function () { + var chatBtn; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, 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")]; + case 1: + chatBtn = _a.sent(); + return [4 /*yield*/, chatBtn.click()]; + case 2: + _a.sent(); + return [2 /*return*/]; + } + }); + }); +} +exports.toggleChat = toggleChat; +function toggleMemberList() { + return __awaiter(this, void 0, void 0, function () { + var memberListBtn; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, 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")]; + case 1: + memberListBtn = _a.sent(); + return [4 /*yield*/, memberListBtn.click()]; + case 2: + _a.sent(); + return [2 /*return*/]; + } + }); + }); +} +exports.toggleMemberList = toggleMemberList; +function chatEnabled() { + return __awaiter(this, void 0, void 0, function () { + var disabled; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.page.waitForSelector("#bfTqV")]; + case 1: + _a.sent(); + return [4 /*yield*/, this.page.evaluate(function () { + disabled = document.querySelector("#bfTqV"); + if (disabled.disabled === false) { + return true; + } + else if (disabled.disabled === true) { + return false; + } + })]; + case 2: + disabled = _a.sent(); + return [2 /*return*/, disabled]; + } + }); + }); +} +exports.chatEnabled = chatEnabled; +function sendMessage(message) { + return __awaiter(this, void 0, void 0, function () { + var chat; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.chatEnabled()]; + case 1: + if (!_a.sent()) return [3 /*break*/, 6]; + return [4 /*yield*/, this.page.waitForSelector("#bfTqV")]; + case 2: + chat = _a.sent(); + return [4 /*yield*/, chat.focus()]; + case 3: + _a.sent(); + return [4 /*yield*/, this.page.$eval("#bfTqV", function (input, message) { + input.value = message; + console.log(input); + console.log(message); + }, message)]; + case 4: + _a.sent(); // replaced `await page.keyboard.type(message)`, because this is a little more instant + return [4 /*yield*/, this.page.keyboard.press("Enter")]; + case 5: + _a.sent(); + _a.label = 6; + case 6: return [2 /*return*/]; + } + }); + }); +} +exports.sendMessage = sendMessage; +function screenshot(path) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.page.screenshot({ path: path, fullPage: true })]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); +} +exports.screenshot = screenshot; diff --git a/built/core/member.js b/built/core/member.js new file mode 100644 index 0000000..9c66588 --- /dev/null +++ b/built/core/member.js @@ -0,0 +1,175 @@ +"use strict"; +// Member Listener +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.memberListener = void 0; +function memberJoinListener(Meet) { + return __awaiter(this, void 0, void 0, function () { + var member; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!true) return [3 /*break*/, 5]; + return [4 /*yield*/, Meet.page.waitForSelector(".iLNCXe", { visible: true, timeout: 0 })]; + case 1: + _a.sent(); // wait for member to join + return [4 /*yield*/, Meet.page.evaluate(function () { + var member = document.querySelector(".iLNCXe"); + return member.innerText.replace(" has joined", ""); + })]; + case 2: + member = _a.sent(); + return [4 /*yield*/, Meet.emit("memberJoin", Meet.members[member])]; + case 3: + _a.sent(); + return [4 /*yield*/, Meet.page.waitForSelector(".iLNCXe", { hidden: true, timeout: 0 })]; + case 4: + _a.sent(); + return [3 /*break*/, 0]; + case 5: return [2 /*return*/]; + } + }); + }); +} +function memberLeaveListener(Meet) { + return __awaiter(this, void 0, void 0, function () { + var _loop_1; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + _loop_1 = function () { + var members, member; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + members = Meet.members; + return [4 /*yield*/, Meet.page.waitForSelector(".aGJE1b", { visible: true, timeout: 0 })]; + case 1: + _b.sent(); // wait for member to leave + return [4 /*yield*/, Meet.page.evaluate(function () { + member = document.querySelector(".aGJE1b"); + if (member.innerText.endsWith(" has left the meeting")) { + return member.innerText.replace(" has left the meeting", ""); + } + else { + return null; + } + })]; + case 2: + member = _b.sent(); + if (member === null) { + return [2 /*return*/, "continue"]; + } + return [4 /*yield*/, Meet.emit("memberLeave", members[member])]; + case 3: + _b.sent(); + return [4 /*yield*/, Meet.page.waitForSelector(".aGJE1b", { hidden: true, timeout: 0 })]; + case 4: + _b.sent(); + return [2 /*return*/]; + } + }); + }; + _a.label = 1; + case 1: + if (!true) return [3 /*break*/, 3]; + return [5 /*yield**/, _loop_1()]; + case 2: + _a.sent(); + return [3 /*break*/, 1]; + case 3: return [2 /*return*/]; + } + }); + }); +} +function memberListener(Meet) { + return __awaiter(this, void 0, void 0, function () { + function getMembers() { + return __awaiter(this, void 0, void 0, function () { + var members; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, Meet.page.evaluate(function () { + var mems = {}; + var member_list = document.querySelector('div[role="list"]'); + for (var i = 0; i < member_list.children.length; i++) { + var member = { + name: member_list.children[i].firstChild.lastChild.firstChild + .firstChild.innerText, + icon: member_list.children[i].firstChild.firstChild + .firstChild.src, + }; + mems[member.name] = member; + } + return mems; + })]; + case 1: + members = _a.sent(); + Meet.members = members; + return [2 /*return*/]; + } + }); + }); + } + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, getMembers()]; + case 1: + _a.sent(); + memberJoinListener(Meet); + memberLeaveListener(Meet); + return [4 /*yield*/, Meet.page.exposeFunction("getMembers", getMembers)]; + case 2: + _a.sent(); + return [4 /*yield*/, Meet.page.evaluate(function () { + var memberObserver = new MutationObserver(function () { + getMembers(); + }); + memberObserver.observe(document.querySelector('div[role="list"]'), { + subtree: true, + childList: true, + }); + })]; + case 3: + _a.sent(); + return [2 /*return*/]; + } + }); + }); +} +exports.memberListener = memberListener; diff --git a/built/core/message.js b/built/core/message.js new file mode 100644 index 0000000..7595b85 --- /dev/null +++ b/built/core/message.js @@ -0,0 +1,96 @@ +"use strict"; +// Message Listener +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.messageListener = void 0; +function messageListener(Meet) { + return __awaiter(this, void 0, void 0, function () { + function getRecentMessage() { + return __awaiter(this, void 0, void 0, function () { + var message; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, Meet.page.evaluate(function () { + var chat = document.querySelector(".z38b6").lastChild; + return { + author: chat.firstChild.firstChild.innerText, + time: chat.firstChild.lastChild.innerText, + content: chat.lastChild.lastChild.innerText, + }; // See div.html + })]; + case 1: + message = _a.sent(); + if (!(message.author !== "You")) return [3 /*break*/, 3]; + return [4 /*yield*/, Meet.emit("message", message)]; + case 2: + _a.sent(); + Meet.recentMessage = message; + return [2 /*return*/, message]; + case 3: return [2 /*return*/]; + } + }); + }); + } + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, Meet.page.waitForSelector(".GDhqjd", { timeout: 0 })]; + case 1: + _a.sent(); + getRecentMessage(); + return [4 /*yield*/, Meet.page.exposeFunction("getRecentMessage", getRecentMessage)]; + case 2: + _a.sent(); + return [4 /*yield*/, Meet.page.evaluate(function () { + // https://stackoverflow.com/questions/47903954/how-to-inject-mutationobserver-to-puppeteer + // https://stackoverflow.com/questions/54109078/puppeteer-wait-for-page-dom-updates-respond-to-new-items-that-are-added-after/54110446#54110446 + var messageObserver = new MutationObserver(function () { + getRecentMessage(); + }); + messageObserver.observe(document.querySelector(".z38b6"), { + subtree: true, + childList: true, + }); + })]; + case 3: + _a.sent(); + return [2 /*return*/]; + } + }); + }); +} +exports.messageListener = messageListener; diff --git a/built/examples/start.js b/built/examples/start.js new file mode 100644 index 0000000..336cb0b --- /dev/null +++ b/built/examples/start.js @@ -0,0 +1,107 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var meet_1 = require("../meet"); +var client = new meet_1.Meet(); +var config = { + meetingLink: "https://meet.google.com/xyz-wxyz-xyz", + email: "", + pw: "", +}; +function command(client, message) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!message.content.startsWith("!quote")) return [3 /*break*/, 2]; + return [4 /*yield*/, client.sendMessage("".concat(message.author, " said, \"").concat(message.content.replace("!quote ", ""), "\" at ").concat(message.time))]; + case 1: + _a.sent(); + _a.label = 2; + case 2: return [2 /*return*/]; + } + }); + }); +} +(function () { return __awaiter(void 0, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + client.once("ready", function () { return __awaiter(void 0, void 0, void 0, function () { + return __generator(this, function (_a) { + console.log("ready"); + return [2 /*return*/]; + }); + }); }); + return [4 /*yield*/, client.login(config)]; + case 1: + _a.sent(); + client.on("message", function (message) { return __awaiter(void 0, void 0, void 0, function () { + return __generator(this, function (_a) { + command(client, message); + return [2 /*return*/]; + }); + }); }); + client.on("memberJoin", function (member) { return __awaiter(void 0, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, client.sendMessage("Welcome, ".concat(member.name, "!"))]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); }); + client.on("memberLeave", function (member) { return __awaiter(void 0, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, client.sendMessage("Goodbye, ".concat(member.name, "!"))]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); }); + return [2 /*return*/]; + } + }); +}); })(); +/* + Async/await syntax is required if you need to execute specific actions with Puppteer or don't want to be limited to only the events already implemented. +*/ +// If errors like "Node is detached" get thrown, restarting almost always fixes most errors diff --git a/built/meet.js b/built/meet.js new file mode 100644 index 0000000..b9559ff --- /dev/null +++ b/built/meet.js @@ -0,0 +1,90 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Meet = void 0; +var events_1 = require("events"); +var puppeteer_extra_1 = __importDefault(require("puppeteer-extra")); +var puppeteer_extra_plugin_stealth_1 = __importDefault(require("puppeteer-extra-plugin-stealth")); +var authenticate_1 = __importDefault(require("./core/authenticate")); +var meeting = __importStar(require("./core/meeting")); +var message = __importStar(require("./core/message")); +var member = __importStar(require("./core/member")); +var audio_1 = __importDefault(require("./core/audio")); // not working +var Meet = /** @class */ (function (_super) { + __extends(Meet, _super); + function Meet() { + var _this = _super.call(this) || this; + _this.login = authenticate_1.default; + _this.toggleMic = meeting.toggleMic; + _this.toggleVideo = meeting.toggleVideo; + _this.toggleChat = meeting.toggleChat; + _this.toggleMemberList = meeting.toggleMemberList; + _this.chatEnabled = meeting.chatEnabled; + _this.sendMessage = meeting.sendMessage; + _this.screenshot = meeting.screenshot; + console.log("Client created!"); + // Listeners (for use in login function) + _this.message = message; + _this.member = member; + _this.audio = audio_1.default; + _this.meetingLink = undefined; + _this.email = undefined; + _this.puppeteer = puppeteer_extra_1.default.use((0, puppeteer_extra_plugin_stealth_1.default)()); + _this.browser = undefined; + _this.page = undefined; + _this.ctx = undefined; + _this.isMicEnabled = true; + _this.isVideoEnabled = true; + _this.isChatEnabled = undefined; + _this.recentMessage = undefined; + _this.members = undefined; + return _this; + } + return Meet; +}(events_1.EventEmitter)); +exports.Meet = Meet; +/* Notes */ +// Various XPaths and element class names or ids are not explained throughout this source; there's probably a better way to have a permanent selector to a specific element though +// The Audio part of this package has not yet been implemented +// This code can be improved in many ways, but I wrote this during the beginning of Covid lockdown; I've only now decided to add a license and create a repository to publish +// This package aims to be similar to the Discord JS library diff --git a/core/audio.js b/core/audio.js deleted file mode 100644 index 67d5156..0000000 --- a/core/audio.js +++ /dev/null @@ -1,24 +0,0 @@ -class Audio { - - constructor(page) { - // 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) { - - // Stream audio - - } - - async stop() { - - // Stop audio stream - } - - -} - -module.exports = Audio; diff --git a/core/audio.ts b/core/audio.ts new file mode 100644 index 0000000..fa707d8 --- /dev/null +++ b/core/audio.ts @@ -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; diff --git a/core/authenticate.js b/core/authenticate.js deleted file mode 100644 index 4bd4b00..0000000 --- a/core/authenticate.js +++ /dev/null @@ -1,50 +0,0 @@ -// 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 -async function auth({ 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'); - -} - -module.exports = { - auth: auth -} diff --git a/core/authenticate.ts b/core/authenticate.ts new file mode 100644 index 0000000..736c439 --- /dev/null +++ b/core/authenticate.ts @@ -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"); +} diff --git a/core/meeting.js b/core/meeting.js deleted file mode 100644 index 77df10e..0000000 --- a/core/meeting.js +++ /dev/null @@ -1,54 +0,0 @@ -// Core meeting methods - -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; -} - -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; -} - -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(); -} - -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(); -} - -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; -} - -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'); - } -} - -async function screenshot(path) { - await this.page.screenshot({ path: path, fullPage: true }); -} - -module.exports = { - toggleMic: toggleMic, - toggleVideo: toggleVideo, - toggleChat: toggleChat, - toggleMemberList: toggleMemberList, - chatEnabled: chatEnabled, - sendMessage: sendMessage, - screenshot: screenshot, - -} diff --git a/core/meeting.ts b/core/meeting.ts new file mode 100644 index 0000000..31de546 --- /dev/null +++ b/core/meeting.ts @@ -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 }); +} diff --git a/core/member.js b/core/member.js deleted file mode 100644 index edad7dc..0000000 --- a/core/member.js +++ /dev/null @@ -1,67 +0,0 @@ -// Member Listener - -async function memberJoinListener(Meet) { - - while (true) { - await Meet.page.waitForSelector('.iLNCXe', { visible: true, timeout: 0 }); // wait for member to join - var member = await Meet.page.evaluate(() => { return document.querySelector('.iLNCXe').innerText.replace(' has joined', ''); }); - await Meet.emit('memberJoin', Meet.members[member]); - await Meet.page.waitForSelector('.iLNCXe', { hidden: true, timeout: 0 }); - } - -} - -async function memberLeaveListener(Meet) { - - while (true) { - members = Meet.members; // memberLeaveListener keeps own copy of member list (because when a member leaves, the list gets updated and memberLeaveListener doesn't get the member's info) - await Meet.page.waitForSelector('.aGJE1b', { visible: true, timeout: 0 }); // wait for member to leave - var member = await Meet.page.evaluate(() => { - member = document.querySelector('.aGJE1b'); - if (member.innerText.endsWith(' has left the meeting')) { - return member.innerText.replace(' has left the meeting', ''); - } else { - return null; - } - }) - if (member === null) {continue;} - await Meet.emit('memberLeave', members[member]); - await Meet.page.waitForSelector('.aGJE1b', { hidden: true, timeout: 0 }); - } - -} - -async function memberListener(Meet) { - - async function getMembers() { - var members = await Meet.page.evaluate(() => { - members = {}; - member_list = document.querySelector('div[role="list"]'); - for (var i = 0; i < member_list.children.length; i++) { - member = { - name: member_list.children[i].firstChild.lastChild.firstChild.firstChild.innerText, - icon: member_list.children[i].firstChild.firstChild.firstChild.src - } - members[member.name] = member; - } - return members; - }) - Meet.members = members; - } - await getMembers(); - - memberJoinListener(Meet); - memberLeaveListener(Meet); - - await Meet.page.exposeFunction('getMembers', getMembers); - - await Meet.page.evaluate(() => { - memberObserver = new MutationObserver(() => {getMembers();}); - memberObserver.observe(document.querySelector('div[role="list"]'), { subtree: true, childList: true }); - }) - -} - -module.exports = { - memberListener: memberListener -} diff --git a/core/member.ts b/core/member.ts new file mode 100644 index 0000000..413855a --- /dev/null +++ b/core/member.ts @@ -0,0 +1,73 @@ +// Member Listener + +async function memberJoinListener(Meet) { + while (true) { + await Meet.page.waitForSelector(".iLNCXe", { visible: true, timeout: 0 }); // wait for member to join + let member = await Meet.page.evaluate(() => { + const member = document.querySelector(".iLNCXe") as HTMLElement; + return member.innerText.replace(" has joined", ""); + }); + await Meet.emit("memberJoin", Meet.members[member]); + await Meet.page.waitForSelector(".iLNCXe", { hidden: true, timeout: 0 }); + } +} + +async function memberLeaveListener(Meet) { + while (true) { + let members = Meet.members; // memberLeaveListener keeps own copy of member list (because when a member leaves, the list gets updated and memberLeaveListener doesn't get the member's info) + await Meet.page.waitForSelector(".aGJE1b", { visible: true, timeout: 0 }); // wait for member to leave + let member = await Meet.page.evaluate(() => { + member = document.querySelector(".aGJE1b"); + if (member.innerText.endsWith(" has left the meeting")) { + return member.innerText.replace(" has left the meeting", ""); + } else { + return null; + } + }); + if (member === null) { + continue; + } + await Meet.emit("memberLeave", members[member]); + await Meet.page.waitForSelector(".aGJE1b", { hidden: true, timeout: 0 }); + } +} + +export async function memberListener(Meet) { + async function getMembers() { + let members = await Meet.page.evaluate(() => { + let mems = {}; + let member_list = document.querySelector('div[role="list"]'); + for (let i = 0; i < member_list.children.length; i++) { + let member = { + name: ( + member_list.children[i].firstChild.lastChild.firstChild + .firstChild as HTMLElement + ).innerText, + icon: ( + member_list.children[i].firstChild.firstChild + .firstChild as HTMLImageElement + ).src, + }; + mems[member.name] = member; + } + return mems; + }); + Meet.members = members; + } + await getMembers(); + + memberJoinListener(Meet); + memberLeaveListener(Meet); + + await Meet.page.exposeFunction("getMembers", getMembers); + + await Meet.page.evaluate(() => { + let memberObserver = new MutationObserver(() => { + getMembers(); + }); + memberObserver.observe(document.querySelector('div[role="list"]'), { + subtree: true, + childList: true, + }); + }); +} diff --git a/core/message.js b/core/message.js deleted file mode 100644 index 19aa845..0000000 --- a/core/message.js +++ /dev/null @@ -1,36 +0,0 @@ -// Message Listener - -async function messageListener(Meet) { - - async function getRecentMessage() { - var message = await Meet.page.evaluate(() => { - chat = document.querySelector('.z38b6').lastChild; - return { - author: chat.firstChild.firstChild.innerText, - time: chat.firstChild.lastChild.innerText, - content: chat.lastChild.lastChild.innerText - }; // See div.html - }) - if (message.author !== "You") { - await Meet.emit('message', message); - Meet.recentMessage = message; - return message; - } - } - - await Meet.page.waitForSelector('.GDhqjd', { timeout: 0 }); getRecentMessage(); - - await Meet.page.exposeFunction('getRecentMessage', getRecentMessage) - - await Meet.page.evaluate(() => { - // https://stackoverflow.com/questions/47903954/how-to-inject-mutationobserver-to-puppeteer - // https://stackoverflow.com/questions/54109078/puppeteer-wait-for-page-dom-updates-respond-to-new-items-that-are-added-after/54110446#54110446 - messageObserver = new MutationObserver(() => {getRecentMessage();}); - messageObserver.observe(document.querySelector('.z38b6'), { subtree: true, childList: true }); - }); - -} - -module.exports = { - messageListener: messageListener -} diff --git a/core/message.ts b/core/message.ts new file mode 100644 index 0000000..22c6c48 --- /dev/null +++ b/core/message.ts @@ -0,0 +1,36 @@ +// Message Listener + +export async function messageListener(Meet) { + async function getRecentMessage() { + var message = await Meet.page.evaluate(() => { + let chat = document.querySelector(".z38b6").lastChild; + return { + author: (chat.firstChild.firstChild as HTMLElement).innerText, + time: (chat.firstChild.lastChild as HTMLElement).innerText, + content: (chat.lastChild.lastChild as HTMLElement).innerText, + }; // See div.html + }); + if (message.author !== "You") { + await Meet.emit("message", message); + Meet.recentMessage = message; + return message; + } + } + + await Meet.page.waitForSelector(".GDhqjd", { timeout: 0 }); + getRecentMessage(); + + await Meet.page.exposeFunction("getRecentMessage", getRecentMessage); + + await Meet.page.evaluate(() => { + // https://stackoverflow.com/questions/47903954/how-to-inject-mutationobserver-to-puppeteer + // https://stackoverflow.com/questions/54109078/puppeteer-wait-for-page-dom-updates-respond-to-new-items-that-are-added-after/54110446#54110446 + let messageObserver = new MutationObserver(() => { + getRecentMessage(); + }); + messageObserver.observe(document.querySelector(".z38b6"), { + subtree: true, + childList: true, + }); + }); +} diff --git a/examples/start.js b/examples/start.js deleted file mode 100644 index 21afecf..0000000 --- a/examples/start.js +++ /dev/null @@ -1,39 +0,0 @@ -const { Meet } = require('../meet'); -const client = new Meet(); - -config = { meetingLink: 'https://meet.google.com/xyz-wxyz-xyz', email: '', pw: '' }; - -async function command(client, message) { - if (message.content.startsWith("!quote")) { - await client.sendMessage(`${message.author} said, "${message.content.replace("!quote ", "")}" at ${message.time}`); - } - -} - -(async () => { - - await client.once('ready', async () => { - console.log('ready'); - }) - - await client.login(config); - - await client.on('message', async (message) => { - command(client, message); - }) - - await client.on('memberJoin', async (member) => { - await client.sendMessage(`Welcome, ${member.name}!`); - }) - - await client.on('memberLeave', async (member) => { - await client.sendMessage(`Goodbye, ${member.name}!`); - }) - -})() - -/* - Async/await syntax is required if you need to execute specific actions with Puppteer or don't want to be limited to only the events already implemented. -*/ - -// If errors like "Node is detached" get thrown, restarting almost always fixes most errors diff --git a/examples/start.ts b/examples/start.ts new file mode 100644 index 0000000..85d6fff --- /dev/null +++ b/examples/start.ts @@ -0,0 +1,44 @@ +import { Meet } from "../meet"; +const client = new Meet(); + +let config = { + meetingLink: "https://meet.google.com/xyz-wxyz-xyz", + email: "", + pw: "", +}; + +async function command(client, message) { + if (message.content.startsWith("!quote")) { + await client.sendMessage( + `${message.author} said, "${message.content.replace("!quote ", "")}" at ${ + message.time + }`, + ); + } +} + +(async () => { + client.once("ready", async () => { + console.log("ready"); + }); + + await client.login(config); + + client.on("message", async (message) => { + command(client, message); + }); + + client.on("memberJoin", async (member) => { + await client.sendMessage(`Welcome, ${member.name}!`); + }); + + client.on("memberLeave", async (member) => { + await client.sendMessage(`Goodbye, ${member.name}!`); + }); +})(); + +/* + Async/await syntax is required if you need to execute specific actions with Puppteer or don't want to be limited to only the events already implemented. +*/ + +// If errors like "Node is detached" get thrown, restarting almost always fixes most errors diff --git a/meet.js b/meet.js deleted file mode 100644 index eae1b69..0000000 --- a/meet.js +++ /dev/null @@ -1,62 +0,0 @@ -const EventEmitter = require('events'); -const puppeteer = require('puppeteer-extra'); -const StealthPlugin = require('puppeteer-extra-plugin-stealth'); -puppeteer.use(StealthPlugin()); - -const authenticate = require('./core/authenticate'); -const meeting = require('./core/meeting'); -const message = require('./core/message'); -const member = require('./core/member'); -const audio = require('./core/audio'); // Not working - - -class Meet extends EventEmitter { - - constructor() { - super(); - console.log("Client created!") - - // Listeners (for use in login function) - this.message = message; - this.member = member; - this.audio = audio; - - this.meetingLink = undefined; - this.email = undefined - - this.puppeteer = puppeteer; - this.browser = undefined; - this.page = undefined; - this.ctx = undefined; - - this.isMicEnabled = true; - this.isVideoEnabled = true; - this.isChatEnabled = undefined; - - this.recentMessage = undefined; - this.members = undefined; - }; - - login = authenticate.auth; - - toggleMic = meeting.toggleMic; - toggleVideo = meeting.toggleVideo; - toggleChat = meeting.toggleChat; - toggleMemberList = meeting.toggleMemberList; - chatEnabled = meeting.chatEnabled; - sendMessage = meeting.sendMessage; - screenshot = meeting.screenshot; - -} - -module.exports.Meet = Meet; - -/* Notes */ - -// Various XPaths and element class names or ids are not explained throughout this source; there's probably a better way to have a permanent selector to a specific element though - -// The Audio part of this package has not yet been implemented - -// This code can be improved in many ways, but I wrote this during the beginning of Covid lockdown; I've only now decided to add a license and create a repository to publish - -// This package aims to be similar to the Discord JS library diff --git a/meet.ts b/meet.ts new file mode 100644 index 0000000..8c9e874 --- /dev/null +++ b/meet.ts @@ -0,0 +1,76 @@ +import { EventEmitter } from "events"; +import puppeteer from "puppeteer-extra"; +import StealthPlugin from "puppeteer-extra-plugin-stealth"; + +import authenticate from "./core/authenticate"; +import * as meeting from "./core/meeting"; +import * as message from "./core/message"; +import * as member from "./core/member"; +import audio from "./core/audio"; // not working + +type puppeteer = typeof puppeteer; + +export class Meet extends EventEmitter { + message: typeof message; + member: typeof member; + audio: typeof audio; + + meetingLink: string | undefined; + email: string | undefined; + + puppeteer: puppeteer; + browser: undefined; + page: undefined; + ctx: undefined; + + isMicEnabled: boolean; + isVideoEnabled: boolean; + isChatEnabled: boolean | undefined; + + recentMessage: string | undefined; + members: string[] | undefined; + constructor() { + super(); + console.log("Client created!"); + + // Listeners (for use in login function) + this.message = message; + this.member = member; + this.audio = audio; + + this.meetingLink = undefined; + this.email = undefined; + + this.puppeteer = puppeteer.use(StealthPlugin()); + this.browser = undefined; + this.page = undefined; + this.ctx = undefined; + + this.isMicEnabled = true; + this.isVideoEnabled = true; + this.isChatEnabled = undefined; + + this.recentMessage = undefined; + this.members = undefined; + } + + login = authenticate; + + toggleMic = meeting.toggleMic; + toggleVideo = meeting.toggleVideo; + toggleChat = meeting.toggleChat; + toggleMemberList = meeting.toggleMemberList; + chatEnabled = meeting.chatEnabled; + sendMessage = meeting.sendMessage; + screenshot = meeting.screenshot; +} + +/* Notes */ + +// Various XPaths and element class names or ids are not explained throughout this source; there's probably a better way to have a permanent selector to a specific element though + +// The Audio part of this package has not yet been implemented + +// This code can be improved in many ways, but I wrote this during the beginning of Covid lockdown; I've only now decided to add a license and create a repository to publish + +// This package aims to be similar to the Discord JS library diff --git a/package-lock.json b/package-lock.json index 07743e7..7def4a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,9 @@ "puppeteer": "^13.7.0", "puppeteer-extra": "^3.2.3", "puppeteer-extra-plugin-stealth": "^2.9.0" + }, + "devDependencies": { + "typescript": "^5.3.3" } }, "node_modules/@types/debug": { @@ -836,6 +839,19 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", @@ -1507,6 +1523,12 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, + "typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true + }, "unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", diff --git a/package.json b/package.json index a3f9f1b..9f20e39 100644 --- a/package.json +++ b/package.json @@ -29,5 +29,8 @@ "puppeteer": "^13.7.0", "puppeteer-extra": "^3.2.3", "puppeteer-extra-plugin-stealth": "^2.9.0" + }, + "devDependencies": { + "typescript": "^5.3.3" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3d6605e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "outDir": "./built", + "allowJs": true, + "target": "es5", + "esModuleInterop": true + }, + "include": ["./**/*"] +} From c84ea08669401635c96a265b411acbfa15b00f7d Mon Sep 17 00:00:00 2001 From: Ahmad Reza Date: Mon, 4 Mar 2024 14:27:19 +0700 Subject: [PATCH 2/3] delete auto generated file --- built/core/audio.js | 61 ------------ built/core/authenticate.js | 187 ------------------------------------- built/core/meeting.js | 183 ------------------------------------ built/core/member.js | 175 ---------------------------------- built/core/message.js | 96 ------------------- built/examples/start.js | 107 --------------------- built/meet.js | 90 ------------------ 7 files changed, 899 deletions(-) delete mode 100644 built/core/audio.js delete mode 100644 built/core/authenticate.js delete mode 100644 built/core/meeting.js delete mode 100644 built/core/member.js delete mode 100644 built/core/message.js delete mode 100644 built/examples/start.js delete mode 100644 built/meet.js diff --git a/built/core/audio.js b/built/core/audio.js deleted file mode 100644 index 2b8324d..0000000 --- a/built/core/audio.js +++ /dev/null @@ -1,61 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var MeetAudio = /** @class */ (function () { - function MeetAudio(page) { - // 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; - } - MeetAudio.prototype.stream = function (_source) { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - return [2 /*return*/]; - }); - }); - }; - MeetAudio.prototype.stop = function () { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - return [2 /*return*/]; - }); - }); - }; - return MeetAudio; -}()); -exports.default = MeetAudio; diff --git a/built/core/authenticate.js b/built/core/authenticate.js deleted file mode 100644 index 542166f..0000000 --- a/built/core/authenticate.js +++ /dev/null @@ -1,187 +0,0 @@ -"use strict"; -// Main initializing function -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -// 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 -function authenticate(_a) { - var meetingLink = _a.meetingLink, email = _a.email, pw = _a.pw; - return __awaiter(this, void 0, void 0, function () { - var _b, _c, _d, signInButton, e_1, input, input, join, i; - return __generator(this, function (_e) { - switch (_e.label) { - case 0: - 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; - _b = this; - return [4 /*yield*/, this.puppeteer.launch({ headless: false })]; - case 1: - _b.browser = _e.sent(); - _c = this; - return [4 /*yield*/, this.browser.newPage()]; - case 2: - _c.page = _e.sent(); - _d = this; - return [4 /*yield*/, this.browser.defaultBrowserContext()]; - case 3: - _d.ctx = _e.sent(); - return [4 /*yield*/, this.ctx.overridePermissions("https://meet.google.com", [ - "microphone", - "camera", - "notifications", - ])]; - case 4: - _e.sent(); - return [4 /*yield*/, this.page.goto(meetingLink)]; - case 5: - _e.sent(); - // Authenticating with credentials - console.log("Logging in..."); - _e.label = 6; - case 6: - _e.trys.push([6, 10, , 11]); - return [4 /*yield*/, this.page.waitForSelector(".NPEfkd", { - visible: true, - timeout: 10000, - })]; - case 7: - signInButton = _e.sent(); - return [4 /*yield*/, signInButton.focus()]; - case 8: - _e.sent(); - return [4 /*yield*/, signInButton.click()]; - case 9: - _e.sent(); - return [3 /*break*/, 11]; - case 10: - e_1 = _e.sent(); - console.log(e_1); - return [3 /*break*/, 11]; - case 11: return [4 /*yield*/, this.page.waitForSelector("input[type=email]", { - visible: true, - timeout: 0, - })]; - case 12: - input = _e.sent(); - return [4 /*yield*/, input.focus()]; - case 13: - _e.sent(); - return [4 /*yield*/, this.page.keyboard.type(email)]; - case 14: - _e.sent(); - return [4 /*yield*/, this.page.keyboard.press("Enter")]; - case 15: - _e.sent(); - return [4 /*yield*/, this.page.waitForSelector("input[type=password]", { - visible: true, - timeout: 0, - })]; - case 16: - input = _e.sent(); - return [4 /*yield*/, input.focus()]; - case 17: - _e.sent(); - return [4 /*yield*/, this.page.keyboard.type(pw)]; - case 18: - _e.sent(); - return [4 /*yield*/, this.page.keyboard.press("Enter")]; - case 19: - _e.sent(); - console.log("Authenticated successfully!"); - return [4 /*yield*/, this.screenshot("logged-in.png")]; - case 20: - _e.sent(); // 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 - return [4 /*yield*/, this.page.waitForSelector("div[role=button]")]; - case 21: - // 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 - _e.sent(); - return [4 /*yield*/, this.page.waitForSelector(".VfPpkd-vQzf8d", { - visible: true, - timeout: 0, - })]; - case 22: - join = _e.sent(); - i = 3; - _e.label = 23; - case 23: - if (!(i > 0)) return [3 /*break*/, 27]; - return [4 /*yield*/, this.toggleMic(this.page)]; - case 24: - _e.sent(); - return [4 /*yield*/, this.toggleVideo(this.page)]; - case 25: - _e.sent(); - _e.label = 26; - case 26: - i--; - return [3 /*break*/, 23]; - case 27: // toggle mic and video 3 times because Google Meet glitches and leaves mic on if it's toggled as soon as page loads - return [4 /*yield*/, join.click()]; - case 28: - _e.sent(); - // 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. - return [4 /*yield*/, 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 })]; - case 29: - // 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. - _e.sent(); // wait for chat button - return [4 /*yield*/, this.toggleMemberList()]; - case 30: - _e.sent(); - return [4 /*yield*/, this.toggleChat()]; - case 31: - _e.sent(); - 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"); - return [2 /*return*/]; - } - }); - }); -} -exports.default = authenticate; diff --git a/built/core/meeting.js b/built/core/meeting.js deleted file mode 100644 index 6110884..0000000 --- a/built/core/meeting.js +++ /dev/null @@ -1,183 +0,0 @@ -"use strict"; -// Core meeting methods -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.screenshot = exports.sendMessage = exports.chatEnabled = exports.toggleMemberList = exports.toggleChat = exports.toggleVideo = exports.toggleMic = void 0; -function toggleMic() { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, this.page.keyboard.down("ControlLeft")]; - case 1: - _a.sent(); - return [4 /*yield*/, this.page.keyboard.press("KeyD")]; - case 2: - _a.sent(); - return [4 /*yield*/, this.page.keyboard.up("ControlLeft")]; - case 3: - _a.sent(); - this.isMicEnabled = !this.isMicEnabled; - return [2 /*return*/]; - } - }); - }); -} -exports.toggleMic = toggleMic; -function toggleVideo() { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, this.page.keyboard.down("ControlLeft")]; - case 1: - _a.sent(); - return [4 /*yield*/, this.page.keyboard.press("KeyE")]; - case 2: - _a.sent(); - return [4 /*yield*/, this.page.keyboard.up("ControlLeft")]; - case 3: - _a.sent(); - this.isVideoEnabled = !this.isVideoEnabled; - return [2 /*return*/]; - } - }); - }); -} -exports.toggleVideo = toggleVideo; -function toggleChat() { - return __awaiter(this, void 0, void 0, function () { - var chatBtn; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, 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")]; - case 1: - chatBtn = _a.sent(); - return [4 /*yield*/, chatBtn.click()]; - case 2: - _a.sent(); - return [2 /*return*/]; - } - }); - }); -} -exports.toggleChat = toggleChat; -function toggleMemberList() { - return __awaiter(this, void 0, void 0, function () { - var memberListBtn; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, 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")]; - case 1: - memberListBtn = _a.sent(); - return [4 /*yield*/, memberListBtn.click()]; - case 2: - _a.sent(); - return [2 /*return*/]; - } - }); - }); -} -exports.toggleMemberList = toggleMemberList; -function chatEnabled() { - return __awaiter(this, void 0, void 0, function () { - var disabled; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, this.page.waitForSelector("#bfTqV")]; - case 1: - _a.sent(); - return [4 /*yield*/, this.page.evaluate(function () { - disabled = document.querySelector("#bfTqV"); - if (disabled.disabled === false) { - return true; - } - else if (disabled.disabled === true) { - return false; - } - })]; - case 2: - disabled = _a.sent(); - return [2 /*return*/, disabled]; - } - }); - }); -} -exports.chatEnabled = chatEnabled; -function sendMessage(message) { - return __awaiter(this, void 0, void 0, function () { - var chat; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, this.chatEnabled()]; - case 1: - if (!_a.sent()) return [3 /*break*/, 6]; - return [4 /*yield*/, this.page.waitForSelector("#bfTqV")]; - case 2: - chat = _a.sent(); - return [4 /*yield*/, chat.focus()]; - case 3: - _a.sent(); - return [4 /*yield*/, this.page.$eval("#bfTqV", function (input, message) { - input.value = message; - console.log(input); - console.log(message); - }, message)]; - case 4: - _a.sent(); // replaced `await page.keyboard.type(message)`, because this is a little more instant - return [4 /*yield*/, this.page.keyboard.press("Enter")]; - case 5: - _a.sent(); - _a.label = 6; - case 6: return [2 /*return*/]; - } - }); - }); -} -exports.sendMessage = sendMessage; -function screenshot(path) { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, this.page.screenshot({ path: path, fullPage: true })]; - case 1: - _a.sent(); - return [2 /*return*/]; - } - }); - }); -} -exports.screenshot = screenshot; diff --git a/built/core/member.js b/built/core/member.js deleted file mode 100644 index 9c66588..0000000 --- a/built/core/member.js +++ /dev/null @@ -1,175 +0,0 @@ -"use strict"; -// Member Listener -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.memberListener = void 0; -function memberJoinListener(Meet) { - return __awaiter(this, void 0, void 0, function () { - var member; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (!true) return [3 /*break*/, 5]; - return [4 /*yield*/, Meet.page.waitForSelector(".iLNCXe", { visible: true, timeout: 0 })]; - case 1: - _a.sent(); // wait for member to join - return [4 /*yield*/, Meet.page.evaluate(function () { - var member = document.querySelector(".iLNCXe"); - return member.innerText.replace(" has joined", ""); - })]; - case 2: - member = _a.sent(); - return [4 /*yield*/, Meet.emit("memberJoin", Meet.members[member])]; - case 3: - _a.sent(); - return [4 /*yield*/, Meet.page.waitForSelector(".iLNCXe", { hidden: true, timeout: 0 })]; - case 4: - _a.sent(); - return [3 /*break*/, 0]; - case 5: return [2 /*return*/]; - } - }); - }); -} -function memberLeaveListener(Meet) { - return __awaiter(this, void 0, void 0, function () { - var _loop_1; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - _loop_1 = function () { - var members, member; - return __generator(this, function (_b) { - switch (_b.label) { - case 0: - members = Meet.members; - return [4 /*yield*/, Meet.page.waitForSelector(".aGJE1b", { visible: true, timeout: 0 })]; - case 1: - _b.sent(); // wait for member to leave - return [4 /*yield*/, Meet.page.evaluate(function () { - member = document.querySelector(".aGJE1b"); - if (member.innerText.endsWith(" has left the meeting")) { - return member.innerText.replace(" has left the meeting", ""); - } - else { - return null; - } - })]; - case 2: - member = _b.sent(); - if (member === null) { - return [2 /*return*/, "continue"]; - } - return [4 /*yield*/, Meet.emit("memberLeave", members[member])]; - case 3: - _b.sent(); - return [4 /*yield*/, Meet.page.waitForSelector(".aGJE1b", { hidden: true, timeout: 0 })]; - case 4: - _b.sent(); - return [2 /*return*/]; - } - }); - }; - _a.label = 1; - case 1: - if (!true) return [3 /*break*/, 3]; - return [5 /*yield**/, _loop_1()]; - case 2: - _a.sent(); - return [3 /*break*/, 1]; - case 3: return [2 /*return*/]; - } - }); - }); -} -function memberListener(Meet) { - return __awaiter(this, void 0, void 0, function () { - function getMembers() { - return __awaiter(this, void 0, void 0, function () { - var members; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, Meet.page.evaluate(function () { - var mems = {}; - var member_list = document.querySelector('div[role="list"]'); - for (var i = 0; i < member_list.children.length; i++) { - var member = { - name: member_list.children[i].firstChild.lastChild.firstChild - .firstChild.innerText, - icon: member_list.children[i].firstChild.firstChild - .firstChild.src, - }; - mems[member.name] = member; - } - return mems; - })]; - case 1: - members = _a.sent(); - Meet.members = members; - return [2 /*return*/]; - } - }); - }); - } - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, getMembers()]; - case 1: - _a.sent(); - memberJoinListener(Meet); - memberLeaveListener(Meet); - return [4 /*yield*/, Meet.page.exposeFunction("getMembers", getMembers)]; - case 2: - _a.sent(); - return [4 /*yield*/, Meet.page.evaluate(function () { - var memberObserver = new MutationObserver(function () { - getMembers(); - }); - memberObserver.observe(document.querySelector('div[role="list"]'), { - subtree: true, - childList: true, - }); - })]; - case 3: - _a.sent(); - return [2 /*return*/]; - } - }); - }); -} -exports.memberListener = memberListener; diff --git a/built/core/message.js b/built/core/message.js deleted file mode 100644 index 7595b85..0000000 --- a/built/core/message.js +++ /dev/null @@ -1,96 +0,0 @@ -"use strict"; -// Message Listener -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.messageListener = void 0; -function messageListener(Meet) { - return __awaiter(this, void 0, void 0, function () { - function getRecentMessage() { - return __awaiter(this, void 0, void 0, function () { - var message; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, Meet.page.evaluate(function () { - var chat = document.querySelector(".z38b6").lastChild; - return { - author: chat.firstChild.firstChild.innerText, - time: chat.firstChild.lastChild.innerText, - content: chat.lastChild.lastChild.innerText, - }; // See div.html - })]; - case 1: - message = _a.sent(); - if (!(message.author !== "You")) return [3 /*break*/, 3]; - return [4 /*yield*/, Meet.emit("message", message)]; - case 2: - _a.sent(); - Meet.recentMessage = message; - return [2 /*return*/, message]; - case 3: return [2 /*return*/]; - } - }); - }); - } - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, Meet.page.waitForSelector(".GDhqjd", { timeout: 0 })]; - case 1: - _a.sent(); - getRecentMessage(); - return [4 /*yield*/, Meet.page.exposeFunction("getRecentMessage", getRecentMessage)]; - case 2: - _a.sent(); - return [4 /*yield*/, Meet.page.evaluate(function () { - // https://stackoverflow.com/questions/47903954/how-to-inject-mutationobserver-to-puppeteer - // https://stackoverflow.com/questions/54109078/puppeteer-wait-for-page-dom-updates-respond-to-new-items-that-are-added-after/54110446#54110446 - var messageObserver = new MutationObserver(function () { - getRecentMessage(); - }); - messageObserver.observe(document.querySelector(".z38b6"), { - subtree: true, - childList: true, - }); - })]; - case 3: - _a.sent(); - return [2 /*return*/]; - } - }); - }); -} -exports.messageListener = messageListener; diff --git a/built/examples/start.js b/built/examples/start.js deleted file mode 100644 index 336cb0b..0000000 --- a/built/examples/start.js +++ /dev/null @@ -1,107 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -var meet_1 = require("../meet"); -var client = new meet_1.Meet(); -var config = { - meetingLink: "https://meet.google.com/xyz-wxyz-xyz", - email: "", - pw: "", -}; -function command(client, message) { - return __awaiter(this, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - if (!message.content.startsWith("!quote")) return [3 /*break*/, 2]; - return [4 /*yield*/, client.sendMessage("".concat(message.author, " said, \"").concat(message.content.replace("!quote ", ""), "\" at ").concat(message.time))]; - case 1: - _a.sent(); - _a.label = 2; - case 2: return [2 /*return*/]; - } - }); - }); -} -(function () { return __awaiter(void 0, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - client.once("ready", function () { return __awaiter(void 0, void 0, void 0, function () { - return __generator(this, function (_a) { - console.log("ready"); - return [2 /*return*/]; - }); - }); }); - return [4 /*yield*/, client.login(config)]; - case 1: - _a.sent(); - client.on("message", function (message) { return __awaiter(void 0, void 0, void 0, function () { - return __generator(this, function (_a) { - command(client, message); - return [2 /*return*/]; - }); - }); }); - client.on("memberJoin", function (member) { return __awaiter(void 0, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, client.sendMessage("Welcome, ".concat(member.name, "!"))]; - case 1: - _a.sent(); - return [2 /*return*/]; - } - }); - }); }); - client.on("memberLeave", function (member) { return __awaiter(void 0, void 0, void 0, function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, client.sendMessage("Goodbye, ".concat(member.name, "!"))]; - case 1: - _a.sent(); - return [2 /*return*/]; - } - }); - }); }); - return [2 /*return*/]; - } - }); -}); })(); -/* - Async/await syntax is required if you need to execute specific actions with Puppteer or don't want to be limited to only the events already implemented. -*/ -// If errors like "Node is detached" get thrown, restarting almost always fixes most errors diff --git a/built/meet.js b/built/meet.js deleted file mode 100644 index b9559ff..0000000 --- a/built/meet.js +++ /dev/null @@ -1,90 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Meet = void 0; -var events_1 = require("events"); -var puppeteer_extra_1 = __importDefault(require("puppeteer-extra")); -var puppeteer_extra_plugin_stealth_1 = __importDefault(require("puppeteer-extra-plugin-stealth")); -var authenticate_1 = __importDefault(require("./core/authenticate")); -var meeting = __importStar(require("./core/meeting")); -var message = __importStar(require("./core/message")); -var member = __importStar(require("./core/member")); -var audio_1 = __importDefault(require("./core/audio")); // not working -var Meet = /** @class */ (function (_super) { - __extends(Meet, _super); - function Meet() { - var _this = _super.call(this) || this; - _this.login = authenticate_1.default; - _this.toggleMic = meeting.toggleMic; - _this.toggleVideo = meeting.toggleVideo; - _this.toggleChat = meeting.toggleChat; - _this.toggleMemberList = meeting.toggleMemberList; - _this.chatEnabled = meeting.chatEnabled; - _this.sendMessage = meeting.sendMessage; - _this.screenshot = meeting.screenshot; - console.log("Client created!"); - // Listeners (for use in login function) - _this.message = message; - _this.member = member; - _this.audio = audio_1.default; - _this.meetingLink = undefined; - _this.email = undefined; - _this.puppeteer = puppeteer_extra_1.default.use((0, puppeteer_extra_plugin_stealth_1.default)()); - _this.browser = undefined; - _this.page = undefined; - _this.ctx = undefined; - _this.isMicEnabled = true; - _this.isVideoEnabled = true; - _this.isChatEnabled = undefined; - _this.recentMessage = undefined; - _this.members = undefined; - return _this; - } - return Meet; -}(events_1.EventEmitter)); -exports.Meet = Meet; -/* Notes */ -// Various XPaths and element class names or ids are not explained throughout this source; there's probably a better way to have a permanent selector to a specific element though -// The Audio part of this package has not yet been implemented -// This code can be improved in many ways, but I wrote this during the beginning of Covid lockdown; I've only now decided to add a license and create a repository to publish -// This package aims to be similar to the Discord JS library From 2c90229572a6f9a6f75397d10fa0defc28024baa Mon Sep 17 00:00:00 2001 From: Ahmad Reza Date: Thu, 7 Mar 2024 13:54:00 +0700 Subject: [PATCH 3/3] added env --- .env.example | 2 ++ .gitignore | 1 + examples/start.ts | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0a99e1a --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +GOOGLE_USERNAME="john@gmail.com" +GOOGLE_PASSWORD="password" diff --git a/.gitignore b/.gitignore index 008001b..f9e9f95 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ built/ docs/ +.env diff --git a/examples/start.ts b/examples/start.ts index 85d6fff..907f772 100644 --- a/examples/start.ts +++ b/examples/start.ts @@ -3,8 +3,8 @@ const client = new Meet(); let config = { meetingLink: "https://meet.google.com/xyz-wxyz-xyz", - email: "", - pw: "", + email: process.env.GOOGLE_USERNAME, + pw: process.env.GOOGLE_PASSWORD, }; async function command(client, message) {