[PowerShell] Set Skype for Business Presence to "In a Call" when you are in a Microsoft Teams call.
I wanted the ability to sync Skype for Business with  Microsoft Teams when I was in a Microsoft Teams call. I couldn't find any solutions online so i created a PowerShell script. You can then create a Windows Service to run this script however often as you want. 5 seconds is good enough for me. 
The only things to make sure you have is Skype Modules installed and an Office 365 app with Microsoft Graph permissions:
Delegated - Read user's presence information
Delegated - Read presence information of all users in your organization
Also set your token to expire after 1 day. My script will check the day and renew it if they don't match.
Enjoy!
______________________________________________________________________
The only things to make sure you have is Skype Modules installed and an Office 365 app with Microsoft Graph permissions:
Delegated - Read user's presence information
Delegated - Read presence information of all users in your organization
Also set your token to expire after 1 day. My script will check the day and renew it if they don't match.
Enjoy!
______________________________________________________________________
# SET YOUR CACHE DIRECTORY TO VARIABLE
$tokenCache = ($env:APPDATA + "\token.tmp")
Function getToken{
    # Azure AD OAuth User Token for Graph API
    # Get OAuth token for a AAD User (returned as $token)
    # Application (client) ID, tenant ID and redirect URI
    $resourceAppIdURI = "https://graph.microsoft.com"
    $clientId         = "xxxxx-xxxxxx-xxxxxxx-xxxxxx-xxxxx"
    $TenantId       = "xxxxx-xxxxxx-xxxxxxx-xxxxxx-xxxxx"
    $redirectUri      = "https://login.microsoftonline.com/common/oauth2/nativeclient"
    # Add required assemblies
    Add-Type -AssemblyName System.Web, PresentationFramework, PresentationCore
    # Scope - Needs to include all permisions required separated with a space
    $scope = "User.Read.All Group.Read.All" # This is just an example set of permissions
    # Random State - state is included in response, if you want to verify response is valid
    $state = Get-Random
    # Encode scope to fit inside query string
    $scopeEncoded = [System.Web.HttpUtility]::UrlEncode($scope)
    # Redirect URI (encode it to fit inside query string)
    $redirectUriEncoded = [System.Web.HttpUtility]::UrlEncode($redirectUri)
    # Construct URI
    $uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/authorize?client_id=$clientId&response_type=code&redirect_uri=$redirectUriEncoded&response_mode=query&scope=$scopeEncoded&state=$state"
    # Create Window for User Sign-In
    $windowProperty = @{
        Width  = 500
        Height = 700
    }
    $signInWindow = New-Object System.Windows.Window -Property $windowProperty  
    # Create WebBrowser for Window
    $browserProperty = @{
        Width  = 480
        Height = 680
    }
    $signInBrowser = New-Object System.Windows.Controls.WebBrowser -Property $browserProperty
    # Navigate Browser to sign-in page
    $signInBrowser.navigate($uri)  
    # Create a condition to check after each page load
    $pageLoaded = {
        # Once a URL contains "code=*", close the Window
        if ($signInBrowser.Source -match "code=[^&]*") {
            # With the form closed and complete with the code, parse the query string
            $urlQueryString = [System.Uri]($signInBrowser.Source).Query
            $script:urlQueryValues = [System.Web.HttpUtility]::ParseQueryString($urlQueryString)
            $signInWindow.Close()
        }
    }
    # Add condition to document completed
    $signInBrowser.Add_LoadCompleted($pageLoaded)
    # Show Window
    $signInWindow.AddChild($signInBrowser)
    $signInWindow.ShowDialog()
    # Extract code from query string
    $authCode = $script:urlQueryValues.GetValues(($script:urlQueryValues.keys | Where-Object { $_ -eq "code" }))
    if ($authCode) {
        # With Auth Code, start getting token
        # Construct URI
        $uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
        # Construct Body
        $body = @{
            client_id    = $clientId
            scope        = $scope
            code         = $authCode[0]
            redirect_uri = $redirectUri
            grant_type   = "authorization_code"
        }
        # Get OAuth 2.0 Token
        $tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body
        # Access Token
        $token = ($tokenRequest.Content | ConvertFrom-Json).access_token
        $store = storeSecureString -filePath $tokenCache -string $token
    }
    else {
        Write-Error "Unable to obtain Auth Code!"
    }
}
function SetSFBStatus
{
import-module "C:\Program Files\WindowsPowerShell\Modules\Microsoft.Lync.Model.dll"
import-module "C:\Program Files\WindowsPowerShell\Modules\Microsoft.Office.Uc.dll"
# Obtain the entry point to the Lync.Model API
$client = [Microsoft.Lync.Model.LyncClient]::GetClient()
$self = $client.Self;
#Set Details of Personal Note and Availability
#Useful availability codes for use below - 3500 Available, 15500 Away (converted to "Off Work" in this script by setting activity ID), 6500 Busy, 9500 Do not disturb, 12000 Be Right Back)
$availability = 6500
$date = [DateTime]::Now
#$message = "Availability script last run $date"
#Publish personal note and presence availability of the local user
$contactInfo = new-object 'System.Collections.Generic.Dictionary[Microsoft.Lync.Model.PublishableContactInformationType, object]'
$contactInfo.Add([Microsoft.Lync.Model.PublishableContactInformationType]::PersonalNote,
            $message)
$contactInfo.Add([Microsoft.Lync.Model.PublishableContactInformationType]::Availability,
            $availability)
If ($availability -eq 6500) {$contactInfo.Add([Microsoft.Lync.Model.PublishableContactInformationType]::ActivityID, "on-the-phone")}
$ar = $self.BeginPublishContactInformation($contactInfo, $null, $null)
$self.EndPublishContactInformation($ar)
}
# Cache the token if doesnt exist, but check modified date and delete it if its not today
# STORE TOKEN TO A SECURE FILE
function storeSecureString{
    Param(
        $filePath,
        $string
    )
    try{
        $stringForFile = $string | ConvertTo-SecureString -AsPlainText -Force -ErrorAction Stop | ConvertFrom-SecureString -ErrorAction Stop
        Set-Content -Path $filePath -Value $stringForFile -Force -ErrorAction Stop | Out-Null
    }catch{
        Throw "Failed to store string: $($Error[0] | out-string)"
    }
}
# LOAD PASSWORD FROM SECURE FILE
function loadSecureString{
    Param(
        $filePath
    )
    try{
        $string = Get-Content $filePath -ErrorAction Stop | ConvertTo-SecureString -ErrorAction Stop
        $string = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($string)
        $string = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($string)
        if($string.Length -lt 3){throw "no valid string returned from cache"}
        return $string
    }catch{
        Throw
    }
}
$Today = (Get-Date).Day
$attempts = 0
while($true){
    If((Get-Content -Path $tokenCache).Length -lt 3){
        getToken
      
        $token = loadSecureString -filePath $tokenCache
        If(((get-item $tokenCache).LastWriteTime).Day -ne $Today){
            getToken
            $token = loadSecureString -filePath $tokenCache
        }
    }Else{
        $token = loadSecureString -filePath $tokenCache
        try{
            # Specify the URI to call and method
            $uri = "https://graph.microsoft.com/beta/me/presence"
            $method = "GET"
            # Run Graph API query
            $query = Invoke-WebRequest -Method $method -Uri $uri -ContentType "application/json" -Headers @{Authorization = "Bearer $token"} -ErrorAction Stop
            $status = ($query.Content | ConvertFrom-Json).activity
            Write-host "Your current Teams status is $status" -ForegroundColor Green
            If($status -eq "InACall") {
          
            # Set Skype Status to in a call
            SetSFBStatus
            }
            return $False
        }catch{
                Write-host "Your token didn't work! Try again..."
                Remove-Item -Path $tokenCache
                return $False
            }
        }
$attempts++
}
 
I only get "your token did not work" :/ . The app IS logging-in, but:
ReplyDelete"Get-Content : Cannot find path 'C:\Users\chavv\AppData\Roaming\token.tmp' because it does not exist.
At C:\Projects\PowerShell\teams\setSkypeStatus.ps1:289 char:9
+ If((Get-Content -Path $tokenCache).Length -lt 3){
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\chavv\AppData\Roaming\token.tmp:String) [Get-Content], ItemNot
FoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
False
Your token didn't work! Try again..."
Any help? 10x
Make sure the app you created in asure is "Visible to Users"
Delete