-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added package source, setup file and requirements.txt
- Loading branch information
Showing
8 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
.vscode | ||
notes.md | ||
venv/ | ||
|
||
# package files | ||
autojoiner/** | ||
!autojoiner/__init__.py | ||
!autojoiner/__main__.py | ||
!autojoiner/autojoin.py | ||
!autojoiner/config_user.py | ||
|
||
# Distribution / packaging | ||
.Python | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
wheels/ | ||
share/python-wheels/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
MANIFEST |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
# autojoin-v2 | ||
|
||
A tool in Python to automate the joining of Zoom or Cisco Webex meetings at scheduled times. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import subprocess | ||
def run_command_windows(cmd): | ||
output = subprocess.Popen(["powershell", "-Command", cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||
std_out, std_err = output.communicate() | ||
return std_out.decode('utf-8').split('\n'), std_err.decode('utf-8').split('\n') | ||
|
||
def run_command_linux(cmd): | ||
# TODO define run_command_linux | ||
raise NotImplementedError() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# standard imports | ||
from datetime import datetime | ||
import platform | ||
import re | ||
import subprocess | ||
|
||
# third party imports | ||
import click | ||
from click.exceptions import ClickException | ||
import importlib | ||
|
||
# local imports | ||
# autojoin = importlib.import_module(".autojoin", "src") | ||
from . import autojoin | ||
from .__init__ import * | ||
|
||
#! Link validation regex patterns | ||
webex_link_pattern = "(^.*[.]webex[.].*$)" | ||
zoom_link_pattern = "" | ||
|
||
############### CLICK FUNCTIONS ############### | ||
@click.group() | ||
def main(): | ||
"""Autojoiner-v2 CLI""" | ||
pass | ||
|
||
@main.command() | ||
@click.argument("meeting_link") | ||
@click.argument("date", type=click.DateTime(["%Y-%m-%d"])) | ||
@click.argument("time", type=click.DateTime(["%H:%M"])) | ||
@click.argument("name") # name of task | ||
@click.option("-r", "--repeat-weekly", is_flag=True, default=False, help="If used, means that this link needs to be joined every week at the specified day and time.") | ||
def add(meeting_link, date, time, repeat_weekly, name): | ||
date_time = datetime.combine(date.date(), time.time()) | ||
print(f"Meeting link: {meeting_link}, Dateime = {date_time}, repeat weekly = {repeat_weekly}") | ||
|
||
if validate_link(meeting_link) is None: | ||
raise Exception(f"{meeting_link} not a valid link!") | ||
|
||
curr_os = platform.system() | ||
if curr_os == 'Windows': | ||
#! use "schtasks" command to add task to Windows Scheduler | ||
|
||
#! Just check if name already exists | ||
std_out, std_err = run_command_windows(f"schtasks /query /fo CSV /tn \"autojoin_tasks\\{name}\"") | ||
|
||
print(f"stdout: {std_out}") | ||
print(f"stderr: {std_err}") | ||
|
||
if "ERROR: The system cannot find the file specified." not in std_err[0]: | ||
raise click.ClickException("Task with that name already exists!") | ||
else: | ||
print("The name doesn't already exist! Need to create the task now.") | ||
if repeat_weekly: | ||
cmd = f"schtasks /create /sc WEEKLY /d {get_weekday_code(date_time.isoweekday())} /st {date_time.strftime('%H:%M')} /tn \"autojoin_tasks\\{name}\" /tr \"powershell -Command autojoiner join {meeting_link}; sleep 10\" /f" | ||
else: | ||
cmd = f"" | ||
|
||
print(f"Running command: '{cmd}'") | ||
std_out, std_err = run_command_windows(cmd) | ||
print("After running:") | ||
print(f"stdout: {std_out}") | ||
print(f"stderr: {std_err}") | ||
|
||
#! Nope, below commented out part useless for now. Name is mandatory to pass. | ||
# first find next available name IF name is not passed | ||
# if name == "": | ||
# output = subprocess.run(["powershell", "-Command", "schtasks /query /fo CSV /tn \"autojoin_tasks\\\" | findstr \"autojoin_task\""], stdout=subprocess.PIPE).stdout.decode('utf-8') | ||
# output = output.split('\n')[:-1] # discard empty element at the end (since it ends with '\n') | ||
# if output[0][:7] == "ERROR: ": # not found | ||
# output = "default" | ||
# else: | ||
# # get last line of output, then extract name | ||
# output = output[-1].split()[0] | ||
# # now output will only contain taskname. | ||
|
||
# print(output) | ||
|
||
elif curr_os == 'Linux': | ||
raise NotImplementedError("Sorry, this tool isn't supported on your OS yet!") | ||
else: | ||
raise NotImplementedError("Sorry, this tool isn't supported on your OS yet!") | ||
|
||
@main.command() | ||
@click.argument("meeting_link") | ||
def join(meeting_link): | ||
print(f"JOIN CALLED on link {meeting_link}! Joining now using Selenium.") | ||
|
||
meeting_platform = validate_link(meeting_link) | ||
if meeting_platform is None: | ||
raise Exception(f"{meeting_link} not a valid link!") | ||
|
||
# now call the Selenium code in autojoin.py | ||
autojoin.main(meeting_link, meeting_platform) | ||
|
||
@main.command() | ||
def info(): | ||
print(platform.system()) | ||
|
||
############### OTHER FUNCTIONS ############### | ||
|
||
def validate_link(text): | ||
if bool(re.match(webex_link_pattern, text)): | ||
return "webex" | ||
elif bool(re.match(zoom_link_pattern, text)): | ||
return "zoom" | ||
else: | ||
return None | ||
|
||
def get_weekday_code(dayno): | ||
if dayno == 0: | ||
return "MON" | ||
elif dayno == 1: | ||
return "TUE" | ||
elif dayno == 2: | ||
return "WED" | ||
elif dayno == 3: | ||
return "THU" | ||
elif dayno == 4: | ||
return "FRI" | ||
elif dayno == 5: | ||
return "SAT" | ||
elif dayno == 6: | ||
return "SUN" | ||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
# standard imports | ||
import platform | ||
import re | ||
import sys | ||
import time | ||
import subprocess | ||
|
||
# third party imports | ||
import importlib | ||
from selenium import webdriver | ||
from selenium.webdriver.common.keys import Keys | ||
from selenium.webdriver.support import expected_conditions as EC | ||
from selenium.webdriver.common.by import By | ||
from selenium.webdriver.support.ui import WebDriverWait | ||
|
||
# local imports | ||
from .config_user import * | ||
from .__init__ import * | ||
|
||
# class URL is passed to this program somehow. | ||
def join_meeting(url: str, browser): | ||
browser.get(url) | ||
|
||
WebDriverWait(browser, 30).until(EC.presence_of_element_located((By.ID, "push_download_join_by_browser"))) | ||
webdriver.ActionChains(browser).send_keys(Keys.ESCAPE).perform() | ||
print("Pressed esc") | ||
browser.find_element_by_id("push_download_join_by_browser").click() | ||
WebDriverWait(browser, 30).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,'//*[@id="pbui_iframe"]'))) | ||
|
||
# Enter name and email, click next | ||
WebDriverWait(browser, 30).until(EC.presence_of_element_located((By.CLASS_NAME, "style-input-2nuAk"))) | ||
name, email = browser.find_elements_by_class_name("style-input-2nuAk") | ||
name.send_keys(name_to_join_with) # ! USES an option from config | ||
email.send_keys(email_to_join_with) # ! USES an option from config | ||
browser.find_element_by_id("guest_next-btn").click() | ||
|
||
# now click ok and join meeting | ||
try: | ||
WebDriverWait(browser, 30).until(EC.presence_of_element_located((By.XPATH, '//button[contains(text(), "Got it")]'))) | ||
browser.find_element_by_xpath('//button[contains(text(), "Got it")]').click() | ||
except: | ||
print("Can't find the Got it!") | ||
|
||
# press ALT+A to allow camera and mic permissions. | ||
print("Wait for 5 sec...") | ||
time.sleep(5) | ||
webdriver.ActionChains(browser).key_down(Keys.ALT).send_keys('a').key_up(Keys.ALT).perform() | ||
print("Pressed ALT+A !!") | ||
|
||
if turn_off_video_before_join: # ! USES an option from config | ||
try: | ||
WebDriverWait(browser, 30).until(EC.presence_of_element_located((By.XPATH, "//button[@data-doi='VIDEO:STOP_VIDEO:MEETSIMPLE_INTERSTITIAL']"))) | ||
browser.find_element_by_xpath("//button[@data-doi='VIDEO:STOP_VIDEO:MEETSIMPLE_INTERSTITIAL']").click() | ||
print("Found the turn off video button and clicked it!") | ||
except: | ||
print("I think video is off already!") | ||
|
||
if mute_before_join: # ! USES an option from config | ||
try: | ||
browser.find_element_by_xpath("//button[@data-doi='AUDIO:MUTE_SELF:MEETSIMPLE_INTERSTITIAL']").click() | ||
print("Found the mute button and clicked it!") | ||
except: | ||
print("I think it's muted already!") | ||
|
||
try: | ||
browser.find_element_by_xpath('//button[contains(text(), "Start meeting")]').click() | ||
print("Started the meeting!") | ||
except: | ||
print("Can't find the Start meeting button! It's probably Join meeting then...") | ||
|
||
try: | ||
browser.find_element_by_xpath('//button[contains(text(), "Join meeting")]').click() | ||
print("Joined the meeting!") | ||
except: | ||
print("Can't find the Join meeting button. It was probably start meeting then...") | ||
|
||
# TODO: remove "firefox" default argument from here because it's already the default in config.py, so lol | ||
# TODO: implement browser "chrome" | ||
def start_browser(name="firefox"): | ||
browser = None | ||
|
||
if name=="firefox": | ||
profile = webdriver.FirefoxProfile() | ||
options = webdriver.FirefoxOptions() | ||
|
||
profile.set_preference("browser.download.folderList", 2) | ||
profile.set_preference("browser.download.manager.showWhenStarting", False) | ||
profile.set_preference("browser.download.dir", useless_download_directory) | ||
profile.set_preference("browser.download.lastDir", useless_download_directory) | ||
profile.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/x-msdownload, application/octet-stream") | ||
|
||
options.set_preference("permissions.default.camera", 1) | ||
options.set_preference("permissions.default.microphone", 1) | ||
|
||
browser = webdriver.Firefox(firefox_profile=profile, options=options) | ||
|
||
if name=="chrome": | ||
raise NotImplementedError() | ||
|
||
return browser | ||
|
||
def main(url, meeting_platform): | ||
|
||
if meeting_platform == 'webex': | ||
browser = start_browser(browser_name) # ! USES an option from config | ||
if browser is None: | ||
print("Invalid 'browser_name' setting in config.py!") | ||
|
||
curr_os = platform.system() | ||
if curr_os == 'Windows': | ||
std_out, std_err = run_command_windows(f"gci {useless_download_directory} -File | where Name -match 'webex(\(\d\))?.exe' | rm") | ||
# p = subprocess.run(f"powershell -Command \"& {{gci {useless_download_directory} -File | where Name -match 'webex(\(\d\))?.exe' | rm}}\"", check=True) | ||
elif curr_os == 'Linux': | ||
p = subprocess.run(f"find {useless_download_directory} -name 'webex(\(\d\))?.exe' -delete", shell=True, check=True) | ||
# else, just ignore as this part isn't very important anyway | ||
print("Cleared useless downloads") | ||
|
||
join_meeting(url, browser) | ||
|
||
elif meeting_platform == 'zoom': | ||
raise NotImplementedError("Autojoin-v2 doesn't work for Zoom yet!") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
name_to_join_with = "" | ||
email_to_join_with = "" | ||
mute_before_join = True | ||
turn_off_video_before_join = True | ||
browser_name = "firefox" # possible values = ["firefox", "chrome"] | ||
useless_download_directory = "" # if this is empty, browsers automatically download to the downloads folder. |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from setuptools import setup | ||
# from io import open | ||
# from os import path | ||
|
||
import pathlib | ||
|
||
# The directory containing this file | ||
HERE = pathlib.Path(__file__).parent | ||
|
||
# The text of the README file | ||
README = (HERE / "README.md").read_text() | ||
|
||
setup( | ||
name='autojoiner', | ||
version='2.0', | ||
packages=['autojoiner'], | ||
install_requires=[ | ||
'click', 'pyautogui', 'selenium', 'cryptography' | ||
], | ||
python_requires='>=2.7', | ||
entry_points=''' | ||
[console_scripts] | ||
autojoiner=src.__main__:main | ||
''', | ||
author="Rishikesh Rachchh", | ||
long_description=README, | ||
long_description_content_type="text/markdown", | ||
license='MIT', | ||
url="https://github.com/rishi255/autojoin-v2", | ||
author_email="[email protected]", | ||
# dependency_links=dependency_links | ||
) |