I am looking to get All Contact folders in Outlook using https://oauthplay.azurewebsites.net/. However, for some reason I cannot get the default "Contacts" folder, just the ones created by the user. For example, I did a GET on https://outlook.office.com/api/v2.0/me/contactfolders, and this is an example of the result:
{
"value": [
{
"Id": "AAMkAGU0ZjM2ZWQ2LTZiYjQtNDY2Ny1hMTBjLTZmOTM4ZTMyMmRlNQAuAAAAAADfJok2QyPPRKN0MNMI2ntdAQAjvTBpWNeCQYCyqAy3mDiPAAAAAAFbAAA=",
"ParentFolderId": "AAMkAGU0ZjM2ZWQ2LTZiYjQtNDY2Ny1hMTBjLTZmOTM4ZTMyMmRlNQAuAAAAAADfJok2QyPPRKN0MNMI2ntdAQAjvTBpWNeCQYCyqAy3mDiPAAAAAAEOAAA=",
"DisplayName": "Test1"
},
{
"Id": "AI2ntdAQAjvTBpWNeCQYCyqAy3mDiPAAAAAAFcAAA=",
"ParentFolderId": "0MNMI2ntdAQAjvTBpWNeCQYCyqAy3mDiPAAAAAAEOAAA=",
"DisplayName": "Test2"
}
]
}
Any idea how to get the default contact folder?
You could use this api:
https://outlook.office.com/api/v2.0/me/contactfolders
This will get the contact folder collection under the default Contacts folder of the signed-in user (.../me/contactfolders), or under the specified contact folder.
For more information, please refer to this link:
Outlook Contacts REST API reference (version 2.0)
Related
I'm trying to implement the dynamic typeahead search as was shown on the below resources:
https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/dynamic-search?tabs=desktop%2Ccsharp
https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-type-ahead-search-adaptive-cards/csharp
I'm having the following Adaptive Card returned from the OnTeamsMessagingExtensionFetchTaskAsync (from message action):
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"id": "choiceSelect",
"type": "Input.ChoiceSet",
"choices": [
{
"title": "Choice 1",
"value": "Choice 1"
},
{
"title": "Choice 2",
"value": "Choice 2"
}
],
"placeholder": "Placeholder text",
"style": "filtered",
"choices.data": {
"type": "Data.Query",
"dataset": "cases"
}
}
]
}
Card is rendered correctly, but when I start typing, I'm getting the following results:
No request is being made. I was trying to debug the JS, but it is troublesome for me - I couldn't pinpoint the exact place it fails, but got these symbols to lookup: executeSearchInvoke, enableAppPermissionEnforcement and enableTenantSettingsForBots in 3.2-app.min.js.
Can anyone point me in the right direction? Is there some kind of dependency or permission I should add to the bot? Some setting in Teams Admin Center? I can add that static typeahead works flawlessly, but I need the dynamic one (big data collection to query).
EDIT:
I have confirmed that the app is added to the team (it is visible on the list of apps in team settings); I've removed and added it again just in case. I am still getting the same results.
List of permissions visible from the App details page:
This app will have permission to:
Receive messages and data that I provide to it.
Send me messages and notifications.
Access my profile information such as my name, email address, company name, and preferred language.
Receive messages and data that team members provide to it in a channel.
Send messages and notifications in a channel.
Access this team's information such as team name, channel list and roster (including team member's names and email addresses) - and use this to contact them​.
I am using Microsoft Teams Toolkit to create a configurable tab. When I am in the configurable tab, I would like to save the Deeplink for this tab to one of my database. However what I see is the App id is changing for the app package I have. I generated the app package using the Teams toolkit and then I uploaded in two Teams Channel on different tenant. I see the app id is changed in both the link. How can I make sure that app id remain same. Also is there any way I can get app id and Tab id dynamically in Tab itself?
[![enter image description here][1]][1]
Here is my code:
microsoftTeams.getContext((context) => {
teamId = context.groupId;
tenantid = context.tid;
channelId = context.channelId;
const entityId =
"_djb2_msteams_prefix_" +
Util.djb2_hash("<<AppId>>" + ":" + context.entityId.replace(/\+/g, " "));
var ctx ;
if (context.subEntityId || context.channelId) {
ctx = encodeURIComponent(JSON.stringify({
subEntityId: context.subEntityId,
channelId: context.channelId
}));
};```
//How to get the app id and the tab id
linkUrl="https://teams.microsoft.com/l/entity/**<<AppId>>**/"+entityId+"/" + "&context=" + ctx+ "&groupId="+context.groupId+"&tenantId="+context.tid;
});
};
[1]: https://i.stack.imgur.com/T5jGc.png
If you're using the same app package, then the app id will be the same in both cases (it's the one defined in your manifest file inside the package), and the entity id will be the same in both tenants also for the same reason.
I have managed to resolve the issue. Below is the way, you can get the app id in your configurable tab dynamically:
microsoftTeams.getTabInstances(function (tabInstances) {
// alert(JSON.stringify(tabInstances));
var tab = JSON.parse(JSON.stringify(tabInstances.teamTabs.find(x => x.channelId ==channelId)));
console.log(JSON.stringify(tab));
appid = tab["appId"];
});
Note: TabInstance doesnt have appId as a property, but when you stringly the object , you see the value.
Getting the App ID
When a customer in a tenant installs your App, this actually creates an instance of that app in their tenant.
If you, as a developer - installs or sideloads multiple copies of the same app in different scopes, this also creates new app-instance
Same for individual manifest uploads to a Team
This teams-app-instance is assigned an 'Internal App-ID'
you can see this in the Teams AppCatalog :
where the externalid is the id you specified in your App's manifest.json
In my personal experience you should be able to use the Teams_App_Manifest_id , but that you may/will into trouble when that resolves to more than one app-instance with that same external-id.
Note that links generated by the teams client tend to use the internal id. I assume to avoid that issue
Getting the Tab ID
as in the Context you have the Team.id and Channel.id and your Tab's Entity.id is, you then can use Graph to iterate across the tabs in that channel
https://graph.microsoft.com/beta/teams/{groupId}/channels/{channelId}/tabs?$expand=teamsApp&$filter=configuration/entityId eq '{entityId}'
Tenant App Catalog:
graph explorer
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#appCatalogs/teamsApps",
"value": [
{
"id": "fcb9685a-8bc4-4b82-947b-12f8d2146ec3",
"externalId": "73fb4e73-97e2-44f6-a880-160933ea881e",
"displayName": "App SSO in csharp",
"distributionMethod": "organization"
},
App install by user from tenant catalog (organization)
graph explorer
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#users('cb4d216f-1e86-4b8b-94f0-c213b258d376')/teamwork/installedApps(teamsApp())",
"#odata.count": 86,
"value": [
{
"id": "Y2I0ZDIxNmYtMWU4Ni00YjhiLTk0ZjAtYzIxM2IyNThkMzc2IyNmY2I5Njg1YS04YmM0LTRiODItOTQ3Yi0xMmY4ZDIxNDZlYzM=",
"teamsApp": {
"id": "fcb9685a-8bc4-4b82-947b-12f8d2146ec3",
"externalId": "73fb4e73-97e2-44f6-a880-160933ea881e",
"displayName": "App SSO in csharp",
"distributionMethod": "organization"
}
},
adding the binary-same Teams app 'for me'
results in the app showing twice in the left nav (as expected, so this also adds an App-instance with its own (internal) App id
"#odata.count": 87,
"value": [
{
"id": "Y2I0ZDIxNmYtMWU4Ni00YjhiLTk0ZjAtYzIxM2IyNThkMzc2IyNmY2I5Njg1YS04YmM0LTRiODItOTQ3Yi0xMmY4ZDIxNDZlYzM=",
"teamsApp": {
"id": "fcb9685a-8bc4-4b82-947b-12f8d2146ec3",
"externalId": "73fb4e73-97e2-44f6-a880-160933ea881e",
"displayName": "App SSO in csharp",
"distributionMethod": "organization"
}
},
// ....
{
"id": "Y2I0ZDIxNmYtMWU4Ni00YjhiLTk0ZjAtYzIxM2IyNThkMzc2IyMyNWYzMDBjMS05ZGE2LTRjYTMtYWUwZS1iN2EwZDJkMDQzZGY=",
"teamsApp": {
"id": "25f300c1-9da6-4ca3-ae0e-b7a0d2d043df",
"externalId": "73fb4e73-97e2-44f6-a880-160933ea881e",
"displayName": "App SSO in csharp",
"distributionMethod": "sideloaded"
}
},
I have a use case where I need to show following information in my personal application.
List of channels in teams where bot is installed.
List of users in teams where bot is installed.
I was exploring connector client for the same and came up with following code:-
const credentials = new MicrosoftAppCredentials(appId, appPassword);
const connectorClient = new ConnectorClient(credentials, {
baseUri: serviceUrl
});
const token = await credentials.getToken();
axios.defaults.headers.common.Authorization = `Bearer ${ token }`;
# To get channels
const response = await axios.get(
'https://smba.trafficmanager.net/in/v3/teams/{teamId}/conversations'
);
# To get members
const users = await connectorClient.conversations.getConversationPagedMembers(teamId);
This works perfect as long as I have the teamId.
But the issue I am facing here is with respect to finding teamId in personal scope. I install my bot application as follows by choosing the Add option.
As far as I understand, the above installs the bot in the personal scope of the user. Now, in this scenario the team id information is not present in conversationUpdate event at all. Please note that this is the first time I am installing the bot in the team, so the data should be available as per Microsoft documentation, but the only information available in channel object is tenant.
{
"membersAdded": [
{
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0"
}
],
"type": "conversationUpdate",
"timestamp": "2017-02-23T19:38:35.312Z",
"localTimestamp": "2017-02-23T12:38:35.312-07:00",
"id": "f:5f85c2ad",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/amer-client-ss.msg/",
"from": {
"id": "29:1I9Is_Sx0OIy2rQ7Xz1lcaPKlO9eqmBRTBuW6XzkFtcjqxTjPaCMij8BVMdBcL9L_RwWNJyAHFQb0TRzXgyQvA"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"id": "19:efa9296d959346209fea44151c742e73#thread.skype"
},
"recipient": {
"id": "28:f5d48856-5b42-41a0-8c3a-c5f944b679b0",
"name": "SongsuggesterBot"
},
"channelData": {
// for me this object is empty
"team": {
"id": "19:efa9296d959346209fea44151c742e73#thread.skype"
},
"eventType": "teamMemberAdded",
"tenant": {
"id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
}
}
}
Next, I also tried to install the bot in the team scope by using Add To Teams option. In this case it prompts me to select a channel to install, in which I choose general.
Now, I do get the team object inside channelData in onConversationUpdate and this flow works perfectly fine.
{
"membersAdded": [
{
"id": "28:64564f44-dd7c-441a-b427-efcd662f21b5"
}
],
"type": "conversationUpdate",
"timestamp": "2021-10-14T13:22:01.6072361Z",
"id": "f:4ebc9a41-5140-7621-33f5-31d97275ce00",
"channelId": "msteams",
"serviceUrl": "https://smba.trafficmanager.net/in/",
"from": {
"id": "29:17ZGff4Pvqz_zSNqEexg-86uBFcB6vnOBZzCwu4_puGdDsrYWCW_DdlB15PrcjC--nLlqD5CwtLMJyzXPY5OSsg",
"aadObjectId": "eac26e98-104a-4785-87aa-bcf77ea1d7c1"
},
"conversation": {
"isGroup": true,
"conversationType": "channel",
"tenantId": "c8fef0de-e240-4456-b523-3285ecc62087",
"id": "19:y7qDBfGH2jE_Ze6G8mJS_CiWiqCaRFfH77jFZvJ1xgU1#thread.tacv2"
},
"recipient": {
"id": "28:64564f44-dd7c-441a-b427-efcd662f21b5",
"name": "Trick"
},
"channelData": {
"team": {
"aadGroupId": "5bc77aa9-9487-49ae-958f-b37b2191e64d",
"name": "test 5",
"id": "19:y7qDBfGH2jE_Ze6G8mJS_CiWiqCaRFfH77jFZvJ1xgU1#thread.tacv2"
},
"eventType": "teamMemberAdded",
"tenant": {
"id": "c8fef0de-e240-4456-b523-3285ecc62087"
}
}
}
So what I am trying to understand here is that, why is the information not coming in case the bot is installed in personal scope?
I am asking this mainly because without personal scope added for bot(i.e if I keep scope only as team), the application does not show for user, inside Apps. But if I allow the scope to be extended to personal the user might select that while installing the application and my teamId information will not be available to fetch the data, that I need.
This brings to my next question, which is, is there any way in which the default add button on the add app screen installs the bot in such a way that I get team object inside channelData, in conversationUpdate in every scenario, i.e whether I choose add or add to team?
Is this how it is supposed to behave or am I missing something. Would love to hear some thoughts on this. Thanks in advance.
It might be that you're over thinking this - here's a more simple view:
if you install a bot into a Team, you'll get a Team Id (and any related channel where it is installed).
if you have "personal" scope set up for the bot, then the user also has the option to install the bot into "personal" scope. As this implies, they are NOT installing the bot INTO an actual Team - that's why you're not receiving a Team Id. It's not broken - it's entirely correct.
If you don't WANT your bot to be able to be installed in personal scope, simply remove that option in your manifest (the "personal" scope) - you have the ability to choose because it depends what you're wanting the user to be able to do with your bot. Some bots only make sense inside a Team, others only in Personal Scope, others only in Group Chat or in a Meeting - you can allow your bot to installed in any/all of these are relevant.
Google People API, unlike Google Plus API doesn't provide an unique id for each Person resource in the response.
Why has this been deprecated/removed and how to uniquely identify a Person in an user's contacts list without an id?
In short, use resourceName as an ID.
The Google People API is the user's list of contacts and have IDs specific to the user. They can create contacts that only have a mailing address or only have a name and no contact info. There is no way for Google to logically collate all of these contacts across all of the Google Contacts users.
Specific contacts or a user have a resourceName field that uniquely identifies that resource (contact) for the authenticating user. You basically want to use that as an id. This is the value you for use, for example, to query People.get.
This is part of an example response of a single contact from People.connections:
{
"resourceName": "people/103710953423417258027",
"etag": "qwApd98gduQ=",
"metadata": {
"sources": [{
"type": "CONTACT",
"id": "1",
"etag": "#rj+KMFHVyHY="
}, {
"type": "PROFILE",
"id": "103710953423417258027",
"etag": "#4eZfef/IuMFw="
}],
"objectType": "PERSON"
},
...
}
resourceName is essentially the "id" of that contact and then in the metadata field it lists the sources the contact data comes from. E.g. CONTACT is for a Google Contacts entry where the user has manually entered name/phone/email/etc. PROFILE is a Google Profile, commonly with Google+ data.
The Person resource docs are a great place to learn more about these values.
Can we use contextual gadget on my personal gmail OR do I have to purchase and app domain for this?
My mainfeast.json is
{
"manifest_version": 2,
"name": "unsendMe-AddContact",
"version": "0.0.0.2",
"description": "Grab the contact of the mail sender and save .",
"icons": {
"128": "ICON_PLACE3 (2) (Custom) (2).png",
"16": "ICON_PLACE3 (2) (Custom).png"
},
"container": ["app"],
"api_console_project_id": "xxxxxxxxxx"
}
Gmail Contextual Gadgets are not available on your personal email. You will need to purchase an enterprise account.
Your manifest doesn't look right - you should check out the link below for the full details:
https://developers.google.com/gmail/contextual_gadgets#hello_world_example_manifest