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
20 changes: 11 additions & 9 deletions 2fa_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
TYPE_DELAY_MS = 100
PRE_ENTER_DELAY = 1
XDOTOOL_TIMEOUT = 10
MIN_TOTP_SECONDS_REMAINING = 8
MIN_TOTP_SECONDS_REMAINING = 15
MAX_AUTOFILL_SUBMISSIONS_RAW = os.environ.get("IBKR_2FA_MAX_SUBMISSIONS", "1")

# Window titles to search for 2FA prompts. Live IBKR accounts can show mobile
Expand Down Expand Up @@ -207,6 +207,15 @@ def focus_input_area(candidate):
run_xdotool(["windowfocus", "--sync", candidate.window_id])
run_xdotool(["mousemove", "--window", candidate.window_id, str(x), str(y)])
run_xdotool(["click", "1"])
time.sleep(0.2)


def type_totp_into_active_window(code):
"""Type into the focused control; Java dialogs can ignore direct window events."""
run_xdotool(["key", "ctrl+a", "BackSpace"])
run_xdotool(["type", "--delay", str(TYPE_DELAY_MS), code], sensitive=True)
time.sleep(PRE_ENTER_DELAY)
run_xdotool(["key", "Return"])


def submit_totp(candidate):
Expand Down Expand Up @@ -246,14 +255,7 @@ def submit_totp(candidate):

focus_input_area(candidate)
code = get_totp()

run_xdotool(["key", "--window", candidate.window_id, "ctrl+a", "BackSpace"])
run_xdotool(
["type", "--window", candidate.window_id, "--delay", str(TYPE_DELAY_MS), code],
sensitive=True,
)
time.sleep(PRE_ENTER_DELAY)
run_xdotool(["key", "--window", candidate.window_id, "Return"])
type_totp_into_active_window(code)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve window targeting when geometry is unavailable

This change now types the TOTP into the currently focused control, but focus_input_area is a no-op when candidate.width or candidate.height is missing. In that case submit_totp still calls type_totp_into_active_window, so the one-time code and Enter keystroke are sent to whichever window happened to be focused (not necessarily the IBKR auth dialog). Before this commit, the --window flag guaranteed delivery to candidate.window_id even without geometry data.

Useful? React with 👍 / 👎.


autofill_submission_count += 1
log.info("Auto-fill submitted, waiting for gateway response...")
Expand Down
2 changes: 2 additions & 0 deletions tests/test_docker_compose_ports.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ grep -Fq ' - IB_GATEWAY_CONC_GC_THREADS=${IB_GATEWAY_CONC_GC_THREADS:-1}' "
grep -Fq ' - ACCEPT_API_FROM_IP=${ACCEPT_API_FROM_IP:?Set ACCEPT_API_FROM_IP to your Cloud Run egress subnet or connector CIDR}' "$compose_file"

grep -Fq 'INPUT_CLICK_POSITION = (0.50, 0.40)' "$repo_dir/2fa_bot.py"
grep -Fq 'MIN_TOTP_SECONDS_REMAINING = 15' "$repo_dir/2fa_bot.py"
grep -Fq 'def type_totp_into_active_window(code):' "$repo_dir/2fa_bot.py"