diff --git a/README.md b/README.md index 7c71250..5819419 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,18 @@ # GoogleMeetBot A bot to automatically join Google Meet meetings at the correct time + + +## How it works +The bot automates a FireFox session using Python and Selenium.
+It joins the programmed google meetings automatically, with microphone and webcam disabled.
+It uses the **schedule** python module to automatically access and close meetings at the given time, it can only join one meeting at the time
+Normally Google Accounts are not allowed to be logged in when using an automated browser, but logging in via stackoverflow.com worksaround this problem
+In case this is not working, you can disable such security feature in your google account settings. Some business and enterprise account may not have this problem by default + +## Dependencies +All python deps are listed in **requirements.txt**, just run
+ pip install -r requirements.txt + +## Add your own meetings +To add your own meetings into **meetings.py**, adding another scheduleMeeting line like the one already present. You can add as many as you want, but remember that this bot does not support joining multiple meetings at the same time + diff --git a/browser_manager.py b/browser_manager.py new file mode 100644 index 0000000..74a95e3 --- /dev/null +++ b/browser_manager.py @@ -0,0 +1,137 @@ +import sys +import selenium +import requests +from selenium import webdriver +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.common.exceptions import TimeoutException +from selenium.webdriver.common.by import By +import os +from enum import Enum +import threading +from selenium.webdriver.common.keys import Keys +from selenium.common.exceptions import NoSuchElementException + +import time as t + + +G_ACCOUNT_MAIL = None +G_ACCOUNT_PASS = None + +FIREFOX_DVD_DIR = "/usr/bin/geckodriver" + +'''Locating by xpath here is the best thing to do here, since google Meet changes selectors, classes name and all that sort of stuff for every meeting + XPaths remaing the same, but a slight change by them would make this program fail. + The xpath is found clicking by inspecting the element of the searched button, and finding the parent div tthat has role="button" tag +''' + +MIC_XPATH = '/html/body/div[1]/c-wiz/div/div/div[8]/div[3]/div/div/div[2]/div/div[1]/div[1]/div[1]/div/div[3]/div[1]/div/div/div' +WEBCAM_XPATH = '/html/body/div[1]/c-wiz/div/div/div[8]/div[3]/div/div/div[2]/div/div[1]/div[1]/div[1]/div/div[3]/div[2]/div/div' +JOIN_XPATH = '/html/body/div[1]/c-wiz/div/div/div[8]/div[3]/div/div/div[2]/div/div[1]/div[2]/div/div[2]/div/div/div[1]' +OPTION_XPATH = '/html/body/div[1]/c-wiz/div/div/div[6]/div[3]/div/div/div[2]/div/div[1]/div[1]/div[1]/div/div[4]/div' + +CHAT_BTN_XPATH = '/html/body/div[1]/c-wiz/div[1]/div/div[6]/div[3]/div[6]/div[3]/div/div[2]/div[3]' +CHAT_SELECTCHAT_BTN_XPATH = '/html/body/div[1]/c-wiz/div[1]/div/div[6]/div[3]/div[3]/div/div[2]/div[2]/div[1]/div[2]' + +#Using tagname for text area because xpath doesn't really work, and we're sure it's the only textarea on the webpage +CHAT_TEXT_XPATH = "textarea" + +HANG_UP_BTN_XPATH = '/html/body/div[1]/c-wiz/div[1]/div/div[8]/div[3]/div[9]/div[2]/div[2]/div' + +CHAT_CLOSE_BTN_XPATH = '/html/body/div[1]/c-wiz/div[1]/div/div[6]/div[3]/div[3]/div/div[2]/div[1]/div[2]/div/button' + +browser = None + +def initFirefox(): + global browser, G_ACCOUNT_MAIL, G_ACCOUNT_PASS + + print("Input your google account credentials. The information will not be stored anywhere and you will have to relogin everytime you restart this bot\n") + G_ACCOUNT_MAIL = input("Type your email: ") + G_ACCOUNT_PASS = input("Type your password: ") + + #webdriver.FirefoxProfile('/home/emamaker/Documents/Projects/GoogleMeetBot/firefox_profile') + browser = webdriver.Firefox() + +def joinMeeting(link): + global browser + + if link == '': + return + + try: + browser.get(link) + t.sleep(15) + print("Trying to join meeting") + clickButton(By.XPATH, MIC_XPATH) + clickButton(By.XPATH, WEBCAM_XPATH) + clickButton(By.XPATH, JOIN_XPATH) + except: + # In this way, in case of any error we can try again + print("Failed to join meeting, trying again in 60 secs") + t.sleep(60) + joinMeeting(link) + + +def clickButton(by, selector): + global browser + WebDriverWait(browser, 5).until(EC.element_to_be_clickable((by, selector))).click() + t.sleep(1) + +def writeText(by, selector, text): + WebDriverWait(browser, 5).until(EC.element_to_be_clickable((by, selector))).clear() + WebDriverWait(browser, 5).until(EC.element_to_be_clickable((by, selector))).send_keys(text + "\n") + +def sendChatMsg(text): + global browser + + #open chat menu + clickButton(By.XPATH, CHAT_BTN_XPATH) + #select chat option + clickButton(By.XPATH, CHAT_SELECTCHAT_BTN_XPATH) + #write msg + writeText(By.TAG_NAME, CHAT_BTN_XPATH, text) + t.sleep(1) + #close chat + clickButton(By.XPATH, CHAT_CLOSE_BTN_XPATH) + + +def checkStarted(): + try: + clickButton(By.XPATH, OPTION_XPATH) + except: + return False + return True + +def loginIntoGoogle(): + global browser + browser.get("https://myaccount.google.com/?utm_source=sign_in_no_continue") + t.sleep(3) + #we can use selectors in this webside, since they're static + #login button + clickButton(By.CSS_SELECTOR, "li.h-c-header__cta-li:nth-child(2) > a:nth-child(1)") + #login with google + #clickButton(By.CSS_SELECTOR, "button.s-btn__icon:nth-child(1)") + #write email + writeText(By.CSS_SELECTOR, "#identifierId", G_ACCOUNT_MAIL) + t.sleep(3) + #write pwd + writeText(By.XPATH, "/html/body/div[1]/div[1]/div[2]/div/div[2]/div/div/div[2]/div/div[1]/div/form/span/section/div/div/div[1]/div[1]/div/div/div/div/div[1]/div/div[1]/input", G_ACCOUNT_PASS) + t.sleep(3) + +def loginIntoGoogleWithStackOverflow(): + global browser + browser.get("https://stackoverflow.com/users/login?ssrc=head&returnurl=https%3a%2f%2fstackoverflow.com%2f") + t.sleep(3) + #we can use selectors in this webside, since they're static + #login with google + clickButton(By.CSS_SELECTOR, "#openid-buttons > button.grid--cell.s-btn.s-btn__icon.s-btn__google.bar-md.ba.bc-black-100") + #login button + #write email + writeText(By.CSS_SELECTOR, "#identifierId", G_ACCOUNT_MAIL) + t.sleep(3) + #write pwd + writeText(By.XPATH, "/html/body/div[1]/div[1]/div[2]/div/div[2]/div/div/div[2]/div/div[1]/div/form/span/section/div/div/div[1]/div[1]/div/div/div/div/div[1]/div/div[1]/input", G_ACCOUNT_PASS) + t.sleep(3) + +def hangUpMeeting(): + clickButton(By.XPATH, HANG_UP_BTN_XPATH) diff --git a/gmeet_bot.py b/gmeet_bot.py new file mode 100644 index 0000000..40f1921 --- /dev/null +++ b/gmeet_bot.py @@ -0,0 +1,19 @@ +'''Automatically join Google Meet meetings, with muted camera and mic + Just give link and it joins. Features chat too + Planning on adding a schedule system''' + +import meetings +import browser_manager +import schedule +import time + +browser_manager.initFirefox() +browser_manager.loginIntoGoogleWithStackOverflow() +#while checkStarted() is False: +#browser.sendChatMsg("hello world") + +meetings.setup_schedule() + +while True: + schedule.run_pending() + time.sleep(1) \ No newline at end of file diff --git a/meetings.py b/meetings.py new file mode 100644 index 0000000..ac0729a --- /dev/null +++ b/meetings.py @@ -0,0 +1,30 @@ +import time as t +import schedule +import browser_manager + +def setup_schedule(): + scheduleMeeting("friday", "21:20", "21:22", "https://meet.google.com/jqn-fgav-aad") + + +def scheduleMeeting(day, startHour, endHour, link): + if str(day).lower() == "monday": + schedule.every().monday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().monday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "tuesday": + schedule.every().tuesday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().tuesday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "wednesday": + schedule.every().wednesday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().wednesday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "thursday": + schedule.every().thursday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().thursday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "friday": + schedule.every().friday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().friday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "saturday": + schedule.every().saturday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().saturday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "sunday": + schedule.every().sunday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().sunday.at(endHour).do(browser_manager.hangUpMeeting) diff --git a/meetings_school.py b/meetings_school.py new file mode 100644 index 0000000..3bc60e1 --- /dev/null +++ b/meetings_school.py @@ -0,0 +1,83 @@ +import time as t +import schedule +import browser_manager + +''' STORE MEET LINKS HERE''' +MATHS = 'https://meet.google.com/lookup/fn2tkngzid?authuser=0&hs=179' +LITERATURE = 'https://meet.google.com/lookup/bhoqky4qee?authuser=0&hs=179' +ENGLISH = 'https://meet.google.com/lookup/hlj4rhyj4p?authuser=0&hs=179' +ELECTRONICS = 'https://meet.google.com/lookup/ehuwfglxvr?authuser=0&hs=179' +SYSTEMS = '' +TPSEE = 'https://meet.google.com/lookup/bc4r2d32ua?authuser=0&hs=179' +TPSEE_ELT = 'https://meet.google.com/lookup/gdaq3kmguh?authuser=0&hs=179' +TPSEE_SYS = 'https://meet.google.com/lookup/gdaq3kmguh?authuser=0&hs=179' +ELT_SYSTEMS = '' + + +'''TIME SCHEDULE - START AND FINISH''' +FIRST_HOUR = "8:00" +SECOND_HOUR = "8:50" +THIRD_HOUR = "9:50" +FOURTH_HOUR = "10:50" +FIFTH_HOUR = "11:50" +SIXTH_HOUR = "12:40" +SEVENTH_HOUR = "13:40" + +def setup_schedule(): + #scheduleMeeting("friday", "20:56", "20:57", "https://meet.google.com/jqn-fgav-aad") + + # Monday schedule + scheduleMeeting("monday", FIRST_HOUR, SECOND_HOUR, TPSEE) + scheduleMeeting("monday", SECOND_HOUR, THIRD_HOUR, ENGLISH) + scheduleMeeting("monday", THIRD_HOUR, FOURTH_HOUR, SYSTEMS) + scheduleMeeting("monday", FOURTH_HOUR, FIFTH_HOUR, MATHS) + scheduleMeeting("monday", FIFTH_HOUR, SEVENTH_HOUR, LITERATURE) + + # Tuesday schedule + scheduleMeeting("tuesday", FIRST_HOUR, THIRD_HOUR, SYSTEMS) + scheduleMeeting("tuesday", THIRD_HOUR, FIFTH_HOUR, ELECTRONICS) + scheduleMeeting("tuesday", FIFTH_HOUR, SIXTH_HOUR, TPSEE_ELT) + scheduleMeeting("tuesday", SIXTH_HOUR, SEVENTH_HOUR, TPSEE) + + # Wednesday schedule + scheduleMeeting("wednesday", FIRST_HOUR, SECOND_HOUR, ELT_SYSTEMS) + scheduleMeeting("wednesday", SECOND_HOUR, THIRD_HOUR, MATHS) + scheduleMeeting("wednesday", THIRD_HOUR, FOURTH_HOUR, LITERATURE) + scheduleMeeting("wednesday", SIXTH_HOUR, SEVENTH_HOUR, LITERATURE) + + # Thursday schedule + scheduleMeeting("thursday", FIRST_HOUR, SECOND_HOUR, ELECTRONICS) + scheduleMeeting("thursday", SECOND_HOUR, THIRD_HOUR, ENGLISH) + scheduleMeeting("thursday", FOURTH_HOUR, FIFTH_HOUR, TPSEE) + scheduleMeeting("thursday", FIFTH_HOUR, SIXTH_HOUR, TPSEE_SYS) + scheduleMeeting("thursday", SIXTH_HOUR, SEVENTH_HOUR, SYSTEMS) + + # Friday schedule + scheduleMeeting("friday", FIRST_HOUR, SECOND_HOUR, MATHS) + scheduleMeeting("friday", SECOND_HOUR, THIRD_HOUR, ENGLISH) + scheduleMeeting("friday", FOURTH_HOUR, FIFTH_HOUR, LITERATURE) + scheduleMeeting("friday", FIFTH_HOUR, SIXTH_HOUR, LITERATURE) + scheduleMeeting("friday", SIXTH_HOUR, SEVENTH_HOUR, TPSEE) + +def scheduleMeeting(day, startHour, endHour, link): + if str(day).lower() == "monday": + schedule.every().monday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().monday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "tuesday": + schedule.every().tuesday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().tuesday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "wednesday": + schedule.every().wednesday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().wednesday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "thursday": + schedule.every().thursday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().thursday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "friday": + schedule.every().friday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().friday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "saturday": + schedule.every().saturday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().saturday.at(endHour).do(browser_manager.hangUpMeeting) + elif str(day).lower() == "sunday": + schedule.every().sunday.at(startHour).do(browser_manager.joinMeeting, link) + schedule.every().sunday.at(endHour).do(browser_manager.hangUpMeeting) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c55de09 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +selenium +schedule