Using autoHotkey and PowerShell to prevent Microsoft Teams to set your status as 'Away'

Posted by : on

Category : powershell   scripts   microsoft   teams   autohotkey


Overview

Have you ever wished there was a way to keep your Teams status as “Active” even when you’re using your smartphone or PC for other purposes?

Your presence status on Microsoft Teams might not seem like a big deal at first. But it’s how your team members and contacts know when you can chat, join a meeting, or collaborate with them on a task.

If you’re working remotely, knowing how to keep your Teams status “Active” can also be crucial to sending the right message to your supervisors. It shows you’re around and engaged in work when you’re supposed to be.

I have seen a bunch of articles, posts about this with some saying it was not possible, I was curious and managed to do it.

Some tricks I have seen ranged from using a excel macro to move cells in a spreadcheet, playing a long youtube video or using an app like Amphetamine or Cafeine.

I know that just a simple script that moves the mouse won’t do it. Simply because it is not treated like a real input. I though with Autohotkey it could be possible, and I was right. Below is the script.

Basically, I tried to do it purely in PowerShell, by using inputs generated with SendWait function. A bit like in this article but the inputs were not registered by Teams when sent from the [System.Windows.Forms.SendKeys]::SendWait function.

NOTE

I have since learned that the SendKeys class has been updated for the .NET Framework 3.0 to enable its use in applications that run on Windows Vista. The enhanced security of Windows Vista (known as User Account Control or UAC) prevents the previous implementation from working as expected. The SendKeys class tries to use the previous implementation first, and if that fails, uses the new implementation. As a result, the SendKeys class may behave differently on different operating systems. Additionally, when the SendKeys class uses the new implementation, the SendWait method will not wait for messages to be processed when they are sent to another process.

When using C#, or any .NET language, there’s a way to force the SendKeys class to use the new implementation by adding the following application setting to your app.config file.

<appSettings>
<add key="SendKeys" value="SendInput"/>
</appSettings>

Or to use the previous implementation, use the value “JournalHook” instead.

I haven’t find any ways to do this in POwerShell yet, hence, the use of AutoHotKey.

Microsoft Teams App Status and Their Meanings

There are different Microsoft Teams Status alternatives on the Teams application. Users can reset status, set custom status messages, and manually change their status whenever they want.

Nonetheless, keep in mind that the green dot (green status) on their profile picture will always automatically disappear as soon as they close the MS Teams App or their computer goes idle.

table


How Long Does Teams Stay Green?

Teams stay green for as long as your computer does not go idle or sleep. This means that you can keep Microsoft Teams green for 15 minutes, 4 hours, or more - it depends on your computer settings. In a corporate environment, this is usually set by the global security policies. I often see the idle time set to 5 minutes.


Let’s do it

Install AutoHotKey

First you need to install Autohotkey. AutoHotkey is a free, open-source scripting language for Windows that allows users to easily create small to complex scripts for all kinds of tasks such as: form fillers, auto-clicking, macros, etc. I use it to automate some desktop tasks.

The script we will run with autohotkey is a file ending with .ahk. There are tons of usefull scripts by different authors which show what AutoHotkey might be capable of. For more ready-to-run scripts and functions, see Scripts and Functions Forum. I suggest you dive in those when you have time, you may very well find new productive ways to do things!

For this matter though, we just need something like this (see below).

MouseGetPos CurX, CurY
MouseMove 0, 0, 0
Click
Sleep, 10
MouseMove CurX, CurY, 0

The script above does the following:

  1. Get the mouse position
  2. Move the mouse cursor to position 0,0
  3. Register a mouse click
  4. Wait, sleep for 10 milliseconds
  5. Move back the mouse cursor to it’s original position

This will simulate user a input.

You could end this article here, just use the AutoHotKey script above and it would be fine. Why not over-engineer the system though ;) ?

PowerShell Script Block to call AHK script

Next, I wanted to use a Powershell Timer to generate an event after a set interval and call a script. My script will create and call the ahk file.

Note that to run a AutoHotKey script, you can do this:

start "" "your ahk script"

Or simply in PowerShell:

& "your ahk script"

Below is a script block that when invoked, will create a ahk file with the same content as above, then execute it.

Script Block

$tm_s = {
    param()
  
    try{
        [string]$ahk_script_data = @"
MouseGetPos CurX, CurY
MouseMove 0, 0, 0
Click
Sleep, 10
MouseMove CurX, CurY, 0
"@

        [bool]$ahk_installed = Test-Path "$ENV:ProgramFiles\AutoHotkey\AutoHotkey.exe"
        if($ahk_installed -eq $False){ return }
        [string]$tmppath = Join-Path "$ENV:Temp" ((New-Guid).Guid -as [string])
        New-Item -Path $tmppath -ItemType Directory -Force -ErrorAction Ignore | Out-Null

        [string]$filename = "{0}.ahk" -f ((New-Guid).Guid -as [string]).SubString(0,6)
        [string]$ahkscript = Join-Path $tmppath $filename

        Set-Content -Path $ahkscript -Value $ahk_script_data -Force
        Start-Sleep 1
        & $ahkscript
        Start-Sleep 1
        Remove-Item -Path $tmppath -Recurse -Force -ErrorAction Ignore | Out-Null
    }catch{
        Write-Error "$_"
    }finally{
        Write-verbose "ended"
}}.GetNewClosure()

[scriptblock]$tm_sb = [scriptblock]::create($tm_s) 

PowerShell Timer Class

Now let’s make a script to call this autohotkey script with a powershell timer, which generates an event after a set interval, with an option to generate recurring events.

We need a timer that we can easily register, unregister, start and stop. Here’s a class I made for this purpose:


class PeriodicScriptRunner : System.IDisposable
{

    [timers.timer]$TimerInst
    [scriptblock]$ScriptBlock
    [string]$Description
    [string]$Interval
    [string]$TimerUid
    [bool]$Started
    [bool]$Disposing

    PeriodicScriptRunner(){ $This.Defaults() }
    PeriodicScriptRunner([scriptblock]$ScriptBlock, [uint]$Interval, [bool]$AutoReset = $True) {
        $This.ScriptBlock = $ScriptBlock
        $This.Interval = $Interval
        $This.TimerInst = [timers.timer]::new($Interval)
        $This.TimerUid = "PeriodicScriptRunner_" + ((New-Guid).Guid -as [string]).Substring(24,12)
        $This.TimerInst.AutoReset = $AutoReset
        $This.TimerInst.Enabled = $False
        Register-ObjectEvent -InputObject $This.TimerInst -EventName elapsed SourceIdentifier $This.TimerUid -Action $This.ScriptBlock | Out-Null
    }

      
    [void] Start()
    {
        if($This.Started) { return }
        $This.Started = $True 
        $This.TimerInst.Enabled = $True 
        $This.TimerInst.start()
    }
    [void] Stop()
    {
        if(!($This.Started)) { return }
        $This.Started = $False 
        $This.TimerInst.Enabled = $False
        $This.TimerInst.Stop()
    }

    [void] Dispose()
    { 
        Write-Verbose -Message "Disposed called"
        $this.Disposing = $true
        $this.Dispose($true)
        [System.GC]::SuppressFinalize($this)
    }

    [void] Dispose([bool]$disposing)
    { 
        if($disposing)
        { 
            $This.Stop()
            Unregister-Event $This.TimerUid
            $This.TimerInst.Dispose()
            $This.Defaults()
        }
    }

    [void] SetDescription([string]$desc) { $This.Description = $desc }

    [void] Defaults()
    {
        $This.TimerInst = $Null
        $This.ScriptBlock = $Null
        $This.Description = $Null
        $This.Interval = 0
        $This.TimerUid = $Null
        $This.Started = $False
        $This.Disposing = $False
    }
    
    [bool] IsValid()
    {
        [bool]$isInvalid = ( ($This.TimerInst -eq $Null) -Or ($This.ScriptBlock -eq $Null) -Or ([string]::IsNullOrEmpty($This.TimerUid)) -Or ($This.Interval -le 0) )
        return !$isInvalid
    }
}


function New-PeriodicScriptRunner  {
   [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory=$True, Position=0)]
        [scriptblock]$ScriptBlock,
        [Parameter(Mandatory=$True, Position=1)]
        [TimeSpan]$Interval,
        [Parameter(Mandatory=$False)]
        [switch]$Start
    )
    try{
        [PeriodicScriptRunner]$Runner = [PeriodicScriptRunner]::new($ScriptBlock,$($Interval.TotalMilliseconds),$True)
        if($Start){
            $Runner.Start()
        }
        return $Runner
    }catch{
        Write-Error $_ -ShowStack
    }
}

Note that the class inherits from the System.IDisposable class so we can dispose it like other objects.

The Start-KeepAlive and Stop-KeepAlive functions

Using the PeriodicScriptRunner class above, we can create a timer to call our scriptblock like this:

Note the delay in the Start-KeepAlive function is set to 4m30s, short of 5 minutes, which is the Idle delay set on my machime, customize to your needs!


function Start-KeepAlive{
    [CmdletBinding(SupportsShouldProcess)]
    param()

    try{
        [PeriodicScriptRunner]$Runner = New-PeriodicScriptRunner -ScriptBlock $tm_sb -Interval ([Timespan]::new(0,4,30)) -Start
        Set-Variable -Name "KeepAliveRunner" -Value ($Runner -as [PeriodicScriptRunner]) -Option AllScope -Scope Global -Force -ErrorAction Ignore
    }catch{
        Write-Error $_ -ShowStack
    }
}


function Stop-KeepAlive{
    [CmdletBinding(SupportsShouldProcess)]
    param()

    try{
        $Runner = Get-Variable -Name "KeepAliveRunner" -ValueOnly -ErrorAction Ignore
        if($Runner -ne $Null){
            if($Runner.IsValid()){
                Write-verbose "Disposing Runner"
                $Runner.Dispose()    
            }else{
                Write-verbose "Runner already disposed!"    
            }
        }else{
            Write-verbose "No Runner Found"
        }
    }catch{
        Write-Error $_ -ShowStack
    }
}

Conclusion

And VOILA, here’s a working and slightly overkill way to keep your status in Green in the MS Teams app :)

All you need to do is to call Start-KeepAlive when you want to keep your status in Green and Stop-KeepAlive when it’s time to return to normal operation.

Enjoy!


About Guillaume Plante
Guillaume Plante

A developper with a passion for technology, music, astronomy and art. Coding range: hardware/drivers, security, ai,. c/c++, powershell

Email : guillaumeplante.qc@gmail.com

Website : https://arsscriptum.ddns.net

Useful Links