Why I can't save my connector on Microsoft Teams? - microsoft-teams

I made a custom connector for teams that display notifications on channels and add Tab on teams.
For the tab parts -> Everything is working well
But I have a problem for the connector parts, I get an error when I try to save my settings in the developer console:
Received error from connectors {"seq":1585127802210,"timestamp":1585127814174,"flightSettings":{"Name":"ConnectorFrontEndSettings","AriaSDKT....
-> registerOnSaveHandler is called
-> setSettings is correctly set with entityId, contentUrl (same configurationUrl as the connector configuration) and the configName.
getSettings -> is called to save to my app the webhook url -> it works
-> notifySuccess is then called and
I checked on the connector dashboard everything seems fine, on the App Studio everything is green also!
I don't know what is happening..
My manifest
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json",
"manifestVersion": "1.5",
"version": "1.0.0",
"id": "ca153ede-92f2-46e7-8695-3726b5343bf4",
"packageName": "com.kagilum.icescrum",
"developer": {
"name": "Kagilum SAS",
"websiteUrl": "https://www.icescrum.com",
"privacyUrl": "https://www.icescrum.com/privacy",
"termsOfUseUrl": "https://www.icescrum.com/termsofuser"
},
"icons": {
"color": "color.png",
"outline": "outline.png"
},
"name": {
"short": "iceScrum",
"full": "Connect with iceScrum"
},
"description": {
"short": "A true Agile project management tool",
"full": "iceScrum is a web application for using Scrum while keeping the spirit of a collaborative workspace. It also offers virtual boards with post-its for sprint backlog, product backlog and others."
},
"accentColor": "#FFFFFF",
"configurableTabs": [
{
"configurationUrl": "https://preview.icescrum.com/msTeams/setupTab/",
"canUpdateConfiguration": true,
"scopes": [
"team",
"groupchat"
],
"supportedSharePointHosts": [
"sharePointFullPage",
"sharePointWebPart"
]
}
],
"connectors": [
{
"connectorId": "f00d8890-daa8-4c87-89f5-83cbab0bccd4",
"configurationUrl": "https://preview.icescrum.com/msTeams/setup/",
"scopes": [
"team"
]
}
],
"permissions": [
"identity",
"messageTeamMembers"
],
"validDomains": [
"preview.icescrum.com"
]
}
Piece of code related to
microsoftTeams.settings.registerOnSaveHandler(function(saveEvent) {
microsoftTeams.settings.setSettings({
entityId: 'icescrum-pkey-' + $scope.settings.project.pkey,
contentUrl: isSettings.serverUrl + '/msTeams/setup/',
configName: $scope.settings.project.name
});
microsoftTeams.settings.getSettings(function(settings) {
$scope.settings.webhookUrl = settings.webhookUrl;
return FormService.httpPost('msTeams/save', $scope.settings, true).then(function() {
saveEvent.notifySuccess(); //wait that the settings are really saved on iceScrum side
});
});
});
microsoftTeams.settings.getSettings(function(settings) {
$scope.setup = !settings.configName;
var tokenData = JSON.parse(localStorage.getItem("msTeams-oauth"));
var userData = JSON.parse(localStorage.getItem("msTeams-user"));
if (tokenData) {
FormService.httpGet('ws/project/user/' + userData.id + '?light=true', {headers: {'Authorization': 'Bearer ' + tokenData['accessToken']}}, true).then(function(projects) {
$scope.projects = projects;
if (settings.entityId) {
$scope.settings.project = _.find($scope.projects, {pkey: settings.entityId.split('icescrum-pkey-')[1]});
}
$scope.ready = true;
});
}
});
Full error:
angular.min.js:113 2020-03-25T20:35:25.953Z Received error from connectors {"seq":1585168484680,"timestamp":1585168525943,"flightSettings":{"Name":"ConnectorFrontEndSettings","AriaSDKToken":"d127f72a3abd41c9b9dd94faca947689-d58285e6-3a68-4cab-a458-37b9d9761d35-7033","SPAEnabled":true,"ClassificationFilterEnabled":true,"ClientRoutingEnabled":true,"EnableYammerGroupOption":true,"EnableFadeMessage":false,"EnableDomainBasedOwaConnectorList":false,"EnableDomainBasedTeamsConnectorList":false,"DevPortalSPAEnabled":true,"ShowHomeNavigationButtonOnConfigurationPage":false,"DisableConnectToO365InlineDeleteFeedbackPage":true},"status":500,"clientType":"SkypeSpaces","connectorType":"f00d8890-daa8-4c87-89f5-83cbab0bccd4","name":"handleMessageError"}

In case this helps anyone else, I spent ages today trying to get to the bottom of this and couldn't find a solution. Until...as a last gasp show of desperation I decide to use App Studio to recreate the entire connector manifest from scratch, including creating a new connector in the portal.
For some reason, this then worked fine - even though I can see that the two manifest files are identical with the exception of the ID (and I already tried regenerating the ID for the original one).

Bit late to the story, but having followed multiple github issues like this and stack overflow posts, I'm pretty convinced that its the problem with Connectors dashboard. If you made any change to settings, they are not really propageted/saved correctly.
So for example, if you changed validDomains or configurationPage URL, they won't actually do anything. You can verify that with your devtools. For me, after changing the configurationPage the Teams is still making request to old URL as well as the new one, but the old request produces error that's listed in question.
The only workaround I was able to find is to recreate connector in dashboard. Reported problem to MS Teams dev team, waiting to hear back.

Also late here, but I ran into this problem and the below was the solution after 3 days of frustration.
Despite everything mentioned in the documentation, the following is required otherwise you'll get this error. This fixed things for me.
microsoftTeams.settings.registerOnSaveHandler(saveEvent => {
microsoftTeams.settings.setSettings({
contentUrl: "https://xxxxxx.ngrok.io/teams/connector"
});
saveEvent.notifySuccess();
});
The documentation states that registering a save handler is optional and Teams will handle notify success if it's not declared. WRONG. You must register a save handler.
The documentation does not state that setSettings is required. WRONG. You must set settings or else you will receive this error.
The documentation does not state that you must save a contentURL. WRONG. You must set content URL in the setSettings. You can apparently omit other things when setting your settings, but not content URL.
The documentation does not specifically mention it, but the contentURL must comply with your validURLs in your manifest. If it does not, you'll also see this error.
So in your case, you must ensure that isSettings.serverUrl (setSettings() contentURL) does match preview.icescrum.com (manifest validURLs). If they do not, you'll see this error.

Related

How to fix messages_tab_disabled error in Slack's chat.postMessage API?

I'm trying to post a direct message to a specific Slack user. I'm using chat.postMethod method. The below example works fine when channel is a channel ID. But when I change it to a users ID (as described in this documentation), I get back a messages_tab_disabled error.
POST URL: https://slack.com/api/chat.postMessage
{
"channel": "U02F7EXXXXX",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Hello"
}
}
]
}
I get back...
{
"ok": false,
"error": "messages_tab_disabled"
}
I can't find much on this other than a description on the documentation page that just says "Messages tab for the app is disabled.".
Where is it and how do I enable it?
I stumbled upon how to fix this when I was poking around settings for my app. In case this helps anyone else in the future...
In your apps configuration page, under Features > App Home, scroll down to the Show Tabs section and there is an option called Messages Tab that is off by default. I turned it on and now I can message users directly.

Teams is not displaying my unfurl response

I have a Teams integration with link unfurling set up. I have the messaging endpoint pointed to a public ngrok URL and ngrok proxying a local node.js server that returns the example payload Microsoft has in it's documentation.
This is my endpoint (express.js):
app.post('/bot-test', (req, res) => {
res.send({
"composeExtension": {
"type": "result",
"attachmentLayout": "list",
"attachments": [
{
"contentType": "application/vnd.microsoft.teams.card.o365connector",
"content": {
"sections": [
{
"activityTitle": "[85069]: Create a cool app",
"activityImage": "https://placekitten.com/200/200"
},
{
"title": "Details",
"facts": [
{
"name": "Assigned to:",
"value": "[Larry Brown](mailto:larryb#example.com)"
},
{
"name": "State:",
"value": "Active"
}
]
}
]
}
}
]
}
});
});
When I post a URL in a message in Teams, I see it POST to that endpoint and it responds without errors, but nothing shows up in Teams. What's going wrong? I can't find any logs on Microsoft's side either. I would expect that Teams renders a card with the response payload.
Office 365 Connector cards are not supported for link unfurls ("Message extension previews" in MS parlance). Changing the response to a supported card type worked. Unfortunately, MS doesn't surface this problem anywhere and it just silently discards the response. I was able to find a somewhat useful error description by inspecting the network request that went out from the Teams web client, however.
See this for supported card types: https://learn.microsoft.com/en-us/microsoftteams/platform/task-modules-and-cards/cards/cards-reference

How to get team id when the bot is installed in personal scope(MS Teams)?

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.

Use GitHub GraphQL to retrieve all open milestones and all issues (open and closed) for the milestone

I understand that GitHub's GraphQL-based v4 API is much more efficient than the v3 API.
I would like to use the GraphQL API to retrieve, for a given repo:
All of the open milestones.
For each milestone, its title, description, all of its issues (open and closed)
For each issue, its title, description, status, and all messages.
Is there a straightforward way to do this?
Yes. It is straightforward to do so . The query looks like :
{
repository(owner: "gatsbyjs", name: "gatsby") {
description
url
milestones(states: [OPEN],first:2) {
nodes{
title
description
url
issues(states:[OPEN,CLOSED], first:2){
nodes{
title
state
url
comments(first:2){
nodes{
url
body
createdAt
}
pageInfo{
hasNextPage
endCursor
}
}
}
pageInfo{
endCursor
hasNextPage
}
}
}
pageInfo{
endCursor
hasNextPage
}
}
}
}
Note:
For the repository which the url is https://github.com/gatsbyjs/gatsby , its owner is gatsbyjs and its name is gatsby
Go to its API Explorer to try and fine tune the query.Click Ctrl+Space will auto-suggest the available fields that can be retrieved.
Do the paginating by yourself to loop through all records by adjusting the starting cursor and the number of records to be returned in first , after.
It gives you the following :
{
"data": {
"repository": {
"description": "Build blazing fast, modern apps and websites with React",
"url": "https://github.com/gatsbyjs/gatsby",
"milestones": {
"nodes": [
{
"title": "Next Major",
"description": "Issues that will require a breaking change, and which would constitute being done in the next major version of Gatsby.",
"url": "https://github.com/gatsbyjs/gatsby/milestone/5",
"issues": {
"nodes": [
{
"title": "Make accessibility warnings errors",
"state": "OPEN",
"url": "https://github.com/gatsbyjs/gatsby/issues/19945",
"comments": {
"nodes": [
{
"url": "https://github.com/gatsbyjs/gatsby/issues/19945#issuecomment-568891716",
"body": "Hiya!\n\nThis issue has gone quiet. Spooky quiet. 👻\n\nWe get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here.\nIf we missed this issue or if you want to keep it open, please reply here. You can also add the label \"not stale\" to keep this issue open!\nAs a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out [gatsby.dev/contribute](https://www.gatsbyjs.org/contributing/how-to-contribute/) for more information about opening PRs, triaging issues, and contributing!\n\nThanks for being a part of the Gatsby community! 💪💜",
"createdAt": "2019-12-25T12:02:26Z"
},
{
"url": "https://github.com/gatsbyjs/gatsby/issues/19945#issuecomment-570779866",
"body": "Hey again!\n\nIt’s been 30 days since anything happened on this issue, so our friendly neighborhood robot (that’s me!) is going to close it.\nPlease keep in mind that I’m only a robot, so if I’ve closed this issue in error, I’m `HUMAN_EMOTION_SORRY`. Please feel free to reopen this issue or create a new one if you need anything else.\nAs a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out [gatsby.dev/contribute](https://www.gatsbyjs.org/contributing/how-to-contribute/) for more information about opening PRs, triaging issues, and contributing!\n\nThanks again for being part of the Gatsby community! 💪💜",
"createdAt": "2020-01-04T12:02:28Z"
}
],
"pageInfo": {
"hasNextPage": false,
"endCursor": "Y3Vyc29yOnYyOpHOIgVo2g=="
}
}
},
{
"title": "Configurable output folder",
"state": "OPEN",
"url": "https://github.com/gatsbyjs/gatsby/issues/1878",
"comments": {
"nodes": [
{
"url": "https://github.com/gatsbyjs/gatsby/issues/1878#issuecomment-324062470",
"body": "Do you have a specific use case in mind? This has been discussed before but no one has come up with a concrete use case that justified adding a new option.\r\n\r\nEvery option we add to Gatsby makes the project more complex which has all sorts of long-term costs so unless something is really valuable, I'd rather people handle this sort of thing themselves e.g. just copy the files to the output directory you want or create a symlink. This could easily be turned into a plugin that people could install, etc.",
"createdAt": "2017-08-22T15:27:41Z"
},
{
"url": "https://github.com/gatsbyjs/gatsby/issues/1878#issuecomment-324074853",
"body": "Yes, I have a use-case. I am going to use Gatsby for a documentation part as a part of complex project. All static files (Gatsby output, plus some others) should be placed into one folder `build`, that will be deployed somehow later. In other words, the Gatsby output is only one subfolder in my setup.\r\n\r\nSo far I have worked this around in `postbuild` step, but it looks hacky:\r\n\r\n```\r\n\"build\": \"gatsby build\",\r\n\"postbuild\": \"mv public build/gatsby-subsite\"\r\n```\r\nAdding configurable output folder will reduce this complexity and will help me not to move files around one more time.",
"createdAt": "2017-08-22T16:08:21Z"
}
],
"pageInfo": {
"hasNextPage": true,
"endCursor": "Y3Vyc29yOnYyOpHOE1D9ZQ=="
}
}
}
],
"pageInfo": {
"endCursor": "Y3Vyc29yOnYyOpLPgAAAAAAAArvODwULXA==",
"hasNextPage": true
}
}
}
],
"pageInfo": {
"endCursor": "Y3Vyc29yOnYyOpHOAEEbsw==",
"hasNextPage": false
}
}
}
}
}

Amazon Alexa Device Discovery for Smart Home API with Lambda Failing

I have setup an Alexa Smart Home Skill, all settings done, oauth2 processed done and skill is enabled on my Amazon Echo device. Lambda function is setup and linked to the skill. When I "Discover Devices" I can see the payload hit my Lambda function in the log. I am literally returning via the context.succeed() method the following JSON with a test appliance. However Echo tells me that it fails to find any devices.
{
"header": {
"messageId": "42e0bf9c-18e2-424f-bb11-f8a12df1a79e",
"name": "DiscoverAppliancesResponse",
"namespace": "Alexa.ConnectedHome.Discovery",
"payloadVersion": "2"
},
"payload": {
"discoveredAppliances": [
{
"actions": [
"incrementPercentage",
"decrementPercentage",
"setPercentage",
"turnOn",
"turnOff"
],
"applianceId": "0d6884ab-030e-8ff4-ffffaa15c06e0453",
"friendlyDescription": "Study Light connected to Loxone Kit",
"friendlyName": "Study Light",
"isReachable": true,
"manufacturerName": "Loxone",
"modelName": "Spot"
}
]
}
}
Does the above payload look correct?
According to https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/smart-home-skill-api-reference#discovery-messages the version attribute is required. Your response seems to be missing that attribute.
In my (very short) experience with this, even the smallest mistake in the response would generate a silent error like the one you are experiencing.
I had the same problem. If you are creating discovery for "Entertainment Device", make sure you have wrapped the output in 'event' key for context.succeed
var payload = {
endpoints:
[
{
"endpointId": "My-id",
"manufacturerName": "Manufacturer",
"friendlyName": "Living room TV",
"description": "65in LED TV from Demo AV Company",
"displayCategories": [ ],
"cookie": {
"data": "e.g. ip address",
},
"capabilities":
[
{
"interface": "Alexa.Speaker",
"version": "1.0",
"type": "AlexaInterface"
},
]
}
]
};
var header = request.directive.header;
header.name = "Discover.Response";
context.succeed({ event: {
header: header, payload: payload
} });
Although, in the sample code, this is never mentioned and an incorrect example is given (https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/steps-to-create-a-smart-home-skill). However, the response body provided includes the "event" key.
Recreating lambda function helped me fix the issue. I also set "Enable trigger" check button while creating, though I'm not sure if that matters. After that my device provided by skill was found successfully.
Edit: Answer was wrong. Only useful information was this
This context.fail syntax is actually deprecated. Look up the Lambda context object properties, it should look more like "callback(null, resultObj)" now.
Did you include the return statement in your function?
return {
"header": header,
"payload": payload
}
It was missing in the example and after adding it, I was able to 'discover' my device.

Resources