Skip to content

Instantly share code, notes, and snippets.

@arnav-t
Last active January 30, 2021 09:13
Show Gist options
  • Save arnav-t/5fef88f61e0dab92cc0470c8b6262db7 to your computer and use it in GitHub Desktop.
Save arnav-t/5fef88f61e0dab92cc0470c8b6262db7 to your computer and use it in GitHub Desktop.
AutoTeams.js - Tampermonkey/Greasemonkey script for autojoining meetings
// ==UserScript==
// @name AutoTeams
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Auto Join MS Teams meetings
// @author arnav-t
// @match https://teams.microsoft.com/*
// @grant none
// ==/UserScript==
class AutoTeams {
constructor() {
this.teams = {
cfd: {
url: 'https://teams.microsoft.com/_#/school/conversations/General?threadId=19:e4cc42e390f54e1e80af6708a137178c@thread.tacv2&ctx=channel',
slots: [
{day: 2, startHour: 8, startMinute: 0, endHour: 10, endMinute: 0},
{day: 6, startHour: 14, startMinute: 30, endHour: 16, endMinute: 30}
]
},
microfluidics: {
url: 'https://teams.microsoft.com/_#/school/conversations/General?threadId=19:58d9533c73f6476094cf9cc008b539e1@thread.tacv2&ctx=channel',
slots: [
{day: 1, startHour: 8, startMinute: 0, endHour: 10, endMinute: 0},
{day: 2, startHour: 12, startMinute: 0, endHour: 13, endMinute: 0},
{day: 4, startHour: 17, startMinute: 0, endHour: 18, endMinute: 0}
]
},
ecpackaging: {
url: 'https://teams.microsoft.com/_#/school/conversations/General?threadId=19:4287713e60414ede8d4db1542483eb10@thread.tacv2&ctx=channel',
slots: [
{day: 1, startHour: 12, startMinute: 0, endHour: 13, endMinute: 0},
{day: 2, startHour: 10, startMinute: 0, endHour: 12, endMinute: 0}
]
},
laser: {
url: 'https://teams.microsoft.com/_#/school/conversations/General?threadId=19:284daeaaba1240429aade22c986a0ad1@thread.tacv2&ctx=channel',
slots: [
{day: 3, startHour: 11, startMinute: 0, endHour: 12, endMinute: 0},
{day: 4, startHour: 12, startMinute: 0, endHour: 13, endMinute: 0},
{day: 5, startHour: 8, startMinute: 0, endHour: 9, endMinute: 0}
]
},
npm: {
url: 'https://teams.microsoft.com/_#/school/conversations/General?threadId=19:642438518d3d44698b5633239b6d3d4c@thread.tacv2&ctx=channel',
slots: [
{day: 3, startHour: 12, startMinute: 0, endHour: 13, endMinute: 0},
{day: 4, startHour: 11, startMinute: 0, endHour: 12, endMinute: 0},
{day: 5, startHour: 9, startMinute: 0, endHour: 11, endMinute: 0}
]
},
human: {
url: 'https://teams.microsoft.com/_#/school/conversations/General?threadId=19:b000645cca6b4f668239f09d46a74c40@thread.tacv2&ctx=channel',
slots: [
{day: 3, startHour: 10, startMinute: 0, endHour: 11, endMinute: 0},
{day: 4, startHour: 9, startMinute: 0, endHour: 10, endMinute: 0},
{day: 5, startHour: 11, startMinute: 0, endHour: 13, endMinute: 0}
]
}
};
this.state = 'idle';
this.initDelay = 1*60*1000;
this.slotRecheckTime = 1*60*1000;
this.loadTime = 15*1000;
this.retryTime = 30*1000;
this.jbMaxRetries = 3;
this.refreshUrl = 'https://teams.microsoft.com/_#/my/file-recent';
this.join = this.join.bind(this);
this.init = this.init.bind(this);
this.init();
}
asyncTimeout(delay) {
return new Promise(res => setTimeout(() => res(), delay));
}
async init() {
console.log('[AT] Initializing AutoTeams.');
this.asyncTimeout(this.initDelay);
// Check slots every slotRecheckTime
setInterval(() => {
if (this.state === 'meeting') {
// Verify that currently in meeting
if (!document.querySelector('#hangup-button')) this.state = 'idle';
else return;
} else if (this.state === 'joining') return;
console.log('[AT] Checking slots.');
const currentDay = (new Date()).getDay();
for (const team in this.teams) {
for (const slot of this.teams[team].slots) {
if (slot.day !== currentDay) continue;
const startTime = new Date(); startTime.setHours(slot.startHour, slot.startMinute, 0, 0);
const endTime = new Date(); endTime.setHours(slot.endHour, slot.endMinute, 0, 0);
const currentTime = new Date();
// If current time is in between start and end time, join meeting
if (currentTime >= startTime && currentTime < endTime) {
console.log(`[AT] Joining team "${team}".`);
const duration = endTime - currentTime;
this.join(team, duration);
return;
}
}
}
}, this.slotRecheckTime);
}
async join(team, duration) {
if (this.state === 'meeting') return;
this.state = 'joining';
// Initalize startTime
const startTime = new Date();
// Load team page
window.location.href = this.teams[team].url;
await this.asyncTimeout(this.loadTime);
console.log('[AT] Team page loaded.');
let joinButton = null;
for (let attempt = 0; attempt < this.jbMaxRetries; ++attempt) {
// Try to fetch join button
console.log(`[AT] Attempting to find meeting (${attempt+1}).`);
joinButton = document.querySelector('.ts-calling-join-button');
if (joinButton) break;
else await this.asyncTimeout(this.retryTime);
}
if (!joinButton) {
console.log('[AT] Meeting not found. Resetting to idle.');
window.location.href = this.refreshUrl;
this.state = 'idle';
return;
}
console.log('[AT] Fetched join button.');
// Attempt to join meeting
joinButton.click();
await this.asyncTimeout(this.loadTime);
console.log('[AT] Turning off camera and microphone.');
// Turn camera and microphone off
document.querySelector('span[title="Mute microphone"]')?.click();
document.querySelector('span[title="Turn camera off"]')?.click();
// Finally join the meeting
joinButton = document.querySelector('.join-btn.ts-btn');
if (!joinButton) {
console.log('[AT] Unable to join. Resetting to idle.');
window.location.href = this.refreshUrl;
this.state = 'idle';
return;
}
joinButton.click();
const remaining = Math.max(duration - (new Date() - startTime), this.loadTime);
console.log(`[AT] Hanging up in ${(remaining/1000).toFixed()} seconds.`);
this.state = 'meeting';
await this.asyncTimeout(remaining);
console.log('[AT] Hanging up.');
// Hang up
document.querySelector('#hangup-button').click();
this.state = 'idle';
}
}
window.onload = () => {
window.at = new AutoTeams();
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment