Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bots/google_meet_bot_adapter/google_meet_ui_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ def fill_out_name_input(self):
except TimeoutException as e:
self.look_for_blocked_element("name_input")
self.look_for_login_required_element("name_input")
self.check_if_meeting_is_found()

if self.google_meet_bot_login_session and self.join_now_button_is_present():
logger.info("This is a signed in bot and name input is not present but the join now button is present. Assuming name input is not present because we don't need to fill it out, so returning.")
Expand Down
92 changes: 92 additions & 0 deletions bots/teams_bot_adapter/teams_chromedriver_payload.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,95 @@
(function () {
const CONV_HOST_REGEX = /(^|\.)teams\.microsoft\.(com|us)$/;
const CONV_PATH_REGEX = /\/conv\/[^/]+\/?$/;

let installed = false;
let done = false;

function isTargetUrl(input) {
try {
const raw = typeof input === 'string' ? input : (input instanceof Request ? input.url : String(input));
const url = new URL(raw, window.location.href);
return CONV_HOST_REGEX.test(url.hostname) && CONV_PATH_REGEX.test(url.pathname);
} catch (_) {
return false;
}
}

// Telemetry must never break the intercepted fetch.
function safeSendJson(message) {
try { window.ws?.sendJson(message); } catch (_) {}
}

function getMethod(input, init) {
if (init?.method) return String(init.method).toUpperCase();
if (input instanceof Request) return input.method.toUpperCase();
return 'GET';
}

// Returns the rewritten body string, or null if the body isn't a conversation join request.
function rewriteBody(bodyText) {
let parsed;
try { parsed = JSON.parse(bodyText); } catch (_) { return null; }
if (!parsed?.conversationRequest) return null;
const v = parsed.participantPropertyBag?.participantScreening?.value;
if (!v) return null;

const shouldOverrideIsBot = v.isBot === true && v.source === 'JoinShield';
if (shouldOverrideIsBot) {
v.isBot = false;
}
safeSendJson({
type: 'JoinShieldBypassActivated',
value: v,
wasIsBotOverridden: shouldOverrideIsBot,
});
return shouldOverrideIsBot ? JSON.stringify(parsed) : bodyText;
}

window.installJoinShieldBypass = function () {
if (done || installed) return true;
installed = true;

const previousFetch = window.fetch;

function uninstall() {
done = true;
// If something wrapped fetch after us, we can't unwind it; the wrapper becomes a passthrough.
if (window.fetch === joinShieldBypassFetch) {
window.fetch = previousFetch;
}
}

function joinShieldBypassFetch(input, init) {
if (done || getMethod(input, init) !== 'POST' || !isTargetUrl(input)) {
return previousFetch.apply(this, arguments);
}

// Only synchronous string bodies are rewritten; anything else (e.g. a Request object)
// passes through untouched so the original fetch timing and semantics are preserved.
if (typeof init?.body !== 'string') {
return previousFetch.apply(this, arguments);
}

let newBody = null;
try { newBody = rewriteBody(init.body); } catch (err) { reportError(err); }
if (newBody === null) return previousFetch.apply(this, arguments);
uninstall();
return previousFetch.call(this, input, { ...init, body: newBody });
}

function reportError(err) {
safeSendJson({
type: 'JoinShieldBypassError',
error: err?.message || String(err),
});
}

window.fetch = joinShieldBypassFetch;
return true;
};
})();

(() => {
let PAYLOAD = null;

Expand Down
11 changes: 11 additions & 0 deletions bots/teams_bot_adapter/teams_ui_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ def set_display_name_to_allow(self, display_name):
# Tell the injected payload to allow this exact display name through Teams' validation regex.
self.driver.execute_script("return window.setDisplayNameToAllowForTeamsNameValidationBypass?.(arguments[0]);", display_name)

def install_join_shield_bypass(self):
if self.teams_bot_login_is_available and self.teams_bot_login_should_be_used:
logger.info("Not installing JoinShield bypass because we are signed in")
return

if self.driver.execute_script("return window.installJoinShieldBypass?.();"):
logger.info("Installed JoinShield bypass")
else:
logger.warning("Could not install JoinShield bypass")

def fill_out_name_input(self):
num_attempts = 60
logger.info("Waiting for the name input field...")
Expand Down Expand Up @@ -526,6 +536,7 @@ def attempt_to_join_meeting(self):
logger.info("Waiting for the Join now button...")
join_button = self.locate_element(step="join_button", condition=EC.presence_of_element_located((By.CSS_SELECTOR, '[data-tid="prejoin-join-button"]')), wait_time_seconds=10)
logger.info("Clicking the Join now button...")
self.install_join_shield_bypass()
self.click_element(join_button, "join_button")

# Start polling participants
Expand Down
2 changes: 1 addition & 1 deletion version.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "1.82.5"
"version": "1.82.6"
}
Loading