[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