-
-
Notifications
You must be signed in to change notification settings - Fork 259
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added the code snippet and required files.
Uploaded the microsoft bot file.
- Loading branch information
Showing
5 changed files
with
362 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,35 @@ | ||
# Microsoft Teams Online Class Attender | ||
|
||
This bot attends the online classes (or meetings) held on Microsoft teams, according to the given timetable. | ||
|
||
|
||
## Configure | ||
|
||
There are few things you need to configure before running this bot. | ||
|
||
- Open Microsoft teams on your browser, login to your account, change the dashboard view to list view (from grid view), so that your classes are displayed in a list view. | ||
-  | ||
- Open *bot.py*, and put your microsoft teams credentials in the **CREDS** dictionary. | ||
- Example - `CREDS = {'email' : 'myemail@email.com', 'passwd':'''mypassword'''}` | ||
- Open *discord_webhook.py* and put your discord webhook URL in the **webhook_url** variable. | ||
- Example - `webhook_url = "https://discordapp.com/...."` | ||
- Make sure that the timezone of the PC is correct. If you're running the bot on cloud, you may want to manually change the timezone of the virtual machine to an appropriate time zone (i.e., the timezone that your online classes follow) | ||
|
||
## Install | ||
|
||
- Clone the repository | ||
- Install requirements.txt `pip install -r requirements.txt` | ||
|
||
|
||
## Run the bot | ||
|
||
- Run the bot `python microsoft-teams_bot.py` | ||
|
||
|
||
## Author | ||
|
||
- Rishav Kumar | ||
|
||
|
||
|
||
Written on Python3. |
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,50 @@ | ||
from discord_webhooks import DiscordWebhooks | ||
|
||
#Put your discord webhook url here. | ||
|
||
webhook_url = '' | ||
|
||
|
||
def send_msg(class_name,status,start_time,end_time): | ||
|
||
WEBHOOK_URL = webhook_url | ||
|
||
webhook = DiscordWebhooks(WEBHOOK_URL) | ||
# Attaches a footer | ||
webhook.set_footer(text='-- Teja Swaroop') | ||
|
||
if(status=="joined"): | ||
|
||
webhook.set_content(title='Class Joined Succesfully', | ||
description="Here's your report with :heart:") | ||
|
||
# Appends a field | ||
webhook.add_field(name='Class', value=class_name) | ||
webhook.add_field(name='Status', value=status) | ||
webhook.add_field(name='Joined at', value=start_time) | ||
webhook.add_field(name='Leaving at', value=end_time) | ||
|
||
elif(status=="left"): | ||
webhook.set_content(title='Class left Succesfully', | ||
description="Here's your report with :heart:") | ||
|
||
# Appends a field | ||
webhook.add_field(name='Class', value=class_name) | ||
webhook.add_field(name='Status', value=status) | ||
webhook.add_field(name='Joined at', value=start_time) | ||
webhook.add_field(name='Left at', value=end_time) | ||
|
||
|
||
elif(status=="noclass"): | ||
webhook.set_content(title='Seems like no class today', | ||
description="No join button found! Assuming no class.") | ||
|
||
# Appends a field | ||
webhook.add_field(name='Class', value=class_name) | ||
webhook.add_field(name='Status', value=status) | ||
webhook.add_field(name='Expected Join time', value=status) | ||
webhook.add_field(name='Expected Leave time', value=end_time) | ||
|
||
webhook.send() | ||
|
||
print("Sent message to discord") |
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,269 @@ | ||
from selenium import webdriver | ||
from selenium.webdriver.support.wait import WebDriverWait | ||
from selenium.webdriver.support import expected_conditions as EC | ||
from selenium.webdriver.common.by import By | ||
from selenium.webdriver.chrome.options import Options | ||
import time | ||
import re | ||
import os.path | ||
from os import path | ||
import sqlite3 | ||
import schedule | ||
from datetime import datetime | ||
from selenium.webdriver.common.action_chains import ActionChains | ||
import discord_webhook | ||
|
||
opt = Options() | ||
opt.add_argument("--disable-infobars") | ||
opt.add_argument("start-maximized") | ||
opt.add_argument("--disable-extensions") | ||
opt.add_argument("--start-maximized") | ||
# Pass the argument 1 to allow and 2 to block | ||
opt.add_experimental_option("prefs", { \ | ||
"profile.default_content_setting_values.media_stream_mic": 1, | ||
"profile.default_content_setting_values.media_stream_camera": 1, | ||
"profile.default_content_setting_values.geolocation": 1, | ||
"profile.default_content_setting_values.notifications": 1 | ||
}) | ||
|
||
# driver = webdriver.Chrome(chrome_options=opt,service_log_path='NUL') | ||
driver = None | ||
URL = "https://teams.microsoft.com" | ||
|
||
#put your teams credentials here | ||
CREDS = {'email' : '','passwd':''} | ||
|
||
|
||
|
||
def login(): | ||
global driver | ||
#login required | ||
print("logging in") | ||
emailField = driver.find_element_by_xpath('//*[@id="i0116"]') | ||
emailField.click() | ||
emailField.send_keys(CREDS['email']) | ||
driver.find_element_by_xpath('//*[@id="idSIButton9"]').click() #Next button | ||
time.sleep(5) | ||
passwordField = driver.find_element_by_xpath('//*[@id="i0118"]') | ||
passwordField.click() | ||
passwordField.send_keys(CREDS['passwd']) | ||
driver.find_element_by_xpath('//*[@id="idSIButton9"]').click() #Sign in button | ||
time.sleep(5) | ||
driver.find_element_by_xpath('//*[@id="idSIButton9"]').click() #remember login | ||
time.sleep(5) | ||
# return driver | ||
|
||
|
||
def createDB(): | ||
conn = sqlite3.connect('timetable.db') | ||
c=conn.cursor() | ||
# Create table | ||
c.execute('''CREATE TABLE timetable(class text, start_time text, end_time text, day text)''') | ||
conn.commit() | ||
conn.close() | ||
print("Created timetable Database") | ||
|
||
|
||
|
||
def validate_input(regex,inp): | ||
if not re.match(regex,inp): | ||
return False | ||
return True | ||
|
||
def validate_day(inp): | ||
days = ["monday","tuesday","wednesday","thursday","friday","saturday","sunday"] | ||
|
||
if inp.lower() in days: | ||
return True | ||
else: | ||
return False | ||
|
||
|
||
def add_timetable(): | ||
if(not(path.exists("timetable.db"))): | ||
createDB() | ||
op = int(input("1. Add class\n2. Done adding\nEnter option : ")) | ||
while(op==1): | ||
name = input("Enter class name : ") | ||
start_time = input("Enter class start time in 24 hour format: (HH:MM) ") | ||
while not(validate_input("\d\d:\d\d",start_time)): | ||
print("Invalid input, try again") | ||
start_time = input("Enter class start time in 24 hour format: (HH:MM) ") | ||
|
||
end_time = input("Enter class end time in 24 hour format: (HH:MM) ") | ||
while not(validate_input("\d\d:\d\d",end_time)): | ||
print("Invalid input, try again") | ||
end_time = input("Enter class end time in 24 hour format: (HH:MM) ") | ||
|
||
day = input("Enter day (Monday/Tuesday/Wednesday..etc) : ") | ||
while not(validate_day(day.strip())): | ||
print("Invalid input, try again") | ||
end_time = input("Enter day (Monday/Tuesday/Wednesday..etc) : ") | ||
|
||
|
||
conn = sqlite3.connect('timetable.db') | ||
c=conn.cursor() | ||
|
||
# Insert a row of data | ||
c.execute("INSERT INTO timetable VALUES ('%s','%s','%s','%s')"%(name,start_time,end_time,day)) | ||
|
||
conn.commit() | ||
conn.close() | ||
|
||
print("Class added to database\n") | ||
|
||
op = int(input("1. Add class\n2. Done adding\nEnter option : ")) | ||
|
||
|
||
def view_timetable(): | ||
conn = sqlite3.connect('timetable.db') | ||
c=conn.cursor() | ||
for row in c.execute('SELECT * FROM timetable'): | ||
print(row) | ||
conn.close() | ||
|
||
|
||
|
||
def joinclass(class_name,start_time,end_time): | ||
global driver | ||
|
||
try_time = int(start_time.split(":")[1]) + 15 | ||
try_time = start_time.split(":")[0] + ":" + str(try_time) | ||
|
||
time.sleep(5) | ||
|
||
|
||
classes_available = driver.find_elements_by_class_name("name-channel-type") | ||
|
||
for i in classes_available: | ||
if class_name.lower() in i.get_attribute('innerHTML').lower(): | ||
print("JOINING CLASS ",class_name) | ||
i.click() | ||
break | ||
|
||
|
||
time.sleep(4) | ||
|
||
|
||
try: | ||
joinbtn = driver.find_element_by_class_name("ts-calling-join-button") | ||
joinbtn.click() | ||
|
||
except: | ||
#join button not found | ||
#refresh every minute until found | ||
k = 1 | ||
while(k<=15): | ||
print("Join button not found, trying again") | ||
time.sleep(60) | ||
driver.refresh() | ||
joinclass(class_name,start_time,end_time) | ||
# schedule.every(1).minutes.do(joinclass,class_name,start_time,end_time) | ||
k+=1 | ||
print("Seems like there is no class today.") | ||
discord_webhook.send_msg(class_name=class_name,status="noclass",start_time=start_time,end_time=end_time) | ||
|
||
|
||
time.sleep(4) | ||
webcam = driver.find_element_by_xpath('//*[@id="page-content-wrapper"]/div[1]/div/calling-pre-join-screen/div/div/div[2]/div[1]/div[2]/div/div/section/div[2]/toggle-button[1]/div/button/span[1]') | ||
if(webcam.get_attribute('title')=='Turn camera off'): | ||
webcam.click() | ||
time.sleep(1) | ||
|
||
microphone = driver.find_element_by_xpath('//*[@id="preJoinAudioButton"]/div/button/span[1]') | ||
if(microphone.get_attribute('title')=='Mute microphone'): | ||
microphone.click() | ||
|
||
time.sleep(1) | ||
joinnowbtn = driver.find_element_by_xpath('//*[@id="page-content-wrapper"]/div[1]/div/calling-pre-join-screen/div/div/div[2]/div[1]/div[2]/div/div/section/div[1]/div/div/button') | ||
joinnowbtn.click() | ||
|
||
discord_webhook.send_msg(class_name=class_name,status="joined",start_time=start_time,end_time=end_time) | ||
|
||
#now schedule leaving class | ||
tmp = "%H:%M" | ||
|
||
class_running_time = datetime.strptime(end_time,tmp) - datetime.strptime(start_time,tmp) | ||
|
||
time.sleep(class_running_time.seconds) | ||
|
||
driver.find_element_by_class_name("ts-calling-screen").click() | ||
|
||
|
||
driver.find_element_by_xpath('//*[@id="teams-app-bar"]/ul/li[3]').click() #come back to homepage | ||
time.sleep(1) | ||
|
||
driver.find_element_by_xpath('//*[@id="hangup-button"]').click() | ||
print("Class left") | ||
discord_webhook.send_msg(class_name=class_name,status="left",start_time=start_time,end_time=end_time) | ||
|
||
|
||
|
||
|
||
|
||
def start_browser(): | ||
|
||
global driver | ||
driver = webdriver.Chrome(chrome_options=opt,service_log_path='NUL') | ||
|
||
driver.get(URL) | ||
|
||
WebDriverWait(driver,10000).until(EC.visibility_of_element_located((By.TAG_NAME,'body'))) | ||
|
||
if("login.microsoftonline.com" in driver.current_url): | ||
login() | ||
|
||
|
||
|
||
def sched(): | ||
conn = sqlite3.connect('timetable.db') | ||
c=conn.cursor() | ||
for row in c.execute('SELECT * FROM timetable'): | ||
#schedule all classes | ||
name = row[0] | ||
start_time = row[1] | ||
end_time = row[2] | ||
day = row[3] | ||
|
||
if day.lower()=="monday": | ||
schedule.every().monday.at(start_time).do(joinclass,name,start_time,end_time) | ||
print("Scheduled class '%s' on %s at %s"%(name,day,start_time)) | ||
if day.lower()=="tuesday": | ||
schedule.every().tuesday.at(start_time).do(joinclass,name,start_time,end_time) | ||
print("Scheduled class '%s' on %s at %s"%(name,day,start_time)) | ||
if day.lower()=="wednesday": | ||
schedule.every().wednesday.at(start_time).do(joinclass,name,start_time,end_time) | ||
print("Scheduled class '%s' on %s at %s"%(name,day,start_time)) | ||
if day.lower()=="thursday": | ||
schedule.every().thursday.at(start_time).do(joinclass,name,start_time,end_time) | ||
print("Scheduled class '%s' on %s at %s"%(name,day,start_time)) | ||
if day.lower()=="friday": | ||
schedule.every().friday.at(start_time).do(joinclass,name,start_time,end_time) | ||
print("Scheduled class '%s' on %s at %s"%(name,day,start_time)) | ||
if day.lower()=="saturday": | ||
schedule.every().saturday.at(start_time).do(joinclass,name,start_time,end_time) | ||
print("Scheduled class '%s' on %s at %s"%(name,day,start_time)) | ||
if day.lower()=="sunday": | ||
schedule.every().sunday.at(start_time).do(joinclass,name,start_time,end_time) | ||
print("Scheduled class '%s' on %s at %s"%(name,day,start_time)) | ||
|
||
|
||
#Start browser | ||
start_browser() | ||
while True: | ||
# Checks whether a scheduled task | ||
# is pending to run or not | ||
schedule.run_pending() | ||
time.sleep(1) | ||
|
||
|
||
if __name__=="__main__": | ||
# joinclass("Maths","15:13","15:15","sunday") | ||
op = int(input(("1. Modify Timetable\n2. View Timetable\n3. Start Bot\nEnter option : "))) | ||
|
||
if(op==1): | ||
add_timetable() | ||
if(op==2): | ||
view_timetable() | ||
if(op==3): | ||
sched() |
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,8 @@ | ||
certifi==2020.6.20 | ||
chardet==3.0.4 | ||
Discord-Webhooks==1.0.4 | ||
idna==2.8 | ||
requests==2.21.0 | ||
schedule==0.6.0 | ||
selenium==3.141.0 | ||
urllib3==1.24.3 |