Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TeamOn

Windows system-tray utility that keeps your machine looking active, so Teams and Slack stop flipping your presence to Away while you are reading, on a call, or thinking.

Every 60 seconds it repositions the mouse cursor by exactly one pixel and leaves it there. That displacement is what Windows counts as user input, which resets the OS idle timer that presence indicators read.

Why one pixel actually works

Teams does not watch its own window — it goes Away after roughly 5 minutes of device inactivity, measured from the OS last-input timer. A single real input event resets that timer, so a 60-second interval leaves a 5× safety margin.

The catch is what "real" means. The obvious implementation sends a relative mouse delta of 1:

# Broken: dx is a "mickey", not a pixel.
SendInput(MOUSEEVENTF_MOVE, dx=1)

Relative motion is scaled by the pointer speed and acceleration curve before it becomes pixels. On a machine with the pointer-speed slider raised, dx=1 scales down and truncates to zero pixels — while SendInput still returns success. The cursor never moves and nothing reports a problem.

TeamOn sends an absolute move instead:

SendInput(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK, ...)

Absolute coordinates are normalized (0–65535) and bypass the pointer transform entirely, so one pixel requested is one pixel delivered. VIRTUALDESK maps that range across the whole virtual desktop rather than the primary monitor, which keeps it correct on multi-monitor setups with negative coordinates.

Direction alternates every pulse (+1, −1, +1, …), so the cursor oscillates between two adjacent pixels instead of drifting across the screen.

And it verifies. After each pulse the cursor position is re-read; if it did not actually move, the pulse raises, the tray icon turns orange, and the failure is logged. A move that Windows accepts but silently discards is the exact bug that motivated this project, and it is now impossible for it to happen quietly.

Install

Requires Windows and Python 3.10+.

pip install -r requirements.txt
python team_on.py

A tray icon appears, disabled by default. Left-click it to toggle.

Icon Meaning
Grey Disabled
Green Enabled, pulsing normally
Orange Enabled, but the last pulse did not move the cursor (retrying)

Build a standalone exe

python -m PyInstaller TeamOn.spec

Produces dist/TeamOn.exe — windowed, no console, no Python install needed to run it.

Use python -m PyInstaller, not the bare pyinstaller command. If several Python installations are on PATH, the bare command can resolve to one that lacks pystray, producing an exe that builds cleanly and then dies at startup with ModuleNotFoundError.

Tests

python -m unittest test_team_on

19 tests, unittest only, no external test dependencies. They run real threads at a 20 ms interval and never touch Win32, so the suite works headless.

Logs

Rotating diagnostic log at %LOCALAPPDATA%\TeamOn\teamon.log (256 KB × 3):

13:44:43 INFO  Pulse sent; before=(662, 658) after=(661, 658)
13:45:43 INFO  Pulse sent; before=(661, 658) after=(662, 658)
13:50:43 ERROR Pulse failed; error_type=OSError error=cursor did not move; ...

before and after should always differ by 1 in X, alternating sign. If they are equal, something is blocking synthetic input.

Limits

Presence still goes Away when the workstation is locked (Win+L), the UAC secure desktop is up, or an RDP session is disconnected. Synthetic input cannot reach the interactive desktop in those states, and no keep-alive fixes it. TeamOn surfaces them as pulse errors instead of pretending to work.

This is a presence and idle-timer tool. It does not defeat screen locks, and it is not a substitute for actually being at your desk.

Architecture

Three layers, kept separate so the testable logic never touches Win32 or the tray:

  1. Win32 — module-level ctypes bindings to user32. The only code that talks to Windows.
  2. LogicPersistentMousePulse and KeepActiveController. Collaborators are injected as constructor callables, so tests use plain fakes. Imports no pystray or PIL.
  3. UITeamOnApp wires the two together into a pystray icon and owns all logging and icon-color decisions. pystray and PIL are imported inside functions, so import team_on works headless.

New behavior belongs in the logic layer with injected dependencies. See CLAUDE.md for the detailed notes.

About

Windows tray utility that keeps Teams/Slack presence available by nudging the cursor 1px every 60s - using an absolute SendInput move, because a relative delta of 1 truncates to zero pixels.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages