Microsoft Teams send Activity Feed Notification through Graph API - azure

I have a with yeoman teams scaffolded tab in which I ask for adminconsent for application permission.
On a Go backend I then use the code to retrieve the access token. I am also getting all Users of the team.
Although I am using only application permissions, I can not send notifications to the admin account. When trying to send notifications to other accounts, the Graph API returns a 204 Status Code but the activity feed of the recipient stays empty.
The application has following application permissions (with granted admin consent):
ChannelMember.ReadAll
GroupMember.ReadAll
Team.ReadBasic.All
TeamMember.Read.All
TeamsActivity.Send
This is the Manifest file with version 1.11:
{
"manifestVersion": "1.11",
"id": "{{APPLICATION_ID}}",
"version": "{{VERSION}}",
"permissions": [
"identity",
"messageTeamMembers"
],
"webApplicationInfo": {
"id": "{{APPLICATION_ID}}",
"resource": "api://{{DOMAIN}}.{{APPLICATION_ID}}"
},
"devicePermissions": [
"notifications"
],
"activities": {
"activityTypes": [
{
"type": "newActivity",
"description": "A new Activity Description"
}
]
}
}
The HTTP POST request to: https://graph.microsoft.com/v1.0/teams/{teamID}/sendNotification
The JSON Body:
{
"topic": {
"source": "entityUrl",
"value": "https://graph.microsoft.com/v1.0/teams/%s"
},
"activityType": "newActivity",
"previewText": {
"content": "A new Activity Description"
},
"recipient": {
"odata.type": "microsoft.graph.aadUserNotificationRecipient",
"userId": "{{USER_ID}}"
}
}
The recipient is also Member of the channel the activity is referring to. I also made sure that the recipient can receive notifications.

Related

Why does the Microsoft Graph API's invite endpoint only return share link URLs for certain email addresses?

When I make requests to the Microsoft Graph API's invite endpoint to create secure (email-based) sharing links for a specific collection of emails that are external to our organization, the response does not contain the requested sharing link for certain email addresses. When I look at the SharePoint site UI to see how the permissions are listed for the item, the email address with issue (RealEmail) shows up differently from the one that does return a sharing link through the API (TestEmail). Both RealEmail and TestEmail are marked as external users, but it seems that the RealEmail external user is recognized as an actual external or guest user account, rather than just being an email unknown to SharePoint, like TestEmail.
Here's the request I made on Microsoft Graph Explorer:
POST https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/items/{item-id}/invite
{
"requireSignIn": true,
"sendInvitation": false,
"roles": [
"read"
],
"recipients": [
{
"email": "RealEmail#example.com"
},
{
"email": "TestEmail#example.com"
}
]
}
And this was the response (with real data removed):
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(permission)",
"value": [
{
"#odata.type": "#microsoft.graph.permission",
"id": "removed",
"roles": [
"read"
],
"grantedTo": {
"user": {
"email": "RealEmail#example.com",
"id": "removed",
"displayName": "RealFirstName RealLastName"
}
}
},
{
"#odata.type": "#microsoft.graph.permission",
"roles": [
"read"
],
"grantedToIdentities": [
{
"user": {
"email": "TestEmail#example.com"
}
}
],
"invitation": {
"signInRequired": true
},
"link": {
"type": "view",
"webUrl": "removed"
}
}
]
}
This is what the Manage Access UI looks like (with real data removed)

Unable to use bot with graph api to call a user on teams whose user id I provided in the calls api

I followed this example https://learn.microsoft.com/en-us/microsoftteams/platform/sbs-calling-and-meeting
These are the steps I have done.
Registered an App in Azure using demo tenant
Created a policy for a demo tenant user for creating the online meeting on behalf of that user.
Added following Graph API Applications permissions to my Azure App
CallRecords.Read.All
Calls.Initiate.All
Calls.AccessMedia.All
Calls.InitiateGroupCall.All
Calls.JoinGroupCall.All
OnlineMeetings.ReadWrite.All
OnlineMeetings.Read.All
Calls.JoinGroupCallAsGuest.All
Granted Admin consent for the above permissions
For Bot
Created a Bot Channel Registeration in Azure account which have
subscription enabled.
While registering the bot, used https://<my_ngrok_url>/api/messages as the messaging endpoint.
Enabled the Teams Channel. Provided App-Name, Resource Group and
other required information I did Select the Calling tab on the Teams
channel page.
Select Enable calling, and then updated the Webhook
(for calling) with (https://yourNgrok/callback.
I am receiving the notifications.So this part works.
For bot my manifest is
{
"$schema": "https://github.com/OfficeDev/microsoft-teams-app-schema/blob/preview/DevPreview/MicrosoftTeams.schema.json",
"manifestVersion": "devPreview",
"version": "1.0.0",
"id": "<<app-id>>",
"packageName": "com.acs.sample",
"developer": {
"name": "Contoso",
"websiteUrl": "https://example.azurewebsites.net",
"privacyUrl": "https://teams.microsoft.com",
"termsOfUseUrl": "https://teams.microsoft.com"
},
"icons": {
"color": "color.png",
"outline": "outline.png"
},
"name": {
"short": "Calling bot",
"full": "Calling bot"
},
"description": {
"short": "Calling bot",
"full": "Calling bot"
},
"accentColor": "#FFFFFF",
"bots": [
{
"botId": "<<app-id>>",
"scopes": [ "personal", "team", "groupchat" ],
"supportsFiles": false,
"isNotificationOnly": false,
"supportsCalling": true,
"supportsVideo": true
}
],
"permissions": [ "identity", "messageTeamMembers" ],
"validDomains": [ "*.ngrok.io" ]
}
Installed Calling Bot in Teams
Installed without any error. I can make calls from the bot and see the notifications on my calling endpoints. One strange behavior I noticed, Its message text-box becomes disabled after a while but I can still make call from the bot.
Now I want to make call to a user from an endpoint.
From my python code. I used this code to get access token.
def getToken():
url = "https://login.microsoftonline.com/tenant_id/oauth2/v2.0/token"
type = 'post'
data={
"client_id": 'client_id',
"client_secret": 'client_secret',
"scope": 'https://graph.microsoft.com/.default',
"grant_type": 'client_credentials',
}
resp = requests.request(
method = type,
url = url,
data = data
)
resp_data = resp.json()
return resp_data['access_token'];
This access token is correct because I used the token to get users list etc using graph api to see if I can use the api or not.
Then using this token I call calls endpoint
def call_user():
accessToken = getToken();
url = "https://graph.microsoft.com/beta/communications/calls"
type = 'post'
data={
"#odata.type": "#microsoft.graph.call",
"callbackUri": "https://my_ngrok_url/call",
"targets": [
{
"#odata.type": "#microsoft.graph.invitationParticipantInfo",
"identity": {
"#odata.type": "#microsoft.graph.identitySet",
"user": {
"#odata.type": "#microsoft.graph.identity",
"displayName": "John",
"id": "user_id_to_whom_i_want_to_call"
}
}
}
],
"requestedModalities": [
"audio"
],
"mediaConfig": {
"#odata.type": "#microsoft.graph.serviceHostedMediaConfig"
}
}
resp = requests.request(
method = type,
url = url,
data = data,
headers={"Authorization": "Bearer " + accessToken},
)
resp_data = resp.json()
return resp_data
It gives internal server error as response. Please help what i am doing wrong.

Create custom Azure DevOps Service Connection for rest api

I have a REST API on the internet that is secured with Azure AD and a required header (custom apikey).
You can call this API in example with postman using the client credentials flow. This all works fine.
I'm now developing a custom Azure DevOps extension that is showing the information returned from that API in the workitem form of ADO. I can call the API (if I remove the security from the API) from the extension through a generic Service Connection. Now I want to get the security working.
So I want to create a custom service connection that will get an accesstoken from the azure AD using ClientID and ClientSecret (client credentials flow).
When I have that working, I can call my API on a secure way.
Also how do I add the mandatory header to the call to the api? I need to add the header as field to the service connection as well right?
So I think I end up with a custom service connection instance that asks for TenantId, ClientId, Client Secret, Audience/Scope, List of headers (name/value).
For a more reference blog post I used this one: https://thingswithcode.blogspot.com/2019/07/using-azure-devops-service-connections.html
{
"id": "api-service-connection",
"description": "Service connection for api",
"type": "ms.vss-endpoint.service-endpoint-type",
"targets": [
"ms.vss-endpoint.endpoint-types"
],
"properties": {
"name": "Call API",
"displayName": "Call API",
"icon": "img/world.png",
"url": {
"displayName": "API Url",
"value": "https://path-to-api",
"helpText": "Url of the API to connect to."
},
"inputDescriptors": [
{
"id": "api-key",
"name": "API Key",
"description": "The value for the header 'ApiKey'",
"inputMode": "textbox",
"isConfidential": false,
"validation": {
"isRequired": true,
"dataType": "string"
}
}
],
"authenticationSchemes": [
{
"type": "ms.vss-endpoint.endpoint-auth-scheme-oauth2" // this is wrong...
}
],
"headers": [
{
"name": "ApiKey",
"value": "{{endpoint.api-key}}"
}
],
"helpMarkDown": "<b>Learn more</b>"
}
}
Thanks

Webhooks for Pages

My app doesn't have the 'pages_manage_metadata' permission yet.
So I use Graph API Explorer to Subscribe my app Webhook for a Facebook Page.
*[GET] me/subscribed_apps
return
"data": [
{
"category": "Business",
"link": "{https://}",
"name": "{App Name}",
"id": "{App Id}",
"subscribed_fields": [
"messages",
"feed",
"mention"
]
}
]
}
When I comment on a page's post, I just get notified with data returned: {}.
The same goes for when I use the 'Feed Test' in the 'Webhooks / Page' menu of the App.
How can I get this data?

Google chat custom cards using dialogflow fulfilment webhook

I am trying to integrate DialogFlow bot with Hangouts Chat (for G Suite). I have enabled the integration on DialogFlow and the basic intents are working fine.
In order to perform backend operations using fulfillment, I have created a firebase cloud function and added this as the webhook URL on DialogFlow fulfillment page.
I have written the cloud function code to identify the intent, and to generate the Webhook response format for a simple text response. This is working, and I am seeing the firestore data being modified in response to the intent.
However for a more complicated intent, I wish to use more of the dynamic card based response that Chat offers. In order to achieve this, I have looked at the documentation for dialogflow card response.
I saw this following code at https://cloud.google.com/dialogflow/docs/integrations/hangouts. When I paste this into the dialogflow intent editor UI under hangouts custom payload (after disabling webhook integration), it works
{
"hangouts": {
"header": {
"title": "Pizza Bot Customer Support",
"subtitle": "pizzabot#example.com",
"imageUrl": "..."
},
"sections": [{
"widgets": [{
"keyValue": {
"icon": "TRAIN",
"topLabel": "Order No.",
"content": "12345"
}
},
{
"keyValue": {
"topLabel": "Status",
"content": "In Delivery"
}
}]
},
{
"header": "Location",
"widgets": [{
"image": {
"imageUrl": "https://dummyimage.com/600x400/000/fff"
}
}]
},
{
"header": "Buttons - i could leave the header out",
"widgets": [{
"buttons": [{
"textButton": {
"text": "OPEN ORDER",
"onClick": {
"openLink": {
"url": "https://example.com/orders/..."
}
}
}
}]
}]
}]
}
}
This is exactly what I need, but I need this response from the webhook. I'm not getting the correct response format to map between the two.
When I try to integrate the same code with the webhook, I am not getting any reply on hangouts chat. When I check the history section on dialogflow UI, here is the response structure as mentioned in Raw interaction log
{
"queryText": "<redacted>",
"parameters": {},
"intent": {
"id": "<redacted>",
"displayName": "<redacted>",
"priority": 500000,
"webhookState": "WEBHOOK_STATE_ENABLED"
},
"intentDetectionConfidence": 1,
"diagnosticInfo": {
"webhook_latency_ms": 284
},
"languageCode": "en",
"slotfillingMetadata": {
"allRequiredParamsPresent": true
},
"id": "<redacted>",
"sessionId": "<redacted>",
"timestamp": "2020-07-30T12:05:29.094Z",
"source": "agent",
"webhookStatus": {
"webhookUsed": true,
"webhookPayload": {
"hangouts": {
"header": {
"subtitle": "pizzabot#example.com",
"title": "Pizza Bot Customer Support",
"imageUrl": "..."
},
"sections": [
{
"widgets": [
{
"keyValue": {
"content": "12345",
"topLabel": "Order No.",
"icon": "TRAIN"
}
},
{
"keyValue": {
"topLabel": "Status",
"content": "In Delivery"
}
}
]
},
{
"widgets": [
{
"image": {
"imageUrl": "https://dummyimage.com/600x400/000/fff"
}
}
],
"header": "Location"
},
{
"widgets": [
{
"buttons": [
{
"textButton": {
"text": "OPEN ORDER",
"onClick": {
"openLink": {
"url": "https://example.com/orders/..."
}
}
}
}
]
}
],
"header": "Buttons - i could leave the header out"
}
]
}
},
"webhookStatus": {
"message": "Webhook execution successful"
}
},
"agentEnvironmentId": {
"agentId": "<redacted>",
"cloudProjectId": "<redacted>"
}
}
I also found this link on chat docs which explains how to show an interactive card based UI https://developers.google.com/hangouts/chat/how-tos/cards-onclick. However I'm not able to understand how to integrate the same with the webhook.
UPDATE
I have followed a tutorial at https://www.leeboonstra.com/Bots/custom-payloads-rich-cards-dialogflow/ and was able to get the card response to show up using the sample code they mention. It is using this deprecated library (https://github.com/dialogflow/dialogflow-fulfillment-nodejs). Here is the code for that to work,
let payload = new Payload("hangouts", json, {
rawPayload: true,
sendAsMessage: true,
});
agent.add(payload);
Here the json variable should be the previous JSON structure I have mentioned. So now, I'm able to map to the correct response format using the deprecated API. However, I'm not able to get the button to send the right response to the back end. Here is the buttons field that I modified from the previous json,
"buttons": [
{
"textButton": {
"text": "Click Me",
"onClick": {
"action": {
"actionMethodName": "snooze",
"parameters": [
{
"key": "time",
"value": "1 day"
},
{
"key": "id",
"value": "123456"
}
]
}
}
}
}
]
As far as I know, responding to a Google Chat (formerly Hangouts Chat) button isn't possible when using the direct Dialogflow integration.
The problem is that the button response can be sent one of two ways:
An event will be sent back to the bot code indicating the click.
Using the onClick.openLink.url property, as most of your test show.
This will take the person clicking it to the URL in question. But once there, you're taken out of the bot flow.
However, the documentation for the Hangouts Chat integration with Dialogflow doesn't provide any information about how this event is passed to Dialogflow, and the last time I tested it - it isn't.
You can write your own integration using Google Chat's API on something like Cloud Functions or Apps Script and have your script call Dialogflow's Detect Intent API to determine what Intent would be triggered by the user (and determine replies or call the webhook for additional processing). Under this scheme, you can choose how to handle the onClick event. Making your own integration also provides you a way to do Incoming Webhooks, which isn't possible when using the Dialogflow integration.

Resources