Facebook get friend list issue in Unity - c#

I use Graph API Explorer User Data Permissions and check "user_friends" ,
use FQL Query to fill in "/me/friends", I can get it below:
{
"data": [
{
"name": "xxx",
"id": "750725034949941"
}
],
"paging": {
"next": "https://graph.facebook.com/v2.2/1569963793218579/friends?limit=25&offset=25& __after_id=enc_AexiIZhvUUA7W93CyCbEKqop8pgwMeZe0FVYg2fNQIrAxRs7nDvv1nuUG_aoKR8JOxUf29FXPlsfg9xVsGgDDToj"
},
"summary": {
"total_count": 1
}
}
But I use Unity app to call FB.Login("user_friends,email,public_actions",callback);
then call FB.API("/me/friends")
I only get the result below:
{"data":[],"summary":{"total_count":1}}
How to modify C# code to get friendList detail?

That´s a new feature of v2.0: You only get the friends who authorized your App too. If no other friend authorized your App, data will be empty. Also, FQL is deprecated.

Related

Application with AAD App Id is not authorized to generate notifications about [...] to the recipient

Good morning,
I'm trying to use the Microsoft graph's API to send notifications in Microsoft Teams feed.
I've created a C# APP and gave all Authorization following the official Guide:
Here
But I couldn't get the things done. So I've chosen to make things a little bit easier by cutting out the whole C# App using only Postman. The result is that I get the same error...
(ATTEMPT 1 WITH POSTMAN) -> This is the body of my request:
{
"topic": {
"source": "entityUrl",
"value": "https://graph.microsoft.com/beta/users/{MY_USER_ID}/teamwork/installedApps/{APP_ID}"
},
"activityType": "taskCreated",
"previewText": {
"content": "New Task Created"
},
"templateParameters": [
{
"name": "taskId",
"value": "Task 12322"
}
]
}
This is my call:
https://graph.microsoft.com:443/beta/users/{MY_USER_ID}/teamwork/microsoft.graph.sendActivityNotification
As result I get this:
{
"error": {
"code": "Forbidden",
"message": "Application with AAD App Id '{APP_ID}' is not authorized to generate notifications about 'https://graph.microsoft.com/beta/users/{MY_USER_ID}/teamwork/installedApps/{APP_ID}' to the recipient.",
"innerError": {
"date": "{date}",
"request-id": "{request-id}",
"client-request-id": "{client-request-id}"
}
}
}
(ATTEMPT 2 WITH POSTMAN) -> This is the body of my request (which is the same as the 1st one):
{
"topic": {
"source": "entityUrl",
"value": "https://graph.microsoft.com/beta/users/{MY_USER_ID}/teamwork/installedApps/{APP_ID}"
},
"activityType": "taskCreated",
"previewText": {
"content": "New Task Created"
},
"templateParameters": [
{
"name": "taskId",
"value": "Task 12322"
}
]
}
This is my call (the things which change):
https://graph.microsoft.com/beta/users/{MY_USER_ID}/teamwork/installedApps/{APP_ID}
and this is the error:
{
"error": {
"code": "UnknownError",
"message": "",
"innerError": {
"date": "{date}",
"request-id": "{request-id}",
"client-request-id": "{client-request-id}"
}
}
}
I think the right call is the first one, but I'm not sure...
I've already given the permission required by the guide I've linked above (TeamsActivity.Send 'delegated' and TeamsActivity.Send 'application').
I'm sure I'm missing something but from GraphExplorer I can't even test the right call/understand which permission I'm missing
Thanks in advance,
Giovanni
---------EDIT 1---------
As requested by Carl Zhao here my Access token, I had to censor some data but I think It will readable in the same way:
{
"typ": "JWT",
"nonce": "FHxXnlAuHU14c4czPflNLrniH-4d-4ZdRNawgEx6LQg",
"alg": "RS256",
"x5t": "nOo3ZDrODXEK1jKWhXslHR_KXEg",
"kid": "nOo3ZDrODXEK1jKWhXslHR_KXEg"
}.{
"aud": "00000003-0000-0000-c000-000000000000",
"iss": "https://sts.windows.net/9e892716-96e0-4bf4-a58a-e43212bfab33/",
"iat": 1614156685,
"nbf": 1614156685,
"exp": 1614160585,
"acct": 0,
"acr": "1",
"acrs": [
"urn:user:registersecurityinfo",
"urn:microsoft:req1",
"urn:microsoft:req2",
"urn:microsoft:req3",
"c1",
"c2",
"c3",
"c4",
"c5",
"c6",
"c7",
"c8",
"c9",
"c10",
"c11",
"c12",
"c13",
"c14",
"c15",
"c16",
"c17",
"c18",
"c19",
"c20",
"c21",
"c22",
"c23",
"c24",
"c25"
],
"aio": "E2ZgYBBXWFm5tc8nMm5J68RHD6qmJ31f8GpGeqzcg8/+XlvTE40B",
"amr": [
"pwd"
],
"app_displayname": "NotificationSender",
"appid": "{APP_ID}",
"appidacr": "0",
"family_name": "{My_surname}",
"given_name": "{My_name}",
"idtyp": "user",
"ipaddr": "{IP}",
"name": "{My_surname} {My_name}",
"oid": "{MY_USER_ID}",
"platf": "14",
"puid": "10032001002D8362",
"rh": "0.AREAFieJnuCW9EuliuQyEr-rM44Tv1nRFotKsOAymqy2XUwRALk.",
"scp": "email IdentityProvider.Read.All IdentityProvider.ReadWrite.All IdentityRiskEvent.Read.All IdentityRiskEvent.ReadWrite.All IdentityRiskyUser.Read.All IdentityRiskyUser.ReadWrite.All IdentityUserFlow.Read.All IdentityUserFlow.ReadWrite.All Notifications.ReadWrite.CreatedByApp openid profile TeamsActivity.Read TeamsActivity.Send TeamsApp.ReadWrite User.Read User.ReadBasic.All UserNotification.ReadWrite.CreatedByApp",
"sub": "EmlIIgfTtCx92uXpG26JS3A4s30BF-L_q08y7WpK45g",
"tenant_region_scope": "EU",
"tid": "9e892716-96e0-4bf4-a58a-e43212bfab33",
"unique_name": "{My_Mail}",
"upn": "{My_Mail}",
"uti": "hfEx4TU7lEOdFnLyO38VAA",
"ver": "1.0",
"wids": [
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13",
"158c047a-c907-4556-b7ef-446551a6b5f7",
"baf37b3a-610e-45da-9e62-d9d1e5e8914b",
"c4e39bd9-1100-46d3-8c65-fb160da0071f",
"9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3",
"b79fbf4d-3ef9-4689-8143-76b194e85509"
],
"xms_st": {
"sub": "xFCFf1A82XccwsRgTd7olVqOBVUE9pZ5d6QDyWjbojc"
},
"xms_tcdt": 1428499913
}.[Signature]
Sorry but I have to post this as an answer since I don't have enough rep to comment. More of a guide than an answer.
Prerequisite: Your C# app is registered in Azure, with Graph API permissions for TeamsActivity.Send
I was able to get this to work by doing the following:
Using the Teams "Developer Portal" app, I went to the "apps" tab and added a new app. Under "app features", I added an activity feed notification, and then I published as an app package (to upload as an org app later, you'll see why in the next step).
For some reason the app (client) id I entered in the previous step did not get put into my manifest file (maybe because I didn't add an SSO provider? I'm not sure). So I modified the teams app manifest to include my C# app's client id, adding this:
"webApplicationInfo": {
"id": "{my C# app's client id}",
"applicationPermissions": [
"TeamsActivity.Send"
]
}
I zipped up my files again and uploaded the package to the Teams admin center.
I was then able to install the custom app I had uploaded to my Teams app.
After installing the app, I tried testing my C# app but neither the id nor externalid values were working for my "APP_ID", was getting the same exception as you mentioned. So I opened Graph Explorer, and found out the id using this query: https://graph.microsoft.com/beta/me/teamwork/installedApps?$expand=teamsApp (it will be a long string, not a GUID).
Once I used that id, everything worked.
From my understanding, the Teams app and your C# app are essentially 2 different registered apps that are tied together using the client id of your C# app, which I assume is just for authentication purposes. I guess if you don't have your C# app's client id in the Teams app's manifest, it can't authenticate, which seemed to be my issue. Also make sure your recipient has the app installed as that can also cause this exception.

Shared OneDrive Directory From Application Authentication

I have an app that processes a bunch of data and generates some results. Currently the app emails the results, but this can be cumbersome and the email can get too big, so I'm looking to have the app store the results to a shared OneDrive folder. The app runs without user interaction.
I've been looking into multiple samples for the Microsoft.Graph sdk. I'v been able to authenticate using an application and the ConfidentialClient workflow, but I'm not sure how to find/access a shared directory in OneDrive. Currently just the root drive request doesn't return any children or anything useful. I could access the shared drive when I used the API to login as my user, but that was using a share link for my user. Do I need to generate a share link somehow for the app or not tied to a user? Or is there some other way to find a shared drive?
Here's the code creating my GraphServiceClient:
public static GraphServiceClient GetAuthenticatedClient()
{
if (graphClient == null)
{
ConfidentialClientApp = ConfidentialClientApplicationBuilder.Create(clientId).WithTenantId(FormBrowser.MsaTenantId).WithClientSecret(FormBrowser.MsaClientSecret).Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(ConfidentialClientApp);
graphClient = new GraphServiceClient(authProvider);
}
return graphClient;
}
Then I've tried some of these different calls just to make sure it is authenticating correctly:
//var shares = await this.graphClient.Shares[encodedUrl].Root.Request().Expand(expandValue).GetAsync();
//ProcessFolder(shares);
var drive = graphClient.Drive.Request().GetAsync();
ProcessFolder(await this.graphClient.Drive.Root.Request().Expand(expandValue).GetAsync());
Here's a sample JSON response from the Drive.Root request:
{
"createdDateTime": "2013-11-07T19:59:00+00:00",
"lastModifiedDateTime": "2019-09-15T02:12:23+00:00",
"name": "root",
"webUrl": "https://<company>.sharepoint.com/Documents",
"fileSystemInfo": {
"createdDateTime": "2013-11-07T19:59:00+00:00",
"lastModifiedDateTime": "2019-09-15T02:12:23+00:00"
},
"folder": {
"childCount": 0
},
"parentReference": {
"driveId": "<stuff>",
"driveType": "documentLibrary"
},
"root": {
},
"size": 0,
"children": [
],
"thumbnails": [
],
"id": "<stuff>",
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#drive/root(thumbnails(),children(thumbnails()))/$entity",
"children#odata.context": "https://graph.microsoft.com/v1.0/$metadata#drive/root/children(thumbnails())",
"thumbnails#odata.context": "https://graph.microsoft.com/v1.0/$metadata#drive/root/thumbnails",
"responseHeaders": {
"Transfer-Encoding": [
"chunked"
],
"Vary": [
"Accept-Encoding"
],
"request-id": [
"<id>"
],
"client-request-id": [
"<id>"
],
"x-ms-ags-diagnostic": [
"{\"ServerInfo\":{\"DataCenter\":\"North Central US\",\"Slice\":\"SliceC\",\"Ring\":\"1\",\"ScaleUnit\":\"000\",\"RoleInstance\":\"<stuff>\",\"ADSiteName\":\"<stuff>\"}}"
],
"OData-Version": [
"4.0"
],
"Duration": [
"259.0577"
],
"Strict-Transport-Security": [
"max-age=31536000"
],
"Cache-Control": [
"private"
],
"Date": [
"Thu, 19 Sep 2019 14:06:27 GMT"
]
},
"statusCode": "OK"
}
So I was able to get there through a round-about way, if someone knows an easier way I'd really appreciate it. Here are the steps I took:
1) Authenticated with my user and loaded the info using the sharing url:
string sharingUrl = "<url>";
string base64Value = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(sharingUrl));
string encodedUrl = "u!" + base64Value.TrimEnd('=').Replace('/', '_').Replace('+', '-');
var shares = await this.graphClient.Shares[encodedUrl].Root.Request().Expand(expandValue).GetAsync();
Once I got the response for that I noted the "driveId" for the drive. Then when I authenticate using my confidentialclient, I can specify the drive in my request:
await this.graphClient.Drives["<driveId from 1>"].Root.Request().Expand(expandValue).GetAsync()
I'm wondering if there's an easier way to find those driveId's, like from the sharepoint site?
Also, it looks like when I get the sharing link from Sharepoint, if I switch the link from "specific people" to "People in " then I can use the Shares to get the drive items.

AWS DynamoDB Write permissions aren't working

I'm doing a proof of concept with connecting to dynamodb, but I appear to be having issues with permissions.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:*"
],
"Resource": [
"arn:aws:dynamodb:us-west-1:172777662485:table/*"
],
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": [
"${www.amazon.com:user_id}"
]
}
}
}
]
}
The code:
DynamoDBContext context = new DynamoDBContext(new AmazonDynamoDBClient("...", "...", new AmazonDynamoDBConfig() { RegionEndpoint = RegionEndpoint.USWest1, MaxErrorRetry = 6 }));
var writeContext = context.CreateBatchWrite<Music>();
writeContext.AddPutItem(new Music() { Artist = "Test Artist", SongTitle = "SongTitle" });
writeContext.Execute();
The error I get is:
arn:aws:iam::...:user/DynamoDBTestUser is not authorized to perform: dynamodb:BatchWriteItem on resource: arn:aws:dynamodb:us-west-1:172777662485:table/Music
Does anyone see anything wrong here?
thanks
Your policy appears to be based on some of the samples from this document:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/specifying-conditions.html
However, your code does not appear to be adding a record with a partition key matching the username.
So there are 2 possible solutions, depending on your desired outcome.
Solution 1:
Assuming you simply want to insert records into the table, you can use a policy such as:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:*"
],
"Resource": "*"
}]
}
This policy grants the user fully DynamoDB access to any DynamoDB resource.
Solution 2:
If you want to restrict access to the table limiting the user to read/put records only for them, then you need to ensure your primary key attribute in your Music structure is populated with your IAM user name.

How to manage Facebook api response data in C# asp.net?

How to convert facebook api response in user readable HTML format?
I call graph api
https://graph.facebook.com/me/feed?access_token=<token>
below is my response data from API.
{
"data": [
{
"id": "100000626589435_240877109276507",
"from": {
"name": "Abhi Patel",
"id": "100000626589435"
},
"type": "status",
"created_time": "2011-08-02T10:36:17+0000",
"updated_time": "2011-08-02T10:36:17+0000"
},
{
"id": "100000626589435_240760105954874",
"from": {
"name": "Abhi Patel",
"id": "100000626589435"
},
"type": "status",
"created_time": "2011-08-02T03:02:21+0000",
"updated_time": "2011-08-02T03:02:21+0000"
},
{
"id": "100000626589435_223775454320006",
"from": {
"name": "Abhi Patel",
"id": "100000626589435"
},
"picture": "http://profile.ak.fbcdn.net/hprofile-ak-snc4/274314_100000898272591_5481895_q.jpg",
"link": "http://www.facebook.com/?ref=nf_fr",
"icon": "http://static.ak.fbcdn.net/images/icons/?8:",
"type": "link",
"created_time": "2011-06-28T18:56:44+0000",
"updated_time": "2011-06-28T18:56:44+0000"
}
],
"paging": {
"previous": "<previous link>",
"next": "<next link>"
}
}
also want paging in facebook response data,
I want 20 records from facebook api response. How to manage this things..
Facebook returns raw JSON data. There are no style elements to it. It's up to you to present the data returned in the format you choose. Imagine if Facebook returned HTML and style elements. That wouldn't work very well for desktop applications or mobile devices. Instead, they just give you the raw data, and you design the HTML elements, or the WPF Views, or whatever to show the data you want to show.
By returning the raw data, you can also store it locally in a database for your own queries, or your own applications purposes.
Edited to add: You can parse out the objects by accessing the JSON elements directly, or you can deserialize the result to C# objects.
Console.WriteLine(response.data[0].from.name);
As for paging, you need to parse out the Paging elements. The Facebook C# SDK returns dynamic objects, so you can do something like
string next = response.paging.next;
string prev = response.paging.prev;
And then just make a request to each URL to fetch the data you want.
Use JSON.net and convert into the XML then it would be easy to manage for you.

YouTube API Comments Feed

I am attempting to get the Comments Feed from a video entry using the YouTube API for .NET. I am working on a program in WPF and C#, but can't seem for the life of me to figure out how to retrieve this feed.
I tried looking at the YouTube API Developer's Guide, but it seems to be missing some information about Comment Feeds (near the bottom of the page).
This has changed in version 3 of the YouTube API. There is a new endpoint called commentThreads/list which allows you to return a thread of comments for a resource.
If you want to return a list of comments on a video resource, set up a GET request with part=id,snippet and videoId=[VIDEO_ID]. I'll be using https://www.youtube.com/watch?v=HwNIDcwfRLY as an example:
HTTP GET https://www.googleapis.com/youtube/v3/commentThreads?part=id%2Csnippet&videoId=HwNIDcwfRLY&key={YOUR_API_KEY}
Let's use the first comment returned as an example:
{
"kind": "youtube#commentThread",
"etag": "\"DsOZ7qVJA4mxdTxZeNzis6uE6ck/jhK_kJqnNF8_fiRI_o7w6ehubv8\"",
"id": "z120sfshyxzewt1nx23sevyr1vu1jd2pr04",
"snippet": {
"videoId": "HwNIDcwfRLY",
"topLevelComment": {
"kind": "youtube#comment",
"etag": "\"DsOZ7qVJA4mxdTxZeNzis6uE6ck/h903NemnXx-8Hfe6lRIYCFERSe4\"",
"id": "z120sfshyxzewt1nx23sevyr1vu1jd2pr04",
"snippet": {
"authorDisplayName": "mach-a-chine seahawksgoonie",
"authorProfileImageUrl": "https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg?sz=50",
"authorChannelUrl": "http://www.youtube.com/channel/UCBmJ0sw7plIZHLvhfz7oo_w",
"authorChannelId": {
"value": "UCBmJ0sw7plIZHLvhfz7oo_w"
},
"videoId": "HwNIDcwfRLY",
"textDisplay": "",
"authorGoogleplusProfileUrl": "https://plus.google.com/102274783439566633837",
"canRate": true,
"viewerRating": "none",
"likeCount": 0,
"publishedAt": "2016-02-05T03:42:35.158Z",
"updatedAt": "2016-02-05T03:42:35.158Z"
}
},
"canReply": true,
"totalReplyCount": 0,
"isPublic": true
}
}
Note that the comment isn't actually in this topLevelComment object. textDisplay returns the empty string, which is a known issue with the YouTube API. We need to make an additional request to commentThreads/list with id=[COMMENT_ID], where [COMMENT_ID] is topLevelComment.id:
HTTP GET https://www.googleapis.com/youtube/v3/commentThreads?part=id%2Csnippet&id=z120sfshyxzewt1nx23sevyr1vu1jd2pr04&key={YOUR_API_KEY}
The resulting response's snippet dictionary will have the user's comment as the value for the textDisplay key:
"snippet": {
"authorDisplayName": "mach-a-chine seahawksgoonie",
"authorProfileImageUrl": "https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg?sz=50",
"authorChannelUrl": "http://www.youtube.com/channel/UCBmJ0sw7plIZHLvhfz7oo_w",
"authorChannelId": {
"value": "UCBmJ0sw7plIZHLvhfz7oo_w"
},
"videoId": "HwNIDcwfRLY",
"textDisplay": "my next ring tone! yeah boy!\ufeff",
"authorGoogleplusProfileUrl": "https://plus.google.com/102274783439566633837",
"canRate": true,
"viewerRating": "none",
"likeCount": 0,
"publishedAt": "2016-02-05T03:42:35.158Z",
"updatedAt": "2016-02-05T03:42:35.158Z"
}
}
The comment is: "my next ring tone! yeah boy!"
Note that you can also pass in a list of up to 50 comma-separated id or videoId strings of comment objects to retrieve per API call.
See the Retrieve comments for a video guide for additional information and sample code.
YouTubeRequest request = ... // Your request object
Video v = ... // Your video object
Feed<Comment> comments = request.GetComments(v);
comments.entries will contain all the comments for the video v as Comment objects, so you don't need to mess with the feed at all.

Categories

Resources