Montag, 24. Oktober 2022

Teams Presence Status via CLI

 Microsoft finally came around at the beginning of this year to open the option to set the (Teams) presence state via their MS Graph API.

For my Asterisk telephony application this comes in handy as I can now set Teams busy when a user is on the phone.

I am using the client credential flow with application type permissions:

#!/bin/bash

token_file="/tmp/azure.token"
response="/tmp/azure.response"

tenant='your tenant ID'
client_id='your client ID'
client_secret='your client secret'
scope='https://graph.microsoft.com/.default'
grant_type='client_credentials'

#status='{"sessionId":"'${client_id}'","availability":"Available","activity":"Available","expirationDuration":"PT1H"}'
#status='{"sessionId":"'${client_id}'","availability":"Busy","activity":"InACall","expirationDuration":"PT2H"}'
status='{"sessionId":"'${client_id}'"}'

if [ "$#" -lt 2 ] ; then
        echo "Usage: $0 <user> <available|busy>"
        exit 1
elif [ -n "${1//[-A-Za-z0-9@._]}" ] ; then
        echo "Error: <user> can only contain alpha-numeric characters and any of '-_.@'"
        exit 1
elif [ "$2" = "busy" ] ; then
        status='{"sessionId":"'${client_id}'","availability":"Busy","activity":"InACall","expirationDuration":"PT2H"}'
fi

user="$1"

update_token()
{
        token=$(curl -s -X POST -H "Content-Type: application/x-www-form-urlencoded"  "https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token" \
         --data-urlencode "client_id=${client_id}" \
         --data-urlencode "client_secret=${client_secret}" \
         --data-urlencode "scope=${scope}" \
         --data-urlencode "grant_type=${grant_type}" | jq -r '.access_token')

        echo $token > "${token_file}"
        chmod 600 "${token_file}"
}

get_token()
{
        if [ ! -f "${token_file}" ] ; then
                update_token
        else
                token=$(cat "${token_file}")
        fi
}

get_user()
{
        error=$(curl -s -X GET -H "Authorization: Bearer ${token}" -w "%{http_code}" -o "${response}" "https://graph.microsoft.com/v1.0/users/${user}")
}

get_token
get_user

if [ "$error" = "401" ] ; then
        update_token
        get_user
fi

if [ "$error" = "404" ] ; then
        echo "Error: <user> (${user}) not found!"
        exit 1
elif [ "$error" != "200" ] ; then
        echo "Error: unhandled HTTP ${error}"
        exit 1
fi

id=$(jq -r '.id' "${response}")
rm -f "${response}"


if [ "${status//availability}" != "${status}" ] ; then
        curl -s -X POST -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" "https://graph.microsoft.com/v1.0/users/${id}/presence/setPresence" -d "${status}"
else
        curl -s -X POST -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" "https://graph.microsoft.com/v1.0/users/${id}/presence/clearPresence" -d "${status}"
fi

I even created a DOS version that runs on current Windows 10/Server 2019+

@echo off
setlocal

REM #################################################
REM  Config section
REM #################################################

set TENANT=your_tenant_id
set CLIENT_ID=your_client_id
set CLIENT_SECRET=your_client_secret

REM #################################################
set SCOPE=https://graph.microsoft.com/.default
set GRANT_TYPE=client_credentials
set "TOKEN_FILE=%TEMP%/azure.token"
set "ERROR_FILE=%TEMP%/azure.error"
set "RESPONSE=%TEMP%/azure.response"

set JSON={}
set TOKEN=
set ID=
set STATUS=available

if %1.==. goto :usage
set USER=%1

if %2.==. goto :usage
if %2==busy set STATUS=busy

call :get_token
call :get_user
set /p ERROR=<%ERROR_FILE%

if %ERROR%=='401' (
    call :update_token
	call :get_token
    call :get_user
)
if %ERROR%=='404' echo "Error: <user> (%USER%) not found!" & goto :end
if not %ERROR%=='200' echo "Error: unhandled HTTP %ERROR%" & goto :end

for /f "usebackq delims=" %%f in ("%RESPONSE%") do set "JSON=%%f"
call :parse_json

set ID=%JSON[id]%

if %STATUS%==busy (
	curl -s -X POST -H "Authorization: Bearer %TOKEN%" -H "Content-Type: application/json" "https://graph.microsoft.com/v1.0/users/%ID%/presence/setPresence" -d "{""sessionId"":""%CLIENT_ID%"",""availability"":""Busy"",""activity"":""InACall"",""expirationDuration"":""PT2H""}"
) else (
	curl -s -X POST -H "Authorization: Bearer %TOKEN%" -H "Content-Type: application/json" "https://graph.microsoft.com/v1.0/users/%ID%/presence/clearPresence" -d "{""sessionId"":""%CLIENT_ID%""}"
)

goto :end

:update_token
    curl -s -X POST -H "Content-Type: application/x-www-form-urlencoded"  "https://login.microsoftonline.com/%TENANT%/oauth2/v2.0/token" --data-urlencode "CLIENT_ID=%CLIENT_ID%" --data-urlencode "CLIENT_SECRET=%CLIENT_SECRET%" --data-urlencode "SCOPE=%SCOPE%" --data-urlencode "GRANT_TYPE=%GRANT_TYPE%" > %TOKEN_FILE%
	exit /B

:get_token
    if not exist %TOKEN_FILE% call :update_token
	for /f "usebackq delims=" %%f in ("%TOKEN_FILE%") do set "JSON=%%f"
	call :parse_json
	set TOKEN=%JSON[access_token]%
	exit /B

:get_user
    curl -s -X GET -H "Authorization: Bearer %TOKEN%" -w '%%{http_code}' -o %RESPONSE% "https://graph.microsoft.com/v1.0/users/%USER%" > %ERROR_FILE%
	exit /B
	
:parse_json
	set "JSON=%JSON:~1,-1%"
	set "JSON=%JSON:":=",%"

	set mod=0
	for %%I in (%JSON%) do (
		set /a mod=!mod
		setlocal enabledelayedexpansion
		if !mod! equ 0 (
			for %%# in ("!var!") do endlocal & set "JSON[%%~#]=%%~I"
		) else (
			endlocal & set "var=%%~I"
		)
	)
	set JSON[
	exit /B

:usage
	echo "Usage: %0% <userprincipalname> <busy available="">"

:end


1 Kommentar:

  1. Hi! Aber es verhindert nicht, das man in Teams angerufen wird. Will sagen, das Teams auch bei "Busy on Buys" dennoch den Call durchreicht, obwohl man den Status auf "InACall" gesetzt hat.

    AntwortenLöschen