using System; using System.Threading.Tasks; using System.Collections.Generic; using System.Linq; using TapTap.Login; using TapTap.Common; using TapTap.Login.Internal; using System.Net.Http; using System.Net.Http.Headers; using TapTap.Common.Internal.Json; using TapTap.Common.Internal.Http; using LC.Newtonsoft.Json; using System.Security.Cryptography; using System.Text; namespace TapTap.Friends { public class TapFriends { private static HttpClient httpClient = null; public static async Task QueryMutualList(string cursor = null, int size = 100) { AccessToken accessToken = await TapLogin.GetAccessToken(); if (accessToken == null) { throw new ArgumentNullException(nameof(accessToken)); } string url = $"{TapTapSdk.CurrentRegion.ApiHost()}/friends/v1/list"; Dictionary queryParams = new Dictionary { { "max_size", size }, {"client_id",TapTapSdk.ClientId } }; if (!string.IsNullOrEmpty(cursor)) { queryParams.Add("continuation_token", cursor); } IEnumerable queryPairs = queryParams.Select(kv => $"{kv.Key}={kv.Value}"); string queries = string.Join("&", queryPairs); url = $"{url}?{queries}"; var dt = DateTime.UtcNow - new DateTime(1970, 1, 1); var ts = (int)dt.TotalSeconds; var uri = new Uri(url); Dictionary headers = new Dictionary { { "Authorization", "MAC " + GetAuthorizationHeader(accessToken.kid,accessToken.macKey,accessToken.macAlgorithm, "GET",uri.PathAndQuery,uri.Host,"443", ts)} }; if (httpClient == null) { httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); httpClient.DefaultRequestHeaders.AcceptCharset.Add(new StringWithQualityHeaderValue("utf-8")); } HttpRequestMessage request = new HttpRequestMessage { RequestUri = new Uri(url), Method = HttpMethod.Get, }; HttpRequestHeaders reqHeaders = request.Headers; if (reqHeaders != null) { foreach (KeyValuePair kv in headers) { reqHeaders.Add(kv.Key, kv.Value.ToString()); } } TapHttpUtils.PrintRequest(httpClient, request); HttpResponseMessage originResponse = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); request.Dispose(); string resultString = await originResponse.Content.ReadAsStringAsync(); originResponse.Dispose(); TapHttpUtils.PrintResponse(originResponse, resultString); Dictionary response; if (originResponse.IsSuccessStatusCode) { response = JsonConvert.DeserializeObject>(resultString, TapJsonConverter.Default); } else { throw new TapException(((int)originResponse.StatusCode), resultString); } if (response.TryGetValue("data", out object dataObj) && dataObj is Dictionary data) { TapFriendResult result = new TapFriendResult(); if (data.TryGetValue("list", out object listObj) && listObj is List list) { result.FriendList = list.Cast>() .Select(item => new TapFriendInfo { OpenId = item["openid"] as string, Name = item.TryGetValue("nickname", out object nicknameObj) && (nicknameObj is string nickname) ? nickname : null, Avatar = item.TryGetValue("avatar", out object avatarObj) && (avatarObj is string avatar) ? avatar : null }) .ToList() .AsReadOnly(); result.Cursor = data.TryGetValue("is_truncated", out object isTruncatedObj) && isTruncatedObj is bool isTruncated && isTruncated ? (data.TryGetValue("next_continuation_token", out object nextSkipObj) && (nextSkipObj is string nextSkip) ? nextSkip : null) : null; return result; } } return null; } private static string GetAuthorizationHeader(string kid, string macKey, string macAlgorithm, string method, string uri, string host, string port, int timestamp) { var nonce = new Random().Next().ToString(); var normalizedString = $"{timestamp}\n{nonce}\n{method}\n{uri}\n{host}\n{port}\n\n"; HashAlgorithm hashGenerator; switch (macAlgorithm) { case "hmac-sha-256": hashGenerator = new HMACSHA256(Encoding.ASCII.GetBytes(macKey)); break; case "hmac-sha-1": hashGenerator = new HMACSHA1(Encoding.ASCII.GetBytes(macKey)); break; default: throw new InvalidOperationException("Unsupported MAC algorithm"); } var hash = Convert.ToBase64String(hashGenerator.ComputeHash(Encoding.ASCII.GetBytes(normalizedString))); var authorizationHeader = new StringBuilder(); authorizationHeader.AppendFormat(@"id=""{0}"",ts=""{1}"",nonce=""{2}"",mac=""{3}""", kid, timestamp, nonce, hash); return authorizationHeader.ToString(); } } }