Skip to content

Commit

Permalink
Added package source, setup file and requirements.txt
Browse files Browse the repository at this point in the history
  • Loading branch information
rishi255 committed Mar 11, 2021
1 parent 36342b8 commit 5f01603
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 0 deletions.
30 changes: 30 additions & 0 deletions .gitignore
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
1 change: 1 addition & 0 deletions README.md
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.
9 changes: 9 additions & 0 deletions autojoiner/__init__.py
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()
127 changes: 127 additions & 0 deletions autojoiner/__main__.py
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()
121 changes: 121 additions & 0 deletions autojoiner/autojoin.py
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!")
6 changes: 6 additions & 0 deletions autojoiner/config_user.py
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 added requirements.txt
Binary file not shown.
32 changes: 32 additions & 0 deletions setup.py
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
)

0 comments on commit 5f01603

Please sign in to comment.