[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